diff --git a/changelog.d/fix-9814-dependent-income-ar-ms-nj-ia.fixed.md b/changelog.d/fix-9814-dependent-income-ar-ms-nj-ia.fixed.md new file mode 100644 index 00000000000..ed1ff9927b2 --- /dev/null +++ b/changelog.d/fix-9814-dependent-income-ar-ms-nj-ia.fixed.md @@ -0,0 +1 @@ +Leave tax unit dependents' income out of Arkansas, Mississippi and New Jersey adjusted gross income and Iowa net income; dependents report it on their own state returns. Mississippi now limits only the head's and spouse's own capital loss, so a dependent's capital gains no longer change the filers' Mississippi adjusted gross income. diff --git a/policyengine_us/parameters/gov/states/ms/tax/income/income_sources.yaml b/policyengine_us/parameters/gov/states/ms/tax/income/income_sources.yaml index b0f936b4f34..1fba876a4c9 100644 --- a/policyengine_us/parameters/gov/states/ms/tax/income/income_sources.yaml +++ b/policyengine_us/parameters/gov/states/ms/tax/income/income_sources.yaml @@ -5,7 +5,10 @@ values: - self_employment_income - sstb_self_employment_income - partnership_s_corp_income - - loss_limited_net_capital_gains_person + # Line 40: the filers' own capital gains and losses, with a net loss + # limited as on the federal return (26 USC 1211(b)). A dependent's + # gains and losses are on the dependent's own return. + - filer_loss_limited_net_capital_gains_person - rental_income # Line 41: rental real estates, royalties, partnerships, S corporations, # trusts, etc. (income or loss from federal Schedule E) diff --git a/policyengine_us/tests/core/test_medical_expense_premium_conservation.py b/policyengine_us/tests/core/test_medical_expense_premium_conservation.py index f5be96ce466..a6f6721a641 100644 --- a/policyengine_us/tests/core/test_medical_expense_premium_conservation.py +++ b/policyengine_us/tests/core/test_medical_expense_premium_conservation.py @@ -293,9 +293,6 @@ def test_nj_excludes_each_dependents_deduction_only_from_their_own_premiums( simulation = _nj_simulation(members, reference_system) paid = np.array([sum(person["premiums"] for person in unit) for unit in members]) other = np.array([sum(person["other"] for person in unit) for unit in members]) - deductions = np.array( - [sum(person["deduction"] for person in unit) for unit in members] - ) person_exclusions = [ [min(person["deduction"], person["premiums"]) for person in unit] for unit in members @@ -307,7 +304,10 @@ def test_nj_excludes_each_dependents_deduction_only_from_their_own_premiums( ) excluded = np.array([sum(exclusions) for exclusions in person_exclusions]) nj_deduction = simulation.calculate("nj_medical_expense_deduction", PERIOD) - actual_exclusion = paid + other + deductions - nj_deduction + # The dependents' separate SE deductions are on their own returns, with + # their income (nj_agi leaves both out), so the parent's deduction does + # not add them. + actual_exclusion = paid + other - nj_deduction # C.54A:3-3(c)(3) excludes only amounts separately deducted. Each person's # exclusion is bounded before summation, so no ALD can consume a different @@ -316,7 +316,7 @@ def test_nj_excludes_each_dependents_deduction_only_from_their_own_premiums( assert (actual_exclusion >= 0).all() assert (actual_exclusion <= excluded).all() np.testing.assert_array_equal(actual_exclusion, excluded) - np.testing.assert_array_equal(nj_deduction, paid + other + deductions - excluded) + np.testing.assert_array_equal(nj_deduction, paid + other - excluded) # Flat strategies keep generation small while varying independent caller diff --git a/policyengine_us/tests/core/test_state_tax_head_spouse_swap_invariance.py b/policyengine_us/tests/core/test_state_tax_head_spouse_swap_invariance.py index 6a2c81e71e9..71b4b936235 100644 --- a/policyengine_us/tests/core/test_state_tax_head_spouse_swap_invariance.py +++ b/policyengine_us/tests/core/test_state_tax_head_spouse_swap_invariance.py @@ -11,8 +11,11 @@ On main these depended on the label: - Arkansas, Delaware, Iowa, Mississippi and Montana put the dependents' income the model counts on the head's column before taxing each column separately. - It now goes on the column of the spouse with the greater income of their - own, and an exact tie splits it (move_dependent_amounts_to_filer). + In Delaware and Montana it now goes on the column of the spouse with the + greater income of their own, and an exact tie splits it + (move_dependent_amounts_to_filer). Arkansas, Iowa and Mississippi now leave + a dependent's income off the filers' columns altogether, since it belongs + on the dependent's own return (PolicyEngine/policyengine-us#9814). - Delaware's combined separate return routed the EITC and the dependent care credit by label on equal incomes and split the dependent personal credits with a heuristic. They are now chosen together to minimise tax. @@ -45,10 +48,11 @@ is finite and the same to the cent. 2. The per-person columns (`ar_agi_indiv`, `de_agi_indiv`, `ia_net_income`, `ms_agi`, `mt_agi_indiv`) are the same for each person, to the cent. -3. Differential: in Arkansas, Delaware, Iowa and Mississippi those columns - equal an independent numpy allocation of each person's own amount, which - conserves the tax unit's total. Montana's own amounts are not - reconstructed, so it is checked by 1 and 2 only. +3. Differential: in Delaware the column equals an independent numpy + allocation of each person's own amount, which conserves the tax unit's + total. In Arkansas, Iowa and Mississippi it equals each filer's own amount, + and zero for a dependent. Montana's own amounts are not reconstructed, so + it is checked by 1 and 2 only. """ import numpy as np @@ -71,6 +75,9 @@ "state_income_tax_before_refundable_credits", ] COLUMNS = ["ar_agi_indiv", "de_agi_indiv", "ia_net_income", "ms_agi", "mt_agi_indiv"] +# Columns that move the dependents' amounts to a filer's column; the others +# leave a dependent's amount off the filers' return. +MOVES_DEPENDENT_AMOUNTS = ["de_agi_indiv"] def money(high): @@ -475,11 +482,9 @@ def calc(name): return calc("ia_gross_income") - calc("ia_income_adjustments") if column == "ms_agi": p = sim.tax_benefit_system.parameters(year).gov.states.ms.tax.income - net = sum(calc(source) for source in p.income_sources) - calc( + return sum(calc(source) for source in p.income_sources) - calc( "ms_agi_adjustments" ) - dependent = calc("is_tax_unit_dependent").astype(bool) - return np.where(dependent, np.maximum(net, 0), net) raise ValueError(column) @@ -547,7 +552,10 @@ def _check(units, year): # 3. Differential against the numpy allocation, where the column # applies (its state, and its years for Iowa). own = _own_amounts(sim, year, column) - reference = _reference_allocation(own, unit, head, spouse, dependent) + if column in MOVES_DEPENDENT_AMOUNTS: + reference = _reference_allocation(own, unit, head, spouse, dependent) + else: + reference = np.where(dependent, 0.0, own) in_state = states[unit // 2] == column[:2].upper() np.testing.assert_allclose( values[in_state], diff --git a/policyengine_us/tests/policy/baseline/gov/states/ar/tax/income/ar_agi_indiv.yaml b/policyengine_us/tests/policy/baseline/gov/states/ar/tax/income/ar_agi_indiv.yaml index 586850ff1bb..b7b3b1af2aa 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ar/tax/income/ar_agi_indiv.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ar/tax/income/ar_agi_indiv.yaml @@ -19,7 +19,7 @@ output: ar_agi_indiv: [900, 1_800] -- name: Dependent AGI goes to the spouse with the greater AGI, AGI capped at 0 +- name: A dependent's AGI stays off the filers' columns, and a negative column is floored at 0 period: 2022 input: people: @@ -43,7 +43,9 @@ members: [person1, person2, person3] state_code: AR output: - ar_agi_indiv: [1_200, 0, 0] + # The dependent's 300 goes on the dependent's own return (Ark. Code + # 26-51-801(a)-(b)). The spouse's 100 - 200 is floored at 0. + ar_agi_indiv: [900, 0, 0] - name: Single filer with 401(k) distributions included in gross income and exempted period: 2024 @@ -65,7 +67,7 @@ ar_capped_retirement_or_disability_benefits_exemption_person: 6_000 ar_agi_indiv: 50_000 -- name: Dependent AGI goes to the spouse with the greater AGI when that spouse is not the head +- name: A dependent's AGI stays off the filers' columns when the head and spouse labels are exchanged period: 2022 input: people: @@ -96,9 +98,9 @@ state_code: AR output: # The same couple as above with the head and spouse labels exchanged. - ar_agi_indiv: [1_200, 0, 0] + ar_agi_indiv: [900, 0, 0] -- name: Spouses with equal AGI split the dependent AGI equally +- name: Spouses with equal AGI keep their own AGI; the dependent's is on the dependent's own return period: 2022 input: people: @@ -128,7 +130,7 @@ members: [person1, person2, person3] state_code: AR output: - ar_agi_indiv: [1_150, 1_150, 0] + ar_agi_indiv: [1_000, 1_000, 0] - name: Only taxable disability benefits enter Arkansas gross income on line 18. period: 2024 @@ -152,3 +154,53 @@ # The 3,000 is under the 6,000 cap and exempt in full. ar_capped_retirement_or_disability_benefits_exemption_person: 3_000 ar_agi_indiv: 30_000 + +- name: A dependent child's interest stays off the parent's Arkansas AGI + period: 2025 + input: + people: + parent: + age: 40 + employment_income: 40_000 + child: + age: 16 + taxable_interest_income: 8_000 + tax_units: + tax_unit: + members: [parent, child] + households: + household: + members: [parent, child] + state_code: AR + output: + # The child files their own AR1000F if their gross income is over the + # filing minimum (Ark. Code 26-51-801(a)); the parent's columns hold only + # the parent's 40,000 of wages, as with no child income. + ar_gross_income_indiv: [40_000, 8_000] + ar_agi_indiv: [40_000, 0] + ar_agi_joint: [40_000, 0] + +- name: A dependent child's wages stay off the parent's Arkansas AGI and tax + period: 2025 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 40 + employment_income: 40_000 + child: + age: 16 + employment_income: 5_000 + tax_units: + tax_unit: + members: [parent, child] + households: + household: + members: [parent, child] + state_code: AR + output: + ar_agi_indiv: [40_000, 0] + ar_agi_joint: [40_000, 0] + # The same tax as the parent with a child who has no income (issue + # #9814 reproduction): 957. + ar_income_tax: 957 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ar/tax/income/ar_agi_joint.yaml b/policyengine_us/tests/policy/baseline/gov/states/ar/tax/income/ar_agi_joint.yaml index b481feb47b3..c4b6d7fad34 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ar/tax/income/ar_agi_joint.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ar/tax/income/ar_agi_joint.yaml @@ -19,7 +19,7 @@ output: ar_agi_joint: [900, 1_800] -- name: Dependent AGI gets attributed to the head; spouse net income is not floored on a joint return +- name: A dependent's AGI stays off the filers' return; spouse net income is not floored on a joint return period: 2022 input: people: @@ -43,11 +43,11 @@ members: [person1, person2, person3] state_code: AR output: - # Head: 900 + dependent's 300 (dependent income floored at 0, then - # attributed to the head). Spouse: 100 - 200 = -100. On a joint return all + # Head: 900. The dependent's 300 goes on the dependent's own return (Ark. + # Code 26-51-801(a)-(b)). Spouse: 100 - 200 = -100. On a joint return all # income goes in Column A, so the spouse's amount offsets the head's; the # tax unit total is floored at 0 only when used as a tax base. - ar_agi_joint: [1_200, -100, 0] + ar_agi_joint: [900, -100, 0] - name: A spouse's business loss offsets the other spouse's income on a joint return period: 2024 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ar/tax/income/ar_gambling_winnings_and_losses.yaml b/policyengine_us/tests/policy/baseline/gov/states/ar/tax/income/ar_gambling_winnings_and_losses.yaml index 2dc500e7140..9bac793802d 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ar/tax/income/ar_gambling_winnings_and_losses.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ar/tax/income/ar_gambling_winnings_and_losses.yaml @@ -42,7 +42,7 @@ ar_gross_income_joint: 60_000 ar_gross_income_indiv: 60_000 -- name: A dependent's gambling winnings go to the head's Arkansas AGI +- name: A dependent's gambling winnings stay off the head's Arkansas AGI period: 2025 input: people: @@ -61,7 +61,8 @@ state_code: AR output: ar_gross_income_joint: [50_000, 1_000] - ar_agi_joint: [51_000, 0] + # The child's winnings go on the child's own Arkansas return. + ar_agi_joint: [50_000, 0] - name: 2015 itemizer with losses below winnings is taxed on net winnings period: 2015 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ia/tax/income/ia_net_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/ia/tax/income/ia_net_income.yaml index 92178c63871..c5f5a26c227 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ia/tax/income/ia_net_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ia/tax/income/ia_net_income.yaml @@ -1,4 +1,4 @@ -- name: IA net income unit test 1 +- name: Dependents' gross income stays off the head's Iowa net income absolute_error_margin: 0.01 period: 2021 input: @@ -24,9 +24,11 @@ members: [person1, person2, person3] state_code: IA output: - ia_net_income: [9_400, 0, 0] + # 10,000 - 2,000. The dependents' 800 and 600 go on their own Iowa + # returns if they must file (Iowa Code 422.13(1)(a)). + ia_net_income: [8_000, 0, 0] -- name: Spouses with equal net income split the dependent net income equally +- name: Spouses keep their own net income; the dependent's is on the dependent's own return absolute_error_margin: 0.01 period: 2022 input: @@ -52,10 +54,9 @@ members: [person1, person2, person3] state_code: IA output: - # The spouses' net incomes are equal, so they split the dependent's. - ia_net_income: [8_300, 8_300, 0] + ia_net_income: [8_000, 8_000, 0] -- name: Dependent net income goes to the spouse with the greater net income, here not the head +- name: A dependent's net income stays off both spouses' columns absolute_error_margin: 0.01 period: 2021 input: @@ -87,9 +88,9 @@ members: [person1, person2, person3] state_code: IA output: - ia_net_income: [8_000, 30_600, 0] + ia_net_income: [8_000, 30_000, 0] -- name: Dependent net income stays with the same spouse when the head and spouse labels are exchanged +- name: A dependent's net income stays off both spouses' columns when the head and spouse labels are exchanged absolute_error_margin: 0.01 period: 2021 input: @@ -121,7 +122,7 @@ members: [person1, person2, person3] state_code: IA output: - ia_net_income: [8_000, 30_600, 0] + ia_net_income: [8_000, 30_000, 0] - name: 2022 single filer with wages and traditional IRA distribution period: 2022 @@ -167,4 +168,49 @@ ia_pension_exclusion: 10_000 ia_net_income: 50_000 +- name: A dependent child's interest stays off the parent's Iowa net income and 2022 tax + period: 2022 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 40 + employment_income: 40_000 + child: + age: 16 + taxable_interest_income: 8_000 + tax_units: + tax_unit: + members: [parent, child] + households: + household: + members: [parent, child] + state_code: IA + output: + # Iowa net income starts from federal AGI (Iowa Code 422.7), which leaves + # out the child's interest; the child files their own IA 1040 with net + # income of 5,000 or more (Iowa Code 422.13(1)(a)). + ia_net_income: [40_000, 0] + # The same tax as with no child income (issue #9814 reproduction). + ia_income_tax: 1_332.05 +- name: A dependent child's wages stay off the parent's Iowa net income + period: 2025 + input: + people: + parent: + age: 40 + employment_income: 40_000 + child: + age: 16 + employment_income: 5_000 + tax_units: + tax_unit: + members: [parent, child] + households: + household: + members: [parent, child] + state_code: IA + output: + ia_net_income: [40_000, 0] + state_agi: 40_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ms/tax/income/adjustments/ms_health_savings_account_adjustment.yaml b/policyengine_us/tests/policy/baseline/gov/states/ms/tax/income/adjustments/ms_health_savings_account_adjustment.yaml index 8776ba67bff..e3bb23078c8 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ms/tax/income/adjustments/ms_health_savings_account_adjustment.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ms/tax/income/adjustments/ms_health_savings_account_adjustment.yaml @@ -120,7 +120,7 @@ # The return claims 1,500, shared 1:2. ms_health_savings_account_adjustment: [500, 1_000] -- name: A dependent's income is not reduced by the filer's deduction, nor by the dependent's own account +- name: A dependent's income and own account stay off the filers' Mississippi AGI period: 2025 input: people: @@ -148,9 +148,9 @@ # account is left out. ms_health_savings_account_adjustment: [1_500, 1_500, 0] ms_agi_adjustments: [1_500, 1_500, 0] - # The child's 5,000 reaches the Mississippi AGI of the spouse with the - # greater income in full: 40,000 - 1,500 + 5,000. - ms_agi: [43_500, 28_500, 0] + # The child's 5,000 goes on the child's own return: 40,000 - 1,500 and + # 30,000 - 1,500. + ms_agi: [38_500, 28_500, 0] - name: A married person filing a separate federal return takes their own deduction period: 2025 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ms/tax/income/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/ms/tax/income/integration.yaml index fba90149b49..1dd96ae472e 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ms/tax/income/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ms/tax/income/integration.yaml @@ -19,18 +19,31 @@ members: [person1, person2, person3] state_code: MS output: + # The child's 3,000 of wages go on the child's own return, so each + # spouse's column holds their own wages. + ms_agi: [20_000, 18_000, 0] ms_deductions_indiv: [2_300, 2_300, 0] - ms_deductions_joint: [3_051.0203, 1_548.9795, 0] + # The 4,600 joint standard deduction and 15,000 of exemptions (12,000 + # married, 1,500 age 65 or older, 1,500 dependent), 19,600 in all, are + # allocated to minimize tax: 10,000 to the head, leaving 10,000 of + # taxable income at the top of the 4% bracket, and 9,600 to the spouse. + # 4,600 x 10,000 / 19,600 = 2,346.94 and 15,000 x 10,000 / 19,600 = + # 7,653.06. + ms_deductions_joint: [2_346.9387, 2_253.0613, 0] ms_total_exemptions_indiv: [7_500, 7_500, 0] - ms_total_exemptions_joint: [9_948.9795, 5_051.02, 0] + ms_total_exemptions_joint: [7_653.0612, 7_346.9388, 0] ms_total_exemptions: 15_000 - ms_taxable_income_indiv: [13_200, 8_200, 0] - ms_taxable_income_joint: [10_000, 11_400, 0] - ms_income_tax_before_credits_indiv: [390, 158, 0] - ms_income_tax_before_credits_joint: [230, 300, 0] + # 20,000 - 2,300 - 7,500 and 18,000 - 2,300 - 7,500. + ms_taxable_income_indiv: [10_200, 8_200, 0] + ms_taxable_income_joint: [10_000, 8_400, 0] + # 2021 rates: 3% of 4,001-5,000, 4% of 5,001-10,000, 5% above. + # 10,200: 30 + 200 + 10 = 240; 8,200: 30 + 128 = 158. + ms_income_tax_before_credits_indiv: [240, 158, 0] + # 10,000: 30 + 200 = 230; 8,400: 30 + 136 = 166. + ms_income_tax_before_credits_joint: [230, 166, 0] + # 396 on the combined return is less than 398 on separate returns. ms_files_separately: false - ms_agi: [23_000, 18_000, 0] - ms_income_tax: 530 + ms_income_tax: 396 - name: Tax unit with taxsimid 99991 in g21.its.csv and g21.ots.csv absolute_error_margin: 0.01 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ms/tax/income/ms_agi.yaml b/policyengine_us/tests/policy/baseline/gov/states/ms/tax/income/ms_agi.yaml index 35ad15eefab..5ad6f33a912 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ms/tax/income/ms_agi.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ms/tax/income/ms_agi.yaml @@ -1,4 +1,4 @@ -- name: The dependent income goes to the spouse with the greater income, here not the head +- name: A dependent's income stays off both spouses' columns period: 2021 input: people: @@ -22,10 +22,11 @@ members: [person1, person2, person3] state_code: MS output: - # The spouse's $10,000 is greater than the head's $9,000. - ms_agi: [9_000, 13_000, 0] + # Each spouse's column holds their own income; the dependent's 3,000 goes + # on the dependent's own return (35 Miss. Admin. Code Pt. III, 10.01.104). + ms_agi: [9_000, 10_000, 0] -- name: The dependent income stays with the same spouse when the head and spouse labels are exchanged +- name: A dependent's income stays off both spouses' columns when the head and spouse labels are exchanged period: 2021 input: people: @@ -55,9 +56,9 @@ members: [person1, person2, person3] state_code: MS output: - ms_agi: [9_000, 13_000, 0] + ms_agi: [9_000, 10_000, 0] -- name: Spouses with equal income split the dependent income equally +- name: Spouses with equal income keep their own income; the dependent's is on the dependent's own return period: 2021 input: people: @@ -87,9 +88,9 @@ members: [person1, person2, person3] state_code: MS output: - ms_agi: [11_500, 11_500, 0] + ms_agi: [10_000, 10_000, 0] -- name: A dependent's negative income is not moved to a spouse +- name: A dependent's negative income does not reduce a spouse's column period: 2021 input: people: @@ -139,7 +140,7 @@ output: ms_agi: 100_000 -- name: A dependent's self-employment adjustments are subtracted once, with the dependent's income +- name: A dependent's self-employment income and adjustments stay on the dependent's own return period: 2025 absolute_error_margin: 0.01 input: @@ -163,9 +164,9 @@ # The child's own adjustments: one-half of self-employment tax (Schedule # SE line 13, 706.48), health insurance (1,000) and SEP (500), 2,206.48. ms_agi_adjustments: [0, 2_206.48] - # The child's income and adjustments reach the parent's Mississippi AGI - # once: 40,000 + 10,000 - 2,206.48. - ms_agi: [47_793.52, 0] + # The child's income and adjustments both go on the child's own + # Mississippi return, so the parent's AGI is the parent's 40,000. + ms_agi: [40_000, 0] - name: Each spouse subtracts only their own self-employed health insurance period: 2025 @@ -213,3 +214,184 @@ # Line 41 is "the income or loss from activities reported on Federal # Schedule E": 30,000 - 5,000. ms_agi: 25_000 + +- name: A dependent child's interest stays off the parent's Mississippi AGI and tax + period: 2025 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 40 + employment_income: 40_000 + child: + age: 16 + taxable_interest_income: 8_000 + tax_units: + tax_unit: + members: [parent, child] + households: + household: + members: [parent, child] + state_code: MS + output: + # A minor resident files their own Form 80-105 when their gross income is + # over the personal exemption plus the standard deduction (2024 Form + # 80-100 instructions, p. 3). The parent's AGI is the parent's wages. + ms_agi: [40_000, 0] + # Head of household: 40,000 - 3,400 standard deduction - 8,000 exemption + # - 1,500 for the dependent = 27,100 of taxable income; 4.4% of the + # 17,100 over 10,000 = 752.40, the same as with no child income. + ms_income_tax: 752.4 + +- name: A dependent child's wages stay off the parent's Mississippi AGI and tax + period: 2025 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 40 + employment_income: 40_000 + child: + age: 16 + employment_income: 5_000 + tax_units: + tax_unit: + members: [parent, child] + households: + household: + members: [parent, child] + state_code: MS + output: + ms_agi: [40_000, 0] + ms_income_tax: 752.4 + +- name: A parent's capital loss is limited to 3,000 when the dependent child has no capital gains + period: 2025 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 40 + employment_income: 40_000 + long_term_capital_gains: -10_000 + child: + age: 16 + tax_units: + tax_unit: + members: [parent, child] + households: + household: + members: [parent, child] + state_code: MS + output: + # Mississippi limits a capital loss as on the federal return (2024 Form + # 80-100 instructions, line 40; 26 USC 1211(b)): 40,000 - 3,000. + ms_agi: [37_000, 0] + # Head of household: 37,000 - 3,400 standard deduction - 8,000 exemption + # - 1,500 for the dependent = 24,100; 4.4% of the 14,100 over 10,000. + ms_income_tax: 620.4 + +- name: A dependent child's capital gain smaller than the parent's loss does not change the parent's AGI + period: 2025 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 40 + employment_income: 40_000 + long_term_capital_gains: -10_000 + child: + age: 16 + long_term_capital_gains: 5_000 + tax_units: + tax_unit: + members: [parent, child] + households: + household: + members: [parent, child] + state_code: MS + output: + # The child's 5,000 gain is on the child's own return. The parent's + # return has only the parent's 10,000 loss, limited to 3,000: + # 40,000 - 3,000. (Allocating the tax unit's limited loss over every + # member gave the parent -6,000 and an AGI of 34,000.) + ms_agi: [37_000, 0] + ms_income_tax: 620.4 + +- name: A dependent child's capital gain larger than the parent's loss does not lift the parent's loss limit + period: 2025 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 40 + employment_income: 40_000 + long_term_capital_gains: -10_000 + child: + age: 16 + long_term_capital_gains: 20_000 + tax_units: + tax_unit: + members: [parent, child] + households: + household: + members: [parent, child] + state_code: MS + output: + # The parent's own net loss is 10,000, limited to 3,000: 40,000 - 3,000. + # (A tax unit net gain of 10,000 left the parent's whole 10,000 loss + # unlimited and gave an AGI of 30,000.) + ms_agi: [37_000, 0] + ms_income_tax: 620.4 + +- name: A dependent child's capital loss does not reduce the parent's capital gain + period: 2025 + input: + people: + parent: + age: 40 + employment_income: 40_000 + long_term_capital_gains: 5_000 + child: + age: 16 + short_term_capital_gains: -10_000 + tax_units: + tax_unit: + members: [parent, child] + households: + household: + members: [parent, child] + state_code: MS + output: + # The child's loss is on the child's own return; the parent keeps the + # whole 5,000 gain: 40,000 + 5,000. + ms_agi: [45_000, 0] + +- name: Spouses share their limited capital loss in proportion to their own losses + period: 2025 + input: + people: + head: + age: 45 + employment_income: 30_000 + long_term_capital_gains: -8_000 + spouse: + age: 45 + employment_income: 20_000 + long_term_capital_gains: -2_000 + child: + age: 15 + long_term_capital_gains: 50_000 + tax_units: + tax_unit: + members: [head, spouse, child] + households: + household: + members: [head, spouse, child] + state_code: MS + output: + # The filers' net loss is 10,000, limited to 3,000 for a joint federal + # return. The head takes 8,000 / 10,000 of it (2,400) and the spouse + # 2,000 / 10,000 (600). The child's 50,000 gain is on the child's own + # return: 30,000 - 2,400 and 20,000 - 600. + ms_agi: [27_600, 19_400, 0] diff --git a/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/deductions/nj_medical_expense_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/deductions/nj_medical_expense_deduction.yaml index e2164500ed8..24c940de918 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/deductions/nj_medical_expense_deduction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/deductions/nj_medical_expense_deduction.yaml @@ -57,7 +57,7 @@ output: nj_medical_expense_deduction: 2_600 -- name: A dependent's self-employed health insurance counts where New Jersey counts the dependent's income +- name: A dependent's self-employed health insurance stays on the dependent's own return period: 2025 absolute_error_margin: 0.01 input: @@ -77,10 +77,11 @@ members: [parent, child] state_code: NJ output: - # New Jersey AGI includes the child's 10,000 of Schedule C income, so the - # child's 1,000 of self-employed health insurance is deducted too. - nj_agi: 50_000 - nj_medical_expense_deduction: 1_000 + # The child's 10,000 of Schedule C income and the child's 1,000 + # self-employed health insurance deduction both go on the child's own + # NJ-1040, so neither is on the parent's return. + nj_agi: 40_000 + nj_medical_expense_deduction: 0 - name: New Jersey allows after-tax premiums above two percent of gross income period: 2025 @@ -170,10 +171,12 @@ members: [parent, child] state_code: NJ output: - nj_agi: 40_000 + # The child's 10,000 of Schedule C income is on the child's own return. + nj_agi: 30_000 dependents_self_employed_health_insurance_ald: 1_000 - # Only the 500 of other expenses remains subject to the 800 floor. - nj_medical_expense_deduction: 1_000 + # The child deducts the 1,000 of premiums on their own return. Only the + # 500 of other expenses remains, under the 600 floor (2% of 30,000). + nj_medical_expense_deduction: 0 - name: New Jersey cannot subtract one dependent's deduction from another dependent's premiums period: 2025 @@ -203,9 +206,10 @@ itemized_medical_expenses: 1_000 # C.54A:3-3(c)(3) excludes separately deducted amounts, not another # person's paid premiums: min(1,000, 0) + min(0, 1,000) = 0 excluded. - # Separate SE deduction 1,000 + general medical premiums 1,000 = 2,000. # https://pub.njleg.gov/bills/9899/PL99/222_.HTM - nj_medical_expense_deduction: 2_000 + # Dependent A's separate SE deduction is on dependent A's own return, so + # only the 1,000 of general medical premiums is deducted. + nj_medical_expense_deduction: 1_000 - name: New Jersey combines pretax premiums with separately deducted SE premiums period: 2025 @@ -256,10 +260,12 @@ # only the dependent's Medicare Part B premiums: $185 x 12 = $2,220. itemized_medical_expenses_long_term_care_premiums: 0 itemized_medical_expenses: 2_220 - nj_agi: 104_000 - # 3,000 + max(0, 2,220 - 0.02 x 104,000) = 3,000 + 140 = 3,140. The - # deduction took none of the Part B premiums. - nj_medical_expense_deduction: 3_140 + # The dependent's 4,000 of Schedule C income, and their 3,000 deduction, + # are on the dependent's own return. + nj_agi: 100_000 + # max(0, 2,220 - 0.02 x 100,000) = 220. The dependent's deduction took + # none of the Part B premiums. + nj_medical_expense_deduction: 220 - name: A filer's own deduction is not removed again from the federal base period: 2025 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/exclusions/nj_other_retirement_income_exclusion.yaml b/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/exclusions/nj_other_retirement_income_exclusion.yaml index e9f9bc444a7..463ff16a908 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/exclusions/nj_other_retirement_income_exclusion.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/exclusions/nj_other_retirement_income_exclusion.yaml @@ -212,3 +212,69 @@ state_code: NJ output: nj_other_retirement_income_exclusion: 0 + +- name: A dependent's wages and income stay off the filer's Worksheet D + # Head 65, $30,000 interest and $10,000 private pension; dependent child 16 with + # $5,000 of wages, which go on the child's own return. Head of household maximum + # $75,000; line 3 = min($40,000 total income, $75,000) - $10,000 pension + # exclusion = $30,000; the filer's own earnings ($0) are under $3,000. + period: 2025 + input: + people: + person1: + is_tax_unit_head: true + age: 65 + taxable_interest_income: 30_000 + taxable_private_pension_income: 10_000 + person2: + is_tax_unit_dependent: true + age: 16 + employment_income: 5_000 + tax_units: + tax_unit: + members: [person1, person2] + spm_units: + spm_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: NJ + output: + nj_pension_retirement_exclusion: 10_000 + nj_other_retirement_income_exclusion: 30_000 + +- name: An age-eligible dependent's income stays off the filer's Worksheet D total income + # Head 65 with $10,000 of private pension and $10,000 of interest; a dependent + # parent aged 70 with $20,000 of interest, which goes on the parent's own + # return. Worksheet D line 1 counts only the filer's total income ("Joint + # filers: If only one spouse is 62 or older, only the income of that spouse + # can be excluded"; 2025 NJ-1040 instructions, Worksheet D). Head of household + # maximum $75,000; line 1 = min($20,000, $75,000) - $10,000 pension exclusion + # = $10,000. Counting the dependent's interest would give $40,000 - $10,000 = + # $30,000. + period: 2025 + input: + people: + person1: + is_tax_unit_head: true + age: 65 + taxable_interest_income: 10_000 + taxable_private_pension_income: 10_000 + person2: + is_tax_unit_dependent: true + age: 70 + taxable_interest_income: 20_000 + tax_units: + tax_unit: + members: [person1, person2] + spm_units: + spm_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: NJ + output: + nj_pension_retirement_exclusion: 10_000 + nj_other_retirement_income_exclusion: 10_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/taxable_income/nj_agi.yaml b/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/taxable_income/nj_agi.yaml index 2b283aa020b..b03da7bc3ec 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/taxable_income/nj_agi.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/taxable_income/nj_agi.yaml @@ -103,3 +103,83 @@ adjusted_gross_income: 10_001 # on federal form nj_agi: 7_001 # special exclusion reduces AGI by $3k # earned income > $3k makes other retirement income exclusion unavailable + +- name: A dependent child's interest stays off the parent's New Jersey AGI + period: 2025 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 40 + employment_income: 40_000 + child: + age: 16 + taxable_interest_income: 8_000 + tax_units: + tax_unit: + members: [parent, child] + households: + household: + members: [parent, child] + state_code: NJ + output: + # The 8,000 is the child's own New Jersey gross income. A dependent over + # the filing threshold files their own NJ-1040 (N.J.S.A. 54A:8-3.1(a)), + # and the parent's return has no line for it. + nj_gross_income: [40_000, 8_000] + nj_agi: 40_000 + # 40,000 - 1,000 personal exemption - 1,500 dependent exemption. + nj_taxable_income: 37_500 + # The same tax as with no child income (issue #9814 reproduction). + nj_income_tax: -80.68 + +- name: A dependent child's wages stay off the parent's New Jersey AGI + period: 2025 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 40 + employment_income: 40_000 + child: + age: 16 + employment_income: 5_000 + tax_units: + tax_unit: + members: [parent, child] + households: + household: + members: [parent, child] + state_code: NJ + output: + nj_agi: 40_000 + nj_taxable_income: 37_500 + nj_income_tax: -80.68 + +- name: A dependent's wages do not lower the filers' pension exclusion percentage + period: 2025 + input: + people: + head: + age: 65 + taxable_pension_income: 90_000 + spouse: + age: 65 + child: + age: 17 + employment_income: 15_000 + tax_units: + tax_unit: + members: [head, spouse, child] + households: + household: + members: [head, spouse, child] + state_code: NJ + output: + # Worksheet D uses the filers' total income (line 27), 90,000, which is + # not over 100,000, so the joint exclusion is 100% of the pension, up to + # 100,000. Counting the child's 15,000 would have put total income at + # 105,000 and the exclusion at 50%. + nj_retirement_exclusion_fraction: 1 + nj_pension_retirement_exclusion: 90_000 + nj_agi: 0 diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/filer_loss_limited_net_capital_gains_person.py b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/filer_loss_limited_net_capital_gains_person.py new file mode 100644 index 00000000000..9f119b6f6f3 --- /dev/null +++ b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/filer_loss_limited_net_capital_gains_person.py @@ -0,0 +1,38 @@ +from policyengine_us.model_api import * + + +class filer_loss_limited_net_capital_gains_person(Variable): + value_type = float + entity = Person + label = "Filer's loss-limited capital gains (person)" + unit = USD + definition_period = YEAR + documentation = ( + "Each head's or spouse's share of filer_loss_limited_net_capital_gains: " + "the person's own capital gains and losses plus capital gain " + "distributions, with a net loss of the head and spouse limited and " + "allocated in proportion. A tax unit dependent's gains and losses are " + "on the dependent's own return, so the dependent has none here and " + "takes no part in the loss limit, unlike " + "loss_limited_net_capital_gains_person." + ) + reference = "https://www.irs.gov/pub/irs-prior/f1040sd--2025.pdf#page=2" + + def formula(person, period, parameters): + head_or_spouse = ~person("is_tax_unit_dependent", period) + own_gains = head_or_spouse * ( + person("capital_gains", period) + + max_(0, person("non_sch_d_capital_gains", period)) + ) + tax_unit = person.tax_unit + filer_gains = tax_unit.sum(own_gains) + filer_limited = tax_unit("filer_loss_limited_net_capital_gains", period) + proportion = np.divide( + own_gains, + filer_gains, + out=np.zeros_like(own_gains), + where=filer_gains != 0, + ) + # With a net gain each filer keeps their own amount; with a net loss + # the limited loss is shared in proportion to each filer's own. + return where(filer_gains < 0, filer_limited * proportion, own_gains) diff --git a/policyengine_us/variables/gov/states/ar/tax/income/ar_agi_indiv.py b/policyengine_us/variables/gov/states/ar/tax/income/ar_agi_indiv.py index 17a3135e45d..3a37d05ed43 100644 --- a/policyengine_us/variables/gov/states/ar/tax/income/ar_agi_indiv.py +++ b/policyengine_us/variables/gov/states/ar/tax/income/ar_agi_indiv.py @@ -5,17 +5,23 @@ class ar_agi_indiv(Variable): value_type = float entity = Person label = "Arkansas adjusted gross income for each individual" + documentation = ( + "Arkansas adjusted gross income in the head's and spouse's columns of " + "the return. A tax unit dependent's income belongs on the dependent's " + "own Arkansas return, so a dependent's column is zero." + ) unit = USD definition_period = YEAR reference = ( "https://www.dfa.arkansas.gov/wp-content/uploads/2022_AR1000F_and_AR1000NR_Instructions.pdf#page=14", - # Filing Status 4: the primary's income in column A, the spouse's in B - "https://www.dfa.arkansas.gov/wp-content/uploads/2025_AR1000F_and_AR1000NR_Instructions.pdf#page=12", - # Borrowed for dependents' income: parents filing separately report a - # child's income on the return of the parent with the greater - # taxable income - "https://www.law.cornell.edu/uscode/text/26/1#g_5_B", - "https://www.irs.gov/instructions/i8814", + # PDF pages 10, 12: who must file (the gross income minimums apply to + # each person, dependents included), and Filing Status 4 (the + # primary's income in column A, the spouse's in column B) + "https://www.dfa.arkansas.gov/wp-content/uploads/2025_AR1000F_and_AR1000NR_Instructions.pdf#page=10", + # Ark. Code 26-51-801(a)-(b): every person over the gross income + # minimum files a return; only spouses may combine their income on + # a single joint return. + "https://codes.findlaw.com/ar/title-26-taxation/ar-code-sect-26-51-801/", ) defined_for = StateCode.AR @@ -23,7 +29,7 @@ def formula(person, period, parameters): gross_income = person("ar_gross_income_indiv", period) income_exemptions = person("ar_exemptions", period) net_income = max_(gross_income - income_exemptions, 0) - # Each spouse's column holds their own income. The dependents' income - # the model counts here goes on the column of the spouse with the - # greater income (a modelling convention; see the helper). - return move_dependent_amounts_to_filer(person, period, net_income) + # Each spouse's column holds their own income. A dependent's income + # goes on the dependent's own return, not on the filer's. + is_dependent = person("is_tax_unit_dependent", period) + return where(is_dependent, 0, net_income) diff --git a/policyengine_us/variables/gov/states/ar/tax/income/ar_agi_joint.py b/policyengine_us/variables/gov/states/ar/tax/income/ar_agi_joint.py index 9a72c0822f1..ab2141c8745 100644 --- a/policyengine_us/variables/gov/states/ar/tax/income/ar_agi_joint.py +++ b/policyengine_us/variables/gov/states/ar/tax/income/ar_agi_joint.py @@ -5,9 +5,24 @@ class ar_agi_joint(Variable): value_type = float entity = Person label = "Arkansas adjusted gross income for each individual" + documentation = ( + "Arkansas adjusted gross income of the head and spouse on a return " + "that is not married filing separately on the same return. A tax " + "unit dependent's income belongs on the dependent's own Arkansas " + "return, so a dependent's amount is zero." + ) unit = USD definition_period = YEAR - reference = "https://www.dfa.arkansas.gov/wp-content/uploads/2022_AR1000F_and_AR1000NR_Instructions.pdf#page=14" + reference = ( + "https://www.dfa.arkansas.gov/wp-content/uploads/2022_AR1000F_and_AR1000NR_Instructions.pdf#page=14", + # Ark. Code 26-51-801(a)-(b): every person over the gross income + # minimum files a return; only spouses may combine their income on + # a single joint return. + "https://codes.findlaw.com/ar/title-26-taxation/ar-code-sect-26-51-801/", + # Who must file: the gross income minimums apply to each person, + # dependents included + "https://www.dfa.arkansas.gov/wp-content/uploads/2025_AR1000F_and_AR1000NR_Instructions.pdf#page=10", + ) defined_for = StateCode.AR def formula(person, period, parameters): @@ -17,9 +32,7 @@ def formula(person, period, parameters): # On a return that is not married filing separately on the same # return, all income goes in Column A, so one spouse's loss offsets # the other's income; the tax unit total is floored at zero when it - # is used as a tax base. - # Allocate any dependent net income (floored at zero) to the head. + # is used as a tax base. A dependent's income goes on the + # dependent's own return, not on the filer's. is_dependent = person("is_tax_unit_dependent", period) - sum_dep_net_income = person.tax_unit.sum(is_dependent * max_(net_income, 0)) - is_head = person("is_tax_unit_head", period) - return ~is_dependent * net_income + is_head * sum_dep_net_income + return where(is_dependent, 0, net_income) diff --git a/policyengine_us/variables/gov/states/ia/tax/income/net_income/ia_net_income.py b/policyengine_us/variables/gov/states/ia/tax/income/net_income/ia_net_income.py index 92215df662b..8d1043388d9 100644 --- a/policyengine_us/variables/gov/states/ia/tax/income/net_income/ia_net_income.py +++ b/policyengine_us/variables/gov/states/ia/tax/income/net_income/ia_net_income.py @@ -5,20 +5,28 @@ class ia_net_income(Variable): value_type = float entity = Person label = "Iowa net income" + documentation = ( + "Iowa net income in the head's and spouse's columns of the return. A " + "tax unit dependent's income belongs on the dependent's own Iowa " + "return, so a dependent's amount is zero." + ) unit = USD definition_period = YEAR reference = ( "https://revenue.iowa.gov/sites/default/files/2022-01/IA1040%2841-001%29.pdf", - "https://revenue.iowa.gov/media/2650/download?inline", - "https://revenue.iowa.gov/sites/default/files/2023-01/2022IA1040%2841001%29.pdf", - "https://revenue.iowa.gov/media/2721/download?inline", - # Status 3: married filing separately on a combined return + # PDF page 7: Status 3, married filing separately on a combined return "https://revenue.iowa.gov/media/2650/download?inline#page=7", - # Borrowed for dependents' income: parents filing separately report a - # child's income on the return of the parent with the greater - # taxable income - "https://www.law.cornell.edu/uscode/text/26/1#g_5_B", - "https://www.irs.gov/instructions/i8814", + "https://revenue.iowa.gov/sites/default/files/2023-01/2022IA1040%2841001%29.pdf", + # PDF pages 4, 8: who must file (c), and the note to dependents + # filing their own returns + "https://revenue.iowa.gov/media/2721/download?inline#page=4", + # Iowa Code 422.7: net income starts from federal adjusted gross + # income, which leaves out a dependent's income. + "https://www.legis.iowa.gov/docs/code/2022/422.7.pdf", + # Iowa Code 422.13(1)(a): a person claimed as a dependent on another + # person's return files their own return with net income of $5,000 + # or more. + "https://www.legis.iowa.gov/docs/code/2022/422.13.pdf", ) defined_for = StateCode.IA @@ -26,7 +34,7 @@ def formula(person, period, parameters): gross_income = person("ia_gross_income", period) income_adjustments = person("ia_income_adjustments", period) net_income = gross_income - income_adjustments - # Each spouse's column holds their own income. The dependents' net - # income the model counts here goes on the column of the spouse with - # the greater net income (a modelling convention; see the helper). - return move_dependent_amounts_to_filer(person, period, net_income) + # Each spouse's column holds their own income. A dependent's income + # goes on the dependent's own return, not on the filer's. + is_dependent = person("is_tax_unit_dependent", period) + return where(is_dependent, 0, net_income) diff --git a/policyengine_us/variables/gov/states/ms/tax/income/ms_agi.py b/policyengine_us/variables/gov/states/ms/tax/income/ms_agi.py index 8c0c6ff2188..b9ee35589bc 100644 --- a/policyengine_us/variables/gov/states/ms/tax/income/ms_agi.py +++ b/policyengine_us/variables/gov/states/ms/tax/income/ms_agi.py @@ -5,18 +5,25 @@ class ms_agi(Variable): value_type = float entity = Person label = "Mississippi adjusted gross income" + documentation = ( + "Mississippi adjusted gross income in the head's and spouse's columns " + "of the return. A tax unit dependent's income belongs on the " + "dependent's own Mississippi return, so a dependent's amount is zero." + ) unit = USD definition_period = YEAR reference = ( - "https://www.dor.ms.gov/sites/default/files/tax-forms/individual/80100221.pdf#page=14", - "https://www.dor.ms.gov/sites/default/files/tax-forms/individual/80105228.pdf", # Line 66 - # Combined return: one spouse's income in Column A, the other's in B + # PDF pages 5, 14: a combined return puts one spouse's income in + # Column A and the other's in Column B (p. 5), and line 66, + # Mississippi adjusted gross income (p. 14) "https://www.dor.ms.gov/sites/default/files/tax-forms/individual/80100221.pdf#page=5", - # Borrowed for dependents' income: parents filing separately report a - # child's income on the return of the parent with the greater - # taxable income - "https://www.law.cornell.edu/uscode/text/26/1#g_5_B", - "https://www.irs.gov/instructions/i8814", + "https://www.dor.ms.gov/sites/default/files/tax-forms/individual/80105228.pdf", # Line 66 + # 35 Miss. Admin. Code Pt. III, 10.01.104: a minor is taxable on their + # own income and files their own return, with their own deductions + # and exemptions. + "https://www.law.cornell.edu/regulations/mississippi/35-Miss-Code-R-SS-3-10-01-104", + # Filing requirements: minor resident taxpayers file their own return + "https://www.dor.ms.gov/sites/default/files/tax-forms/individual/80100241.pdf#page=4", ) defined_for = StateCode.MS @@ -27,11 +34,7 @@ def formula(person, period, parameters): # A spouse's column can be negative on a joint or combined return; # the tax computation combines it with the other column. net_income = gross_income - adjustments - # Each spouse's column holds their own income. The dependents' - # positive income the model counts here goes on the column of the - # spouse with the greater income (a modelling convention; see the - # helper). + # Each spouse's column holds their own income. A dependent's income + # goes on the dependent's own return, not on the filer's. is_dependent = person("is_tax_unit_dependent", period) - return move_dependent_amounts_to_filer( - person, period, where(is_dependent, max_(net_income, 0), net_income) - ) + return where(is_dependent, 0, net_income) diff --git a/policyengine_us/variables/gov/states/nj/tax/income/adjusted_gross_income/nj_agi.py b/policyengine_us/variables/gov/states/nj/tax/income/adjusted_gross_income/nj_agi.py index 88a626ad420..ee6c9ca48ef 100644 --- a/policyengine_us/variables/gov/states/nj/tax/income/adjusted_gross_income/nj_agi.py +++ b/policyengine_us/variables/gov/states/nj/tax/income/adjusted_gross_income/nj_agi.py @@ -5,15 +5,29 @@ class nj_agi(Variable): value_type = float entity = TaxUnit label = "New Jersey adjusted gross income" + documentation = ( + "New Jersey adjusted gross income of the head and spouse. A tax unit " + "dependent's income belongs on the dependent's own New Jersey return, " + "so it is left out." + ) unit = USD definition_period = YEAR reference = ( - "https://law.justia.com/codes/new-jersey/2022/title-54/section-54-8a-36/" + "https://law.justia.com/codes/new-jersey/2022/title-54/section-54-8a-36/", + # N.J.S.A. 54A:8-3.1(a): each individual over the gross income + # threshold files a return; (b): only a husband and wife file a + # joint return; (f): a minor's return is filed by a fiduciary. + "https://codes.findlaw.com/nj/title-54a-new-jersey-gross-income-tax-act/nj-st-sect-54a-8-3-1/", + # 2025 NJ-1040 instructions, PDF pages 5, 40: who must file, with no + # exception for dependents, and Worksheet L ("dependents who do not + # file a New Jersey tax return") + "https://www.nj.gov/treasury/taxation/pdf/current/1040i.pdf#page=5", ) defined_for = StateCode.NJ def formula(tax_unit, period, parameters): - total_income = add(tax_unit, period, ["nj_total_income"]) + # A dependent's income goes on the dependent's own return. + total_income = tax_unit_non_dep_add(tax_unit, period, ["nj_total_income"]) p = parameters(period).gov.states.nj.tax.income exclusions = add(tax_unit, period, p.all_exclusions) return max_(0, total_income - exclusions) diff --git a/policyengine_us/variables/gov/states/nj/tax/income/deductions/nj_medical_expense_deduction.py b/policyengine_us/variables/gov/states/nj/tax/income/deductions/nj_medical_expense_deduction.py index e8e57c100b9..81b37a91495 100644 --- a/policyengine_us/variables/gov/states/nj/tax/income/deductions/nj_medical_expense_deduction.py +++ b/policyengine_us/variables/gov/states/nj/tax/income/deductions/nj_medical_expense_deduction.py @@ -27,18 +27,15 @@ class nj_medical_expense_deduction(Variable): def formula(tax_unit, period, parameters): p = parameters(period).gov.states.nj.tax.income.deductions.medical_expenses - # nj_agi counts every member's income, dependents included, so the - # dependents' self-employed health insurance deductions count too. - self_employed_medical_expense_deduction = add( - tax_unit, - period, - [ - "self_employed_health_insurance_ald", - "dependents_self_employed_health_insurance_ald", - ], + # The filers' own self-employed health insurance deduction. A + # dependent's self-employment income and its deduction go on the + # dependent's own return, as nj_agi leaves out the dependent's income. + self_employed_medical_expense_deduction = tax_unit( + "self_employed_health_insurance_ald", period ) # C.54A:3-3 excludes premiums counted by the separate SE deduction - # under C.54A:3-5, including dependents' deductions recognized by NJ. + # under C.54A:3-5, including a dependent's deduction on their own + # return. # The federal base already excludes the filers' deduction and the # long-term care part (Form 7206 line 2) of each dependent's own # deduction. Remove the medical part (line 1) of each dependent's diff --git a/policyengine_us/variables/gov/states/nj/tax/income/exclusions/nj_other_retirement_income_exclusion.py b/policyengine_us/variables/gov/states/nj/tax/income/exclusions/nj_other_retirement_income_exclusion.py index f6dc7e5ff8e..2823b9cf65a 100644 --- a/policyengine_us/variables/gov/states/nj/tax/income/exclusions/nj_other_retirement_income_exclusion.py +++ b/policyengine_us/variables/gov/states/nj/tax/income/exclusions/nj_other_retirement_income_exclusion.py @@ -9,7 +9,7 @@ class nj_other_retirement_income_exclusion(Variable): documentation = "New Jersey other retirement income exclusion" definition_period = YEAR reference = ( - "https://www.state.nj.us/treasury/taxation/pdf/current/1040i.pdf#page=21", + "https://www.state.nj.us/treasury/taxation/pdf/current/1040i.pdf#page=23", "https://law.justia.com/codes/new-jersey/2022/title-54a/section-54a-6-15/", ) defined_for = StateCode.NJ @@ -32,8 +32,9 @@ def formula(tax_unit, period, parameters): filing_status = tax_unit("filing_status", period) exclusion_cap = p.max_amount[filing_status] - # calculate maximum exclusion - total_income_person = age_eligible * person("nj_total_income", period) + # calculate maximum exclusion on the age-eligible filers' total + # income; a dependent's income goes on the dependent's own return + total_income_person = eligible_person * person("nj_total_income", period) total_income = tax_unit.sum(total_income_person) maximum_exclusion = min_(fraction * total_income, exclusion_cap) @@ -41,8 +42,9 @@ def formula(tax_unit, period, parameters): used = min_(pension_income * fraction, exclusion_cap) unused_exclusion = max_(0, maximum_exclusion - used) - # calculate earnings eligibility - earnings = add(tax_unit, period, ["earned_income"]) + # calculate earnings eligibility on the filers' own earnings + # (Worksheet D lines 4-8), leaving out a dependent's + earnings = tax_unit_non_dep_add(tax_unit, period, ["earned_income"]) limit = p.other_retirement_income.earned_income_threshold earnings_eligible = earnings <= limit diff --git a/policyengine_us/variables/gov/states/nj/tax/income/exclusions/nj_retirement_exclusion_fraction.py b/policyengine_us/variables/gov/states/nj/tax/income/exclusions/nj_retirement_exclusion_fraction.py index 44b22979a59..b0c6622d7c0 100644 --- a/policyengine_us/variables/gov/states/nj/tax/income/exclusions/nj_retirement_exclusion_fraction.py +++ b/policyengine_us/variables/gov/states/nj/tax/income/exclusions/nj_retirement_exclusion_fraction.py @@ -14,7 +14,9 @@ class nj_retirement_exclusion_fraction(Variable): defined_for = StateCode.NJ def formula(tax_unit, period, parameters): - total_income = add(tax_unit, period, ["nj_total_income"]) + # Total income (line 27) of the head and spouse; a dependent's + # income goes on the dependent's own return. + total_income = tax_unit_non_dep_add(tax_unit, period, ["nj_total_income"]) filing_status = tax_unit("filing_status", period) status = filing_status.possible_values p = parameters(period).gov.states.nj.tax.income.exclusions.retirement.pension