diff --git a/changelog.d/529-batch-2.added.md b/changelog.d/529-batch-2.added.md new file mode 100644 index 00000000000..b8a0411a1c2 --- /dev/null +++ b/changelog.d/529-batch-2.added.md @@ -0,0 +1 @@ +Add 529 plan contribution deductions for MD, MA, MI, MS, MO, NE, NJ, and ND. diff --git a/policyengine_us/parameters/gov/states/ma/tax/income/deductions/plan_529_contributions/cap.yaml b/policyengine_us/parameters/gov/states/ma/tax/income/deductions/plan_529_contributions/cap.yaml new file mode 100644 index 00000000000..12d39f60bb2 --- /dev/null +++ b/policyengine_us/parameters/gov/states/ma/tax/income/deductions/plan_529_contributions/cap.yaml @@ -0,0 +1,23 @@ +description: Massachusetts caps the deduction for 529 education savings plan contributions at this amount. +metadata: + label: Massachusetts 529 plan contribution deduction cap + period: year + unit: currency-USD + breakdown: + - filing_status + reference: + - title: Massachusetts General Laws Chapter 62 § 3(B)(a)(14) + href: https://malegislature.gov/Laws/GeneralLaws/PartI/TitleIX/Chapter62/Section3 + - title: Massachusetts DOR 529 Deduction Information + href: https://www.mass.gov/info-details/529-plan-deduction + +JOINT: + 2021-01-01: 2_000 +SURVIVING_SPOUSE: + 2021-01-01: 2_000 +SINGLE: + 2021-01-01: 1_000 +HEAD_OF_HOUSEHOLD: + 2021-01-01: 1_000 +SEPARATE: + 2021-01-01: 1_000 diff --git a/policyengine_us/parameters/gov/states/md/tax/income/agi/subtractions/plan_529_contributions/cap.yaml b/policyengine_us/parameters/gov/states/md/tax/income/agi/subtractions/plan_529_contributions/cap.yaml new file mode 100644 index 00000000000..62f15ef5105 --- /dev/null +++ b/policyengine_us/parameters/gov/states/md/tax/income/agi/subtractions/plan_529_contributions/cap.yaml @@ -0,0 +1,23 @@ +description: Maryland caps the subtraction for 529 education savings plan contributions at this amount per beneficiary. +metadata: + label: Maryland 529 plan contribution subtraction cap + period: year + unit: currency-USD + breakdown: + - filing_status + reference: + - title: Maryland Tax-General Article § 10-208(p) + href: https://mgaleg.maryland.gov/mgawebsite/Laws/StatuteText?article=gtg§ion=10-208&enession=2024RS + - title: Maryland 2025 Resident Tax Forms and Instructions - Subtractions + href: https://www.marylandcomptroller.gov/content/dam/mdcomp/tax/instructions/2025/resident-booklet.pdf#page=15 + +JOINT: + 2021-01-01: 5_000 +SURVIVING_SPOUSE: + 2021-01-01: 5_000 +SINGLE: + 2021-01-01: 2_500 +HEAD_OF_HOUSEHOLD: + 2021-01-01: 2_500 +SEPARATE: + 2021-01-01: 2_500 diff --git a/policyengine_us/parameters/gov/states/md/tax/income/agi/subtractions/sources.yaml b/policyengine_us/parameters/gov/states/md/tax/income/agi/subtractions/sources.yaml index 58a0addde54..2d10338760e 100644 --- a/policyengine_us/parameters/gov/states/md/tax/income/agi/subtractions/sources.yaml +++ b/policyengine_us/parameters/gov/states/md/tax/income/agi/subtractions/sources.yaml @@ -5,12 +5,14 @@ values: - md_pension_subtraction - md_socsec_subtraction - md_two_income_subtraction + - md_529_deduction 2022-01-01: - md_dependent_care_subtraction - md_pension_subtraction - md_socsec_subtraction - md_two_income_subtraction - md_hundred_year_subtraction + - md_529_deduction metadata: reference: diff --git a/policyengine_us/parameters/gov/states/mi/tax/income/deductions/plan_529_contributions/cap.yaml b/policyengine_us/parameters/gov/states/mi/tax/income/deductions/plan_529_contributions/cap.yaml new file mode 100644 index 00000000000..2543c4db623 --- /dev/null +++ b/policyengine_us/parameters/gov/states/mi/tax/income/deductions/plan_529_contributions/cap.yaml @@ -0,0 +1,23 @@ +description: Michigan caps the subtraction for 529 education savings plan contributions at this amount. +metadata: + label: Michigan 529 plan contribution subtraction cap + period: year + unit: currency-USD + breakdown: + - filing_status + reference: + - title: Michigan Compiled Laws § 206.30(1)(bb) + href: http://legislature.mi.gov/doc.aspx?mcl-206-30 + - title: Michigan 2024 Individual Income Tax Instructions + href: https://www.michigan.gov/taxes/-/media/Project/Websites/taxes/Forms/IIT/TY2024/MI-1040-Instructions.pdf#page=13 + +JOINT: + 2021-01-01: 10_000 +SURVIVING_SPOUSE: + 2021-01-01: 10_000 +SINGLE: + 2021-01-01: 5_000 +HEAD_OF_HOUSEHOLD: + 2021-01-01: 5_000 +SEPARATE: + 2021-01-01: 5_000 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 a253e4bcbd7..cd05a3643ce 100644 --- a/policyengine_us/parameters/gov/states/mi/tax/income/subtractions.yaml +++ b/policyengine_us/parameters/gov/states/mi/tax/income/subtractions.yaml @@ -9,6 +9,7 @@ values: - mi_standard_deduction # Line 24 & 25 - mi_pension_benefit # Line 26 - mi_interest_dividends_capital_gains_deduction # Line 27 + - mi_529_deduction metadata: unit: list label: Michigan taxable income subtraction sources diff --git a/policyengine_us/parameters/gov/states/mo/tax/income/subtractions/agi_subtractions.yaml b/policyengine_us/parameters/gov/states/mo/tax/income/subtractions/agi_subtractions.yaml index e77a2bb4f8d..f66dbe12515 100644 --- a/policyengine_us/parameters/gov/states/mo/tax/income/subtractions/agi_subtractions.yaml +++ b/policyengine_us/parameters/gov/states/mo/tax/income/subtractions/agi_subtractions.yaml @@ -2,9 +2,11 @@ description: Missouri subtracts these sources from federal adjusted gross income values: 2008-01-01: - mo_qualified_health_insurance_premiums + - mo_529_deduction 2025-01-01: - mo_qualified_health_insurance_premiums - mo_capital_gains_subtraction_person + - mo_529_deduction metadata: diff --git a/policyengine_us/parameters/gov/states/mo/tax/income/subtractions/plan_529_contributions/cap.yaml b/policyengine_us/parameters/gov/states/mo/tax/income/subtractions/plan_529_contributions/cap.yaml new file mode 100644 index 00000000000..71fea29a693 --- /dev/null +++ b/policyengine_us/parameters/gov/states/mo/tax/income/subtractions/plan_529_contributions/cap.yaml @@ -0,0 +1,23 @@ +description: Missouri caps the subtraction for 529 education savings plan contributions at this amount. +metadata: + label: Missouri 529 plan contribution subtraction cap + period: year + unit: currency-USD + breakdown: + - filing_status + reference: + - title: Revisor of Missouri § 143.121.2(4) + href: https://www.revisor.mo.gov/main/OneSection.aspx?section=143.121 + - title: 2025 MO-1040 Book - Individual Income Tax Long Form + href: https://dor.mo.gov/forms/MO-1040%20Instructions_2025.pdf#page=12 + +JOINT: + 2021-01-01: 16_000 +SURVIVING_SPOUSE: + 2021-01-01: 16_000 +SINGLE: + 2021-01-01: 8_000 +HEAD_OF_HOUSEHOLD: + 2021-01-01: 8_000 +SEPARATE: + 2021-01-01: 8_000 diff --git a/policyengine_us/parameters/gov/states/ms/tax/income/adjustments/adjustments.yaml b/policyengine_us/parameters/gov/states/ms/tax/income/adjustments/adjustments.yaml index b9ca714e33d..c61a8538eb2 100644 --- a/policyengine_us/parameters/gov/states/ms/tax/income/adjustments/adjustments.yaml +++ b/policyengine_us/parameters/gov/states/ms/tax/income/adjustments/adjustments.yaml @@ -6,6 +6,7 @@ values: - ms_national_guard_or_reserve_pay_adjustment # Line 55 - alimony_expense # Line 53 - ms_retirement_income_exemption + - ms_529_deduction metadata: label: Mississippi adjusted gross income adjustments period: year diff --git a/policyengine_us/parameters/gov/states/ms/tax/income/adjustments/plan_529_contributions/cap.yaml b/policyengine_us/parameters/gov/states/ms/tax/income/adjustments/plan_529_contributions/cap.yaml new file mode 100644 index 00000000000..ebcb798e333 --- /dev/null +++ b/policyengine_us/parameters/gov/states/ms/tax/income/adjustments/plan_529_contributions/cap.yaml @@ -0,0 +1,23 @@ +description: Mississippi caps the adjustment for 529 education savings plan contributions at this amount. +metadata: + label: Mississippi 529 plan contribution adjustment cap + period: year + unit: currency-USD + breakdown: + - filing_status + reference: + - title: Mississippi Code § 27-7-18(2) + href: https://law.justia.com/codes/mississippi/2022/title-27/chapter-7/article-1/section-27-7-18/ + - title: Mississippi Income Tax Instructions 2025 + href: https://www.dor.ms.gov/sites/default/files/tax-forms/individual/80100251%202.pdf#page=12 + +JOINT: + 2021-01-01: 20_000 +SURVIVING_SPOUSE: + 2021-01-01: 20_000 +SINGLE: + 2021-01-01: 10_000 +HEAD_OF_HOUSEHOLD: + 2021-01-01: 10_000 +SEPARATE: + 2021-01-01: 10_000 diff --git a/policyengine_us/parameters/gov/states/nd/tax/income/taxable_income/subtractions/plan_529_contributions/cap.yaml b/policyengine_us/parameters/gov/states/nd/tax/income/taxable_income/subtractions/plan_529_contributions/cap.yaml new file mode 100644 index 00000000000..7cf4b1eb08a --- /dev/null +++ b/policyengine_us/parameters/gov/states/nd/tax/income/taxable_income/subtractions/plan_529_contributions/cap.yaml @@ -0,0 +1,23 @@ +description: North Dakota caps the subtraction for 529 education savings plan contributions at this amount. +metadata: + label: North Dakota 529 plan contribution subtraction cap + period: year + unit: currency-USD + breakdown: + - filing_status + reference: + - title: North Dakota Century Code § 57-38-30.3(2)(p) + href: https://law.justia.com/codes/north-dakota/2022/title-57/chapter-57-38/section-57-38-30-3/ + - title: 2025 North Dakota income tax instructions + href: https://www.tax.nd.gov/sites/www/files/documents/forms/individual/2025-iit/2025-individual-income-tax-booklet.pdf#page=14 + +JOINT: + 2021-01-01: 10_000 +SURVIVING_SPOUSE: + 2021-01-01: 10_000 +SINGLE: + 2021-01-01: 5_000 +HEAD_OF_HOUSEHOLD: + 2021-01-01: 5_000 +SEPARATE: + 2021-01-01: 5_000 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 8bcf7c4b5c6..7afa7983626 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 @@ -6,6 +6,7 @@ values: - nd_qdiv_subtraction # qualified dividends - taxable_social_security - military_retirement_pay + - nd_529_deduction 2023-01-01: - us_govt_interest @@ -14,6 +15,7 @@ values: - taxable_social_security - military_retirement_pay - military_service_income # military pay exculsion + - nd_529_deduction metadata: unit: list diff --git a/policyengine_us/parameters/gov/states/ne/tax/income/agi/subtractions/plan_529_contributions/cap.yaml b/policyengine_us/parameters/gov/states/ne/tax/income/agi/subtractions/plan_529_contributions/cap.yaml new file mode 100644 index 00000000000..c7104921eb7 --- /dev/null +++ b/policyengine_us/parameters/gov/states/ne/tax/income/agi/subtractions/plan_529_contributions/cap.yaml @@ -0,0 +1,23 @@ +description: Nebraska caps the subtraction for 529 education savings plan contributions at this amount. +metadata: + label: Nebraska 529 plan contribution subtraction cap + period: year + unit: currency-USD + breakdown: + - filing_status + reference: + - title: Nebraska Revised Statute § 77-2716(20) + href: https://www.nebraskalegislature.gov/laws/statutes.php?statute=77-2716 + - title: 2025 Nebraska Individual Income Tax Booklet + href: https://revenue.nebraska.gov/sites/default/files/doc/tax-forms/2025/f_Individual_Income_Tax_Booklet.pdf#page=26 + +JOINT: + 2021-01-01: 10_000 +SURVIVING_SPOUSE: + 2021-01-01: 10_000 +SINGLE: + 2021-01-01: 10_000 +HEAD_OF_HOUSEHOLD: + 2021-01-01: 10_000 +SEPARATE: + 2021-01-01: 5_000 diff --git a/policyengine_us/parameters/gov/states/ne/tax/income/agi/subtractions/subtractions.yaml b/policyengine_us/parameters/gov/states/ne/tax/income/agi/subtractions/subtractions.yaml index 5301a07fa7c..8d64f368ef2 100644 --- a/policyengine_us/parameters/gov/states/ne/tax/income/agi/subtractions/subtractions.yaml +++ b/policyengine_us/parameters/gov/states/ne/tax/income/agi/subtractions/subtractions.yaml @@ -3,10 +3,12 @@ values: 2021-01-01: - ne_social_security_subtraction - ne_military_retirement_subtraction + - ne_529_deduction 2024-01-01: - ne_social_security_subtraction - taxable_public_pension_income - ne_military_retirement_subtraction + - ne_529_deduction metadata: diff --git a/policyengine_us/parameters/gov/states/nj/tax/income/deductions/plan_529_contributions/cap.yaml b/policyengine_us/parameters/gov/states/nj/tax/income/deductions/plan_529_contributions/cap.yaml new file mode 100644 index 00000000000..1aa29b0167b --- /dev/null +++ b/policyengine_us/parameters/gov/states/nj/tax/income/deductions/plan_529_contributions/cap.yaml @@ -0,0 +1,12 @@ +description: New Jersey caps the deduction for 529 education savings plan contributions at this amount. +metadata: + label: New Jersey 529 plan contribution deduction cap + period: year + unit: currency-USD + reference: + - title: New Jersey P.L. 2021, c.419 § 1 + href: https://www.njleg.state.nj.us/bill-search/2020/A5535 + - title: NJ-1040 Instructions 2024 + href: https://www.nj.gov/treasury/taxation/pdf/current/1040i.pdf +values: + 2022-01-01: 10_000 diff --git a/policyengine_us/parameters/gov/states/nj/tax/income/deductions/plan_529_contributions/income_limit.yaml b/policyengine_us/parameters/gov/states/nj/tax/income/deductions/plan_529_contributions/income_limit.yaml new file mode 100644 index 00000000000..16ec3dd42f1 --- /dev/null +++ b/policyengine_us/parameters/gov/states/nj/tax/income/deductions/plan_529_contributions/income_limit.yaml @@ -0,0 +1,12 @@ +description: New Jersey limits the 529 plan contribution deduction to taxpayers with adjusted gross income at or below this amount. +metadata: + label: New Jersey 529 plan contribution deduction AGI limit + period: year + unit: currency-USD + reference: + - title: New Jersey P.L. 2021, c.419 § 1 + href: https://www.njleg.state.nj.us/bill-search/2020/A5535 + - title: NJ-1040 Instructions 2024 + href: https://www.nj.gov/treasury/taxation/pdf/current/1040i.pdf +values: + 2022-01-01: 200_000 diff --git a/policyengine_us/parameters/gov/states/nj/tax/income/subtractions.yaml b/policyengine_us/parameters/gov/states/nj/tax/income/subtractions.yaml index b1397d0460a..8a95955de67 100644 --- a/policyengine_us/parameters/gov/states/nj/tax/income/subtractions.yaml +++ b/policyengine_us/parameters/gov/states/nj/tax/income/subtractions.yaml @@ -9,3 +9,5 @@ values: # Social Security is already excluded from NJ gross income (not in gross_income_sources.yaml) # per NJ Statute 54A:6-15. No subtractions are needed. 2021-01-01: [] + 2022-01-01: + - nj_529_deduction diff --git a/policyengine_us/tests/policy/baseline/gov/states/ma/tax/income/deductions/plan_529_contributions/ma_529_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/ma/tax/income/deductions/plan_529_contributions/ma_529_deduction.yaml new file mode 100644 index 00000000000..9896e45268e --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ma/tax/income/deductions/plan_529_contributions/ma_529_deduction.yaml @@ -0,0 +1,41 @@ +- name: Case 1, single filer with contribution below cap. + period: 2024 + absolute_error_margin: 0.1 + input: + state_code: MA + filing_status: SINGLE + investment_in_529_plan: 800 + output: + ma_529_deduction: 800 + +- name: Case 2, single filer with contribution above cap. + period: 2024 + absolute_error_margin: 0.1 + input: + state_code: MA + filing_status: SINGLE + investment_in_529_plan: 3_000 + output: + # Capped at $1,000 + ma_529_deduction: 1_000 + +- name: Case 3, joint filer with contribution above cap. + period: 2024 + absolute_error_margin: 0.1 + input: + state_code: MA + filing_status: JOINT + investment_in_529_plan: 5_000 + output: + # Capped at $2,000 + ma_529_deduction: 2_000 + +- name: Case 4, joint filer with no contributions. + period: 2024 + absolute_error_margin: 0.1 + input: + state_code: MA + filing_status: JOINT + investment_in_529_plan: 0 + output: + ma_529_deduction: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/tax/income/agi/subtractions/plan_529_contributions/md_529_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/tax/income/agi/subtractions/plan_529_contributions/md_529_deduction.yaml new file mode 100644 index 00000000000..f36700bb269 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/md/tax/income/agi/subtractions/plan_529_contributions/md_529_deduction.yaml @@ -0,0 +1,45 @@ +- name: Case 1, single filer with contribution below cap. + period: 2024 + absolute_error_margin: 0.1 + input: + state_code: MD + filing_status: SINGLE + investment_in_529_plan: 2_000 + count_529_contribution_beneficiaries: 1 + output: + md_529_deduction: 2_000 + +- name: Case 2, single filer with contribution above cap. + period: 2024 + absolute_error_margin: 0.1 + input: + state_code: MD + filing_status: SINGLE + investment_in_529_plan: 5_000 + count_529_contribution_beneficiaries: 1 + output: + # Capped at $2,500 per beneficiary * 1 beneficiary = $2,500 + md_529_deduction: 2_500 + +- name: Case 3, joint filer with two beneficiaries. + period: 2024 + absolute_error_margin: 0.1 + input: + state_code: MD + filing_status: JOINT + investment_in_529_plan: 15_000 + count_529_contribution_beneficiaries: 2 + output: + # Capped at $5,000 per beneficiary * 2 beneficiaries = $10,000 + md_529_deduction: 10_000 + +- name: Case 4, joint filer with no contributions. + period: 2024 + absolute_error_margin: 0.1 + input: + state_code: MD + filing_status: JOINT + investment_in_529_plan: 0 + count_529_contribution_beneficiaries: 1 + output: + md_529_deduction: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/deductions/plan_529_contributions/mi_529_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/deductions/plan_529_contributions/mi_529_deduction.yaml new file mode 100644 index 00000000000..2f90e729876 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/deductions/plan_529_contributions/mi_529_deduction.yaml @@ -0,0 +1,41 @@ +- name: Case 1, single filer with contribution below cap. + period: 2024 + absolute_error_margin: 0.1 + input: + state_code: MI + filing_status: SINGLE + investment_in_529_plan: 3_000 + output: + mi_529_deduction: 3_000 + +- name: Case 2, single filer with contribution above cap. + period: 2024 + absolute_error_margin: 0.1 + input: + state_code: MI + filing_status: SINGLE + investment_in_529_plan: 8_000 + output: + # Capped at $5,000 + mi_529_deduction: 5_000 + +- name: Case 3, joint filer with contribution above cap. + period: 2024 + absolute_error_margin: 0.1 + input: + state_code: MI + filing_status: JOINT + investment_in_529_plan: 15_000 + output: + # Capped at $10,000 + mi_529_deduction: 10_000 + +- name: Case 4, joint filer with no contributions. + period: 2024 + absolute_error_margin: 0.1 + input: + state_code: MI + filing_status: JOINT + investment_in_529_plan: 0 + output: + mi_529_deduction: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/subtractions/plan_529_contributions/mo_529_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/subtractions/plan_529_contributions/mo_529_deduction.yaml new file mode 100644 index 00000000000..f689917fe3a --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/subtractions/plan_529_contributions/mo_529_deduction.yaml @@ -0,0 +1,41 @@ +- name: Case 1, single filer with contribution below cap. + period: 2024 + absolute_error_margin: 0.1 + input: + state_code: MO + filing_status: SINGLE + investment_in_529_plan_indv: 5_000 + output: + mo_529_deduction: 5_000 + +- name: Case 2, single filer with contribution above cap. + period: 2024 + absolute_error_margin: 0.1 + input: + state_code: MO + filing_status: SINGLE + investment_in_529_plan_indv: 12_000 + output: + # Capped at $8,000 + mo_529_deduction: 8_000 + +- name: Case 3, joint filer with contribution above cap. + period: 2024 + absolute_error_margin: 0.1 + input: + state_code: MO + filing_status: JOINT + investment_in_529_plan_indv: 20_000 + output: + # Capped at $16,000 + mo_529_deduction: 16_000 + +- name: Case 4, single filer with no contributions. + period: 2024 + absolute_error_margin: 0.1 + input: + state_code: MO + filing_status: SINGLE + investment_in_529_plan_indv: 0 + output: + mo_529_deduction: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ms/tax/income/adjustments/plan_529_contributions/ms_529_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/ms/tax/income/adjustments/plan_529_contributions/ms_529_deduction.yaml new file mode 100644 index 00000000000..76c8c1afe0a --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ms/tax/income/adjustments/plan_529_contributions/ms_529_deduction.yaml @@ -0,0 +1,41 @@ +- name: Case 1, single filer with contribution below cap. + period: 2024 + absolute_error_margin: 0.1 + input: + state_code: MS + filing_status: SINGLE + investment_in_529_plan_indv: 5_000 + output: + ms_529_deduction: 5_000 + +- name: Case 2, single filer with contribution above cap. + period: 2024 + absolute_error_margin: 0.1 + input: + state_code: MS + filing_status: SINGLE + investment_in_529_plan_indv: 15_000 + output: + # Capped at $10,000 + ms_529_deduction: 10_000 + +- name: Case 3, joint filer with contribution above cap. + period: 2024 + absolute_error_margin: 0.1 + input: + state_code: MS + filing_status: JOINT + investment_in_529_plan_indv: 25_000 + output: + # Capped at $20,000 + ms_529_deduction: 20_000 + +- name: Case 4, single filer with no contributions. + period: 2024 + absolute_error_margin: 0.1 + input: + state_code: MS + filing_status: SINGLE + investment_in_529_plan_indv: 0 + output: + ms_529_deduction: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nd/tax/income/taxable_income/subtractions/plan_529_contributions/nd_529_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/nd/tax/income/taxable_income/subtractions/plan_529_contributions/nd_529_deduction.yaml new file mode 100644 index 00000000000..9c5584d6431 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/nd/tax/income/taxable_income/subtractions/plan_529_contributions/nd_529_deduction.yaml @@ -0,0 +1,41 @@ +- name: Case 1, single filer with contribution below cap. + period: 2024 + absolute_error_margin: 0.1 + input: + state_code: ND + filing_status: SINGLE + investment_in_529_plan: 3_000 + output: + nd_529_deduction: 3_000 + +- name: Case 2, single filer with contribution above cap. + period: 2024 + absolute_error_margin: 0.1 + input: + state_code: ND + filing_status: SINGLE + investment_in_529_plan: 8_000 + output: + # Capped at $5,000 + nd_529_deduction: 5_000 + +- name: Case 3, joint filer with contribution above cap. + period: 2024 + absolute_error_margin: 0.1 + input: + state_code: ND + filing_status: JOINT + investment_in_529_plan: 15_000 + output: + # Capped at $10,000 + nd_529_deduction: 10_000 + +- name: Case 4, joint filer with no contributions. + period: 2024 + absolute_error_margin: 0.1 + input: + state_code: ND + filing_status: JOINT + investment_in_529_plan: 0 + output: + nd_529_deduction: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ne/tax/income/agi/subtractions/plan_529_contributions/ne_529_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/ne/tax/income/agi/subtractions/plan_529_contributions/ne_529_deduction.yaml new file mode 100644 index 00000000000..55616274d9a --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ne/tax/income/agi/subtractions/plan_529_contributions/ne_529_deduction.yaml @@ -0,0 +1,41 @@ +- name: Case 1, single filer with contribution below cap. + period: 2024 + absolute_error_margin: 0.1 + input: + state_code: NE + filing_status: SINGLE + investment_in_529_plan: 5_000 + output: + ne_529_deduction: 5_000 + +- name: Case 2, single filer with contribution above cap. + period: 2024 + absolute_error_margin: 0.1 + input: + state_code: NE + filing_status: SINGLE + investment_in_529_plan: 15_000 + output: + # Capped at $10,000 + ne_529_deduction: 10_000 + +- name: Case 3, married filing separately with contribution above cap. + period: 2024 + absolute_error_margin: 0.1 + input: + state_code: NE + filing_status: SEPARATE + investment_in_529_plan: 8_000 + output: + # MFS capped at $5,000 + ne_529_deduction: 5_000 + +- name: Case 4, joint filer with no contributions. + period: 2024 + absolute_error_margin: 0.1 + input: + state_code: NE + filing_status: JOINT + investment_in_529_plan: 0 + output: + ne_529_deduction: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/deductions/plan_529_contributions/nj_529_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/deductions/plan_529_contributions/nj_529_deduction.yaml new file mode 100644 index 00000000000..906d537ce4f --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/deductions/plan_529_contributions/nj_529_deduction.yaml @@ -0,0 +1,41 @@ +- name: Case 1, filer with contribution below cap and income below limit. + period: 2024 + absolute_error_margin: 0.1 + input: + state_code: NJ + investment_in_529_plan_indv: 5_000 + employment_income: 100_000 + output: + nj_529_deduction: 5_000 + +- name: Case 2, filer with contribution above cap and income below limit. + period: 2024 + absolute_error_margin: 0.1 + input: + state_code: NJ + investment_in_529_plan_indv: 15_000 + employment_income: 100_000 + output: + # Capped at $10,000 + nj_529_deduction: 10_000 + +- name: Case 3, filer with income above AGI limit. + period: 2024 + absolute_error_margin: 0.1 + input: + state_code: NJ + investment_in_529_plan_indv: 5_000 + employment_income: 250_000 + output: + # AGI exceeds $200,000 limit, no deduction + nj_529_deduction: 0 + +- name: Case 4, filer with no contributions. + period: 2024 + absolute_error_margin: 0.1 + input: + state_code: NJ + investment_in_529_plan_indv: 0 + employment_income: 100_000 + output: + nj_529_deduction: 0 diff --git a/policyengine_us/variables/gov/states/ma/tax/income/deductions/plan_529_contributions/ma_529_deduction.py b/policyengine_us/variables/gov/states/ma/tax/income/deductions/plan_529_contributions/ma_529_deduction.py new file mode 100644 index 00000000000..056137858af --- /dev/null +++ b/policyengine_us/variables/gov/states/ma/tax/income/deductions/plan_529_contributions/ma_529_deduction.py @@ -0,0 +1,23 @@ +from policyengine_us.model_api import * + + +class ma_529_deduction(Variable): + value_type = float + entity = TaxUnit + label = "Massachusetts 529 plan contribution deduction" + unit = USD + definition_period = YEAR + reference = ( + "https://malegislature.gov/Laws/GeneralLaws/PartI/TitleIX/Chapter62/Section3", + "https://www.mass.gov/info-details/529-plan-deduction", + ) + defined_for = StateCode.MA + + def formula(tax_unit, period, parameters): + p = parameters( + period + ).gov.states.ma.tax.income.deductions.plan_529_contributions + contributions = tax_unit("investment_in_529_plan", period) + filing_status = tax_unit("filing_status", period) + cap = p.cap[filing_status] + return min_(contributions, cap) diff --git a/policyengine_us/variables/gov/states/ma/tax/income/taxable_income/ma_part_b_taxable_income_deductions.py b/policyengine_us/variables/gov/states/ma/tax/income/taxable_income/ma_part_b_taxable_income_deductions.py index fb8a5946b2f..3089a00c7d8 100644 --- a/policyengine_us/variables/gov/states/ma/tax/income/taxable_income/ma_part_b_taxable_income_deductions.py +++ b/policyengine_us/variables/gov/states/ma/tax/income/taxable_income/ma_part_b_taxable_income_deductions.py @@ -51,9 +51,12 @@ def formula(tax_unit, period, parameters): ) # (B)(a)(13): Charitable contributions deduction. charitable_deduction = tax_unit("charitable_deduction", period) + # (B)(a)(14): 529 plan contribution deduction. + plan_529_deduction = tax_unit("ma_529_deduction", period) return ( fica + bank_interest_deduction + rent_deduction + charitable_deduction + + plan_529_deduction ) diff --git a/policyengine_us/variables/gov/states/md/tax/income/agi/subtractions/plan_529_contributions/md_529_deduction.py b/policyengine_us/variables/gov/states/md/tax/income/agi/subtractions/plan_529_contributions/md_529_deduction.py new file mode 100644 index 00000000000..017f9c17ab3 --- /dev/null +++ b/policyengine_us/variables/gov/states/md/tax/income/agi/subtractions/plan_529_contributions/md_529_deduction.py @@ -0,0 +1,26 @@ +from policyengine_us.model_api import * + + +class md_529_deduction(Variable): + value_type = float + entity = TaxUnit + label = "Maryland 529 plan contribution subtraction" + unit = USD + definition_period = YEAR + reference = ( + "https://mgaleg.maryland.gov/mgawebsite/Laws/StatuteText?article=gtg§ion=10-208&enession=2024RS", + "https://www.marylandcomptroller.gov/content/dam/mdcomp/tax/instructions/2025/resident-booklet.pdf#page=15", + ) + defined_for = StateCode.MD + + def formula(tax_unit, period, parameters): + p = parameters( + period + ).gov.states.md.tax.income.agi.subtractions.plan_529_contributions + contributions = tax_unit("investment_in_529_plan", period) + beneficiaries = add( + tax_unit, period, ["count_529_contribution_beneficiaries"] + ) + filing_status = tax_unit("filing_status", period) + cap = p.cap[filing_status] * max_(beneficiaries, 1) + return min_(contributions, cap) diff --git a/policyengine_us/variables/gov/states/mi/tax/income/deductions/plan_529_contributions/mi_529_deduction.py b/policyengine_us/variables/gov/states/mi/tax/income/deductions/plan_529_contributions/mi_529_deduction.py new file mode 100644 index 00000000000..9593f81e67f --- /dev/null +++ b/policyengine_us/variables/gov/states/mi/tax/income/deductions/plan_529_contributions/mi_529_deduction.py @@ -0,0 +1,23 @@ +from policyengine_us.model_api import * + + +class mi_529_deduction(Variable): + value_type = float + entity = TaxUnit + label = "Michigan 529 plan contribution subtraction" + unit = USD + definition_period = YEAR + reference = ( + "http://legislature.mi.gov/doc.aspx?mcl-206-30", + "https://www.michigan.gov/taxes/-/media/Project/Websites/taxes/Forms/IIT/TY2024/MI-1040-Instructions.pdf#page=13", + ) + defined_for = StateCode.MI + + def formula(tax_unit, period, parameters): + p = parameters( + period + ).gov.states.mi.tax.income.deductions.plan_529_contributions + contributions = tax_unit("investment_in_529_plan", period) + filing_status = tax_unit("filing_status", period) + cap = p.cap[filing_status] + return min_(contributions, cap) diff --git a/policyengine_us/variables/gov/states/mo/tax/income/subtractions/plan_529_contributions/mo_529_deduction.py b/policyengine_us/variables/gov/states/mo/tax/income/subtractions/plan_529_contributions/mo_529_deduction.py new file mode 100644 index 00000000000..63c7f3d5119 --- /dev/null +++ b/policyengine_us/variables/gov/states/mo/tax/income/subtractions/plan_529_contributions/mo_529_deduction.py @@ -0,0 +1,31 @@ +from policyengine_us.model_api import * + + +class mo_529_deduction(Variable): + value_type = float + entity = Person + label = "Missouri 529 plan contribution subtraction" + unit = USD + definition_period = YEAR + reference = ( + "https://www.revisor.mo.gov/main/OneSection.aspx?section=143.121", + "https://dor.mo.gov/forms/MO-1040%20Instructions_2025.pdf#page=12", + ) + defined_for = StateCode.MO + + def formula(person, period, parameters): + p = parameters( + period + ).gov.states.mo.tax.income.subtractions.plan_529_contributions + # Get the TaxUnit-level deduction cap + filing_status = person.tax_unit("filing_status", period) + cap = p.cap[filing_status] + total_contributions = person.tax_unit("investment_in_529_plan", period) + unit_deduction = min_(total_contributions, cap) + # Allocate proportionally to each person + person_contributions = person("investment_in_529_plan_indv", period) + total = person.tax_unit.sum(person_contributions) + share = np.zeros_like(total) + mask = total != 0 + share[mask] = person_contributions[mask] / total[mask] + return share * unit_deduction diff --git a/policyengine_us/variables/gov/states/ms/tax/income/adjustments/plan_529_contributions/ms_529_deduction.py b/policyengine_us/variables/gov/states/ms/tax/income/adjustments/plan_529_contributions/ms_529_deduction.py new file mode 100644 index 00000000000..0fc3d8a349d --- /dev/null +++ b/policyengine_us/variables/gov/states/ms/tax/income/adjustments/plan_529_contributions/ms_529_deduction.py @@ -0,0 +1,31 @@ +from policyengine_us.model_api import * + + +class ms_529_deduction(Variable): + value_type = float + entity = Person + label = "Mississippi 529 plan contribution adjustment" + unit = USD + definition_period = YEAR + reference = ( + "https://law.justia.com/codes/mississippi/2022/title-27/chapter-7/article-1/section-27-7-18/", + "https://www.dor.ms.gov/sites/default/files/tax-forms/individual/80100251%202.pdf#page=12", + ) + defined_for = StateCode.MS + + def formula(person, period, parameters): + p = parameters( + period + ).gov.states.ms.tax.income.adjustments.plan_529_contributions + # Get the TaxUnit-level deduction cap + filing_status = person.tax_unit("filing_status", period) + cap = p.cap[filing_status] + total_contributions = person.tax_unit("investment_in_529_plan", period) + unit_deduction = min_(total_contributions, cap) + # Allocate proportionally to each person + person_contributions = person("investment_in_529_plan_indv", period) + total = person.tax_unit.sum(person_contributions) + share = np.zeros_like(total) + mask = total != 0 + share[mask] = person_contributions[mask] / total[mask] + return share * unit_deduction diff --git a/policyengine_us/variables/gov/states/nd/tax/income/taxable_income/subtractions/plan_529_contributions/nd_529_deduction.py b/policyengine_us/variables/gov/states/nd/tax/income/taxable_income/subtractions/plan_529_contributions/nd_529_deduction.py new file mode 100644 index 00000000000..c08576609ba --- /dev/null +++ b/policyengine_us/variables/gov/states/nd/tax/income/taxable_income/subtractions/plan_529_contributions/nd_529_deduction.py @@ -0,0 +1,23 @@ +from policyengine_us.model_api import * + + +class nd_529_deduction(Variable): + value_type = float + entity = TaxUnit + label = "North Dakota 529 plan contribution subtraction" + unit = USD + definition_period = YEAR + reference = ( + "https://law.justia.com/codes/north-dakota/2022/title-57/chapter-57-38/section-57-38-30-3/", + "https://www.tax.nd.gov/sites/www/files/documents/forms/individual/2025-iit/2025-individual-income-tax-booklet.pdf#page=14", + ) + defined_for = StateCode.ND + + def formula(tax_unit, period, parameters): + p = parameters( + period + ).gov.states.nd.tax.income.taxable_income.subtractions.plan_529_contributions + contributions = tax_unit("investment_in_529_plan", period) + filing_status = tax_unit("filing_status", period) + cap = p.cap[filing_status] + return min_(contributions, cap) diff --git a/policyengine_us/variables/gov/states/ne/tax/income/agi/subtractions/plan_529_contributions/ne_529_deduction.py b/policyengine_us/variables/gov/states/ne/tax/income/agi/subtractions/plan_529_contributions/ne_529_deduction.py new file mode 100644 index 00000000000..bb4493ccba2 --- /dev/null +++ b/policyengine_us/variables/gov/states/ne/tax/income/agi/subtractions/plan_529_contributions/ne_529_deduction.py @@ -0,0 +1,23 @@ +from policyengine_us.model_api import * + + +class ne_529_deduction(Variable): + value_type = float + entity = TaxUnit + label = "Nebraska 529 plan contribution subtraction" + unit = USD + definition_period = YEAR + reference = ( + "https://www.nebraskalegislature.gov/laws/statutes.php?statute=77-2716", + "https://revenue.nebraska.gov/sites/default/files/doc/tax-forms/2025/f_Individual_Income_Tax_Booklet.pdf#page=26", + ) + defined_for = StateCode.NE + + def formula(tax_unit, period, parameters): + p = parameters( + period + ).gov.states.ne.tax.income.agi.subtractions.plan_529_contributions + contributions = tax_unit("investment_in_529_plan", period) + filing_status = tax_unit("filing_status", period) + cap = p.cap[filing_status] + return min_(contributions, cap) diff --git a/policyengine_us/variables/gov/states/nj/tax/income/deductions/plan_529_contributions/nj_529_deduction.py b/policyengine_us/variables/gov/states/nj/tax/income/deductions/plan_529_contributions/nj_529_deduction.py new file mode 100644 index 00000000000..d44aa2d8bf4 --- /dev/null +++ b/policyengine_us/variables/gov/states/nj/tax/income/deductions/plan_529_contributions/nj_529_deduction.py @@ -0,0 +1,32 @@ +from policyengine_us.model_api import * + + +class nj_529_deduction(Variable): + value_type = float + entity = Person + label = "New Jersey 529 plan contribution deduction" + unit = USD + definition_period = YEAR + reference = ( + "https://www.njleg.state.nj.us/bill-search/2020/A5535", + "https://www.nj.gov/treasury/taxation/pdf/current/1040i.pdf", + ) + defined_for = StateCode.NJ + + def formula(person, period, parameters): + p = parameters( + period + ).gov.states.nj.tax.income.deductions.plan_529_contributions + # Check AGI income limit + agi = person.tax_unit("adjusted_gross_income", period) + income_eligible = agi <= p.income_limit + # Get the TaxUnit-level deduction cap + total_contributions = person.tax_unit("investment_in_529_plan", period) + unit_deduction = min_(total_contributions, p.cap) + # Allocate proportionally to each person + person_contributions = person("investment_in_529_plan_indv", period) + total = person.tax_unit.sum(person_contributions) + share = np.zeros_like(total) + mask = total != 0 + share[mask] = person_contributions[mask] / total[mask] + return where(income_eligible, share * unit_deduction, 0)