diff --git a/changelog_entry.yaml b/changelog_entry.yaml index e69de29bb2d..87b1e131ebe 100644 --- a/changelog_entry.yaml +++ b/changelog_entry.yaml @@ -0,0 +1,4 @@ +- bump: patch + changes: + fixed: + - Missouri Social Security deduction now correctly requires age 62+ (SSDI has no age limit per MO Form MO-A Section C). diff --git a/policyengine_us/parameters/gov/states/mo/tax/income/deductions/social_security_and_public_pension/age_threshold.yaml b/policyengine_us/parameters/gov/states/mo/tax/income/deductions/social_security_and_public_pension/age_threshold.yaml new file mode 100644 index 00000000000..72ca25dc97c --- /dev/null +++ b/policyengine_us/parameters/gov/states/mo/tax/income/deductions/social_security_and_public_pension/age_threshold.yaml @@ -0,0 +1,13 @@ +description: Missouri requires taxpayers to be at least this age by December 31 to claim the social security deduction; social security disability has no age limit. +metadata: + unit: year + period: year + label: Missouri social security deduction age threshold + reference: + - title: 2024 Form MO-A - Individual Income Tax Adjustments, Part 3 - Section C + href: https://dor.mo.gov/forms/MO-A_2024.pdf#page=3 + - title: MO Statute Chapter 143.124 + href: https://revisor.mo.gov/main/OneSection.aspx?section=143.124 + +values: + 2021-01-01: 62 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/deductions/mo_pension_and_ss_or_ssd_deduction/integration_tests/mo_pension_and_ss_or_ssd.yaml b/policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/deductions/mo_pension_and_ss_or_ssd_deduction/integration_tests/mo_pension_and_ss_or_ssd.yaml index 9203feebea7..67686256a6c 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/deductions/mo_pension_and_ss_or_ssd_deduction/integration_tests/mo_pension_and_ss_or_ssd.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/deductions/mo_pension_and_ss_or_ssd_deduction/integration_tests/mo_pension_and_ss_or_ssd.yaml @@ -322,7 +322,8 @@ employment_income: 18_010 taxable_interest_income: 5_505 taxable_private_pension_income: 500 - social_security: 9_000 + # Use SSDI since people are under 62 - SSDI has no age limit for MO deduction + social_security_disability: 9_000 ssi: 0 # not in TAXSIM35 ma_state_supplement: 0 # not in TAXSIM35 wic: 0 # not in TAXSIM35 @@ -332,7 +333,8 @@ employment_income: 57_010 taxable_interest_income: 5_505 taxable_private_pension_income: 500 - social_security: 9_000 + # Use SSDI since people are under 62 - SSDI has no age limit for MO deduction + social_security_disability: 9_000 ssi: 0 # not in TAXSIM35 ma_state_supplement: 0 # not in TAXSIM35 wic: 0 # not in TAXSIM35 @@ -418,3 +420,34 @@ output: # expected results from hand calculations using 2021 MO Form 1040 mo_pension_and_ss_or_ssd_deduction_section_b: [1_485, 1_485, 0] mo_pension_and_ss_or_ssd_deduction_section_c: [750, 750, 0] + +# Test from GitHub issue #7293: MO elderly pension and SS exemption +- name: MO elderly pension and SS exemption for age 75 (GitHub issue 7293) + period: 2024 + absolute_error_margin: 10 + input: + people: + person1: + age: 75 + employment_income: 0 + taxable_interest_income: 131 + taxable_private_pension_income: 25_717 + social_security: 24_087 + is_tax_unit_head: true + tax_units: + tax_unit: + members: [person1] + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_fips: 29 # Missouri + output: + # MO taxable income = Federal AGI - standard deduction - fed tax deduction - pension/SS deduction + # Federal AGI = $33,656, Standard deduction = $16,550 (65+ single) + # Pension/SS deduction = Section B ($5,152) + Section C ($7,808) = $12,960 + # Federal income tax deduction ~$455 + mo_taxable_income: 3691 + mo_income_tax: 54 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/deductions/mo_pension_and_ss_or_ssd_deduction/mo_pension_and_ss_or_ssd_deduction_section_c.yaml b/policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/deductions/mo_pension_and_ss_or_ssd_deduction/mo_pension_and_ss_or_ssd_deduction_section_c.yaml index c82a0c11ac2..33648e2e0f4 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/deductions/mo_pension_and_ss_or_ssd_deduction/mo_pension_and_ss_or_ssd_deduction_section_c.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/deductions/mo_pension_and_ss_or_ssd_deduction/mo_pension_and_ss_or_ssd_deduction_section_c.yaml @@ -1,3 +1,6 @@ +# Tests for Missouri Social Security/SSDI Deduction (Section C of Form MO-A) +# Per MO-A 2024: SS deduction requires age 62+ by Dec 31; SSDI has no age limit. + - name: No Values period: 2021 absolute_error_margin: 0 @@ -6,10 +9,11 @@ taxable_social_security: 0 filing_status: SINGLE state_code: MO + age: 65 output: mo_pension_and_ss_or_ssd_deduction_section_c: 0.0 -- name: AGI below threshold, private pension income, taxable social security income, single +- name: AGI below threshold, taxable social security income, single, age 65 period: 2021 absolute_error_margin: 0 input: @@ -17,10 +21,11 @@ taxable_social_security: 5_000 filing_status: SINGLE state_code: MO + age: 65 output: mo_pension_and_ss_or_ssd_deduction_section_c: 5_000 -- name: AGI below threshold, private pension income, without taxable social security income, single +- name: AGI below threshold, without taxable social security income, single, age 65 period: 2021 absolute_error_margin: 30_000 input: @@ -28,10 +33,11 @@ taxable_social_security: 0 filing_status: SINGLE state_code: MO + age: 65 output: mo_pension_and_ss_or_ssd_deduction_section_c: 0.0 -- name: AGI above threshold, private pension income, taxable social security income, single +- name: AGI above threshold, taxable social security income, single, age 65 period: 2021 absolute_error_margin: 0 input: @@ -39,10 +45,11 @@ taxable_social_security: 15_000 filing_status: SINGLE state_code: MO + age: 65 output: mo_pension_and_ss_or_ssd_deduction_section_c: 5_000 -- name: AGI above threshold, private pension income, without taxable social security income, single +- name: AGI above threshold, without taxable social security income, single, age 65 period: 2021 absolute_error_margin: 0 input: @@ -50,10 +57,11 @@ taxable_social_security: 0 filing_status: SINGLE state_code: MO + age: 65 output: mo_pension_and_ss_or_ssd_deduction_section_c: 0.0 -- name: AGI above threshold, private pension income, taxable social security income, joint +- name: AGI above threshold, taxable social security income, joint, age 65 period: 2021 absolute_error_margin: 0 input: @@ -61,10 +69,11 @@ taxable_social_security: 10_000 filing_status: JOINT state_code: MO + age: 65 output: mo_pension_and_ss_or_ssd_deduction_section_c: 5_000 -- name: AGI above threshold, private pension income, without taxable social security income, joint +- name: AGI above threshold, without taxable social security income, joint, age 65 period: 2021 absolute_error_margin: 0 input: @@ -72,82 +81,74 @@ taxable_social_security: 0 filing_status: JOINT state_code: MO + age: 65 output: mo_pension_and_ss_or_ssd_deduction_section_c: 0.0 -- name: AGI below threshold, private pension income, taxable social security income, single - period: 2021 +- name: 2024 No Income Threshold Applies, age 65 + period: 2024 absolute_error_margin: 0 input: - mo_adjusted_gross_income: 30_000 - taxable_social_security: 5_000 - filing_status: SINGLE - state_code: MO - output: - mo_pension_and_ss_or_ssd_deduction_section_c: 5_000 - -- name: AGI below threshold, private pension income, without taxable social security income, single - period: 2021 - absolute_error_margin: 30_000 - input: - mo_adjusted_gross_income: 30_000 - taxable_social_security: 0 - filing_status: SINGLE + mo_adjusted_gross_income: 105_000 + taxable_social_security: 10_000 + filing_status: JOINT state_code: MO + age: 65 output: - mo_pension_and_ss_or_ssd_deduction_section_c: 0.0 + mo_pension_and_ss_or_ssd_deduction_section_c: 10_000 -- name: AGI above threshold, private pension income, taxable social security income, single - period: 2021 - absolute_error_margin: 0 - input: - mo_adjusted_gross_income: 95_000 - taxable_social_security: 15_000 - filing_status: SINGLE - state_code: MO - output: - mo_pension_and_ss_or_ssd_deduction_section_c: 5_000 +# Age requirement tests - SS requires age 62+, SSDI has no age limit -- name: AGI above threshold, private pension income, without taxable social security income, single - period: 2021 +- name: Age 61 with regular SS - no deduction (under age threshold) + period: 2024 absolute_error_margin: 0 input: - mo_adjusted_gross_income: 95_000 - taxable_social_security: 0 + mo_adjusted_gross_income: 50_000 + social_security_retirement: 20_000 + taxable_social_security: 10_000 filing_status: SINGLE state_code: MO + age: 61 output: mo_pension_and_ss_or_ssd_deduction_section_c: 0.0 -- name: AGI above threshold, private pension income, taxable social security income, joint - period: 2021 +- name: Age 62 with regular SS - full deduction (meets age threshold) + period: 2024 absolute_error_margin: 0 input: - mo_adjusted_gross_income: 105_000 - taxable_social_security: 10_000 - filing_status: JOINT + mo_adjusted_gross_income: 50_000 + social_security_retirement: 20_000 + taxable_social_security: 10_000 + filing_status: SINGLE state_code: MO + age: 62 output: - mo_pension_and_ss_or_ssd_deduction_section_c: 5_000 + mo_pension_and_ss_or_ssd_deduction_section_c: 10_000 -- name: AGI above threshold, private pension income, without taxable social security income, joint - period: 2021 +- name: Age 61 with SSDI - full deduction (no age limit for disability) + period: 2024 absolute_error_margin: 0 input: - mo_adjusted_gross_income: 105_000 - taxable_social_security: 0 - filing_status: JOINT + mo_adjusted_gross_income: 50_000 + social_security_disability: 20_000 + taxable_social_security: 10_000 + filing_status: SINGLE state_code: MO + age: 61 output: - mo_pension_and_ss_or_ssd_deduction_section_c: 0.0 + mo_pension_and_ss_or_ssd_deduction_section_c: 10_000 -- name: 2024 No Income Threshold Applies +- name: Age 55 with mixed SS and SSDI - only SSDI portion deductible period: 2024 - absolute_error_margin: 0 + absolute_error_margin: 0.1 input: - mo_adjusted_gross_income: 105_000 - taxable_social_security: 10_000 - filing_status: JOINT + mo_adjusted_gross_income: 50_000 + social_security_retirement: 10_000 + social_security_disability: 10_000 + taxable_social_security: 10_000 + filing_status: SINGLE state_code: MO + age: 55 output: - mo_pension_and_ss_or_ssd_deduction_section_c: 10_000 + # 50% of SS is SSDI, so 50% of taxable SS is deductible + mo_pension_and_ss_or_ssd_deduction_section_c: 5_000 diff --git a/policyengine_us/variables/gov/states/mo/tax/income/deductions/mo_pension_and_ss_or_ssd_deduction_section_c.py b/policyengine_us/variables/gov/states/mo/tax/income/deductions/mo_pension_and_ss_or_ssd_deduction_section_c.py index a21eefb085f..4b06844e6b1 100644 --- a/policyengine_us/variables/gov/states/mo/tax/income/deductions/mo_pension_and_ss_or_ssd_deduction_section_c.py +++ b/policyengine_us/variables/gov/states/mo/tax/income/deductions/mo_pension_and_ss_or_ssd_deduction_section_c.py @@ -4,12 +4,11 @@ class mo_pension_and_ss_or_ssd_deduction_section_c(Variable): value_type = float entity = Person - label = "Missouri Pension and Social Security or SS Disability Deduction" + label = "Missouri Social Security or SS Disability Deduction (Section C)" unit = USD definition_period = YEAR reference = ( - "https://dor.mo.gov/forms/MO-A_2021.pdf#page=3", - "https://dor.mo.gov/forms/MO-1040%20Fillable%20Calculating_2021.pdf#page=2", + "https://dor.mo.gov/forms/MO-A_2024.pdf#page=3", "https://revisor.mo.gov/main/OneSection.aspx?section=143.124", ) defined_for = StateCode.MO @@ -18,22 +17,56 @@ def formula(person, period, parameters): p = parameters( period ).gov.states.mo.tax.income.deductions.social_security_and_public_pension - ind_taxable_ben = person("taxable_social_security", period) + + # Per MO-A 2024 Section C: SS deduction requires age 62+ by Dec 31; + # SSDI has no age limit. + age = person("age", period) + age_threshold = p.age_threshold + meets_age_requirement = age >= age_threshold + + # Get taxable social security (allocated from tax unit calculation) + ind_taxable_ss = person("taxable_social_security", period) + + # Calculate the SSDI portion of taxable SS for those under age threshold. + # SSDI has no age limit, so we need to determine what portion of + # taxable SS is attributable to SSDI. + ind_ssdi = person("social_security_disability", period) + ind_total_ss = person("social_security", period) + + # Compute SSDI fraction of total SS (avoid divide by zero) + ssdi_fraction = np.zeros_like(ind_total_ss) + mask = ind_total_ss > 0 + ssdi_fraction[mask] = ind_ssdi[mask] / ind_total_ss[mask] + + # Taxable SSDI is the SSDI fraction of taxable SS + ind_taxable_ssdi = ind_taxable_ss * ssdi_fraction + + # For age 62+: full taxable SS is deductible + # For under 62: only taxable SSDI portion is deductible + eligible_taxable_benefits = where( + meets_age_requirement, ind_taxable_ss, ind_taxable_ssdi + ) + if p.income_threshold_applies: + # Pre-2024: Apply income-based phase-out tax_unit = person.tax_unit ind_mo_agi = person("mo_adjusted_gross_income", period) unit_mo_agi = tax_unit.sum(ind_mo_agi) filing_status = tax_unit("filing_status", period) unit_allowance = p.mo_ss_or_ssd_deduction_allowance[filing_status] unit_agi_over_allowance = max_(0, unit_mo_agi - unit_allowance) - unit_taxable_ben = tax_unit.sum(ind_taxable_ben) + unit_eligible_ben = tax_unit.sum(eligible_taxable_benefits) unit_deduction = max_( - 0, unit_taxable_ben - unit_agi_over_allowance + 0, unit_eligible_ben - unit_agi_over_allowance + ) + # Compute individual's share of tax unit deduction + ind_frac = np.zeros_like(eligible_taxable_benefits) + ben_mask = eligible_taxable_benefits > 0 + ind_frac[ben_mask] = ( + eligible_taxable_benefits[ben_mask] + / unit_eligible_ben[ben_mask] ) - # Compute the individual's share of the tax unit's taxable benefits. - # Use a mask rather than where to avoid a divide-by-zero warning. Default to zero. - ind_frac = np.zeros_like(ind_taxable_ben) - mask = ind_taxable_ben > 0 - ind_frac[mask] = ind_taxable_ben[mask] / unit_taxable_ben[mask] return ind_frac * unit_deduction - return ind_taxable_ben + + # 2024+: No income threshold, full eligible benefits are deductible + return eligible_taxable_benefits