├── 2012 ├── example_filing_separately.py ├── example_joint_return.py ├── example_single.py ├── f1040.py ├── f1040sa.py ├── f1040sd.py ├── f1040sse.py ├── f6251.py └── form.py ├── 2013 ├── ca540.py ├── ca540sca.py ├── ca540sp.py ├── example_joint_return.py ├── example_joint_return_amt.py ├── example_marginal_rates_joint.py ├── example_marginal_rates_single.py ├── example_single.py ├── f1040.py ├── f1040sa.py ├── f1040sd.py ├── f1040sse.py ├── f2441.py ├── f6251.py ├── f8606.py ├── f8801.py ├── f8801_2014.py ├── f8959.py ├── f8960.py └── form.py ├── 2014 ├── ca540.py ├── ca540sca.py ├── ca540sp.py ├── example_joint_return.py ├── example_joint_return_amt.py ├── example_marginal_rates_joint.py ├── example_marginal_rates_single.py ├── example_single.py ├── f1040.py ├── f1040sa.py ├── f1040sd.py ├── f1040sse.py ├── f2441.py ├── f6251.py ├── f8606.py ├── f8801.py ├── f8801_2015.py ├── f8959.py ├── f8960.py └── form.py ├── 2015 ├── ca540.py ├── ca540sca.py ├── ca540sp.py ├── example_joint_return.py ├── example_joint_return_amt.py ├── example_marginal_rates_joint.py ├── example_marginal_rates_single.py ├── example_single.py ├── f1040.py ├── f1040sa.py ├── f1040sd.py ├── f1040sse.py ├── f2441.py ├── f6251.py ├── f8606.py ├── f8801.py ├── f8801_2016.py ├── f8959.py ├── f8960.py └── form.py ├── 2016 ├── ca540.py ├── ca540sca.py ├── ca540sp.py ├── example_joint_return.py ├── example_joint_return_amt.py ├── example_marginal_rates_joint.py ├── example_marginal_rates_single.py ├── example_single.py ├── f1040.py ├── f1040sa.py ├── f1040sd.py ├── f1040sse.py ├── f2441.py ├── f6251.py ├── f8606.py ├── f8801.py ├── f8801_2017.py ├── f8959.py ├── f8960.py └── form.py ├── 2017 ├── ca540.py ├── ca540sca.py ├── ca540sp.py ├── example_joint_return.py ├── example_joint_return_amt.py ├── example_marginal_rates_joint.py ├── example_marginal_rates_single.py ├── example_single.py ├── f1040.py ├── f1040sa.py ├── f1040sd.py ├── f1040sse.py ├── f2441.py ├── f6251.py ├── f8606.py ├── f8801.py ├── f8801_2018.py ├── f8959.py ├── f8960.py └── form.py ├── 2018 ├── ca540.py ├── ca540sca.py ├── ca540sp.py ├── example_joint_return.py ├── example_joint_return_amt.py ├── example_marginal_rates_joint.py ├── example_marginal_rates_single.py ├── example_single.py ├── f1040.py ├── f1040sa.py ├── f1040sd.py ├── f1040sse.py ├── f2441.py ├── f6251.py ├── f8606.py ├── f8801.py ├── f8801_2019.py ├── f8959.py ├── f8960.py └── form.py ├── 2019 ├── ca540.py ├── ca540sca.py ├── ca540sp.py ├── example_joint_return.py ├── example_joint_return_amt.py ├── example_marginal_rates_joint.py ├── example_marginal_rates_single.py ├── example_single.py ├── f1040.py ├── f1040sa.py ├── f1040sd.py ├── f1040sse.py ├── f2441.py ├── f6251.py ├── f8606.py ├── f8801.py ├── f8801_2020.py ├── f8959.py ├── f8960.py └── form.py ├── 2020 ├── ca540.py ├── ca540sca.py ├── ca540sp.py ├── example_joint_return.py ├── example_joint_return_amt.py ├── example_marginal_rates_joint.py ├── example_marginal_rates_single.py ├── example_single.py ├── f1040.py ├── f1040sa.py ├── f1040sd.py ├── f1040sse.py ├── f2441.py ├── f6251.py ├── f8606.py ├── f8801.py ├── f8801_2021.py ├── f8959.py ├── f8960.py ├── f8995a.py └── form.py ├── 2021 ├── ca540.py ├── ca540sca.py ├── ca540sp.py ├── example_joint_return.py ├── example_joint_return_amt.py ├── example_marginal_rates_joint.py ├── example_marginal_rates_single.py ├── example_single.py ├── f1040.py ├── f1040sa.py ├── f1040sd.py ├── f1040sse.py ├── f2441.py ├── f6251.py ├── f8606.py ├── f8801.py ├── f8801_2022.py ├── f8812.py ├── f8959.py ├── f8960.py ├── f8995a.py └── form.py ├── 2022 ├── ca540.py ├── ca540sca.py ├── ca540sp.py ├── ca5805.py ├── example_joint_return.py ├── example_joint_return_amt.py ├── example_marginal_rates_joint.py ├── example_marginal_rates_single.py ├── example_single.py ├── f1040.py ├── f1040sa.py ├── f1040sd.py ├── f1040sse.py ├── f2441.py ├── f6251.py ├── f8606.py ├── f8801.py ├── f8801_2023.py ├── f8812.py ├── f8959.py ├── f8960.py ├── f8995a.py └── form.py ├── 2023 ├── ca540.py ├── ca540sca.py ├── ca540sp.py ├── ca5805.py ├── example_joint_return.py ├── example_joint_return_amt.py ├── example_marginal_rates_joint.py ├── example_marginal_rates_single.py ├── example_single.py ├── f1040.py ├── f1040sa.py ├── f1040sd.py ├── f1040sse.py ├── f2441.py ├── f6251.py ├── f8606.py ├── f8801.py ├── f8801_2024.py ├── f8812.py ├── f8959.py ├── f8960.py ├── f8995a.py └── form.py ├── 2024 ├── ca540.py ├── ca540sca.py ├── ca540sp.py ├── ca5805.py ├── example_joint_return.py ├── example_joint_return_amt.py ├── example_marginal_rates_joint.py ├── example_marginal_rates_single.py ├── example_single.py ├── f1040.py ├── f1040sa.py ├── f1040sd.py ├── f1040sse.py ├── f2441.py ├── f6251.py ├── f8606.py ├── f8801.py ├── f8812.py ├── f8959.py ├── f8960.py ├── f8995a.py └── form.py ├── .gitignore └── README /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | -------------------------------------------------------------------------------- /2012/example_filing_separately.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Example of a married couple filing separately. Each spouse has an income 4 | # of 100k. Also, the first spouse has some investment income and 5 | # business (Schedule C) income. 6 | # 7 | # First spouse will be subject to AMT as seen in the results (form 6251 8 | # gets created). 9 | # 10 | # Both spouses are owed refunds. 11 | # 12 | 13 | from f1040 import F1040 14 | from form import FilingStatus 15 | 16 | print('First Return:') 17 | 18 | inputs = { 19 | 'status': FilingStatus.SEPARATE, 20 | 'exemptions': 1, 21 | 'wages': 100000.00, # W2 box 1 22 | 'wages_ss': 100000.00, # W2 box 3 23 | 'withholding': 24000.00, # W2 box 2 24 | 'ss_withheld': 4200.00, # W2 box 4 25 | 'taxable_interest': 1500.00, 26 | 'tax_exempt_interest': 700.00, 27 | 'dividends': 3000.00, 28 | 'qualified_dividends': 2000.00, 29 | 'capital_gain_dist': 500.00, 30 | 'capital_gain_long': 5000.00, 31 | 'business_income': 5000.00, 32 | 33 | # Extra items for schedule A 34 | 'F1040sa' : { 35 | '5' : 15000, # state tax paid 36 | '16' : 500, # charitable contributions 37 | }, 38 | } 39 | 40 | f = F1040(inputs) 41 | f.printAllForms() 42 | 43 | print('Second Return:') 44 | 45 | inputs = { 46 | 'status': FilingStatus.SEPARATE, 47 | 'exemptions': 1, 48 | 'wages': 100000.00, # W2 box 1 49 | 'wages_ss': 100000.00, # W2 box 3 50 | 'withholding': 20000.00, # W2 box 2 51 | 'ss_withheld': 4200.00, # W2 box 4 52 | } 53 | 54 | f = F1040(inputs) 55 | f.printAllForms() 56 | -------------------------------------------------------------------------------- /2012/example_joint_return.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Example of a married couple filing jointly. Each spouse has an income 4 | # of 100k. Also, the first spouse has some investment income and 5 | # business (Schedule C) income. 6 | # 7 | # Due to high state taxes paid (see Schedule A) this couple will pay AMT. 8 | # Form 6251 is shown in the output. 9 | # 10 | # A balance is owed on the return. 11 | # 12 | 13 | from f1040 import F1040 14 | from form import FilingStatus 15 | 16 | # Note that some of the quantities below we need to list separately for each 17 | # spouse in an array. 18 | 19 | inputs = { 20 | 'status': FilingStatus.JOINT, 21 | 'exemptions': 2, 22 | 'wages': [100000.00, 100000.00], # W2 box 1 23 | 'wages_ss': [100000.00, 100000.00], # W2 box 3 24 | 'withholding': 37000.00, # W2 box 2 25 | 'ss_withheld': [4200.00, 4200.00], # W2 box 4 26 | 'taxable_interest': 1500.00, 27 | 'tax_exempt_interest': 700.00, 28 | 'dividends': 3000.00, 29 | 'qualified_dividends': 2000.00, 30 | 'capital_gain_dist': 500.00, 31 | 'capital_gain_long': 5000.00, 32 | 'business_income': [5000.00, 0.00], 33 | 34 | # Extra items for schedule A 35 | 'F1040sa' : { 36 | '5' : 25000, # state tax paid 37 | '16' : 500, # charitable contributions 38 | }, 39 | } 40 | 41 | f = F1040(inputs) 42 | f.printAllForms() 43 | -------------------------------------------------------------------------------- /2012/example_single.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Example of a single person with wage income of 100k. Also some investment 4 | # income and business (Schedule C) income. 5 | # 6 | 7 | from f1040 import F1040 8 | from form import FilingStatus 9 | 10 | inputs = { 11 | 'status': FilingStatus.SINGLE, 12 | 'exemptions': 1, 13 | 'wages': 100000.00, # W2 box 1 14 | 'wages_ss': 100000.00, # W2 box 3 15 | 'withholding': 24000.00, # W2 box 2 16 | 'ss_withheld': 4200.00, # W2 box 4 17 | 'taxable_interest': 1500.00, 18 | 'tax_exempt_interest': 700.00, 19 | 'dividends': 3000.00, 20 | 'qualified_dividends': 2000.00, 21 | 'capital_gain_dist': 500.00, 22 | 'capital_gain_long': 5000.00, 23 | 'business_income': 5000.00, 24 | 25 | # Extra items for schedule A 26 | 'F1040sa' : { 27 | '5' : 10000, # state tax paid 28 | '16' : 500, # charitable contributions 29 | }, 30 | } 31 | 32 | f = F1040(inputs) 33 | f.printAllForms() 34 | -------------------------------------------------------------------------------- /2012/f1040sa.py: -------------------------------------------------------------------------------- 1 | from form import Form 2 | 3 | class F1040sa(Form): 4 | def __init__(f, inputs, f1040): 5 | super(F1040sa, f).__init__(inputs) 6 | if f['1']: 7 | f['2'] = f1040['38'] 8 | f['3'] = f['2'] * .075 9 | f['4'] = max(0, f['1'] - f['3']) 10 | f['9'] = f.rowsum(['5', '6', '7', '8']) 11 | f['15'] = f.rowsum(['10', '11', '12', '13', '14']) 12 | f['19'] = f.rowsum(['16', '17', '18']) 13 | f['24'] = f.rowsum(['21', '22', '23']) 14 | if '24' in f: 15 | f['25'] = f1040['38'] 16 | f['26'] = f['25'] * .02 17 | f['27'] = max(0, f['24'] - f['26']) 18 | f['29'] = f.rowsum(['4', '9', '15', '19', '20', '27', '28']) 19 | f.must_file = True 20 | 21 | def title(self): 22 | return 'Schedule A' 23 | -------------------------------------------------------------------------------- /2012/f1040sd.py: -------------------------------------------------------------------------------- 1 | from form import Form 2 | 3 | class F1040sd(Form): 4 | def __init__(f, inputs): 5 | super(F1040sd, f).__init__(inputs) 6 | if 'capital_gain_long' not in inputs and 'capital_gain_short' not in inputs: 7 | return 8 | f.must_file = True 9 | f['1'] = inputs.get('capital_gain_short') 10 | f['7'] = f.rowsum(['1', '2', '3', '4', '5', '6']) 11 | f['8'] = inputs.get('capital_gain_long') 12 | f['13'] = inputs.get('capital_gain_dist') 13 | f['15'] = f.rowsum(['8', '9', '10', '11', '12', '13', '14']) 14 | f['16'] = f.rowsum(['7', '15']) 15 | if f['16'] < 0: 16 | f['21'] = max(f['16'], -3000) 17 | 18 | # If lines 15 and 16 are both gains and line 18 or 19 has a value: 19 | # Use the Schedule D tax worksheet 20 | # Else if lines 15 and 16 are both gains or you have qualified divs: 21 | # Use the Qualified Dividends and Capital Gain Tax Worksheet 22 | # Else 23 | # Use tax tables 24 | 25 | def title(self): 26 | return 'Schedule D' 27 | -------------------------------------------------------------------------------- /2013/ca540sca.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class CA540sca(Form): 4 | STD_DED = [3906, 7812, 3906, 7812, 7812] 5 | 6 | def __init__(f, inputs, f1040, f1040sa): 7 | super(CA540sca, f).__init__(inputs) 8 | f['B19'] = f1040.get('19') 9 | f['B22'] = f.rowsum(['B7', 'B8a', 'B9a', 'B10', 'B11', 'B12', 'B13', 10 | 'B14', 'B15b', 'B16b', 'B17', 'B18', 'B19', 11 | 'B20b', 'B21']) 12 | f['C22'] = f.rowsum(['C7', 'C8a', 'C9a', 'C10', 'C11', 'C12', 'C13', 13 | 'C14', 'C15b', 'C16b', 'C17', 'C18', 'C19', 14 | 'C20b', 'C21']) 15 | f['B36'] = f.rowsum(['B23', 'B24', 'B25', 'B34', 'B35']) 16 | f['C36'] = f.rowsum(['C24', 'C31a', 'C33']) 17 | f['B37'] = f['B22'] - f['B36'] 18 | f['C37'] = f['C22'] - f['C36'] 19 | 20 | f['38'] = f1040sa.rowsum(['4', '9', '15', '19', '20', '27', '28']) 21 | f['39'] = f1040sa.rowsum(['5', '8']) 22 | f['40'] = f['38'] - f['39'] 23 | f['42'] = f.rowsum(['40', '41']) 24 | f['43'] = f.itemized_deductions_worksheet(inputs, f1040, f1040sa) 25 | f['44'] = max(f['43'], f.STD_DED[inputs['status']]) 26 | if f['B37'] or f['C37'] or f['44'] != f.STD_DED[inputs['status']]: 27 | f.must_file = True 28 | 29 | def itemized_deductions_worksheet(f, inputs, f1040, f1040sa): 30 | LIMITS = [172615, 345235, 172615, 258927, 345235] 31 | w = {} 32 | w['1'] = f['42'] 33 | if f1040['37'] <= LIMITS[inputs['status']]: 34 | return w['1'] 35 | w['2'] = f1040sa.rowsum(['4', '14', '20']) or 0 36 | w['3'] = w['1'] - w['2'] 37 | w['4'] = w['3'] * .8 38 | w['5'] = f1040['37'] 39 | w['6'] = LIMITS[inputs['status']] 40 | w['7'] = w['5'] - w['6'] 41 | if w['7'] <= 0: 42 | return w['1'] 43 | w['8'] = w['7'] * .06 44 | w['9'] = min(w['4'], w['8']) 45 | w['10'] = w['1'] - w['9'] 46 | return w['10'] 47 | 48 | def title(self): 49 | return 'CA 540 Schedule CA' 50 | -------------------------------------------------------------------------------- /2013/ca540sp.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class CA540sp(Form): 4 | LIMITS = [172615, 345235, 172615, 258927, 345235] 5 | EXEMPTION_LIMITS = [238051, 317401, 158700, 238051, 317401] 6 | EXEMPTIONS = [63481, 84640, 42319, 63481, 84640] 7 | 8 | def __init__(f, inputs, ca540, ca540sca, f1040, f1040sa): 9 | super(CA540sp, f).__init__(inputs) 10 | if ca540['18'] != ca540sca.STD_DED[inputs['status']]: 11 | f['2'] = min(f1040sa['4'], .025 * f1040['37']) 12 | f['3'] = f1040sa.rowsum(['6', '7']) 13 | f['4'] = f1040sa.rowsum(['10', '11', '12']) 14 | f['5'] = f1040sa.get('27') 15 | else: 16 | f['1'] = ca540['18'] 17 | f['14'] = f.rowsum(['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', 18 | '11', '12', '13']) 19 | f['15'] = ca540['19'] 20 | f['16'] = ca540sca.rowsum(['B21d', 'B21e']) 21 | f['17'] = -max(0, f1040['12']) 22 | if ca540sca.mustFile(): 23 | f['18'] = ca540sca['42'] - ca540sca['43'] or None 24 | f['19'] = f.rowsum(['14', '15', '16', '17', '18']) 25 | f['21'] = f['19'] - f['20'] 26 | assert(inputs['status'] != FilingStatus.SEPARATE or f['21'] <= 327976) 27 | 28 | f['22'] = f.exemption(inputs) 29 | f['23'] = max(0, f['21'] - f['22']) 30 | f['24'] = f['23'] * .07 31 | f['25'] = ca540['31'] 32 | f['26'] = max(0, f['24'] - f['25']) or None 33 | if f['26']: 34 | f.must_file = True 35 | 36 | def exemption(f, inputs): 37 | w = {} 38 | w['1'] = f.EXEMPTIONS[inputs['status']] 39 | w['2'] = f['21'] 40 | w['3'] = f.EXEMPTION_LIMITS[inputs['status']] 41 | w['4'] = max(0, w['2'] - w['3']) 42 | w['5'] = w['4'] * .25 43 | w['6'] = max(0, w['1'] - w['5']) 44 | return w['6'] 45 | 46 | def title(self): 47 | return 'CA 540 Schedule P' 48 | -------------------------------------------------------------------------------- /2013/example_joint_return.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Example of a married couple filing jointly. Each spouse has an income 4 | # of 100k. Also, the first spouse has some investment income and 5 | # business (Schedule C) income. 6 | # 7 | # A balance is owed on the return. 8 | # 9 | # On the state return, a refund is due. 10 | # 11 | 12 | from f1040 import F1040 13 | from ca540 import CA540 14 | from form import FilingStatus 15 | 16 | # Note that some of the quantities below we need to list separately for each 17 | # spouse in an array. Everything else we just need a total. 18 | 19 | inputs = { 20 | 'status': FilingStatus.JOINT, 21 | 'exemptions': 2, 22 | 'wages': [100000.00, 100000.00], # W2 box 1 23 | 'withholding': 37000.00, # W2 box 2 24 | 'wages_ss': [100000.00, 100000.00], # W2 box 3 25 | 'ss_withheld': [ 6200.00, 6200.00], # W2 box 4 26 | 'wages_medicare': [100000.00, 100000.00], # W2 box 5 27 | 'medicare_withheld': [ 1450.00, 1450.00], # W2 box 6 28 | 'state_withholding': 18000.00, # W2 box 17 29 | 30 | # These are other state tax payments made in 2013 not included in 31 | # 'state_withholding' that are deductible on schedule A: 32 | 'extra_state_tax_payments': 3000.00, 33 | 34 | 'taxable_interest': 1500.00, 35 | 'tax_exempt_interest': 700.00, 36 | 'dividends': 3000.00, 37 | 'qualified_dividends': 2000.00, 38 | 'capital_gain_dist': 500.00, 39 | 'capital_gain_long': 5000.00, 40 | 'business_income': [5000.00, 0.00], 41 | 42 | # Extra items for schedule A 43 | 'F1040sa' : { 44 | '16' : 500, # charitable contributions 45 | }, 46 | } 47 | 48 | f = F1040(inputs) 49 | f.printAllForms() 50 | print('') 51 | ca = CA540(inputs, f) 52 | ca.printAllForms() 53 | -------------------------------------------------------------------------------- /2013/example_joint_return_amt.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Example of a married couple filing jointly. Each spouse has an income 4 | # of 100k. Also, the first spouse has some investment income and 5 | # business (Schedule C) income. 6 | # 7 | # This example increases the amount of state tax paid on Schedule A, causing 8 | # this couple to be paying AMT instead of regular tax. Form 6251 is added. 9 | # 10 | # A balance is owed on the return. 11 | # 12 | # On the state return, a refund is due. 13 | # 14 | 15 | from f1040 import F1040 16 | from ca540 import CA540 17 | from form import FilingStatus 18 | 19 | # Note that some of the quantities below we need to list separately for each 20 | # spouse in an array. Everything else we just need a total. 21 | 22 | inputs = { 23 | 'status': FilingStatus.JOINT, 24 | 'exemptions': 2, 25 | 'wages': [100000.00, 100000.00], # W2 box 1 26 | 'withholding': 37000.00, # W2 box 2 27 | 'wages_ss': [100000.00, 100000.00], # W2 box 3 28 | 'ss_withheld': [ 6200.00, 6200.00], # W2 box 4 29 | 'wages_medicare': [100000.00, 100000.00], # W2 box 5 30 | 'medicare_withheld': [ 1450.00, 1450.00], # W2 box 6 31 | 'state_withholding': 18000.00, # W2 box 17 32 | 33 | # These are other state tax payments made in 2013 not included in 34 | # 'state_withholding' that are deductible on schedule A: 35 | 'extra_state_tax_payments': 8000.00, 36 | 37 | 'taxable_interest': 1500.00, 38 | 'tax_exempt_interest': 700.00, 39 | 'dividends': 3000.00, 40 | 'qualified_dividends': 2000.00, 41 | 'capital_gain_dist': 500.00, 42 | 'capital_gain_long': 5000.00, 43 | 'business_income': [5000.00, 0.00], 44 | 45 | # Extra items for schedule A 46 | 'F1040sa' : { 47 | '16' : 500, # charitable contributions 48 | }, 49 | } 50 | 51 | f = F1040(inputs) 52 | f.printAllForms() 53 | print('') 54 | ca = CA540(inputs, f) 55 | ca.printAllForms() 56 | -------------------------------------------------------------------------------- /2013/example_single.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Example of a single person with wage income of 100k. Also some investment 4 | # income and business (Schedule C) income. 5 | # 6 | 7 | from f1040 import F1040 8 | from ca540 import CA540 9 | from form import FilingStatus 10 | 11 | inputs = { 12 | 'status': FilingStatus.SINGLE, 13 | 'exemptions': 1, 14 | 'wages': 100000.00, # W2 box 1 15 | 'withholding': 20000.00, # W2 box 2 16 | 'wages_ss': 100000.00, # W2 box 3 17 | 'ss_withheld': 6200.00, # W2 box 4 18 | 'wages_medicare': 100000.00, # W2 box 5 19 | 'medicare_withheld': 1450.00, # W2 box 6 20 | 'state_withholding': 10000.00, # W2 box 17 21 | 22 | # These are other state tax payments made in 2013 not included in 23 | # 'state_withholding' that are deductible on schedule A: 24 | 'extra_state_tax_payments': 1000.00, 25 | 26 | 'taxable_interest': 1500.00, 27 | 'tax_exempt_interest': 700.00, 28 | 'dividends': 3000.00, 29 | 'qualified_dividends': 2000.00, 30 | 'capital_gain_dist': 500.00, 31 | 'capital_gain_long': 5000.00, 32 | 'business_income': 5000.00, 33 | 34 | # Extra items for schedule A 35 | 'F1040sa' : { 36 | '16' : 500, # charitable contributions 37 | }, 38 | } 39 | 40 | f = F1040(inputs) 41 | f.printAllForms() 42 | print('') 43 | ca = CA540(inputs, f) 44 | ca.printAllForms() 45 | -------------------------------------------------------------------------------- /2013/f1040sa.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F1040sa(Form): 4 | LIMITS = [250000, 300000, 150000, 275000, 300000] 5 | 6 | def __init__(f, inputs, f1040): 7 | super(F1040sa, f).__init__(inputs) 8 | if f['1']: 9 | f['2'] = f1040['38'] 10 | # TODO: 7.5% if born before January 2, 1949 11 | f['3'] = f['2'] * .10 12 | f['4'] = max(0, f['1'] - f['3']) 13 | f['5'] = inputs['state_withholding'] + \ 14 | inputs.get('extra_state_tax_payments', 0) 15 | f['9'] = f.rowsum(['5', '6', '7', '8']) 16 | f['15'] = f.rowsum(['10', '11', '12', '13', '14']) 17 | f['19'] = f.rowsum(['16', '17', '18']) 18 | f['24'] = f.rowsum(['21', '22', '23']) 19 | if '24' in f: 20 | f['25'] = f1040['38'] 21 | f['26'] = f['25'] * .02 22 | f['27'] = max(0, f['24'] - f['26']) 23 | f['29'] = f.worksheet(inputs, f1040)['10'] 24 | f.must_file = True 25 | 26 | def worksheet(f, inputs, f1040): 27 | w = {} 28 | w['1'] = f.rowsum(['4', '9', '15', '19', '20', '27', '28']) or 0 29 | # TODO: gambling, casualty, theft losses from line 28 30 | w['2'] = f.rowsum(['4', '14', '20']) or 0 31 | if w['2'] >= w['1']: 32 | w['10'] = w['1'] 33 | return w 34 | w['3'] = w['1'] - w['2'] 35 | w['4'] = w['3'] * .8 36 | w['5'] = f1040['38'] 37 | w['6'] = f.LIMITS[inputs['status']] 38 | if w['6'] >= w['5']: 39 | w['10'] = w['1'] 40 | return w 41 | w['7'] = w['5'] - w['6'] 42 | w['8'] = w['7'] * .03 43 | w['9'] = min(w['4'], w['8']) 44 | w['10'] = w['1'] - w['9'] 45 | return w 46 | 47 | def title(self): 48 | return 'Schedule A' 49 | -------------------------------------------------------------------------------- /2013/f1040sd.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F1040sd(Form): 4 | def __init__(f, inputs): 5 | super(F1040sd, f).__init__(inputs) 6 | if 'capital_gain_long' not in inputs and 'capital_gain_short' not in inputs: 7 | return 8 | f.must_file = True 9 | f['1'] = inputs.get('capital_gain_short') 10 | f['7'] = f.rowsum(['1', '2', '3', '4', '5', '6']) 11 | f['8'] = inputs.get('capital_gain_long') 12 | f['13'] = inputs.get('capital_gain_dist') 13 | f['15'] = f.rowsum(['8', '9', '10', '11', '12', '13', '14']) 14 | f['16'] = f.rowsum(['7', '15']) 15 | if f['16'] < 0: 16 | cutoff = -3000 17 | if inputs['status'] == FilingStatus.SEPARATE: 18 | cutoff = -1500 19 | f['21'] = max(f['16'], cutoff) 20 | 21 | # If lines 15 and 16 are both gains and line 18 or 19 has a value: 22 | # Use the Schedule D tax worksheet 23 | # Else if lines 15 and 16 are both gains or you have qualified divs: 24 | # Use the Qualified Dividends and Capital Gain Tax Worksheet 25 | # Else 26 | # Use tax tables 27 | 28 | def title(self): 29 | return 'Schedule D' 30 | -------------------------------------------------------------------------------- /2013/f2441.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F2441(Form): 4 | """Form 2441, Child and Dependent Care Expenses""" 5 | def __init__(f, inputs, sse): 6 | super(F2441, f).__init__(inputs) 7 | # Part III must be completed before Part II 8 | f['15'] = f['12'] + f['13'] - f['14'] 9 | f['17'] = min(f['15'], f['16']) 10 | 11 | # TODO: logic for filing separately 12 | assert(inputs['status'] != FilingStatus.SEPARATE) 13 | if inputs['status'] == FilingStatus.JOINT: 14 | f['18'] = f.earned_income(inputs['wages'][0], sse[0]) 15 | f['19'] = f.earned_income(inputs['wages'][1], sse[1]) 16 | else: 17 | f['18'] = f['19'] = f.earned_income(inputs['wages'], sse) 18 | f['20'] = min(f['17'], f['18'], f['19']) 19 | f['21'] = 5000 20 | # TODO: partnership/proprietorship-provided childcare benefits 21 | f['22'] = 0 22 | f['23'] = f['15'] - f['22'] 23 | f['24'] = min(f['20'], f['21'], f['22']) 24 | f['25'] = min(f['20'], f['21']) 25 | f['26'] = max(0, f['23'] - f['25']) 26 | assert(f['24'] == 0) 27 | if f['24'] or f['25'] or f['26']: 28 | f.must_file = True 29 | f['27'] = min(inputs.get('dependent_care_persons', 0), 2) * 3000 30 | f['28'] = f['24'] + f['25'] 31 | f['29'] = f['27'] - f['28'] 32 | # TODO: child care credit 33 | assert(f['29'] <= 0) 34 | return 35 | 36 | def earned_income(f, wages, sse): 37 | if sse.mustFile(): 38 | return wages + (sse['A3'] or sse['B3']) - (sse['A6'] or sse['B13']) 39 | else: 40 | return wages 41 | 42 | def title(self): 43 | return 'Form 2441' 44 | -------------------------------------------------------------------------------- /2013/f8606.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8606(Form): 4 | """Form 8606, Nondeductible IRAs""" 5 | def __init__(f, inputs, spouse): 6 | super(F8606, f).__init__(inputs, init_idx=spouse) 7 | f.spouse = spouse 8 | f['3'] = f['1'] + f['2'] 9 | f['5'] = f['3'] - f['4'] 10 | f['9'] = f.rowsum(['6', '7', '8']) 11 | f['10'] = min(1000, f['5'] * 1000.0 / f['9']) 12 | f['11'] = f['8'] * f['10'] / 1000.0 13 | f['12'] = f['7'] * f['10'] / 1000.0 14 | f['13'] = f['11'] + f['12'] 15 | f['14'] = f['3'] - f['13'] 16 | f['15'] = f['7'] - f['12'] 17 | f['17'] = f['11'] 18 | f['18'] = f['16'] - f['17'] 19 | # TODO: Roth IRA distributions 20 | f.must_file = True 21 | return 22 | 23 | def title(self): 24 | if self.spouse is not None: 25 | return 'Form 8606 [%d]' % self.spouse 26 | else: 27 | return 'Form 8606' 28 | -------------------------------------------------------------------------------- /2013/f8801.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8801(Form): 4 | """Form 8801, Credit for Prior Year Minimum Tax""" 5 | def __init__(f, inputs, f1040, f6251): 6 | super(F8801, f).__init__(inputs) 7 | f['21'] = inputs.get('prior_amt_credit') 8 | if not f['21']: 9 | return 10 | 11 | f.must_file = True 12 | f['22'] = max(0, f1040['44'] - \ 13 | (f1040.rowsum(['47', '48', '49', '50', '51', '52', '53']) or 0)) 14 | f['23'] = f6251.get('33') 15 | f['24'] = max(0, f['22'] - f['23']) 16 | f.comment['25'] = 'Minimum tax credit' 17 | f['25'] = min(f['21'], f['24']) 18 | if f['25']: 19 | f6251.must_file = True 20 | f.comment['26'] = 'Credit carryforward to 2014' 21 | f['26'] = f['21'] - f['25'] 22 | 23 | def title(self): 24 | return 'Form 8801 (for 2013 filing)' 25 | -------------------------------------------------------------------------------- /2013/f8959.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8959(Form): 4 | """Form 8959, Additional Medicare Tax""" 5 | THRESHOLDS = [200000, 250000, 125000, 200000, 200000] 6 | def __init__(f, inputs, f1040, sse): 7 | super(F8959, f).__init__(inputs) 8 | f['1'] = f.spouseSum(inputs, 'wages_medicare') 9 | # TODO: forms 4137, 8919 10 | f['4'] = f.rowsum(['1', '2', '3']) 11 | if f.get('4'): 12 | f['5'] = f.THRESHOLDS[inputs['status']] 13 | f['6'] = max(0, f['4'] - f['5']) 14 | f['7'] = f['6'] * .009 15 | 16 | if inputs['status'] == FilingStatus.JOINT: 17 | if sse[0].mustFile() or sse[1].mustFile(): 18 | f['8'] = max(0, (sse[0]['A4'] or sse[0]['B6'] or 0) + 19 | (sse[1]['A4'] or sse[1]['B6'] or 0)) 20 | else: 21 | if sse.mustFile(): 22 | f['8'] = max(0, sse['A4'] or sse['B6']) 23 | if f.get('8'): 24 | f['9'] = f.THRESHOLDS[inputs['status']] 25 | f['10'] = f.get('4') 26 | f['11'] = max(0, f['9'] - f['10']) 27 | f['12'] = max(0, f['8'] - f['11']) 28 | f['13'] = f['12'] * .009 or None 29 | 30 | if f.get('14'): 31 | f['15'] = f.THRESHOLDS[inputs['status']] 32 | f['16'] = max(0, f['14'] - f['15']) 33 | f['17'] = f['16'] * .009 or None 34 | 35 | f['18'] = f.rowsum(['7', '13', '17']) 36 | 37 | f['19'] = f.spouseSum(inputs, 'medicare_withheld') 38 | f['20'] = f.get('1') 39 | f['21'] = f['20'] * .0145 or None 40 | if f['19'] or f['21']: 41 | f['22'] = f['19'] - f['21'] 42 | f['24'] = f.rowsum(['22', '23']) 43 | 44 | if f.get('18') or f.get('24'): 45 | f.must_file = True 46 | return 47 | 48 | def title(self): 49 | return 'Form 8959' 50 | -------------------------------------------------------------------------------- /2013/f8960.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8960(Form): 4 | """Form 8960, Net Investment Income Tax""" 5 | THRESHOLDS = [200000, 250000, 125000, 200000, 250000] 6 | def __init__(f, inputs, f1040, sched_a): 7 | super(F8960, f).__init__(inputs) 8 | f['1'] = f1040.get('8a') 9 | f['2'] = f1040.get('9a') 10 | # TODO: annuities 11 | f['4a'] = f1040.get('17') 12 | f['4c'] = f.rowsum(['4a', '4b']) 13 | f['5a'] = f1040.rowsum(['13', '14']) 14 | f['5d'] = f.rowsum(['5a', '5b', '5c']) 15 | f['8'] = f.rowsum(['1', '2', '3', '4c', '5d', '6', '7']) 16 | if sched_a: 17 | # This is one example of a "reasonable method allocation" but 18 | # not the only way. 19 | # Note: this only works for state income tax, not sales tax 20 | f['9b'] = f['8'] * sched_a['5'] / f1040['38'] 21 | f['9d'] = f.rowsum(['9a', '9b', '9c']) 22 | f['11'] = f.rowsum(['9d', '10']) 23 | f['12'] = max(0, f['8'] - f['11']) 24 | f['13'] = f1040['38'] 25 | f['14'] = f.THRESHOLDS[inputs['status']] 26 | f['15'] = max(0, f['13'] - f['14']) 27 | f['16'] = min(f['12'], f['15']) 28 | if f['16']: 29 | f['17'] = f['16'] * .038 30 | f.must_file = True 31 | return 32 | 33 | def title(self): 34 | return 'Form 8960' 35 | -------------------------------------------------------------------------------- /2014/ca540sca.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class CA540sca(Form): 4 | STD_DED = [3992, 7984, 3992, 7984, 7984] 5 | 6 | def __init__(f, inputs, f1040, f1040sa): 7 | super(CA540sca, f).__init__(inputs) 8 | f['B19'] = f1040.get('19') 9 | f['B22'] = f.rowsum(['B7', 'B8a', 'B9a', 'B10', 'B11', 'B12', 'B13', 10 | 'B14', 'B15b', 'B16b', 'B17', 'B18', 'B19', 11 | 'B20b', 'B21']) 12 | f['C22'] = f.rowsum(['C7', 'C8a', 'C9a', 'C10', 'C11', 'C12', 'C13', 13 | 'C14', 'C15b', 'C16b', 'C17', 'C18', 'C19', 14 | 'C20b', 'C21']) 15 | f['B36'] = f.rowsum(['B23', 'B24', 'B25', 'B34', 'B35']) 16 | f['C36'] = f.rowsum(['C24', 'C31a', 'C33']) 17 | f['B37'] = f['B22'] - f['B36'] 18 | f['C37'] = f['C22'] - f['C36'] 19 | 20 | f['38'] = f1040sa.rowsum(['4', '9', '15', '19', '20', '27', '28']) 21 | f['39'] = f1040sa.rowsum(['5', '8']) 22 | f['40'] = f['38'] - f['39'] 23 | f['42'] = f.rowsum(['40', '41']) 24 | f['43'] = f.itemized_deductions_worksheet(inputs, f1040, f1040sa) 25 | f['44'] = max(f['43'], f.STD_DED[inputs['status']]) 26 | if f['B37'] or f['C37'] or f['44'] != f.STD_DED[inputs['status']]: 27 | f.must_file = True 28 | 29 | def itemized_deductions_worksheet(f, inputs, f1040, f1040sa): 30 | LIMITS = [176413, 352830, 176413, 264623, 352830] 31 | w = {} 32 | w['1'] = f['42'] 33 | if f1040['37'] <= LIMITS[inputs['status']]: 34 | return w['1'] 35 | w['2'] = f1040sa.rowsum(['4', '14', '20']) or 0 36 | w['3'] = w['1'] - w['2'] 37 | if w['3'] == 0: 38 | return w['1'] 39 | w['4'] = w['3'] * .8 40 | w['5'] = f1040['37'] 41 | w['6'] = LIMITS[inputs['status']] 42 | w['7'] = w['5'] - w['6'] 43 | if w['7'] <= 0: 44 | return w['1'] 45 | w['8'] = w['7'] * .06 46 | w['9'] = min(w['4'], w['8']) 47 | w['10'] = w['1'] - w['9'] 48 | return w['10'] 49 | 50 | def title(self): 51 | return 'CA 540 Schedule CA' 52 | -------------------------------------------------------------------------------- /2014/ca540sp.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class CA540sp(Form): 4 | EXEMPTION_LIMITS = [243288, 324384, 162191, 243288, 324384] 5 | EXEMPTIONS = [64878, 86502, 43250, 64878, 86502] 6 | 7 | def __init__(f, inputs, ca540, ca540sca, f1040, f1040sa): 8 | super(CA540sp, f).__init__(inputs) 9 | if ca540['18'] != ca540sca.STD_DED[inputs['status']]: 10 | f['2'] = min(f1040sa['4'], .025 * f1040['37']) 11 | f['3'] = f1040sa.rowsum(['6', '7']) 12 | f['5'] = f1040sa.get('27') 13 | else: 14 | f['1'] = ca540['18'] 15 | f['14'] = f.rowsum(['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', 16 | '11', '12', '13']) 17 | f['15'] = ca540['19'] 18 | if not f['15']: 19 | f['15'] = ca540['17'] - ca540['18'] 20 | f['16'] = ca540sca.rowsum(['B21d', 'B21e']) 21 | f['17'] = -max(0, f1040['12']) 22 | if ca540sca.mustFile(): 23 | f['18'] = ca540sca['42'] - ca540sca['43'] or None 24 | f['19'] = f.rowsum(['14', '15', '16', '17', '18']) 25 | f['21'] = f['19'] - f['20'] 26 | assert(inputs['status'] != FilingStatus.SEPARATE or f['21'] <= 327976) 27 | 28 | f['22'] = f.exemption(inputs) 29 | f['23'] = max(0, f['21'] - f['22']) 30 | f['24'] = f['23'] * .07 31 | f['25'] = ca540['31'] 32 | f['26'] = max(0, f['24'] - f['25']) or None 33 | if f['26']: 34 | f.must_file = True 35 | 36 | def exemption(f, inputs): 37 | w = {} 38 | w['1'] = f.EXEMPTIONS[inputs['status']] 39 | w['2'] = f['21'] 40 | w['3'] = f.EXEMPTION_LIMITS[inputs['status']] 41 | w['4'] = max(0, w['2'] - w['3']) 42 | w['5'] = w['4'] * .25 43 | w['6'] = max(0, w['1'] - w['5']) 44 | return w['6'] 45 | 46 | def title(self): 47 | return 'CA 540 Schedule P' 48 | -------------------------------------------------------------------------------- /2014/example_joint_return.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Example of a married couple filing jointly. Each spouse has an income 4 | # of 100k. Also, the first spouse has some investment income and 5 | # business (Schedule C) income. 6 | # 7 | # A balance is owed on the return. 8 | # 9 | # On the state return, a refund is due. 10 | # 11 | 12 | from f1040 import F1040 13 | from ca540 import CA540 14 | from form import FilingStatus 15 | 16 | # Note that some of the quantities below we need to list separately for each 17 | # spouse in an array. Everything else we just need a total. 18 | 19 | inputs = { 20 | 'status': FilingStatus.JOINT, 21 | 'exemptions': 2, 22 | 'wages': [100000.00, 100000.00], # W2 box 1 23 | 'withholding': 37000.00, # W2 box 2 24 | 'wages_ss': [100000.00, 100000.00], # W2 box 3 25 | 'ss_withheld': [ 6200.00, 6200.00], # W2 box 4 26 | 'wages_medicare': [100000.00, 100000.00], # W2 box 5 27 | 'medicare_withheld': [ 1450.00, 1450.00], # W2 box 6 28 | 'state_withholding': 18000.00, # W2 box 17 29 | 30 | # These are other state tax payments made in 2014 not included in 31 | # 'state_withholding' that are deductible on schedule A: 32 | 'extra_state_tax_payments': 3000.00, 33 | 34 | 'taxable_interest': 1500.00, 35 | 'tax_exempt_interest': 700.00, 36 | 'dividends': 3000.00, 37 | 'qualified_dividends': 2000.00, 38 | 'capital_gain_dist': 500.00, 39 | 'capital_gain_long': 5000.00, 40 | 'business_income': [5000.00, 0.00], 41 | 42 | # Extra items for schedule A 43 | 'F1040sa' : { 44 | '16' : 500, # charitable contributions 45 | }, 46 | } 47 | 48 | f = F1040(inputs) 49 | f.printAllForms() 50 | print('') 51 | ca = CA540(inputs, f) 52 | ca.printAllForms() 53 | -------------------------------------------------------------------------------- /2014/example_joint_return_amt.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Example of a married couple filing jointly. Each spouse has an income 4 | # of 100k. Also, the first spouse has some investment income and 5 | # business (Schedule C) income. 6 | # 7 | # This example increases the amount of state tax paid on Schedule A, causing 8 | # this couple to be paying AMT instead of regular tax. Form 6251 is added. 9 | # 10 | # A balance is owed on the return. 11 | # 12 | # On the state return, a refund is due. 13 | # 14 | 15 | from f1040 import F1040 16 | from ca540 import CA540 17 | from form import FilingStatus 18 | 19 | # Note that some of the quantities below we need to list separately for each 20 | # spouse in an array. Everything else we just need a total. 21 | 22 | inputs = { 23 | 'status': FilingStatus.JOINT, 24 | 'exemptions': 2, 25 | 'wages': [100000.00, 100000.00], # W2 box 1 26 | 'withholding': 37000.00, # W2 box 2 27 | 'wages_ss': [100000.00, 100000.00], # W2 box 3 28 | 'ss_withheld': [ 6200.00, 6200.00], # W2 box 4 29 | 'wages_medicare': [100000.00, 100000.00], # W2 box 5 30 | 'medicare_withheld': [ 1450.00, 1450.00], # W2 box 6 31 | 'state_withholding': 18000.00, # W2 box 17 32 | 33 | # These are other state tax payments made in 2014 not included in 34 | # 'state_withholding' that are deductible on schedule A: 35 | 'extra_state_tax_payments': 8000.00, 36 | 37 | 'taxable_interest': 1500.00, 38 | 'tax_exempt_interest': 700.00, 39 | 'dividends': 3000.00, 40 | 'qualified_dividends': 2000.00, 41 | 'capital_gain_dist': 500.00, 42 | 'capital_gain_long': 5000.00, 43 | 'business_income': [5000.00, 0.00], 44 | 45 | # Extra items for schedule A 46 | 'F1040sa' : { 47 | '16' : 500, # charitable contributions 48 | }, 49 | } 50 | 51 | f = F1040(inputs) 52 | f.printAllForms() 53 | print('') 54 | ca = CA540(inputs, f) 55 | ca.printAllForms() 56 | -------------------------------------------------------------------------------- /2014/example_single.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Example of a single person with wage income of 100k. Also some investment 4 | # income and business (Schedule C) income. 5 | # 6 | 7 | from f1040 import F1040 8 | from ca540 import CA540 9 | from form import FilingStatus 10 | 11 | inputs = { 12 | 'status': FilingStatus.SINGLE, 13 | 'exemptions': 1, 14 | 'wages': 100000.00, # W2 box 1 15 | 'withholding': 20000.00, # W2 box 2 16 | 'wages_ss': 100000.00, # W2 box 3 17 | 'ss_withheld': 6200.00, # W2 box 4 18 | 'wages_medicare': 100000.00, # W2 box 5 19 | 'medicare_withheld': 1450.00, # W2 box 6 20 | 'state_withholding': 10000.00, # W2 box 17 21 | 22 | # These are other state tax payments made in 2014 not included in 23 | # 'state_withholding' that are deductible on schedule A: 24 | 'extra_state_tax_payments': 1000.00, 25 | 26 | 'taxable_interest': 1500.00, 27 | 'tax_exempt_interest': 700.00, 28 | 'dividends': 3000.00, 29 | 'qualified_dividends': 2000.00, 30 | 'capital_gain_dist': 500.00, 31 | 'capital_gain_long': 5000.00, 32 | 'business_income': 5000.00, 33 | 34 | # Extra items for schedule A 35 | 'F1040sa' : { 36 | '16' : 500, # charitable contributions 37 | }, 38 | } 39 | 40 | f = F1040(inputs) 41 | f.printAllForms() 42 | print('') 43 | ca = CA540(inputs, f) 44 | ca.printAllForms() 45 | -------------------------------------------------------------------------------- /2014/f1040sa.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F1040sa(Form): 4 | LIMITS = [254200, 305050, 152525, 279650, 305050] 5 | 6 | def __init__(f, inputs, f1040): 7 | super(F1040sa, f).__init__(inputs) 8 | if f['1']: 9 | f['2'] = f1040['38'] 10 | # TODO: 7.5% if born before January 2, 1950 11 | f['3'] = f['2'] * .10 12 | f['4'] = max(0, f['1'] - f['3']) 13 | f['5'] = inputs['state_withholding'] + \ 14 | inputs.get('extra_state_tax_payments', 0) 15 | f['9'] = f.rowsum(['5', '6', '7', '8']) 16 | f['15'] = f.rowsum(['10', '11', '12', '13', '14']) 17 | f['19'] = f.rowsum(['16', '17', '18']) 18 | f['24'] = f.rowsum(['21', '22', '23']) 19 | if '24' in f: 20 | f['25'] = f1040['38'] 21 | f['26'] = f['25'] * .02 22 | f['27'] = max(0, f['24'] - f['26']) 23 | f['29'] = f.worksheet(inputs, f1040)['10'] 24 | f.must_file = True 25 | 26 | def worksheet(f, inputs, f1040): 27 | w = {} 28 | w['1'] = f.rowsum(['4', '9', '15', '19', '20', '27', '28']) or 0 29 | # TODO: gambling, casualty, theft losses from line 28 30 | w['2'] = f.rowsum(['4', '14', '20']) or 0 31 | if w['2'] >= w['1']: 32 | w['10'] = w['1'] 33 | return w 34 | w['3'] = w['1'] - w['2'] 35 | w['4'] = w['3'] * .8 36 | w['5'] = f1040['38'] 37 | w['6'] = f.LIMITS[inputs['status']] 38 | if w['6'] >= w['5']: 39 | w['10'] = w['1'] 40 | return w 41 | w['7'] = w['5'] - w['6'] 42 | w['8'] = w['7'] * .03 43 | w['9'] = min(w['4'], w['8']) 44 | w['10'] = w['1'] - w['9'] 45 | return w 46 | 47 | def title(self): 48 | return 'Schedule A' 49 | -------------------------------------------------------------------------------- /2014/f1040sd.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F1040sd(Form): 4 | def __init__(f, inputs): 5 | super(F1040sd, f).__init__(inputs) 6 | if 'capital_gain_long' not in inputs and 'capital_gain_short' not in inputs: 7 | return 8 | f.must_file = True 9 | f['1'] = inputs.get('capital_gain_short') 10 | f['7'] = f.rowsum(['1', '2', '3', '4', '5', '6']) 11 | f['8'] = inputs.get('capital_gain_long') 12 | f['13'] = inputs.get('capital_gain_dist') 13 | f['15'] = f.rowsum(['8', '9', '10', '11', '12', '13', '14']) 14 | f['16'] = f.rowsum(['7', '15']) 15 | if f['16'] < 0: 16 | cutoff = -3000 17 | if inputs['status'] == FilingStatus.SEPARATE: 18 | cutoff = -1500 19 | f['21'] = max(f['16'], cutoff) 20 | 21 | # If lines 15 and 16 are both gains and line 18 or 19 has a value: 22 | # Use the Schedule D tax worksheet 23 | # Else if lines 15 and 16 are both gains or you have qualified divs: 24 | # Use the Qualified Dividends and Capital Gain Tax Worksheet 25 | # Else 26 | # Use tax tables 27 | 28 | def title(self): 29 | return 'Schedule D' 30 | -------------------------------------------------------------------------------- /2014/f2441.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F2441(Form): 4 | """Form 2441, Child and Dependent Care Expenses""" 5 | def __init__(f, inputs, sse): 6 | super(F2441, f).__init__(inputs) 7 | # Part III must be completed before Part II 8 | f['15'] = f['12'] + f['13'] - f['14'] 9 | f['17'] = min(f['15'], f['16']) 10 | 11 | # TODO: logic for filing separately 12 | assert(inputs['status'] != FilingStatus.SEPARATE) 13 | if inputs['status'] == FilingStatus.JOINT: 14 | f['18'] = f.earned_income(inputs['wages'][0], sse[0]) 15 | f['19'] = f.earned_income(inputs['wages'][1], sse[1]) 16 | else: 17 | f['18'] = f['19'] = f.earned_income(inputs['wages'], sse) 18 | f['20'] = min(f['17'], f['18'], f['19']) 19 | f['21'] = 5000 20 | # TODO: partnership/proprietorship-provided childcare benefits 21 | f['22'] = 0 22 | f['23'] = f['15'] - f['22'] 23 | f.comment['24'] = 'Deductible benefits' 24 | f['24'] = min(f['20'], f['21'], f['22']) 25 | f.comment['25'] = 'Excluded benefits' 26 | f['25'] = min(f['20'], f['21']) 27 | f.comment['26'] = 'Taxable benefits' 28 | f['26'] = max(0, f['23'] - f['25']) 29 | assert(f['24'] == 0) 30 | if f['24'] or f['25'] or f['26']: 31 | f.must_file = True 32 | f['27'] = min(inputs.get('dependent_care_persons', 0), 2) * 3000 33 | f['28'] = f['24'] + f['25'] 34 | f['29'] = f['27'] - f['28'] 35 | # TODO: child care credit 36 | assert(f['29'] <= 0) 37 | return 38 | 39 | def earned_income(f, wages, sse): 40 | if sse.mustFile(): 41 | return wages + (sse['A3'] or sse['B3']) - (sse['A6'] or sse['B13']) 42 | else: 43 | return wages 44 | 45 | def title(self): 46 | return 'Form 2441' 47 | -------------------------------------------------------------------------------- /2014/f8606.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8606(Form): 4 | """Form 8606, Nondeductible IRAs""" 5 | def __init__(f, inputs, spouse): 6 | super(F8606, f).__init__(inputs, init_idx=spouse) 7 | f.spouse = spouse 8 | f['3'] = f['1'] + f['2'] 9 | f['5'] = f['3'] - f['4'] 10 | f['9'] = f.rowsum(['6', '7', '8']) 11 | f['10'] = min(1000, f['5'] * 1000.0 / f['9']) 12 | f['11'] = f['8'] * f['10'] / 1000.0 13 | f['12'] = f['7'] * f['10'] / 1000.0 14 | f['13'] = f['11'] + f['12'] 15 | f['14'] = f['3'] - f['13'] 16 | f['15'] = f['7'] - f['12'] 17 | f['17'] = f['11'] 18 | f['18'] = f['16'] - f['17'] 19 | # TODO: Roth IRA distributions 20 | f.must_file = True 21 | return 22 | 23 | def title(self): 24 | if self.spouse is not None: 25 | return 'Form 8606 [%d]' % self.spouse 26 | else: 27 | return 'Form 8606' 28 | -------------------------------------------------------------------------------- /2014/f8801.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8801(Form): 4 | """Form 8801, Credit for Prior Year Minimum Tax""" 5 | def __init__(f, inputs, f1040, f6251): 6 | super(F8801, f).__init__(inputs) 7 | f['21'] = inputs.get('prior_amt_credit') 8 | if not f['21']: 9 | return 10 | 11 | f.must_file = True 12 | f['22'] = max(0, f1040['44'] + f1040['46'] - \ 13 | (f1040.rowsum(['48', '49', '50', '51', '52', '53', '54']) or 0)) 14 | f['23'] = f6251.get('33') 15 | f['24'] = max(0, f['22'] - f['23']) 16 | f.comment['25'] = 'Minimum tax credit' 17 | f['25'] = min(f['21'], f['24']) 18 | if f['25']: 19 | f6251.must_file = True 20 | f.comment['26'] = 'Credit carryforward to 2015' 21 | f['26'] = f['21'] - f['25'] 22 | 23 | def title(self): 24 | return 'Form 8801 (for 2014 filing)' 25 | -------------------------------------------------------------------------------- /2014/f8959.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8959(Form): 4 | """Form 8959, Additional Medicare Tax""" 5 | THRESHOLDS = [200000, 250000, 125000, 200000, 200000] 6 | def __init__(f, inputs, f1040, sse): 7 | super(F8959, f).__init__(inputs) 8 | f['1'] = f.spouseSum(inputs, 'wages_medicare') 9 | # TODO: forms 4137, 8919 10 | f['4'] = f.rowsum(['1', '2', '3']) 11 | if f.get('4'): 12 | f['5'] = f.THRESHOLDS[inputs['status']] 13 | f['6'] = max(0, f['4'] - f['5']) 14 | f['7'] = f['6'] * .009 15 | 16 | if inputs['status'] == FilingStatus.JOINT: 17 | if sse[0].mustFile() or sse[1].mustFile(): 18 | f['8'] = max(0, (sse[0]['A4'] or sse[0]['B6'] or 0) + 19 | (sse[1]['A4'] or sse[1]['B6'] or 0)) 20 | else: 21 | if sse.mustFile(): 22 | f['8'] = max(0, sse['A4'] or sse['B6']) 23 | if f.get('8'): 24 | assert(f['8'] >= 400) 25 | f['9'] = f.THRESHOLDS[inputs['status']] 26 | f['10'] = f.get('4') 27 | f['11'] = max(0, f['9'] - f['10']) 28 | f['12'] = max(0, f['8'] - f['11']) 29 | f['13'] = f['12'] * .009 or None 30 | 31 | if f.get('14'): 32 | f['15'] = f.THRESHOLDS[inputs['status']] 33 | f['16'] = max(0, f['14'] - f['15']) 34 | f['17'] = f['16'] * .009 or None 35 | 36 | f['18'] = f.rowsum(['7', '13', '17']) 37 | 38 | f['19'] = f.spouseSum(inputs, 'medicare_withheld') 39 | f['20'] = f.get('1') 40 | f['21'] = f['20'] * .0145 or None 41 | if f['19'] or f['21']: 42 | f['22'] = f['19'] - f['21'] 43 | f['24'] = f.rowsum(['22', '23']) 44 | 45 | if f.get('18') or f.get('24'): 46 | f.must_file = True 47 | return 48 | 49 | def title(self): 50 | return 'Form 8959' 51 | -------------------------------------------------------------------------------- /2014/f8960.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8960(Form): 4 | """Form 8960, Net Investment Income Tax""" 5 | THRESHOLDS = [200000, 250000, 125000, 200000, 250000] 6 | def __init__(f, inputs, f1040, sched_a): 7 | super(F8960, f).__init__(inputs) 8 | f['1'] = f1040.get('8a') 9 | f['2'] = f1040.get('9a') 10 | # TODO: annuities 11 | f['4a'] = f1040.get('17') 12 | f['4c'] = f.rowsum(['4a', '4b']) 13 | f['5a'] = f1040.rowsum(['13', '14']) 14 | f['5d'] = f.rowsum(['5a', '5b', '5c']) 15 | f['8'] = f.rowsum(['1', '2', '3', '4c', '5d', '6', '7']) 16 | if sched_a: 17 | # This is one example of a "reasonable method allocation" but 18 | # not the only way. 19 | # Note: this only works for state income tax, not sales tax 20 | f['9b'] = f['8'] * sched_a['5'] / f1040['38'] 21 | f['9d'] = f.rowsum(['9a', '9b', '9c']) 22 | f['11'] = f.rowsum(['9d', '10']) 23 | f['12'] = max(0, f['8'] - f['11']) 24 | f['13'] = f1040['38'] 25 | f['14'] = f.THRESHOLDS[inputs['status']] 26 | f['15'] = max(0, f['13'] - f['14']) 27 | f['16'] = min(f['12'], f['15']) 28 | if f['16']: 29 | f['17'] = f['16'] * .038 30 | f.must_file = True 31 | return 32 | 33 | def title(self): 34 | return 'Form 8960' 35 | -------------------------------------------------------------------------------- /2015/ca540sca.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class CA540sca(Form): 4 | STD_DED = [4044, 8088, 4044, 8088, 8088] 5 | 6 | def __init__(f, inputs, f1040, f1040sa): 7 | super(CA540sca, f).__init__(inputs) 8 | f['B19'] = f1040.get('19') 9 | f['B22'] = f.rowsum(['B7', 'B8a', 'B9a', 'B10', 'B11', 'B12', 'B13', 10 | 'B14', 'B15b', 'B16b', 'B17', 'B18', 'B19', 11 | 'B20b', 'B21']) 12 | f['C22'] = f.rowsum(['C7', 'C8a', 'C9a', 'C10', 'C11', 'C12', 'C13', 13 | 'C14', 'C15b', 'C16b', 'C17', 'C18', 'C19', 14 | 'C20b', 'C21']) 15 | f['B36'] = f.rowsum(['B23', 'B24', 'B25', 'B34', 'B35']) 16 | f['C36'] = f.rowsum(['C24', 'C31a', 'C33']) 17 | f['B37'] = f['B22'] - f['B36'] 18 | f['C37'] = f['C22'] - f['C36'] 19 | 20 | f['38'] = f1040sa.rowsum(['4', '9', '15', '19', '20', '27', '28']) 21 | f['39'] = f1040sa.rowsum(['5', '8']) 22 | f['40'] = f['38'] - f['39'] 23 | f['42'] = f.rowsum(['40', '41']) 24 | f['43'] = f.itemized_deductions_worksheet(inputs, f1040, f1040sa) 25 | f['44'] = max(f['43'], f.STD_DED[inputs['status']]) 26 | if f['B37'] or f['C37'] or f['44'] != f.STD_DED[inputs['status']]: 27 | f.must_file = True 28 | 29 | def itemized_deductions_worksheet(f, inputs, f1040, f1040sa): 30 | LIMITS = [178706, 357417, 178706, 268063, 357417] 31 | w = {} 32 | w['1'] = f['42'] 33 | if f1040['37'] <= LIMITS[inputs['status']]: 34 | return w['1'] 35 | w['2'] = f1040sa.rowsum(['4', '14', '20']) or 0 36 | w['3'] = w['1'] - w['2'] 37 | if w['3'] == 0: 38 | return w['1'] 39 | w['4'] = w['3'] * .8 40 | w['5'] = f1040['37'] 41 | w['6'] = LIMITS[inputs['status']] 42 | w['7'] = w['5'] - w['6'] 43 | if w['7'] <= 0: 44 | return w['1'] 45 | w['8'] = w['7'] * .06 46 | w['9'] = min(w['4'], w['8']) 47 | w['10'] = w['1'] - w['9'] 48 | return w['10'] 49 | 50 | def title(self): 51 | return 'CA 540 Schedule CA' 52 | -------------------------------------------------------------------------------- /2015/ca540sp.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class CA540sp(Form): 4 | EXEMPTION_LIMITS = [246451, 328601, 164299, 246451, 328601] 5 | EXEMPTIONS = [65721, 87627, 43812, 65721, 87627] 6 | 7 | def __init__(f, inputs, ca540, ca540sca, f1040, f1040sa): 8 | super(CA540sp, f).__init__(inputs) 9 | if ca540['18'] != ca540sca.STD_DED[inputs['status']]: 10 | f['2'] = min(f1040sa['4'], .025 * f1040['37']) 11 | f['3'] = f1040sa.rowsum(['6', '7']) 12 | f['5'] = f1040sa.get('27') 13 | else: 14 | f['1'] = ca540['18'] 15 | f['14'] = f.rowsum(['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', 16 | '11', '12', '13']) 17 | f['15'] = ca540['19'] 18 | if not f['15']: 19 | f['15'] = ca540['17'] - ca540['18'] 20 | f['16'] = ca540sca.rowsum(['B21d', 'B21e']) 21 | f['17'] = -max(0, f1040['12']) 22 | if ca540sca.mustFile(): 23 | f['18'] = ca540sca['42'] - ca540sca['43'] or None 24 | f['19'] = f.rowsum(['14', '15', '16', '17', '18']) 25 | f['21'] = f['19'] - f['20'] 26 | assert(inputs['status'] != FilingStatus.SEPARATE or f['21'] <= 327976) 27 | 28 | f['22'] = f.exemption(inputs) 29 | f['23'] = max(0, f['21'] - f['22']) 30 | f['24'] = f['23'] * .07 31 | f['25'] = ca540['31'] 32 | f['26'] = max(0, f['24'] - f['25']) or None 33 | if f['26']: 34 | f.must_file = True 35 | 36 | def exemption(f, inputs): 37 | w = {} 38 | w['1'] = f.EXEMPTIONS[inputs['status']] 39 | w['2'] = f['21'] 40 | w['3'] = f.EXEMPTION_LIMITS[inputs['status']] 41 | w['4'] = max(0, w['2'] - w['3']) 42 | w['5'] = w['4'] * .25 43 | w['6'] = max(0, w['1'] - w['5']) 44 | return w['6'] 45 | 46 | def title(self): 47 | return 'CA 540 Schedule P' 48 | -------------------------------------------------------------------------------- /2015/example_joint_return.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Example of a married couple filing jointly. Each spouse has an income 4 | # of 100k. Also, the first spouse has some investment income and 5 | # business (Schedule C) income. 6 | # 7 | # A balance is owed on the return. 8 | # 9 | # On the state return, a refund is due. 10 | # 11 | 12 | from f1040 import F1040 13 | from ca540 import CA540 14 | from form import FilingStatus 15 | 16 | # Note that some of the quantities below we need to list separately for each 17 | # spouse in an array. Everything else we just need a total. 18 | 19 | inputs = { 20 | 'status': FilingStatus.JOINT, 21 | 'exemptions': 2, 22 | 'wages': [100000.00, 100000.00], # W2 box 1 23 | 'withholding': 37000.00, # W2 box 2 24 | 'wages_ss': [100000.00, 100000.00], # W2 box 3 25 | 'ss_withheld': [ 6200.00, 6200.00], # W2 box 4 26 | 'wages_medicare': [100000.00, 100000.00], # W2 box 5 27 | 'medicare_withheld': [ 1450.00, 1450.00], # W2 box 6 28 | 'state_withholding': 18000.00, # W2 box 17 29 | 30 | # These are other state tax payments made in the tax year not included in 31 | # 'state_withholding' that are deductible on schedule A: 32 | 'extra_state_tax_payments': 3000.00, 33 | 34 | 'taxable_interest': 1500.00, 35 | 'tax_exempt_interest': 700.00, 36 | 'dividends': 3000.00, 37 | 'qualified_dividends': 2000.00, 38 | 'capital_gain_dist': 500.00, 39 | 'capital_gain_long': 5000.00, 40 | 'business_income': [5000.00, 0.00], 41 | 42 | # Extra items for schedule A 43 | 'F1040sa' : { 44 | '16' : 500, # charitable contributions 45 | }, 46 | } 47 | 48 | f = F1040(inputs) 49 | f.printAllForms() 50 | print('') 51 | ca = CA540(inputs, f) 52 | ca.printAllForms() 53 | -------------------------------------------------------------------------------- /2015/example_joint_return_amt.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Example of a married couple filing jointly. Each spouse has an income 4 | # of 100k. Also, the first spouse has some investment income and 5 | # business (Schedule C) income. 6 | # 7 | # This example increases the amount of state tax paid on Schedule A, causing 8 | # this couple to be paying AMT instead of regular tax. Form 6251 is added. 9 | # 10 | # A balance is owed on the return. 11 | # 12 | # On the state return, a refund is due. 13 | # 14 | 15 | from f1040 import F1040 16 | from ca540 import CA540 17 | from form import FilingStatus 18 | 19 | # Note that some of the quantities below we need to list separately for each 20 | # spouse in an array. Everything else we just need a total. 21 | 22 | inputs = { 23 | 'status': FilingStatus.JOINT, 24 | 'exemptions': 2, 25 | 'wages': [100000.00, 100000.00], # W2 box 1 26 | 'withholding': 37000.00, # W2 box 2 27 | 'wages_ss': [100000.00, 100000.00], # W2 box 3 28 | 'ss_withheld': [ 6200.00, 6200.00], # W2 box 4 29 | 'wages_medicare': [100000.00, 100000.00], # W2 box 5 30 | 'medicare_withheld': [ 1450.00, 1450.00], # W2 box 6 31 | 'state_withholding': 18000.00, # W2 box 17 32 | 33 | # These are other state tax payments made in the tax year not included in 34 | # 'state_withholding' that are deductible on schedule A: 35 | 'extra_state_tax_payments': 8000.00, 36 | 37 | 'taxable_interest': 1500.00, 38 | 'tax_exempt_interest': 700.00, 39 | 'dividends': 3000.00, 40 | 'qualified_dividends': 2000.00, 41 | 'capital_gain_dist': 500.00, 42 | 'capital_gain_long': 5000.00, 43 | 'business_income': [5000.00, 0.00], 44 | 45 | # Extra items for schedule A 46 | 'F1040sa' : { 47 | '16' : 500, # charitable contributions 48 | }, 49 | } 50 | 51 | f = F1040(inputs) 52 | f.printAllForms() 53 | print('') 54 | ca = CA540(inputs, f) 55 | ca.printAllForms() 56 | -------------------------------------------------------------------------------- /2015/example_single.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Example of a single person with wage income of 100k. Also some investment 4 | # income and business (Schedule C) income. 5 | # 6 | 7 | from f1040 import F1040 8 | from ca540 import CA540 9 | from form import FilingStatus 10 | 11 | inputs = { 12 | 'status': FilingStatus.SINGLE, 13 | 'exemptions': 1, 14 | 'wages': 100000.00, # W2 box 1 15 | 'withholding': 20000.00, # W2 box 2 16 | 'wages_ss': 100000.00, # W2 box 3 17 | 'ss_withheld': 6200.00, # W2 box 4 18 | 'wages_medicare': 100000.00, # W2 box 5 19 | 'medicare_withheld': 1450.00, # W2 box 6 20 | 'state_withholding': 10000.00, # W2 box 17 21 | 22 | # These are other state tax payments made in the tax year not included in 23 | # 'state_withholding' that are deductible on schedule A: 24 | 'extra_state_tax_payments': 1000.00, 25 | 26 | 'taxable_interest': 1500.00, 27 | 'tax_exempt_interest': 700.00, 28 | 'dividends': 3000.00, 29 | 'qualified_dividends': 2000.00, 30 | 'capital_gain_dist': 500.00, 31 | 'capital_gain_long': 5000.00, 32 | 'business_income': 5000.00, 33 | 34 | # Extra items for schedule A 35 | 'F1040sa' : { 36 | '16' : 500, # charitable contributions 37 | }, 38 | } 39 | 40 | f = F1040(inputs) 41 | f.printAllForms() 42 | print('') 43 | ca = CA540(inputs, f) 44 | ca.printAllForms() 45 | -------------------------------------------------------------------------------- /2015/f1040sa.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F1040sa(Form): 4 | LIMITS = [258250, 309900, 154950, 284050, 309900] 5 | 6 | def __init__(f, inputs, f1040): 7 | super(F1040sa, f).__init__(inputs) 8 | if f['1']: 9 | f['2'] = f1040['38'] 10 | # TODO: 7.5% if born before January 2, 1950 11 | f['3'] = f['2'] * .10 12 | f['4'] = max(0, f['1'] - f['3']) 13 | f['5'] = inputs['state_withholding'] + \ 14 | inputs.get('extra_state_tax_payments', 0) 15 | f['9'] = f.rowsum(['5', '6', '7', '8']) 16 | f['15'] = f.rowsum(['10', '11', '12', '13', '14']) 17 | f['19'] = f.rowsum(['16', '17', '18']) 18 | f['24'] = f.rowsum(['21', '22', '23']) 19 | if '24' in f: 20 | f['25'] = f1040['38'] 21 | f['26'] = f['25'] * .02 22 | f['27'] = max(0, f['24'] - f['26']) 23 | f['29'] = f.worksheet(inputs, f1040)['10'] 24 | f.must_file = True 25 | 26 | def worksheet(f, inputs, f1040): 27 | w = {} 28 | w['1'] = f.rowsum(['4', '9', '15', '19', '20', '27', '28']) or 0 29 | # TODO: gambling, casualty, theft losses from line 28 30 | w['2'] = f.rowsum(['4', '14', '20']) or 0 31 | if w['2'] >= w['1']: 32 | w['10'] = w['1'] 33 | return w 34 | w['3'] = w['1'] - w['2'] 35 | w['4'] = w['3'] * .8 36 | w['5'] = f1040['38'] 37 | w['6'] = f.LIMITS[inputs['status']] 38 | if w['6'] >= w['5']: 39 | w['10'] = w['1'] 40 | return w 41 | w['7'] = w['5'] - w['6'] 42 | w['8'] = w['7'] * .03 43 | w['9'] = min(w['4'], w['8']) 44 | w['10'] = w['1'] - w['9'] 45 | return w 46 | 47 | def title(self): 48 | return 'Schedule A' 49 | -------------------------------------------------------------------------------- /2015/f1040sd.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F1040sd(Form): 4 | def __init__(f, inputs): 5 | super(F1040sd, f).__init__(inputs) 6 | if 'capital_gain_long' not in inputs and 'capital_gain_short' not in inputs: 7 | return 8 | f.must_file = True 9 | f['1'] = inputs.get('capital_gain_short') 10 | f['7'] = f.rowsum(['1', '2', '3', '4', '5', '6']) 11 | f['8'] = inputs.get('capital_gain_long') 12 | f['13'] = inputs.get('capital_gain_dist') 13 | f['15'] = f.rowsum(['8', '9', '10', '11', '12', '13', '14']) 14 | f['16'] = f.rowsum(['7', '15']) 15 | if f['16'] < 0: 16 | cutoff = -3000 17 | if inputs['status'] == FilingStatus.SEPARATE: 18 | cutoff = -1500 19 | f['21'] = max(f['16'], cutoff) 20 | 21 | # If lines 15 and 16 are both gains and line 18 or 19 has a value: 22 | # Use the Schedule D tax worksheet 23 | # Else if lines 15 and 16 are both gains or you have qualified divs: 24 | # Use the Qualified Dividends and Capital Gain Tax Worksheet 25 | # Else 26 | # Use tax tables 27 | 28 | def title(self): 29 | return 'Schedule D' 30 | -------------------------------------------------------------------------------- /2015/f8606.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8606(Form): 4 | """Form 8606, Nondeductible IRAs""" 5 | def __init__(f, inputs, spouse): 6 | super(F8606, f).__init__(inputs, init_idx=spouse) 7 | f.spouse = spouse 8 | f['3'] = f['1'] + f['2'] 9 | f['5'] = f['3'] - f['4'] 10 | f['9'] = f.rowsum(['6', '7', '8']) 11 | f['10'] = min(1000, f['5'] * 1000.0 / f['9']) 12 | f['11'] = f['8'] * f['10'] / 1000.0 13 | f['12'] = f['7'] * f['10'] / 1000.0 14 | f['13'] = f['11'] + f['12'] 15 | f['14'] = f['3'] - f['13'] 16 | f['15'] = f['7'] - f['12'] 17 | f['17'] = f['11'] 18 | f['18'] = f['16'] - f['17'] 19 | # TODO: Roth IRA distributions 20 | f.must_file = True 21 | return 22 | 23 | def title(self): 24 | if self.spouse is not None: 25 | return 'Form 8606 [%d]' % self.spouse 26 | else: 27 | return 'Form 8606' 28 | -------------------------------------------------------------------------------- /2015/f8801.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8801(Form): 4 | """Form 8801, Credit for Prior Year Minimum Tax""" 5 | def __init__(f, inputs, f1040, f6251): 6 | super(F8801, f).__init__(inputs) 7 | f['21'] = inputs.get('prior_amt_credit') 8 | if not f['21']: 9 | return 10 | 11 | f.must_file = True 12 | f['22'] = max(0, f1040['44'] + f1040['46'] - \ 13 | (f1040.rowsum(['48', '49', '50', '51', '52', '53', '54']) or 0)) 14 | f['23'] = f6251.get('33') 15 | f['24'] = max(0, f['22'] - f['23']) 16 | f.comment['25'] = 'Minimum tax credit' 17 | f['25'] = min(f['21'], f['24']) 18 | if f['25']: 19 | f6251.must_file = True 20 | f.comment['26'] = 'Credit carryforward to 2016' 21 | f['26'] = f['21'] - f['25'] 22 | 23 | def title(self): 24 | return 'Form 8801 (for 2015 filing)' 25 | -------------------------------------------------------------------------------- /2015/f8959.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8959(Form): 4 | """Form 8959, Additional Medicare Tax""" 5 | THRESHOLDS = [200000, 250000, 125000, 200000, 200000] 6 | def __init__(f, inputs, f1040, sse): 7 | super(F8959, f).__init__(inputs) 8 | f['1'] = f.spouseSum(inputs, 'wages_medicare') 9 | # TODO: forms 4137, 8919 10 | f['4'] = f.rowsum(['1', '2', '3']) 11 | if f.get('4'): 12 | f['5'] = f.THRESHOLDS[inputs['status']] 13 | f['6'] = max(0, f['4'] - f['5']) 14 | f['7'] = f['6'] * .009 15 | 16 | if inputs['status'] == FilingStatus.JOINT: 17 | if sse[0].mustFile() or sse[1].mustFile(): 18 | f['8'] = max(0, (sse[0]['A4'] or sse[0]['B6'] or 0) + 19 | (sse[1]['A4'] or sse[1]['B6'] or 0)) 20 | else: 21 | if sse.mustFile(): 22 | f['8'] = max(0, sse['A4'] or sse['B6']) 23 | if f.get('8'): 24 | assert(f['8'] >= 400) 25 | f['9'] = f.THRESHOLDS[inputs['status']] 26 | f['10'] = f.get('4') 27 | f['11'] = max(0, f['9'] - f['10']) 28 | f['12'] = max(0, f['8'] - f['11']) 29 | f['13'] = f['12'] * .009 or None 30 | 31 | if f.get('14'): 32 | f['15'] = f.THRESHOLDS[inputs['status']] 33 | f['16'] = max(0, f['14'] - f['15']) 34 | f['17'] = f['16'] * .009 or None 35 | 36 | f['18'] = f.rowsum(['7', '13', '17']) 37 | 38 | f['19'] = f.spouseSum(inputs, 'medicare_withheld') 39 | f['20'] = f.get('1') 40 | f['21'] = f['20'] * .0145 or None 41 | if f['19'] or f['21']: 42 | f['22'] = max(0, f['19'] - f['21']) 43 | f['24'] = f.rowsum(['22', '23']) 44 | 45 | if f.get('18') or f.get('24'): 46 | f.must_file = True 47 | return 48 | 49 | def title(self): 50 | return 'Form 8959' 51 | -------------------------------------------------------------------------------- /2015/f8960.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8960(Form): 4 | """Form 8960, Net Investment Income Tax""" 5 | THRESHOLDS = [200000, 250000, 125000, 200000, 250000] 6 | def __init__(f, inputs, f1040, sched_a): 7 | super(F8960, f).__init__(inputs) 8 | f['1'] = f1040.get('8a') 9 | f['2'] = f1040.get('9a') 10 | # TODO: annuities 11 | f['4a'] = f1040.get('17') 12 | f['4c'] = f.rowsum(['4a', '4b']) 13 | f['5a'] = f1040.rowsum(['13', '14']) 14 | f['5d'] = f.rowsum(['5a', '5b', '5c']) 15 | f['8'] = f.rowsum(['1', '2', '3', '4c', '5d', '6', '7']) 16 | if sched_a: 17 | # This is one example of a "reasonable method allocation" but 18 | # not the only way. 19 | # Note: this only works for state income tax, not sales tax 20 | f['9b'] = f['8'] * sched_a['5'] / f1040['38'] 21 | f['9d'] = f.rowsum(['9a', '9b', '9c']) 22 | f['11'] = f.rowsum(['9d', '10']) 23 | f['12'] = max(0, f['8'] - f['11']) 24 | f['13'] = f1040['38'] 25 | f['14'] = f.THRESHOLDS[inputs['status']] 26 | f['15'] = max(0, f['13'] - f['14']) 27 | f['16'] = min(f['12'], f['15']) 28 | if f['16']: 29 | f['17'] = f['16'] * .038 30 | f.must_file = True 31 | return 32 | 33 | def title(self): 34 | return 'Form 8960' 35 | -------------------------------------------------------------------------------- /2016/ca540sp.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class CA540sp(Form): 4 | EXEMPTION_LIMITS = [251626, 335502, 167749, 251626, 335502] 5 | EXEMPTIONS = [67101, 89467, 44732, 67101, 89467] 6 | 7 | def __init__(f, inputs, ca540, ca540sca, f1040, f1040sa): 8 | super(CA540sp, f).__init__(inputs) 9 | if ca540['18'] != ca540sca.STD_DED[inputs['status']]: 10 | f['2'] = min(f1040sa['4'], .025 * f1040['37']) 11 | f['3'] = f1040sa.rowsum(['6', '7']) 12 | f['5'] = f1040sa.get('27') 13 | else: 14 | f['1'] = ca540['18'] 15 | f['14'] = f.rowsum(['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', 16 | '11', '12', '13']) 17 | f['15'] = ca540['19'] 18 | if not f['15']: 19 | f['15'] = ca540['17'] - ca540['18'] 20 | f['16'] = ca540sca.rowsum(['B21b', 'B21d', 'B21e']) 21 | f['17'] = -max(0, f1040['12']) 22 | if ca540['18'] != ca540sca.STD_DED[inputs['status']]: 23 | f['18'] = -(ca540sca['42'] - ca540sca['43']) or None 24 | f['19'] = f.rowsum(['14', '15', '16', '17', '18']) 25 | f['21'] = f['19'] - f['20'] 26 | assert(inputs['status'] != FilingStatus.SEPARATE or f['21'] <= 346677) 27 | 28 | f['22'] = f.exemption(inputs) 29 | f['23'] = max(0, f['21'] - f['22']) 30 | f['24'] = f['23'] * .07 31 | f['25'] = ca540['31'] 32 | f['26'] = max(0, f['24'] - f['25']) or None 33 | if f['26']: 34 | f.must_file = True 35 | 36 | def exemption(f, inputs): 37 | w = {} 38 | w['1'] = f.EXEMPTIONS[inputs['status']] 39 | w['2'] = f['21'] 40 | w['3'] = f.EXEMPTION_LIMITS[inputs['status']] 41 | w['4'] = max(0, w['2'] - w['3']) 42 | w['5'] = w['4'] * .25 43 | w['6'] = max(0, w['1'] - w['5']) 44 | return w['6'] 45 | 46 | def title(self): 47 | return 'CA 540 Schedule P' 48 | -------------------------------------------------------------------------------- /2016/example_joint_return.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Example of a married couple filing jointly. Each spouse has an income 4 | # of 100k. Also, the first spouse has some investment income and 5 | # business (Schedule C) income. 6 | # 7 | # A balance is owed on the return. 8 | # 9 | # On the state return, a refund is due. 10 | # 11 | 12 | from f1040 import F1040 13 | from ca540 import CA540 14 | from form import FilingStatus 15 | 16 | # Note that some of the quantities below we need to list separately for each 17 | # spouse in an array. Everything else we just need a total. 18 | 19 | inputs = { 20 | 'status': FilingStatus.JOINT, 21 | 'exemptions': 2, 22 | 'wages': [100000.00, 100000.00], # W2 box 1 23 | 'withholding': 37000.00, # W2 box 2 24 | 'wages_ss': [100000.00, 100000.00], # W2 box 3 25 | 'ss_withheld': [ 6200.00, 6200.00], # W2 box 4 26 | 'wages_medicare': [100000.00, 100000.00], # W2 box 5 27 | 'medicare_withheld': [ 1450.00, 1450.00], # W2 box 6 28 | 'state_withholding': 18000.00, # W2 box 17 29 | 30 | # These are other state tax payments made in the tax year not included in 31 | # 'state_withholding' that are deductible on schedule A: 32 | 'extra_state_tax_payments': 3000.00, 33 | 34 | 'taxable_interest': 1500.00, 35 | 'tax_exempt_interest': 700.00, 36 | 'dividends': 3000.00, 37 | 'qualified_dividends': 2000.00, 38 | 'capital_gain_dist': 500.00, 39 | 'capital_gain_long': 5000.00, 40 | 'business_income': [5000.00, 0.00], 41 | 42 | # Extra items for schedule A 43 | 'F1040sa' : { 44 | '16' : 500, # charitable contributions 45 | }, 46 | } 47 | 48 | f = F1040(inputs) 49 | f.printAllForms() 50 | print('') 51 | ca = CA540(inputs, f) 52 | ca.printAllForms() 53 | -------------------------------------------------------------------------------- /2016/example_joint_return_amt.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Example of a married couple filing jointly. Each spouse has an income 4 | # of 100k. Also, the first spouse has some investment income and 5 | # business (Schedule C) income. 6 | # 7 | # This example increases the amount of state tax paid on Schedule A, causing 8 | # this couple to be paying AMT instead of regular tax. Form 6251 is added. 9 | # 10 | # A balance is owed on the return. 11 | # 12 | # On the state return, a refund is due. 13 | # 14 | 15 | from f1040 import F1040 16 | from ca540 import CA540 17 | from form import FilingStatus 18 | 19 | # Note that some of the quantities below we need to list separately for each 20 | # spouse in an array. Everything else we just need a total. 21 | 22 | inputs = { 23 | 'status': FilingStatus.JOINT, 24 | 'exemptions': 2, 25 | 'wages': [100000.00, 100000.00], # W2 box 1 26 | 'withholding': 37000.00, # W2 box 2 27 | 'wages_ss': [100000.00, 100000.00], # W2 box 3 28 | 'ss_withheld': [ 6200.00, 6200.00], # W2 box 4 29 | 'wages_medicare': [100000.00, 100000.00], # W2 box 5 30 | 'medicare_withheld': [ 1450.00, 1450.00], # W2 box 6 31 | 'state_withholding': 18000.00, # W2 box 17 32 | 33 | # These are other state tax payments made in the tax year not included in 34 | # 'state_withholding' that are deductible on schedule A: 35 | 'extra_state_tax_payments': 8000.00, 36 | 37 | 'taxable_interest': 1500.00, 38 | 'tax_exempt_interest': 700.00, 39 | 'dividends': 3000.00, 40 | 'qualified_dividends': 2000.00, 41 | 'capital_gain_dist': 500.00, 42 | 'capital_gain_long': 5000.00, 43 | 'business_income': [5000.00, 0.00], 44 | 45 | # Extra items for schedule A 46 | 'F1040sa' : { 47 | '16' : 500, # charitable contributions 48 | }, 49 | } 50 | 51 | f = F1040(inputs) 52 | f.printAllForms() 53 | print('') 54 | ca = CA540(inputs, f) 55 | ca.printAllForms() 56 | -------------------------------------------------------------------------------- /2016/example_single.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Example of a single person with wage income of 100k. Also some investment 4 | # income and business (Schedule C) income. 5 | # 6 | 7 | from f1040 import F1040 8 | from ca540 import CA540 9 | from form import FilingStatus 10 | 11 | inputs = { 12 | 'status': FilingStatus.SINGLE, 13 | 'exemptions': 1, 14 | 'wages': 100000.00, # W2 box 1 15 | 'withholding': 20000.00, # W2 box 2 16 | 'wages_ss': 100000.00, # W2 box 3 17 | 'ss_withheld': 6200.00, # W2 box 4 18 | 'wages_medicare': 100000.00, # W2 box 5 19 | 'medicare_withheld': 1450.00, # W2 box 6 20 | 'state_withholding': 10000.00, # W2 box 17 21 | 22 | # These are other state tax payments made in the tax year not included in 23 | # 'state_withholding' that are deductible on schedule A: 24 | 'extra_state_tax_payments': 1000.00, 25 | 26 | 'taxable_interest': 1500.00, 27 | 'tax_exempt_interest': 700.00, 28 | 'dividends': 3000.00, 29 | 'qualified_dividends': 2000.00, 30 | 'capital_gain_dist': 500.00, 31 | 'capital_gain_long': 5000.00, 32 | 'business_income': 5000.00, 33 | 34 | # Extra items for schedule A 35 | 'F1040sa' : { 36 | '16' : 500, # charitable contributions 37 | }, 38 | } 39 | 40 | f = F1040(inputs) 41 | f.printAllForms() 42 | print('') 43 | ca = CA540(inputs, f) 44 | ca.printAllForms() 45 | -------------------------------------------------------------------------------- /2016/f1040sa.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F1040sa(Form): 4 | LIMITS = [259400, 311300, 155650, 285350, 311300] 5 | 6 | def __init__(f, inputs, f1040): 7 | super(F1040sa, f).__init__(inputs) 8 | if f['1']: 9 | f['2'] = f1040['38'] 10 | # TODO: 7.5% if born before January 2, 1950 11 | f['3'] = f['2'] * .10 12 | f['4'] = max(0, f['1'] - f['3']) 13 | f['5'] = inputs['state_withholding'] + \ 14 | inputs.get('extra_state_tax_payments', 0) 15 | f['9'] = f.rowsum(['5', '6', '7', '8']) 16 | f['15'] = f.rowsum(['10', '11', '12', '13', '14']) 17 | f['19'] = f.rowsum(['16', '17', '18']) 18 | f['24'] = f.rowsum(['21', '22', '23']) 19 | if '24' in f: 20 | f['25'] = f1040['38'] 21 | f['26'] = f['25'] * .02 22 | f['27'] = max(0, f['24'] - f['26']) 23 | f['29'] = f.worksheet(inputs, f1040)['10'] 24 | f.must_file = True 25 | 26 | def worksheet(f, inputs, f1040): 27 | w = {} 28 | w['1'] = f.rowsum(['4', '9', '15', '19', '20', '27', '28']) or 0 29 | # TODO: gambling, casualty, theft losses from line 28 30 | w['2'] = f.rowsum(['4', '14', '20']) or 0 31 | if w['2'] >= w['1']: 32 | w['10'] = w['1'] 33 | return w 34 | w['3'] = w['1'] - w['2'] 35 | w['4'] = w['3'] * .8 36 | w['5'] = f1040['38'] 37 | w['6'] = f.LIMITS[inputs['status']] 38 | if w['6'] >= w['5']: 39 | w['10'] = w['1'] 40 | return w 41 | w['7'] = w['5'] - w['6'] 42 | w['8'] = w['7'] * .03 43 | w['9'] = min(w['4'], w['8']) 44 | w['10'] = w['1'] - w['9'] 45 | return w 46 | 47 | def title(self): 48 | return 'Schedule A' 49 | -------------------------------------------------------------------------------- /2016/f1040sd.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F1040sd(Form): 4 | def __init__(f, inputs): 5 | super(F1040sd, f).__init__(inputs) 6 | if 'capital_gain_long' not in inputs and 'capital_gain_short' not in inputs: 7 | return 8 | f.must_file = True 9 | f['1'] = inputs.get('capital_gain_short') 10 | f['7'] = f.rowsum(['1', '2', '3', '4', '5', '6']) 11 | f['8'] = inputs.get('capital_gain_long') 12 | f['13'] = inputs.get('capital_gain_dist') 13 | f['15'] = f.rowsum(['8', '9', '10', '11', '12', '13', '14']) 14 | f['16'] = f.rowsum(['7', '15']) 15 | if f['16'] < 0: 16 | cutoff = -3000 17 | if inputs['status'] == FilingStatus.SEPARATE: 18 | cutoff = -1500 19 | f['21'] = max(f['16'], cutoff) 20 | 21 | # If lines 15 and 16 are both gains and line 18 or 19 has a value: 22 | # Use the Schedule D tax worksheet 23 | # Else if lines 15 and 16 are both gains or you have qualified divs: 24 | # Use the Qualified Dividends and Capital Gain Tax Worksheet 25 | # Else 26 | # Use tax tables 27 | 28 | def title(self): 29 | return 'Schedule D' 30 | -------------------------------------------------------------------------------- /2016/f8606.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8606(Form): 4 | """Form 8606, Nondeductible IRAs""" 5 | def __init__(f, inputs, spouse): 6 | super(F8606, f).__init__(inputs, init_idx=spouse) 7 | f.spouse = spouse 8 | f['3'] = f['1'] + f['2'] 9 | f['5'] = f['3'] - f['4'] 10 | f['9'] = f.rowsum(['6', '7', '8']) 11 | f['10'] = min(10000, f['5'] * 10000.0 / f['9']) 12 | f['11'] = f['8'] * f['10'] / 10000.0 13 | f['12'] = f['7'] * f['10'] / 10000.0 14 | f['13'] = f['11'] + f['12'] 15 | f['14'] = f['3'] - f['13'] 16 | f['15'] = f['7'] - f['12'] 17 | f['17'] = f['11'] 18 | f['18'] = f['16'] - f['17'] 19 | # TODO: Roth IRA distributions 20 | f.must_file = True 21 | return 22 | 23 | def title(self): 24 | if self.spouse is not None: 25 | return 'Form 8606 [%d]' % self.spouse 26 | else: 27 | return 'Form 8606' 28 | -------------------------------------------------------------------------------- /2016/f8801.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8801(Form): 4 | """Form 8801, Credit for Prior Year Minimum Tax""" 5 | def __init__(f, inputs, f1040, f6251): 6 | super(F8801, f).__init__(inputs) 7 | f['21'] = inputs.get('prior_amt_credit') 8 | if not f['21']: 9 | return 10 | 11 | f.must_file = True 12 | f['22'] = max(0, f1040['44'] + f1040['46'] - \ 13 | (f1040.rowsum(['48', '49', '50', '51', '52', '53', '54']) or 0)) 14 | f['23'] = f6251.get('33') 15 | f['24'] = max(0, f['22'] - f['23']) 16 | f.comment['25'] = 'Minimum tax credit' 17 | f['25'] = min(f['21'], f['24']) 18 | if f['25']: 19 | f6251.must_file = True 20 | f.comment['26'] = 'Credit carryforward to 2017' 21 | f['26'] = f['21'] - f['25'] 22 | 23 | def title(self): 24 | return 'Form 8801 (for 2016 filing)' 25 | -------------------------------------------------------------------------------- /2016/f8959.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8959(Form): 4 | """Form 8959, Additional Medicare Tax""" 5 | THRESHOLDS = [200000, 250000, 125000, 200000, 200000] 6 | def __init__(f, inputs, f1040, sse): 7 | super(F8959, f).__init__(inputs) 8 | f['1'] = f.spouseSum(inputs, 'wages_medicare') 9 | # TODO: forms 4137, 8919 10 | f['4'] = f.rowsum(['1', '2', '3']) 11 | if f.get('4'): 12 | f['5'] = f.THRESHOLDS[inputs['status']] 13 | f['6'] = max(0, f['4'] - f['5']) 14 | f['7'] = f['6'] * .009 15 | 16 | if inputs['status'] == FilingStatus.JOINT: 17 | if sse[0].mustFile() or sse[1].mustFile(): 18 | f['8'] = max(0, (sse[0]['A4'] or sse[0]['B6'] or 0) + 19 | (sse[1]['A4'] or sse[1]['B6'] or 0)) 20 | else: 21 | if sse.mustFile(): 22 | f['8'] = max(0, sse['A4'] or sse['B6']) 23 | if f.get('8'): 24 | assert(f['8'] >= 400) 25 | f['9'] = f.THRESHOLDS[inputs['status']] 26 | f['10'] = f.get('4') 27 | f['11'] = max(0, f['9'] - f['10']) 28 | f['12'] = max(0, f['8'] - f['11']) 29 | f['13'] = f['12'] * .009 or None 30 | 31 | if f.get('14'): 32 | f['15'] = f.THRESHOLDS[inputs['status']] 33 | f['16'] = max(0, f['14'] - f['15']) 34 | f['17'] = f['16'] * .009 or None 35 | 36 | f['18'] = f.rowsum(['7', '13', '17']) 37 | 38 | f['19'] = f.spouseSum(inputs, 'medicare_withheld') 39 | f['20'] = f.get('1') 40 | f['21'] = f['20'] * .0145 or None 41 | if f['19'] or f['21']: 42 | f['22'] = max(0, f['19'] - f['21']) 43 | f['24'] = f.rowsum(['22', '23']) 44 | 45 | if f.get('18') or f.get('24'): 46 | f.must_file = True 47 | return 48 | 49 | def title(self): 50 | return 'Form 8959' 51 | -------------------------------------------------------------------------------- /2016/f8960.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8960(Form): 4 | """Form 8960, Net Investment Income Tax""" 5 | THRESHOLDS = [200000, 250000, 125000, 200000, 250000] 6 | def __init__(f, inputs, f1040, sched_a): 7 | super(F8960, f).__init__(inputs) 8 | f['1'] = f1040.get('8a') 9 | f['2'] = f1040.get('9a') 10 | # TODO: annuities 11 | f['4a'] = f1040.get('17') 12 | f['4c'] = f.rowsum(['4a', '4b']) 13 | f['5a'] = f1040.rowsum(['13', '14']) 14 | f['5d'] = f.rowsum(['5a', '5b', '5c']) 15 | f['8'] = f.rowsum(['1', '2', '3', '4c', '5d', '6', '7']) 16 | if sched_a: 17 | # This is one example of a "reasonable method allocation" but 18 | # not the only way. 19 | # Note: this only works for state income tax, not sales tax 20 | f['9b'] = f['8'] * sched_a['5'] / f1040['38'] 21 | f['9d'] = f.rowsum(['9a', '9b', '9c']) 22 | f['11'] = f.rowsum(['9d', '10']) 23 | f['12'] = max(0, f['8'] - f['11']) 24 | f['13'] = f1040['38'] 25 | f['14'] = f.THRESHOLDS[inputs['status']] 26 | f['15'] = max(0, f['13'] - f['14']) 27 | f['16'] = min(f['12'], f['15']) 28 | if f['16']: 29 | f['17'] = f['16'] * .038 30 | f.must_file = True 31 | return 32 | 33 | def title(self): 34 | return 'Form 8960' 35 | -------------------------------------------------------------------------------- /2017/ca540sca.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class CA540sca(Form): 4 | STD_DED = [4236, 8472, 4236, 8472, 8472] 5 | 6 | def __init__(f, inputs, f1040, f1040sa): 7 | super(CA540sca, f).__init__(inputs) 8 | f['B19'] = f1040.get('19') 9 | f['B22'] = f.rowsum(['B7', 'B8a', 'B9a', 'B10', 'B11', 'B12', 'B13', 10 | 'B14', 'B15b', 'B16b', 'B17', 'B18', 'B19', 11 | 'B20b', 'B21']) 12 | f['C22'] = f.rowsum(['C7', 'C8a', 'C9a', 'C10', 'C11', 'C12', 'C13', 13 | 'C14', 'C15b', 'C16b', 'C17', 'C18', 'C19', 14 | 'C20b', 'C21']) 15 | f['B36'] = f.rowsum(['B23', 'B24', 'B25', 'B35']) 16 | f['C36'] = f.rowsum(['C24', 'C31a', 'C33']) 17 | f['B37'] = f['B22'] - f['B36'] 18 | f['C37'] = f['C22'] - f['C36'] 19 | 20 | f['38'] = f1040sa.rowsum(['4', '9', '15', '19', '20', '27', '28']) 21 | f['39'] = f1040sa.rowsum(['5', '8']) 22 | f['40'] = f['38'] - f['39'] 23 | f['42'] = f.rowsum(['40', '41']) 24 | f['43'] = f.itemized_deductions_worksheet(inputs, f1040, f1040sa) 25 | f['44'] = max(f['43'], f.STD_DED[inputs['status']]) 26 | if f['B37'] or f['C37'] or f['44'] != f.STD_DED[inputs['status']]: 27 | f.must_file = True 28 | 29 | def itemized_deductions_worksheet(f, inputs, f1040, f1040sa): 30 | LIMITS = [187203, 374411, 187203, 280808, 374411] 31 | w = {} 32 | w['1'] = f['42'] 33 | if f1040['37'] <= LIMITS[inputs['status']]: 34 | return w['1'] 35 | w['2'] = f1040sa.rowsum(['4', '14', '20']) or 0 36 | w['3'] = w['1'] - w['2'] 37 | if w['3'] == 0: 38 | return w['1'] 39 | w['4'] = w['3'] * .8 40 | w['5'] = f1040['37'] 41 | w['6'] = LIMITS[inputs['status']] 42 | w['7'] = w['5'] - w['6'] 43 | if w['7'] <= 0: 44 | return w['1'] 45 | w['8'] = w['7'] * .06 46 | w['9'] = min(w['4'], w['8']) 47 | w['10'] = w['1'] - w['9'] 48 | return w['10'] 49 | 50 | def title(self): 51 | return 'CA 540 Schedule CA' 52 | -------------------------------------------------------------------------------- /2017/ca540sp.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class CA540sp(Form): 4 | EXEMPTION_LIMITS = [258168, 344225, 172110, 258168, 344225] 5 | EXEMPTIONS = [68846, 91793, 45895, 68846, 91793] 6 | 7 | def __init__(f, inputs, ca540, ca540sca, f1040, f1040sa): 8 | super(CA540sp, f).__init__(inputs) 9 | if ca540['18'] != ca540sca.STD_DED[inputs['status']]: 10 | f['2'] = min(f1040sa['4'], .025 * f1040['37']) 11 | f['3'] = f1040sa.rowsum(['6', '7']) 12 | f['5'] = f1040sa.get('27') 13 | else: 14 | f['1'] = ca540['18'] 15 | f['14'] = f.rowsum(['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', 16 | '11', '12', '13']) 17 | f['15'] = ca540['19'] 18 | if not f['15']: 19 | f['15'] = ca540['17'] - ca540['18'] 20 | f['16'] = ca540sca.rowsum(['B21b', 'B21d', 'B21e']) 21 | f['17'] = -max(0, f1040['12']) 22 | if ca540['18'] != ca540sca.STD_DED[inputs['status']]: 23 | f['18'] = -(ca540sca['42'] - ca540sca['43']) or None 24 | f['19'] = f.rowsum(['14', '15', '16', '17', '18']) 25 | f['21'] = f['19'] - f['20'] 26 | assert(inputs['status'] != FilingStatus.SEPARATE or f['21'] <= 355690) 27 | 28 | f['22'] = f.exemption(inputs) 29 | f['23'] = max(0, f['21'] - f['22']) 30 | f['24'] = f['23'] * .07 31 | f['25'] = ca540['31'] 32 | f['26'] = max(0, f['24'] - f['25']) or None 33 | if f['26']: 34 | f.must_file = True 35 | 36 | def exemption(f, inputs): 37 | w = {} 38 | w['1'] = f.EXEMPTIONS[inputs['status']] 39 | w['2'] = f['21'] 40 | w['3'] = f.EXEMPTION_LIMITS[inputs['status']] 41 | w['4'] = max(0, w['2'] - w['3']) 42 | w['5'] = w['4'] * .25 43 | w['6'] = max(0, w['1'] - w['5']) 44 | return w['6'] 45 | 46 | def title(self): 47 | return 'CA 540 Schedule P' 48 | -------------------------------------------------------------------------------- /2017/example_joint_return.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Example of a married couple filing jointly. Each spouse has an income 4 | # of 100k. Also, the first spouse has some investment income and 5 | # business (Schedule C) income. 6 | # 7 | # A balance is owed on the return. 8 | # 9 | # On the state return, a refund is due. 10 | # 11 | 12 | from f1040 import F1040 13 | from ca540 import CA540 14 | from form import FilingStatus 15 | 16 | # Note that some of the quantities below we need to list separately for each 17 | # spouse in an array. Everything else we just need a total. 18 | 19 | inputs = { 20 | 'status': FilingStatus.JOINT, 21 | 'exemptions': 2, 22 | 'wages': [100000.00, 100000.00], # W2 box 1 23 | 'withholding': 37000.00, # W2 box 2 24 | 'wages_ss': [100000.00, 100000.00], # W2 box 3 25 | 'ss_withheld': [ 6200.00, 6200.00], # W2 box 4 26 | 'wages_medicare': [100000.00, 100000.00], # W2 box 5 27 | 'medicare_withheld': [ 1450.00, 1450.00], # W2 box 6 28 | 'state_withholding': 18000.00, # W2 box 17 29 | 30 | # These are other state tax payments made in the tax year not included in 31 | # 'state_withholding' that are deductible on schedule A: 32 | 'extra_state_tax_payments': 3000.00, 33 | 34 | 'taxable_interest': 1500.00, 35 | 'tax_exempt_interest': 700.00, 36 | 'dividends': 3000.00, 37 | 'qualified_dividends': 2000.00, 38 | 'capital_gain_dist': 500.00, 39 | 'capital_gain_long': 5000.00, 40 | 'business_income': [5000.00, 0.00], 41 | 42 | # Extra items for schedule A 43 | 'F1040sa' : { 44 | '16' : 500, # charitable contributions 45 | }, 46 | } 47 | 48 | f = F1040(inputs) 49 | f.printAllForms() 50 | print('') 51 | ca = CA540(inputs, f) 52 | ca.printAllForms() 53 | -------------------------------------------------------------------------------- /2017/example_joint_return_amt.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Example of a married couple filing jointly. Each spouse has an income 4 | # of 100k. Also, the first spouse has some investment income and 5 | # business (Schedule C) income. 6 | # 7 | # This example increases the amount of state tax paid on Schedule A, causing 8 | # this couple to be paying AMT instead of regular tax. Form 6251 is added. 9 | # 10 | # A balance is owed on the return. 11 | # 12 | # On the state return, a refund is due. 13 | # 14 | 15 | from f1040 import F1040 16 | from ca540 import CA540 17 | from form import FilingStatus 18 | 19 | # Note that some of the quantities below we need to list separately for each 20 | # spouse in an array. Everything else we just need a total. 21 | 22 | inputs = { 23 | 'status': FilingStatus.JOINT, 24 | 'exemptions': 2, 25 | 'wages': [120000.00, 120000.00], # W2 box 1 26 | 'withholding': 42000.00, # W2 box 2 27 | 'wages_ss': [120000.00, 120000.00], # W2 box 3 28 | 'ss_withheld': [ 6200.00, 6200.00], # W2 box 4 29 | 'wages_medicare': [120000.00, 120000.00], # W2 box 5 30 | 'medicare_withheld': [ 1450.00, 1450.00], # W2 box 6 31 | 'state_withholding': 20000.00, # W2 box 17 32 | 33 | # These are other state tax payments made in the tax year not included in 34 | # 'state_withholding' that are deductible on schedule A: 35 | 'extra_state_tax_payments': 8000.00, 36 | 37 | 'taxable_interest': 1500.00, 38 | 'tax_exempt_interest': 700.00, 39 | 'dividends': 3000.00, 40 | 'qualified_dividends': 2000.00, 41 | 'capital_gain_dist': 500.00, 42 | 'capital_gain_long': 5000.00, 43 | 'business_income': [5000.00, 0.00], 44 | 45 | # Extra items for schedule A 46 | 'F1040sa' : { 47 | '16' : 500, # charitable contributions 48 | }, 49 | } 50 | 51 | f = F1040(inputs) 52 | f.printAllForms() 53 | print('') 54 | ca = CA540(inputs, f) 55 | ca.printAllForms() 56 | -------------------------------------------------------------------------------- /2017/example_single.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Example of a single person with wage income of 100k. Also some investment 4 | # income and business (Schedule C) income. 5 | # 6 | 7 | from f1040 import F1040 8 | from ca540 import CA540 9 | from form import FilingStatus 10 | 11 | inputs = { 12 | 'status': FilingStatus.SINGLE, 13 | 'exemptions': 1, 14 | 'wages': 100000.00, # W2 box 1 15 | 'withholding': 20000.00, # W2 box 2 16 | 'wages_ss': 100000.00, # W2 box 3 17 | 'ss_withheld': 6200.00, # W2 box 4 18 | 'wages_medicare': 100000.00, # W2 box 5 19 | 'medicare_withheld': 1450.00, # W2 box 6 20 | 'state_withholding': 10000.00, # W2 box 17 21 | 22 | # These are other state tax payments made in the tax year not included in 23 | # 'state_withholding' that are deductible on schedule A: 24 | 'extra_state_tax_payments': 1000.00, 25 | 26 | 'taxable_interest': 1500.00, 27 | 'tax_exempt_interest': 700.00, 28 | 'dividends': 3000.00, 29 | 'qualified_dividends': 2000.00, 30 | 'capital_gain_dist': 500.00, 31 | 'capital_gain_long': 5000.00, 32 | 'business_income': 5000.00, 33 | 34 | # Extra items for schedule A 35 | 'F1040sa' : { 36 | '16' : 500, # charitable contributions 37 | }, 38 | } 39 | 40 | f = F1040(inputs) 41 | f.printAllForms() 42 | print('') 43 | ca = CA540(inputs, f) 44 | ca.printAllForms() 45 | -------------------------------------------------------------------------------- /2017/f1040sa.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F1040sa(Form): 4 | LIMITS = [261500, 313800, 156900, 287650, 313800] 5 | 6 | def __init__(f, inputs, f1040): 7 | super(F1040sa, f).__init__(inputs) 8 | if f['1']: 9 | f['2'] = f1040['38'] 10 | f['3'] = f['2'] * .075 11 | f['4'] = max(0, f['1'] - f['3']) 12 | f['5'] = inputs['state_withholding'] + \ 13 | inputs.get('extra_state_tax_payments', 0) 14 | f['9'] = f.rowsum(['5', '6', '7', '8']) 15 | f['15'] = f.rowsum(['10', '11', '12', '13', '14']) 16 | f['19'] = f.rowsum(['16', '17', '18']) 17 | f['24'] = f.rowsum(['21', '22', '23']) 18 | if '24' in f: 19 | f['25'] = f1040['38'] 20 | f['26'] = f['25'] * .02 21 | f['27'] = max(0, f['24'] - f['26']) 22 | f['29'] = f.worksheet(inputs, f1040)['10'] 23 | f.must_file = True 24 | 25 | def worksheet(f, inputs, f1040): 26 | w = {} 27 | w['1'] = f.rowsum(['4', '9', '15', '19', '20', '27', '28']) or 0 28 | # TODO: gambling, casualty, theft losses from line 28 29 | w['2'] = f.rowsum(['4', '14', '20']) or 0 30 | if w['2'] >= w['1']: 31 | w['10'] = w['1'] 32 | return w 33 | w['3'] = w['1'] - w['2'] 34 | w['4'] = w['3'] * .8 35 | w['5'] = f1040['38'] 36 | w['6'] = f.LIMITS[inputs['status']] 37 | if w['6'] >= w['5']: 38 | w['10'] = w['1'] 39 | return w 40 | w['7'] = w['5'] - w['6'] 41 | w['8'] = w['7'] * .03 42 | w['9'] = min(w['4'], w['8']) 43 | w['10'] = w['1'] - w['9'] 44 | return w 45 | 46 | def title(self): 47 | return 'Schedule A' 48 | -------------------------------------------------------------------------------- /2017/f1040sd.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F1040sd(Form): 4 | def __init__(f, inputs): 5 | super(F1040sd, f).__init__(inputs) 6 | if 'capital_gain_long' not in inputs and 'capital_gain_short' not in inputs: 7 | return 8 | f.must_file = True 9 | f['1'] = inputs.get('capital_gain_short') 10 | f['6'] = inputs.get('capital_gain_carryover_short') 11 | f['7'] = f.rowsum(['1', '2', '3', '4', '5', '6']) 12 | f['8'] = inputs.get('capital_gain_long') 13 | f['13'] = inputs.get('capital_gain_dist') 14 | f['14'] = inputs.get('capital_gain_carryover_long') 15 | f['15'] = f.rowsum(['8', '9', '10', '11', '12', '13', '14']) 16 | f['16'] = f.rowsum(['7', '15']) 17 | if f['16'] < 0: 18 | cutoff = -3000 19 | if inputs['status'] == FilingStatus.SEPARATE: 20 | cutoff = -1500 21 | f['21'] = max(f['16'], cutoff) 22 | 23 | # If lines 15 and 16 are both gains and line 18 or 19 has a value: 24 | # Use the Schedule D tax worksheet 25 | # Else if lines 15 and 16 are both gains or you have qualified divs: 26 | # Use the Qualified Dividends and Capital Gain Tax Worksheet 27 | # Else 28 | # Use tax tables 29 | 30 | def title(self): 31 | return 'Schedule D' 32 | -------------------------------------------------------------------------------- /2017/f8606.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8606(Form): 4 | """Form 8606, Nondeductible IRAs""" 5 | def __init__(f, inputs, spouse): 6 | super(F8606, f).__init__(inputs, init_idx=spouse) 7 | f.spouse = spouse 8 | f['3'] = f['1'] + f['2'] 9 | f['5'] = f['3'] - f['4'] 10 | f['9'] = f.rowsum(['6', '7', '8']) 11 | f['10'] = min(10000, f['5'] * 10000.0 / f['9']) 12 | f['11'] = f['8'] * f['10'] / 10000.0 13 | f['12'] = f['7'] * f['10'] / 10000.0 14 | f['13'] = f['11'] + f['12'] 15 | f['14'] = f['3'] - f['13'] 16 | f['15a'] = f['7'] - f['12'] 17 | f['15c'] = f['15a'] - f['15b'] 18 | f['17'] = f['11'] 19 | f['18'] = f['16'] - f['17'] 20 | # TODO: Roth IRA distributions 21 | f.must_file = True 22 | return 23 | 24 | def title(self): 25 | if self.spouse is not None: 26 | return 'Form 8606 [%d]' % self.spouse 27 | else: 28 | return 'Form 8606' 29 | -------------------------------------------------------------------------------- /2017/f8801.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8801(Form): 4 | """Form 8801, Credit for Prior Year Minimum Tax""" 5 | def __init__(f, inputs, f1040, f6251): 6 | super(F8801, f).__init__(inputs) 7 | f['21'] = inputs.get('prior_amt_credit') 8 | if not f['21']: 9 | return 10 | 11 | f.must_file = True 12 | f['22'] = max(0, f1040['44'] + f1040['46'] - \ 13 | (f1040.rowsum(['48', '49', '50', '51', '52', '53', '54']) or 0)) 14 | f['23'] = f6251.get('33') 15 | f['24'] = max(0, f['22'] - f['23']) 16 | f.comment['25'] = 'Minimum tax credit' 17 | f['25'] = min(f['21'], f['24']) 18 | if f['25']: 19 | f6251.must_file = True 20 | f.comment['26'] = 'Credit carryforward to 2018' 21 | f['26'] = f['21'] - f['25'] 22 | 23 | def title(self): 24 | return 'Form 8801 (for 2017 filing)' 25 | -------------------------------------------------------------------------------- /2017/f8959.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8959(Form): 4 | """Form 8959, Additional Medicare Tax""" 5 | THRESHOLDS = [200000, 250000, 125000, 200000, 200000] 6 | def __init__(f, inputs, f1040, sse): 7 | super(F8959, f).__init__(inputs) 8 | f['1'] = f.spouseSum(inputs, 'wages_medicare') 9 | # TODO: forms 4137, 8919 10 | f['4'] = f.rowsum(['1', '2', '3']) 11 | if f.get('4'): 12 | f['5'] = f.THRESHOLDS[inputs['status']] 13 | f['6'] = max(0, f['4'] - f['5']) 14 | f['7'] = f['6'] * .009 15 | 16 | if inputs['status'] == FilingStatus.JOINT: 17 | if sse[0].mustFile() or sse[1].mustFile(): 18 | f['8'] = max(0, (sse[0]['A4'] or sse[0]['B6'] or 0) + 19 | (sse[1]['A4'] or sse[1]['B6'] or 0)) 20 | else: 21 | if sse.mustFile(): 22 | f['8'] = max(0, sse['A4'] or sse['B6']) 23 | if f.get('8'): 24 | assert(f['8'] >= 400) 25 | f['9'] = f.THRESHOLDS[inputs['status']] 26 | f['10'] = f.get('4') 27 | f['11'] = max(0, f['9'] - f['10']) 28 | f['12'] = max(0, f['8'] - f['11']) 29 | f['13'] = f['12'] * .009 or None 30 | 31 | if f.get('14'): 32 | f['15'] = f.THRESHOLDS[inputs['status']] 33 | f['16'] = max(0, f['14'] - f['15']) 34 | f['17'] = f['16'] * .009 or None 35 | 36 | f['18'] = f.rowsum(['7', '13', '17']) 37 | 38 | f['19'] = f.spouseSum(inputs, 'medicare_withheld') 39 | f['20'] = f.get('1') 40 | f['21'] = f['20'] * .0145 or None 41 | if f['19'] or f['21']: 42 | f['22'] = max(0, f['19'] - f['21']) 43 | f['24'] = f.rowsum(['22', '23']) 44 | 45 | if f.get('18') or f.get('24'): 46 | f.must_file = True 47 | return 48 | 49 | def title(self): 50 | return 'Form 8959' 51 | -------------------------------------------------------------------------------- /2017/f8960.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8960(Form): 4 | """Form 8960, Net Investment Income Tax""" 5 | THRESHOLDS = [200000, 250000, 125000, 200000, 250000] 6 | def __init__(f, inputs, f1040, sched_a): 7 | super(F8960, f).__init__(inputs) 8 | f['1'] = f1040.get('8a') 9 | f['2'] = f1040.get('9a') 10 | # TODO: annuities 11 | f['4a'] = f1040.get('17') 12 | f['4c'] = f.rowsum(['4a', '4b']) 13 | f['5a'] = f1040.rowsum(['13', '14']) 14 | f['5d'] = f.rowsum(['5a', '5b', '5c']) 15 | f['8'] = f.rowsum(['1', '2', '3', '4c', '5d', '6', '7']) 16 | if sched_a: 17 | # This is one example of a "reasonable method allocation" but 18 | # not the only way. 19 | # Note: this only works for state income tax, not sales tax 20 | f['9b'] = f['8'] * sched_a['5'] / f1040['38'] 21 | f['9d'] = f.rowsum(['9a', '9b', '9c']) 22 | f['11'] = f.rowsum(['9d', '10']) 23 | f['12'] = max(0, f['8'] - f['11']) 24 | f['13'] = f1040['38'] 25 | f['14'] = f.THRESHOLDS[inputs['status']] 26 | f['15'] = max(0, f['13'] - f['14']) 27 | f['16'] = min(f['12'], f['15']) 28 | if f['16']: 29 | f['17'] = f['16'] * .038 30 | f.must_file = True 31 | return 32 | 33 | def title(self): 34 | return 'Form 8960' 35 | -------------------------------------------------------------------------------- /2018/ca540sp.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class CA540sp(Form): 4 | EXEMPTION_LIMITS = [268237, 357650, 178822, 268237, 357650] 5 | EXEMPTIONS = [71531, 95373, 47685, 71531, 95373] 6 | 7 | def __init__(f, inputs, ca540, ca540sca, f1040, f1040sa): 8 | super(CA540sp, f).__init__(inputs) 9 | if ca540['18'] != ca540sca.STD_DED[inputs['status']]: 10 | f['2'] = min(f1040sa['4'], .025 * f1040['7']) 11 | f['3'] = f1040sa.rowsum(['5b', '5c']) 12 | f['5'] = ca540sca['II25'] 13 | else: 14 | f['1'] = ca540['18'] 15 | f['14'] = f.rowsum(['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', 16 | '11', '12', '13']) 17 | f['15'] = ca540['19'] 18 | if not f['15']: 19 | f['15'] = ca540['17'] - ca540['18'] 20 | f['16'] = ca540sca.rowsum(['B21b', 'B21d', 'B21e']) 21 | f['17'] = -max(0, f1040['s12']) 22 | if ca540['18'] != ca540sca.STD_DED[inputs['status']]: 23 | f['18'] = -(ca540sca['II28'] - ca540sca['II29']) or None 24 | f['19'] = f.rowsum(['14', '15', '16', '17', '18']) 25 | f['21'] = f['19'] - f['20'] 26 | assert(inputs['status'] != FilingStatus.SEPARATE or f['21'] <= 369562) 27 | 28 | f['22'] = f.exemption(inputs) 29 | f['23'] = max(0, f['21'] - f['22']) 30 | f['24'] = f['23'] * .07 31 | f['25'] = ca540['31'] 32 | f['26'] = max(0, f['24'] - f['25']) 33 | if f['26'] or f['21'] > f['22'] and f.rowsum(['4', '7', '8', '9', '10', 34 | '11', '12', '13']): 35 | f.must_file = True 36 | 37 | def exemption(f, inputs): 38 | w = {} 39 | w['1'] = f.EXEMPTIONS[inputs['status']] 40 | w['2'] = f['21'] 41 | w['3'] = f.EXEMPTION_LIMITS[inputs['status']] 42 | w['4'] = max(0, w['2'] - w['3']) 43 | w['5'] = w['4'] * .25 44 | w['6'] = max(0, w['1'] - w['5']) 45 | return w['6'] 46 | 47 | def title(self): 48 | return 'CA 540 Schedule P' 49 | -------------------------------------------------------------------------------- /2018/example_joint_return.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Example of a married couple filing jointly. Each spouse has an income 4 | # of 100k. Also, the first spouse has some investment income and 5 | # business (Schedule C) income. 6 | # 7 | # A balance is owed on the return. 8 | # 9 | # On the state return, a refund is due. 10 | # 11 | 12 | from f1040 import F1040 13 | from ca540 import CA540 14 | from form import FilingStatus 15 | 16 | # Note that some of the quantities below we need to list separately for each 17 | # spouse in an array. Everything else we just need a total. 18 | 19 | inputs = { 20 | 'status': FilingStatus.JOINT, 21 | 'exemptions': 2, 22 | 'wages': [100000.00, 100000.00], # W2 box 1 23 | 'withholding': 37000.00, # W2 box 2 24 | 'wages_ss': [100000.00, 100000.00], # W2 box 3 25 | 'ss_withheld': [ 6200.00, 6200.00], # W2 box 4 26 | 'wages_medicare': [100000.00, 100000.00], # W2 box 5 27 | 'medicare_withheld': [ 1450.00, 1450.00], # W2 box 6 28 | 'state_withholding': 18000.00, # W2 box 17 29 | 30 | # These are other state tax payments made in the tax year not included in 31 | # 'state_withholding' that are deductible on schedule A: 32 | 'extra_state_tax_payments': 3000.00, 33 | 34 | 'taxable_interest': 1500.00, 35 | 'tax_exempt_interest': 700.00, 36 | 'dividends': 3000.00, 37 | 'qualified_dividends': 2000.00, 38 | 'capital_gain_dist': 500.00, 39 | 'capital_gain_long': 5000.00, 40 | 'business_income': [5000.00, 0.00], 41 | 42 | # Extra items for schedule A 43 | 'F1040sa' : { 44 | '11' : 500, # charitable contributions 45 | }, 46 | } 47 | 48 | f = F1040(inputs) 49 | f.printAllForms() 50 | print('') 51 | ca = CA540(inputs, f) 52 | ca.printAllForms() 53 | -------------------------------------------------------------------------------- /2018/example_joint_return_amt.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Example of a married couple filing jointly. Each spouse has an income 4 | # of 100k. Also, the first spouse has some investment income and 5 | # business (Schedule C) income. 6 | # 7 | # This example increases the amount of state tax paid on Schedule A, causing 8 | # this couple to be paying AMT instead of regular tax. Form 6251 is added. 9 | # 10 | # A balance is owed on the return. 11 | # 12 | # On the state return, a refund is due. 13 | # 14 | 15 | from f1040 import F1040 16 | from ca540 import CA540 17 | from form import FilingStatus 18 | 19 | # Note that some of the quantities below we need to list separately for each 20 | # spouse in an array. Everything else we just need a total. 21 | 22 | inputs = { 23 | 'status': FilingStatus.JOINT, 24 | 'exemptions': 2, 25 | 'wages': [120000.00, 120000.00], # W2 box 1 26 | 'withholding': 42000.00, # W2 box 2 27 | 'wages_ss': [120000.00, 120000.00], # W2 box 3 28 | 'ss_withheld': [ 6200.00, 6200.00], # W2 box 4 29 | 'wages_medicare': [120000.00, 120000.00], # W2 box 5 30 | 'medicare_withheld': [ 1450.00, 1450.00], # W2 box 6 31 | 'state_withholding': 20000.00, # W2 box 17 32 | 33 | # These are other state tax payments made in the tax year not included in 34 | # 'state_withholding' that are deductible on schedule A: 35 | 'extra_state_tax_payments': 8000.00, 36 | 37 | 'taxable_interest': 1500.00, 38 | 'tax_exempt_interest': 700.00, 39 | 'dividends': 3000.00, 40 | 'qualified_dividends': 2000.00, 41 | 'capital_gain_dist': 500.00, 42 | 'capital_gain_long': 5000.00, 43 | 'business_income': [5000.00, 0.00], 44 | 45 | # Extra items for schedule A 46 | 'F1040sa' : { 47 | '11' : 500, # charitable contributions 48 | }, 49 | } 50 | 51 | f = F1040(inputs) 52 | f.printAllForms() 53 | print('') 54 | ca = CA540(inputs, f) 55 | ca.printAllForms() 56 | -------------------------------------------------------------------------------- /2018/example_single.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Example of a single person with wage income of 100k. Also some investment 4 | # income and business (Schedule C) income. 5 | # 6 | 7 | from f1040 import F1040 8 | from ca540 import CA540 9 | from form import FilingStatus 10 | 11 | inputs = { 12 | 'status': FilingStatus.SINGLE, 13 | 'exemptions': 1, 14 | 'wages': 100000.00, # W2 box 1 15 | 'withholding': 20000.00, # W2 box 2 16 | 'wages_ss': 100000.00, # W2 box 3 17 | 'ss_withheld': 6200.00, # W2 box 4 18 | 'wages_medicare': 100000.00, # W2 box 5 19 | 'medicare_withheld': 1450.00, # W2 box 6 20 | 'state_withholding': 10000.00, # W2 box 17 21 | 22 | # These are other state tax payments made in the tax year not included in 23 | # 'state_withholding' that are deductible on schedule A: 24 | 'extra_state_tax_payments': 1000.00, 25 | 26 | 'taxable_interest': 1500.00, 27 | 'tax_exempt_interest': 700.00, 28 | 'dividends': 3000.00, 29 | 'qualified_dividends': 2000.00, 30 | 'capital_gain_dist': 500.00, 31 | 'capital_gain_long': 5000.00, 32 | 'business_income': 5000.00, 33 | 34 | # Extra items for schedule A 35 | 'F1040sa' : { 36 | '11' : 500, # charitable contributions 37 | }, 38 | } 39 | 40 | f = F1040(inputs) 41 | f.printAllForms() 42 | print('') 43 | ca = CA540(inputs, f) 44 | ca.printAllForms() 45 | -------------------------------------------------------------------------------- /2018/f1040sa.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F1040sa(Form): 4 | def __init__(f, inputs, f1040): 5 | super(F1040sa, f).__init__(inputs) 6 | if f['1']: 7 | f['2'] = f1040['7'] 8 | f['3'] = f['2'] * .075 9 | f['4'] = max(0, f['1'] - f['3']) 10 | f['5a'] = inputs['state_withholding'] + \ 11 | inputs.get('extra_state_tax_payments', 0) 12 | f['5d'] = f.rowsum(['5a', '5b', '5c']) 13 | f['5e'] = min(f['5d'], 14 | 5000 if inputs['status'] == FilingStatus.SEPARATE else 10000) 15 | f['7'] = f.rowsum(['5e', '6']) 16 | f['10'] = f.rowsum(['8e', '9']) 17 | f['14'] = f.rowsum(['11', '12', '13']) 18 | f['17'] = f.rowsum(['4', '7', '10', '14', '15', '16']) 19 | 20 | def title(self): 21 | return 'Schedule A' 22 | -------------------------------------------------------------------------------- /2018/f1040sd.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F1040sd(Form): 4 | def __init__(f, inputs): 5 | super(F1040sd, f).__init__(inputs) 6 | if 'capital_gain_long' not in inputs and 'capital_gain_short' not in inputs: 7 | return 8 | f.must_file = True 9 | f['1'] = inputs.get('capital_gain_short') 10 | f['6'] = inputs.get('capital_gain_carryover_short') 11 | f['7'] = f.rowsum(['1', '2', '3', '4', '5', '6']) 12 | f['8'] = inputs.get('capital_gain_long') 13 | f['13'] = inputs.get('capital_gain_dist') 14 | f['14'] = inputs.get('capital_gain_carryover_long') 15 | f['15'] = f.rowsum(['8', '9', '10', '11', '12', '13', '14']) 16 | f['16'] = f.rowsum(['7', '15']) 17 | if f['16'] < 0: 18 | cutoff = -3000 19 | if inputs['status'] == FilingStatus.SEPARATE: 20 | cutoff = -1500 21 | f['21'] = max(f['16'], cutoff) 22 | 23 | # If lines 15 and 16 are both gains and line 18 or 19 has a value: 24 | # Use the Schedule D tax worksheet 25 | # Else if lines 15 and 16 are both gains or you have qualified divs: 26 | # Use the Qualified Dividends and Capital Gain Tax Worksheet 27 | # Else 28 | # Use tax tables 29 | 30 | def title(self): 31 | return 'Schedule D' 32 | -------------------------------------------------------------------------------- /2018/f8606.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8606(Form): 4 | """Form 8606, Nondeductible IRAs""" 5 | def __init__(f, inputs, spouse): 6 | super(F8606, f).__init__(inputs, init_idx=spouse) 7 | f.spouse = spouse 8 | f['3'] = f['1'] + f['2'] 9 | f['5'] = f['3'] - f['4'] 10 | f['9'] = f.rowsum(['6', '7', '8']) 11 | f['10'] = min(10000, f['5'] * 10000.0 / f['9']) 12 | f['11'] = f['8'] * f['10'] / 10000.0 13 | f['12'] = f['7'] * f['10'] / 10000.0 14 | f['13'] = f['11'] + f['12'] 15 | f['14'] = f['3'] - f['13'] 16 | f['15a'] = f['7'] - f['12'] 17 | f['15c'] = f['15a'] - f['15b'] 18 | f['16'] = f['8'] 19 | f['17'] = f['11'] 20 | f['18'] = f['16'] - f['17'] 21 | # TODO: Roth IRA distributions 22 | f.must_file = True 23 | return 24 | 25 | def title(self): 26 | if self.spouse is not None: 27 | return 'Form 8606 [%d]' % self.spouse 28 | else: 29 | return 'Form 8606' 30 | -------------------------------------------------------------------------------- /2018/f8801.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8801(Form): 4 | """Form 8801, Credit for Prior Year Minimum Tax""" 5 | def __init__(f, inputs, f1040, f6251): 6 | super(F8801, f).__init__(inputs) 7 | f['21'] = inputs.get('prior_amt_credit') 8 | if not f['21']: 9 | return 10 | 11 | f.must_file = True 12 | f['22'] = max(0, f1040['11a'] + f1040['s46'] - \ 13 | (f1040.rowsum(['12a', 's48', 's49', 's50', 's51', 's53', 's54']) or 0)) 14 | f['23'] = f6251.get('9') 15 | f['24'] = max(0, f['22'] - f['23']) 16 | f.comment['25'] = 'Minimum tax credit' 17 | f['25'] = min(f['21'], f['24']) 18 | if f['25']: 19 | f6251.must_file = True 20 | f.comment['26'] = 'Credit carryforward to 2019' 21 | f['26'] = f['21'] - f['25'] 22 | 23 | def title(self): 24 | return 'Form 8801 (for 2018 filing)' 25 | -------------------------------------------------------------------------------- /2018/f8959.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8959(Form): 4 | """Form 8959, Additional Medicare Tax""" 5 | THRESHOLDS = [200000, 250000, 125000, 200000, 200000] 6 | def __init__(f, inputs, sse): 7 | super(F8959, f).__init__(inputs) 8 | f['1'] = f.spouseSum(inputs, 'wages_medicare') 9 | # TODO: forms 4137, 8919 10 | f['4'] = f.rowsum(['1', '2', '3']) 11 | if f.get('4'): 12 | f['5'] = f.THRESHOLDS[inputs['status']] 13 | f['6'] = max(0, f['4'] - f['5']) 14 | f['7'] = f['6'] * .009 15 | 16 | if inputs['status'] == FilingStatus.JOINT: 17 | if sse[0].mustFile() or sse[1].mustFile(): 18 | f['8'] = max(0, (sse[0]['A4'] or sse[0]['B6'] or 0) + 19 | (sse[1]['A4'] or sse[1]['B6'] or 0)) 20 | else: 21 | if sse.mustFile(): 22 | f['8'] = max(0, sse['A4'] or sse['B6']) 23 | if f.get('8'): 24 | assert(f['8'] >= 400) 25 | f['9'] = f.THRESHOLDS[inputs['status']] 26 | f['10'] = f.get('4') 27 | f['11'] = max(0, f['9'] - f['10']) 28 | f['12'] = max(0, f['8'] - f['11']) 29 | f['13'] = f['12'] * .009 or None 30 | 31 | if f.get('14'): 32 | f['15'] = f.THRESHOLDS[inputs['status']] 33 | f['16'] = max(0, f['14'] - f['15']) 34 | f['17'] = f['16'] * .009 or None 35 | 36 | f['18'] = f.rowsum(['7', '13', '17']) 37 | 38 | f['19'] = f.spouseSum(inputs, 'medicare_withheld') 39 | f['20'] = f.get('1') 40 | f['21'] = f['20'] * .0145 or None 41 | if f['19'] or f['21']: 42 | f['22'] = max(0, f['19'] - f['21']) 43 | f['24'] = f.rowsum(['22', '23']) 44 | 45 | if f.get('18') or f.get('24'): 46 | f.must_file = True 47 | return 48 | 49 | def title(self): 50 | return 'Form 8959' 51 | -------------------------------------------------------------------------------- /2018/f8960.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8960(Form): 4 | """Form 8960, Net Investment Income Tax""" 5 | THRESHOLDS = [200000, 250000, 125000, 200000, 250000] 6 | def __init__(f, inputs, f1040, sched_a): 7 | super(F8960, f).__init__(inputs) 8 | f['1'] = f1040.get('2b') 9 | f['2'] = f1040.get('3b') 10 | # TODO: annuities 11 | f['4a'] = f1040.get('s17') 12 | f['4c'] = f.rowsum(['4a', '4b']) 13 | f['5a'] = f1040.rowsum(['s13', 's14']) 14 | f['5d'] = f.rowsum(['5a', '5b', '5c']) 15 | f['8'] = f.rowsum(['1', '2', '3', '4c', '5d', '6', '7']) 16 | if sched_a: 17 | # This is one example of a "reasonable method allocation" but 18 | # not the only way. 19 | # Note: this only works for state income tax, not sales tax 20 | f['9b'] = f['8'] * sched_a['5a'] / f1040['7'] 21 | f['9d'] = f.rowsum(['9a', '9b', '9c']) 22 | f['11'] = f.rowsum(['9d', '10']) 23 | f['12'] = max(0, f['8'] - f['11']) 24 | f['13'] = f1040['7'] 25 | f['14'] = f.THRESHOLDS[inputs['status']] 26 | f['15'] = max(0, f['13'] - f['14']) 27 | f['16'] = min(f['12'], f['15']) 28 | if f['16']: 29 | f['17'] = f['16'] * .038 30 | f.must_file = True 31 | return 32 | 33 | def title(self): 34 | return 'Form 8960' 35 | -------------------------------------------------------------------------------- /2019/ca540sp.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class CA540sp(Form): 4 | EXEMPTION_LIMITS = [276552, 368737, 184365, 276552, 368737] 5 | EXEMPTIONS = [73748, 98330, 49163, 73748, 98330] 6 | 7 | def __init__(f, inputs, ca540, ca540sca, f1040, f1040sa): 8 | super(CA540sp, f).__init__(inputs) 9 | if ca540['18'] != ca540sca.STD_DED[inputs['status']]: 10 | f['2'] = min(f1040sa['4'], .025 * f1040['8b']) 11 | f['3'] = f1040sa.rowsum(['5b', '5c']) 12 | f['5'] = ca540sca['2_25'] 13 | else: 14 | f['1'] = ca540['18'] 15 | f['14'] = f.rowsum(['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', 16 | '11', '12', '13']) 17 | f['15'] = ca540['19'] 18 | if not f['15']: 19 | f['15'] = ca540['17'] - ca540['18'] 20 | f['16'] = ca540sca.rowsum(['1B_B8b', '1B_B8d', '1B_B8e']) 21 | f['17'] = -max(0, f1040['s1_3']) 22 | if ca540['18'] != ca540sca.STD_DED[inputs['status']]: 23 | f['18'] = -(ca540sca['2_28'] - ca540sca['2_29']) or None 24 | f['19'] = f.rowsum(['14', '15', '16', '17', '18']) 25 | f['21'] = f['19'] - f['20'] 26 | assert(inputs['status'] != FilingStatus.SEPARATE or f['21'] <= 381017) 27 | 28 | f['22'] = f.exemption(inputs) 29 | f['23'] = max(0, f['21'] - f['22']) 30 | f['24'] = f['23'] * .07 31 | f['25'] = ca540['31'] 32 | f['26'] = max(0, f['24'] - f['25']) 33 | if f['26'] or f['21'] > f['22'] and f.rowsum(['4', '7', '8', '9', '10', 34 | '11', '12', '13']): 35 | f.must_file = True 36 | 37 | def exemption(f, inputs): 38 | w = {} 39 | w['1'] = f.EXEMPTIONS[inputs['status']] 40 | w['2'] = f['21'] 41 | w['3'] = f.EXEMPTION_LIMITS[inputs['status']] 42 | w['4'] = max(0, w['2'] - w['3']) 43 | w['5'] = w['4'] * .25 44 | w['6'] = max(0, w['1'] - w['5']) 45 | return w['6'] 46 | 47 | def title(self): 48 | return 'CA 540 Schedule P' 49 | -------------------------------------------------------------------------------- /2019/example_joint_return.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Example of a married couple filing jointly. Each spouse has an income 4 | # of 100k. Also, the first spouse has some investment income and 5 | # business (Schedule C) income. 6 | # 7 | # A balance is owed on the return. 8 | # 9 | # On the state return, a refund is due. 10 | # 11 | 12 | from f1040 import F1040 13 | from ca540 import CA540 14 | from form import FilingStatus 15 | 16 | # Note that some of the quantities below we need to list separately for each 17 | # spouse in an array. Everything else we just need a total. 18 | 19 | inputs = { 20 | 'status': FilingStatus.JOINT, 21 | 'exemptions': 2, 22 | 'wages': [100000.00, 100000.00], # W2 box 1 23 | 'withholding': 37000.00, # W2 box 2 24 | 'wages_ss': [100000.00, 100000.00], # W2 box 3 25 | 'ss_withheld': [ 6200.00, 6200.00], # W2 box 4 26 | 'wages_medicare': [100000.00, 100000.00], # W2 box 5 27 | 'medicare_withheld': [ 1450.00, 1450.00], # W2 box 6 28 | 'state_withholding': 18000.00, # W2 box 17 29 | 30 | # These are other state tax payments made in the tax year not included in 31 | # 'state_withholding' that are deductible on schedule A: 32 | 'extra_state_tax_payments': 3000.00, 33 | 34 | 'taxable_interest': 1500.00, 35 | 'tax_exempt_interest': 700.00, 36 | 'dividends': 3000.00, 37 | 'qualified_dividends': 2000.00, 38 | 'capital_gain_dist': 500.00, 39 | 'capital_gain_long': 5000.00, 40 | 'business_income': [5000.00, 0.00], 41 | 42 | # Extra items for schedule A 43 | 'F1040sa' : { 44 | '11' : 500, # charitable contributions 45 | }, 46 | } 47 | 48 | f = F1040(inputs) 49 | f.printAllForms() 50 | print('') 51 | ca = CA540(inputs, f) 52 | ca.printAllForms() 53 | -------------------------------------------------------------------------------- /2019/example_joint_return_amt.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Example of a married couple filing jointly. Each spouse has an income 4 | # of 100k. Also, the first spouse has some investment income and 5 | # business (Schedule C) income. 6 | # 7 | # This example increases the amount of state tax paid on Schedule A, causing 8 | # this couple to be paying AMT instead of regular tax. Form 6251 is added. 9 | # 10 | # A balance is owed on the return. 11 | # 12 | # On the state return, a refund is due. 13 | # 14 | 15 | from f1040 import F1040 16 | from ca540 import CA540 17 | from form import FilingStatus 18 | 19 | # Note that some of the quantities below we need to list separately for each 20 | # spouse in an array. Everything else we just need a total. 21 | 22 | inputs = { 23 | 'status': FilingStatus.JOINT, 24 | 'exemptions': 2, 25 | 'wages': [120000.00, 120000.00], # W2 box 1 26 | 'withholding': 42000.00, # W2 box 2 27 | 'wages_ss': [120000.00, 120000.00], # W2 box 3 28 | 'ss_withheld': [ 6200.00, 6200.00], # W2 box 4 29 | 'wages_medicare': [120000.00, 120000.00], # W2 box 5 30 | 'medicare_withheld': [ 1450.00, 1450.00], # W2 box 6 31 | 'state_withholding': 20000.00, # W2 box 17 32 | 33 | # These are other state tax payments made in the tax year not included in 34 | # 'state_withholding' that are deductible on schedule A: 35 | 'extra_state_tax_payments': 8000.00, 36 | 37 | 'taxable_interest': 1500.00, 38 | 'tax_exempt_interest': 700.00, 39 | 'dividends': 3000.00, 40 | 'qualified_dividends': 2000.00, 41 | 'capital_gain_dist': 500.00, 42 | 'capital_gain_long': 5000.00, 43 | 'business_income': [5000.00, 0.00], 44 | 45 | # Extra items for schedule A 46 | 'F1040sa' : { 47 | '11' : 500, # charitable contributions 48 | }, 49 | } 50 | 51 | f = F1040(inputs) 52 | f.printAllForms() 53 | print('') 54 | ca = CA540(inputs, f) 55 | ca.printAllForms() 56 | -------------------------------------------------------------------------------- /2019/example_single.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Example of a single person with wage income of 100k. Also some investment 4 | # income and business (Schedule C) income. 5 | # 6 | 7 | from f1040 import F1040 8 | from ca540 import CA540 9 | from form import FilingStatus 10 | 11 | inputs = { 12 | 'status': FilingStatus.SINGLE, 13 | 'exemptions': 1, 14 | 'wages': 100000.00, # W2 box 1 15 | 'withholding': 20000.00, # W2 box 2 16 | 'wages_ss': 100000.00, # W2 box 3 17 | 'ss_withheld': 6200.00, # W2 box 4 18 | 'wages_medicare': 100000.00, # W2 box 5 19 | 'medicare_withheld': 1450.00, # W2 box 6 20 | 'state_withholding': 10000.00, # W2 box 17 21 | 22 | # These are other state tax payments made in the tax year not included in 23 | # 'state_withholding' that are deductible on schedule A: 24 | 'extra_state_tax_payments': 1000.00, 25 | 26 | 'taxable_interest': 1500.00, 27 | 'tax_exempt_interest': 700.00, 28 | 'dividends': 3000.00, 29 | 'qualified_dividends': 2000.00, 30 | 'capital_gain_dist': 500.00, 31 | 'capital_gain_long': 5000.00, 32 | 'business_income': 5000.00, 33 | 34 | # Extra items for schedule A 35 | 'F1040sa' : { 36 | '11' : 500, # charitable contributions 37 | }, 38 | } 39 | 40 | f = F1040(inputs) 41 | f.printAllForms() 42 | print('') 43 | ca = CA540(inputs, f) 44 | ca.printAllForms() 45 | -------------------------------------------------------------------------------- /2019/f1040sa.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F1040sa(Form): 4 | def __init__(f, inputs, f1040): 5 | super(F1040sa, f).__init__(inputs) 6 | if f['1']: 7 | f['2'] = f1040['8b'] 8 | f['3'] = f['2'] * .075 9 | f['4'] = max(0, f['1'] - f['3']) 10 | f['5a'] = inputs['state_withholding'] + \ 11 | inputs.get('extra_state_tax_payments', 0) 12 | f['5d'] = f.rowsum(['5a', '5b', '5c']) 13 | f['5e'] = min(f['5d'], 14 | 5000 if inputs['status'] == FilingStatus.SEPARATE else 10000) 15 | f['7'] = f.rowsum(['5e', '6']) 16 | f['10'] = f.rowsum(['8e', '9']) 17 | f['14'] = f.rowsum(['11', '12', '13']) 18 | f['17'] = f.rowsum(['4', '7', '10', '14', '15', '16']) 19 | 20 | def title(self): 21 | return 'Schedule A' 22 | -------------------------------------------------------------------------------- /2019/f1040sd.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F1040sd(Form): 4 | def __init__(f, inputs): 5 | super(F1040sd, f).__init__(inputs) 6 | if 'capital_gain_long' not in inputs and 'capital_gain_short' not in inputs: 7 | return 8 | f.must_file = True 9 | f['1'] = inputs.get('capital_gain_short') 10 | f['6'] = inputs.get('capital_gain_carryover_short') 11 | f['7'] = f.rowsum(['1', '2', '3', '4', '5', '6']) 12 | f['8'] = inputs.get('capital_gain_long') 13 | f['13'] = inputs.get('capital_gain_dist') 14 | f['14'] = inputs.get('capital_gain_carryover_long') 15 | f['15'] = f.rowsum(['8', '9', '10', '11', '12', '13', '14']) 16 | f['16'] = f.rowsum(['7', '15']) 17 | if f['16'] < 0: 18 | cutoff = -3000 19 | if inputs['status'] == FilingStatus.SEPARATE: 20 | cutoff = -1500 21 | f['21'] = max(f['16'], cutoff) 22 | 23 | # If lines 15 and 16 are both gains and line 18 or 19 has a value: 24 | # Use the Schedule D tax worksheet 25 | # Else if lines 15 and 16 are both gains or you have qualified divs: 26 | # Use the Qualified Dividends and Capital Gain Tax Worksheet 27 | # Else 28 | # Use tax tables 29 | 30 | def title(self): 31 | return 'Schedule D' 32 | -------------------------------------------------------------------------------- /2019/f8606.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8606(Form): 4 | """Form 8606, Nondeductible IRAs""" 5 | def __init__(f, inputs, spouse): 6 | super(F8606, f).__init__(inputs, init_idx=spouse) 7 | f.spouse = spouse 8 | f['3'] = f['1'] + f['2'] 9 | f['5'] = f['3'] - f['4'] 10 | f['9'] = f.rowsum(['6', '7', '8']) 11 | f['10'] = min(10000, f['5'] * 10000.0 / f['9']) 12 | f['11'] = f['8'] * f['10'] / 10000.0 13 | f['12'] = f['7'] * f['10'] / 10000.0 14 | f['13'] = f['11'] + f['12'] 15 | f['14'] = f['3'] - f['13'] 16 | f['15a'] = f['7'] - f['12'] 17 | f['15c'] = f['15a'] - f['15b'] 18 | f['16'] = f['8'] 19 | f['17'] = f['11'] 20 | f['18'] = f['16'] - f['17'] 21 | # TODO: Roth IRA distributions 22 | f.must_file = True 23 | return 24 | 25 | def title(self): 26 | if self.spouse is not None: 27 | return 'Form 8606 [%d]' % self.spouse 28 | else: 29 | return 'Form 8606' 30 | -------------------------------------------------------------------------------- /2019/f8801.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8801(Form): 4 | """Form 8801, Credit for Prior Year Minimum Tax""" 5 | def __init__(f, inputs, f1040, f6251): 6 | super(F8801, f).__init__(inputs) 7 | f['21'] = inputs.get('prior_amt_credit') 8 | if not f['21']: 9 | return 10 | 11 | f.must_file = True 12 | f['22'] = max(0, f1040['12a'] + f1040['s2_2'] - \ 13 | (f1040.rowsum(['13a', 's3_1', 's3_2', 's3_3', 's3_4', 's3_5', 's3_6']) or 0)) 14 | f['23'] = f6251.get('9') 15 | f['24'] = max(0, f['22'] - f['23']) 16 | f.comment['25'] = 'Minimum tax credit' 17 | f['25'] = min(f['21'], f['24']) 18 | if f['25']: 19 | f6251.must_file = True 20 | f.comment['26'] = 'Credit carryforward to 2020' 21 | f['26'] = f['21'] - f['25'] 22 | 23 | def title(self): 24 | return 'Form 8801 (for 2019 filing)' 25 | -------------------------------------------------------------------------------- /2019/f8959.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8959(Form): 4 | """Form 8959, Additional Medicare Tax""" 5 | THRESHOLDS = [200000, 250000, 125000, 200000, 200000] 6 | def __init__(f, inputs, sse): 7 | super(F8959, f).__init__(inputs) 8 | f['1'] = f.spouseSum(inputs, 'wages_medicare') 9 | # TODO: forms 4137, 8919 10 | f['4'] = f.rowsum(['1', '2', '3']) 11 | if f.get('4'): 12 | f['5'] = f.THRESHOLDS[inputs['status']] 13 | f['6'] = max(0, f['4'] - f['5']) 14 | f['7'] = f['6'] * .009 15 | 16 | if inputs['status'] == FilingStatus.JOINT: 17 | if sse[0].mustFile() or sse[1].mustFile(): 18 | f['8'] = max(0, (sse[0]['A4'] or sse[0]['B6'] or 0) + 19 | (sse[1]['A4'] or sse[1]['B6'] or 0)) 20 | else: 21 | if sse.mustFile(): 22 | f['8'] = max(0, sse['A4'] or sse['B6']) 23 | if f.get('8'): 24 | assert(f['8'] >= 400) 25 | f['9'] = f.THRESHOLDS[inputs['status']] 26 | f['10'] = f.get('4') 27 | f['11'] = max(0, f['9'] - f['10']) 28 | f['12'] = max(0, f['8'] - f['11']) 29 | f['13'] = f['12'] * .009 or None 30 | 31 | if f.get('14'): 32 | f['15'] = f.THRESHOLDS[inputs['status']] 33 | f['16'] = max(0, f['14'] - f['15']) 34 | f['17'] = f['16'] * .009 or None 35 | 36 | f['18'] = f.rowsum(['7', '13', '17']) 37 | 38 | f['19'] = f.spouseSum(inputs, 'medicare_withheld') 39 | f['20'] = f.get('1') 40 | f['21'] = f['20'] * .0145 or None 41 | if f['19'] or f['21']: 42 | f['22'] = max(0, f['19'] - f['21']) 43 | f['24'] = f.rowsum(['22', '23']) 44 | 45 | if f.get('18') or f.get('24'): 46 | f.must_file = True 47 | return 48 | 49 | def title(self): 50 | return 'Form 8959' 51 | -------------------------------------------------------------------------------- /2019/f8960.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8960(Form): 4 | """Form 8960, Net Investment Income Tax""" 5 | THRESHOLDS = [200000, 250000, 125000, 200000, 250000] 6 | def __init__(f, inputs, f1040, sched_a): 7 | super(F8960, f).__init__(inputs) 8 | f['1'] = f1040.get('2b') 9 | f['2'] = f1040.get('3b') 10 | # TODO: annuities 11 | f['4a'] = f1040.get('s1_5') 12 | f['4c'] = f.rowsum(['4a', '4b']) 13 | f['5a'] = f1040.rowsum(['6', 's1_4']) 14 | f['5d'] = f.rowsum(['5a', '5b', '5c']) 15 | f['8'] = f.rowsum(['1', '2', '3', '4c', '5d', '6', '7']) 16 | if sched_a: 17 | # This is one example of a "reasonable method allocation" but 18 | # not the only way. 19 | # Note: this only works for state income tax, not sales tax 20 | f['9b'] = f['8'] * sched_a['5a'] / f1040['8b'] 21 | f['9d'] = f.rowsum(['9a', '9b', '9c']) 22 | f['11'] = f.rowsum(['9d', '10']) 23 | f['12'] = max(0, f['8'] - f['11']) 24 | f['13'] = f1040['8b'] 25 | f['14'] = f.THRESHOLDS[inputs['status']] 26 | f['15'] = max(0, f['13'] - f['14']) 27 | f['16'] = min(f['12'], f['15']) 28 | if f['16']: 29 | f['17'] = f['16'] * .038 30 | f.must_file = True 31 | return 32 | 33 | def title(self): 34 | return 'Form 8960' 35 | -------------------------------------------------------------------------------- /2020/ca540sp.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class CA540sp(Form): 4 | EXEMPTION_LIMITS = [280424, 373899, 186946, 280424, 373899] 5 | EXEMPTIONS = [74780, 99707, 49851, 74780, 99707] 6 | 7 | def __init__(f, inputs, ca540, ca540sca, f1040, f1040sa): 8 | super(CA540sp, f).__init__(inputs) 9 | if ca540['18'] != ca540sca.STD_DED[inputs['status']]: 10 | f['2'] = min(f1040sa['4'], .025 * f1040['11']) 11 | f['3'] = f1040sa.rowsum(['5b', '5c']) 12 | f['5'] = ca540sca['2_25'] 13 | else: 14 | f['1'] = ca540['18'] 15 | f['14'] = f.rowsum(['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', 16 | '11', '12', '13']) 17 | f['15'] = ca540['19'] 18 | if not f['15']: 19 | f['15'] = ca540['17'] - ca540['18'] 20 | f['16'] = ca540sca.rowsum(['1B_B8b', '1B_B8d', '1B_B8e']) 21 | f['17'] = -max(0, f1040['s1_3']) 22 | if ca540['18'] != ca540sca.STD_DED[inputs['status']]: 23 | f['18'] = -(ca540sca['2_28'] - ca540sca['2_29']) or None 24 | f['19'] = f.rowsum(['14', '15', '16', '17', '18']) 25 | f['21'] = f['19'] - f['20'] 26 | assert(inputs['status'] != FilingStatus.SEPARATE or f['21'] <= 386350) 27 | 28 | f['22'] = f.exemption(inputs) 29 | f['23'] = max(0, f['21'] - f['22']) 30 | f['24'] = f['23'] * .07 31 | f['25'] = ca540['31'] 32 | f['26'] = max(0, f['24'] - f['25']) 33 | if f['26'] or f['21'] > f['22'] and f.rowsum(['4', '7', '8', '9', '10', 34 | '11', '12', '13']): 35 | f.must_file = True 36 | 37 | def exemption(f, inputs): 38 | w = {} 39 | w['1'] = f.EXEMPTIONS[inputs['status']] 40 | w['2'] = f['21'] 41 | w['3'] = f.EXEMPTION_LIMITS[inputs['status']] 42 | w['4'] = max(0, w['2'] - w['3']) 43 | w['5'] = w['4'] * .25 44 | w['6'] = max(0, w['1'] - w['5']) 45 | return w['6'] 46 | 47 | def title(self): 48 | return 'CA 540 Schedule P' 49 | -------------------------------------------------------------------------------- /2020/example_joint_return.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Example of a married couple filing jointly. Each spouse has an income 4 | # of 100k. Also, the first spouse has some investment income and 5 | # business (Schedule C) income. 6 | # 7 | # A balance is owed on the return. 8 | # 9 | # On the state return, a refund is due. 10 | # 11 | 12 | from f1040 import F1040 13 | from ca540 import CA540 14 | from form import FilingStatus 15 | 16 | # Note that some of the quantities below we need to list separately for each 17 | # spouse in an array. Everything else we just need a total. 18 | 19 | inputs = { 20 | 'status': FilingStatus.JOINT, 21 | 'exemptions': 2, 22 | 'wages': [100000.00, 100000.00], # W2 box 1 23 | 'withholding': 37000.00, # W2 box 2 24 | 'wages_ss': [100000.00, 100000.00], # W2 box 3 25 | 'ss_withheld': [ 6200.00, 6200.00], # W2 box 4 26 | 'wages_medicare': [100000.00, 100000.00], # W2 box 5 27 | 'medicare_withheld': [ 1450.00, 1450.00], # W2 box 6 28 | 'state_withholding': 18000.00, # W2 box 17 29 | 30 | # These are other state tax payments made in the tax year not included in 31 | # 'state_withholding' that are deductible on schedule A: 32 | 'extra_state_tax_payments': 3000.00, 33 | 34 | 'taxable_interest': 1500.00, 35 | 'tax_exempt_interest': 700.00, 36 | 'dividends': 3000.00, 37 | 'qualified_dividends': 2000.00, 38 | 'capital_gain_dist': 500.00, 39 | 'capital_gain_long': 5000.00, 40 | 'business_income': [5000.00, 0.00], 41 | 42 | # Extra items for schedule A 43 | 'F1040sa' : { 44 | '11' : 500, # charitable contributions 45 | }, 46 | } 47 | 48 | f = F1040(inputs) 49 | f.printAllForms() 50 | print('') 51 | ca = CA540(inputs, f) 52 | ca.printAllForms() 53 | -------------------------------------------------------------------------------- /2020/example_joint_return_amt.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Example of a married couple filing jointly. Each spouse has an income 4 | # of 100k. Also, the first spouse has some investment income and 5 | # business (Schedule C) income. 6 | # 7 | # This example increases the amount of state tax paid on Schedule A, causing 8 | # this couple to be paying AMT instead of regular tax. Form 6251 is added. 9 | # 10 | # A balance is owed on the return. 11 | # 12 | # On the state return, a refund is due. 13 | # 14 | 15 | from f1040 import F1040 16 | from ca540 import CA540 17 | from form import FilingStatus 18 | 19 | # Note that some of the quantities below we need to list separately for each 20 | # spouse in an array. Everything else we just need a total. 21 | 22 | inputs = { 23 | 'status': FilingStatus.JOINT, 24 | 'exemptions': 2, 25 | 'wages': [120000.00, 120000.00], # W2 box 1 26 | 'withholding': 42000.00, # W2 box 2 27 | 'wages_ss': [120000.00, 120000.00], # W2 box 3 28 | 'ss_withheld': [ 6200.00, 6200.00], # W2 box 4 29 | 'wages_medicare': [120000.00, 120000.00], # W2 box 5 30 | 'medicare_withheld': [ 1450.00, 1450.00], # W2 box 6 31 | 'state_withholding': 20000.00, # W2 box 17 32 | 33 | # These are other state tax payments made in the tax year not included in 34 | # 'state_withholding' that are deductible on schedule A: 35 | 'extra_state_tax_payments': 8000.00, 36 | 37 | 'taxable_interest': 1500.00, 38 | 'tax_exempt_interest': 700.00, 39 | 'dividends': 3000.00, 40 | 'qualified_dividends': 2000.00, 41 | 'capital_gain_dist': 500.00, 42 | 'capital_gain_long': 5000.00, 43 | 'business_income': [5000.00, 0.00], 44 | 45 | # Extra items for schedule A 46 | 'F1040sa' : { 47 | '11' : 500, # charitable contributions 48 | }, 49 | } 50 | 51 | f = F1040(inputs) 52 | f.printAllForms() 53 | print('') 54 | ca = CA540(inputs, f) 55 | ca.printAllForms() 56 | -------------------------------------------------------------------------------- /2020/example_single.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Example of a single person with wage income of 100k. Also some investment 4 | # income and business (Schedule C) income. 5 | # 6 | 7 | from f1040 import F1040 8 | from ca540 import CA540 9 | from form import FilingStatus 10 | 11 | inputs = { 12 | 'status': FilingStatus.SINGLE, 13 | 'exemptions': 1, 14 | 'wages': 100000.00, # W2 box 1 15 | 'withholding': 20000.00, # W2 box 2 16 | 'wages_ss': 100000.00, # W2 box 3 17 | 'ss_withheld': 6200.00, # W2 box 4 18 | 'wages_medicare': 100000.00, # W2 box 5 19 | 'medicare_withheld': 1450.00, # W2 box 6 20 | 'state_withholding': 10000.00, # W2 box 17 21 | 22 | # These are other state tax payments made in the tax year not included in 23 | # 'state_withholding' that are deductible on schedule A: 24 | 'extra_state_tax_payments': 1000.00, 25 | 26 | 'taxable_interest': 1500.00, 27 | 'tax_exempt_interest': 700.00, 28 | 'dividends': 3000.00, 29 | 'qualified_dividends': 2000.00, 30 | 'capital_gain_dist': 500.00, 31 | 'capital_gain_long': 5000.00, 32 | 'business_income': 5000.00, 33 | 34 | # Extra items for schedule A 35 | 'F1040sa' : { 36 | '11' : 500, # charitable contributions 37 | }, 38 | } 39 | 40 | f = F1040(inputs) 41 | f.printAllForms() 42 | print('') 43 | ca = CA540(inputs, f) 44 | ca.printAllForms() 45 | -------------------------------------------------------------------------------- /2020/f1040sa.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F1040sa(Form): 4 | def __init__(f, inputs, f1040): 5 | super(F1040sa, f).__init__(inputs) 6 | if f['1']: 7 | f['2'] = f1040['11'] 8 | f['3'] = f['2'] * .075 9 | f['4'] = max(0, f['1'] - f['3']) 10 | f['5a'] = inputs['state_withholding'] + \ 11 | inputs.get('extra_state_tax_payments', 0) 12 | f['5d'] = f.rowsum(['5a', '5b', '5c']) 13 | f['5e'] = min(f['5d'], 14 | 5000 if inputs['status'] == FilingStatus.SEPARATE else 10000) 15 | f['7'] = f.rowsum(['5e', '6']) 16 | f['8e'] = f.rowsum(['8a', '8b', '8c', '8d']) 17 | f['10'] = f.rowsum(['8e', '9']) 18 | f['14'] = f.rowsum(['11', '12', '13']) 19 | f['17'] = f.rowsum(['4', '7', '10', '14', '15', '16']) 20 | 21 | def title(self): 22 | return 'Schedule A' 23 | -------------------------------------------------------------------------------- /2020/f1040sd.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F1040sd(Form): 4 | def __init__(f, inputs): 5 | super(F1040sd, f).__init__(inputs) 6 | if 'capital_gain_long' not in inputs and 'capital_gain_short' not in inputs: 7 | return 8 | f.must_file = True 9 | f['1'] = inputs.get('capital_gain_short') 10 | f['6'] = inputs.get('capital_gain_carryover_short') 11 | f['7'] = f.rowsum(['1', '2', '3', '4', '5', '6']) 12 | f['8'] = inputs.get('capital_gain_long') 13 | f['13'] = inputs.get('capital_gain_dist') 14 | f['14'] = inputs.get('capital_gain_carryover_long') 15 | f['15'] = f.rowsum(['8', '9', '10', '11', '12', '13', '14']) 16 | f['16'] = f.rowsum(['7', '15']) 17 | if f['16'] < 0: 18 | cutoff = -3000 19 | if inputs['status'] == FilingStatus.SEPARATE: 20 | cutoff = -1500 21 | f['21'] = max(f['16'], cutoff) 22 | 23 | # If lines 15 and 16 are both gains and line 18 or 19 has a value: 24 | # Use the Schedule D tax worksheet 25 | # Else if lines 15 and 16 are both gains or you have qualified divs: 26 | # Use the Qualified Dividends and Capital Gain Tax Worksheet 27 | # Else 28 | # Use tax tables 29 | 30 | def title(self): 31 | return 'Schedule D' 32 | -------------------------------------------------------------------------------- /2020/f1040sse.py: -------------------------------------------------------------------------------- 1 | from form import Form 2 | 3 | SS_WAGE_LIMIT = 137700 4 | 5 | # Not implemented: optional methods 6 | class F1040sse(Form): 7 | def __init__(self, inputs): 8 | super(F1040sse, self).__init__(inputs) 9 | self['2'] = inputs.get('business_income') 10 | self['3'] = self.rowsum(['1a', '1b', '2']) 11 | if self['3'] > 0: 12 | self['4a'] = self['3'] * 0.9235 13 | else: 14 | self['4a'] = self['3'] 15 | self['4c'] = self.rowsum(['4a', '4b']) 16 | if self['4c'] < 400: 17 | return 18 | self.must_file = True 19 | self['6'] = self.rowsum(['4c', '5b']) 20 | self['7'] = SS_WAGE_LIMIT 21 | self['8a'] = inputs['wages_ss'] 22 | if self['8a'] < SS_WAGE_LIMIT: 23 | self['8d'] = self.rowsum(['8a', '8b', '8c']) 24 | self['9'] = self['7'] - self['8d'] 25 | if self['9'] <= 0: 26 | self['9'] = 0 27 | self['10'] = 0 28 | else: 29 | self['10'] = min(self['6'], self['9']) * .124 30 | self['11'] = self['6'] * .029 31 | self['12'] = self.rowsum(['10', '11']) 32 | self['13'] = self['12'] * .5 33 | 34 | def title(self): 35 | return 'Schedule SE' 36 | -------------------------------------------------------------------------------- /2020/f8606.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8606(Form): 4 | """Form 8606, Nondeductible IRAs""" 5 | def __init__(f, inputs, spouse): 6 | super(F8606, f).__init__(inputs, init_idx=spouse) 7 | f.spouse = spouse 8 | f['3'] = f['1'] + f['2'] 9 | f['5'] = f['3'] - f['4'] 10 | f['9'] = f.rowsum(['6', '7', '8']) 11 | f['10'] = min(10000, f['5'] * 10000.0 / f['9']) 12 | f['11'] = f['8'] * f['10'] / 10000.0 13 | f['12'] = f['7'] * f['10'] / 10000.0 14 | f['13'] = f['11'] + f['12'] 15 | f['14'] = f['3'] - f['13'] 16 | f['15a'] = f['7'] - f['12'] 17 | f['15c'] = f['15a'] - f['15b'] 18 | f['16'] = f['8'] 19 | f['17'] = f['11'] 20 | f['18'] = f['16'] - f['17'] 21 | # TODO: Roth IRA distributions 22 | f.must_file = True 23 | return 24 | 25 | def title(self): 26 | if self.spouse is not None: 27 | return 'Form 8606 [%d]' % self.spouse 28 | else: 29 | return 'Form 8606' 30 | -------------------------------------------------------------------------------- /2020/f8801.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8801(Form): 4 | """Form 8801, Credit for Prior Year Minimum Tax""" 5 | def __init__(f, inputs, f1040, f6251): 6 | super(F8801, f).__init__(inputs) 7 | f['21'] = inputs.get('prior_amt_credit') 8 | if not f['21']: 9 | return 10 | 11 | f.must_file = True 12 | f['22'] = max(0, f1040['16'] + f1040['s2_2'] - \ 13 | (f1040.rowsum(['19', 's3_1', 's3_2', 's3_3', 's3_4', 's3_5', 's3_6']) or 0)) 14 | f['23'] = f6251.get('9') 15 | f['24'] = max(0, f['22'] - f['23']) 16 | f.comment['25'] = 'Minimum tax credit' 17 | f['25'] = min(f['21'], f['24']) 18 | if f['25']: 19 | f6251.must_file = True 20 | f.comment['26'] = 'Credit carryforward to 2021' 21 | f['26'] = f['21'] - f['25'] 22 | 23 | def title(self): 24 | return 'Form 8801 (for 2020 filing)' 25 | -------------------------------------------------------------------------------- /2020/f8959.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8959(Form): 4 | """Form 8959, Additional Medicare Tax""" 5 | THRESHOLDS = [200000, 250000, 125000, 200000, 200000] 6 | def __init__(f, inputs, sse): 7 | super(F8959, f).__init__(inputs) 8 | f['1'] = f.spouseSum(inputs, 'wages_medicare') 9 | # TODO: forms 4137, 8919 10 | f['4'] = f.rowsum(['1', '2', '3']) 11 | if f.get('4'): 12 | f['5'] = f.THRESHOLDS[inputs['status']] 13 | f['6'] = max(0, f['4'] - f['5']) 14 | f['7'] = f['6'] * .009 15 | 16 | if inputs['status'] == FilingStatus.JOINT: 17 | if sse[0].mustFile() or sse[1].mustFile(): 18 | f['8'] = max(0, (sse[0]['6'] or 0) + (sse[1]['6'] or 0)) 19 | else: 20 | if sse.mustFile(): 21 | f['8'] = max(0, sse['6']) 22 | if f.get('8'): 23 | assert(f['8'] >= 400) 24 | f['9'] = f.THRESHOLDS[inputs['status']] 25 | f['10'] = f.get('4') 26 | f['11'] = max(0, f['9'] - f['10']) 27 | f['12'] = max(0, f['8'] - f['11']) 28 | f['13'] = f['12'] * .009 or None 29 | 30 | if f.get('14'): 31 | f['15'] = f.THRESHOLDS[inputs['status']] 32 | f['16'] = max(0, f['14'] - f['15']) 33 | f['17'] = f['16'] * .009 or None 34 | 35 | f['18'] = f.rowsum(['7', '13', '17']) 36 | 37 | f['19'] = f.spouseSum(inputs, 'medicare_withheld') 38 | f['20'] = f.get('1') 39 | f['21'] = f['20'] * .0145 or None 40 | if f['19'] or f['21']: 41 | f['22'] = max(0, f['19'] - f['21']) 42 | f['24'] = f.rowsum(['22', '23']) 43 | 44 | if f.get('18') or f.get('24'): 45 | f.must_file = True 46 | return 47 | 48 | def title(self): 49 | return 'Form 8959' 50 | -------------------------------------------------------------------------------- /2020/f8960.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8960(Form): 4 | """Form 8960, Net Investment Income Tax""" 5 | THRESHOLDS = [200000, 250000, 125000, 200000, 250000] 6 | def __init__(f, inputs, f1040, sched_a): 7 | super(F8960, f).__init__(inputs) 8 | f['1'] = f1040.get('2b') 9 | f['2'] = f1040.get('3b') 10 | # TODO: annuities 11 | f['4a'] = f1040.get('s1_5') 12 | f['4c'] = f.rowsum(['4a', '4b']) 13 | f['5a'] = f1040.rowsum(['7', 's1_4']) 14 | f['5d'] = f.rowsum(['5a', '5b', '5c']) 15 | f['8'] = f.rowsum(['1', '2', '3', '4c', '5d', '6', '7']) 16 | if sched_a: 17 | # This is one example of a "reasonable method allocation" but 18 | # not the only way. 19 | # Note: this only works for state income tax, not sales tax 20 | f['9b'] = f['8'] * sched_a['5a'] / f1040['11'] 21 | f['9d'] = f.rowsum(['9a', '9b', '9c']) 22 | f['11'] = f.rowsum(['9d', '10']) 23 | f['12'] = max(0, f['8'] - f['11']) 24 | f['13'] = f1040['11'] 25 | f['14'] = f.THRESHOLDS[inputs['status']] 26 | f['15'] = max(0, f['13'] - f['14']) 27 | f['16'] = min(f['12'], f['15']) 28 | if f['16']: 29 | f['17'] = f['16'] * .038 30 | f.must_file = True 31 | return 32 | 33 | def title(self): 34 | return 'Form 8960' 35 | -------------------------------------------------------------------------------- /2020/f8995a.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8995A(Form): 4 | """Form 8995A, Qualified Business Income Deduction""" 5 | def __init__(f, inputs, f1040, sched_d): 6 | super(F8995A, f).__init__(inputs) 7 | # TODO: Parts I, II, and III 8 | 9 | f['28'] = inputs.get('section_199A_dividends') 10 | f['30'] = max(0, f['28'] + f['29']) 11 | f['31'] = f['30'] * 0.20 12 | f['32'] = f.rowsum(['27', '31']) 13 | f['33'] = f1040['11'] - f1040['12'] 14 | 15 | if sched_d.mustFile(): 16 | f['34'] = f1040['3a'] + max(0, min(sched_d['15'], sched_d['16'])) 17 | else: 18 | f['34'] = f1040['3a'] + f1040['7'] 19 | 20 | f['35'] = max(0, f['33'] - f['34']) 21 | f['36'] = f['35'] * 0.20 22 | f['37'] = min(f['32'], f['36']) 23 | f.comment['39'] = 'Total QBI deduction' 24 | f['39'] = f.rowsum(['37', '38']) 25 | f.comment['40'] = 'REIT and PTP carryforward' 26 | f['40'] = min(0, f['28'] + f['29']) 27 | 28 | if f['39'] or f['40']: 29 | f.must_file = True 30 | 31 | def title(self): 32 | return 'Form 8995A' 33 | -------------------------------------------------------------------------------- /2021/ca540sp.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class CA540sp(Form): 4 | EXEMPTION_LIMITS = [292763, 390351, 195172, 292763, 390351] 5 | EXEMPTIONS = [73070, 104094, 52044, 78070, 104094] 6 | 7 | def __init__(f, inputs, ca540, ca540sca, f1040, f1040sa): 8 | super(CA540sp, f).__init__(inputs) 9 | if ca540['18'] != ca540sca.STD_DED[inputs['status']]: 10 | f['2'] = min(f1040sa['4'], .025 * f1040['11']) 11 | f['3'] = f1040sa.rowsum(['5b', '5c']) 12 | f['5'] = ca540sca['2_25'] 13 | else: 14 | f['1'] = ca540['18'] 15 | f['14'] = f.rowsum(['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', 16 | '11', '12', '13']) 17 | f['15'] = ca540['19'] 18 | if not f['15']: 19 | f['15'] = ca540['17'] - ca540['18'] 20 | f['16'] = ca540sca.rowsum(['1B_B9b1', '1B_B9b2', '1B_B9b3']) 21 | f['17'] = -max(0, f1040['s1_3']) 22 | if ca540['18'] != ca540sca.STD_DED[inputs['status']]: 23 | f['18'] = -(ca540sca['2_28'] - ca540sca['2_29']) or None 24 | f['19'] = f.rowsum(['14', '15', '16', '17', '18']) 25 | f['21'] = f['19'] - f['20'] 26 | assert(inputs['status'] != FilingStatus.SEPARATE or f['21'] <= 403348) 27 | 28 | f['22'] = f.exemption(inputs) 29 | f['23'] = max(0, f['21'] - f['22']) 30 | f['24'] = f['23'] * .07 31 | f['25'] = ca540['31'] 32 | f['26'] = max(0, f['24'] - f['25']) 33 | if f['26'] or f['21'] > f['22'] and f.rowsum(['4', '7', '8', '9', '10', 34 | '11', '12', '13']): 35 | f.must_file = True 36 | 37 | def exemption(f, inputs): 38 | w = {} 39 | w['1'] = f.EXEMPTIONS[inputs['status']] 40 | w['2'] = f['21'] 41 | w['3'] = f.EXEMPTION_LIMITS[inputs['status']] 42 | w['4'] = max(0, w['2'] - w['3']) 43 | w['5'] = w['4'] * .25 44 | w['6'] = max(0, w['1'] - w['5']) 45 | return w['6'] 46 | 47 | def title(self): 48 | return 'CA 540 Schedule P' 49 | -------------------------------------------------------------------------------- /2021/example_joint_return.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Example of a married couple filing jointly. Each spouse has an income 4 | # of 100k. Also, the first spouse has some investment income and 5 | # business (Schedule C) income. 6 | # 7 | # A balance is owed on the return. 8 | # 9 | # On the state return, a refund is due. 10 | # 11 | 12 | from f1040 import F1040 13 | from ca540 import CA540 14 | from form import FilingStatus 15 | 16 | # Note that some of the quantities below we need to list separately for each 17 | # spouse in an array. Everything else we just need a total. 18 | 19 | inputs = { 20 | 'status': FilingStatus.JOINT, 21 | 'exemptions': 2, 22 | 'qualifying_children': 1, 23 | 'wages': [100000.00, 100000.00], # W2 box 1 24 | 'withholding': 37000.00, # W2 box 2 25 | 'wages_ss': [100000.00, 100000.00], # W2 box 3 26 | 'ss_withheld': [ 6200.00, 6200.00], # W2 box 4 27 | 'wages_medicare': [100000.00, 100000.00], # W2 box 5 28 | 'medicare_withheld': [ 1450.00, 1450.00], # W2 box 6 29 | 'state_withholding': 18000.00, # W2 box 17 30 | 31 | # These are other state tax payments made in the tax year not included in 32 | # 'state_withholding' that are deductible on schedule A: 33 | 'extra_state_tax_payments': 3000.00, 34 | 35 | 'taxable_interest': 1500.00, 36 | 'tax_exempt_interest': 700.00, 37 | 'dividends': 3000.00, 38 | 'qualified_dividends': 2000.00, 39 | 'capital_gain_dist': 500.00, 40 | 'capital_gain_long': 5000.00, 41 | 'business_income': [5000.00, 0.00], 42 | 43 | # Extra items for schedule A 44 | 'F1040sa' : { 45 | '11' : 500, # charitable contributions 46 | }, 47 | } 48 | 49 | f = F1040(inputs) 50 | f.printAllForms() 51 | print('') 52 | ca = CA540(inputs, f) 53 | ca.printAllForms() 54 | -------------------------------------------------------------------------------- /2021/example_joint_return_amt.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Example of a married couple filing jointly. Each spouse has an income 4 | # of 100k. Also, the first spouse has some investment income and 5 | # business (Schedule C) income. 6 | # 7 | # This example increases the amount of state tax paid on Schedule A, causing 8 | # this couple to be paying AMT instead of regular tax. Form 6251 is added. 9 | # 10 | # A balance is owed on the return. 11 | # 12 | # On the state return, a refund is due. 13 | # 14 | 15 | from f1040 import F1040 16 | from ca540 import CA540 17 | from form import FilingStatus 18 | 19 | # Note that some of the quantities below we need to list separately for each 20 | # spouse in an array. Everything else we just need a total. 21 | 22 | inputs = { 23 | 'status': FilingStatus.JOINT, 24 | 'exemptions': 2, 25 | 'wages': [120000.00, 120000.00], # W2 box 1 26 | 'withholding': 42000.00, # W2 box 2 27 | 'wages_ss': [120000.00, 120000.00], # W2 box 3 28 | 'ss_withheld': [ 6200.00, 6200.00], # W2 box 4 29 | 'wages_medicare': [120000.00, 120000.00], # W2 box 5 30 | 'medicare_withheld': [ 1450.00, 1450.00], # W2 box 6 31 | 'state_withholding': 20000.00, # W2 box 17 32 | 33 | # These are other state tax payments made in the tax year not included in 34 | # 'state_withholding' that are deductible on schedule A: 35 | 'extra_state_tax_payments': 8000.00, 36 | 37 | 'taxable_interest': 1500.00, 38 | 'tax_exempt_interest': 700.00, 39 | 'dividends': 3000.00, 40 | 'qualified_dividends': 2000.00, 41 | 'capital_gain_dist': 500.00, 42 | 'capital_gain_long': 5000.00, 43 | 'business_income': [5000.00, 0.00], 44 | 45 | # Extra items for schedule A 46 | 'F1040sa' : { 47 | '11' : 500, # charitable contributions 48 | }, 49 | } 50 | 51 | f = F1040(inputs) 52 | f.printAllForms() 53 | print('') 54 | ca = CA540(inputs, f) 55 | ca.printAllForms() 56 | -------------------------------------------------------------------------------- /2021/example_single.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Example of a single person with wage income of 100k. Also some investment 4 | # income and business (Schedule C) income. 5 | # 6 | 7 | from f1040 import F1040 8 | from ca540 import CA540 9 | from form import FilingStatus 10 | 11 | inputs = { 12 | 'status': FilingStatus.SINGLE, 13 | 'exemptions': 1, 14 | 'wages': 100000.00, # W2 box 1 15 | 'withholding': 20000.00, # W2 box 2 16 | 'wages_ss': 100000.00, # W2 box 3 17 | 'ss_withheld': 6200.00, # W2 box 4 18 | 'wages_medicare': 100000.00, # W2 box 5 19 | 'medicare_withheld': 1450.00, # W2 box 6 20 | 'state_withholding': 10000.00, # W2 box 17 21 | 22 | # These are other state tax payments made in the tax year not included in 23 | # 'state_withholding' that are deductible on schedule A: 24 | 'extra_state_tax_payments': 1000.00, 25 | 26 | 'taxable_interest': 1500.00, 27 | 'tax_exempt_interest': 700.00, 28 | 'dividends': 3000.00, 29 | 'qualified_dividends': 2000.00, 30 | 'capital_gain_dist': 500.00, 31 | 'capital_gain_long': 5000.00, 32 | 'business_income': 5000.00, 33 | 34 | # Extra items for schedule A 35 | 'F1040sa' : { 36 | '11' : 500, # charitable contributions 37 | }, 38 | } 39 | 40 | f = F1040(inputs) 41 | f.printAllForms() 42 | print('') 43 | ca = CA540(inputs, f) 44 | ca.printAllForms() 45 | -------------------------------------------------------------------------------- /2021/f1040sa.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F1040sa(Form): 4 | def __init__(f, inputs, f1040): 5 | super(F1040sa, f).__init__(inputs) 6 | if f['1']: 7 | f['2'] = f1040['11'] 8 | f['3'] = f['2'] * .075 9 | f['4'] = max(0, f['1'] - f['3']) 10 | f['5a'] = inputs['state_withholding'] + \ 11 | inputs.get('extra_state_tax_payments', 0) 12 | f['5d'] = f.rowsum(['5a', '5b', '5c']) 13 | f['5e'] = min(f['5d'], 14 | 5000 if inputs['status'] == FilingStatus.SEPARATE else 10000) 15 | f['7'] = f.rowsum(['5e', '6']) 16 | f['8e'] = f.rowsum(['8a', '8b', '8c', '8d']) 17 | f['10'] = f.rowsum(['8e', '9']) 18 | f['14'] = f.rowsum(['11', '12', '13']) 19 | f['17'] = f.rowsum(['4', '7', '10', '14', '15', '16']) 20 | 21 | def title(self): 22 | return 'Schedule A' 23 | -------------------------------------------------------------------------------- /2021/f1040sd.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F1040sd(Form): 4 | def __init__(f, inputs): 5 | super(F1040sd, f).__init__(inputs) 6 | if 'capital_gain_long' not in inputs \ 7 | and 'capital_gain_short' not in inputs \ 8 | and 'capital_gain_carryover_short' not in inputs \ 9 | and 'capital_gain_carryover_long' not in inputs: 10 | return 11 | f.must_file = True 12 | f['1'] = inputs.get('capital_gain_short') 13 | f['6'] = inputs.get('capital_gain_carryover_short') 14 | f['7'] = f.rowsum(['1', '2', '3', '4', '5', '6']) 15 | f['8'] = inputs.get('capital_gain_long') 16 | f['13'] = inputs.get('capital_gain_dist') 17 | f['14'] = inputs.get('capital_gain_carryover_long') 18 | f['15'] = f.rowsum(['8', '9', '10', '11', '12', '13', '14']) 19 | f['16'] = f.rowsum(['7', '15']) 20 | if f['16'] < 0: 21 | cutoff = -3000 22 | if inputs['status'] == FilingStatus.SEPARATE: 23 | cutoff = -1500 24 | f['21'] = max(f['16'], cutoff) 25 | 26 | # If lines 15 and 16 are both gains and line 18 or 19 has a value: 27 | # Use the Schedule D tax worksheet 28 | # Else if lines 15 and 16 are both gains or you have qualified divs: 29 | # Use the Qualified Dividends and Capital Gain Tax Worksheet 30 | # Else 31 | # Use tax tables 32 | 33 | def title(self): 34 | return 'Schedule D' 35 | -------------------------------------------------------------------------------- /2021/f1040sse.py: -------------------------------------------------------------------------------- 1 | from form import Form 2 | 3 | SS_WAGE_LIMIT = 142800 4 | 5 | # Not implemented: optional methods 6 | class F1040sse(Form): 7 | def __init__(self, inputs): 8 | super(F1040sse, self).__init__(inputs) 9 | self['2'] = inputs.get('business_income') 10 | self['3'] = self.rowsum(['1a', '1b', '2']) 11 | if self['3'] > 0: 12 | self['4a'] = self['3'] * 0.9235 13 | else: 14 | self['4a'] = self['3'] 15 | self['4c'] = self.rowsum(['4a', '4b']) 16 | if self['4c'] < 400: 17 | return 18 | self.must_file = True 19 | self['6'] = self.rowsum(['4c', '5b']) 20 | self['7'] = SS_WAGE_LIMIT 21 | self['8a'] = inputs['wages_ss'] 22 | if self['8a'] < SS_WAGE_LIMIT: 23 | self['8d'] = self.rowsum(['8a', '8b', '8c']) 24 | self['9'] = self['7'] - self['8d'] 25 | if self['9'] <= 0: 26 | self['9'] = 0 27 | self['10'] = 0 28 | else: 29 | self['10'] = min(self['6'], self['9']) * .124 30 | self['11'] = self['6'] * .029 31 | self['12'] = self.rowsum(['10', '11']) 32 | self['13'] = self['12'] * .5 33 | 34 | def title(self): 35 | return 'Schedule SE' 36 | -------------------------------------------------------------------------------- /2021/f8606.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8606(Form): 4 | """Form 8606, Nondeductible IRAs""" 5 | def __init__(f, inputs, spouse): 6 | super(F8606, f).__init__(inputs, init_idx=spouse) 7 | f.spouse = spouse 8 | f.comment['2'] = 'Starting basis in traditional IRAs' 9 | f['3'] = f['1'] + f['2'] 10 | f['5'] = f['3'] - f['4'] 11 | f['9'] = f.rowsum(['6', '7', '8']) 12 | f['10'] = min(10000, f['5'] * 10000.0 / f['9']) 13 | f['11'] = f['8'] * f['10'] / 10000.0 14 | f['12'] = f['7'] * f['10'] / 10000.0 15 | f['13'] = f['11'] + f['12'] 16 | f.comment['14'] = 'Ending basis in traditional IRAs' 17 | f['14'] = f['3'] - f['13'] 18 | f['15a'] = f['7'] - f['12'] 19 | f.comment['15c'] = 'Taxable Distribution' 20 | f['15c'] = f['15a'] - f['15b'] 21 | f['16'] = f['8'] 22 | f['17'] = f['11'] 23 | f.comment['18'] = 'Taxable Conversion' 24 | f['18'] = f['16'] - f['17'] 25 | # TODO: Roth IRA distributions 26 | f.must_file = True 27 | return 28 | 29 | def title(self): 30 | if self.spouse is not None: 31 | return 'Form 8606 [%d]' % self.spouse 32 | else: 33 | return 'Form 8606' 34 | -------------------------------------------------------------------------------- /2021/f8801.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8801(Form): 4 | """Form 8801, Credit for Prior Year Minimum Tax""" 5 | def __init__(f, inputs, f1040, f6251): 6 | super(F8801, f).__init__(inputs) 7 | f['21'] = inputs.get('prior_amt_credit') 8 | if not f['21']: 9 | return 10 | 11 | f.must_file = True 12 | f['22'] = max(0, f1040['16'] + f1040['s2_2'] - \ 13 | (f1040.rowsum(['19', 's3_1', 's3_2', 's3_3', 's3_4', 's3_5', 14 | 's3_6a', 's3_6c', 's3_6d', 's3_6e', 's3_6f', 15 | 's3_6g', 's3_6h', 's3_6i', 's3_6j', 's3_6l', 16 | 's3_6z']) or 0)) 17 | f['23'] = f6251.get('9') 18 | f['24'] = max(0, f['22'] - f['23']) 19 | f.comment['25'] = 'Minimum tax credit' 20 | f['25'] = min(f['21'], f['24']) 21 | if f['25']: 22 | f6251.must_file = True 23 | f.comment['26'] = 'Credit carryforward to 2022' 24 | f['26'] = f['21'] - f['25'] 25 | 26 | def title(self): 27 | return 'Form 8801 (for 2021 filing)' 28 | -------------------------------------------------------------------------------- /2021/f8959.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8959(Form): 4 | """Form 8959, Additional Medicare Tax""" 5 | THRESHOLDS = [200000, 250000, 125000, 200000, 200000] 6 | def __init__(f, inputs, sse): 7 | super(F8959, f).__init__(inputs) 8 | f['1'] = f.spouseSum(inputs, 'wages_medicare') 9 | # TODO: forms 4137, 8919 10 | f['4'] = f.rowsum(['1', '2', '3']) 11 | if f.get('4'): 12 | f['5'] = f.THRESHOLDS[inputs['status']] 13 | f['6'] = max(0, f['4'] - f['5']) 14 | f['7'] = f['6'] * .009 15 | 16 | if inputs['status'] == FilingStatus.JOINT: 17 | if sse[0].mustFile() or sse[1].mustFile(): 18 | f['8'] = max(0, (sse[0]['6'] or 0) + (sse[1]['6'] or 0)) 19 | else: 20 | if sse.mustFile(): 21 | f['8'] = max(0, sse['6']) 22 | if f.get('8'): 23 | assert(f['8'] >= 400) 24 | f['9'] = f.THRESHOLDS[inputs['status']] 25 | f['10'] = f.get('4') 26 | f['11'] = max(0, f['9'] - f['10']) 27 | f['12'] = max(0, f['8'] - f['11']) 28 | f['13'] = f['12'] * .009 or None 29 | 30 | if f.get('14'): 31 | f['15'] = f.THRESHOLDS[inputs['status']] 32 | f['16'] = max(0, f['14'] - f['15']) 33 | f['17'] = f['16'] * .009 or None 34 | 35 | f.comment['18'] = 'Total Additional Medicare Tax' 36 | f['18'] = f.rowsum(['7', '13', '17']) 37 | 38 | f['19'] = f.spouseSum(inputs, 'medicare_withheld') 39 | f['20'] = f.get('1') 40 | f['21'] = f['20'] * .0145 or None 41 | if f['19'] or f['21']: 42 | f['22'] = max(0, f['19'] - f['21']) 43 | 44 | f.comment['24'] = 'Total Additional Medicare Tax withholding' 45 | f['24'] = f.rowsum(['22', '23']) 46 | 47 | if f.get('18') or f.get('24'): 48 | f.must_file = True 49 | return 50 | 51 | def title(self): 52 | return 'Form 8959' 53 | -------------------------------------------------------------------------------- /2021/f8960.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8960(Form): 4 | """Form 8960, Net Investment Income Tax""" 5 | THRESHOLDS = [200000, 250000, 125000, 200000, 250000] 6 | def __init__(f, inputs, f1040, sched_a): 7 | super(F8960, f).__init__(inputs) 8 | f['1'] = f1040.get('2b') 9 | f['2'] = f1040.get('3b') 10 | # TODO: annuities 11 | f['4a'] = f1040.get('s1_5') 12 | f['4c'] = f.rowsum(['4a', '4b']) 13 | f['5a'] = f1040.rowsum(['7', 's1_4']) 14 | f['5d'] = f.rowsum(['5a', '5b', '5c']) 15 | f['8'] = f.rowsum(['1', '2', '3', '4c', '5d', '6', '7']) 16 | if sched_a: 17 | # This is one example of a "reasonable method allocation" but 18 | # not the only way. 19 | # Note: this only works for state income tax, not sales tax 20 | f['9b'] = f['8'] * sched_a['5a'] / f1040['11'] 21 | f['9d'] = f.rowsum(['9a', '9b', '9c']) 22 | f['11'] = f.rowsum(['9d', '10']) 23 | f['12'] = max(0, f['8'] - f['11']) 24 | f['13'] = f1040['11'] 25 | f['14'] = f.THRESHOLDS[inputs['status']] 26 | f['15'] = max(0, f['13'] - f['14']) 27 | f['16'] = min(f['12'], f['15']) 28 | if f['16']: 29 | f['17'] = f['16'] * .038 30 | f.must_file = True 31 | return 32 | 33 | def title(self): 34 | return 'Form 8960' 35 | -------------------------------------------------------------------------------- /2021/f8995a.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8995A(Form): 4 | """Form 8995A, Qualified Business Income Deduction""" 5 | def __init__(f, inputs, f1040, sched_d): 6 | super(F8995A, f).__init__(inputs) 7 | # TODO: Parts I, II, and III 8 | 9 | f['28'] = inputs.get('section_199A_dividends') 10 | f['30'] = max(0, f['28'] + f['29']) 11 | f['31'] = f['30'] * 0.20 12 | f['32'] = f.rowsum(['27', '31']) 13 | f['33'] = f1040['11'] - f1040['12c'] 14 | 15 | if sched_d.mustFile(): 16 | f['34'] = f1040['3a'] + max(0, min(sched_d['15'], sched_d['16'])) 17 | else: 18 | f['34'] = f1040['3a'] + f1040['7'] 19 | 20 | f['35'] = max(0, f['33'] - f['34']) 21 | f['36'] = f['35'] * 0.20 22 | f['37'] = min(f['32'], f['36']) 23 | f.comment['39'] = 'Total QBI deduction' 24 | f['39'] = f.rowsum(['37', '38']) 25 | f.comment['40'] = 'REIT and PTP carryforward' 26 | f['40'] = min(0, f['28'] + f['29']) 27 | 28 | if f['39'] or f['40']: 29 | f.must_file = True 30 | 31 | def title(self): 32 | return 'Form 8995A' 33 | -------------------------------------------------------------------------------- /2022/ca540sp.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class CA540sp(Form): 4 | EXEMPTION_LIMITS = [317062, 422750, 211371, 317062, 422750] 5 | EXEMPTIONS = [84550, 112734, 56364, 84550, 112734] 6 | 7 | def __init__(f, inputs, ca540, ca540sca, f1040, f1040sa): 8 | super(CA540sp, f).__init__(inputs) 9 | if ca540['18'] != ca540sca.STD_DED[inputs['status']]: 10 | f['2'] = min(f1040sa['4'], .025 * f1040['11']) 11 | f['3'] = f1040sa.rowsum(['5b', '5c']) 12 | f['5'] = ca540sca['2_25'] 13 | else: 14 | f['1'] = ca540['18'] 15 | f['14'] = f.rowsum(['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', 16 | '11', '12', '13']) 17 | f['15'] = ca540['19'] 18 | if not f['15']: 19 | f['15'] = ca540['17'] - ca540['18'] 20 | f['16'] = ca540sca.rowsum(['1B_B9b1', '1B_B9b2', '1B_B9b3']) 21 | f['17'] = -max(0, f1040['s1_3']) 22 | if ca540['18'] != ca540sca.STD_DED[inputs['status']]: 23 | f['18'] = -(ca540sca['2_28'] - ca540sca['2_29']) or None 24 | f['19'] = f.rowsum(['14', '15', '16', '17', '18']) 25 | f['21'] = f['19'] - f['20'] 26 | assert(inputs['status'] != FilingStatus.SEPARATE or f['21'] <= 436827) 27 | 28 | f['22'] = f.exemption(inputs) 29 | f['23'] = max(0, f['21'] - f['22']) 30 | f['24'] = f['23'] * .07 31 | f['25'] = ca540['31'] 32 | f['26'] = max(0, f['24'] - f['25']) 33 | if f['26'] or f['21'] > f['22'] and f.rowsum(['4', '7', '8', '9', '10', 34 | '11', '12', '13']): 35 | f.must_file = True 36 | 37 | def exemption(f, inputs): 38 | w = {} 39 | w['1'] = f.EXEMPTIONS[inputs['status']] 40 | w['2'] = f['21'] 41 | w['3'] = f.EXEMPTION_LIMITS[inputs['status']] 42 | w['4'] = max(0, w['2'] - w['3']) 43 | w['5'] = w['4'] * .25 44 | w['6'] = max(0, w['1'] - w['5']) 45 | return w['6'] 46 | 47 | def title(self): 48 | return 'CA 540 Schedule P' 49 | -------------------------------------------------------------------------------- /2022/example_joint_return.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Example of a married couple filing jointly. Each spouse has an income 4 | # of 100k. Also, the first spouse has some investment income and 5 | # business (Schedule C) income. 6 | # 7 | # A balance is owed on the return. 8 | # 9 | # On the state return, a refund is due. 10 | # 11 | 12 | from f1040 import F1040 13 | from ca540 import CA540 14 | from form import FilingStatus 15 | 16 | # Note that some of the quantities below we need to list separately for each 17 | # spouse in an array. Everything else we just need a total. 18 | 19 | inputs = { 20 | 'status': FilingStatus.JOINT, 21 | 'exemptions': 2, 22 | 'qualifying_children': 1, 23 | 'wages': [100000.00, 100000.00], # W2 box 1 24 | 'withholding': 37000.00, # W2 box 2 25 | 'wages_ss': [100000.00, 100000.00], # W2 box 3 26 | 'ss_withheld': [ 6200.00, 6200.00], # W2 box 4 27 | 'wages_medicare': [100000.00, 100000.00], # W2 box 5 28 | 'medicare_withheld': [ 1450.00, 1450.00], # W2 box 6 29 | 'state_withholding': 18000.00, # W2 box 17 30 | 31 | # These are other state tax payments made in the tax year not included in 32 | # 'state_withholding' that are deductible on schedule A: 33 | 'extra_state_tax_payments': 3000.00, 34 | 35 | 'taxable_interest': 1500.00, 36 | 'tax_exempt_interest': 700.00, 37 | 'dividends': 3000.00, 38 | 'qualified_dividends': 2000.00, 39 | 'capital_gain_dist': 500.00, 40 | 'capital_gain_long': 5000.00, 41 | 'business_income': [5000.00, 0.00], 42 | 43 | # Extra items for schedule A 44 | 'F1040sa' : { 45 | '11' : 500, # charitable contributions 46 | }, 47 | } 48 | 49 | f = F1040(inputs) 50 | f.printAllForms() 51 | print('') 52 | ca = CA540(inputs, f) 53 | ca.printAllForms() 54 | -------------------------------------------------------------------------------- /2022/example_joint_return_amt.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Example of a married couple filing jointly. Each spouse has an income 4 | # of 100k. Also, the first spouse has some investment income and 5 | # business (Schedule C) income. 6 | # 7 | # This example increases the amount of state tax paid on Schedule A, causing 8 | # this couple to be paying AMT instead of regular tax. Form 6251 is added. 9 | # 10 | # A balance is owed on the return. 11 | # 12 | # On the state return, a refund is due. 13 | # 14 | 15 | from f1040 import F1040 16 | from ca540 import CA540 17 | from form import FilingStatus 18 | 19 | # Note that some of the quantities below we need to list separately for each 20 | # spouse in an array. Everything else we just need a total. 21 | 22 | inputs = { 23 | 'status': FilingStatus.JOINT, 24 | 'exemptions': 2, 25 | 'wages': [120000.00, 120000.00], # W2 box 1 26 | 'withholding': 42000.00, # W2 box 2 27 | 'wages_ss': [120000.00, 120000.00], # W2 box 3 28 | 'ss_withheld': [ 6200.00, 6200.00], # W2 box 4 29 | 'wages_medicare': [120000.00, 120000.00], # W2 box 5 30 | 'medicare_withheld': [ 1450.00, 1450.00], # W2 box 6 31 | 'state_withholding': 20000.00, # W2 box 17 32 | 33 | # These are other state tax payments made in the tax year not included in 34 | # 'state_withholding' that are deductible on schedule A: 35 | 'extra_state_tax_payments': 8000.00, 36 | 37 | 'taxable_interest': 1500.00, 38 | 'tax_exempt_interest': 700.00, 39 | 'dividends': 3000.00, 40 | 'qualified_dividends': 2000.00, 41 | 'capital_gain_dist': 500.00, 42 | 'capital_gain_long': 5000.00, 43 | 'business_income': [5000.00, 0.00], 44 | 45 | # Extra items for schedule A 46 | 'F1040sa' : { 47 | '11' : 500, # charitable contributions 48 | }, 49 | } 50 | 51 | f = F1040(inputs) 52 | f.printAllForms() 53 | print('') 54 | ca = CA540(inputs, f) 55 | ca.printAllForms() 56 | -------------------------------------------------------------------------------- /2022/example_single.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Example of a single person with wage income of 100k. Also some investment 4 | # income and business (Schedule C) income. 5 | # 6 | 7 | from f1040 import F1040 8 | from ca540 import CA540 9 | from form import FilingStatus 10 | 11 | inputs = { 12 | 'status': FilingStatus.SINGLE, 13 | 'exemptions': 1, 14 | 'wages': 100000.00, # W2 box 1 15 | 'withholding': 20000.00, # W2 box 2 16 | 'wages_ss': 100000.00, # W2 box 3 17 | 'ss_withheld': 6200.00, # W2 box 4 18 | 'wages_medicare': 100000.00, # W2 box 5 19 | 'medicare_withheld': 1450.00, # W2 box 6 20 | 'state_withholding': 10000.00, # W2 box 17 21 | 22 | # These are other state tax payments made in the tax year not included in 23 | # 'state_withholding' that are deductible on schedule A: 24 | 'extra_state_tax_payments': 1000.00, 25 | 26 | 'taxable_interest': 1500.00, 27 | 'tax_exempt_interest': 700.00, 28 | 'dividends': 3000.00, 29 | 'qualified_dividends': 2000.00, 30 | 'capital_gain_dist': 500.00, 31 | 'capital_gain_long': 5000.00, 32 | 'business_income': 5000.00, 33 | 34 | # Extra items for schedule A 35 | 'F1040sa' : { 36 | '11' : 500, # charitable contributions 37 | }, 38 | } 39 | 40 | f = F1040(inputs) 41 | f.printAllForms() 42 | print('') 43 | ca = CA540(inputs, f) 44 | ca.printAllForms() 45 | -------------------------------------------------------------------------------- /2022/f1040sa.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F1040sa(Form): 4 | def __init__(f, inputs, f1040): 5 | super(F1040sa, f).__init__(inputs) 6 | if f['1']: 7 | f['2'] = f1040['11'] 8 | f['3'] = f['2'] * .075 9 | f['4'] = max(0, f['1'] - f['3']) 10 | f['5a'] = inputs['state_withholding'] + \ 11 | inputs.get('extra_state_tax_payments', 0) 12 | f['5d'] = f.rowsum(['5a', '5b', '5c']) 13 | f['5e'] = min(f['5d'], 14 | 5000 if inputs['status'] == FilingStatus.SEPARATE else 10000) 15 | f['7'] = f.rowsum(['5e', '6']) 16 | f['8e'] = f.rowsum(['8a', '8b', '8c', '8d']) 17 | f['10'] = f.rowsum(['8e', '9']) 18 | f['14'] = f.rowsum(['11', '12', '13']) 19 | f['17'] = f.rowsum(['4', '7', '10', '14', '15', '16']) 20 | 21 | def title(self): 22 | return 'Schedule A' 23 | -------------------------------------------------------------------------------- /2022/f1040sd.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F1040sd(Form): 4 | def __init__(f, inputs): 5 | super(F1040sd, f).__init__(inputs) 6 | if 'capital_gain_long' not in inputs \ 7 | and 'capital_gain_short' not in inputs \ 8 | and 'capital_gain_carryover_short' not in inputs \ 9 | and 'capital_gain_carryover_long' not in inputs: 10 | return 11 | f.must_file = True 12 | f['1'] = inputs.get('capital_gain_short') 13 | f['6'] = inputs.get('capital_gain_carryover_short') 14 | f['7'] = f.rowsum(['1', '2', '3', '4', '5', '6']) 15 | f['8'] = inputs.get('capital_gain_long') 16 | f['13'] = inputs.get('capital_gain_dist') 17 | f['14'] = inputs.get('capital_gain_carryover_long') 18 | f['15'] = f.rowsum(['8', '9', '10', '11', '12', '13', '14']) 19 | f['16'] = f.rowsum(['7', '15']) 20 | if f['16'] < 0: 21 | cutoff = -3000 22 | if inputs['status'] == FilingStatus.SEPARATE: 23 | cutoff = -1500 24 | f['21'] = max(f['16'], cutoff) 25 | 26 | # If lines 15 and 16 are both gains and line 18 or 19 has a value: 27 | # Use the Schedule D tax worksheet 28 | # Else if lines 15 and 16 are both gains or you have qualified divs: 29 | # Use the Qualified Dividends and Capital Gain Tax Worksheet 30 | # Else 31 | # Use tax tables 32 | 33 | def title(self): 34 | return 'Schedule D' 35 | -------------------------------------------------------------------------------- /2022/f1040sse.py: -------------------------------------------------------------------------------- 1 | from form import Form 2 | 3 | SS_WAGE_LIMIT = 147000 4 | 5 | # Not implemented: optional methods 6 | class F1040sse(Form): 7 | def __init__(self, inputs): 8 | super(F1040sse, self).__init__(inputs) 9 | self['2'] = inputs.get('business_income') 10 | self['3'] = self.rowsum(['1a', '1b', '2']) 11 | if self['3'] > 0: 12 | self['4a'] = self['3'] * 0.9235 13 | else: 14 | self['4a'] = self['3'] 15 | self['4c'] = self.rowsum(['4a', '4b']) 16 | if self['4c'] < 400: 17 | return 18 | self.must_file = True 19 | self['6'] = self.rowsum(['4c', '5b']) 20 | self['7'] = SS_WAGE_LIMIT 21 | self['8a'] = inputs['wages_ss'] 22 | if self['8a'] < SS_WAGE_LIMIT: 23 | self['8d'] = self.rowsum(['8a', '8b', '8c']) 24 | self['9'] = self['7'] - self['8d'] 25 | if self['9'] <= 0: 26 | self['9'] = 0 27 | self['10'] = 0 28 | else: 29 | self['10'] = min(self['6'], self['9']) * .124 30 | self['11'] = self['6'] * .029 31 | self['12'] = self.rowsum(['10', '11']) 32 | self['13'] = self['12'] * .5 33 | 34 | def title(self): 35 | return 'Schedule SE' 36 | -------------------------------------------------------------------------------- /2022/f8606.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8606(Form): 4 | """Form 8606, Nondeductible IRAs""" 5 | def __init__(f, inputs, spouse): 6 | super(F8606, f).__init__(inputs, init_idx=spouse) 7 | f.spouse = spouse 8 | f.comment['2'] = 'Starting basis in traditional IRAs' 9 | f['3'] = f['1'] + f['2'] 10 | f['5'] = f['3'] - f['4'] 11 | f['9'] = f.rowsum(['6', '7', '8']) 12 | f['10'] = min(10000, f['5'] * 10000.0 / f['9']) 13 | f['11'] = f['8'] * f['10'] / 10000.0 14 | f['12'] = f['7'] * f['10'] / 10000.0 15 | f['13'] = f['11'] + f['12'] 16 | f.comment['14'] = 'Ending basis in traditional IRAs' 17 | f['14'] = f['3'] - f['13'] 18 | f['15a'] = f['7'] - f['12'] 19 | f.comment['15c'] = 'Taxable Distribution' 20 | f['15c'] = f['15a'] - f['15b'] 21 | f['16'] = f['8'] 22 | f['17'] = f['11'] 23 | f.comment['18'] = 'Taxable Conversion' 24 | f['18'] = f['16'] - f['17'] 25 | # TODO: Roth IRA distributions 26 | f.must_file = True 27 | return 28 | 29 | def title(self): 30 | if self.spouse is not None: 31 | return 'Form 8606 [%d]' % self.spouse 32 | else: 33 | return 'Form 8606' 34 | -------------------------------------------------------------------------------- /2022/f8801.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8801(Form): 4 | """Form 8801, Credit for Prior Year Minimum Tax""" 5 | def __init__(f, inputs, f1040, f6251): 6 | super(F8801, f).__init__(inputs) 7 | f['21'] = inputs.get('prior_amt_credit') 8 | if not f['21']: 9 | return 10 | 11 | f.must_file = True 12 | f['22'] = max(0, f1040['16'] + f1040['s2_2'] - \ 13 | (f1040.rowsum(['19', 's3_1', 's3_2', 's3_3', 's3_4', 's3_5', 14 | 's3_6a', 's3_6c', 's3_6d', 's3_6e', 's3_6f', 15 | 's3_6g', 's3_6h', 's3_6i', 's3_6j', 's3_6l', 16 | 's3_6z']) or 0)) 17 | f['23'] = f6251.get('9') 18 | f['24'] = max(0, f['22'] - f['23']) 19 | f.comment['25'] = 'Minimum tax credit' 20 | f['25'] = min(f['21'], f['24']) 21 | if f['25']: 22 | f6251.must_file = True 23 | f.comment['26'] = 'Credit carryforward to 2023' 24 | f['26'] = f['21'] - f['25'] 25 | 26 | def title(self): 27 | return 'Form 8801 (for 2022 filing)' 28 | -------------------------------------------------------------------------------- /2022/f8812.py: -------------------------------------------------------------------------------- 1 | import math 2 | from form import Form, FilingStatus 3 | 4 | class F8812(Form): 5 | """Schedule 8812, Credits for Qualifying Children and Other Dependents""" 6 | def __init__(f, inputs, f1040): 7 | super(F8812, f).__init__(inputs) 8 | if not inputs.get('qualifying_children'): 9 | return 10 | f['1'] = f1040['11'] 11 | f['2d'] = f.rowsum(['2a', '2b', '2c']) 12 | f['3'] = f.rowsum(['1', '2d']) 13 | f['4'] = inputs.get('qualifying_children') 14 | f['5'] = f['4'] * 2000 15 | f['7'] = f['6'] * 500 16 | f['8'] = f.rowsum(['5', '7']) 17 | if inputs['status'] == FilingStatus.JOINT: 18 | f['9'] = 400000 19 | else: 20 | f['9'] = 200000 21 | f['10'] = math.ceil(max(0, f['3'] - f['9']) / 1000.0) * 1000 22 | f['11'] = f['10'] * .05 23 | f['12'] = max(0, f['8'] - f['11']) 24 | 25 | if f['12']: 26 | f['13'] = f1040['18'] - (f1040.rowsum(['s3_1', 's3_2', 's3_3', 27 | 's3_4', 's3_6d', 's3_6e', 's3_6f', 's3_6l']) or 0) 28 | else: 29 | f['13'] = 0 30 | f['14'] = min(f['12'], f['13']) 31 | if f['14']: 32 | f.must_file = True 33 | 34 | if f['12'] > f['14']: 35 | raise RuntimeError('TODO: additional child tax credit') 36 | 37 | def title(self): 38 | return 'Schedule 8812' 39 | -------------------------------------------------------------------------------- /2022/f8959.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8959(Form): 4 | """Form 8959, Additional Medicare Tax""" 5 | THRESHOLDS = [200000, 250000, 125000, 200000, 200000] 6 | def __init__(f, inputs, sse): 7 | super(F8959, f).__init__(inputs) 8 | f['1'] = f.spouseSum(inputs, 'wages_medicare') 9 | # TODO: forms 4137, 8919 10 | f['4'] = f.rowsum(['1', '2', '3']) 11 | if f.get('4'): 12 | f['5'] = f.THRESHOLDS[inputs['status']] 13 | f['6'] = max(0, f['4'] - f['5']) 14 | f['7'] = f['6'] * .009 15 | 16 | if inputs['status'] == FilingStatus.JOINT: 17 | if sse[0].mustFile() or sse[1].mustFile(): 18 | f['8'] = max(0, (sse[0]['6'] or 0) + (sse[1]['6'] or 0)) 19 | else: 20 | if sse.mustFile(): 21 | f['8'] = max(0, sse['6']) 22 | if f.get('8'): 23 | assert(f['8'] >= 400) 24 | f['9'] = f.THRESHOLDS[inputs['status']] 25 | f['10'] = f.get('4') 26 | f['11'] = max(0, f['9'] - f['10']) 27 | f['12'] = max(0, f['8'] - f['11']) 28 | f['13'] = f['12'] * .009 or None 29 | 30 | if f.get('14'): 31 | f['15'] = f.THRESHOLDS[inputs['status']] 32 | f['16'] = max(0, f['14'] - f['15']) 33 | f['17'] = f['16'] * .009 or None 34 | 35 | f.comment['18'] = 'Total Additional Medicare Tax' 36 | f['18'] = f.rowsum(['7', '13', '17']) 37 | 38 | f['19'] = f.spouseSum(inputs, 'medicare_withheld') 39 | f['20'] = f.get('1') 40 | f['21'] = f['20'] * .0145 or None 41 | if f['19'] or f['21']: 42 | f['22'] = max(0, f['19'] - f['21']) 43 | 44 | f.comment['24'] = 'Total Additional Medicare Tax withholding' 45 | f['24'] = f.rowsum(['22', '23']) 46 | 47 | if f.get('18') or f.get('24'): 48 | f.must_file = True 49 | return 50 | 51 | def title(self): 52 | return 'Form 8959' 53 | -------------------------------------------------------------------------------- /2022/f8960.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8960(Form): 4 | """Form 8960, Net Investment Income Tax""" 5 | THRESHOLDS = [200000, 250000, 125000, 200000, 250000] 6 | def __init__(f, inputs, f1040, sched_a): 7 | super(F8960, f).__init__(inputs) 8 | f['1'] = f1040.get('2b') 9 | f['2'] = f1040.get('3b') 10 | # TODO: annuities 11 | f['4a'] = f1040.rowsum(['s1_3', 's1_5']) 12 | f['4c'] = f.rowsum(['4a', '4b']) 13 | f['5a'] = f1040.rowsum(['7', 's1_4']) 14 | f['5d'] = f.rowsum(['5a', '5b', '5c']) 15 | f['8'] = f.rowsum(['1', '2', '3', '4c', '5d', '6', '7']) 16 | if sched_a: 17 | # This is one example of a "reasonable method allocation" but 18 | # not the only way. 19 | # Note: this only works for state income tax, not sales tax 20 | f['9b'] = f['8'] * sched_a['5a'] / f1040['11'] 21 | f['9d'] = f.rowsum(['9a', '9b', '9c']) 22 | f['11'] = f.rowsum(['9d', '10']) 23 | f['12'] = max(0, f['8'] - f['11']) 24 | f['13'] = f1040['11'] 25 | f['14'] = f.THRESHOLDS[inputs['status']] 26 | f['15'] = max(0, f['13'] - f['14']) 27 | f['16'] = min(f['12'], f['15']) 28 | if f['16']: 29 | f['17'] = f['16'] * .038 30 | f.must_file = True 31 | 32 | def title(self): 33 | return 'Form 8960' 34 | -------------------------------------------------------------------------------- /2022/f8995a.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8995A(Form): 4 | """Form 8995A, Qualified Business Income Deduction""" 5 | def __init__(f, inputs, f1040, sched_d): 6 | super(F8995A, f).__init__(inputs) 7 | # TODO: Parts I, II, and III 8 | 9 | f['28'] = inputs.get('section_199A_dividends') 10 | f['30'] = max(0, f['28'] + f['29']) 11 | f['31'] = f['30'] * 0.20 12 | f['32'] = f.rowsum(['27', '31']) 13 | f['33'] = f1040['11'] - f1040['12'] 14 | 15 | if sched_d.mustFile(): 16 | f['34'] = f1040['3a'] + max(0, min(sched_d['15'], sched_d['16'])) 17 | else: 18 | f['34'] = f1040['3a'] + f1040['7'] 19 | 20 | f['35'] = max(0, f['33'] - f['34']) 21 | f['36'] = f['35'] * 0.20 22 | f['37'] = min(f['32'], f['36']) 23 | f.comment['39'] = 'Total QBI deduction' 24 | f['39'] = f.rowsum(['37', '38']) 25 | f.comment['40'] = 'REIT and PTP carryforward' 26 | f['40'] = min(0, f['28'] + f['29']) 27 | 28 | if f['39'] or f['40']: 29 | f.must_file = True 30 | 31 | def title(self): 32 | return 'Form 8995A' 33 | -------------------------------------------------------------------------------- /2023/ca540sp.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class CA540sp(Form): 4 | EXEMPTION_LIMITS = [326891, 435855, 217924, 326891, 435855] 5 | EXEMPTIONS = [87171, 116229, 58111, 87171, 116229] 6 | 7 | def __init__(f, inputs, ca540, ca540sca, f1040, f1040sa): 8 | super(CA540sp, f).__init__(inputs) 9 | if ca540['18'] != ca540sca.STD_DED[inputs['status']]: 10 | f['2'] = min(f1040sa['4'], .025 * f1040['11']) 11 | f['3'] = f1040sa.rowsum(['5b', '5c']) 12 | f['5'] = ca540sca['2_25'] 13 | else: 14 | f['1'] = ca540['18'] 15 | f['14'] = f.rowsum(['[1-9]', '1[0-3]']) 16 | f['15'] = ca540['19'] 17 | if not f['15']: 18 | f['15'] = ca540['17'] - ca540['18'] 19 | f['16'] = ca540sca.rowsum(['1B_B9b1', '1B_B9b2', '1B_B9b3']) 20 | f['17'] = -max(0, f1040['s1_3']) 21 | if ca540['18'] != ca540sca.STD_DED[inputs['status']]: 22 | f['18'] = -(ca540sca['2_28'] - ca540sca['2_29']) or None 23 | f['19'] = f.rowsum(['1[4-8]']) 24 | f['21'] = f['19'] - f['20'] 25 | assert(inputs['status'] != FilingStatus.SEPARATE or f['21'] <= 450368) 26 | 27 | f['22'] = f.exemption(inputs) 28 | f['23'] = max(0, f['21'] - f['22']) 29 | f['24'] = f['23'] * .07 30 | f['25'] = ca540['31'] 31 | f['26'] = max(0, f['24'] - f['25']) 32 | if f['26'] or f['21'] > f['22'] and f.rowsum(['4', '[7-9]', '1[0-3]']): 33 | f.must_file = True 34 | 35 | def exemption(f, inputs): 36 | w = {} 37 | w['1'] = f.EXEMPTIONS[inputs['status']] 38 | w['2'] = f['21'] 39 | w['3'] = f.EXEMPTION_LIMITS[inputs['status']] 40 | w['4'] = max(0, w['2'] - w['3']) 41 | w['5'] = w['4'] * .25 42 | w['6'] = max(0, w['1'] - w['5']) 43 | return w['6'] 44 | 45 | def title(self): 46 | return 'CA 540 Schedule P' 47 | -------------------------------------------------------------------------------- /2023/example_joint_return.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Example of a married couple filing jointly. Each spouse has an income 4 | # of 100k. Also, the first spouse has some investment income and 5 | # business (Schedule C) income. 6 | # 7 | # Refunds are due on both returns. 8 | # 9 | 10 | from f1040 import F1040 11 | from ca540 import CA540 12 | from form import FilingStatus 13 | 14 | # Note that some of the quantities below we need to list separately for each 15 | # spouse in an array. Everything else we just need a total. 16 | 17 | inputs = { 18 | 'status': FilingStatus.JOINT, 19 | 'exemptions': 2, 20 | 'qualifying_children': 1, 21 | 'wages': [100000.00, 100000.00], # W2 box 1 22 | 'withholding': 37000.00, # W2 box 2 23 | 'wages_ss': [100000.00, 100000.00], # W2 box 3 24 | 'ss_withheld': [ 6200.00, 6200.00], # W2 box 4 25 | 'wages_medicare': [100000.00, 100000.00], # W2 box 5 26 | 'medicare_withheld': [ 1450.00, 1450.00], # W2 box 6 27 | 'state_withholding': 18000.00, # W2 box 17 28 | 29 | # These are other state tax payments made in the tax year not included in 30 | # 'state_withholding' that are deductible on schedule A: 31 | 'extra_state_tax_payments': 3000.00, 32 | 33 | 'taxable_interest': 1500.00, 34 | 'tax_exempt_interest': 700.00, 35 | 'dividends': 3000.00, 36 | 'qualified_dividends': 2000.00, 37 | 'capital_gain_dist': 500.00, 38 | 'capital_gain_long': 5000.00, 39 | 'business_income': [5000.00, 0.00], 40 | 41 | # Extra items for schedule A 42 | 'F1040sa' : { 43 | '11' : 500, # charitable contributions 44 | }, 45 | } 46 | 47 | f = F1040(inputs) 48 | f.printAllForms() 49 | print('') 50 | ca = CA540(inputs, f) 51 | ca.printAllForms() 52 | -------------------------------------------------------------------------------- /2023/example_single.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Example of a single person with wage income of 100k. Also some investment 4 | # income and business (Schedule C) income. 5 | # 6 | 7 | from f1040 import F1040 8 | from ca540 import CA540 9 | from form import FilingStatus 10 | 11 | inputs = { 12 | 'status': FilingStatus.SINGLE, 13 | 'exemptions': 1, 14 | 'wages': 100000.00, # W2 box 1 15 | 'withholding': 20000.00, # W2 box 2 16 | 'wages_ss': 100000.00, # W2 box 3 17 | 'ss_withheld': 6200.00, # W2 box 4 18 | 'wages_medicare': 100000.00, # W2 box 5 19 | 'medicare_withheld': 1450.00, # W2 box 6 20 | 'state_withholding': 10000.00, # W2 box 17 21 | 22 | # These are other state tax payments made in the tax year not included in 23 | # 'state_withholding' that are deductible on schedule A: 24 | 'extra_state_tax_payments': 1000.00, 25 | 26 | 'taxable_interest': 1500.00, 27 | 'tax_exempt_interest': 700.00, 28 | 'dividends': 3000.00, 29 | 'qualified_dividends': 2000.00, 30 | 'capital_gain_dist': 500.00, 31 | 'capital_gain_long': 5000.00, 32 | 'business_income': 5000.00, 33 | 34 | # Extra items for schedule A 35 | 'F1040sa' : { 36 | '11' : 500, # charitable contributions 37 | }, 38 | } 39 | 40 | f = F1040(inputs) 41 | f.printAllForms() 42 | print('') 43 | ca = CA540(inputs, f) 44 | ca.printAllForms() 45 | -------------------------------------------------------------------------------- /2023/f1040sa.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F1040sa(Form): 4 | def __init__(f, inputs, f1040): 5 | super(F1040sa, f).__init__(inputs) 6 | if f['1']: 7 | f['2'] = f1040['11'] 8 | f['3'] = f['2'] * .075 9 | f['4'] = max(0, f['1'] - f['3']) 10 | f['5a'] = inputs['state_withholding'] + \ 11 | inputs.get('extra_state_tax_payments', 0) 12 | f['5d'] = f.rowsum(['5a', '5b', '5c']) 13 | f['5e'] = min(f['5d'], 14 | 5000 if inputs['status'] == FilingStatus.SEPARATE else 10000) 15 | f['7'] = f.rowsum(['5e', '6']) 16 | f['8e'] = f.rowsum(['8a', '8b', '8c', '8d']) 17 | f['10'] = f.rowsum(['8e', '9']) 18 | f['14'] = f.rowsum(['11', '12', '13']) 19 | f['17'] = f.rowsum(['4', '7', '10', '14', '15', '16']) 20 | 21 | def title(self): 22 | return 'Schedule A' 23 | -------------------------------------------------------------------------------- /2023/f1040sd.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F1040sd(Form): 4 | def __init__(f, inputs): 5 | super(F1040sd, f).__init__(inputs) 6 | if 'capital_gain_long' not in inputs \ 7 | and 'capital_gain_short' not in inputs \ 8 | and 'capital_gain_carryover_short' not in inputs \ 9 | and 'capital_gain_carryover_long' not in inputs: 10 | return 11 | f.must_file = True 12 | f['1'] = inputs.get('capital_gain_short') 13 | f['6'] = inputs.get('capital_gain_carryover_short') 14 | f['7'] = f.rowsum(['1', '2', '3', '4', '5', '6']) 15 | f['8'] = inputs.get('capital_gain_long') 16 | f['13'] = inputs.get('capital_gain_dist') 17 | f['14'] = inputs.get('capital_gain_carryover_long') 18 | f['15'] = f.rowsum(['8', '9', '10', '11', '12', '13', '14']) 19 | f['16'] = f.rowsum(['7', '15']) 20 | if f['16'] < 0: 21 | cutoff = -3000 22 | if inputs['status'] == FilingStatus.SEPARATE: 23 | cutoff = -1500 24 | f['21'] = max(f['16'], cutoff) 25 | 26 | # If lines 15 and 16 are both gains and line 18 or 19 has a value: 27 | # Use the Schedule D tax worksheet 28 | # Else if lines 15 and 16 are both gains or you have qualified divs: 29 | # Use the Qualified Dividends and Capital Gain Tax Worksheet 30 | # Else 31 | # Use tax tables 32 | 33 | def title(self): 34 | return 'Schedule D' 35 | -------------------------------------------------------------------------------- /2023/f1040sse.py: -------------------------------------------------------------------------------- 1 | from form import Form 2 | 3 | SS_WAGE_LIMIT = 160200 4 | 5 | # Not implemented: optional methods 6 | class F1040sse(Form): 7 | def __init__(self, inputs): 8 | super(F1040sse, self).__init__(inputs) 9 | self['2'] = inputs.get('business_income') 10 | self['3'] = self.rowsum(['1a', '1b', '2']) 11 | if self['3'] > 0: 12 | self['4a'] = self['3'] * 0.9235 13 | else: 14 | self['4a'] = self['3'] 15 | self['4c'] = self.rowsum(['4a', '4b']) 16 | if self['4c'] < 400: 17 | return 18 | self.must_file = True 19 | self['6'] = self.rowsum(['4c', '5b']) 20 | self['7'] = SS_WAGE_LIMIT 21 | self['8a'] = inputs['wages_ss'] 22 | if self['8a'] < SS_WAGE_LIMIT: 23 | self['8d'] = self.rowsum(['8a', '8b', '8c']) 24 | self['9'] = self['7'] - self['8d'] 25 | if self['9'] <= 0: 26 | self['9'] = 0 27 | self['10'] = 0 28 | else: 29 | self['10'] = min(self['6'], self['9']) * .124 30 | self['11'] = self['6'] * .029 31 | self['12'] = self.rowsum(['10', '11']) 32 | self['13'] = self['12'] * .5 33 | 34 | def title(self): 35 | return 'Schedule SE' 36 | -------------------------------------------------------------------------------- /2023/f8606.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8606(Form): 4 | """Form 8606, Nondeductible IRAs""" 5 | def __init__(f, inputs, spouse): 6 | super(F8606, f).__init__(inputs, init_idx=spouse) 7 | f.spouse = spouse 8 | f.comment['2'] = 'Starting basis in traditional IRAs' 9 | f['3'] = f['1'] + f['2'] 10 | f['5'] = f['3'] - f['4'] 11 | f['9'] = f.rowsum(['6', '7', '8']) 12 | f['10'] = min(10000, f['5'] * 10000.0 / f['9']) 13 | f['11'] = f['8'] * f['10'] / 10000.0 14 | f['12'] = f['7'] * f['10'] / 10000.0 15 | f['13'] = f['11'] + f['12'] 16 | f.comment['14'] = 'Ending basis in traditional IRAs' 17 | f['14'] = f['3'] - f['13'] 18 | f['15a'] = f['7'] - f['12'] 19 | f.comment['15c'] = 'Taxable Distribution' 20 | f['15c'] = f['15a'] - f['15b'] 21 | f['16'] = f['8'] 22 | f['17'] = f['11'] 23 | f.comment['18'] = 'Taxable Conversion' 24 | f['18'] = f['16'] - f['17'] 25 | # TODO: Roth IRA distributions 26 | f.must_file = True 27 | return 28 | 29 | def title(self): 30 | if self.spouse is not None: 31 | return 'Form 8606 [%d]' % self.spouse 32 | else: 33 | return 'Form 8606' 34 | -------------------------------------------------------------------------------- /2023/f8801.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8801(Form): 4 | """Form 8801, Credit for Prior Year Minimum Tax""" 5 | def __init__(f, inputs, f1040, f6251): 6 | super(F8801, f).__init__(inputs) 7 | f['21'] = inputs.get('prior_amt_credit') 8 | if not f['21']: 9 | return 10 | 11 | f.must_file = True 12 | f['22'] = max(0, f1040['16'] + f1040['s2_2'] - \ 13 | (f1040.rowsum(['19', 's3_[1-4]', 's3_5[a-b]', 's3_6[acdefghijlmz]']) or 0)) 14 | f['23'] = f6251.get('9') 15 | f['24'] = max(0, f['22'] - f['23']) 16 | f.comment['25'] = 'Minimum tax credit' 17 | f['25'] = min(f['21'], f['24']) 18 | if f['25']: 19 | f6251.must_file = True 20 | f.comment['26'] = 'Credit carryforward to 2024' 21 | f['26'] = f['21'] - f['25'] 22 | 23 | def title(self): 24 | return 'Form 8801 (for 2023 filing)' 25 | -------------------------------------------------------------------------------- /2023/f8812.py: -------------------------------------------------------------------------------- 1 | import math 2 | from form import Form, FilingStatus 3 | 4 | class F8812(Form): 5 | """Schedule 8812, Credits for Qualifying Children and Other Dependents""" 6 | def __init__(f, inputs, f1040): 7 | super(F8812, f).__init__(inputs) 8 | if not inputs.get('qualifying_children'): 9 | return 10 | f['1'] = f1040['11'] 11 | f['2d'] = f.rowsum(['2a', '2b', '2c']) 12 | f['3'] = f.rowsum(['1', '2d']) 13 | f['4'] = inputs.get('qualifying_children') 14 | f['5'] = f['4'] * 2000 15 | f['7'] = f['6'] * 500 16 | f['8'] = f.rowsum(['5', '7']) 17 | if inputs['status'] == FilingStatus.JOINT: 18 | f['9'] = 400000 19 | else: 20 | f['9'] = 200000 21 | if f.disable_rounding: 22 | f['10'] = max(0, f['3'] - f['9']) 23 | else: 24 | f['10'] = math.ceil(max(0, f['3'] - f['9']) / 1000.0) * 1000 25 | f['11'] = f['10'] * .05 26 | f['12'] = max(0, f['8'] - f['11']) 27 | 28 | if f['12']: 29 | f['13'] = f1040['18'] - (f1040.rowsum(['s3_[1-4]', 's3_5b', 30 | 's3_6d', 's3_6f', 's3_6l', 's3_6m']) or 0) 31 | else: 32 | f['13'] = 0 33 | f['14'] = min(f['12'], f['13']) 34 | if f['14']: 35 | f.must_file = True 36 | 37 | if f['12'] > f['14']: 38 | raise RuntimeError('TODO: additional child tax credit') 39 | 40 | def title(self): 41 | return 'Schedule 8812' 42 | -------------------------------------------------------------------------------- /2023/f8959.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8959(Form): 4 | """Form 8959, Additional Medicare Tax""" 5 | THRESHOLDS = [200000, 250000, 125000, 200000, 200000] 6 | def __init__(f, inputs, sse): 7 | super(F8959, f).__init__(inputs) 8 | f['1'] = f.spouseSum(inputs, 'wages_medicare') 9 | # TODO: forms 4137, 8919 10 | f['4'] = f.rowsum(['1', '2', '3']) 11 | if f.get('4'): 12 | f['5'] = f.THRESHOLDS[inputs['status']] 13 | f['6'] = max(0, f['4'] - f['5']) 14 | f['7'] = f['6'] * .009 15 | 16 | if inputs['status'] == FilingStatus.JOINT: 17 | if sse[0].mustFile() or sse[1].mustFile(): 18 | f['8'] = max(0, (sse[0]['6'] or 0) + (sse[1]['6'] or 0)) 19 | else: 20 | if sse.mustFile(): 21 | f['8'] = max(0, sse['6']) 22 | if f.get('8'): 23 | f['9'] = f.THRESHOLDS[inputs['status']] 24 | f['10'] = f.get('4') 25 | f['11'] = max(0, f['9'] - f['10']) 26 | f['12'] = max(0, f['8'] - f['11']) 27 | f['13'] = f['12'] * .009 or None 28 | 29 | if f.get('14'): 30 | f['15'] = f.THRESHOLDS[inputs['status']] 31 | f['16'] = max(0, f['14'] - f['15']) 32 | f['17'] = f['16'] * .009 or None 33 | 34 | f.comment['18'] = 'Total Additional Medicare Tax' 35 | f['18'] = f.rowsum(['7', '13', '17']) 36 | 37 | f['19'] = f.spouseSum(inputs, 'medicare_withheld') 38 | f['20'] = f.get('1') 39 | f['21'] = f['20'] * .0145 or None 40 | if f['19'] or f['21']: 41 | f['22'] = max(0, f['19'] - f['21']) 42 | 43 | f.comment['24'] = 'Total Additional Medicare Tax withholding' 44 | f['24'] = f.rowsum(['22', '23']) 45 | 46 | if f.get('18') or f.get('24'): 47 | f.must_file = True 48 | 49 | def title(self): 50 | return 'Form 8959' 51 | -------------------------------------------------------------------------------- /2023/f8960.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8960(Form): 4 | """Form 8960, Net Investment Income Tax""" 5 | THRESHOLDS = [200000, 250000, 125000, 200000, 250000] 6 | def __init__(f, inputs, f1040, sched_a): 7 | super(F8960, f).__init__(inputs) 8 | f['1'] = f1040.get('2b') 9 | f['2'] = f1040.get('3b') 10 | # TODO: annuities 11 | f['4a'] = f1040.rowsum(['s1_3', 's1_5']) 12 | f['4c'] = f.rowsum(['4a', '4b']) 13 | f['5a'] = f1040.rowsum(['7', 's1_4']) 14 | f['5d'] = f.rowsum(['5a', '5b', '5c']) 15 | f['8'] = f.rowsum(['1', '2', '3', '4c', '5d', '6', '7']) 16 | if sched_a: 17 | # This is one example of a "reasonable method allocation" but 18 | # not the only way. 19 | # Note: this only works for state income tax, not sales tax 20 | f['9b'] = f['8'] * sched_a['5a'] / f1040['11'] 21 | f['9d'] = f.rowsum(['9a', '9b', '9c']) 22 | f['11'] = f.rowsum(['9d', '10']) 23 | f['12'] = max(0, f['8'] - f['11']) 24 | f['13'] = f1040['11'] 25 | f['14'] = f.THRESHOLDS[inputs['status']] 26 | f['15'] = max(0, f['13'] - f['14']) 27 | f['16'] = min(f['12'], f['15']) 28 | if f['16']: 29 | f['17'] = f['16'] * .038 30 | f.must_file = True 31 | 32 | def title(self): 33 | return 'Form 8960' 34 | -------------------------------------------------------------------------------- /2023/f8995a.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8995A(Form): 4 | """Form 8995A, Qualified Business Income Deduction""" 5 | def __init__(f, inputs, f1040, sched_d): 6 | super(F8995A, f).__init__(inputs) 7 | # TODO: Parts I, II, and III 8 | 9 | f['28'] = inputs.get('section_199A_dividends') 10 | f['30'] = max(0, f['28'] + f['29']) 11 | f['31'] = f['30'] * 0.20 12 | f['32'] = f.rowsum(['27', '31']) 13 | f['33'] = f1040['11'] - f1040['12'] 14 | 15 | if sched_d.mustFile(): 16 | f['34'] = f1040['3a'] + max(0, min(sched_d['15'], sched_d['16'])) 17 | else: 18 | f['34'] = f1040['3a'] + f1040['7'] 19 | 20 | f['35'] = max(0, f['33'] - f['34']) 21 | f['36'] = f['35'] * 0.20 22 | f['37'] = min(f['32'], f['36']) 23 | f.comment['39'] = 'Total QBI deduction' 24 | f['39'] = f.rowsum(['37', '38']) 25 | f.comment['40'] = 'REIT and PTP carryforward' 26 | f['40'] = min(0, f['28'] + f['29']) 27 | 28 | if f['39'] or f['40']: 29 | f.must_file = True 30 | 31 | def title(self): 32 | return 'Form 8995A' 33 | -------------------------------------------------------------------------------- /2024/ca540sp.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class CA540sp(Form): 4 | EXEMPTION_LIMITS = [337678, 450238, 225115, 337678, 450238] 5 | EXEMPTIONS = [90048, 120065, 60029, 90048, 120065] 6 | 7 | def __init__(f, inputs, ca540, ca540sca, f1040, f1040sa): 8 | super(CA540sp, f).__init__(inputs) 9 | if ca540['18'] != ca540sca.STD_DED[inputs['status']]: 10 | f['2'] = min(f1040sa['4'], .025 * f1040['11']) 11 | f['3'] = f1040sa.rowsum(['5b', '5c']) 12 | f['5'] = ca540sca['2_25'] 13 | else: 14 | f['1'] = ca540['18'] 15 | f['14'] = f.rowsum(['[1-9]', '1[0-3]']) 16 | f['15'] = ca540['19'] 17 | if not f['15']: 18 | f['15'] = ca540['17'] - ca540['18'] 19 | f['16'] = ca540sca.rowsum(['1B_B9b1', '1B_B9b2', '1B_B9b3']) 20 | f['17'] = -max(0, f1040['s1_3']) 21 | if ca540['18'] != ca540sca.STD_DED[inputs['status']]: 22 | f['18'] = -(ca540sca['2_28'] - ca540sca['2_29']) or None 23 | f['19'] = f.rowsum(['1[4-8]']) 24 | f['21'] = f['19'] - f['20'] 25 | assert(inputs['status'] != FilingStatus.SEPARATE or f['21'] <= 465231) 26 | 27 | f['22'] = f.exemption(inputs) 28 | f['23'] = max(0, f['21'] - f['22']) 29 | f['24'] = f['23'] * .07 30 | f['25'] = ca540['31'] 31 | f['26'] = max(0, f['24'] - f['25']) 32 | if f['26'] or f['21'] > f['22'] and f.rowsum(['4', '[7-9]', '1[0-3]']): 33 | f.must_file = True 34 | 35 | def exemption(f, inputs): 36 | w = {} 37 | w['1'] = f.EXEMPTIONS[inputs['status']] 38 | w['2'] = f['21'] 39 | w['3'] = f.EXEMPTION_LIMITS[inputs['status']] 40 | w['4'] = max(0, w['2'] - w['3']) 41 | w['5'] = w['4'] * .25 42 | w['6'] = max(0, w['1'] - w['5']) 43 | return w['6'] 44 | 45 | def title(self): 46 | return 'CA 540 Schedule P' 47 | -------------------------------------------------------------------------------- /2024/example_joint_return.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Example of a married couple filing jointly. Each spouse has an income 4 | # of 100k. Also, the first spouse has some investment income and 5 | # business (Schedule C) income. 6 | # 7 | # Refunds are due on both returns. 8 | # 9 | 10 | from f1040 import F1040 11 | from ca540 import CA540 12 | from form import FilingStatus 13 | 14 | # Note that some of the quantities below we need to list separately for each 15 | # spouse in an array. Everything else we just need a total. 16 | 17 | inputs = { 18 | 'status': FilingStatus.JOINT, 19 | 'exemptions': 2, 20 | 'qualifying_children': 1, 21 | 'wages': [100000.00, 100000.00], # W2 box 1 22 | 'withholding': 37000.00, # W2 box 2 23 | 'wages_ss': [100000.00, 100000.00], # W2 box 3 24 | 'ss_withheld': [ 6200.00, 6200.00], # W2 box 4 25 | 'wages_medicare': [100000.00, 100000.00], # W2 box 5 26 | 'medicare_withheld': [ 1450.00, 1450.00], # W2 box 6 27 | 'state_withholding': 18000.00, # W2 box 17 28 | 29 | # These are other state tax payments made in the tax year not included in 30 | # 'state_withholding' that are deductible on schedule A: 31 | 'extra_state_tax_payments': 3000.00, 32 | 33 | 'taxable_interest': 1500.00, 34 | 'tax_exempt_interest': 700.00, 35 | 'dividends': 3000.00, 36 | 'qualified_dividends': 2000.00, 37 | 'capital_gain_dist': 500.00, 38 | 'capital_gain_long': 5000.00, 39 | 'business_income': [5000.00, 0.00], 40 | 41 | # Extra items for schedule A 42 | 'F1040sa' : { 43 | '11' : 500, # charitable contributions 44 | }, 45 | } 46 | 47 | f = F1040(inputs) 48 | f.printAllForms() 49 | print('') 50 | ca = CA540(inputs, f) 51 | ca.printAllForms() 52 | -------------------------------------------------------------------------------- /2024/example_single.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Example of a single person with wage income of 100k. Also some investment 4 | # income and business (Schedule C) income. 5 | # 6 | 7 | from f1040 import F1040 8 | from ca540 import CA540 9 | from form import FilingStatus 10 | 11 | inputs = { 12 | 'status': FilingStatus.SINGLE, 13 | 'exemptions': 1, 14 | 'wages': 100000.00, # W2 box 1 15 | 'withholding': 20000.00, # W2 box 2 16 | 'wages_ss': 100000.00, # W2 box 3 17 | 'ss_withheld': 6200.00, # W2 box 4 18 | 'wages_medicare': 100000.00, # W2 box 5 19 | 'medicare_withheld': 1450.00, # W2 box 6 20 | 'state_withholding': 10000.00, # W2 box 17 21 | 22 | # These are other state tax payments made in the tax year not included in 23 | # 'state_withholding' that are deductible on schedule A: 24 | 'extra_state_tax_payments': 1000.00, 25 | 26 | 'taxable_interest': 1500.00, 27 | 'tax_exempt_interest': 700.00, 28 | 'dividends': 3000.00, 29 | 'qualified_dividends': 2000.00, 30 | 'capital_gain_dist': 500.00, 31 | 'capital_gain_long': 5000.00, 32 | 'business_income': 5000.00, 33 | 34 | # Extra items for schedule A 35 | 'F1040sa' : { 36 | '11' : 500, # charitable contributions 37 | }, 38 | } 39 | 40 | f = F1040(inputs) 41 | f.printAllForms() 42 | print('') 43 | ca = CA540(inputs, f) 44 | ca.printAllForms() 45 | -------------------------------------------------------------------------------- /2024/f1040sa.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F1040sa(Form): 4 | def __init__(f, inputs, f1040): 5 | super(F1040sa, f).__init__(inputs) 6 | if f['1']: 7 | f['2'] = f1040['11'] 8 | f['3'] = f['2'] * .075 9 | f['4'] = max(0, f['1'] - f['3']) 10 | f['5a'] = inputs['state_withholding'] + \ 11 | inputs.get('extra_state_tax_payments', 0) 12 | f['5d'] = f.rowsum(['5a', '5b', '5c']) 13 | f['5e'] = min(f['5d'], 14 | 5000 if inputs['status'] == FilingStatus.SEPARATE else 10000) 15 | f['7'] = f.rowsum(['5e', '6']) 16 | f['8e'] = f.rowsum(['8a', '8b', '8c']) 17 | f['10'] = f.rowsum(['8e', '9']) 18 | f['14'] = f.rowsum(['11', '12', '13']) 19 | f['17'] = f.rowsum(['4', '7', '10', '14', '15', '16']) 20 | 21 | def title(self): 22 | return 'Schedule A' 23 | -------------------------------------------------------------------------------- /2024/f1040sd.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F1040sd(Form): 4 | def __init__(f, inputs): 5 | super(F1040sd, f).__init__(inputs) 6 | if 'capital_gain_long' not in inputs \ 7 | and 'capital_gain_short' not in inputs \ 8 | and 'capital_gain_carryover_short' not in inputs \ 9 | and 'capital_gain_carryover_long' not in inputs: 10 | return 11 | f.must_file = True 12 | f['1'] = inputs.get('capital_gain_short') 13 | f['6'] = inputs.get('capital_gain_carryover_short') 14 | f['7'] = f.rowsum(['1', '2', '3', '4', '5', '6']) 15 | f['8'] = inputs.get('capital_gain_long') 16 | f['13'] = inputs.get('capital_gain_dist') 17 | f['14'] = inputs.get('capital_gain_carryover_long') 18 | f['15'] = f.rowsum(['8', '9', '10', '11', '12', '13', '14']) 19 | f['16'] = f.rowsum(['7', '15']) 20 | if f['16'] < 0: 21 | cutoff = -3000 22 | if inputs['status'] == FilingStatus.SEPARATE: 23 | cutoff = -1500 24 | f['21'] = max(f['16'], cutoff) 25 | 26 | # If lines 15 and 16 are both gains and line 18 or 19 has a value: 27 | # Use the Schedule D tax worksheet 28 | # Else if lines 15 and 16 are both gains or you have qualified divs: 29 | # Use the Qualified Dividends and Capital Gain Tax Worksheet 30 | # Else 31 | # Use tax tables 32 | 33 | def title(self): 34 | return 'Schedule D' 35 | -------------------------------------------------------------------------------- /2024/f1040sse.py: -------------------------------------------------------------------------------- 1 | from form import Form 2 | 3 | SS_WAGE_LIMIT = 168600 4 | 5 | # Not implemented: optional methods 6 | class F1040sse(Form): 7 | def __init__(self, inputs): 8 | super(F1040sse, self).__init__(inputs) 9 | self['2'] = inputs.get('business_income') 10 | self['3'] = self.rowsum(['1a', '1b', '2']) 11 | if self['3'] > 0: 12 | self['4a'] = self['3'] * 0.9235 13 | else: 14 | self['4a'] = self['3'] 15 | self['4c'] = self.rowsum(['4a', '4b']) 16 | if self['4c'] < 400: 17 | return 18 | self.must_file = True 19 | self['6'] = self.rowsum(['4c', '5b']) 20 | self['7'] = SS_WAGE_LIMIT 21 | self['8a'] = inputs['wages_ss'] 22 | if self['8a'] < SS_WAGE_LIMIT: 23 | self['8d'] = self.rowsum(['8a', '8b', '8c']) 24 | self['9'] = self['7'] - self['8d'] 25 | if self['9'] <= 0: 26 | self['9'] = 0 27 | self['10'] = 0 28 | else: 29 | self['10'] = min(self['6'], self['9']) * .124 30 | self['11'] = self['6'] * .029 31 | self['12'] = self.rowsum(['10', '11']) 32 | self['13'] = self['12'] * .5 33 | 34 | def title(self): 35 | return 'Schedule SE' 36 | -------------------------------------------------------------------------------- /2024/f8606.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8606(Form): 4 | """Form 8606, Nondeductible IRAs""" 5 | def __init__(f, inputs, spouse): 6 | super(F8606, f).__init__(inputs, init_idx=spouse) 7 | f.spouse = spouse 8 | f.comment['2'] = 'Starting basis in traditional IRAs' 9 | f['3'] = f['1'] + f['2'] 10 | f['5'] = f['3'] - f['4'] 11 | f['9'] = f.rowsum(['6', '7', '8']) 12 | f['10'] = min(10000, f['5'] * 10000.0 / f['9']) 13 | f['11'] = f['8'] * f['10'] / 10000.0 14 | f['12'] = f['7'] * f['10'] / 10000.0 15 | f['13'] = f['11'] + f['12'] 16 | f.comment['14'] = 'Ending basis in traditional IRAs' 17 | f['14'] = f['3'] - f['13'] 18 | f['15a'] = f['7'] - f['12'] 19 | f.comment['15c'] = 'Taxable Distribution' 20 | f['15c'] = f['15a'] - f['15b'] 21 | f['16'] = f['8'] 22 | f['17'] = f['11'] 23 | f.comment['18'] = 'Taxable Conversion' 24 | f['18'] = f['16'] - f['17'] 25 | # TODO: Roth IRA distributions 26 | f.must_file = True 27 | return 28 | 29 | def title(self): 30 | if self.spouse is not None: 31 | return 'Form 8606 [%d]' % self.spouse 32 | else: 33 | return 'Form 8606' 34 | -------------------------------------------------------------------------------- /2024/f8801.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8801(Form): 4 | """Form 8801, Credit for Prior Year Minimum Tax""" 5 | def __init__(f, inputs, f1040, f6251): 6 | super(F8801, f).__init__(inputs) 7 | f['21'] = inputs.get('prior_amt_credit') 8 | if not f['21']: 9 | return 10 | 11 | f.must_file = True 12 | f['22'] = max(0, f1040['16'] + f1040['s2_1z'] - \ 13 | (f1040.rowsum(['19', 's3_[1-4]', 's3_5[a-b]', 's3_6[acdefghijlmz]']) or 0)) 14 | f['23'] = f6251.get('9') 15 | f['24'] = max(0, f['22'] - f['23']) 16 | f.comment['25'] = 'Minimum tax credit' 17 | f['25'] = min(f['21'], f['24']) 18 | if f['25']: 19 | f6251.must_file = True 20 | f.comment['26'] = 'Credit carryforward to 2025' 21 | f['26'] = f['21'] - f['25'] 22 | 23 | def title(self): 24 | return 'Form 8801 (for 2024 filing)' 25 | -------------------------------------------------------------------------------- /2024/f8812.py: -------------------------------------------------------------------------------- 1 | import math 2 | from form import Form, FilingStatus 3 | 4 | class F8812(Form): 5 | """Schedule 8812, Credits for Qualifying Children and Other Dependents""" 6 | def __init__(f, inputs, f1040): 7 | super(F8812, f).__init__(inputs) 8 | if not inputs.get('qualifying_children'): 9 | return 10 | f['1'] = f1040['11'] 11 | f['2d'] = f.rowsum(['2a', '2b', '2c']) 12 | f['3'] = f.rowsum(['1', '2d']) 13 | f['4'] = inputs.get('qualifying_children') 14 | f['5'] = f['4'] * 2000 15 | f['7'] = f['6'] * 500 16 | f['8'] = f.rowsum(['5', '7']) 17 | if inputs['status'] == FilingStatus.JOINT: 18 | f['9'] = 400000 19 | else: 20 | f['9'] = 200000 21 | if f.disable_rounding: 22 | f['10'] = max(0, f['3'] - f['9']) 23 | else: 24 | f['10'] = math.ceil(max(0, f['3'] - f['9']) / 1000.0) * 1000 25 | f['11'] = f['10'] * .05 26 | f['12'] = max(0, f['8'] - f['11']) 27 | 28 | if f['12']: 29 | f['13'] = f1040['18'] - (f1040.rowsum(['s3_[1-4]', 's3_5b', 30 | 's3_6d', 's3_6f', 's3_6l', 's3_6m']) or 0) 31 | else: 32 | f['13'] = 0 33 | f['14'] = min(f['12'], f['13']) 34 | if f['14']: 35 | f.must_file = True 36 | 37 | if f['12'] > f['14']: 38 | raise RuntimeError('TODO: additional child tax credit') 39 | 40 | def title(self): 41 | return 'Schedule 8812' 42 | -------------------------------------------------------------------------------- /2024/f8959.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8959(Form): 4 | """Form 8959, Additional Medicare Tax""" 5 | THRESHOLDS = [200000, 250000, 125000, 200000, 200000] 6 | def __init__(f, inputs, sse): 7 | super(F8959, f).__init__(inputs) 8 | f['1'] = f.spouseSum(inputs, 'wages_medicare') 9 | # TODO: forms 4137, 8919 10 | f['4'] = f.rowsum(['1', '2', '3']) 11 | if f.get('4'): 12 | f['5'] = f.THRESHOLDS[inputs['status']] 13 | f['6'] = max(0, f['4'] - f['5']) 14 | f['7'] = f['6'] * .009 15 | 16 | if inputs['status'] == FilingStatus.JOINT: 17 | if sse[0].mustFile() or sse[1].mustFile(): 18 | f['8'] = max(0, (sse[0]['6'] or 0) + (sse[1]['6'] or 0)) 19 | else: 20 | if sse.mustFile(): 21 | f['8'] = max(0, sse['6']) 22 | if f.get('8'): 23 | f['9'] = f.THRESHOLDS[inputs['status']] 24 | f['10'] = f.get('4') 25 | f['11'] = max(0, f['9'] - f['10']) 26 | f['12'] = max(0, f['8'] - f['11']) 27 | f['13'] = f['12'] * .009 or None 28 | 29 | if f.get('14'): 30 | f['15'] = f.THRESHOLDS[inputs['status']] 31 | f['16'] = max(0, f['14'] - f['15']) 32 | f['17'] = f['16'] * .009 or None 33 | 34 | f.comment['18'] = 'Total Additional Medicare Tax' 35 | f['18'] = f.rowsum(['7', '13', '17']) 36 | 37 | f['19'] = f.spouseSum(inputs, 'medicare_withheld') 38 | f['20'] = f.get('1') 39 | f['21'] = f['20'] * .0145 or None 40 | if f['19'] or f['21']: 41 | f['22'] = max(0, f['19'] - f['21']) 42 | 43 | f.comment['24'] = 'Total Additional Medicare Tax withholding' 44 | f['24'] = f.rowsum(['22', '23']) 45 | 46 | if f.get('18') or f.get('24'): 47 | f.must_file = True 48 | 49 | def title(self): 50 | return 'Form 8959' 51 | -------------------------------------------------------------------------------- /2024/f8960.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8960(Form): 4 | """Form 8960, Net Investment Income Tax""" 5 | THRESHOLDS = [200000, 250000, 125000, 200000, 250000] 6 | def __init__(f, inputs, f1040, sched_a): 7 | super(F8960, f).__init__(inputs) 8 | f['1'] = f1040.get('2b') 9 | f['2'] = f1040.get('3b') 10 | # TODO: annuities 11 | f['4a'] = f1040.rowsum(['s1_3', 's1_5']) 12 | f['4c'] = f.rowsum(['4a', '4b']) 13 | f['5a'] = f1040.rowsum(['7', 's1_4']) 14 | f['5d'] = f.rowsum(['5a', '5b', '5c']) 15 | f['8'] = f.rowsum(['1', '2', '3', '4c', '5d', '6', '7']) 16 | if sched_a: 17 | # This is one example of a "reasonable method allocation" but 18 | # not the only way. 19 | # Note: this only works for state income tax, not sales tax 20 | f['9b'] = f['8'] * sched_a['5a'] / f1040['11'] 21 | f['9d'] = f.rowsum(['9a', '9b', '9c']) 22 | f['11'] = f.rowsum(['9d', '10']) 23 | f['12'] = max(0, f['8'] - f['11']) 24 | f['13'] = f1040['11'] 25 | f['14'] = f.THRESHOLDS[inputs['status']] 26 | f['15'] = max(0, f['13'] - f['14']) 27 | f['16'] = min(f['12'], f['15']) 28 | if f['16']: 29 | f['17'] = f['16'] * .038 30 | f.must_file = True 31 | 32 | def title(self): 33 | return 'Form 8960' 34 | -------------------------------------------------------------------------------- /2024/f8995a.py: -------------------------------------------------------------------------------- 1 | from form import Form, FilingStatus 2 | 3 | class F8995A(Form): 4 | """Form 8995A, Qualified Business Income Deduction""" 5 | def __init__(f, inputs, f1040, sched_d): 6 | super(F8995A, f).__init__(inputs) 7 | # TODO: Parts I, II, and III 8 | 9 | f['28'] = inputs.get('section_199A_dividends') 10 | f['30'] = max(0, f['28'] + f['29']) 11 | f['31'] = f['30'] * 0.20 12 | f['32'] = f.rowsum(['27', '31']) 13 | f['33'] = f1040['11'] - f1040['12'] 14 | 15 | if sched_d.mustFile(): 16 | f['34'] = f1040['3a'] + max(0, min(sched_d['15'], sched_d['16'])) 17 | else: 18 | f['34'] = f1040['3a'] + f1040['7'] 19 | 20 | f['35'] = max(0, f['33'] - f['34']) 21 | f['36'] = f['35'] * 0.20 22 | f['37'] = min(f['32'], f['36']) 23 | f.comment['39'] = 'Total QBI deduction' 24 | f['39'] = f.rowsum(['37', '38']) 25 | f.comment['40'] = 'REIT and PTP carryforward' 26 | f['40'] = min(0, f['28'] + f['29']) 27 | 28 | if f['39'] or f['40']: 29 | f.must_file = True 30 | 31 | def title(self): 32 | return 'Form 8995A' 33 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | python-taxes 2 | by David Moore 3 | 4 | Python code for computing Federal and CA state taxes. 5 | 6 | Licensed under the GNU GPL v2.0. Please contact me if you are planning to 7 | use or redistribute this code for any serious purpose. 8 | 9 | Disclaimer: This code comes with no warranty. It will probably not work 10 | for your tax return or specific circumstances without modification. I am 11 | not a tax accountant and this is not tax advice. 12 | 13 | See example_*.py files in each year's subdirectory for usage instructions. 14 | --------------------------------------------------------------------------------