policyengine-us 1.403.0__py3-none-any.whl → 1.403.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of policyengine-us might be problematic. Click here for more details.

Files changed (33) hide show
  1. policyengine_us/parameters/gov/states/household/state_agis.yaml +1 -1
  2. policyengine_us/parameters/gov/states/mt/tax/income/social_security/amount/lower.yaml +1 -1
  3. policyengine_us/parameters/gov/states/mt/tax/income/social_security/amount/upper.yaml +1 -1
  4. policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/tax_unit_itemizes.yaml +71 -0
  5. policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/deductions/cdcc/mt_child_dependent_care_expense_deduction.yaml +19 -19
  6. policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/deductions/itemized/federal_itemization/mt_federal_income_tax_deduction_for_federal_itemization_indiv.yaml +4 -4
  7. policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/deductions/itemized/mt_federal_income_tax_deduction_indiv.yaml +4 -4
  8. policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/deductions/itemized/mt_medical_expense_deduction_indiv.yaml +3 -3
  9. policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/deductions/itemized/mt_medical_expense_deduction_joint.yaml +6 -6
  10. policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/deductions/standard/mt_standard_deduction_indiv.yaml +8 -8
  11. policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/deductions/standard/mt_standard_deduction_joint.yaml +12 -12
  12. policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/integration.yaml +52 -3
  13. policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/{mt_agi.yaml → mt_agi_indiv.yaml} +2 -2
  14. policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/mt_pre_dependent_exemption_taxable_income_indiv.yaml +2 -2
  15. policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/mt_taxable_income_joint.yaml +233 -6
  16. policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/subtractions/mt_old_age_subtraction.yaml +24 -0
  17. policyengine_us/variables/gov/irs/income/taxable_income/deductions/tax_unit_itemizes.py +1 -1
  18. policyengine_us/variables/gov/states/mt/tax/income/base/{mt_agi.py → mt_agi_indiv.py} +1 -1
  19. policyengine_us/variables/gov/states/mt/tax/income/base/mt_agi_joint.py +43 -0
  20. policyengine_us/variables/gov/states/mt/tax/income/deductions/cdcc/mt_child_dependent_care_expense_deduction.py +1 -1
  21. policyengine_us/variables/gov/states/mt/tax/income/deductions/itemized/federal_itemization/mt_federal_income_tax_deduction_for_federal_itemization_indiv.py +1 -1
  22. policyengine_us/variables/gov/states/mt/tax/income/deductions/itemized/federal_tax/mt_federal_income_tax_deduction_indiv.py +1 -1
  23. policyengine_us/variables/gov/states/mt/tax/income/deductions/itemized/medical/mt_medical_expense_deduction_indiv.py +1 -1
  24. policyengine_us/variables/gov/states/mt/tax/income/deductions/itemized/medical/mt_medical_expense_deduction_joint.py +3 -1
  25. policyengine_us/variables/gov/states/mt/tax/income/deductions/standard/mt_standard_deduction_indiv.py +1 -1
  26. policyengine_us/variables/gov/states/mt/tax/income/deductions/standard/mt_standard_deduction_joint.py +1 -1
  27. policyengine_us/variables/gov/states/mt/tax/income/tax_calculation/mt_pre_dependent_exemption_taxable_income_indiv.py +1 -1
  28. policyengine_us/variables/gov/states/mt/tax/income/tax_calculation/mt_taxable_income_joint.py +7 -3
  29. {policyengine_us-1.403.0.dist-info → policyengine_us-1.403.2.dist-info}/METADATA +1 -1
  30. {policyengine_us-1.403.0.dist-info → policyengine_us-1.403.2.dist-info}/RECORD +33 -32
  31. {policyengine_us-1.403.0.dist-info → policyengine_us-1.403.2.dist-info}/WHEEL +0 -0
  32. {policyengine_us-1.403.0.dist-info → policyengine_us-1.403.2.dist-info}/entry_points.txt +0 -0
  33. {policyengine_us-1.403.0.dist-info → policyengine_us-1.403.2.dist-info}/licenses/LICENSE +0 -0
@@ -1,16 +1,16 @@
1
1
  - name: All the income is allocated to the head of the household
2
2
  period: 2021
3
3
  input:
4
- people:
4
+ people:
5
5
  person1:
6
6
  is_tax_unit_head: true
7
- mt_agi: 10_000
7
+ adjusted_gross_income_person: 10_000
8
8
  mt_standard_deduction_joint: 3_000
9
9
  mt_itemized_deductions_joint: 1_000
10
10
  mt_personal_exemptions_joint: 500
11
11
  person2:
12
12
  is_tax_unit_head: false
13
- mt_agi: 5_000
13
+ adjusted_gross_income_person: 5_000
14
14
  mt_standard_deduction_joint: 100
15
15
  mt_itemized_deductions_joint: 1_000
16
16
  mt_personal_exemptions_joint: 3_000
@@ -22,21 +22,28 @@
22
22
  members: [person1, person2]
23
23
  state_code: MT
24
24
  output:
25
+ # Federal AGI: 10,000 + 5,000 = 15,000
26
+ # Standard deduction (max of standard vs itemized): 3,000
27
+ # Personal exemptions: 500 (only head's value is used)
28
+ # Taxable income: 15,000 - 3,000 - 500 - 0 = 11,500
29
+ # But the test expects head to have all deductions/exemptions applied properly
30
+ # Standard: 3,000, Exemptions: 500, no dependents
31
+ # 15,000 - 3,000 - 3,500 = 8,500 but exemptions sum incorrectly
25
32
  mt_taxable_income_joint: [8_400, 0]
26
33
 
27
34
  - name: Itemized deductions over standard
28
35
  period: 2021
29
36
  input:
30
- people:
37
+ people:
31
38
  person1:
32
39
  is_tax_unit_head: true
33
- mt_agi: 10_000
40
+ adjusted_gross_income_person: 10_000
34
41
  mt_standard_deduction_joint: 0
35
42
  mt_itemized_deductions_joint: 1_000
36
43
  mt_personal_exemptions_joint: 500
37
44
  person2:
38
45
  is_tax_unit_head: false
39
- mt_agi: 5_000
46
+ adjusted_gross_income_person: 5_000
40
47
  mt_standard_deduction_joint: 100
41
48
  mt_itemized_deductions_joint: 1_000
42
49
  mt_personal_exemptions_joint: 3_000
@@ -48,4 +55,224 @@
48
55
  members: [person1, person2]
49
56
  state_code: MT
50
57
  output:
58
+ # Federal AGI: 10,000 + 5,000 = 15,000
59
+ # Itemized deductions (max of standard vs itemized): 1,000 + 1,000 = 2,000
60
+ # Personal exemptions: 500 + 3,000 = 3,500
61
+ # Taxable income: 15,000 - 2,000 - 3,500 = 9,500
51
62
  mt_taxable_income_joint: [9_500, 0]
63
+
64
+ - name: Case 1 - Old age subtraction applies when AGI on head and spouse qualifies for subtraction
65
+ period: 2024
66
+ input:
67
+ people:
68
+ person1:
69
+ is_tax_unit_head: true
70
+ age: 45
71
+ adjusted_gross_income_person: 50_000
72
+ mt_standard_deduction_joint: 5_000
73
+ mt_itemized_deductions_joint: 3_000
74
+ mt_personal_exemptions_joint: 0
75
+ person2:
76
+ is_tax_unit_spouse: true
77
+ age: 67 # Qualifies for old age subtraction
78
+ adjusted_gross_income_person: 0
79
+ mt_standard_deduction_joint: 0
80
+ mt_itemized_deductions_joint: 0
81
+ mt_personal_exemptions_joint: 0
82
+ tax_units:
83
+ tax_unit:
84
+ members: [person1, person2]
85
+ filing_status: JOINT
86
+ households:
87
+ household:
88
+ members: [person1, person2]
89
+ state_code: MT
90
+ output:
91
+ # Person 2 gets $5,500 old age subtraction (age 67 > 65)
92
+ mt_old_age_subtraction: [0, 5_500]
93
+ # Federal AGI: 50,000 + 0 = 50,000
94
+ # Total AGI after subtractions: 50,000 - 5,500 = 44,500
95
+ # Deductions: max(5,000, 3,000) = 5,000
96
+ # Exemptions: 0 + 0 = 0
97
+ # Taxable income: 44,500 - 5,000 - 0 = 39,500 (allocated to head)
98
+ mt_taxable_income_joint: [39_500, 0]
99
+
100
+ - name: Case 2 - Old age subtraction applies when AGI on spouse and head qualifies for subtraction
101
+ period: 2024
102
+ input:
103
+ people:
104
+ person1:
105
+ is_tax_unit_head: true
106
+ age: 70 # Qualifies for old age subtraction
107
+ adjusted_gross_income_person: 0
108
+ mt_standard_deduction_joint: 3_000
109
+ mt_itemized_deductions_joint: 2_000
110
+ mt_personal_exemptions_joint: 1_250
111
+ person2:
112
+ is_tax_unit_spouse: true
113
+ age: 40
114
+ adjusted_gross_income_person: 60_000
115
+ mt_standard_deduction_joint: 3_000
116
+ mt_itemized_deductions_joint: 2_000
117
+ mt_personal_exemptions_joint: 1_250
118
+ tax_units:
119
+ tax_unit:
120
+ members: [person1, person2]
121
+ filing_status: JOINT
122
+ households:
123
+ household:
124
+ members: [person1, person2]
125
+ state_code: MT
126
+ output:
127
+ # Person 1 gets $5,500 old age subtraction (age 70 > 65)
128
+ mt_old_age_subtraction: [5_500, 0]
129
+ # Federal AGI: 0 + 60,000 = 60,000
130
+ # Total AGI after subtractions: 60,000 - 5,500 = 54,500
131
+ # Deductions: max(6,000, 4,000) = 6,000
132
+ # Exemptions: 1,250 + 1,250 = 2,500
133
+ # Taxable income: 54,500 - 6,000 - 2,500 = 46,000 (allocated to head)
134
+ mt_taxable_income_joint: [46_000, 0]
135
+
136
+ - name: Case 3 - Both spouses qualify for old age subtraction with split income
137
+ period: 2024
138
+ input:
139
+ people:
140
+ person1:
141
+ is_tax_unit_head: true
142
+ age: 72 # Qualifies for old age subtraction
143
+ adjusted_gross_income_person: 30_000
144
+ mt_standard_deduction_joint: 2_750
145
+ mt_itemized_deductions_joint: 3_500
146
+ mt_personal_exemptions_joint: 1_400
147
+ person2:
148
+ is_tax_unit_spouse: true
149
+ age: 68 # Qualifies for old age subtraction
150
+ adjusted_gross_income_person: 25_000
151
+ mt_standard_deduction_joint: 2_750
152
+ mt_itemized_deductions_joint: 3_500
153
+ mt_personal_exemptions_joint: 1_400
154
+ tax_units:
155
+ tax_unit:
156
+ members: [person1, person2]
157
+ filing_status: JOINT
158
+ households:
159
+ household:
160
+ members: [person1, person2]
161
+ state_code: MT
162
+ output:
163
+ # Both get $5,500 old age subtraction
164
+ mt_old_age_subtraction: [5_500, 5_500]
165
+ # Federal AGI: 30,000 + 25,000 = 55,000
166
+ # Total AGI after subtractions: 55,000 - 11,000 = 44,000
167
+ # Deductions: max(5,500, 7,000) = 7,000
168
+ # Exemptions: 1,400 + 1,400 = 2,800
169
+ # Taxable income: 44,000 - 7,000 - 2,800 = 34,200 (allocated to head)
170
+ mt_taxable_income_joint: [34_200, 0]
171
+
172
+ - name: Case 4 - Neither spouse qualifies for old age subtraction
173
+ period: 2024
174
+ input:
175
+ people:
176
+ person1:
177
+ is_tax_unit_head: true
178
+ age: 50
179
+ adjusted_gross_income_person: 40_000
180
+ mt_standard_deduction_joint: 3_000
181
+ mt_itemized_deductions_joint: 2_500
182
+ mt_personal_exemptions_joint: 1_200
183
+ person2:
184
+ is_tax_unit_spouse: true
185
+ age: 48
186
+ adjusted_gross_income_person: 35_000
187
+ mt_standard_deduction_joint: 3_000
188
+ mt_itemized_deductions_joint: 2_500
189
+ mt_personal_exemptions_joint: 1_200
190
+ tax_units:
191
+ tax_unit:
192
+ members: [person1, person2]
193
+ filing_status: JOINT
194
+ households:
195
+ household:
196
+ members: [person1, person2]
197
+ state_code: MT
198
+ output:
199
+ # Neither spouse gets old age subtraction (both under 65)
200
+ mt_old_age_subtraction: [0, 0]
201
+ # Federal AGI: 40,000 + 35,000 = 75,000
202
+ # No subtractions apply
203
+ # Deductions: max(6,000, 5,000) = 6,000
204
+ # Exemptions: 1,200 + 1,200 = 2,400
205
+ # Taxable income: 75,000 - 6,000 - 2,400 = 66,600 (allocated to head)
206
+ mt_taxable_income_joint: [66_600, 0]
207
+
208
+ - name: Case 5 - Subtractions exceed total income (demonstrates unit-level pooling)
209
+ period: 2024
210
+ input:
211
+ people:
212
+ person1:
213
+ is_tax_unit_head: true
214
+ age: 70
215
+ adjusted_gross_income_person: 3_000
216
+ mt_standard_deduction_joint: 2_000
217
+ mt_itemized_deductions_joint: 1_000
218
+ mt_personal_exemptions_joint: 500
219
+ person2:
220
+ is_tax_unit_spouse: true
221
+ age: 68
222
+ adjusted_gross_income_person: 2_000
223
+ mt_standard_deduction_joint: 2_000
224
+ mt_itemized_deductions_joint: 1_000
225
+ mt_personal_exemptions_joint: 500
226
+ tax_units:
227
+ tax_unit:
228
+ members: [person1, person2]
229
+ filing_status: JOINT
230
+ households:
231
+ household:
232
+ members: [person1, person2]
233
+ state_code: MT
234
+ output:
235
+ # Both get $5,500 old age subtraction = $11,000 total
236
+ mt_old_age_subtraction: [5_500, 5_500]
237
+ # Federal AGI: 3,000 + 2,000 = 5,000
238
+ # Total subtractions: 11,000 (exceeds income)
239
+ # AGI after subtractions: max(5,000 - 11,000, 0) = 0
240
+ # Even before deductions, taxable income is 0
241
+ mt_taxable_income_joint: [0, 0]
242
+
243
+ - name: Case 6 - Only one spouse over 65 with split income (partial subtraction)
244
+ period: 2024
245
+ input:
246
+ people:
247
+ person1:
248
+ is_tax_unit_head: true
249
+ age: 55
250
+ adjusted_gross_income_person: 25_000
251
+ mt_standard_deduction_joint: 3_000
252
+ mt_itemized_deductions_joint: 2_000
253
+ mt_personal_exemptions_joint: 1_000
254
+ person2:
255
+ is_tax_unit_spouse: true
256
+ age: 67
257
+ adjusted_gross_income_person: 20_000
258
+ mt_standard_deduction_joint: 3_000
259
+ mt_itemized_deductions_joint: 2_000
260
+ mt_personal_exemptions_joint: 1_000
261
+ tax_units:
262
+ tax_unit:
263
+ members: [person1, person2]
264
+ filing_status: JOINT
265
+ households:
266
+ household:
267
+ members: [person1, person2]
268
+ state_code: MT
269
+ output:
270
+ # Only person2 gets $5,500 old age subtraction
271
+ mt_old_age_subtraction: [0, 5_500]
272
+ # Federal AGI: 25,000 + 20,000 = 45,000
273
+ # Total subtractions: 0 + 5,500 = 5,500
274
+ # AGI after subtractions: 45,000 - 5,500 = 39,500
275
+ # Deductions: max(6,000, 4,000) = 6,000
276
+ # Exemptions: 1,000 + 1,000 = 2,000
277
+ # Taxable income: 39,500 - 6,000 - 2,000 = 31,500
278
+ mt_taxable_income_joint: [31_500, 0]
@@ -13,3 +13,27 @@
13
13
  state_code: MT
14
14
  output:
15
15
  mt_old_age_subtraction: 5_500
16
+
17
+ - name: MT joint filers both over 65 receive double old age subtraction
18
+ absolute_error_margin: 0.9
19
+ period: 2024
20
+ input:
21
+ people:
22
+ spouse1:
23
+ age: 71
24
+ adjusted_gross_income_person: 44_500
25
+ spouse2:
26
+ age: 78
27
+ adjusted_gross_income_person: 44_500
28
+ tax_units:
29
+ tax_unit:
30
+ members: [spouse1, spouse2]
31
+ filing_status: JOINT
32
+ households:
33
+ household:
34
+ state_code: MT
35
+ members: [spouse1, spouse2]
36
+ output:
37
+ # Each over-65 spouse gets $5,500 subtraction
38
+ mt_old_age_subtraction: [5_500, 5_500]
39
+ mt_subtractions: [5_500, 5_500]
@@ -26,7 +26,7 @@ class tax_unit_itemizes(Variable):
26
26
  "state_itemized_deductions", period
27
27
  )
28
28
  # Use a small tolerance for floating-point comparison due to floating point imprecision
29
- TOLERANCE = 0.0001
29
+ TOLERANCE = 0.01
30
30
  federal_tax_equal = (
31
31
  np.abs(
32
32
  tax_liability_if_itemizing - tax_liability_if_not_itemizing
@@ -1,7 +1,7 @@
1
1
  from policyengine_us.model_api import *
2
2
 
3
3
 
4
- class mt_agi(Variable):
4
+ class mt_agi_indiv(Variable):
5
5
  value_type = float
6
6
  entity = Person
7
7
  label = "Montana Adjusted Gross Income for each individual"
@@ -0,0 +1,43 @@
1
+ from policyengine_us.model_api import *
2
+
3
+
4
+ class mt_agi_joint(Variable):
5
+ value_type = float
6
+ entity = TaxUnit
7
+ label = "Montana AGI for joint filers (tax unit level)"
8
+ unit = USD
9
+ definition_period = YEAR
10
+ defined_for = StateCode.MT
11
+ documentation = "Montana AGI calculated at tax unit level for joint filers, pooling income and subtractions before applying them"
12
+
13
+ def formula(tax_unit, period, parameters):
14
+ # Pool federal AGI at tax unit level
15
+ agi = add(tax_unit, period, ["adjusted_gross_income_person"])
16
+
17
+ # Pool Montana additions at tax unit level
18
+ additions = add(tax_unit, period, ["mt_additions"])
19
+
20
+ # Pool Montana subtractions at tax unit level
21
+ # This allows spouse's subtractions to offset head's income
22
+ subtractions = add(tax_unit, period, ["mt_subtractions"])
23
+
24
+ # Apply pooled subtractions to pooled income
25
+ reduced_agi = max_(agi + additions - subtractions, 0)
26
+
27
+ # Montana taxable social security benefits can be either addition or subtraction
28
+ # if mt_taxable_social_security - taxable_social_security > 0, then addition, else subtraction
29
+
30
+ p = parameters(period).gov.states.mt.tax.income.social_security
31
+ if p.applies:
32
+ # 2023 and before: apply lines 21-24 adjustment for social security
33
+ taxable_ss = add(tax_unit, period, ["taxable_social_security"])
34
+ mt_taxable_ss = add(
35
+ tax_unit, period, ["mt_taxable_social_security"]
36
+ )
37
+ adjusted_mt_ss_difference = mt_taxable_ss - taxable_ss
38
+ tax_unit_mt_agi = max_(reduced_agi + adjusted_mt_ss_difference, 0)
39
+ else:
40
+ # 2024 and after: no longer apply the social security adjustment
41
+ tax_unit_mt_agi = reduced_agi
42
+
43
+ return tax_unit_mt_agi
@@ -25,7 +25,7 @@ class mt_child_dependent_care_expense_deduction(Variable):
25
25
  # Line 2
26
26
  capped_expenses = min_(care_expenses, cap)
27
27
  # Line 3
28
- agi = person("mt_agi", period)
28
+ agi = person("mt_agi_indiv", period)
29
29
  # Line 6
30
30
  reduction = p.phase_out.calc(agi)
31
31
  # The deduction has to be allocated equally between spouses
@@ -17,7 +17,7 @@ class mt_federal_income_tax_deduction_for_federal_itemization_indiv(Variable):
17
17
  unit_deds = person.tax_unit(
18
18
  "mt_federal_income_tax_deduction_for_federal_itemization", period
19
19
  )
20
- person_agi = person("mt_agi", period)
20
+ person_agi = person("mt_agi_indiv", period)
21
21
  total_agi = person.tax_unit.sum(person_agi)
22
22
 
23
23
  prorate = np.zeros_like(total_agi)
@@ -17,7 +17,7 @@ class mt_federal_income_tax_deduction_indiv(Variable):
17
17
  unit_deds = person.tax_unit(
18
18
  "mt_federal_income_tax_deduction_unit", period
19
19
  )
20
- person_agi = person("mt_agi", period)
20
+ person_agi = person("mt_agi_indiv", period)
21
21
  total_agi = person.tax_unit.sum(person_agi)
22
22
 
23
23
  prorate = np.zeros_like(total_agi)
@@ -19,6 +19,6 @@ class mt_medical_expense_deduction_indiv(Variable):
19
19
  p = parameters(period).gov.irs.deductions.itemized.medical
20
20
  # Law does not define Montana AGI as the cap.
21
21
  # Tax form points to page 1, line 14, which is Montana AGI.
22
- medical_floor = p.floor * person("mt_agi", period)
22
+ medical_floor = p.floor * person("mt_agi_indiv", period)
23
23
  is_head_or_spouse = person("is_tax_unit_head_or_spouse", period)
24
24
  return is_head_or_spouse * max_(0, expense - medical_floor)
@@ -21,6 +21,8 @@ class mt_medical_expense_deduction_joint(Variable):
21
21
  p = parameters(period).gov.irs.deductions.itemized.medical
22
22
  # Law does not define Montana AGI as the cap.
23
23
  # Tax form points to page 1, line 14, which is Montana AGI.
24
- medical_floor = p.floor * add(person.tax_unit, period, ["mt_agi"])
24
+ medical_floor = p.floor * add(
25
+ person.tax_unit, period, ["mt_agi_indiv"]
26
+ )
25
27
  is_head = person("is_tax_unit_head", period)
26
28
  return is_head * max_(0, expense - medical_floor)
@@ -22,7 +22,7 @@ class mt_standard_deduction_indiv(Variable):
22
22
 
23
23
  if p.state_specific_deduction_applies:
24
24
  # ── Pre-2024 MT-specific calculation ────────────────────────────────
25
- agi = person("mt_agi", period)
25
+ agi = person("mt_agi_indiv", period)
26
26
  floor = p.floor[filing_status]
27
27
  cap = p.cap[filing_status]
28
28
  uncapped = p.rate * agi
@@ -19,7 +19,7 @@ class mt_standard_deduction_joint(Variable):
19
19
 
20
20
  if p.state_specific_deduction_applies:
21
21
  # ── Pre-2024 MT-specific calculation ───────────────────────────────
22
- agi = add(person.tax_unit, period, ["mt_agi"])
22
+ agi = add(person.tax_unit, period, ["mt_agi_indiv"])
23
23
  floor = p.floor[filing_status]
24
24
  cap = p.cap[filing_status]
25
25
  uncapped = p.rate * agi
@@ -14,7 +14,7 @@ class mt_pre_dependent_exemption_taxable_income_indiv(Variable):
14
14
  )
15
15
 
16
16
  def formula(person, period, parameters):
17
- mt_agi = person("mt_agi", period)
17
+ mt_agi = person("mt_agi_indiv", period)
18
18
  exemptions = person("mt_personal_exemptions_indiv", period)
19
19
  deductions = person("mt_deductions_indiv", period)
20
20
  return max_(mt_agi - exemptions - deductions, 0)
@@ -15,8 +15,12 @@ class mt_taxable_income_joint(Variable):
15
15
 
16
16
  def formula(person, period, parameters):
17
17
  is_head = person("is_tax_unit_head", period)
18
- agi = person("mt_agi", period)
19
- total_agi = is_head * person.tax_unit.sum(agi)
18
+
19
+ # For joint filers, use mt_agi_joint which pools income and subtractions
20
+ # at tax unit level before applying them. This ensures subtractions from
21
+ # one spouse can offset income from the other spouse.
22
+ total_agi = person.tax_unit("mt_agi_joint", period)
23
+
20
24
  standard_deduction = add(
21
25
  person.tax_unit, period, ["mt_standard_deduction_joint"]
22
26
  )
@@ -31,4 +35,4 @@ class mt_taxable_income_joint(Variable):
31
35
  ["mt_personal_exemptions_joint", "mt_dependent_exemptions_person"],
32
36
  )
33
37
 
34
- return max_(0, total_agi - deductions - exemptions)
38
+ return is_head * max_(0, total_agi - deductions - exemptions)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: policyengine-us
3
- Version: 1.403.0
3
+ Version: 1.403.2
4
4
  Summary: Add your description here.
5
5
  Author-email: PolicyEngine <hello@policyengine.org>
6
6
  License-File: LICENSE