Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/ny-itemized-phaseout-line-40.fixed.md
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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",
]
Original file line number Diff line number Diff line change
@@ -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)
Original file line number Diff line number Diff line change
@@ -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
Loading