diff --git a/changelog.d/nj-staynj-property-tax-relief.changed.md b/changelog.d/nj-staynj-property-tax-relief.changed.md new file mode 100644 index 00000000000..dfac5ffc07c --- /dev/null +++ b/changelog.d/nj-staynj-property-tax-relief.changed.md @@ -0,0 +1 @@ +Update New Jersey Stay NJ property tax relief baseline calculations. diff --git a/policyengine_us/parameters/gov/household/household_state_benefits.yaml b/policyengine_us/parameters/gov/household/household_state_benefits.yaml index 6f6817d5bff..08587a63acd 100644 --- a/policyengine_us/parameters/gov/household/household_state_benefits.yaml +++ b/policyengine_us/parameters/gov/household/household_state_benefits.yaml @@ -173,6 +173,99 @@ values: - ar_child_care_subsidies # Washington child care subsidies - wa_child_care_subsidies + 2026-01-01: + # Delaware benefits + - de_ssp + - de_child_care_subsidies + # Indiana benefits + - in_ssp + - in_child_care_subsidies + # Connecticut benefits + - ct_ssp + # Georgia benefits + - ga_ssp + # Maine benefits + - me_ssp + # Missouri benefits + - mo_ssp + # Massachusetts benefits + - ma_state_supplement + # Maryland benefits + - md_paa + # Michigan benefits + - mi_ssp + # Minnesota benefits + - mn_msa + # Colorado benefits + - co_state_supplement + - co_oap + - co_child_care_subsidies + # California benefits + - ca_child_care_subsidies + - ca_cvrp + - ca_care + - ca_fera + - ca_la_ez_save + - ca_la_infant_supplement + - ca_la_expectant_parent_payment + - ca_capi + - ca_state_supplement + # Alabama benefits + - al_ssp + - al_child_care_subsidies + # Alaska benefits + - ak_ssp + # Arizona benefits + - az_child_care_subsidies + # Florida benefits + - fl_oss + # Idaho benefits + - id_aabd + - id_child_care_subsidies + # Kentucky benefits + - ky_ssp + # Georgia benefits + - ga_child_care_subsidies + # Maryland benefits + - md_child_care_subsidies + # Nebraska benefits + - ne_aabd + - ne_child_care_subsidies + # New Mexico benefits + - nm_ssi_state_supplement + # South Carolina benefits + - sc_ssi_state_supplement + # Texas benefits + - tx_ssi_state_supplement + # Virginia benefits + - va_child_care_subsidies + # West Virginia benefits + - wv_child_care_subsidies + # North Carolina benefits + - nc_scca + # South Carolina benefits + - sc_child_care_subsidies + # Massachusetts benefits + - ma_eaedc + - ma_tafdc + # DC benefits + - dc_ossp + # Kansas benefits + - ks_sspp + # Hawaii benefits + - hi_oss + # Washington benefits + - wa_ssp + - wa_sfa + - wa_rca + # Louisiana benefits + - la_oss + # Arkansas benefits + - ar_child_care_subsidies + # Washington child care subsidies + - wa_child_care_subsidies + # New Jersey benefits + - nj_property_tax_relief metadata: unit: list period: year diff --git a/policyengine_us/parameters/gov/states/nj/tax/income/credits/property_tax_relief/income/additional_sources.yaml b/policyengine_us/parameters/gov/states/nj/tax/income/credits/property_tax_relief/income/additional_sources.yaml new file mode 100644 index 00000000000..587fb1575fc --- /dev/null +++ b/policyengine_us/parameters/gov/states/nj/tax/income/credits/property_tax_relief/income/additional_sources.yaml @@ -0,0 +1,15 @@ +description: New Jersey uses these additional income sources when calculating property tax relief application income under the property tax relief program. +metadata: + unit: list + period: year + label: New Jersey property tax relief additional income sources + reference: + - title: NJ Division of Taxation - PAS-1 Income Calculation + href: https://www.nj.gov/treasury/taxation/relief.shtml#collapsePAS + - title: 2025 Form PAS-1 instructions, lines 17a-17f and 18a-18f + href: https://www.nj.gov/treasury/taxation/pdf/25-pas1in.pdf#page=9 +values: + 2025-01-01: + - tax_exempt_interest_income + - tax_exempt_ira_distributions + - social_security diff --git a/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/credits/anchor/nj_anchor.yaml b/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/credits/anchor/nj_anchor.yaml index 0e8c07b1e09..10516414f23 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/credits/anchor/nj_anchor.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/credits/anchor/nj_anchor.yaml @@ -686,3 +686,52 @@ # Senior homeowner, income $250K at upper limit (uses <=) # Senior homeowner upper income tier: $1,250 nj_anchor: 1_250 + +- name: Edge case 17, homeowner ANCHOR is capped by property taxes paid. + period: 2024 + input: + people: + person1: + age: 72 + employment_income: 100_000 + real_estate_taxes: 100 + is_tax_unit_head: true + tax_units: + tax_unit: + members: [person1] + spm_units: + spm_unit: + members: [person1] + spm_unit_tenure_type: OWNER_WITH_MORTGAGE + households: + household: + members: [person1] + state_fips: 34 + output: + # Senior homeowner lower income tier would be $1,750. + # Total property tax relief is capped by property taxes paid. + nj_anchor: 100 + +- name: Edge case 18, homeowner ANCHOR is unchanged when property taxes exceed the benefit. + period: 2024 + input: + people: + person1: + age: 72 + employment_income: 100_000 + real_estate_taxes: 2_000 + is_tax_unit_head: true + tax_units: + tax_unit: + members: [person1] + spm_units: + spm_unit: + members: [person1] + spm_unit_tenure_type: OWNER_WITH_MORTGAGE + households: + household: + members: [person1] + state_fips: 34 + output: + # Senior homeowner lower income tier is below property taxes paid. + nj_anchor: 1_750 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/credits/property_tax_relief/nj_property_tax_relief.yaml b/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/credits/property_tax_relief/nj_property_tax_relief.yaml new file mode 100644 index 00000000000..7027533b8e4 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/credits/property_tax_relief/nj_property_tax_relief.yaml @@ -0,0 +1,112 @@ +- name: Case 1, combined relief reaches the Stay NJ target. + period: 2026 + input: + people: + person1: + age: 70 + employment_income: 100_000 + real_estate_taxes: 10_000 + is_tax_unit_head: true + tax_units: + tax_unit: + members: [person1] + nj_senior_freeze: 500 + spm_units: + spm_unit: + members: [person1] + spm_unit_tenure_type: OWNER_WITH_MORTGAGE + households: + household: + members: [person1] + state_fips: 34 + output: + # Stay NJ target = 50% of $10,000 = $5,000. + nj_anchor: 1_750 + nj_capped_senior_freeze: 500 + nj_staynj: 2_750 + nj_property_tax_relief: 5_000 + household_state_benefits: 5_000 + +- name: Case 2, combined relief is capped by property taxes paid. + period: 2026 + input: + people: + person1: + age: 70 + employment_income: 100_000 + real_estate_taxes: 2_000 + is_tax_unit_head: true + tax_units: + tax_unit: + members: [person1] + nj_senior_freeze: 1_000 + spm_units: + spm_unit: + members: [person1] + spm_unit_tenure_type: OWNER_WITH_MORTGAGE + households: + household: + members: [person1] + state_fips: 34 + output: + # ANCHOR = $1,750 and Senior Freeze is capped to the remaining $250. + nj_anchor: 1_750 + nj_capped_senior_freeze: 250 + nj_staynj: 0 + nj_property_tax_relief: 2_000 + household_state_benefits: 2_000 + +- name: Case 3, New Jersey property tax relief starts in 2026. + period: 2025 + input: + people: + person1: + age: 70 + employment_income: 100_000 + real_estate_taxes: 10_000 + is_tax_unit_head: true + tax_units: + tax_unit: + members: [person1] + nj_senior_freeze: 500 + spm_units: + spm_unit: + members: [person1] + spm_unit_tenure_type: OWNER_WITH_MORTGAGE + households: + household: + members: [person1] + state_fips: 34 + output: + nj_property_tax_relief: 0 + household_state_benefits: 0 + +- name: Case 4, Senior Freeze fully absorbs the Stay NJ residual without binding the property tax cap. + period: 2026 + input: + people: + person1: + age: 70 + employment_income: 100_000 + real_estate_taxes: 12_000 + is_tax_unit_head: true + tax_units: + tax_unit: + members: [person1] + nj_senior_freeze: 6_000 + spm_units: + spm_unit: + members: [person1] + spm_unit_tenure_type: OWNER_WITH_MORTGAGE + households: + household: + members: [person1] + state_fips: 34 + output: + # Stay NJ target = 50% of $12,000 = $6,000. + # ANCHOR + capped Senior Freeze exceed that target, while total relief remains below property taxes. + nj_anchor: 1_750 + nj_capped_senior_freeze: 6_000 + nj_staynj: 0 + nj_property_tax_relief: 7_750 + household_state_benefits: 7_750 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/credits/property_tax_relief/nj_property_tax_relief_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/credits/property_tax_relief/nj_property_tax_relief_income.yaml new file mode 100644 index 00000000000..6c71fb2cfc6 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/credits/property_tax_relief/nj_property_tax_relief_income.yaml @@ -0,0 +1,168 @@ +- name: Case 1, property tax relief income includes PAS-1 additional income sources. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 60 + employment_income: 80_000 + tax_exempt_interest_income: 1_000 + tax_exempt_ira_distributions: 2_000 + social_security_retirement: 4_000 + total_disability_payments: 3_000 + is_tax_unit_head: true + tax_units: + tax_unit: + members: [person1] + households: + household: + members: [person1] + state_code: NJ + output: + nj_property_tax_relief_income: 90_000 + +- name: Case 2, disability pension is excluded at full retirement age. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + employment_income: 50_000 + total_disability_payments: 3_000 + is_tax_unit_head: true + tax_units: + tax_unit: + members: [person1] + households: + household: + members: [person1] + state_code: NJ + output: + nj_property_tax_relief_income: 50_000 + +- name: Case 3, property tax relief income sums across head and spouse. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + employment_income: 100_000 + tax_exempt_interest_income: 5_000 + is_tax_unit_head: true + person2: + age: 69 + social_security_retirement: 20_000 + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: NJ + output: + nj_property_tax_relief_income: 125_000 + +- name: Case 4, property tax relief income includes tax-exempt interest. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + employment_income: 80_000 + tax_exempt_interest_income: 1_234 + is_tax_unit_head: true + tax_units: + tax_unit: + members: [person1] + households: + household: + members: [person1] + state_code: NJ + output: + nj_property_tax_relief_income: 81_234 + +- name: Case 5, property tax relief income includes tax-exempt IRA distributions. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + employment_income: 80_000 + tax_exempt_ira_distributions: 2_345 + is_tax_unit_head: true + tax_units: + tax_unit: + members: [person1] + households: + household: + members: [person1] + state_code: NJ + output: + nj_property_tax_relief_income: 82_345 + +- name: Case 6, property tax relief income includes Social Security. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + employment_income: 80_000 + social_security_retirement: 7_000 + is_tax_unit_head: true + tax_units: + tax_unit: + members: [person1] + households: + household: + members: [person1] + state_code: NJ + output: + nj_property_tax_relief_income: 87_000 + +- name: Case 7, property tax relief income includes pre-retirement-age disability payments. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 60 + employment_income: 80_000 + total_disability_payments: 3_000 + is_tax_unit_head: true + tax_units: + tax_unit: + members: [person1] + households: + household: + members: [person1] + state_code: NJ + output: + nj_property_tax_relief_income: 83_000 + +- name: Case 8, property tax relief income is inactive before 2026. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 60 + employment_income: 80_000 + tax_exempt_interest_income: 1_000 + social_security_retirement: 4_000 + total_disability_payments: 3_000 + is_tax_unit_head: true + tax_units: + tax_unit: + members: [person1] + households: + household: + members: [person1] + state_code: NJ + output: + nj_property_tax_relief_income: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/credits/staynj/nj_capped_senior_freeze.yaml b/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/credits/staynj/nj_capped_senior_freeze.yaml new file mode 100644 index 00000000000..e4a7ba0f035 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/credits/staynj/nj_capped_senior_freeze.yaml @@ -0,0 +1,75 @@ +- name: Case 1, Senior Freeze below remaining property taxes is unchanged. + period: 2026 + input: + people: + person1: + age: 70 + employment_income: 100_000 + real_estate_taxes: 10_000 + is_tax_unit_head: true + tax_units: + tax_unit: + members: [person1] + nj_senior_freeze: 500 + spm_units: + spm_unit: + members: [person1] + spm_unit_tenure_type: OWNER_WITH_MORTGAGE + households: + household: + members: [person1] + state_fips: 34 + output: + # ANCHOR = $1,750, leaving $8,250 before the property-tax cap. + nj_capped_senior_freeze: 500 + +- name: Case 2, Senior Freeze is capped after ANCHOR. + period: 2026 + input: + people: + person1: + age: 70 + employment_income: 100_000 + real_estate_taxes: 2_000 + is_tax_unit_head: true + tax_units: + tax_unit: + members: [person1] + nj_senior_freeze: 1_000 + spm_units: + spm_unit: + members: [person1] + spm_unit_tenure_type: OWNER_WITH_MORTGAGE + households: + household: + members: [person1] + state_fips: 34 + output: + # ANCHOR = $1,750, leaving $250 before the property-tax cap. + nj_capped_senior_freeze: 250 + +- name: Case 3, no remaining property taxes after capped ANCHOR. + period: 2026 + input: + people: + person1: + age: 70 + employment_income: 100_000 + real_estate_taxes: 100 + is_tax_unit_head: true + tax_units: + tax_unit: + members: [person1] + nj_senior_freeze: 1_000 + spm_units: + spm_unit: + members: [person1] + spm_unit_tenure_type: OWNER_WITH_MORTGAGE + households: + household: + members: [person1] + state_fips: 34 + output: + # ANCHOR is capped at the $100 property tax amount. + nj_anchor: 100 + nj_capped_senior_freeze: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/credits/staynj/nj_staynj.yaml b/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/credits/staynj/nj_staynj.yaml index c5ef0115c34..fcf09a74a24 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/credits/staynj/nj_staynj.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/credits/staynj/nj_staynj.yaml @@ -525,8 +525,8 @@ state_fips: 34 output: # min($100 * 50%, $6,500) = min($50, $6,500) = $50 - # ANCHOR = $1,750 - # max($50 - $1,750, 0) = $0 + # ANCHOR is capped at property taxes paid = $100 + # max($50 - $100, 0) = $0 nj_staynj: 0 - name: Edge case 10, couple where only spouse is 65 plus. @@ -630,8 +630,8 @@ state_fips: 34 output: # min($1 * 50%, $6,500) = $0.50 - # ANCHOR = $1,750 - # max($0.50 - $1,750, 0) = $0 + # ANCHOR is capped at property taxes paid = $1 + # max($0.50 - $1, 0) = $0 nj_staynj: 0 - name: Edge case 14, senior who neither owns nor rents is ineligible. diff --git a/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/credits/staynj/nj_staynj_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/credits/staynj/nj_staynj_eligible.yaml index 8f0a71fc54d..e41772418f7 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/credits/staynj/nj_staynj_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/credits/staynj/nj_staynj_eligible.yaml @@ -451,3 +451,76 @@ output: # Age 95 >= 65, homeowner, income $50K < $500K nj_staynj_eligible: true + +- name: Edge case 11, senior homeowner is ineligible when PAS-1 income exceeds limit. + period: 2026 + input: + people: + person1: + age: 70 + employment_income: 490_000 + social_security_retirement: 15_000 + real_estate_taxes: 10_000 + is_tax_unit_head: true + tax_units: + tax_unit: + members: [person1] + spm_units: + spm_unit: + members: [person1] + spm_unit_tenure_type: OWNER_WITH_MORTGAGE + households: + household: + members: [person1] + state_fips: 34 + output: + # PAS-1 income includes Social Security, so $490K + $15K >= $500K. + nj_staynj_eligible: false + +- name: Edge case 12, age 65 homeowner with PAS-1 income just below limit is eligible. + period: 2026 + input: + people: + person1: + age: 65 + employment_income: 480_000 + social_security_retirement: 19_999 + real_estate_taxes: 10_000 + is_tax_unit_head: true + tax_units: + tax_unit: + members: [person1] + spm_units: + spm_unit: + members: [person1] + spm_unit_tenure_type: OWNER_WITH_MORTGAGE + households: + household: + members: [person1] + state_fips: 34 + output: + # PAS-1 income includes Social Security, so $499,999 < $500,000. + nj_staynj_eligible: true + +- name: Edge case 13, Stay NJ eligibility is inactive before 2026. + period: 2024 + input: + people: + person1: + age: 70 + employment_income: 100_000 + real_estate_taxes: 10_000 + is_tax_unit_head: true + tax_units: + tax_unit: + members: [person1] + spm_units: + spm_unit: + members: [person1] + spm_unit_tenure_type: OWNER_WITH_MORTGAGE + households: + household: + members: [person1] + state_fips: 34 + output: + nj_staynj_eligible: false diff --git a/policyengine_us/variables/gov/states/nj/tax/income/credits/anchor/nj_anchor.py b/policyengine_us/variables/gov/states/nj/tax/income/credits/anchor/nj_anchor.py index 7dac68651e2..4dde48c2929 100644 --- a/policyengine_us/variables/gov/states/nj/tax/income/credits/anchor/nj_anchor.py +++ b/policyengine_us/variables/gov/states/nj/tax/income/credits/anchor/nj_anchor.py @@ -24,7 +24,8 @@ def formula(tax_unit, period, parameters): is_senior = greater_age >= p.age_threshold # Determine if homeowner or renter - pays_property_taxes = add(tax_unit, period, ["real_estate_taxes"]) > 0 + property_taxes = add(tax_unit, period, ["real_estate_taxes"]) + pays_property_taxes = property_taxes > 0 pays_rent = tax_unit("rents", period) is_homeowner = pays_property_taxes & ~pays_rent is_renter = pays_rent & ~pays_property_taxes @@ -46,6 +47,7 @@ def formula(tax_unit, period, parameters): homeowner_amount = where( is_senior, homeowner_senior_amount, homeowner_non_senior_amount ) + homeowner_amount = min_(homeowner_amount, property_taxes) # Calculate renter benefit amounts renter_amount = where( diff --git a/policyengine_us/variables/gov/states/nj/tax/income/credits/property_tax_credit/nj_property_tax_credit.py b/policyengine_us/variables/gov/states/nj/tax/income/credits/property_tax_credit/nj_property_tax_credit.py index 83e96e8111c..cb75da0f5fc 100644 --- a/policyengine_us/variables/gov/states/nj/tax/income/credits/property_tax_credit/nj_property_tax_credit.py +++ b/policyengine_us/variables/gov/states/nj/tax/income/credits/property_tax_credit/nj_property_tax_credit.py @@ -8,9 +8,9 @@ class nj_property_tax_credit(Variable): unit = USD definition_period = YEAR reference = ( - "https://www.nj.gov/treasury/taxation/pdf/other_forms/tgi-ee/2021/1040i.pdf#page=49" - "https://www.nj.gov/treasury/taxation/pdf/other_forms/tgi-ee/2022/1040i.pdf#page=50" - "https://www.state.nj.us/treasury/taxation/pdf/current/1040i.pdf#page=52" + "https://www.nj.gov/treasury/taxation/pdf/other_forms/tgi-ee/2021/1040i.pdf#page=49", + "https://www.nj.gov/treasury/taxation/pdf/other_forms/tgi-ee/2022/1040i.pdf#page=50", + "https://www.state.nj.us/treasury/taxation/pdf/current/1040i.pdf#page=52", ) defined_for = "nj_property_tax_credit_eligible" diff --git a/policyengine_us/variables/gov/states/nj/tax/income/credits/property_tax_credit/nj_property_tax_credit_eligible.py b/policyengine_us/variables/gov/states/nj/tax/income/credits/property_tax_credit/nj_property_tax_credit_eligible.py index 4f6e4d54c3f..2f560a0e02a 100644 --- a/policyengine_us/variables/gov/states/nj/tax/income/credits/property_tax_credit/nj_property_tax_credit_eligible.py +++ b/policyengine_us/variables/gov/states/nj/tax/income/credits/property_tax_credit/nj_property_tax_credit_eligible.py @@ -7,9 +7,9 @@ class nj_property_tax_credit_eligible(Variable): label = "New Jersey property tax credit eligibility" definition_period = YEAR reference = ( - "https://www.nj.gov/treasury/taxation/pdf/other_forms/tgi-ee/2021/1040i.pdf#page=26" - "https://www.nj.gov/treasury/taxation/pdf/other_forms/tgi-ee/2022/1040i.pdf#page=26" - "https://www.state.nj.us/treasury/taxation/pdf/current/1040i.pdf#page=27" + "https://www.nj.gov/treasury/taxation/pdf/other_forms/tgi-ee/2021/1040i.pdf#page=26", + "https://www.nj.gov/treasury/taxation/pdf/other_forms/tgi-ee/2022/1040i.pdf#page=26", + "https://www.state.nj.us/treasury/taxation/pdf/current/1040i.pdf#page=27", ) defined_for = StateCode.NJ diff --git a/policyengine_us/variables/gov/states/nj/tax/income/credits/property_tax_relief/nj_property_tax_relief.py b/policyengine_us/variables/gov/states/nj/tax/income/credits/property_tax_relief/nj_property_tax_relief.py new file mode 100644 index 00000000000..60e87e3a801 --- /dev/null +++ b/policyengine_us/variables/gov/states/nj/tax/income/credits/property_tax_relief/nj_property_tax_relief.py @@ -0,0 +1,24 @@ +from policyengine_us.model_api import * + + +class nj_property_tax_relief(Variable): + value_type = float + entity = TaxUnit + label = "New Jersey property tax relief benefits" + unit = USD + definition_period = YEAR + reference = ( + "https://pub.njleg.state.nj.us/Bills/2022/PL23/75_.PDF#page=3", + "https://pub.njleg.state.nj.us/Bills/2024/PL24/88_.PDF", + "https://lis.njleg.state.nj.us/nxt/gateway.dll?f=templates&fn=default.htm&vid=Publish:10.1048/Enu", + "https://www.nj.gov/treasury/taxation/relief.shtml", + "https://www.nj.gov/treasury/taxation/staynj/calculation.shtml", + ) + defined_for = StateCode.NJ + + def formula_2026(tax_unit, period, parameters): + return ( + tax_unit("nj_anchor", period) + + tax_unit("nj_capped_senior_freeze", period) + + tax_unit("nj_staynj", period) + ) diff --git a/policyengine_us/variables/gov/states/nj/tax/income/credits/property_tax_relief/nj_property_tax_relief_income.py b/policyengine_us/variables/gov/states/nj/tax/income/credits/property_tax_relief/nj_property_tax_relief_income.py new file mode 100644 index 00000000000..ab2c5bcc74d --- /dev/null +++ b/policyengine_us/variables/gov/states/nj/tax/income/credits/property_tax_relief/nj_property_tax_relief_income.py @@ -0,0 +1,39 @@ +from policyengine_us.model_api import * + + +class nj_property_tax_relief_income(Variable): + value_type = float + entity = TaxUnit + label = "New Jersey property tax relief application income" + unit = USD + definition_period = YEAR + reference = ( + "https://www.nj.gov/treasury/taxation/relief.shtml#collapsePAS", + "https://www.nj.gov/treasury/taxation/pdf/25-pas1in.pdf#page=9", + ) + defined_for = StateCode.NJ + + def formula_2026(tax_unit, period, parameters): + p = parameters( + period + ).gov.states.nj.tax.income.credits.property_tax_relief.income + + person = tax_unit.members + nj_gross_income = add(tax_unit, period, ["nj_gross_income"]) + additional_sources = add(tax_unit, period, p.additional_sources) + + age = person("age", period) + full_retirement_age = ( + person("ss_full_retirement_age_months", period) / MONTHS_IN_YEAR + ) + # Covers permanent-disability wage substitutes, not VA or military benefits. + total_disability_payments = person("total_disability_payments", period) + disability_pension_before_retirement_age = tax_unit.sum( + (age < full_retirement_age) * total_disability_payments + ) + + return ( + nj_gross_income + + additional_sources + + disability_pension_before_retirement_age + ) diff --git a/policyengine_us/variables/gov/states/nj/tax/income/credits/staynj/nj_capped_senior_freeze.py b/policyengine_us/variables/gov/states/nj/tax/income/credits/staynj/nj_capped_senior_freeze.py new file mode 100644 index 00000000000..711edb6c728 --- /dev/null +++ b/policyengine_us/variables/gov/states/nj/tax/income/credits/staynj/nj_capped_senior_freeze.py @@ -0,0 +1,23 @@ +from policyengine_us.model_api import * + + +class nj_capped_senior_freeze(Variable): + value_type = float + entity = TaxUnit + label = "New Jersey capped Senior Freeze benefit" + unit = USD + definition_period = YEAR + reference = ( + "https://pub.njleg.state.nj.us/Bills/2022/PL23/75_.PDF#page=3", + "https://pub.njleg.state.nj.us/Bills/2024/PL24/88_.PDF", + "https://lis.njleg.state.nj.us/nxt/gateway.dll?f=templates&fn=default.htm&vid=Publish:10.1048/Enu", + "https://www.nj.gov/treasury/taxation/staynj/calculation.shtml", + "https://www.nj.gov/treasury/taxation/ptr/index.shtml", + ) + defined_for = StateCode.NJ + + def formula(tax_unit, period, parameters): + property_taxes = add(tax_unit, period, ["real_estate_taxes"]) + anchor = tax_unit("nj_anchor", period) + senior_freeze = tax_unit("nj_senior_freeze", period) + return min_(senior_freeze, max_(property_taxes - anchor, 0)) diff --git a/policyengine_us/variables/gov/states/nj/tax/income/credits/staynj/nj_staynj.py b/policyengine_us/variables/gov/states/nj/tax/income/credits/staynj/nj_staynj.py index ea260a860d6..888a2f4f2db 100644 --- a/policyengine_us/variables/gov/states/nj/tax/income/credits/staynj/nj_staynj.py +++ b/policyengine_us/variables/gov/states/nj/tax/income/credits/staynj/nj_staynj.py @@ -24,6 +24,6 @@ def formula(tax_unit, period, parameters): # Subtract ANCHOR and Senior Freeze benefits anchor_benefit = tax_unit("nj_anchor", period) - senior_freeze = tax_unit("nj_senior_freeze", period) + senior_freeze = tax_unit("nj_capped_senior_freeze", period) return max_(target_benefit - anchor_benefit - senior_freeze, 0) diff --git a/policyengine_us/variables/gov/states/nj/tax/income/credits/staynj/nj_staynj_eligible.py b/policyengine_us/variables/gov/states/nj/tax/income/credits/staynj/nj_staynj_eligible.py index 4d40f177582..c7efb1b5f23 100644 --- a/policyengine_us/variables/gov/states/nj/tax/income/credits/staynj/nj_staynj_eligible.py +++ b/policyengine_us/variables/gov/states/nj/tax/income/credits/staynj/nj_staynj_eligible.py @@ -12,16 +12,16 @@ class nj_staynj_eligible(Variable): ) defined_for = StateCode.NJ - def formula(tax_unit, period, parameters): + def formula_2026(tax_unit, period, parameters): p = parameters(period).gov.states.nj.tax.income.credits.staynj # Age 65+ for head or spouse greater_age = tax_unit("greater_age_head_spouse", period) age_eligible = greater_age >= p.age_threshold - # Gross income below limit (strict less than per statute) - gross_income = add(tax_unit, period, ["nj_gross_income"]) - income_eligible = gross_income < p.income_limit + # Property tax relief application income below limit (strict less than per statute) + income = tax_unit("nj_property_tax_relief_income", period) + income_eligible = income < p.income_limit # Must be homeowner (pays property taxes, not renter) pays_property_taxes = add(tax_unit, period, ["real_estate_taxes"]) > 0 diff --git a/policyengine_us/variables/gov/states/nj/tax/income/property_tax/nj_potential_property_tax_deduction.py b/policyengine_us/variables/gov/states/nj/tax/income/property_tax/nj_potential_property_tax_deduction.py index 66fd3d6b033..769da2335b9 100644 --- a/policyengine_us/variables/gov/states/nj/tax/income/property_tax/nj_potential_property_tax_deduction.py +++ b/policyengine_us/variables/gov/states/nj/tax/income/property_tax/nj_potential_property_tax_deduction.py @@ -8,8 +8,8 @@ class nj_potential_property_tax_deduction(Variable): unit = USD definition_period = YEAR reference = ( - "https://law.justia.com/codes/new-jersey/2022/title-54a/section-54a-3a-17/" - "https://www.state.nj.us/treasury/taxation/pdf/other_forms/tgi-ee/2021/1040i.pdf#page=25" + "https://law.justia.com/codes/new-jersey/2022/title-54a/section-54a-3a-17/", + "https://www.state.nj.us/treasury/taxation/pdf/other_forms/tgi-ee/2021/1040i.pdf#page=25", ) defined_for = "nj_property_tax_deduction_eligible"