diff --git a/changelog.d/fix-wi-retirement-exclusion-state-income-tax.fixed.md b/changelog.d/fix-wi-retirement-exclusion-state-income-tax.fixed.md new file mode 100644 index 00000000000..1dbe04e6ec1 --- /dev/null +++ b/changelog.d/fix-wi-retirement-exclusion-state-income-tax.fixed.md @@ -0,0 +1 @@ +Include Wisconsin retirement income exclusion in state_income_tax aggregation, fixing overstated tax for retirees. diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_income_tax.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_income_tax.yaml new file mode 100644 index 00000000000..63c852dfe49 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_income_tax.yaml @@ -0,0 +1,27 @@ +# Integration test from issue #7805: +# Single filer, age 85, WI, 2025 with retirement income. +# Verifies that state_income_tax equals wi_income_tax when the +# retirement income exclusion path produces a lower tax. +- name: WI retirement exclusion reflected in state_income_tax + absolute_error_margin: 1 + period: 2025 + input: + people: + person1: + age: 85 + taxable_interest_income: 18_145 + short_term_capital_gains: 11_677 + long_term_capital_gains: 85_655 + taxable_pension_income: 71_458 + social_security: 19_429 + tax_units: + tax_unit: + members: [person1] + filing_status: SINGLE + households: + household: + members: [person1] + state_code: WI + output: + wi_income_tax: 6_631 + state_income_tax: 6_631 diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_income_tax_before_refundable_credits.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_income_tax_before_refundable_credits.yaml index 17880e318c9..2de67b8e595 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_income_tax_before_refundable_credits.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_income_tax_before_refundable_credits.yaml @@ -17,3 +17,44 @@ state_code: WI output: wi_income_tax_before_refundable_credits: 0 + +- name: WI income tax before refundable credits with retirement exclusion + # This intermediate remains the standard pre-refundable-credit tax, + # even when the exclusion path wins in wi_income_tax. + # standard_before_refundable = max(0, 5000 - 500) = 4500 + absolute_error_margin: 0.01 + period: 2025 + input: + wi_income_tax_before_credits: 5_000 + wi_non_refundable_credits: 500 + wi_retirement_income_exclusion_tax: 3_000 + wi_refundable_credits: 200 + state_code: WI + output: + wi_income_tax_before_refundable_credits: 4_500 + +- name: WI income tax before refundable credits - standard path lower + # standard_before_refundable = max(0, 2000 - 500) = 1500 + absolute_error_margin: 0.01 + period: 2025 + input: + wi_income_tax_before_credits: 2_000 + wi_non_refundable_credits: 500 + wi_retirement_income_exclusion_tax: 5_000 + wi_refundable_credits: 200 + state_code: WI + output: + wi_income_tax_before_refundable_credits: 1_500 + +- name: WI state income tax still follows the lower exclusion path + absolute_error_margin: 0.01 + period: 2025 + input: + wi_income_tax_before_credits: 5_000 + wi_non_refundable_credits: 500 + wi_retirement_income_exclusion_tax: 3_000 + wi_refundable_credits: 200 + state_code: WI + output: + wi_income_tax: 3_000 + state_income_tax: 3_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_retirement_income_exclusion.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_retirement_income_exclusion.yaml index eb87edea7f4..ed7f3527215 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_retirement_income_exclusion.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_retirement_income_exclusion.yaml @@ -1,4 +1,7 @@ - name: Exclusion beneficial - no credits, Line 16 reduces tax + # standard_before_refundable = max(0, 350 - 0) = 350 + # exclusion_tax = 70 (from wi_retirement_income_exclusion_tax) + # wi_income_tax = min(350 - 0, 70) = 70 absolute_error_margin: 0.01 period: 2025 input: @@ -8,15 +11,18 @@ filing_status: SINGLE state_code: WI wi_taxable_income: 10_000 - wi_income_tax_before_refundable_credits: 350 + wi_income_tax_before_credits: 350 + wi_non_refundable_credits: 0 + wi_retirement_income_exclusion_tax: 70 wi_refundable_credits: 0 wi_retirement_income_subtraction: 0 output: - # after_exclusion = max(0, 350 - 280) = 70 - # wi_income_tax = 70 - 0 = 70 wi_income_tax: 70 - name: Exclusion NOT beneficial - refundable credits make standard path better + # standard_before_refundable = max(0, 350 - 0) = 350 + # exclusion_tax = 280 + # wi_income_tax = min(350 - 300, 280) = 50 (standard path wins) absolute_error_margin: 0.01 period: 2025 input: @@ -26,12 +32,12 @@ filing_status: SINGLE state_code: WI wi_taxable_income: 10_000 - wi_income_tax_before_refundable_credits: 350 + wi_income_tax_before_credits: 350 + wi_non_refundable_credits: 0 + wi_retirement_income_exclusion_tax: 280 wi_refundable_credits: 300 wi_retirement_income_subtraction: 0 output: - # reduction = 0, after_exclusion = 350 - # wi_income_tax = 350 - 300 = 50 wi_income_tax: 50 - name: Not eligible for exclusion - age 64, standard path used @@ -44,7 +50,8 @@ filing_status: SINGLE state_code: WI wi_taxable_income: 10_000 - wi_income_tax_before_refundable_credits: 350 + wi_income_tax_before_credits: 350 + wi_non_refundable_credits: 0 wi_refundable_credits: 0 wi_retirement_income_subtraction: 0 output: diff --git a/policyengine_us/tests/policy/baseline/household/income/household/household_state_income_tax.yaml b/policyengine_us/tests/policy/baseline/household/income/household/household_state_income_tax.yaml index b9ba5a7247a..753acf6b771 100644 --- a/policyengine_us/tests/policy/baseline/household/income/household/household_state_income_tax.yaml +++ b/policyengine_us/tests/policy/baseline/household/income/household/household_state_income_tax.yaml @@ -40,3 +40,14 @@ spm_unit_state_tax_reported: 1_000 output: household_state_income_tax: 500 + +- name: Wisconsin household state income tax follows the lower exclusion path + period: 2025 + input: + state_code: WI + wi_income_tax_before_credits: 5_000 + wi_non_refundable_credits: 500 + wi_retirement_income_exclusion_tax: 3_000 + wi_refundable_credits: 200 + output: + household_state_income_tax: 3_000 diff --git a/policyengine_us/variables/gov/states/tax/income/state_income_tax.py b/policyengine_us/variables/gov/states/tax/income/state_income_tax.py index f490643db0e..90cfec914e9 100644 --- a/policyengine_us/variables/gov/states/tax/income/state_income_tax.py +++ b/policyengine_us/variables/gov/states/tax/income/state_income_tax.py @@ -27,4 +27,10 @@ def formula(tax_unit, period, parameters): ["state_income_tax_before_refundable_credits"], ) refundable_credits = add(tax_unit, period, ["state_refundable_credits"]) - return income_tax_before_refundable_credits - refundable_credits + default_tax = income_tax_before_refundable_credits - refundable_credits + state_code = tax_unit.household("state_code", period) + return where( + state_code == StateCode.WI, + tax_unit("wi_income_tax", period), + default_tax, + ) diff --git a/policyengine_us/variables/gov/states/wi/tax/income/wi_income_tax_before_refundable_credits.py b/policyengine_us/variables/gov/states/wi/tax/income/wi_income_tax_before_refundable_credits.py index 561dd0eedd4..e0ffc34505f 100644 --- a/policyengine_us/variables/gov/states/wi/tax/income/wi_income_tax_before_refundable_credits.py +++ b/policyengine_us/variables/gov/states/wi/tax/income/wi_income_tax_before_refundable_credits.py @@ -8,11 +8,12 @@ class wi_income_tax_before_refundable_credits(Variable): unit = USD definition_period = YEAR reference = ( - "https://www.revenue.wi.gov/TaxForms2021/2021-Form1f.pdf" - "https://www.revenue.wi.gov/TaxForms2021/2021-Form1-Inst.pdf" - "https://www.revenue.wi.gov/TaxForms2022/2022-Form1f.pdf" - "https://www.revenue.wi.gov/TaxForms2022/2022-Form1-Inst.pdf" - "https://docs.legis.wisconsin.gov/misc/lfb/informational_papers/january_2023/0002_individual_income_tax_informational_paper_2.pdf" + "https://www.revenue.wi.gov/TaxForms2021/2021-Form1f.pdf", + "https://www.revenue.wi.gov/TaxForms2021/2021-Form1-Inst.pdf", + "https://www.revenue.wi.gov/TaxForms2022/2022-Form1f.pdf", + "https://www.revenue.wi.gov/TaxForms2022/2022-Form1-Inst.pdf", + "https://docs.legis.wisconsin.gov/misc/lfb/informational_papers/january_2023/0002_individual_income_tax_informational_paper_2.pdf", + "https://docs.legis.wisconsin.gov/statutes/statutes/71/i/05/6/b/54m/a", ) defined_for = StateCode.WI diff --git a/policyengine_us/variables/household/income/household/household_state_income_tax.py b/policyengine_us/variables/household/income/household/household_state_income_tax.py index f291c01fcca..b61e9b7a5b4 100644 --- a/policyengine_us/variables/household/income/household/household_state_income_tax.py +++ b/policyengine_us/variables/household/income/household/household_state_income_tax.py @@ -20,8 +20,14 @@ def formula(tax_unit, period, parameters): 0, ) else: - return add( + default_tax = add( tax_unit, period, ["state_income_tax_before_refundable_credits"], ) - add(tax_unit, period, ["state_refundable_credits"]) + state_code = tax_unit.household("state_code", period) + return where( + state_code == StateCode.WI, + tax_unit("wi_income_tax", period), + default_tax, + )