Skip to content
Open
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
4 changes: 4 additions & 0 deletions changelog_entry.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- bump: minor
changes:
changed:
- Pin New York itemized deductions to pre-TCJA rules per Tax Law § 615
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Test NY pre-TCJA itemized deductions conformity
# NY Tax Law § 615 requires using pre-TCJA federal rules

- name: NY SALT deduction uses sales tax and real estate taxes (not income tax)
period: 2024
input:
state_code: NY
state_sales_tax: 2_000
local_sales_tax: 500
real_estate_taxes: 8_000
state_withheld_income_tax: 15_000 # Higher than sales, but not deductible for NY
output:
# NY should use sales tax (2,500) + real estate (8,000) = 10,500
# NOT income tax which is higher
ny_salt_deduction: 10_500

- name: NY SALT deduction is uncapped (no $10,000 limit)
period: 2024
input:
state_code: NY
state_sales_tax: 5_000
local_sales_tax: 0
real_estate_taxes: 20_000
output:
# NY has no cap: 5,000 + 0 + 20,000 = 25,000
ny_salt_deduction: 25_000

- name: NY allows miscellaneous deductions with 2% floor (not suspended like federal)
period: 2024
input:
state_code: NY
employment_income: 100_000
unreimbursed_business_employee_expenses: 5_000
tax_preparation_fees: 500
output:
# Total misc: 5,500
# AGI floor (2%): 100,000 * 0.02 = 2,000
# Deduction: 5,500 - 2,000 = 3,500
ny_misc_deduction: 3_500

- name: NY misc deduction is zero when below floor
period: 2024
input:
state_code: NY
employment_income: 100_000
unreimbursed_business_employee_expenses: 1_500
output:
# Total misc: 1,500
# AGI floor (2%): 100,000 * 0.02 = 2,000
# Deduction: max(0, 1,500 - 2,000) = 0
ny_misc_deduction: 0

- name: NY allows casualty loss deduction (not limited to federal disasters)
period: 2024
input:
state_code: NY
employment_income: 100_000
casualty_loss: 15_000
output:
# Casualty loss: 15,000
# AGI floor (10%): 100,000 * 0.10 = 10,000
# Deduction: 15,000 - 10,000 = 5,000
ny_casualty_loss_deduction: 5_000

- name: NY itemized deductions max includes all pre-TCJA components
period: 2024
input:
state_code: NY
employment_income: 100_000
charitable_cash_donations: 5_000
real_estate_taxes: 12_000
state_sales_tax: 3_000
local_sales_tax: 0
medical_out_of_pocket_expenses: 10_000
unreimbursed_business_employee_expenses: 4_000
casualty_loss: 15_000
output:
# Charitable: 5,000
# Medical: 10,000 - (100,000 * 0.075) = 2,500
# NY SALT: 3,000 + 0 + 12,000 = 15,000 (uncapped)
# NY Misc: 4,000 - (100,000 * 0.02) = 2,000
# NY Casualty: 15,000 - (100,000 * 0.10) = 5,000
# Total: 5,000 + 2,500 + 15,000 + 2,000 + 5,000 = 29,500
ny_itemized_deductions_max: 29_500
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
absolute_error_margin: 0
input:
state_code: NY
# Explicitly set sales taxes to 0 to test default behavior
# without IRS sales tax table lookup values
state_sales_tax: 0
local_sales_tax: 0
output:
ny_standard_deduction: 8_000
ny_itemized_deductions: 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,33 @@
class ny_itemized_deductions_max(Variable):
value_type = float
entity = TaxUnit
label = "NY uncapped itemized deductions"
label = "NY itemized deductions before reduction"
unit = USD
definition_period = YEAR
reference = "https://www.nysenate.gov/legislation/laws/TAX/615"
reference = (
"https://www.nysenate.gov/legislation/laws/TAX/615",
"https://www.tax.ny.gov/pit/file/itemized-deductions.htm",
)
defined_for = StateCode.NY
documentation = """
NY Tax Law § 615 requires itemized deductions to be computed using
federal rules as they existed immediately prior to the enactment of
Public Law 115-97 (TCJA). This means:
- No SALT cap ($10,000 limit does not apply)
- State/local income taxes are NOT deductible (only sales + property)
- Miscellaneous deductions still allowed with 2% AGI floor
- Casualty losses not limited to federally declared disasters
- NY-specific college tuition deduction addition per § 615(d)
"""
adds = [
"itemized_deductions_less_salt",
"real_estate_taxes",
# Federal deductions that don't differ from pre-TCJA
"charitable_deduction",
"interest_deduction",
"medical_expense_deduction",
# NY-specific pre-TCJA versions
"ny_salt_deduction",
"ny_misc_deduction",
"ny_casualty_loss_deduction",
# NY-specific addition per Tax Law § 615(d)
"ny_college_tuition_deduction",
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from policyengine_us.model_api import *


class ny_casualty_loss_deduction(Variable):
value_type = float
entity = TaxUnit
label = "NY casualty loss deduction"
unit = USD
definition_period = YEAR
reference = (
"https://www.nysenate.gov/legislation/laws/TAX/615",
"https://www.tax.ny.gov/pit/file/itemized-deductions.htm",
)
defined_for = StateCode.NY
documentation = """
NY Tax Law § 615 requires itemized deductions to be computed using
pre-TCJA federal rules. Unlike federal rules post-2017, NY still
allows casualty and theft loss deductions not limited to federally
declared disasters. The 10% AGI floor still applies.
"""

def formula(tax_unit, period, parameters):
# NY uses pre-TCJA rules: casualty losses are still deductible
# (not limited to federally declared disasters like federal post-2017)
loss = add(tax_unit, period, ["casualty_loss"])
p = parameters(period).gov.irs.deductions.itemized.casualty
positive_agi = tax_unit("positive_agi", period)
# Apply the 10% AGI floor (pre-TCJA rule)
return max_(0, loss - positive_agi * p.floor)
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from policyengine_us.model_api import *


class ny_misc_deduction(Variable):
value_type = float
entity = TaxUnit
label = "NY miscellaneous deduction"
unit = USD
definition_period = YEAR
reference = (
"https://www.nysenate.gov/legislation/laws/TAX/615",
"https://www.tax.ny.gov/pit/file/itemized-deductions.htm",
)
defined_for = StateCode.NY
documentation = """
NY Tax Law § 615 requires itemized deductions to be computed using
pre-TCJA federal rules. Unlike federal rules post-2017, NY still
allows miscellaneous itemized deductions subject to the 2% AGI floor.
These include unreimbursed employee expenses and tax preparation fees.
"""

def formula(tax_unit, period, parameters):
# NY uses pre-TCJA rules: misc deductions are still allowed
# with the 2% AGI floor (not suspended like federal post-2017)
p = parameters(period).gov.irs.deductions.itemized.misc
expenses = tax_unit("total_misc_deductions", period)
misc_floor = p.floor * tax_unit("positive_agi", period)
return max_(0, expenses - misc_floor)
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from policyengine_us.model_api import *


class ny_salt_deduction(Variable):
value_type = float
entity = TaxUnit
label = "NY SALT deduction"
unit = USD
definition_period = YEAR
reference = (
"https://www.nysenate.gov/legislation/laws/TAX/615",
"https://www.tax.ny.gov/pit/file/itemized-deductions.htm",
)
defined_for = StateCode.NY
documentation = """
NY Tax Law § 615 requires itemized deductions to be computed using
pre-TCJA federal rules. For SALT:
- No $10,000 cap applies
- State and local income taxes are NOT deductible (add-back required per § 615(c))
- State and local sales taxes ARE deductible
- Real estate taxes ARE deductible
"""

def formula(tax_unit, period, parameters):
# NY allows sales tax and real estate taxes, but NOT income taxes
# Per Tax Law § 615(c), state/local income taxes must be subtracted
sales_tax = add(
tax_unit, period, ["state_sales_tax", "local_sales_tax"]
)
real_estate_taxes = add(tax_unit, period, ["real_estate_taxes"])
return sales_tax + real_estate_taxes