diff --git a/changelog.d/mi-military-survivor-spouse-only.fixed.md b/changelog.d/mi-military-survivor-spouse-only.fixed.md new file mode 100644 index 00000000000..b5a01fd9d57 --- /dev/null +++ b/changelog.d/mi-military-survivor-spouse-only.fixed.md @@ -0,0 +1 @@ +Restrict military survivor retirement tax exemptions to eligible surviving spouses in Arizona, Idaho, Indiana, Michigan, North Dakota, New Mexico, Rhode Island, South Carolina, and Virginia. Apply New Mexico's 2025 survivor eligibility date and use Michigan's eligible subtraction in retirement deduction worksheets. Limit South Carolina's general survivor retirement deduction to surviving spouses, base its maximum on the deceased spouse's age, and drop the 2021 reduction by the military survivor deduction. diff --git a/policyengine_us/parameters/gov/states/az/tax/income/subtractions/military_retirement/surviving_spouse_eligible.yaml b/policyengine_us/parameters/gov/states/az/tax/income/subtractions/military_retirement/surviving_spouse_eligible.yaml new file mode 100644 index 00000000000..2a55ba932c1 --- /dev/null +++ b/policyengine_us/parameters/gov/states/az/tax/income/subtractions/military_retirement/surviving_spouse_eligible.yaml @@ -0,0 +1,15 @@ +description: Arizona includes a deceased veteran's surviving spouse's military survivor benefit payments in the military retirement subtraction if this is true. +values: + 2018-01-01: false + 2023-01-01: true +metadata: + unit: bool + period: year + label: Arizona military retirement subtraction surviving spouse eligibility + reference: + - title: A.R.S. 43-1022(2), (26) + href: https://www.azleg.gov/ars/43/01022.htm + - title: Arizona Individual Income Tax Ruling ITR 11-3 + href: https://azdor.gov/sites/default/files/2023-03/RULINGS_INDV_2011_itr11-3.pdf#page=2 + - title: 2025 Arizona Form 140 Resident Personal Income Tax Instructions, line 29b + href: https://azdor.gov/sites/default/files/document/FORMS_INDIVIDUAL_2025_140i.pdf diff --git a/policyengine_us/parameters/gov/states/id/tax/income/deductions/retirement_benefits/income_sources.yaml b/policyengine_us/parameters/gov/states/id/tax/income/deductions/retirement_benefits/income_sources.yaml index e640086326a..f4a8c8f7f81 100644 --- a/policyengine_us/parameters/gov/states/id/tax/income/deductions/retirement_benefits/income_sources.yaml +++ b/policyengine_us/parameters/gov/states/id/tax/income/deductions/retirement_benefits/income_sources.yaml @@ -2,7 +2,7 @@ description: Idaho counts the following income sources under the retirement bene values: 2021-01-01: - public_pension_income - - military_retirement_income + - id_eligible_military_retirement_income 2025-01-01: - public_pension_income diff --git a/policyengine_us/parameters/gov/states/mi/tax/income/subtractions.yaml b/policyengine_us/parameters/gov/states/mi/tax/income/subtractions.yaml index 4710ac468a7..b9d9ff8abd2 100644 --- a/policyengine_us/parameters/gov/states/mi/tax/income/subtractions.yaml +++ b/policyengine_us/parameters/gov/states/mi/tax/income/subtractions.yaml @@ -2,7 +2,7 @@ description: Michigan subtracts these sources to federal adjusted gross income w values: 2017-01-01: - us_govt_interest # Line 10 - - military_retirement_income # Line 11 + - mi_military_retirement_subtraction # Line 11 - taxable_social_security # Line 14 - military_service_income # Line 14 - section_22_income # Line 22 @@ -14,7 +14,7 @@ values: - mi_other_subtractions 2026-01-01: - us_govt_interest # Line 10 - - military_retirement_income # Line 11 + - mi_military_retirement_subtraction # Line 11 - taxable_social_security # Line 14 - military_service_income # Line 14 diff --git a/policyengine_us/parameters/gov/states/nd/tax/income/taxable_income/subtractions/sources.yaml b/policyengine_us/parameters/gov/states/nd/tax/income/taxable_income/subtractions/sources.yaml index 1529671dcf1..09aaf7f05b6 100644 --- a/policyengine_us/parameters/gov/states/nd/tax/income/taxable_income/subtractions/sources.yaml +++ b/policyengine_us/parameters/gov/states/nd/tax/income/taxable_income/subtractions/sources.yaml @@ -5,7 +5,7 @@ values: - nd_ltcg_subtraction # long-term capital gains - nd_qdiv_subtraction # qualified dividends - taxable_social_security - - military_retirement_income + - nd_military_retirement_subtraction - nd_529_deduction - nd_stillborn_subtraction - nd_other_subtractions @@ -15,7 +15,7 @@ values: - nd_ltcg_subtraction # long-term capital gains - nd_qdiv_subtraction # qualified dividends - taxable_social_security - - military_retirement_income + - nd_military_retirement_subtraction - nd_529_deduction - nd_stillborn_subtraction - nd_other_subtractions @@ -25,7 +25,7 @@ values: - nd_ltcg_subtraction # long-term capital gains - nd_qdiv_subtraction # qualified dividends - taxable_social_security - - military_retirement_income + - nd_military_retirement_subtraction - military_service_income # military pay exclusion - nd_529_deduction - nd_stillborn_subtraction diff --git a/policyengine_us/parameters/gov/states/nm/tax/income/exemptions/armed_forces_retirement_pay/survivors_eligible.yaml b/policyengine_us/parameters/gov/states/nm/tax/income/exemptions/armed_forces_retirement_pay/survivors_eligible.yaml new file mode 100644 index 00000000000..ca0d874063e --- /dev/null +++ b/policyengine_us/parameters/gov/states/nm/tax/income/exemptions/armed_forces_retirement_pay/survivors_eligible.yaml @@ -0,0 +1,12 @@ +description: New Mexico allows surviving spouses to claim the armed forces retirement pay exemption when this is true. +values: + 2022-01-01: false + 2025-01-01: true + +metadata: + label: New Mexico armed forces retirement surviving spouse eligibility + unit: bool + period: year + reference: + - title: 2024 HB 252, Sections 32 and 42 (PDF pages 102 and 147), amending NMSA 7-2-5.13 and setting applicability + href: https://www.nmlegis.gov/Sessions/24%20Regular/final/HB0252.PDF#page=102 diff --git a/policyengine_us/parameters/gov/states/ri/tax/income/agi/subtractions/subtractions.yaml b/policyengine_us/parameters/gov/states/ri/tax/income/agi/subtractions/subtractions.yaml index ea4b9bf49ca..afb8cd1d13c 100644 --- a/policyengine_us/parameters/gov/states/ri/tax/income/agi/subtractions/subtractions.yaml +++ b/policyengine_us/parameters/gov/states/ri/tax/income/agi/subtractions/subtractions.yaml @@ -13,7 +13,7 @@ values: - ri_tuition_saving_program_contribution_subtraction - ri_retirement_income_subtraction - us_govt_interest - - military_retirement_income + - ri_military_retirement_subtraction - ri_other_subtractions metadata: diff --git a/policyengine_us/parameters/gov/states/ri/tax/income/agi/subtractions/taxable_retirement_income/sources.yaml b/policyengine_us/parameters/gov/states/ri/tax/income/agi/subtractions/taxable_retirement_income/sources.yaml index b9e9db7b587..d004981f19e 100644 --- a/policyengine_us/parameters/gov/states/ri/tax/income/agi/subtractions/taxable_retirement_income/sources.yaml +++ b/policyengine_us/parameters/gov/states/ri/tax/income/agi/subtractions/taxable_retirement_income/sources.yaml @@ -5,10 +5,6 @@ values: - military_retirement_income - taxable_401k_distributions - taxable_403b_distributions - 2023-01-01: - - taxable_pension_income - - taxable_401k_distributions - - taxable_403b_distributions metadata: unit: list period: year @@ -18,3 +14,5 @@ metadata: href: https://webserver.rilegislature.gov/Statutes/TITLE44/44-30/44-II/44-30-12.htm - title: Rhode Island Division of Taxation Publication 2021-02 Line 5b href: https://tax.ri.gov/sites/g/files/xkgbur541/files/notice/Pub_2021_02_pension_income_guide_04_06_21.pdf#page=5 + - title: Rhode Island Division of Taxation Publication 2024-01, pension and annuity income modification + href: https://tax.ri.gov/sites/g/files/xkgbur541/files/2024-03/PUB_2024-01_retirement_income_guide.pdf#page=7 diff --git a/policyengine_us/parameters/gov/states/sc/tax/income/subtractions/retirement/subtract_military.yaml b/policyengine_us/parameters/gov/states/sc/tax/income/subtractions/retirement/subtract_military.yaml deleted file mode 100644 index 207fc239a9c..00000000000 --- a/policyengine_us/parameters/gov/states/sc/tax/income/subtractions/retirement/subtract_military.yaml +++ /dev/null @@ -1,27 +0,0 @@ -description: South Carolina subtracts the survivors retirement deduction from the military retirement deduction if this is true. -metadata: - unit: bool - label: South Carolina choice to subtract survivors retirement deduction from military retirement deduction - reference: - - title: SC 1040 tax form instructions (2025) - href: https://dor.sc.gov/sites/dor/files/forms/SC1040Instr_2025.pdf#page=12 - - title: Instructions for Form SC-1040 (line 3p) - href: https://dor.sc.gov/forms-site/Forms/IITPacket_2021.pdf#page=17 - - title: Instructions for Form SC-1040 (line 3p) - href: https://dor.sc.gov/forms-site/Forms/IITPacket_2022.pdf#page=19 - - title: SC-1040 2021 - href: https://dor.sc.gov/forms-site/Forms/SC1040_2021.pdf#page=2 - - title: SC-1040 2022 - href: https://dor.sc.gov/forms-site/Forms/SC1040_2022.pdf#page=2 - # The law indicates the 2022 case. We model calculation of the survivors retirement deduction in 2021 based on the tax form, - # where the retirement deduction is subtracts from military retirement deduction. - - title: South Carolina State SECTION 12-6-1170(C)(1) - href: https://www.scstatehouse.gov/code/t12c006.php - - title: SC 1040 tax form instructions (2023) - href: https://dor.sc.gov/forms-site/Forms/IITPacket_2023.pdf#page=25 - - title: SC 1040 tax form instructions (2024) - href: https://dor.sc.gov/forms-site/Forms/SC1040Instr_2024.pdf#page=12 - -values: - 2021-01-01: true - 2022-01-01: false diff --git a/policyengine_us/tests/code_health/test_source_conflicts.py b/policyengine_us/tests/code_health/test_source_conflicts.py index 4a9b7f803db..b2f15532b65 100644 --- a/policyengine_us/tests/code_health/test_source_conflicts.py +++ b/policyengine_us/tests/code_health/test_source_conflicts.py @@ -171,6 +171,7 @@ "policyengine_us/variables/gov/states/mn/dcyf/mfip/income/earned/mn_mfip_dependent_care_deduction.py": 1, "policyengine_us/variables/gov/states/mn/tax/income/deductions/mn_itemized_deductions.py": 1, "policyengine_us/variables/gov/states/mo/dese/ccs/eligibility/mo_ccs_income_eligible.py": 1, + "policyengine_us/variables/gov/states/sc/tax/income/subtractions/retirement/sc_retirement_deduction_survivors.py": 1, "policyengine_us/variables/gov/states/vt/tax/income/credits/cdcc/vt_low_income_cdcc_eligible.py": 1, "policyengine_us/variables/gov/states/wa/dor/property_tax_exemption/senior_disabled/wa_pte_categorical_eligible.py": 1, "policyengine_us/variables/gov/territories/pr/tax/income/pr_agi.py": 1, diff --git a/policyengine_us/tests/core/test_mi_military_survivor_income_invariants.py b/policyengine_us/tests/core/test_mi_military_survivor_income_invariants.py new file mode 100644 index 00000000000..13828a3c576 --- /dev/null +++ b/policyengine_us/tests/core/test_mi_military_survivor_income_invariants.py @@ -0,0 +1,192 @@ +"""Non-spouse military survivor benefits stay in Michigan's tax base. + +Each year uses one vectorized simulation of paired tax units. The pairs +share every input except increasing military survivor benefits, including +benefits paid to dependents. Seeded incomes span the retirement deduction +caps and ages span Michigan's retirement tiers. This catches an exemption +that incorrectly leaves taxable income flat as well as deductions that +incorrectly count taxable survivor benefits against another deduction. +""" + +import numpy as np +import pytest + +from policyengine_us import Simulation + + +SEED = 20261022 +TOLERANCE = 0.05 # dollars; model calculations use float32 arrays +FILER_AGES = [20, 40, 59, 62, 65, 67, 70, 75, 80, 85] + + +def _draw_units(year): + rng = np.random.default_rng(SEED + year) + units = [] + for i in range(24): + joint = i % 2 == 1 + + def filer(age): + return { + "age": age, + "employment_income": float(rng.integers(0, 80_001)), + "taxable_private_pension_income": float(rng.integers(0, 100_001)), + "military_retirement_pay": ( + 0.0 if i % 3 == 0 else float(rng.integers(0, 50_001)) + ), + "taxable_interest_income": float(rng.integers(0, 20_001)), + "retirement_benefits_from_ss_exempt_employment": ( + 10_000.0 if age >= 59 and i % 3 != 0 else 0.0 + ), + "year_of_retirement": 2008 if i % 4 == 0 else 2018, + "military_retirement_pay_survivors": float(rng.integers(0, 30_001)), + # Unchanged pairs check that units cannot affect each other. + "increment": 0.0 if i % 6 == 0 else float(rng.integers(1, 30_001)), + } + + head = filer(FILER_AGES[i % len(FILER_AGES)]) + spouse = filer(FILER_AGES[(i + 3) % len(FILER_AGES)]) if joint else None + dependents = [ + { + "age": 8 + j * 4, + "military_retirement_pay_survivors": float(rng.integers(0, 10_001)), + "increment": 0.0 if i % 6 == 0 else float(rng.integers(1, 10_001)), + } + for j in range(i % 3) + ] + units.append({"head": head, "spouse": spouse, "dependents": dependents}) + + # Guarantee a young filer above the zero floor, independently of the draw. + units[0] = { + "head": { + "age": 20, + "employment_income": 40_000.0, + "military_retirement_pay_survivors": 0.0, + "increment": 10_000.0, + }, + "spouse": None, + "dependents": [], + } + # This pair changes only a dependent's benefits, which belong on their + # own return and must not change the filer's AGI or Michigan deductions. + units[1]["head"]["increment"] = 0.0 + units[1]["spouse"]["increment"] = 0.0 + units[1]["dependents"][0]["increment"] = 10_000.0 + # Guarantee a pair whose small survivor increase remains below the tax + # base's zero floor, alongside the positive-base example above. + units[2]["head"] = { + "age": 20, + "employment_income": 0.0, + "military_retirement_pay_survivors": 0.0, + "increment": 1_000.0, + } + return units + + +def _paired_situation(units, year): + people, tax_units, marital_units = {}, {}, {} + households, families, spm_units = {}, {}, {} + for increased in (False, True): + for i, unit in enumerate(units): + key = f"{int(increased)}_{i}" + members, couple = [], [] + + def add_person(role, record): + name = f"{role}_{key}" + members.append(name) + person = { + variable: {year: amount} + for variable, amount in record.items() + if variable != "increment" + } + benefits = record["military_retirement_pay_survivors"] + person["military_retirement_pay_survivors"] = { + year: benefits + increased * record["increment"] + } + person.update( + { + "is_surviving_spouse": {year: False}, + "is_tax_unit_head": {year: role == "head"}, + "is_tax_unit_spouse": {year: role == "spouse"}, + "is_tax_unit_dependent": {year: role.startswith("dependent")}, + } + ) + people[name] = person + return name + + couple.append(add_person("head", unit["head"])) + if unit["spouse"] is not None: + couple.append(add_person("spouse", unit["spouse"])) + marital_units[f"couple_{key}"] = {"members": couple} + for j, record in enumerate(unit["dependents"]): + name = add_person(f"dependent_{j}", record) + marital_units[f"dependent_{key}_{j}"] = {"members": [name]} + tax_units[f"tax_unit_{key}"] = {"members": members} + households[f"household_{key}"] = { + "members": members, + "state_name": {year: "MI"}, + } + families[f"family_{key}"] = {"members": members} + spm_units[f"spm_unit_{key}"] = {"members": members} + return { + "people": people, + "tax_units": tax_units, + "marital_units": marital_units, + "households": households, + "families": families, + "spm_units": spm_units, + } + + +@pytest.mark.parametrize("year", [2024, 2025, 2026]) +def test_nonspouse_survivor_benefits_never_reduce_michigan_taxable_income(year): + units = _draw_units(year) + sim = Simulation(situation=_paired_situation(units, year)) + count = len(units) + + def paired(variable): + amounts = sim.calculate(variable, year) + return amounts[:count], amounts[count:] + + increments = np.array( + [ + unit["head"]["increment"] + + (unit["spouse"]["increment"] if unit["spouse"] is not None else 0) + for unit in units + ] + ) + # Child benefits are excluded from the filer's federal AGI; every dollar + # paid to the head or spouse is federally taxable on this return. + agi_before, agi_after = paired("adjusted_gross_income") + np.testing.assert_allclose( + agi_after - agi_before, increments, rtol=0, atol=TOLERANCE + ) + for variable in ("mi_subtractions", "mi_exemptions"): + before, after = paired(variable) + np.testing.assert_allclose( + after, + before, + rtol=0, + atol=TOLERANCE, + err_msg=f"Non-spouse survivor benefits changed {variable}", + ) + + before, after = paired("mi_taxable_income") + assert (after >= before - TOLERANCE).all() + above_floor = before > TOLERANCE + np.testing.assert_allclose( + after[above_floor] - before[above_floor], + increments[above_floor], + rtol=0, + atol=TOLERANCE, + err_msg="Non-spouse benefits must increase a positive Michigan tax base", + ) + unchanged_filer_income = increments == 0 + np.testing.assert_allclose( + after[unchanged_filer_income], + before[unchanged_filer_income], + rtol=0, + atol=TOLERANCE, + ) + assert (above_floor & (increments > 0)).any() + assert ((before == 0) & (after == 0) & (increments > 0)).any() + assert unchanged_filer_income.any() diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/tax/income/subtractions/az_military_retirement_subtraction.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/tax/income/subtractions/az_military_retirement_subtraction.yaml index 6cfdf96d24a..25768be8c5b 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/az/tax/income/subtractions/az_military_retirement_subtraction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/az/tax/income/subtractions/az_military_retirement_subtraction.yaml @@ -119,3 +119,68 @@ state_code: AZ output: az_military_retirement_subtraction: 9_000 + +- name: Arizona excludes a surviving spouse's military survivor benefits. + period: 2024 + input: + state_code: AZ + is_surviving_spouse: true + military_retirement_pay_survivors: 10_000 + output: + az_military_retirement_subtraction: 10_000 + +- name: Arizona leaves a non-spouse survivor's SBP payments off line 29b and counts them toward the $2,500 public pension subtraction. + period: 2024 + input: + state_code: AZ + age: 20 + is_surviving_spouse: false + military_retirement_pay_survivors: 10_000 + output: + az_military_retirement_subtraction: 0 + az_public_pension_exclusion: 2_500 + +- name: Arizona subtracts own military retirement pay in full and a non-spouse survivor's SBP payments up to $2,500. + period: 2024 + input: + state_code: AZ + is_surviving_spouse: false + military_retirement_pay: 30_000 + military_retirement_pay_survivors: 10_000 + output: + az_military_retirement_subtraction: 30_000 + az_public_pension_exclusion: 2_500 + +- name: In 2021, Arizona counts a surviving spouse's SBP payments toward the $2,500 public pension subtraction. + period: 2021 + input: + state_code: AZ + is_surviving_spouse: true + military_retirement_pay_survivors: 6_000 + output: + az_military_retirement_subtraction: 0 + # min(6,000, 2,500) = 2,500 under 43-1022(2) and ITR 11-3. ADOR moved a surviving spouse's payments to line 29b in 2023. + az_public_pension_exclusion: 2_500 + +- name: In 2022, Arizona counts a surviving spouse's SBP payments and public pension toward one $2,500 subtraction. + period: 2022 + input: + state_code: AZ + is_surviving_spouse: true + military_retirement_pay_survivors: 1_000 + taxable_public_pension_income: 2_000 + output: + az_military_retirement_subtraction: 0 + # min(1,000 + 2,000, 2,500) = 2,500. + az_public_pension_exclusion: 2_500 + +- name: In 2023, Arizona subtracts a surviving spouse's SBP payments in full on line 29b. + period: 2023 + input: + state_code: AZ + is_surviving_spouse: true + military_retirement_pay_survivors: 6_000 + output: + # Arizona sets no line 29b cap from 2021 (43-1022(26)) and leaves these payments out of the $2,500 subtraction. + az_military_retirement_subtraction: 6_000 + az_public_pension_exclusion: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/tax/income/subtractions/az_public_pension_exclusion.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/tax/income/subtractions/az_public_pension_exclusion.yaml index 5eb5a626fd8..ee30d2576e6 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/az/tax/income/subtractions/az_public_pension_exclusion.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/az/tax/income/subtractions/az_public_pension_exclusion.yaml @@ -96,3 +96,49 @@ state_code: AZ output: az_public_pension_exclusion: 5_000 + +- name: In 2021, Arizona counts a non-spouse survivor's SBP payments toward the $2,500 public pension subtraction. + period: 2021 + input: + state_code: AZ + age: 20 + is_surviving_spouse: false + military_retirement_pay_survivors: 6_000 + output: + az_military_retirement_subtraction: 0 + # min(6,000, 2,500) = 2,500 under 43-1022(2). Arizona puts no non-spouse survivor's payments on line 29b. + az_public_pension_exclusion: 2_500 + +- name: In 2023, Arizona still counts a non-spouse survivor's SBP payments toward the $2,500 public pension subtraction. + period: 2023 + input: + state_code: AZ + age: 20 + is_surviving_spouse: false + military_retirement_pay_survivors: 6_000 + output: + az_military_retirement_subtraction: 0 + # ADOR moved only a surviving spouse's payments to line 29b, so min(6,000, 2,500) = 2,500 under 43-1022(2). + az_public_pension_exclusion: 2_500 + +- name: Arizona leaves a dependent's SBP payments out of the filer's $2,500 public pension subtraction. + period: 2023 + input: + people: + head: + age: 45 + taxable_public_pension_income: 1_000 + child: + age: 10 + is_tax_unit_dependent: true + military_retirement_pay_survivors: 4_000 + tax_units: + tax_unit: + members: [head, child] + households: + household: + members: [head, child] + state_code: AZ + output: + # Only the head's 1,000 counts; the child's 4,000 goes on the child's own return. + az_public_pension_exclusion: 1_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/id/tax/income/deductions/retirement_benefits/id_retirement_benefits_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/id/tax/income/deductions/retirement_benefits/id_retirement_benefits_deduction.yaml index e219b830b1f..72f0567011f 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/id/tax/income/deductions/retirement_benefits/id_retirement_benefits_deduction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/id/tax/income/deductions/retirement_benefits/id_retirement_benefits_deduction.yaml @@ -102,3 +102,36 @@ state_code: ID output: id_retirement_benefits_deduction: 30_000 + +- name: Idaho general retirement deduction excludes non-spouse military survivor benefits before 2025. + period: 2024 + input: + state_code: ID + age: 65 + is_surviving_spouse: false + military_retirement_pay_survivors: 10_000 + output: + id_retirement_benefits_deduction_relevant_income: 0 + id_retirement_benefits_deduction: 0 + +- name: Idaho general retirement deduction includes an unremarried surviving spouse before 2025. + period: 2024 + input: + state_code: ID + age: 65 + is_surviving_spouse: true + military_retirement_pay_survivors: 10_000 + output: + id_retirement_benefits_deduction_relevant_income: 10_000 + id_retirement_benefits_deduction: 10_000 + +- name: Idaho general retirement deduction retains own military pay before 2025. + period: 2024 + input: + state_code: ID + age: 65 + is_surviving_spouse: false + military_retirement_pay: 10_000 + output: + id_retirement_benefits_deduction_relevant_income: 10_000 + id_retirement_benefits_deduction: 10_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/id/tax/income/subtractions/military_retirement/id_military_retirement_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/id/tax/income/subtractions/military_retirement/id_military_retirement_deduction.yaml index 901804ef8e0..3bb0e02ec91 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/id/tax/income/subtractions/military_retirement/id_military_retirement_deduction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/id/tax/income/subtractions/military_retirement/id_military_retirement_deduction.yaml @@ -88,6 +88,7 @@ people: person1: is_tax_unit_head_or_spouse: true + is_surviving_spouse: true military_retirement_pay_survivors: 25_000 age: 65 tax_units: @@ -101,3 +102,85 @@ output: id_military_retirement_deduction: 25_000 +- name: Idaho military retirement deduction excludes a non-spouse survivor. + period: 2025 + input: + state_code: ID + age: 65 + is_surviving_spouse: false + military_retirement_pay_survivors: 25_000 + output: + id_military_retirement_deduction: 0 + +- name: Idaho military retirement deduction keeps own pay when non-spouse survivor benefits are present. + period: 2025 + input: + state_code: ID + age: 65 + is_surviving_spouse: false + military_retirement_pay: 30_000 + military_retirement_pay_survivors: 25_000 + output: + id_military_retirement_deduction: 30_000 + +- name: Idaho military survivor deduction excludes a remarried survivor filing separately. + period: 2025 + input: + people: + survivor: + is_surviving_spouse: true + military_retirement_pay_survivors: 25_000 + new_spouse: {} + families: + family: + members: [survivor, new_spouse] + marital_units: + couple: + members: [survivor, new_spouse] + tax_units: + survivor_return: + members: [survivor] + filing_status: SEPARATE + spouse_return: + members: [new_spouse] + filing_status: SEPARATE + households: + household: + members: [survivor, new_spouse] + state_code: ID + output: + id_military_retirement_deduction: [0, 0] + +- name: Idaho unmarried widow remains eligible when a married adult child shares the family. + period: 2025 + input: + people: + survivor: + age: 65 + is_surviving_spouse: true + military_retirement_pay_survivors: 25_000 + adult_child: + age: 40 + child_spouse: + age: 40 + families: + family: + members: [survivor, adult_child, child_spouse] + marital_units: + widow: + members: [survivor] + couple: + members: [adult_child, child_spouse] + tax_units: + survivor_return: + members: [survivor] + filing_status: SINGLE + child_return: + members: [adult_child, child_spouse] + filing_status: JOINT + households: + household: + members: [survivor, adult_child, child_spouse] + state_code: ID + output: + id_military_retirement_deduction: [25_000, 0] diff --git a/policyengine_us/tests/policy/baseline/gov/states/in/tax/income/deductions/in_military_retirement_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/in/tax/income/deductions/in_military_retirement_deduction.yaml index 5d7d2005361..033fd22097c 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/in/tax/income/deductions/in_military_retirement_deduction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/in/tax/income/deductions/in_military_retirement_deduction.yaml @@ -21,6 +21,7 @@ person1: military_retirement_pay: 30_000 person2: + is_surviving_spouse: true military_retirement_pay_survivors: 10_000 tax_units: tax_unit: @@ -31,3 +32,32 @@ state_code: IN output: in_military_retirement_deduction: 40_000 + +- name: Indiana non-spouse survivor benefits do not qualify for the military retirement deduction. + period: 2024 + input: + age: 20 + state_code: IN + is_surviving_spouse: false + military_retirement_pay_survivors: 10_000 + output: + in_military_retirement_deduction: 0 + +- name: Indiana service member can deduct own pay without deducting non-spouse survivor benefits. + period: 2024 + input: + state_code: IN + is_surviving_spouse: false + military_retirement_pay: 30_000 + military_retirement_pay_survivors: 10_000 + output: + in_military_retirement_deduction: 30_000 + +- name: Indiana surviving spouse benefits retain the 2021 phase-in. + period: 2021 + input: + state_code: IN + is_surviving_spouse: true + military_retirement_pay_survivors: 10_000 + output: + in_military_retirement_deduction: 6_250 + 0.75 * (10_000 - 6_250) diff --git a/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/deductions/interest_dividends_capital_gains/mi_interest_dividends_capital_gains_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/deductions/interest_dividends_capital_gains/mi_interest_dividends_capital_gains_deduction.yaml index b58c60f5e55..b57dc766323 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/deductions/interest_dividends_capital_gains/mi_interest_dividends_capital_gains_deduction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/deductions/interest_dividends_capital_gains/mi_interest_dividends_capital_gains_deduction.yaml @@ -127,6 +127,7 @@ taxable_pension_income: 10_000 military_retirement_pay: 500 military_retirement_pay_survivors: 500 + is_surviving_spouse: true taxable_interest_income: 5_000 dividend_income: 400 capital_gains: 100 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/deductions/retirement/expanded/mi_expanded_retirement_benefits_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/deductions/retirement/expanded/mi_expanded_retirement_benefits_deduction.yaml index 3839bf0b58a..7ffcd0ae033 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/deductions/retirement/expanded/mi_expanded_retirement_benefits_deduction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/deductions/retirement/expanded/mi_expanded_retirement_benefits_deduction.yaml @@ -206,6 +206,7 @@ person1: taxable_pension_income: 20_000 military_retirement_pay_survivors: 1_000 + is_surviving_spouse: true is_tax_unit_head_or_spouse: true tax_units: tax_unit: diff --git a/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/deductions/retirement/tier_one/mi_retirement_benefits_deduction_tier_one_amount.yaml b/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/deductions/retirement/tier_one/mi_retirement_benefits_deduction_tier_one_amount.yaml index c6e1780f293..68d3aba2430 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/deductions/retirement/tier_one/mi_retirement_benefits_deduction_tier_one_amount.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/deductions/retirement/tier_one/mi_retirement_benefits_deduction_tier_one_amount.yaml @@ -470,6 +470,7 @@ taxable_private_pension_income: 50_000 military_retirement_pay: 10_000 military_retirement_pay_survivors: 10_000 + is_surviving_spouse: true is_tax_unit_head_or_spouse: true tax_units: tax_unit: diff --git a/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/deductions/retirement/tier_three/ss_exempt/not_retired/mi_retirement_benefits_deduction_tier_three_ss_exempt_not_retired.yaml b/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/deductions/retirement/tier_three/ss_exempt/not_retired/mi_retirement_benefits_deduction_tier_three_ss_exempt_not_retired.yaml index 040f0f15a38..66a380bc2af 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/deductions/retirement/tier_three/ss_exempt/not_retired/mi_retirement_benefits_deduction_tier_three_ss_exempt_not_retired.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/deductions/retirement/tier_three/ss_exempt/not_retired/mi_retirement_benefits_deduction_tier_three_ss_exempt_not_retired.yaml @@ -303,6 +303,7 @@ person1: taxable_pension_income: 40_000 military_retirement_pay_survivors: 1_000 + is_surviving_spouse: true is_tax_unit_head_or_spouse: true tax_units: tax_unit: diff --git a/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/deductions/retirement/tier_three/ss_exempt/retired/mi_retirement_benefits_deduction_tier_three_ss_exempt_retired.yaml b/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/deductions/retirement/tier_three/ss_exempt/retired/mi_retirement_benefits_deduction_tier_three_ss_exempt_retired.yaml index a4dc58d4a61..b7104cc8fd9 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/deductions/retirement/tier_three/ss_exempt/retired/mi_retirement_benefits_deduction_tier_three_ss_exempt_retired.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/deductions/retirement/tier_three/ss_exempt/retired/mi_retirement_benefits_deduction_tier_three_ss_exempt_retired.yaml @@ -162,6 +162,7 @@ person1: taxable_pension_income: 40_000 military_retirement_pay_survivors: 1_000 + is_surviving_spouse: true is_tax_unit_head_or_spouse: true tax_units: tax_unit: diff --git a/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/deductions/standard/tier_three/mi_standard_deduction_tier_three.yaml b/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/deductions/standard/tier_three/mi_standard_deduction_tier_three.yaml index cb513bc35af..dc3b0247fec 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/deductions/standard/tier_three/mi_standard_deduction_tier_three.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/deductions/standard/tier_three/mi_standard_deduction_tier_three.yaml @@ -165,6 +165,7 @@ person1: military_retirement_pay: 1_000 military_retirement_pay_survivors: 500 + is_surviving_spouse: true military_service_income: 500 taxable_social_security: 100 is_tax_unit_head_or_spouse: true diff --git a/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/deductions/standard/tier_two/mi_standard_deduction_tier_two.yaml b/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/deductions/standard/tier_two/mi_standard_deduction_tier_two.yaml index f62ff181535..bd4819556fd 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/deductions/standard/tier_two/mi_standard_deduction_tier_two.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/deductions/standard/tier_two/mi_standard_deduction_tier_two.yaml @@ -76,6 +76,7 @@ person1: military_retirement_pay: 3_000 military_retirement_pay_survivors: 2_000 + is_surviving_spouse: true is_tax_unit_head_or_spouse: true tax_units: tax_unit: diff --git a/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/mi_military_retirement_subtraction.yaml b/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/mi_military_retirement_subtraction.yaml new file mode 100644 index 00000000000..3eee26f1030 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/mi_military_retirement_subtraction.yaml @@ -0,0 +1,117 @@ +- name: Michigan subtracts the retiree's own military retirement pay + period: 2024 + input: + state_code: MI + military_retirement_pay: 10_000 + military_retirement_pay_survivors: 0 + is_surviving_spouse: false + output: + mi_military_retirement_subtraction: 10_000 + +- name: Michigan subtracts military survivor benefits received by a surviving spouse + period: 2024 + input: + state_code: MI + military_retirement_pay: 0 + military_retirement_pay_survivors: 10_000 + is_surviving_spouse: true + output: + mi_military_retirement_subtraction: 10_000 + +- name: Michigan taxes military survivor benefits received by a non-spouse + period: 2024 + input: + state_code: MI + military_retirement_pay: 0 + military_retirement_pay_survivors: 10_000 + is_surviving_spouse: false + output: + mi_military_retirement_subtraction: 0 + +- name: Michigan evaluates survivor eligibility separately for each filer and excludes dependent pay + period: 2025 + input: + people: + head: + age: 40 + employment_income: 40_000 + military_retirement_pay: 20_000 + military_retirement_pay_survivors: 10_000 + is_surviving_spouse: true + spouse: + age: 40 + military_retirement_pay: 5_000 + military_retirement_pay_survivors: 15_000 + is_surviving_spouse: false + dependent: + age: 15 + military_retirement_pay: 3_000 + military_retirement_pay_survivors: 5_000 + is_surviving_spouse: true + tax_units: + tax_unit: + members: [head, spouse, dependent] + marital_units: + couple: + members: [head, spouse] + child: + members: [dependent] + households: + household: + members: [head, spouse, dependent] + state_code: MI + output: + mi_military_retirement_subtraction: [30_000, 5_000, 8_000] + mi_subtractions: 35_000 + +- name: Taxable non-spouse survivor benefits do not reduce the 2024 expanded retirement cap + period: 2024 + input: + state_code: MI + age: 62 + taxable_private_pension_income: 50_000 + military_retirement_pay_survivors: 10_000 + is_surviving_spouse: false + output: + mi_military_retirement_subtraction: 0 + # Worksheet 3.3 uses only the military subtraction on Schedule 1 line 11. + # The full $64,040 cap times 50% remains available for civilian pensions. + mi_expanded_retirement_benefits_deduction: 32_020 + mi_retirement_benefits_deduction_tier_one_amount: 50_000 + +- name: Taxable non-spouse survivor benefits do not reduce the 2025 expanded retirement cap + period: 2025 + input: + state_code: MI + age: 62 + taxable_private_pension_income: 50_000 + military_retirement_pay_survivors: 10_000 + is_surviving_spouse: false + output: + mi_military_retirement_subtraction: 0 + # $65,897 times 75%, with no military subtraction to offset the cap. + mi_expanded_retirement_benefits_deduction: 49_422.75 + +- name: Taxable non-spouse survivor benefits do not reduce a senior's private pension cap + period: 2024 + input: + state_code: MI + age: 82 + taxable_private_pension_income: 70_000 + military_retirement_pay_survivors: 10_000 + is_surviving_spouse: false + output: + mi_military_retirement_subtraction: 0 + mi_retirement_benefits_deduction_tier_one_amount: 64_040 + +- name: Taxable non-spouse survivor benefits do not reduce a senior's investment income deduction + period: 2024 + input: + state_code: MI + age: 82 + taxable_interest_income: 20_000 + military_retirement_pay_survivors: 10_000 + is_surviving_spouse: false + output: + mi_military_retirement_subtraction: 0 + mi_interest_dividends_capital_gains_deduction: 14_274 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/mi_taxable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/mi_taxable_income.yaml index 9bc049de866..13cb0d4198b 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/mi_taxable_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/mi_taxable_income.yaml @@ -19,3 +19,48 @@ state_code: MI output: # 20_000 - 19_000 - 5_000 < 0 mi_taxable_income: 0 + +- name: A non-spouse military survivor pays Michigan tax on benefits from a deceased parent. + period: 2024 + input: + age: 20 + employment_income: 40_000 + military_retirement_pay_survivors: 10_000 + is_surviving_spouse: false + state_code: MI + output: + adjusted_gross_income: 50_000 + mi_military_retirement_subtraction: 0 + mi_subtractions: 0 + mi_taxable_income: 44_400 + mi_income_tax_before_non_refundable_credits: 1_887 + +- name: A surviving spouse subtracts military survivor retirement benefits. + period: 2024 + input: + age: 40 + employment_income: 40_000 + military_retirement_pay_survivors: 10_000 + is_surviving_spouse: true + state_code: MI + output: + adjusted_gross_income: 50_000 + mi_military_retirement_subtraction: 10_000 + mi_subtractions: 10_000 + mi_taxable_income: 34_400 + mi_income_tax_before_non_refundable_credits: 1_462 + +- name: A military retiree subtracts their own pay without surviving-spouse status. + period: 2024 + input: + age: 40 + employment_income: 40_000 + military_retirement_pay: 10_000 + is_surviving_spouse: false + state_code: MI + output: + adjusted_gross_income: 50_000 + mi_military_retirement_subtraction: 10_000 + mi_subtractions: 10_000 + mi_taxable_income: 34_400 + mi_income_tax_before_non_refundable_credits: 1_462 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nd/tax/income/subtractions/nd_military_retirement_subtraction.yaml b/policyengine_us/tests/policy/baseline/gov/states/nd/tax/income/subtractions/nd_military_retirement_subtraction.yaml new file mode 100644 index 00000000000..ab91d1bdb8a --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/nd/tax/income/subtractions/nd_military_retirement_subtraction.yaml @@ -0,0 +1,57 @@ +- name: North Dakota does not subtract a nonspouse survivor's benefits on their own return. + period: 2024 + input: + state_code: ND + age: 20 + is_surviving_spouse: false + employment_income: 40_000 + military_retirement_pay_survivors: 10_000 + output: + adjusted_gross_income: 50_000 + nd_military_retirement_subtraction: 0 + nd_subtractions: 0 + nd_taxable_income: 35_400 + +- name: North Dakota subtracts a surviving spouse's military survivor benefits. + period: 2024 + input: + state_code: ND + age: 50 + is_surviving_spouse: true + military_retirement_pay_survivors: 10_000 + output: + nd_military_retirement_subtraction: 10_000 + nd_subtractions: 10_000 + +- name: North Dakota subtracts own military retirement pay without requiring surviving spouse status. + period: 2024 + input: + state_code: ND + age: 60 + is_surviving_spouse: false + military_retirement_pay: 20_000 + output: + nd_military_retirement_subtraction: 20_000 + nd_subtractions: 20_000 + +- name: North Dakota keeps own retirement pay separate from a nonspouse survivor annuity. + period: 2025 + input: + state_code: ND + is_surviving_spouse: false + military_retirement_pay: 20_000 + military_retirement_pay_survivors: 10_000 + output: + nd_military_retirement_subtraction: 20_000 + nd_subtractions: 20_000 + +- name: North Dakota combines own retirement and eligible surviving spouse benefits. + period: 2025 + input: + state_code: ND + is_surviving_spouse: true + military_retirement_pay: 20_000 + military_retirement_pay_survivors: 10_000 + output: + nd_military_retirement_subtraction: 30_000 + nd_subtractions: 30_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/exemptions/nm_armed_forces_retirement_pay_exemption_person.yaml b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/exemptions/nm_armed_forces_retirement_pay_exemption_person.yaml index 439b2215b0e..91544b953c2 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/exemptions/nm_armed_forces_retirement_pay_exemption_person.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/exemptions/nm_armed_forces_retirement_pay_exemption_person.yaml @@ -33,3 +33,67 @@ is_tax_unit_head_or_spouse: false output: nm_armed_forces_retirement_pay_exemption_person: 0 + +- name: New Mexico does not exempt a nonspouse survivor annuity in 2024. + period: 2024 + input: + state_code: NM + age: 20 + is_surviving_spouse: false + is_tax_unit_head_or_spouse: true + military_retirement_pay_survivors: 10_000 + output: + nm_armed_forces_retirement_pay_exemption_person: 0 + +- name: New Mexico does not exempt a surviving spouse's annuity before 2025. + period: 2024 + input: + state_code: NM + is_surviving_spouse: true + is_tax_unit_head_or_spouse: true + military_retirement_pay_survivors: 10_000 + output: + nm_armed_forces_retirement_pay_exemption_person: 0 + +- name: New Mexico exempts a surviving spouse's annuity starting in 2025. + period: 2025 + input: + state_code: NM + is_surviving_spouse: true + is_tax_unit_head_or_spouse: true + military_retirement_pay_survivors: 10_000 + output: + nm_armed_forces_retirement_pay_exemption_person: 10_000 + +- name: New Mexico does not extend the 2025 survivor exemption to nonspouses. + period: 2025 + input: + state_code: NM + age: 20 + is_surviving_spouse: false + is_tax_unit_head_or_spouse: true + military_retirement_pay_survivors: 10_000 + output: + nm_armed_forces_retirement_pay_exemption_person: 0 + +- name: New Mexico keeps own retirement pay separate from nonspouse survivor benefits. + period: 2025 + input: + state_code: NM + is_surviving_spouse: false + is_tax_unit_head_or_spouse: true + military_retirement_pay: 25_000 + military_retirement_pay_survivors: 10_000 + output: + nm_armed_forces_retirement_pay_exemption_person: 25_000 + +- name: New Mexico applies one cap to own pay and eligible surviving spouse benefits. + period: 2025 + input: + state_code: NM + is_surviving_spouse: true + is_tax_unit_head_or_spouse: true + military_retirement_pay: 25_000 + military_retirement_pay_survivors: 10_000 + output: + nm_armed_forces_retirement_pay_exemption_person: 30_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/agi/subtractions/ri_military_retirement_subtraction.yaml b/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/agi/subtractions/ri_military_retirement_subtraction.yaml new file mode 100644 index 00000000000..fcd6f492584 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/agi/subtractions/ri_military_retirement_subtraction.yaml @@ -0,0 +1,59 @@ +- name: Rhode Island does not subtract a nonspouse survivor annuity on the survivor's own return. + period: 2024 + input: + state_code: RI + age: 20 + is_surviving_spouse: false + employment_income: 40_000 + military_retirement_pay_survivors: 10_000 + output: + adjusted_gross_income: 50_000 + ri_military_retirement_subtraction: 0 + ri_subtractions: 0 + ri_agi: 50_000 + +- name: Rhode Island subtracts surviving spouse annuities starting in 2023. + period: 2023 + input: + state_code: RI + age: 50 + is_surviving_spouse: true + military_retirement_pay_survivors: 10_000 + output: + ri_military_retirement_subtraction: 10_000 + ri_subtractions: 10_000 + +- name: Rhode Island subtracts own military retirement pay without surviving spouse status. + period: 2024 + input: + state_code: RI + age: 60 + is_surviving_spouse: false + military_retirement_pay: 20_000 + output: + ri_military_retirement_subtraction: 20_000 + ri_subtractions: 20_000 + +- name: Rhode Island's separate military subtraction is unavailable before 2023. + period: 2022 + input: + state_code: RI + age: 50 + is_surviving_spouse: true + military_retirement_pay: 20_000 + military_retirement_pay_survivors: 10_000 + output: + ri_military_retirement_subtraction: 0 + ri_subtractions: 0 + +- name: Rhode Island keeps own retirement pay separate from a nonspouse survivor annuity. + period: 2025 + input: + state_code: RI + age: 60 + is_surviving_spouse: false + military_retirement_pay: 20_000 + military_retirement_pay_survivors: 10_000 + output: + ri_military_retirement_subtraction: 20_000 + ri_subtractions: 20_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/agi/subtractions/taxable_retirement_income/ri_retirement_income_subtraction.yaml b/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/agi/subtractions/taxable_retirement_income/ri_retirement_income_subtraction.yaml index cf0fb9772ce..5d99228c5bb 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/agi/subtractions/taxable_retirement_income/ri_retirement_income_subtraction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/agi/subtractions/taxable_retirement_income/ri_retirement_income_subtraction.yaml @@ -148,3 +148,46 @@ military_retirement_pay_survivors: 5_000 output: ri_retirement_income_subtraction: 15_000 + +- name: 2024 - Nonspouse survivor benefits remain eligible under the general pension rules. + period: 2024 + input: + state_code: RI + age: 75 + is_surviving_spouse: false + ri_retirement_income_subtraction_eligible: true + taxable_pension_income: 15_000 + military_retirement_pay_survivors: 10_000 + output: + ri_military_retirement_subtraction: 0 + ri_retirement_income_subtraction: 20_000 + ri_subtractions: 20_000 + +- name: 2024 - Own military pay is excluded once and nonspouse survivor benefits use the general pension cap. + period: 2024 + input: + state_code: RI + age: 75 + is_surviving_spouse: false + ri_retirement_income_subtraction_eligible: true + taxable_pension_income: 10_000 + military_retirement_pay: 20_000 + military_retirement_pay_survivors: 5_000 + output: + ri_military_retirement_subtraction: 20_000 + ri_retirement_income_subtraction: 15_000 + ri_subtractions: 35_000 + +- name: 2025 - Surviving spouse military benefits cannot be counted again in the general pension subtraction. + period: 2025 + input: + state_code: RI + age: 75 + is_surviving_spouse: true + ri_retirement_income_subtraction_eligible: true + taxable_pension_income: 10_000 + military_retirement_pay_survivors: 20_000 + output: + ri_military_retirement_subtraction: 20_000 + ri_retirement_income_subtraction: 10_000 + ri_subtractions: 30_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/subtractions/military_retirement/sc_military_deduction_survivors.yaml b/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/subtractions/military_retirement/sc_military_deduction_survivors.yaml index e0b3ea4a9fa..6a7bb85b71b 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/subtractions/military_retirement/sc_military_deduction_survivors.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/subtractions/military_retirement/sc_military_deduction_survivors.yaml @@ -2,6 +2,7 @@ period: 2022 input: military_retirement_pay_survivors: 11_000 + is_surviving_spouse: true state_code: SC output: sc_military_deduction_survivors: 11_000 @@ -35,13 +36,14 @@ sc_military_deduction_survivors: [0, 0] sc_military_deduction: 0 -- name: The head's survivor benefits are deducted and the dependent's are not. +- name: The surviving spouse head's benefits are deducted and the dependent's are not. period: 2025 input: people: person1: age: 60 military_retirement_pay_survivors: 9_000 + is_surviving_spouse: true person2: age: 15 is_tax_unit_dependent: true @@ -61,3 +63,40 @@ output: sc_military_deduction_survivors: [9_000, 0] sc_military_deduction: 9_000 + +- name: South Carolina does not subtract a nonspouse survivor annuity on the survivor's own return. + period: 2024 + input: + state_code: SC + age: 20 + is_surviving_spouse: false + is_tax_unit_head_or_spouse: true + military_retirement_pay_survivors: 10_000 + output: + sc_military_deduction_survivors: 0 + sc_military_deduction: 0 + +- name: South Carolina's spouse condition also applies before the 2022 military deduction expansion. + period: 2021 + input: + state_code: SC + age: 20 + is_surviving_spouse: false + is_tax_unit_head_or_spouse: true + military_retirement_pay_survivors: 10_000 + output: + sc_military_deduction_survivors: 0 + +- name: South Carolina keeps own retirement pay separate from a nonspouse survivor annuity. + period: 2025 + input: + state_code: SC + age: 60 + is_surviving_spouse: false + is_tax_unit_head_or_spouse: true + military_retirement_pay: 20_000 + military_retirement_pay_survivors: 10_000 + output: + sc_military_deduction_indv: 20_000 + sc_military_deduction_survivors: 0 + sc_military_deduction: 20_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/subtractions/retirement/sc_retirement_deduction_survivors.yaml b/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/subtractions/retirement/sc_retirement_deduction_survivors.yaml index fd7fdae44c7..51226624c68 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/subtractions/retirement/sc_retirement_deduction_survivors.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/subtractions/retirement/sc_retirement_deduction_survivors.yaml @@ -3,6 +3,7 @@ input: pension_survivors: 1_000 military_retirement_pay_survivors: 4_000 + is_surviving_spouse: true age: 70 state_code: SC output: @@ -13,6 +14,7 @@ input: pension_survivors: 4_000 military_retirement_pay_survivors: 6_000 + is_surviving_spouse: true age: 64 state_code: SC output: @@ -23,20 +25,41 @@ input: pension_survivors: 2_000 military_retirement_pay_survivors: 4_000 + is_surviving_spouse: true age: 70 state_code: SC output: sc_retirement_deduction_survivors: 2_000 +# 12-6-1170(C)(1) excludes a military retiree's surviving spouse from the +# 2021 worksheet's reduction for military retirement, so the $6,000 military +# survivor deduction does not reduce the $3,000 maximum. - name: Case 4, in 2021, below age threshold period: 2021 input: pension_survivors: 4_000 military_retirement_pay_survivors: 6_000 + is_surviving_spouse: true age: 64 state_code: SC output: - sc_retirement_deduction_survivors: 0 + sc_military_deduction_survivors: 6_000 + # min(4_000, 3_000) + sc_retirement_deduction_survivors: 3_000 + +- name: Military survivor income that was not deducted does not reduce the general pension cap. + period: 2021 + input: + pension_survivors: 4_000 + military_retirement_pay_survivors: 6_000 + # Isolate the worksheet offset from the separately tested military + # eligibility calculation; line p-6 contains only an allowed deduction. + sc_military_deduction_survivors: 0 + is_surviving_spouse: true + age: 64 + state_code: SC + output: + sc_retirement_deduction_survivors: 3_000 # Only the head's and spouse's survivor benefits are in the filer's federal # AGI; a dependent reports their own on their own return. @@ -65,3 +88,84 @@ state_code: SC output: sc_retirement_deduction_survivors: [0, 0] + +- name: A 2021 filer who is not a surviving spouse gets no survivor deduction. + period: 2021 + input: + employment_income: 40_000 + pension_survivors: 4_000 + military_retirement_pay_survivors: 6_000 + is_surviving_spouse: false + age: 64 + state_code: SC + output: + sc_military_deduction_survivors: 0 + sc_retirement_deduction_survivors: 0 + +- name: The same 2021 filer as a surviving spouse keeps the survivor deduction. + period: 2021 + input: + employment_income: 40_000 + pension_survivors: 4_000 + military_retirement_pay_survivors: 6_000 + is_surviving_spouse: true + age: 64 + state_code: SC + output: + sc_military_deduction_survivors: 6_000 + # min(4_000, 3_000); no military reduction for a surviving spouse. + sc_retirement_deduction_survivors: 3_000 + +- name: A 2021 surviving spouse over 65 is not reduced by military survivor benefits. + period: 2021 + input: + pension_survivors: 12_000 + military_retirement_pay_survivors: 6_000 + is_surviving_spouse: true + age: 70 + state_code: SC + output: + sc_military_deduction_survivors: 6_000 + # min(12_000, 10_000). The 2021 worksheet would give + # min(12_000, 10_000 - 6_000) = 4_000. + sc_retirement_deduction_survivors: 10_000 + sc_retirement_deduction: 10_000 + sc_military_deduction: 6_000 + +- name: The survivor maximum follows the deceased spouse's age, below 65. + period: 2022 + input: + pension_survivors: 5_000 + is_surviving_spouse: true + age: 70 + deceased_spouse_age: 60 + state_code: SC + output: + # The deceased spouse would be 60, so the maximum is $3,000 though the + # survivor is 70. + sc_retirement_deduction_survivors: 3_000 + +- name: The survivor maximum follows the deceased spouse's age, 65 or older. + period: 2022 + input: + pension_survivors: 5_000 + is_surviving_spouse: true + age: 60 + deceased_spouse_age: 66 + state_code: SC + output: + # The deceased spouse would be 66, so the maximum is $10,000 though the + # survivor is 60. + sc_retirement_deduction_survivors: 5_000 + +- name: A survivor gets the $10,000 maximum when the deceased spouse would have reached 65. + period: 2022 + input: + pension_survivors: 12_000 + is_surviving_spouse: true + age: 60 + deceased_spouse_age: 65 + state_code: SC + output: + # min(12_000, 10_000) + sc_retirement_deduction_survivors: 10_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/va/tax/income/subtractions/va_military_benefit_subtraction.yaml b/policyengine_us/tests/policy/baseline/gov/states/va/tax/income/subtractions/va_military_benefit_subtraction.yaml index 5aee17fc803..e683997670a 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/va/tax/income/subtractions/va_military_benefit_subtraction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/va/tax/income/subtractions/va_military_benefit_subtraction.yaml @@ -249,3 +249,71 @@ output: # Under $40,000 limit, full amount subtracted va_military_benefit_subtraction: 35_000 + +- name: Virginia does not subtract a nonspouse survivor annuity on the survivor's own return. + period: 2024 + input: + state_code: VA + age: 20 + is_surviving_spouse: false + is_tax_unit_head_or_spouse: true + military_retirement_pay_survivors: 10_000 + output: + va_military_benefit_subtraction: 0 + +- name: Virginia subtracts an eligible surviving spouse annuity without an age minimum in 2024. + period: 2024 + input: + state_code: VA + age: 40 + is_surviving_spouse: true + is_tax_unit_head_or_spouse: true + military_retirement_pay_survivors: 10_000 + output: + va_military_benefit_subtraction: 10_000 + +- name: Virginia's surviving spouse subtraction retains the age minimum in 2023. + period: 2023 + input: + state_code: VA + age: 54 + is_surviving_spouse: true + is_tax_unit_head_or_spouse: true + military_retirement_pay_survivors: 25_000 + output: + va_military_benefit_subtraction: 0 + +- name: Virginia applies the 2023 cap to an age-eligible surviving spouse. + period: 2023 + input: + state_code: VA + age: 55 + is_surviving_spouse: true + is_tax_unit_head_or_spouse: true + military_retirement_pay_survivors: 25_000 + output: + va_military_benefit_subtraction: 20_000 + +- name: Virginia keeps own retirement pay separate from a nonspouse survivor annuity. + period: 2025 + input: + state_code: VA + age: 60 + is_surviving_spouse: false + is_tax_unit_head_or_spouse: true + military_retirement_pay: 30_000 + military_retirement_pay_survivors: 20_000 + output: + va_military_benefit_subtraction: 30_000 + +- name: Virginia applies one cap to own pay and eligible surviving spouse benefits. + period: 2025 + input: + state_code: VA + age: 60 + is_surviving_spouse: true + is_tax_unit_head_or_spouse: true + military_retirement_pay: 30_000 + military_retirement_pay_survivors: 20_000 + output: + va_military_benefit_subtraction: 40_000 diff --git a/policyengine_us/variables/gov/states/az/tax/income/subtractions/az_military_retirement_subtraction.py b/policyengine_us/variables/gov/states/az/tax/income/subtractions/az_military_retirement_subtraction.py index c4f78c6d0ee..6f4787d8cd5 100644 --- a/policyengine_us/variables/gov/states/az/tax/income/subtractions/az_military_retirement_subtraction.py +++ b/policyengine_us/variables/gov/states/az/tax/income/subtractions/az_military_retirement_subtraction.py @@ -10,6 +10,8 @@ class az_military_retirement_subtraction(Variable): "https://www.azleg.gov/ars/43/01022.htm", "https://azdor.gov/sites/default/files/2023-03/FORMS_INDIVIDUAL_2022_140BOOKLET.pdf#page=25", "https://azdor.gov/sites/default/files/2023-03/FORMS_INDIVIDUAL_2021_140BOOKLET.pdf#page=27", + "https://azdor.gov/sites/default/files/document/FORMS_INDIVIDUAL_2025_140i.pdf", + "https://azdor.gov/sites/default/files/2023-03/RULINGS_INDV_2011_itr11-3.pdf#page=2", ) definition_period = YEAR defined_for = StateCode.AZ @@ -18,7 +20,15 @@ def formula(tax_unit, period, parameters): p = parameters(period).gov.states.az.tax.income.subtractions.military_retirement person = tax_unit.members head_or_spouse = person("is_tax_unit_head_or_spouse", period) - military_retirement_income = person("military_retirement_income", period) + # In ITR 11-3, ADOR puts SBP benefits under 43-1022(2) + # (az_public_pension_exclusion). From 2023, ADOR moves a deceased + # veteran's surviving spouse's payments to line 29b in its Form 140 + # instructions. + retirement_pay = person("military_retirement_pay", period) + survivor_pay = person("military_retirement_pay_survivors", period) + surviving_spouse = person("is_surviving_spouse", period) + on_line_29b = surviving_spouse & p.surviving_spouse_eligible + military_retirement_income = retirement_pay + survivor_pay * on_line_29b eligible_military_pay = military_retirement_income * head_or_spouse capped_military_pay = min_(eligible_military_pay, p.max_amount) return tax_unit.sum(capped_military_pay) diff --git a/policyengine_us/variables/gov/states/az/tax/income/subtractions/az_public_pension_exclusion.py b/policyengine_us/variables/gov/states/az/tax/income/subtractions/az_public_pension_exclusion.py index 6391cec2579..d564b3f0c30 100644 --- a/policyengine_us/variables/gov/states/az/tax/income/subtractions/az_public_pension_exclusion.py +++ b/policyengine_us/variables/gov/states/az/tax/income/subtractions/az_public_pension_exclusion.py @@ -11,6 +11,7 @@ class az_public_pension_exclusion(Variable): "https://azdor.gov/sites/default/files/2023-03/FORMS_INDIVIDUAL_2022_140BOOKLET.pdf#page=18", "https://azdor.gov/sites/default/files/2023-03/FORMS_INDIVIDUAL_2021_140BOOKLET.pdf#page=23", "https://www.azleg.gov/ars/43/01022.htm", + "https://azdor.gov/sites/default/files/2023-03/RULINGS_INDV_2011_itr11-3.pdf#page=2", ) defined_for = StateCode.AZ @@ -18,7 +19,18 @@ def formula(tax_unit, period, parameters): p = parameters(period).gov.states.az.tax.income.subtractions.pension person = tax_unit.members head_or_spouse = person("is_tax_unit_head_or_spouse", period) - pension_income = person("taxable_public_pension_income", period) + # Arizona counts SBP annuities as benefits from a plan established by + # federal law (43-1022(2); ITR 11-3). From 2023, a surviving spouse + # reports these payments on line 29b instead. + p_military = parameters( + period + ).gov.states.az.tax.income.subtractions.military_retirement + survivor_pay = person("military_retirement_pay_survivors", period) + surviving_spouse = person("is_surviving_spouse", period) + on_line_29b = surviving_spouse & p_military.surviving_spouse_eligible + pension_income = person("taxable_public_pension_income", period) + where( + on_line_29b, 0, survivor_pay + ) eligible_pension_income = pension_income * head_or_spouse total_allowed_pension_exclusion = min_( p.public_pension_cap, eligible_pension_income diff --git a/policyengine_us/variables/gov/states/id/tax/income/subtractions/military_retirement/id_eligible_military_retirement_income.py b/policyengine_us/variables/gov/states/id/tax/income/subtractions/military_retirement/id_eligible_military_retirement_income.py new file mode 100644 index 00000000000..972031a7113 --- /dev/null +++ b/policyengine_us/variables/gov/states/id/tax/income/subtractions/military_retirement/id_eligible_military_retirement_income.py @@ -0,0 +1,26 @@ +from policyengine_us.model_api import * + + +class id_eligible_military_retirement_income(Variable): + value_type = float + entity = Person + label = "Idaho eligible military retirement and survivor income" + unit = USD + definition_period = YEAR + defined_for = StateCode.ID + reference = ( + "https://legislature.idaho.gov/statutesrules/idstat/title63/t63ch30/sect63-3022a/", + "https://legislature.idaho.gov/wp-content/uploads/sessioninfo/2025/legislation/H0479.pdf", + ) + + def formula(person, period, parameters): + # Section 63-3022A covers the retired member and the member's + # unremarried widow or widower, including under the 2025 amendment. + surviving_spouse = person("is_surviving_spouse", period) + # Marital units contain one unmarried person or a married couple, + # independently of other relatives and of how spouses file taxes. + married = person.marital_unit.nb_persons() > 1 + eligible_survivor = surviving_spouse & ~married + retirement_pay = person("military_retirement_pay", period) + survivor_pay = person("military_retirement_pay_survivors", period) + return retirement_pay + survivor_pay * eligible_survivor diff --git a/policyengine_us/variables/gov/states/id/tax/income/subtractions/military_retirement/id_military_retirement_deduction.py b/policyengine_us/variables/gov/states/id/tax/income/subtractions/military_retirement/id_military_retirement_deduction.py index 8d1c74736b6..1551022b6f8 100644 --- a/policyengine_us/variables/gov/states/id/tax/income/subtractions/military_retirement/id_military_retirement_deduction.py +++ b/policyengine_us/variables/gov/states/id/tax/income/subtractions/military_retirement/id_military_retirement_deduction.py @@ -17,7 +17,9 @@ def formula(tax_unit, period, parameters): p = parameters(period).gov.states.id.tax.income.subtractions.military_retirement person = tax_unit.members head_or_spouse = person("is_tax_unit_head_or_spouse", period) - military_retirement_income = person("military_retirement_income", period) + military_retirement_income = person( + "id_eligible_military_retirement_income", period + ) eligible_military_pay = military_retirement_income * head_or_spouse # Multiply by applies parameter (False when military retirement diff --git a/policyengine_us/variables/gov/states/in/tax/income/deductions/in_military_retirement_deduction.py b/policyengine_us/variables/gov/states/in/tax/income/deductions/in_military_retirement_deduction.py index c2f10bb42c1..e779f327503 100644 --- a/policyengine_us/variables/gov/states/in/tax/income/deductions/in_military_retirement_deduction.py +++ b/policyengine_us/variables/gov/states/in/tax/income/deductions/in_military_retirement_deduction.py @@ -10,6 +10,7 @@ class in_military_retirement_deduction(Variable): reference = ( "http://iga.in.gov/legislative/laws/2021/ic/titles/006#6-3-2-4", "https://forms.in.gov/Download.aspx?id=16915#page=20", + "https://www.in.gov/dor/files/ib27.pdf", ) defined_for = StateCode.IN @@ -23,7 +24,11 @@ def formula(tax_unit, period, parameters): # Dependents' income is not in federal AGI; they report it on their # own return. head_or_spouse = person("is_tax_unit_head_or_spouse", period) - military_retirement = person("military_retirement_income", period) + # IC 6-3-2-4(a)(2) covers the service member and surviving spouse. + retirement_pay = person("military_retirement_pay", period) + survivor_pay = person("military_retirement_pay_survivors", period) + surviving_spouse = person("is_surviving_spouse", period) + military_retirement = retirement_pay + survivor_pay * surviving_spouse excess = max_(0, military_retirement - p.base) person_deduction = min_(military_retirement, p.base) + p.excess_rate * excess return tax_unit.sum(person_deduction * head_or_spouse) diff --git a/policyengine_us/variables/gov/states/mi/tax/income/deductions/interest_dividends_capital_gains/mi_interest_dividends_capital_gains_deduction.py b/policyengine_us/variables/gov/states/mi/tax/income/deductions/interest_dividends_capital_gains/mi_interest_dividends_capital_gains_deduction.py index 298e32bf081..b7ad93fae0d 100644 --- a/policyengine_us/variables/gov/states/mi/tax/income/deductions/interest_dividends_capital_gains/mi_interest_dividends_capital_gains_deduction.py +++ b/policyengine_us/variables/gov/states/mi/tax/income/deductions/interest_dividends_capital_gains/mi_interest_dividends_capital_gains_deduction.py @@ -35,7 +35,7 @@ def formula(tax_unit, period, parameters): person, period, [ - "military_retirement_income", + "mi_military_retirement_subtraction", "taxable_pension_income", ], ) diff --git a/policyengine_us/variables/gov/states/mi/tax/income/deductions/retirement/expanded/mi_expanded_retirement_benefits_deduction.py b/policyengine_us/variables/gov/states/mi/tax/income/deductions/retirement/expanded/mi_expanded_retirement_benefits_deduction.py index ad383d90463..761fee0a84a 100644 --- a/policyengine_us/variables/gov/states/mi/tax/income/deductions/retirement/expanded/mi_expanded_retirement_benefits_deduction.py +++ b/policyengine_us/variables/gov/states/mi/tax/income/deductions/retirement/expanded/mi_expanded_retirement_benefits_deduction.py @@ -54,7 +54,7 @@ def formula(tax_unit, period, parameters): # Line 1 tier_one_cap = p.tier_one.amount[filing_status] head_or_spouse_military_retirement_pay = ( - person("military_retirement_income", period) * is_head_or_spouse + person("mi_military_retirement_subtraction", period) * is_head_or_spouse ) # Line 2 total_military_retirement_pay = tax_unit.sum( diff --git a/policyengine_us/variables/gov/states/mi/tax/income/deductions/retirement/tier_one/mi_retirement_benefits_deduction_tier_one_amount.py b/policyengine_us/variables/gov/states/mi/tax/income/deductions/retirement/tier_one/mi_retirement_benefits_deduction_tier_one_amount.py index e38dd186796..8e6aea6f8ee 100644 --- a/policyengine_us/variables/gov/states/mi/tax/income/deductions/retirement/tier_one/mi_retirement_benefits_deduction_tier_one_amount.py +++ b/policyengine_us/variables/gov/states/mi/tax/income/deductions/retirement/tier_one/mi_retirement_benefits_deduction_tier_one_amount.py @@ -32,7 +32,7 @@ def formula(tax_unit, period, parameters): # Line 10 is_head_or_spouse = person("is_tax_unit_head_or_spouse", period) military_retirement_pay = ( - person("military_retirement_income", period) * is_head_or_spouse + person("mi_military_retirement_subtraction", period) * is_head_or_spouse ) total_military_retirement_pay = tax_unit.sum(military_retirement_pay) diff --git a/policyengine_us/variables/gov/states/mi/tax/income/deductions/retirement/tier_three/ss_exempt/not_retired/mi_retirement_benefits_deduction_tier_three_ss_exempt_not_retired.py b/policyengine_us/variables/gov/states/mi/tax/income/deductions/retirement/tier_three/ss_exempt/not_retired/mi_retirement_benefits_deduction_tier_three_ss_exempt_not_retired.py index bdc3aa18b3a..bde1d0eff4b 100644 --- a/policyengine_us/variables/gov/states/mi/tax/income/deductions/retirement/tier_three/ss_exempt/not_retired/mi_retirement_benefits_deduction_tier_three_ss_exempt_not_retired.py +++ b/policyengine_us/variables/gov/states/mi/tax/income/deductions/retirement/tier_three/ss_exempt/not_retired/mi_retirement_benefits_deduction_tier_three_ss_exempt_not_retired.py @@ -63,7 +63,9 @@ def formula(tax_unit, period, parameters): # the tier one or tier three deduction amount # Worksheet 3.2 counts only military retirement income on the filer's Schedule 1, line 11, # which excludes a dependent's pay (not in the filer's AGI). - military_pay = person("military_retirement_income", period) * is_head_or_spouse + military_pay = ( + person("mi_military_retirement_subtraction", period) * is_head_or_spouse + ) military_retirement_pay_received = tax_unit.sum(military_pay) > 0 # Line 8 tier_one_amount = tax_unit( diff --git a/policyengine_us/variables/gov/states/mi/tax/income/deductions/retirement/tier_three/ss_exempt/retired/mi_retirement_benefits_deduction_tier_three_ss_exempt_retired.py b/policyengine_us/variables/gov/states/mi/tax/income/deductions/retirement/tier_three/ss_exempt/retired/mi_retirement_benefits_deduction_tier_three_ss_exempt_retired.py index 5a7ed0ced0f..87d5cac3b9f 100644 --- a/policyengine_us/variables/gov/states/mi/tax/income/deductions/retirement/tier_three/ss_exempt/retired/mi_retirement_benefits_deduction_tier_three_ss_exempt_retired.py +++ b/policyengine_us/variables/gov/states/mi/tax/income/deductions/retirement/tier_three/ss_exempt/retired/mi_retirement_benefits_deduction_tier_three_ss_exempt_retired.py @@ -78,7 +78,9 @@ def formula(tax_unit, period, parameters): # the tier one or tier three deduction amount # Worksheet 3.1 counts only military retirement income on the filer's Schedule 1, line 11, # which excludes a dependent's pay (not in the filer's AGI). - military_pay = person("military_retirement_income", period) * is_head_or_spouse + military_pay = ( + person("mi_military_retirement_subtraction", period) * is_head_or_spouse + ) military_retirement_pay_eligible = tax_unit.sum(military_pay) > 0 # Line 8 tier_one_amount = tax_unit( diff --git a/policyengine_us/variables/gov/states/mi/tax/income/deductions/standard/tier_three/mi_standard_deduction_tier_three.py b/policyengine_us/variables/gov/states/mi/tax/income/deductions/standard/tier_three/mi_standard_deduction_tier_three.py index 059d5935ad1..cf03976a763 100644 --- a/policyengine_us/variables/gov/states/mi/tax/income/deductions/standard/tier_three/mi_standard_deduction_tier_three.py +++ b/policyengine_us/variables/gov/states/mi/tax/income/deductions/standard/tier_three/mi_standard_deduction_tier_three.py @@ -31,7 +31,9 @@ def formula(tax_unit, period, parameters): person = tax_unit.members is_head_or_spouse = person("is_tax_unit_head_or_spouse", period) # Line 2: enter military retirement pay and survivor benefits - military_retirement_income = person("military_retirement_income", period) + military_retirement_income = person( + "mi_military_retirement_subtraction", period + ) # Line 3: enter military service income or taxable social security taxable_ss = person("taxable_social_security", period) military_service_income = person("military_service_income", period) diff --git a/policyengine_us/variables/gov/states/mi/tax/income/deductions/standard/tier_two/mi_standard_deduction_tier_two.py b/policyengine_us/variables/gov/states/mi/tax/income/deductions/standard/tier_two/mi_standard_deduction_tier_two.py index d97feba92af..b4d341417a8 100644 --- a/policyengine_us/variables/gov/states/mi/tax/income/deductions/standard/tier_two/mi_standard_deduction_tier_two.py +++ b/policyengine_us/variables/gov/states/mi/tax/income/deductions/standard/tier_two/mi_standard_deduction_tier_two.py @@ -33,7 +33,7 @@ def formula(tax_unit, period, parameters): person, period, [ - "military_retirement_income", + "mi_military_retirement_subtraction", "military_service_income", ], ) diff --git a/policyengine_us/variables/gov/states/mi/tax/income/mi_military_retirement_subtraction.py b/policyengine_us/variables/gov/states/mi/tax/income/mi_military_retirement_subtraction.py new file mode 100644 index 00000000000..079de197a01 --- /dev/null +++ b/policyengine_us/variables/gov/states/mi/tax/income/mi_military_retirement_subtraction.py @@ -0,0 +1,22 @@ +from policyengine_us.model_api import * + + +class mi_military_retirement_subtraction(Variable): + value_type = float + entity = Person + label = "Michigan military retirement subtraction" + unit = USD + definition_period = YEAR + defined_for = StateCode.MI + reference = ( + "https://www.legislature.mi.gov/documents/mcl/pdf/mcl-206-30.pdf", # (1)(e), (8)(c) + "https://www.michigan.gov/taxes/questions/iit/accordion/military/are-military-survivor-retirement-benefits-that-pass-to-a-beneficiary", + "https://www.michigan.gov/taxes/-/media/Project/Websites/taxes/Forms/IIT/TY2024/MI-1040-Instructions.pdf#page=13", + "https://www.michigan.gov/taxes/-/media/Project/Websites/taxes/Forms/IIT/TY2025/MI-1040-Book.pdf#page=12", + ) + + def formula(person, period, parameters): + own_pay = person("military_retirement_pay", period) + survivor_pay = person("military_retirement_pay_survivors", period) + surviving_spouse = person("is_surviving_spouse", period) + return own_pay + survivor_pay * surviving_spouse diff --git a/policyengine_us/variables/gov/states/nd/tax/income/subtractions/nd_military_retirement_subtraction.py b/policyengine_us/variables/gov/states/nd/tax/income/subtractions/nd_military_retirement_subtraction.py new file mode 100644 index 00000000000..1cd20c8e999 --- /dev/null +++ b/policyengine_us/variables/gov/states/nd/tax/income/subtractions/nd_military_retirement_subtraction.py @@ -0,0 +1,20 @@ +from policyengine_us.model_api import * + + +class nd_military_retirement_subtraction(Variable): + value_type = float + entity = Person + label = "North Dakota military retirement subtraction" + unit = USD + definition_period = YEAR + defined_for = StateCode.ND + reference = ( + "https://ndlegis.gov/cencode/t57c38.pdf#page=35", # 57-38-30.3(2)(r) + "https://www.tax.nd.gov/military-service-members", + ) + + def formula(person, period, parameters): + retirement_pay = person("military_retirement_pay", period) + survivor_pay = person("military_retirement_pay_survivors", period) + surviving_spouse = person("is_surviving_spouse", period) + return retirement_pay + survivor_pay * surviving_spouse diff --git a/policyengine_us/variables/gov/states/nm/tax/income/exemptions/nm_armed_forces_retirement_pay_exemption_person.py b/policyengine_us/variables/gov/states/nm/tax/income/exemptions/nm_armed_forces_retirement_pay_exemption_person.py index 96301c66b50..d34111f6f19 100644 --- a/policyengine_us/variables/gov/states/nm/tax/income/exemptions/nm_armed_forces_retirement_pay_exemption_person.py +++ b/policyengine_us/variables/gov/states/nm/tax/income/exemptions/nm_armed_forces_retirement_pay_exemption_person.py @@ -7,14 +7,23 @@ class nm_armed_forces_retirement_pay_exemption_person(Variable): label = "New Mexico armed forces retirement pay exemption per person " unit = USD definition_period = YEAR - reference = "https://law.justia.com/codes/new-mexico/2024/chapter-7/article-2/section-7-2-5-13/" + reference = ( + # NMSA 7-2-5.13; HB 252 sections 32 and 42 (PDF pages 102, 147) + # extend eligibility to surviving spouses for tax years beginning in 2025. + "https://www.nmlegis.gov/Sessions/24%20Regular/final/HB0252.PDF#page=102", + ) defined_for = StateCode.NM def formula(person, period, parameters): p = parameters( period ).gov.states.nm.tax.income.exemptions.armed_forces_retirement_pay - armed_forces_retirement_pay = person("military_retirement_income", period) + retirement_pay = person("military_retirement_pay", period) + survivor_pay = person("military_retirement_pay_survivors", period) + surviving_spouse = person("is_surviving_spouse", period) + armed_forces_retirement_pay = ( + retirement_pay + survivor_pay * surviving_spouse * p.survivors_eligible + ) capped_amount = min_(armed_forces_retirement_pay, p.cap) qualifies = person("is_tax_unit_head_or_spouse", period) diff --git a/policyengine_us/variables/gov/states/ri/tax/income/agi/subtractions/ri_military_retirement_subtraction.py b/policyengine_us/variables/gov/states/ri/tax/income/agi/subtractions/ri_military_retirement_subtraction.py new file mode 100644 index 00000000000..5dda1163ea7 --- /dev/null +++ b/policyengine_us/variables/gov/states/ri/tax/income/agi/subtractions/ri_military_retirement_subtraction.py @@ -0,0 +1,24 @@ +from policyengine_us.model_api import * + + +class ri_military_retirement_subtraction(Variable): + value_type = float + entity = Person + label = "Rhode Island military service pension subtraction" + unit = USD + definition_period = YEAR + defined_for = StateCode.RI + reference = ( + # R.I. Gen. Laws 44-30-12(c)(11); the Division of Taxation's + # interpretation extends the exemption to surviving spouses. + "https://webserver.rilegislature.gov/Statutes/TITLE44/44-30/44-II/44-30-12.htm", + "https://tax.ri.gov/sites/g/files/xkgbur541/files/2024-03/PUB_2024-01_retirement_income_guide.pdf#page=16", + "https://tax.ri.gov/sites/g/files/xkgbur541/files/2025-01/PUB_2025-01_Retirement_Income_Guide.pdf#page=17", + "https://tax.ri.gov/sites/g/files/xkgbur541/files/2026-02/PUB_2026-01_Retirement_Income_Guide.pdf#page=15", + ) + + def formula_2023_01_01(person, period, parameters): + retirement_pay = person("military_retirement_pay", period) + survivor_pay = person("military_retirement_pay_survivors", period) + surviving_spouse = person("is_surviving_spouse", period) + return retirement_pay + survivor_pay * surviving_spouse diff --git a/policyengine_us/variables/gov/states/ri/tax/income/agi/subtractions/taxable_retirement_income/ri_retirement_income_subtraction.py b/policyengine_us/variables/gov/states/ri/tax/income/agi/subtractions/taxable_retirement_income/ri_retirement_income_subtraction.py index b2c3eb06faa..c89f5ead2cf 100644 --- a/policyengine_us/variables/gov/states/ri/tax/income/agi/subtractions/taxable_retirement_income/ri_retirement_income_subtraction.py +++ b/policyengine_us/variables/gov/states/ri/tax/income/agi/subtractions/taxable_retirement_income/ri_retirement_income_subtraction.py @@ -18,6 +18,11 @@ def formula(tax_unit, period, parameters): ).gov.states.ri.tax.income.agi.subtractions.taxable_retirement_income person = tax_unit.members retirement_income = add(person, period, p.sources) + # Military pensions deducted under the separate military subtraction + # cannot also use the general pension exclusion. Other survivor + # annuities remain eligible for the general age and income rules. + military_subtraction = person("ri_military_retirement_subtraction", period) + retirement_income = max_(retirement_income - military_subtraction, 0) head_or_spouse = person("is_tax_unit_head_or_spouse", period) # Cap applies per person, not per tax unit capped_per_person = min_(retirement_income, p.cap) * head_or_spouse diff --git a/policyengine_us/variables/gov/states/sc/tax/income/subtractions/military_retirement/sc_military_deduction_survivors.py b/policyengine_us/variables/gov/states/sc/tax/income/subtractions/military_retirement/sc_military_deduction_survivors.py index debbbc42f47..b9e7754b2b5 100644 --- a/policyengine_us/variables/gov/states/sc/tax/income/subtractions/military_retirement/sc_military_deduction_survivors.py +++ b/policyengine_us/variables/gov/states/sc/tax/income/subtractions/military_retirement/sc_military_deduction_survivors.py @@ -9,6 +9,7 @@ class sc_military_deduction_survivors(Variable): unit = USD reference = ( "https://www.scstatehouse.gov/code/t12c006.php", # SECTION 12-6-1171(C) + "https://dor.sc.gov/sites/dor/files/policies/RR22-11.pdf", "https://dor.sc.gov/forms-site/Forms/IITPacket_2021.pdf#page=17", ) definition_period = YEAR @@ -17,4 +18,9 @@ def formula(person, period, parameters): # Only the head's and spouse's survivor benefits are in the filer's # federal AGI; a dependent reports their own on their own return. head_or_spouse = person("is_tax_unit_head_or_spouse", period) - return person("military_retirement_pay_survivors", period) * head_or_spouse + surviving_spouse = person("is_surviving_spouse", period) + return ( + person("military_retirement_pay_survivors", period) + * head_or_spouse + * surviving_spouse + ) diff --git a/policyengine_us/variables/gov/states/sc/tax/income/subtractions/retirement/sc_retirement_deduction_survivors.py b/policyengine_us/variables/gov/states/sc/tax/income/subtractions/retirement/sc_retirement_deduction_survivors.py index 2bd3dc6155d..3d84d79aba2 100644 --- a/policyengine_us/variables/gov/states/sc/tax/income/subtractions/retirement/sc_retirement_deduction_survivors.py +++ b/policyengine_us/variables/gov/states/sc/tax/income/subtractions/retirement/sc_retirement_deduction_survivors.py @@ -8,36 +8,35 @@ class sc_retirement_deduction_survivors(Variable): defined_for = StateCode.SC unit = USD reference = ( - "https://www.scstatehouse.gov/code/t12c006.php", # SECTION 12-6-1170(A)(1) + "https://www.scstatehouse.gov/code/t12c006.php", # SECTION 12-6-1170(A)(1), (A)(3), (C)(1) + "https://dor.sc.gov/sites/dor/files/policies/RR21-13.pdf#page=11", + "https://dor.sc.gov/sites/dor/files/policies/RR22-11.pdf#page=39", + "https://dor.sc.gov/sites/dor/files/forms/SC1040Instr_2021.pdf", "https://dor.sc.gov/forms-site/Forms/IITPacket_2021.pdf#page=17", ) definition_period = YEAR def formula(person, period, parameters): p = parameters(period).gov.states.sc.tax.income.subtractions.retirement - age = person("age", period) - # line 1 - max_deduction_allowed = p.cap.calc(age) - # line 2 - military_retirement_pay_survivors = person( - "military_retirement_pay_survivors", period - ) - # line 3 - retirement_deduction_available = max_( - max_deduction_allowed - military_retirement_pay_survivors, 0 - ) - # line 4: only the head's and spouse's survivor benefits are in the + # Line 1: a surviving spouse applies the deduction "in the same manner + # that the deduction applied to the deceased spouse" (12-6-1170(A)(3)), + # so the survivor takes the maximum for the age the deceased spouse + # would have reached. + deceased_spouse_age = person("deceased_spouse_age", period) + max_deduction_allowed = p.cap.calc(deceased_spouse_age) + # Source conflict: the 2021 SC1040 worksheet (line p-3, line 2) + # reduces this maximum by the military retirement deduction, but + # 12-6-1170(C)(1) and RR22-11 exclude a military retiree's surviving + # spouse from that reduction. Following the statute, no reduction + # applies in any year (candidate + # us-sc:2021-survivor-offset-surviving-spouse). + # Line 4: only the head's and spouse's survivor benefits are in the # filer's federal AGI; a dependent reports their own on their own - # return. + # return. The deduction passes only to the original owner's surviving + # spouse, not to other beneficiaries of an inherited account. head_or_spouse = person("is_tax_unit_head_or_spouse", period) + surviving_spouse = person("is_surviving_spouse", period) retirement_income_survivors = ( - person("pension_survivors", period) * head_or_spouse - ) - # In 2021, South Carolina subtracts the survivors retirement deduction from the military retirement deduction - # In 2022, it does not - cap = ( - retirement_deduction_available - if p.subtract_military - else max_deduction_allowed + person("pension_survivors", period) * head_or_spouse * surviving_spouse ) - return min_(retirement_income_survivors, cap) + return min_(retirement_income_survivors, max_deduction_allowed) diff --git a/policyengine_us/variables/gov/states/va/tax/income/subtractions/va_military_benefit_subtraction_person.py b/policyengine_us/variables/gov/states/va/tax/income/subtractions/va_military_benefit_subtraction_person.py index e969fff99c7..1d75ab16025 100644 --- a/policyengine_us/variables/gov/states/va/tax/income/subtractions/va_military_benefit_subtraction_person.py +++ b/policyengine_us/variables/gov/states/va/tax/income/subtractions/va_military_benefit_subtraction_person.py @@ -8,14 +8,18 @@ class va_military_benefit_subtraction_person(Variable): defined_for = StateCode.VA unit = USD definition_period = YEAR - reference = "https://law.lis.virginia.gov/vacodefull/title58.1/chapter3/article2/" + reference = ( + "https://law.lis.virginia.gov/vacode/title58.1/chapter3/section58.1-322.02/", + "https://www.tax.virginia.gov/military-benefits-faq", + ) def formula(person, period, parameters): p = parameters(period).gov.states.va.tax.income.subtractions.military_benefit - subtractable = min_( - person("military_retirement_income", period), - p.amount, - ) + retirement_pay = person("military_retirement_pay", period) + survivor_pay = person("military_retirement_pay_survivors", period) + surviving_spouse = person("is_surviving_spouse", period) + military_benefits = retirement_pay + survivor_pay * surviving_spouse + subtractable = min_(military_benefits, p.amount) eligible = person("is_tax_unit_head_or_spouse", period) if p.availability: diff --git a/policyengine_us/variables/household/demographic/person/deceased_spouse_age.py b/policyengine_us/variables/household/demographic/person/deceased_spouse_age.py new file mode 100644 index 00000000000..d735be35ea7 --- /dev/null +++ b/policyengine_us/variables/household/demographic/person/deceased_spouse_age.py @@ -0,0 +1,17 @@ +from policyengine_us.model_api import * + + +class deceased_spouse_age(Variable): + value_type = float + entity = Person + label = "Age the deceased spouse would have reached" + documentation = ( + "For a surviving spouse, the age the deceased spouse would have " + "reached by the end of the year had they lived. Without an input, " + "this takes the person's own age." + ) + unit = "year" + definition_period = YEAR + + def formula(person, period, parameters): + return person("age", period) diff --git a/source_conflicts/candidates.yaml b/source_conflicts/candidates.yaml index c27bd7db7a0..6e21717e07d 100644 --- a/source_conflicts/candidates.yaml +++ b/source_conflicts/candidates.yaml @@ -883,6 +883,49 @@ candidates: prs: [] state: unreviewed note: Verify the legal basis for the amended-return remedy and treatment of exactly $3,000. +- id: us-az:military-survivor-benefit-recipient + program: income-tax + years: + from: 2021 + type: tax + question: Does Arizona subtract Survivor Benefit Plan (SBP) payments under 43-1022(2)'s $2,500 subtraction for + federal plans or in full on line 29b under 43-1022(26), and does the answer differ for a deceased veteran's + surviving spouse and for other recipients? + sources: + - kind: statute + citation: A.R.S. 43-1022(2)(a) and (26) + url: https://www.azleg.gov/ars/43/01022.htm + - kind: guidance + citation: Arizona Individual Income Tax Ruling ITR 11-3 + url: https://azdor.gov/sites/default/files/2023-03/RULINGS_INDV_2011_itr11-3.pdf#page=2 + - kind: instructions + citation: 2025 Arizona Form 140 instructions, line 29b + url: https://azdor.gov/sites/default/files/document/FORMS_INDIVIDUAL_2025_140i.pdf + readings: + statute: Paragraph (2)(a) allows up to $2,500 of benefits from "any other retirement system or plan established + by federal law", except "retired or retainer pay of the uniformed services of the United States that qualifies + for a subtraction under paragraph 26". Paragraph (26) covers "annuities and pensions received as retired or + retainer pay" and names no survivors. + other: ITR 11-3 says "Arizona allows a subtraction of up to $2,500 for uniformed services retired pay and SBP + benefits." From 2023, the Form 140 instructions put the payments of "the surviving spouse of a deceased military + veteran" on line 29b, which has no cap from 2021. + example: A 2021 surviving spouse with $6,000 of SBP payments and no other income subtracts $2,500 under (2) and + $0 on line 29b. In 2023 the same filer subtracts $6,000 on line 29b and $0 under (2). A non-spouse recipient + subtracts $2,500 under (2) in both years. + pe_us: + follows: gap-fill + code: + - policyengine_us/variables/gov/states/az/tax/income/subtractions/az_military_retirement_subtraction.py + - policyengine_us/variables/gov/states/az/tax/income/subtractions/az_public_pension_exclusion.py + - policyengine_us/parameters/gov/states/az/tax/income/subtractions/military_retirement/surviving_spouse_eligible.yaml + prs: + - https://github.com/PolicyEngine/policyengine-us/pull/10398 + state: ruled + note: Neither paragraph says whether a survivor's SBP payments count as annuities "received as retired or retainer + pay", and (2)(a)'s exception turns on that question, so PolicyEngine follows ADOR's reading for each year. A + surviving spouse's payments stay under (2) through 2022, as ITR 11-3 provides, and move to line 29b from 2023, as + the Form 140 instructions provide. ADOR has moved no other recipient's payments, so those stay under (2) in every + year. - id: us-ca:capi-payment-standard-offset program: capi years: @@ -8334,6 +8377,33 @@ candidates: use the same wording. The RI-22-0024 state plan groups, RIte Start and FCEP have no conflicting text, and CHIP (RI-22-0025 CS27) ends the period "on the last day of the 12th month consistent with paragraphs (5) and (16) of section 1902(e)". R.I. Gen. Laws 42-12.3-3(g) does not say when the 12-month period begins. +- id: us-ri:pension-modification-agi-threshold-strict + program: income-tax + years: + from: 2021 + type: tax + question: Does a filer whose federal AGI equals the threshold qualify for the pension and annuity modification? + sources: + - kind: statute + citation: R.I. Gen. Laws § 44-30-12(c), income test for the taxable retirement income modification + url: https://webserver.rilegislature.gov/Statutes/TITLE44/44-30/44-II/44-30-12.htm + - kind: worksheet + citation: RI-1040 instructions, line 1t worksheet + url: https://tax.ri.gov/sites/g/files/xkgbur541/files/2025-12/2025%201040R%20Instructions%20122025.pdf#page=10 + readings: + statute: Federal AGI must be less than the threshold; AGI equal to it does not qualify. + other: The worksheet allows AGI less than or equal to the threshold. + example: A 2025 single filer born in 1950 with AGI of exactly $107,000 gets no modification under the statute and + qualifies under the worksheet. + pe_us: + follows: statute + code: + - policyengine_us/variables/gov/states/ri/tax/income/agi/subtractions/taxable_retirement_income/ri_retirement_income_subtraction_eligible.py + prs: + - https://github.com/PolicyEngine/policyengine-us/pull/10398 + state: ruled + note: The code already uses a strict comparison and the eligibility tests pin AGI equal to the threshold as + ineligible. Verify the statute's and the worksheet's wording before registering. - id: us-ri:ssp-category-d-without-federal-ssi program: ssi-state-supplement years: @@ -8469,6 +8539,38 @@ candidates: note: The model computes the couple jointly at $90, which the couple.yaml comment derives from the per-individual allowance in §40-6-27(a)(3); ri_ssp.yaml Case 18 pins [45, 45] for a couple with no income. SSA administered Category E only through December 31, 2010. +- id: us-sc:2021-survivor-offset-surviving-spouse + program: income-tax + years: + from: 2021 + to: 2021 + type: tax + question: Is a surviving spouse's 2021 retirement deduction maximum reduced by the military retirement deduction + attributable to the deceased spouse? + sources: + - kind: statute + citation: S.C. Code § 12-6-1170(C)(1) + url: https://www.scstatehouse.gov/code/t12c006.php + - kind: guidance + citation: SCDOR Revenue Ruling 22-11 + url: https://dor.sc.gov/sites/dor/files/policies/RR22-11.pdf#page=39 + - kind: worksheet + citation: 2021 SC1040 retirement deduction worksheet, line p-3, line 2 + url: https://dor.sc.gov/forms-site/Forms/IITPacket_2021.pdf#page=17 + readings: + statute: The reduction for military retirement does not apply to a military retiree's surviving spouse. + other: The 2021 worksheet subtracts the surviving spouse's military retirement deduction from the survivor + retirement deduction maximum. + example: A 2021 surviving spouse whose deceased spouse would be 64, with $4,000 of survivor pension and $6,000 of + military survivor benefits, deducts $3,000 under the statute and $0 under the worksheet. + pe_us: + follows: statute + code: + - policyengine_us/variables/gov/states/sc/tax/income/subtractions/retirement/sc_retirement_deduction_survivors.py + prs: + - https://github.com/PolicyEngine/policyengine-us/pull/10398 + state: ruled + note: RR22-11 agrees with the statute. PR 10398 removes the 2021 reduction and the parameter that toggled it. - id: us-sd:liheap-2025-published-income-limit program: liheap years: