diff --git a/changelog.d/ny-itemized-phaseout-line-40.fixed.md b/changelog.d/ny-itemized-phaseout-line-40.fixed.md new file mode 100644 index 00000000000..4378b2fd7f5 --- /dev/null +++ b/changelog.d/ny-itemized-phaseout-line-40.fixed.md @@ -0,0 +1 @@ +Wired New York's orphaned itemized-deduction phase-out parameter into the IT-196 Line 40 pre-TCJA overall limitation (26 U.S.C. 68 via NY Tax Law 615 conformity), reducing itemized deductions by the lesser of 3% of federal AGI over the applicable amount or 80% of the deductions subject to the limitation. diff --git a/policyengine_us/parameters/gov/states/ny/tax/income/deductions/itemized/phase_out/agi_excess_rate.yaml b/policyengine_us/parameters/gov/states/ny/tax/income/deductions/itemized/phase_out/agi_excess_rate.yaml new file mode 100644 index 00000000000..75775b149fa --- /dev/null +++ b/policyengine_us/parameters/gov/states/ny/tax/income/deductions/itemized/phase_out/agi_excess_rate.yaml @@ -0,0 +1,14 @@ +description: New York reduces itemized deductions by this share of federal adjusted gross income above the applicable amount, as one of the two limbs of the pre-TCJA federal overall limitation on itemized deductions. +values: + 2021-01-01: 0.03 +metadata: + unit: /1 + period: year + label: New York itemized deduction phase-out AGI excess rate + reference: + # Pre-TCJA IRC § 68(a)(1): reduce itemized deductions by 3% of AGI over the + # applicable amount. NY Tax Law § 615 conforms to pre-TCJA federal rules. + - title: 26 U.S. Code § 68 - Overall limitation on itemized deductions (a)(1) + href: https://www.law.cornell.edu/uscode/text/26/68#a_1 + - title: 2025 NY Form IT-196 Instructions, Line 40 Total itemized deductions worksheet (line 8, multiply by 3%) + href: https://www.tax.ny.gov/pdf/current_forms/it/it196i.pdf#page=19 diff --git a/policyengine_us/parameters/gov/states/ny/tax/income/deductions/itemized/phase_out/deduction_limited_rate.yaml b/policyengine_us/parameters/gov/states/ny/tax/income/deductions/itemized/phase_out/deduction_limited_rate.yaml new file mode 100644 index 00000000000..8fef40862dd --- /dev/null +++ b/policyengine_us/parameters/gov/states/ny/tax/income/deductions/itemized/phase_out/deduction_limited_rate.yaml @@ -0,0 +1,15 @@ +description: New York caps the pre-TCJA overall limitation on itemized deductions at this share of the deductions that are subject to the limitation. +values: + 2021-01-01: 0.80 +metadata: + unit: /1 + period: year + label: New York itemized deduction phase-out limited-deduction cap rate + reference: + # Pre-TCJA IRC § 68(a)(2): the reduction cannot exceed 80% of the itemized + # deductions otherwise allowable (excluding medical, investment interest, + # casualty/theft, and wagering losses per § 68(c)). NY Tax Law § 615 conforms. + - title: 26 U.S. Code § 68 - Overall limitation on itemized deductions (a)(2) + href: https://www.law.cornell.edu/uscode/text/26/68#a_2 + - title: 2025 NY Form IT-196 Instructions, Line 40 Total itemized deductions worksheet (line 4, multiply by 80%) + href: https://www.tax.ny.gov/pdf/current_forms/it/it196i.pdf#page=19 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/taxable_income/deductions/itemized/ny_itemized_deductions_phase_out.yaml b/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/taxable_income/deductions/itemized/ny_itemized_deductions_phase_out.yaml new file mode 100644 index 00000000000..07de472c2ad --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/taxable_income/deductions/itemized/ny_itemized_deductions_phase_out.yaml @@ -0,0 +1,136 @@ +# IT-196 Line 40 "Total itemized deductions worksheet" (pre-TCJA IRC 68 +# overall limitation, to which NY Tax Law 615 conforms). The reduction is the +# LESSER OF 3% of federal AGI over the Table 1 applicable amount, or 80% of the +# deductions subject to the limitation. +# 2025 applicable amounts (Table 1): SINGLE 340,700; JOINT 408,850. + +- name: SINGLE, federal AGI just below the 2025 applicable amount - not limited. + period: 2025 + absolute_error_margin: 0.01 + input: + ny_itemized_deductions_max: 100_000 + ny_college_tuition_deduction: 0 + medical_expense_deduction: 0 + investment_interest_expense: 0 + ny_casualty_loss_deduction: 0 + adjusted_gross_income: 340_000 + filing_status: SINGLE + state_code: NY + output: + ny_itemized_deductions_phase_out_applies: false + ny_itemized_deductions_phase_out: 0 + +- name: SINGLE, federal AGI just above the 2025 applicable amount - 3% limb binds. + period: 2025 + absolute_error_margin: 0.01 + input: + ny_itemized_deductions_max: 100_000 + ny_college_tuition_deduction: 0 + medical_expense_deduction: 0 + investment_interest_expense: 0 + ny_casualty_loss_deduction: 0 + adjusted_gross_income: 350_000 + filing_status: SINGLE + state_code: NY + output: + ny_itemized_deductions_phase_out_applies: true + # L1 = 100,000; L2 = 0; L3 = 100,000; L4 = 0.80 * 100,000 = 80,000. + # AGI excess = 350,000 - 340,700 = 9,300; L8 = 0.03 * 9,300 = 279. + # Reduction = min(80,000, 279) = 279. + ny_itemized_deductions_phase_out: 279 + +- name: JOINT, federal AGI just below the 2025 applicable amount - not limited. + period: 2025 + absolute_error_margin: 0.01 + input: + ny_itemized_deductions_max: 100_000 + ny_college_tuition_deduction: 0 + medical_expense_deduction: 0 + investment_interest_expense: 0 + ny_casualty_loss_deduction: 0 + adjusted_gross_income: 408_000 + filing_status: JOINT + state_code: NY + output: + ny_itemized_deductions_phase_out_applies: false + ny_itemized_deductions_phase_out: 0 + +- name: JOINT, federal AGI above the 2025 applicable amount - 3% limb binds. + period: 2025 + absolute_error_margin: 0.01 + input: + ny_itemized_deductions_max: 100_000 + ny_college_tuition_deduction: 0 + medical_expense_deduction: 0 + investment_interest_expense: 0 + ny_casualty_loss_deduction: 0 + adjusted_gross_income: 500_000 + filing_status: JOINT + state_code: NY + output: + ny_itemized_deductions_phase_out_applies: true + # L1 = 100,000; L2 = 0; L4 = 80,000. + # AGI excess = 500,000 - 408,850 = 91,150; L8 = 0.03 * 91,150 = 2,734.50. + # Reduction = min(80,000, 2,734.50) = 2,734.50. + ny_itemized_deductions_phase_out: 2_734.50 + +- name: JOINT, very high AGI - 80% limb binds (cap on the reduction). + period: 2025 + absolute_error_margin: 0.01 + input: + ny_itemized_deductions_max: 20_000 + ny_college_tuition_deduction: 0 + medical_expense_deduction: 0 + investment_interest_expense: 0 + ny_casualty_loss_deduction: 0 + adjusted_gross_income: 5_000_000 + filing_status: JOINT + state_code: NY + output: + ny_itemized_deductions_phase_out_applies: true + # L1 = 20,000; L3 = 20,000; L4 = 0.80 * 20,000 = 16,000. + # AGI excess = 5,000,000 - 408,850 = 4,591,150; L8 = 0.03 * that = 137,734.50. + # Reduction = min(16,000, 137,734.50) = 16,000 (the 80% cap binds). + ny_itemized_deductions_phase_out: 16_000 + +- name: Medical, investment interest, and casualty are excluded from the 80% base (26 U.S.C. 68(c)). + period: 2025 + absolute_error_margin: 0.01 + input: + # ny_itemized_deductions_max = 100,000 total, of which 40,000 is excluded + # (medical 25,000 + investment interest 10,000 + casualty 5,000). Only the + # remaining 60,000 is subject to the limitation. + ny_itemized_deductions_max: 100_000 + ny_college_tuition_deduction: 0 + medical_expense_deduction: 25_000 + investment_interest_expense: 10_000 + ny_casualty_loss_deduction: 5_000 + adjusted_gross_income: 5_000_000 + filing_status: JOINT + state_code: NY + output: + ny_itemized_deductions_phase_out_applies: true + # L1 = 100,000; L2 = 25,000 + 10,000 + 5,000 = 40,000; L3 = 60,000. + # L4 = 0.80 * 60,000 = 48,000. AGI excess huge so L8 >> L4. + # Reduction = min(48,000, large) = 48,000. + ny_itemized_deductions_phase_out: 48_000 + +- name: NY-only college tuition deduction is excluded from L1 (added after Line 40). + period: 2025 + absolute_error_margin: 0.01 + input: + # max includes 30,000 of NY college tuition deduction that is not part of + # the federal Line 40 worksheet; the Pease base should ignore it. + ny_itemized_deductions_max: 130_000 + ny_college_tuition_deduction: 30_000 + medical_expense_deduction: 0 + investment_interest_expense: 0 + ny_casualty_loss_deduction: 0 + adjusted_gross_income: 500_000 + filing_status: JOINT + state_code: NY + output: + ny_itemized_deductions_phase_out_applies: true + # L1 = 130,000 - 30,000 = 100,000; L4 = 80,000; L8 = 2,734.50. + # Reduction = min(80,000, 2,734.50) = 2,734.50 (same as without tuition). + ny_itemized_deductions_phase_out: 2_734.50 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/taxable_income/deductions/itemized/ny_itemized_deductions_phase_out_integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/taxable_income/deductions/itemized/ny_itemized_deductions_phase_out_integration.yaml new file mode 100644 index 00000000000..7704dceee09 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/taxable_income/deductions/itemized/ny_itemized_deductions_phase_out_integration.yaml @@ -0,0 +1,81 @@ +# End-to-end wiring of the IT-196 Line 40 pre-TCJA limitation into +# ny_itemized_deductions, computed from raw household inputs. Confirms the +# Line 40 (federal-AGI-gated) reduction is applied in addition to the Line 46 +# (NY-AGI-gated) 615(f)/(g) reduction. Sales taxes are set to 0 so the NY SALT +# deduction reduces to real estate taxes only. + +- name: JOINT 2025 high earner - Line 40 limitation applies (below Line 46 NY-AGI threshold). + period: 2025 + absolute_error_margin: 0.05 + input: + people: + head: + age: 45 + taxable_interest_income: 420_000 + deductible_mortgage_interest: 50_000 + real_estate_taxes: 25_000 + charitable_cash_donations: 10_000 + spouse: + age: 45 + tax_units: + tax_unit: + members: [head, spouse] + state_sales_tax: 0 + local_sales_tax: 0 + marital_units: + marital_unit: + members: [head, spouse] + families: + family: + members: [head, spouse] + spm_units: + spm_unit: + members: [head, spouse] + households: + household: + members: [head, spouse] + state_code: NY + output: + # Federal AGI = 420,000 > 408,850 (JOINT applicable amount), so Line 40 applies. + # L1 = mortgage 50,000 + real estate 25,000 + charitable 10,000 = 85,000. + # L4 = 0.80 * 85,000 = 68,000. AGI excess = 11,150; L8 = 0.03 * 11,150 = 334.50. + # Line 40 reduction = min(68,000, 334.50) = 334.50. + ny_itemized_deductions_phase_out: 334.50 + +- name: JOINT 2025 modest itemizer below the Line 40 applicable amount - no limitation. + period: 2025 + absolute_error_margin: 0.05 + input: + people: + head: + age: 45 + taxable_interest_income: 300_000 + deductible_mortgage_interest: 50_000 + real_estate_taxes: 25_000 + charitable_cash_donations: 10_000 + spouse: + age: 45 + tax_units: + tax_unit: + members: [head, spouse] + state_sales_tax: 0 + local_sales_tax: 0 + marital_units: + marital_unit: + members: [head, spouse] + families: + family: + members: [head, spouse] + spm_units: + spm_unit: + members: [head, spouse] + households: + household: + members: [head, spouse] + state_code: NY + output: + # Federal AGI = 300,000 < 408,850, so no Line 40 limitation. + ny_itemized_deductions_phase_out: 0 + # ny_itemized_deductions_max = 50,000 + 25,000 + 10,000 = 85,000 passes + # through Line 40 untouched (Line 46 still applies on NY AGI 300,000). + ny_itemized_deductions_phase_out_applies: false diff --git a/policyengine_us/variables/gov/states/ny/tax/income/taxable_income/deductions/itemized/ny_itemized_deductions.py b/policyengine_us/variables/gov/states/ny/tax/income/taxable_income/deductions/itemized/ny_itemized_deductions.py index 62cbc2811c8..7cacb948f3e 100644 --- a/policyengine_us/variables/gov/states/ny/tax/income/taxable_income/deductions/itemized/ny_itemized_deductions.py +++ b/policyengine_us/variables/gov/states/ny/tax/income/taxable_income/deductions/itemized/ny_itemized_deductions.py @@ -11,4 +11,12 @@ class ny_itemized_deductions(Variable): defined_for = StateCode.NY adds = ["ny_itemized_deductions_max"] - subtracts = ["ny_itemized_deductions_reduction"] + # IT-196 applies two reductions in sequence: the pre-TCJA federal overall + # limitation (Line 40, keyed on federal AGI, 26 U.S.C. 68 via NY Tax Law + # 615 conformity) first, then NY's own higher-income adjustment (Line 46, + # keyed on NY AGI per 615(f)/(g)). Both are modeled here as amounts + # subtracted from ny_itemized_deductions_max. + subtracts = [ + "ny_itemized_deductions_phase_out", + "ny_itemized_deductions_reduction", + ] diff --git a/policyengine_us/variables/gov/states/ny/tax/income/taxable_income/deductions/itemized/phase_out/ny_itemized_deductions_phase_out.py b/policyengine_us/variables/gov/states/ny/tax/income/taxable_income/deductions/itemized/phase_out/ny_itemized_deductions_phase_out.py new file mode 100644 index 00000000000..27565cc41af --- /dev/null +++ b/policyengine_us/variables/gov/states/ny/tax/income/taxable_income/deductions/itemized/phase_out/ny_itemized_deductions_phase_out.py @@ -0,0 +1,89 @@ +from policyengine_us.model_api import * + + +class ny_itemized_deductions_phase_out(Variable): + value_type = float + entity = TaxUnit + label = "New York itemized deductions pre-TCJA overall limitation" + unit = USD + definition_period = YEAR + reference = ( + "https://www.law.cornell.edu/uscode/text/26/68", # pre-TCJA IRC 68 + "https://www.nysenate.gov/legislation/laws/TAX/615", # NY conformity + "https://www.tax.ny.gov/pdf/current_forms/it/it196i.pdf#page=19", # Line 40 worksheet + ) + defined_for = "ny_itemized_deductions_phase_out_applies" + documentation = """ + IT-196 Line 40, Total itemized deductions worksheet. This is the pre-TCJA + federal "Pease" overall limitation on itemized deductions (26 U.S.C. 68 as + it existed before Public Law 115-97), to which NY Tax Law 615 conforms. + The limitation reduces itemized deductions by the LESSER OF: + (a) 3% of federal AGI over the applicable amount (worksheet lines 5-8), or + (b) 80% of the deductions that are subject to the limitation (lines 1-4). + + Mapping of the worksheet's line-total inputs to PolicyEngine variables + (federal Form IT-196 line numbers in brackets): + + Worksheet line 1 (L1) = form lines 4, 9, 15, 19, 20, 28, 39, i.e. the total + itemized deductions before the limitation. In PolicyEngine this is + ny_itemized_deductions_max minus the NY-only college tuition addition + (form line 48/43, which is added after line 40 and is not on this + worksheet). Form line 39 ("other itemized deductions": gambling losses, + estate tax on income in respect of a decedent, amortizable bond premium, + federal disaster loss, etc.) is not modeled in PolicyEngine and is therefore + treated as zero. + + Worksheet line 2 (L2) = form lines 4, 14, 16a, 20, 29, 30, 37, i.e. the + deductions NOT subject to the limitation (26 U.S.C. 68(c)): + - line 4 medical/dental -> medical_expense_deduction + - line 14 investment interest -> investment_interest_expense + - line 20 casualty/theft loss -> ny_casualty_loss_deduction + - lines 16a/29/30/37 (qualified contributions, gambling losses, + income-producing-property casualty, federal disaster loss) are not + separately modeled in PolicyEngine and are treated as zero. + + L3 = L1 - L2 (worksheet line 3) is therefore the mortgage interest, real + estate taxes, charitable gifts, and 2%-floor miscellaneous deductions - + exactly the 68(c) base subject to the limitation. Because investment + interest appears in both L1 (inside interest_deduction) and L2, it cancels + out of L3, as intended. + """ + + def formula(tax_unit, period, parameters): + p = parameters(period).gov.states.ny.tax.income.deductions.itemized.phase_out + + # Worksheet line 1 (L1): total itemized deductions before the limitation. + # ny_itemized_deductions_max includes the NY-only college tuition + # deduction (form line 48/43), which is added after line 40 and is not + # part of this worksheet, so it is removed here. + itemized_max = tax_unit("ny_itemized_deductions_max", period) + college_tuition = tax_unit("ny_college_tuition_deduction", period) + l1 = itemized_max - college_tuition + + # Worksheet line 2 (L2): deductions not subject to the limitation + # (26 U.S.C. 68(c)) - medical, investment interest, casualty/theft. + medical = tax_unit("medical_expense_deduction", period) + investment_interest = add(tax_unit, period, ["investment_interest_expense"]) + casualty = tax_unit("ny_casualty_loss_deduction", period) + l2 = medical + investment_interest + casualty + + # Worksheet line 3 (L3): deductions subject to the limitation. Floored at + # zero to implement the worksheet's line-3 stop ("If line 2 is not less + # than line 1, your deduction is not limited"). + l3 = max_(l1 - l2, 0) + + # Worksheet line 4 (L4): 80% of the limited deductions. + l4 = l3 * p.deduction_limited_rate + + # Worksheet lines 5-8: 3% of federal AGI over the applicable amount. + # The defined_for guard already restricts this to filers whose federal + # AGI exceeds the applicable amount (worksheet line 7), so the excess is + # positive; max_ guards the vectorized boundary. + agi = tax_unit("adjusted_gross_income", period) + filing_status = tax_unit("filing_status", period) + applicable_amount = p.start[filing_status] + agi_excess = max_(agi - applicable_amount, 0) + l8 = agi_excess * p.agi_excess_rate + + # Worksheet lines 9-10: reduce by the smaller of the two limbs. + return min_(l4, l8) diff --git a/policyengine_us/variables/gov/states/ny/tax/income/taxable_income/deductions/itemized/phase_out/ny_itemized_deductions_phase_out_applies.py b/policyengine_us/variables/gov/states/ny/tax/income/taxable_income/deductions/itemized/phase_out/ny_itemized_deductions_phase_out_applies.py new file mode 100644 index 00000000000..414bda3c7e6 --- /dev/null +++ b/policyengine_us/variables/gov/states/ny/tax/income/taxable_income/deductions/itemized/phase_out/ny_itemized_deductions_phase_out_applies.py @@ -0,0 +1,25 @@ +from policyengine_us.model_api import * + + +class ny_itemized_deductions_phase_out_applies(Variable): + value_type = bool + entity = TaxUnit + label = "Whether the pre-TCJA overall limitation on New York itemized deductions applies" + definition_period = YEAR + reference = ( + "https://www.law.cornell.edu/uscode/text/26/68#b", # applicable amount + "https://www.tax.ny.gov/pdf/current_forms/it/it196i.pdf#page=19", # Line 40 worksheet, line 7 + ) + defined_for = StateCode.NY + + def formula(tax_unit, period, parameters): + # IT-196 Line 40 worksheet, lines 5-7: the limitation only applies when + # federal AGI (Form IT-201/IT-203 line 19) exceeds the Table 1 + # applicable amount for the filing status. NY Tax Law § 615 conforms to + # the pre-TCJA federal 26 U.S.C. § 68(b) applicable amount, which NY + # continues to inflation-adjust. + p = parameters(period).gov.states.ny.tax.income.deductions.itemized.phase_out + agi = tax_unit("adjusted_gross_income", period) + filing_status = tax_unit("filing_status", period) + applicable_amount = p.start[filing_status] + return agi > applicable_amount