|
| 1 | +from form import Form, FilingStatus |
| 2 | +from f1040 import F1040 |
| 3 | +from ca540sca import CA540sca |
| 4 | +from ca540sp import CA540sp |
| 5 | +import math |
| 6 | + |
| 7 | +class CA540(Form): |
| 8 | + EXEMPTION = 149 |
| 9 | + DEPENDENT_EXEMPTION = 461 |
| 10 | + BRACKET_RATES = [.01, .02, .04, .06, .08, .093, .103, .113, .123] |
| 11 | + BRACKET_LIMITS = [ |
| 12 | + [10756, 25499, 40245, 55866, 70606, 360659, 432787, 721314], # SINGLE |
| 13 | + [21512, 50998, 80490, 111732, 141212, 721318, 865574, 1442628],# JOINT |
| 14 | + [10756, 25499, 40245, 55866, 70606, 360659, 432787, 721314], # SEPARATE |
| 15 | + [21527, 51000, 65744, 81364, 96107, 490493, 588593, 980987], # HEAD |
| 16 | + [21512, 50998, 80490, 111732, 141212, 721318, 865574, 1442628],# WIDOW |
| 17 | + ] |
| 18 | + MENTAL_HEALTH_LIMIT = 1000000 |
| 19 | + MENTAL_HEALTH_RATE = .01 |
| 20 | + |
| 21 | + def __init__(f, inputs, f1040): |
| 22 | + super(CA540, f).__init__(inputs) |
| 23 | + f.must_file = True |
| 24 | + f.addForm(f) |
| 25 | + |
| 26 | + for i in f1040.forms: |
| 27 | + if i.__class__.__name__ == 'F1040sa': |
| 28 | + f1040sa = i |
| 29 | + |
| 30 | + if inputs['status'] in [FilingStatus.JOINT, FilingStatus.WIDOW]: |
| 31 | + personal = 2 |
| 32 | + else: |
| 33 | + personal = 1 |
| 34 | + |
| 35 | + f['7'] = personal * f.EXEMPTION |
| 36 | + # TODO: blind or senior |
| 37 | + f['10'] = (inputs['exemptions'] - personal) * f.DEPENDENT_EXEMPTION |
| 38 | + f['11'] = f.rowsum(['7', '8', '9', '10']) |
| 39 | + |
| 40 | + # Line 12 is just informational. Leave it out of our computations. |
| 41 | + f.comment['13'] = 'Federal AGI' |
| 42 | + f['13'] = f1040['11'] |
| 43 | + sca = f.addForm(CA540sca(inputs, f1040, f1040sa)) |
| 44 | + f['14'] = sca['1C_B27'] |
| 45 | + f['15'] = f['13'] - f['14'] |
| 46 | + f['16'] = sca['1C_C27'] |
| 47 | + f.comment['17'] = 'CA AGI' |
| 48 | + f['17'] = f['15'] + f['16'] |
| 49 | + f['18'] = sca['2_30'] |
| 50 | + f.comment['19'] = 'Taxable income' |
| 51 | + f['19'] = max(0, f['17'] - f['18']) |
| 52 | + |
| 53 | + f.comment['31'] = 'Tax' |
| 54 | + f['31'] = f.tax_rate_schedule(inputs['status'], f['19']) |
| 55 | + f['32'] = f.agi_limitation_worksheet(inputs['status']) |
| 56 | + f['33'] = max(0, f['31'] - f['32']) |
| 57 | + f['35'] = f['33'] + f['34'] |
| 58 | + |
| 59 | + f['47'] = f.rowsum(['40', '43', '44', '45', '46']) |
| 60 | + f['48'] = max(0, f['35'] - f['47']) |
| 61 | + |
| 62 | + sp = f.addForm(CA540sp(inputs, f, sca, f1040, f1040sa)) |
| 63 | + f['61'] = sp.get('26') |
| 64 | + f.comment['61'] = 'AMT' |
| 65 | + |
| 66 | + if f['19'] > f.MENTAL_HEALTH_LIMIT: |
| 67 | + f['62'] = (f['19'] - f.MENTAL_HEALTH_LIMIT) * f.MENTAL_HEALTH_RATE |
| 68 | + f.comment['64'] = 'Total tax' |
| 69 | + f['64'] = f.rowsum(['48', '61', '62', '63']) |
| 70 | + |
| 71 | + f['71'] = inputs.get('state_withholding') |
| 72 | + f['72'] = inputs.get('state_estimated_payments') |
| 73 | + # TODO: real estate withholding |
| 74 | + |
| 75 | + f.comment['78'] = 'Total payments' |
| 76 | + f['78'] = f.rowsum(['71', '72', '73', '75', '76', '77']) |
| 77 | + |
| 78 | + if f['78'] > f['91']: |
| 79 | + f['93'] = f['78'] - f['91'] |
| 80 | + else: |
| 81 | + f['94'] = f['91'] - f['78'] |
| 82 | + |
| 83 | + if f['93'] > f['92']: |
| 84 | + f['95'] = f['93'] - f['92'] |
| 85 | + else: |
| 86 | + f['96'] = f['92'] - f['93'] |
| 87 | + |
| 88 | + if f['95'] > f['64']: |
| 89 | + f.comment['97'] = 'Refund' |
| 90 | + f['97'] = f['95'] - f['64'] |
| 91 | + else: |
| 92 | + f.comment['100'] = 'Tax due' |
| 93 | + f['100'] = f['64'] - f['95'] |
| 94 | + f.comment['111'] = 'Amount you owe' |
| 95 | + f['111'] = f.rowsum(['94', '96', '100', '110']) |
| 96 | + |
| 97 | + @classmethod |
| 98 | + def tax_rate_schedule(f, status, val): |
| 99 | + # TODO: rounding of amounts less than 100000 to match tax table |
| 100 | + tax = 0 |
| 101 | + prev = 0 |
| 102 | + i = 0 |
| 103 | + for lim in f.BRACKET_LIMITS[status]: |
| 104 | + if val <= lim: |
| 105 | + break |
| 106 | + tax += f.BRACKET_RATES[i] * (lim - prev) |
| 107 | + prev = lim |
| 108 | + i += 1 |
| 109 | + tax += f.BRACKET_RATES[i] * (val - prev) |
| 110 | + return tax |
| 111 | + |
| 112 | + def agi_limitation_worksheet(f, status): |
| 113 | + LIMITS = [244857, 489719, 244857, 367291, 489719] |
| 114 | + w = {} |
| 115 | + w['a'] = f['13'] |
| 116 | + w['b'] = LIMITS[status] |
| 117 | + if w['a'] <= w['b']: |
| 118 | + return f['11'] |
| 119 | + w['c'] = w['a'] - w['b'] |
| 120 | + divisor = 1250.00 if status == FilingStatus.SEPARATE else 2500.00 |
| 121 | + w['d'] = math.ceil(w['c'] / divisor) |
| 122 | + w['e'] = w['d'] * 6 |
| 123 | + w['f'] = (f['7'] + f['8'] + f['9']) / f.EXEMPTION |
| 124 | + w['g'] = w['e'] * w['f'] |
| 125 | + w['h'] = f['7'] + f['8'] + f['9'] |
| 126 | + w['i'] = max(0, w['h'] - w['g']) |
| 127 | + w['j'] = f['10'] / f.DEPENDENT_EXEMPTION |
| 128 | + w['k'] = w['e'] * w['j'] |
| 129 | + w['l'] = f['10'] |
| 130 | + w['m'] = max(0, w['l'] - w['k']) |
| 131 | + w['n'] = w['i'] + w['m'] |
| 132 | + return w['n'] |
| 133 | + |
| 134 | + def title(f): |
| 135 | + return 'CA Form 540' |
0 commit comments