diff --git a/changelog.d/rrb-federal-tax.added.md b/changelog.d/rrb-federal-tax.added.md new file mode 100644 index 00000000000..ec745630257 --- /dev/null +++ b/changelog.d/rrb-federal-tax.added.md @@ -0,0 +1 @@ +Add inputs for the three parts of Railroad Retirement Act benefits: the tier 1 social security equivalent benefit, the taxable rest of the annuity and its tax-free part. diff --git a/changelog.d/rrb-federal-tax.fixed.md b/changelog.d/rrb-federal-tax.fixed.md new file mode 100644 index 00000000000..e0582bebdb2 --- /dev/null +++ b/changelog.d/rrb-federal-tax.fixed.md @@ -0,0 +1 @@ +Tax Railroad Retirement Act benefits federally, with tier 1 social security equivalent benefits under IRC 86 alongside Social Security and the rest of the annuity as a pension under IRC 72(r). Keep them out of every state income tax, Minnesota's and Vermont's minimum taxes and the state withholding estimates, as 45 U.S.C. 231m(a) requires, and count them as each state's rules do in Arizona's property tax credit, Minnesota's elderly and disabled subtraction, Oregon's retirement income credit, West Virginia's senior citizen deduction, Pennsylvania's property tax and rent rebate and Utah's homeowner and renter relief. diff --git a/changelog.d/vet-rrb-income-totals.fixed.md b/changelog.d/vet-rrb-income-totals.fixed.md new file mode 100644 index 00000000000..5eeb08ad4f2 --- /dev/null +++ b/changelog.d/vet-rrb-income-totals.fixed.md @@ -0,0 +1 @@ +Count veterans' benefits and Railroad Retirement Board benefits in household and SPM benefits, and in the copies of those lists that the BOOST, End Child Poverty Act and Texas rebate reforms define, so both net incomes and SPM resources include them. diff --git a/policyengine_us/parameters/gov/hhs/medicaid/income/modification.yaml b/policyengine_us/parameters/gov/hhs/medicaid/income/modification.yaml index 24c48d75830..ad9b1461f83 100644 --- a/policyengine_us/parameters/gov/hhs/medicaid/income/modification.yaml +++ b/policyengine_us/parameters/gov/hhs/medicaid/income/modification.yaml @@ -5,6 +5,9 @@ values: - section_911_excluded_income - tax_exempt_interest_income - tax_exempt_social_security + # Section 36B(d)(2)(B)(iii) adds back social security benefits as section + # 86(d) defines them, which include tier 1 railroad retirement benefits. + - tax_exempt_railroad_social_security_equivalent_benefits metadata: unit: list label: Medicaid/CHIP/ACA-related MAGI additions over AGI diff --git a/policyengine_us/parameters/gov/household/household_benefits.yaml b/policyengine_us/parameters/gov/household/household_benefits.yaml index 2c48d38ca0e..18e36e3901d 100644 --- a/policyengine_us/parameters/gov/household/household_benefits.yaml +++ b/policyengine_us/parameters/gov/household/household_benefits.yaml @@ -19,6 +19,10 @@ values: - educational_assistance - financial_assistance - survivor_benefits + # Census money income counts veterans' payments, and railroad retirement + # as a government transfer (P60-200 Appendix A). + - veterans_benefits + - railroad_benefits - household_head_start_benefits # One-time energy relief payments. # Paid at the same time as the Alaska Permanent Fund Dividend, @@ -52,6 +56,10 @@ values: - educational_assistance - financial_assistance - survivor_benefits + # Census money income counts veterans' payments, and railroad retirement + # as a government transfer (P60-200 Appendix A). + - veterans_benefits + - railroad_benefits - household_head_start_benefits # One-time energy relief payments. # Paid at the same time as the Alaska Permanent Fund Dividend, @@ -72,3 +80,6 @@ metadata: unit: list period: year label: Household benefits + reference: + - title: "Census Bureau, P60-200 Appendix A, Definitions and Explanations #page 3-4" + href: https://www2.census.gov/programs-surveys/cps/methodology/Appendix%20A%20P60-200%20income%20measurement%20definition.pdf#page=3 diff --git a/policyengine_us/parameters/gov/irs/deductions/itemized/salt_and_real_estate/state_sales_tax_table/income_sources.yaml b/policyengine_us/parameters/gov/irs/deductions/itemized/salt_and_real_estate/state_sales_tax_table/income_sources.yaml index c3f01b309b2..b837590cb45 100644 --- a/policyengine_us/parameters/gov/irs/deductions/itemized/salt_and_real_estate/state_sales_tax_table/income_sources.yaml +++ b/policyengine_us/parameters/gov/irs/deductions/itemized/salt_and_real_estate/state_sales_tax_table/income_sources.yaml @@ -44,11 +44,13 @@ values: - veterans_benefits # Nontaxable combat pay - not available. - workers_compensation - # Nontaxable part of social security and railroad retirement benefits; - # railroad retirement benefits are not included. + # Nontaxable part of social security and railroad retirement benefits. - tax_exempt_social_security - # Nontaxable IRA, pension, or annuity distributions (excluding rollovers). + - tax_exempt_railroad_social_security_equivalent_benefits + # Nontaxable IRA, pension, or annuity distributions (excluding rollovers), + # including Railroad Retirement Act annuities' tax-free part. - tax_exempt_pension_income + - tax_exempt_railroad_pension_benefits - tax_exempt_retirement_distributions # Public assistance payments. - tanf diff --git a/policyengine_us/parameters/gov/irs/gross_income/sources.yaml b/policyengine_us/parameters/gov/irs/gross_income/sources.yaml index a2474e69e74..c269293693d 100644 --- a/policyengine_us/parameters/gov/irs/gross_income/sources.yaml +++ b/policyengine_us/parameters/gov/irs/gross_income/sources.yaml @@ -21,6 +21,10 @@ values: - dividend_income - taxable_pension_income # Pension income includes annuities and endowment contracts + # Railroad Retirement Act benefits other than the social security + # equivalent benefit, which IRC 72(r)(1) treats as benefits from a + # qualified employer plan (Form 1040 line 5b). + - taxable_railroad_pension_benefits # Disability benefits from a plan paid for by the employer (§ 105(a)), # on Form 1040 line 1h until minimum retirement age, then line 5. # Benefits from a plan the recipient paid for are excluded (§ 104(a)(3)). @@ -35,6 +39,9 @@ values: - estate_income - taxable_unemployment_compensation - taxable_social_security + # Tier 1 railroad retirement benefits, which section 86 taxes together + # with Social Security (IRC 86(d)(1)(B); Form 1040 line 6b). + - taxable_railroad_social_security_equivalent_benefits - illicit_income - taxable_retirement_distributions - taxable_roth_conversions @@ -56,6 +63,10 @@ metadata: reference: - title: 26 U.S. Code § 61 - Gross income defined href: https://www.law.cornell.edu/uscode/text/26/61 + - title: 26 U.S. Code § 72(r) - Certain railroad retirement benefits treated as received under employer plans + href: https://www.law.cornell.edu/uscode/text/26/72#r + - title: 26 U.S. Code § 86(d) - Social security benefit + href: https://www.law.cornell.edu/uscode/text/26/86#d - title: 26 U.S. Code § 129 - Dependent care assistance programs href: https://www.law.cornell.edu/uscode/text/26/129 - title: 26 U.S. Code § 111 - Recovery of tax benefit items diff --git a/policyengine_us/parameters/gov/ssa/ssi/income/sources/unearned.yaml b/policyengine_us/parameters/gov/ssa/ssi/income/sources/unearned.yaml index 9492706d3be..f71c3c18244 100644 --- a/policyengine_us/parameters/gov/ssa/ssi/income/sources/unearned.yaml +++ b/policyengine_us/parameters/gov/ssa/ssi/income/sources/unearned.yaml @@ -4,9 +4,9 @@ values: # (a) Annuities, pensions, and other periodic payments. - pension_income - social_security - # NOTE: Household net income, SNAP, HUD annual income and other program - # income lists count social_security and ssi but not railroad_benefits, so - # an SSI recipient's railroad annuity drops out of them. + # NOTE: SNAP, HUD annual income and other program income lists count + # social_security and ssi but not railroad_benefits, so an SSI recipient's + # railroad annuity drops out of them. # https://github.com/PolicyEngine/policyengine-us/issues/10551 - railroad_benefits - disability_benefits diff --git a/policyengine_us/parameters/gov/states/az/tax/income/subtractions/subtractions.yaml b/policyengine_us/parameters/gov/states/az/tax/income/subtractions/subtractions.yaml index 6e0dbfd05a1..da2adf2d9da 100644 --- a/policyengine_us/parameters/gov/states/az/tax/income/subtractions/subtractions.yaml +++ b/policyengine_us/parameters/gov/states/az/tax/income/subtractions/subtractions.yaml @@ -12,6 +12,7 @@ values: - az_adoption_expense_subtraction # Other subtractions - Item D - az_families_tax_rebate_subtraction # Line 36, Item U - az_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) 2025-01-01: - us_govt_interest # Line 28 - az_public_pension_exclusion # Line 29a @@ -29,6 +30,7 @@ values: - auto_loan_interest_deduction # MCTCP worksheet Line 4 (Sch 1-A Line 30) - additional_senior_deduction # MCTCP worksheet Line 5 (Sch 1-A Line 37) - az_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) # HB 4168 (2026): auto loan interest sunsets after TY2025 (43-1022(36)); # adds child & dependent care and IRC 530A distribution subtractions. 2026-01-01: @@ -48,6 +50,7 @@ values: - az_dependent_care_expense_subtraction # 43-1022(34) - az_530a_distribution_subtraction # 43-1022(33) - az_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) metadata: unit: list period: year @@ -71,3 +74,5 @@ metadata: href: https://www.azleg.gov/ars/43/01022.htm - title: Arizona HB 4168 (2026), Sec. 15, amending A.R.S. 43-1022(31)-(36) href: https://www.azleg.gov/legtext/57leg/2R/bills/HB4168H.pdf#page=28 + - title: 45 U.S. Code § 231m(a) - Railroad Retirement Act annuities are exempt from state tax + href: https://www.law.cornell.edu/uscode/text/45/231m#a diff --git a/policyengine_us/parameters/gov/states/ca/tax/income/agi/subtractions.yaml b/policyengine_us/parameters/gov/states/ca/tax/income/agi/subtractions.yaml index 13260aaa4b0..d21470f0925 100644 --- a/policyengine_us/parameters/gov/states/ca/tax/income/agi/subtractions.yaml +++ b/policyengine_us/parameters/gov/states/ca/tax/income/agi/subtractions.yaml @@ -6,6 +6,7 @@ values: - us_govt_interest - salt_refund_income - ca_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) 2025-01-01: - taxable_social_security - taxable_unemployment_compensation @@ -13,6 +14,7 @@ values: - salt_refund_income - ca_military_retirement_exclusion - ca_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) metadata: reference: @@ -24,6 +26,8 @@ metadata: href: https://www.ftb.ca.gov/forms/2024/2024-540-ca-instructions.html - title: 2025 California Form 540 Schedule CA instructions href: https://www.ftb.ca.gov/forms/2025/2025-540-ca-instructions.html + - title: 45 U.S. Code § 231m(a) - Railroad Retirement Act annuities are exempt from state tax + href: https://www.law.cornell.edu/uscode/text/45/231m#a period: year unit: list label: California AGI subtractions diff --git a/policyengine_us/parameters/gov/states/co/tax/income/subtractions/subtractions.yaml b/policyengine_us/parameters/gov/states/co/tax/income/subtractions/subtractions.yaml index 3d10afd68fa..2461b917464 100644 --- a/policyengine_us/parameters/gov/states/co/tax/income/subtractions/subtractions.yaml +++ b/policyengine_us/parameters/gov/states/co/tax/income/subtractions/subtractions.yaml @@ -8,6 +8,7 @@ values: - co_collegeinvest_subtraction - co_charitable_contribution_subtraction - co_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) # Colorado Agricultural Land Capital Gain Subtraction # Qualified Reservation Income # PERA/DPSRS Subtraction, for PERA contributions made in 1984–1986 or DPSRS contributions made in 1986 @@ -29,6 +30,7 @@ values: - co_charitable_contribution_subtraction - co_able_contribution_subtraction - co_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) # Colorado Agricultural Land Capital Gain Subtraction # Qualified Reservation Income # PERA/DPSRS Subtraction, for PERA contributions made in 1984–1986 or DPSRS contributions made in 1986 @@ -58,3 +60,5 @@ metadata: href: https://tax.colorado.gov/sites/tax/files/documents/DR0104_book_2024.pdf#page=18 - title: 2025 Colorado Individual Tax Filing Guide 104 Book href: https://tax.colorado.gov/sites/tax/files/documents/Book104_2025.pdf#page=18 + - title: 45 U.S. Code § 231m(a) - Railroad Retirement Act annuities are exempt from state tax + href: https://www.law.cornell.edu/uscode/text/45/231m#a diff --git a/policyengine_us/parameters/gov/states/ct/tax/income/subtractions/subtractions.yaml b/policyengine_us/parameters/gov/states/ct/tax/income/subtractions/subtractions.yaml index 2cb8935c118..cacc25579b5 100644 --- a/policyengine_us/parameters/gov/states/ct/tax/income/subtractions/subtractions.yaml +++ b/policyengine_us/parameters/gov/states/ct/tax/income/subtractions/subtractions.yaml @@ -8,6 +8,7 @@ values: - ct_pension_annuity_subtraction #ln 48b - salt_refund_income #ln 38 - ct_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) metadata: unit: list @@ -26,3 +27,5 @@ metadata: href: https://portal.ct.gov/-/media/drs/forms/2024/income/2024-ct-1040-instructions_1224.pdf#page=8 - title: 2025 Form CT-1040 Connecticut Resident Income Tax Return Instructions, Lines 39,41,44,48b href: https://portal.ct.gov/-/media/drs/forms/2025/income/2025-ct-1040-instructions_1225.pdf#page=8 + - title: 45 U.S. Code § 231m(a) - Railroad Retirement Act annuities are exempt from state tax + href: https://www.law.cornell.edu/uscode/text/45/231m#a diff --git a/policyengine_us/parameters/gov/states/dc/tax/income/subtractions/sources.yaml b/policyengine_us/parameters/gov/states/dc/tax/income/subtractions/sources.yaml index 58b685cfc8a..9f182f4070c 100644 --- a/policyengine_us/parameters/gov/states/dc/tax/income/subtractions/sources.yaml +++ b/policyengine_us/parameters/gov/states/dc/tax/income/subtractions/sources.yaml @@ -13,6 +13,8 @@ metadata: href: https://otr.cfo.dc.gov/sites/default/files/dc/sites/otr/publication/attachments/2023_D40_Book_Final_012324.pdf#page=34 - title: 2024 DC Form D-40 Booklet href: https://otr.cfo.dc.gov/sites/default/files/dc/sites/otr/publication/attachments/2024_D40_Booklet_011525.pdf#page=34 + - title: 45 U.S. Code § 231m(a) - Railroad Retirement Act annuities are exempt from state tax + href: https://www.law.cornell.edu/uscode/text/45/231m#a values: 2021-01-01: - taxable_social_security @@ -23,3 +25,4 @@ values: - salt_refund_income - dc_survivor_benefits_subtraction - dc_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) diff --git a/policyengine_us/parameters/gov/states/de/tax/income/subtractions/subtractions.yaml b/policyengine_us/parameters/gov/states/de/tax/income/subtractions/subtractions.yaml index c3d94c1174c..d307981bf5d 100644 --- a/policyengine_us/parameters/gov/states/de/tax/income/subtractions/subtractions.yaml +++ b/policyengine_us/parameters/gov/states/de/tax/income/subtractions/subtractions.yaml @@ -10,6 +10,7 @@ values: - taxable_unemployment_compensation # (10) - salt_refund_income # 30 Del. C. § 1106(b)(9) / PIT-RES Line 7 - de_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) # The elderly or disabled income exclusion is computed separately # - de_elderly_or_disabled_income_exclusion # (2) 2022-01-01: @@ -19,6 +20,7 @@ values: - de_529_plan_subtraction # HB 145 - salt_refund_income # 30 Del. C. § 1106(b)(9) / PIT-RES Line 7 - de_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) metadata: unit: list @@ -31,3 +33,5 @@ metadata: href: https://legis.delaware.gov/BillDetail/79061 - title: 2021 Delaware Resident Individual Income Tax Return (PIT-RES) Instructions, Line 7 href: https://revenuefiles.delaware.gov/2021/PIT-RES_TY21_2021-01_Instructions.pdf + - title: 45 U.S. Code § 231m(a) - Railroad Retirement Act annuities are exempt from state tax + href: https://www.law.cornell.edu/uscode/text/45/231m#a diff --git a/policyengine_us/parameters/gov/states/ga/tax/income/subtractions/subtractions.yaml b/policyengine_us/parameters/gov/states/ga/tax/income/subtractions/subtractions.yaml index a26405360c9..7d18f479b84 100644 --- a/policyengine_us/parameters/gov/states/ga/tax/income/subtractions/subtractions.yaml +++ b/policyengine_us/parameters/gov/states/ga/tax/income/subtractions/subtractions.yaml @@ -11,6 +11,7 @@ values: - taxable_social_security - ga_investment_in_529_plan_deduction - ga_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) 2022-01-01: - us_govt_interest - ga_retirement_exclusion @@ -18,6 +19,7 @@ values: - taxable_social_security - ga_investment_in_529_plan_deduction - ga_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) 2026-01-01: - us_govt_interest - ga_retirement_exclusion @@ -27,6 +29,7 @@ values: - ga_qualified_overtime_exclusion - ga_cash_tip_exclusion - ga_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) 2029-01-01: - us_govt_interest - ga_retirement_exclusion @@ -34,6 +37,7 @@ values: - taxable_social_security - ga_investment_in_529_plan_deduction - ga_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) metadata: unit: list @@ -52,3 +56,5 @@ metadata: href: https://www.legis.ga.gov/api/legislation/document/20252026/249080#page=6 - title: Georgia HB463 (2025-2026), Section 2-4 adding O.C.G.A. § 48-7-27(a)(17) - cash tips exclusion href: https://www.legis.ga.gov/api/legislation/document/20252026/249080#page=7 + - title: 45 U.S. Code § 231m(a) - Railroad Retirement Act annuities are exempt from state tax + href: https://www.law.cornell.edu/uscode/text/45/231m#a diff --git a/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/subtractions.yaml b/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/subtractions.yaml index 7201735126e..c01c5f4543a 100644 --- a/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/subtractions.yaml +++ b/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/subtractions.yaml @@ -8,6 +8,7 @@ values: - us_govt_interest # Line 18 - student_loan_interest_ald # Line 18 - hi_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) 2025-01-01: - taxable_pension_income # Line 13 - military_retirement_income # Line 13 @@ -16,6 +17,7 @@ values: - us_govt_interest # Line 18 - hi_student_loan_interest_subtraction # Line 18 - hi_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) metadata: @@ -31,3 +33,5 @@ metadata: href: https://law.justia.com/codes/hawaii/2022/title-14/chapter-235/section-235-7/ - title: 2025 Hawaii N-11 Instructions Subtractions href: https://files.hawaii.gov/tax/forms/current/n11ins.pdf#page=13 + - title: 45 U.S. Code § 231m(a) - Railroad Retirement Act annuities are exempt from state tax + href: https://www.law.cornell.edu/uscode/text/45/231m#a diff --git a/policyengine_us/parameters/gov/states/ia/tax/income/taxable_income/subtractions.yaml b/policyengine_us/parameters/gov/states/ia/tax/income/taxable_income/subtractions.yaml index 521a4ba10a8..ee8d201bc3f 100644 --- a/policyengine_us/parameters/gov/states/ia/tax/income/taxable_income/subtractions.yaml +++ b/policyengine_us/parameters/gov/states/ia/tax/income/taxable_income/subtractions.yaml @@ -21,6 +21,7 @@ values: - ia_disability_income_exclusion # Schedule 1, line 11, column B, and line 19 - ia_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) metadata: unit: list @@ -35,3 +36,5 @@ metadata: href: https://revenue.iowa.gov/media/4096/download?inline=#page=1 - title: 2024 IA1040 Iowa Income Tax Return Instruction href: https://revenue.iowa.gov/media/4152/download?inline#page=61 + - title: 45 U.S. Code § 231m(a) - Railroad Retirement Act annuities are exempt from state tax + href: https://www.law.cornell.edu/uscode/text/45/231m#a diff --git a/policyengine_us/parameters/gov/states/id/tax/income/subtractions/subtractions.yaml b/policyengine_us/parameters/gov/states/id/tax/income/subtractions/subtractions.yaml index 1051f9780fb..3e22bb1f87e 100644 --- a/policyengine_us/parameters/gov/states/id/tax/income/subtractions/subtractions.yaml +++ b/policyengine_us/parameters/gov/states/id/tax/income/subtractions/subtractions.yaml @@ -13,6 +13,7 @@ values: - id_529_plan_subtraction # Idaho Code 63-3022(n) - id_health_insurance_premiums_subtraction # Idaho Code 63-3022P - id_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) 2025-01-01: - salt_refund_income # (2) - us_govt_interest # (3)(a) @@ -27,6 +28,7 @@ values: - id_529_plan_subtraction # Idaho Code 63-3022(n) - id_health_insurance_premiums_subtraction # Idaho Code 63-3022P - id_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) metadata: @@ -44,3 +46,5 @@ metadata: href: https://legislature.idaho.gov/statutesrules/idstat/Title63/T63CH30/SECT63-3022P/ - title: Section 35.01.01.254 - (5) / (3) (a) href: https://regulations.justia.com/states/idaho/35/35-01-01/section-35-01-01-254/ + - title: 45 U.S. Code § 231m(a) - Railroad Retirement Act annuities are exempt from state tax + href: https://www.law.cornell.edu/uscode/text/45/231m#a diff --git a/policyengine_us/parameters/gov/states/il/tax/income/base/subtractions.yaml b/policyengine_us/parameters/gov/states/il/tax/income/base/subtractions.yaml index 381f7ae3250..338d7ac807c 100644 --- a/policyengine_us/parameters/gov/states/il/tax/income/base/subtractions.yaml +++ b/policyengine_us/parameters/gov/states/il/tax/income/base/subtractions.yaml @@ -11,6 +11,7 @@ values: - il_529_plan_subtraction - salt_refund_income - il_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) metadata: unit: list period: year @@ -26,3 +27,5 @@ metadata: href: https://tax.illinois.gov/content/dam/soi/en/web/tax/forms/incometax/documents/currentyear/individual/il-1040-schedule-m-instr.pdf#page=3 - title: Illinois Department of Revenue Publication 120, Retirement Income href: https://tax.illinois.gov/content/dam/soi/en/web/tax/research/publications/pubs/documents/pub-120.pdf#page=2 + - title: 45 U.S. Code § 231m(a) - Railroad Retirement Act annuities are exempt from state tax + href: https://www.law.cornell.edu/uscode/text/45/231m#a diff --git a/policyengine_us/parameters/gov/states/in/tax/income/deductions/deductions.yaml b/policyengine_us/parameters/gov/states/in/tax/income/deductions/deductions.yaml index 92a8269d3e8..bd7aaf6a911 100644 --- a/policyengine_us/parameters/gov/states/in/tax/income/deductions/deductions.yaml +++ b/policyengine_us/parameters/gov/states/in/tax/income/deductions/deductions.yaml @@ -5,7 +5,8 @@ values: - in_homeowners_property_tax_deduction - salt_refund_income - us_govt_interest - - tax_unit_taxable_social_security # includes railroad retirement benefits + - tax_unit_taxable_social_security + - taxable_railroad_benefits # 45 U.S.C. 231m(a) - in_military_service_deduction - in_nonpublic_school_deduction - in_nol @@ -27,7 +28,9 @@ values: # IC 6-8-5-1 - us_govt_interest # US Code 26-A-1-B-II-§86 - - tax_unit_taxable_social_security # includes railroad retirement benefits + - tax_unit_taxable_social_security + # 45 U.S.C. 231m(a) + - taxable_railroad_benefits # IC 6-3-2-4-(a)(1) - in_military_service_deduction # IC 6-3-2-22-(d)(1) @@ -59,7 +62,9 @@ values: # IC 6-8-5-1 - us_govt_interest # US Code 26-A-1-B-II-§86 - - tax_unit_taxable_social_security # includes railroad retirement benefits + - tax_unit_taxable_social_security + # 45 U.S.C. 231m(a) + - taxable_railroad_benefits # IC 6-3-2-4-(a)(1) - in_military_service_deduction # IC 6-3-2-22-(d)(1) @@ -93,7 +98,9 @@ values: # IC 6-8-5-1 - us_govt_interest # US Code 26-A-1-B-II-§86 - - tax_unit_taxable_social_security # includes railroad retirement benefits + - tax_unit_taxable_social_security + # 45 U.S.C. 231m(a) + - taxable_railroad_benefits # IC 6-3-2-4-(a)(1) - in_military_service_deduction # IC 6-3-2-22-(d)(1) @@ -133,7 +140,9 @@ values: # IC 6-8-5-1 - us_govt_interest # US Code 26-A-1-B-II-§86 - - tax_unit_taxable_social_security # includes railroad retirement benefits + - tax_unit_taxable_social_security + # 45 U.S.C. 231m(a) + - taxable_railroad_benefits # IC 6-3-2-4-(a)(1) - in_military_service_deduction # IC 6-3-2-22-(d)(1) @@ -170,3 +179,5 @@ metadata: href: https://www.law.cornell.edu/uscode/text - title: "2025 IT-40 Income Tax Form Instruction Booklet #page 19,23" href: https://forms.in.gov/Download.aspx?id=16915#page=19 + - title: 45 U.S. Code § 231m(a) - Railroad Retirement Act annuities are exempt from state tax + href: https://www.law.cornell.edu/uscode/text/45/231m#a diff --git a/policyengine_us/parameters/gov/states/ky/tax/income/subtractions.yaml b/policyengine_us/parameters/gov/states/ky/tax/income/subtractions.yaml index 58be361ebf2..073f59ae7e8 100644 --- a/policyengine_us/parameters/gov/states/ky/tax/income/subtractions.yaml +++ b/policyengine_us/parameters/gov/states/ky/tax/income/subtractions.yaml @@ -9,6 +9,7 @@ values: # - Kentucky depreciation - military_service_income - ky_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) metadata: unit: list reference: @@ -28,3 +29,5 @@ metadata: href: https://revenue.ky.gov/Forms/740%20Packet%20Instructions%205-9-23.pdf#page=23 - title: 2021 Kentucky Individual Income Tax Forms href: https://taxsim.nber.org/historical_state_tax_forms/KY/2021/Form%20740%20Packet%20Instructions-2021.pdf#page=27 + - title: 45 U.S. Code § 231m(a) - Railroad Retirement Act annuities are exempt from state tax + href: https://www.law.cornell.edu/uscode/text/45/231m#a diff --git a/policyengine_us/parameters/gov/states/la/tax/income/exempt_income/sources.yaml b/policyengine_us/parameters/gov/states/la/tax/income/exempt_income/sources.yaml index 1e6ce5bfda9..df5b1315aef 100644 --- a/policyengine_us/parameters/gov/states/la/tax/income/exempt_income/sources.yaml +++ b/policyengine_us/parameters/gov/states/la/tax/income/exempt_income/sources.yaml @@ -13,6 +13,7 @@ values: - taxable_public_pension_income - la_retirement_exemption_person # 06E - taxable_social_security # 07E + - taxable_railroad_benefits # 45 U.S.C. 231m(a) # Native American Income 08E # START Savings Program Contributions 09E - la_military_pay_exclusion # 10E @@ -51,3 +52,5 @@ metadata: href: https://www.legis.la.gov/legis/Law.aspx?d=102133 - title: R.S. 47:293(9)(e) href: https://www.legis.la.gov/legis/Law.aspx?d=101760 + - title: 45 U.S. Code § 231m(a) - Railroad Retirement Act annuities are exempt from state tax + href: https://www.law.cornell.edu/uscode/text/45/231m#a 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 13e8f6ca82f..4d273a3ae60 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 @@ -10,6 +10,7 @@ values: - md_529_deduction - salt_refund_income - md_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) 2022-01-01: - us_govt_interest - md_dependent_care_subtraction @@ -21,6 +22,7 @@ values: - md_529_deduction - salt_refund_income - md_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) metadata: reference: @@ -33,6 +35,8 @@ metadata: # Bill introducing the hundred year subtraction - title: House Bill 186 href: https://mgaleg.maryland.gov/mgawebsite/Legislation/Details/hb0186?ys=2022RS + - title: 45 U.S. Code § 231m(a) - Railroad Retirement Act annuities are exempt from state tax + href: https://www.law.cornell.edu/uscode/text/45/231m#a period: year unit: list label: Maryland adjusted gross income subtractions 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 b9d9ff8abd2..9f23193500d 100644 --- a/policyengine_us/parameters/gov/states/mi/tax/income/subtractions.yaml +++ b/policyengine_us/parameters/gov/states/mi/tax/income/subtractions.yaml @@ -12,6 +12,7 @@ values: - mi_529_deduction - salt_refund_income # Line 16 - mi_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) 2026-01-01: - us_govt_interest # Line 10 - mi_military_retirement_subtraction # Line 11 @@ -27,6 +28,7 @@ values: - mi_qualified_overtime_deduction # Qualified overtime compensation (PA 24 of 2025), TY2026-2028 - salt_refund_income # Line 16 - mi_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) metadata: unit: list label: Michigan taxable income subtraction sources @@ -48,3 +50,5 @@ metadata: href: https://www.michigan.gov/taxes/-/media/Project/Websites/taxes/Forms/IIT/TY2024/MI-1040-Instructions.pdf#page=13 - title: Michigan Retirement and Pension Benefits href: https://web.archive.org/web/20250202214915/https://www.michigan.gov/taxes/iit/retirement-and-pension-benefits + - title: 45 U.S. Code § 231m(a) - Railroad Retirement Act annuities are exempt from state tax + href: https://www.law.cornell.edu/uscode/text/45/231m#a diff --git a/policyengine_us/parameters/gov/states/mn/tax/income/subtractions/sources.yaml b/policyengine_us/parameters/gov/states/mn/tax/income/subtractions/sources.yaml index 712d9eb223b..9fd99614706 100644 --- a/policyengine_us/parameters/gov/states/mn/tax/income/subtractions/sources.yaml +++ b/policyengine_us/parameters/gov/states/mn/tax/income/subtractions/sources.yaml @@ -15,6 +15,7 @@ values: - mn_529_contribution_subtraction - salt_refund_income - mn_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) 2023-01-01: - mn_charity_subtraction - mn_social_security_subtraction @@ -27,6 +28,7 @@ values: - mn_529_contribution_subtraction - salt_refund_income - mn_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) metadata: @@ -46,3 +48,5 @@ metadata: href: https://www.revenue.state.mn.us/sites/default/files/2025-11/m1m-24.pdf - title: 2025 Minnesota Schedule M1M, Income Additions and Subtractions href: https://www.revenue.state.mn.us/sites/default/files/2026-07/m1m-25.pdf#page=1 + - title: 45 U.S. Code § 231m(a) - Railroad Retirement Act annuities are exempt from state tax + href: https://www.law.cornell.edu/uscode/text/45/231m#a 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 c4c63f69efa..5edde528d1d 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 @@ -7,6 +7,7 @@ values: - salt_refund_income # 143.121 RSMo / Form MO-A Line 9 - military_retirement_pay # 143.121.3(12) RSMo / Form MO-A Line 10 - mo_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) 2025-01-01: - mo_qualified_health_insurance_premiums - mo_capital_gains_subtraction_person @@ -15,6 +16,7 @@ values: - salt_refund_income # 143.121 RSMo / Form MO-A Line 9 - military_retirement_pay # 143.121.3(12) RSMo / Form MO-A Line 10 - mo_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) metadata: @@ -28,3 +30,5 @@ metadata: href: https://www.revisor.mo.gov/main/OneSection.aspx?section=143.121&bid=57543 - title: 2025 MO-1040 Book - Individual Income Tax Long Form href: https://dor.mo.gov/forms/MO-1040%20Instructions_2025.pdf#page=12 + - title: 45 U.S. Code § 231m(a) - Railroad Retirement Act annuities are exempt from state tax + href: https://www.law.cornell.edu/uscode/text/45/231m#a diff --git a/policyengine_us/parameters/gov/states/mt/tax/income/subtractions/subtractions.yaml b/policyengine_us/parameters/gov/states/mt/tax/income/subtractions/subtractions.yaml index a2c38938f6a..199bb39679c 100644 --- a/policyengine_us/parameters/gov/states/mt/tax/income/subtractions/subtractions.yaml +++ b/policyengine_us/parameters/gov/states/mt/tax/income/subtractions/subtractions.yaml @@ -4,6 +4,7 @@ values: - mt_disability_income_exclusion_person - mt_unemployment_compensation_subtraction - mt_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) 2021-01-01: - mt_interest_exemption_person - us_govt_interest_person @@ -14,6 +15,7 @@ values: - mt_unemployment_compensation_subtraction - salt_refund_income # Form 2 Subtractions Schedule - mt_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) 2024-01-01: # MCA 15-30-2120(3)(n) allows exempting up to 50% of military retirement pay # (capped at MT earned income, (3)(n)(i)) and up to 50% of military survivor @@ -26,6 +28,7 @@ values: # after December 31, 2023 (section 70(1)). - salt_refund_income # MCA 15-30-2120 / Form 2 Subtractions Schedule - mt_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) 2025-01-01: # Section 15-30-2120 starts from federal taxable income, so federal # OBBBA Schedule 1-A deductions (enhanced senior deduction, qualified @@ -39,6 +42,7 @@ values: - mt_federal_obbba_subtraction - salt_refund_income # MCA 15-30-2120 / Form 2 Subtractions Schedule - mt_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) metadata: unit: list period: year @@ -54,6 +58,8 @@ metadata: href: https://leg.mt.gov/bills/mca/title_0150/chapter_0300/part_0210/section_0100/0150-0300-0210-0100.html - title: 2025 Montana Form 2 Instructions href: https://revenuefiles.mt.gov/files/Forms/Montana-Individual-Income-Tax-Return-Form-2-Instructions/2025_Montana_Individual_Income_Tax_Return_Form_2_Instructions.pdf#page=21 + - title: 45 U.S. Code § 231m(a) - Railroad Retirement Act annuities are exempt from state tax + href: https://www.law.cornell.edu/uscode/text/45/231m#a - title: "2021 Montana Session Laws, chapter 503 (SB 399), section 65 (repealer) and section 70 (effective dates) #page 68,70" href: https://archive.legmt.gov/content/Publications/sales/2021SessionLaws_V3_ERRATA-VERSION.pdf#page=68 - title: 2024 Montana Form 2 instructions, repealed deductions diff --git a/policyengine_us/parameters/gov/states/nc/tax/income/deductions/deductions.yaml b/policyengine_us/parameters/gov/states/nc/tax/income/deductions/deductions.yaml index d40b99c6571..364260b81ef 100644 --- a/policyengine_us/parameters/gov/states/nc/tax/income/deductions/deductions.yaml +++ b/policyengine_us/parameters/gov/states/nc/tax/income/deductions/deductions.yaml @@ -5,6 +5,7 @@ values: - nc_standard_or_itemized_deductions - nc_child_deduction - taxable_social_security + - taxable_railroad_benefits # 45 U.S.C. 231m(a) - salt_refund_income - nc_other_subtractions 2022-01-01: @@ -12,6 +13,7 @@ values: - nc_standard_or_itemized_deductions - nc_child_deduction - taxable_social_security + - taxable_railroad_benefits # 45 U.S.C. 231m(a) - nc_military_retirement_deduction - salt_refund_income - nc_other_subtractions @@ -30,3 +32,5 @@ metadata: href: https://www.ncdor.gov/2022-d-400-schedule-s-web-fill-version/open#page=17 - title: 2021 D-400 Schedule S href: https://www.ncdor.gov/2021-d-400-schedule-s-north-carolina-supplemental-schedule-web/open + - title: 45 U.S. Code § 231m(a) - Railroad Retirement Act annuities are exempt from state tax + href: https://www.law.cornell.edu/uscode/text/45/231m#a 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 09aaf7f05b6..d77d348bb6f 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 @@ -9,6 +9,7 @@ values: - nd_529_deduction - nd_stillborn_subtraction - nd_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) 2022-01-01: - us_govt_interest @@ -19,6 +20,7 @@ values: - nd_529_deduction - nd_stillborn_subtraction - nd_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) 2023-01-01: - us_govt_interest @@ -30,6 +32,7 @@ values: - nd_529_deduction - nd_stillborn_subtraction - nd_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) metadata: @@ -55,3 +58,5 @@ metadata: href: https://www.tax.nd.gov/sites/www/files/documents/forms/individual/2025-iit/2025-individual-income-tax-booklet.pdf#page=14 - title: North Dakota Century Code Title 57 - Taxation Chapter 57-38 - Income Tax, 57-38-30.3 (2) href: https://law.justia.com/codes/north-dakota/2022/title-57/chapter-57-38/ + - title: 45 U.S. Code § 231m(a) - Railroad Retirement Act annuities are exempt from state tax + href: https://www.law.cornell.edu/uscode/text/45/231m#a 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 1d364997dd8..834688eab96 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 @@ -7,6 +7,7 @@ values: - ne_529_deduction - salt_refund_income - ne_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) 2024-01-01: - ne_social_security_subtraction - taxable_public_pension_income @@ -15,6 +16,7 @@ values: - ne_529_deduction - salt_refund_income - ne_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) metadata: unit: list @@ -25,3 +27,5 @@ metadata: href: https://www.nebraskalegislature.gov/laws/statutes.php?statute=77-2716 - title: 2025 Nebraska Individual Income Tax Booklet, Schedule I href: https://revenue.nebraska.gov/sites/default/files/doc/tax-forms/2025/f_Individual_Income_Tax_Booklet.pdf#page=26 + - title: 45 U.S. Code § 231m(a) - Railroad Retirement Act annuities are exempt from state tax + href: https://www.law.cornell.edu/uscode/text/45/231m#a diff --git a/policyengine_us/parameters/gov/states/nm/tax/income/other_deductions_and_exemptions.yaml b/policyengine_us/parameters/gov/states/nm/tax/income/other_deductions_and_exemptions.yaml index a1db98438f2..779d6d0564a 100644 --- a/policyengine_us/parameters/gov/states/nm/tax/income/other_deductions_and_exemptions.yaml +++ b/policyengine_us/parameters/gov/states/nm/tax/income/other_deductions_and_exemptions.yaml @@ -5,6 +5,7 @@ values: - nm_529_plan_deduction - military_service_income - salt_refund_income # Form PIT-ADJ + - taxable_railroad_benefits # 45 U.S.C. 231m(a) - nm_other_subtractions metadata: unit: list @@ -22,3 +23,5 @@ metadata: href: https://www.srca.nm.gov/parts/title03/03.003.0001.html - title: New Mexico Income Tax Act, Chapter 7 - Taxation, 7-2-32. Deduction; payments into education trust fund. href: https://nmonesource.com/nmos/nmsa/en/item/4340/index.do#!fragment/zoupio-_Toc141270056/BQCwhgziBcwMYgK4DsDWszIQewE4BUBTADwBdoAvbRABwEtsBaAfX2zgEYAWDgJgHYADIICsANgCUAGmTZShCAEVEhXAE9oAck1SIhMLgTLVG7bv2GQAZTykAQhoBKAUQAyzgGoBBAHIBhZylSMAAjaFJ2CQkgA + - title: 45 U.S. Code § 231m(a) - Railroad Retirement Act annuities are exempt from state tax + href: https://www.law.cornell.edu/uscode/text/45/231m#a diff --git a/policyengine_us/parameters/gov/states/ny/tax/income/agi/subtractions/sources.yaml b/policyengine_us/parameters/gov/states/ny/tax/income/agi/subtractions/sources.yaml index 30ced102407..56a74dab4f8 100644 --- a/policyengine_us/parameters/gov/states/ny/tax/income/agi/subtractions/sources.yaml +++ b/policyengine_us/parameters/gov/states/ny/tax/income/agi/subtractions/sources.yaml @@ -14,6 +14,7 @@ values: - taxable_public_pension_income - military_retirement_income - ny_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) metadata: unit: list period: year @@ -33,3 +34,5 @@ metadata: href: https://newyork.public.law/laws/n.y._tax_law_section_612 - title: N.Y. Comp. Codes R. & Regs. tit. 20 § 112.3 href: https://www.law.cornell.edu/regulations/new-york/20-NYCRR-112.3 + - title: 45 U.S. Code § 231m(a) - Railroad Retirement Act annuities are exempt from state tax + href: https://www.law.cornell.edu/uscode/text/45/231m#a diff --git a/policyengine_us/parameters/gov/states/oh/tax/income/deductions/deductions.yaml b/policyengine_us/parameters/gov/states/oh/tax/income/deductions/deductions.yaml index ef7a46700a8..61013a97728 100644 --- a/policyengine_us/parameters/gov/states/oh/tax/income/deductions/deductions.yaml +++ b/policyengine_us/parameters/gov/states/oh/tax/income/deductions/deductions.yaml @@ -3,6 +3,7 @@ values: 2021-01-01: - oh_section_179_expense_add_back - taxable_social_security + - taxable_railroad_benefits # 45 U.S.C. 231m(a) - oh_uniformed_services_retirement_income_deduction - oh_529_plan_deduction_person - pell_grant @@ -33,3 +34,5 @@ metadata: href: https://dam.assets.ohio.gov/image/upload/v1735920104/tax.ohio.gov/forms/ohio_individual/individual/2024/it1040-booklet.pdf#page=19 - title: Ohio Income Tax Form 2025 Instructions (Page 20) href: https://dam.assets.ohio.gov/image/upload/v1767095693/tax.ohio.gov/forms/ohio_individual/individual/2025/it1040-booklet.pdf#page=20 + - title: 45 U.S. Code § 231m(a) - Railroad Retirement Act annuities are exempt from state tax + href: https://www.law.cornell.edu/uscode/text/45/231m#a diff --git a/policyengine_us/parameters/gov/states/ok/tax/income/agi/subtractions/subtractions.yaml b/policyengine_us/parameters/gov/states/ok/tax/income/agi/subtractions/subtractions.yaml index 1b2dada1d70..99a6281d9f1 100644 --- a/policyengine_us/parameters/gov/states/ok/tax/income/agi/subtractions/subtractions.yaml +++ b/policyengine_us/parameters/gov/states/ok/tax/income/agi/subtractions/subtractions.yaml @@ -8,6 +8,7 @@ values: - ok_529_plan_deduction - salt_refund_income - ok_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) metadata: unit: list @@ -24,3 +25,5 @@ metadata: href: https://oklahoma.gov/content/dam/ok/en/tax/documents/forms/individuals/past-year/2024/511-Pkt-2024.pdf#page=16 - title: 2025 Form 511 instructions href: https://oklahoma.gov/content/dam/ok/en/tax/documents/forms/individuals/current/511-Pkt.pdf#page=16 + - title: 45 U.S. Code § 231m(a) - Railroad Retirement Act annuities are exempt from state tax + href: https://www.law.cornell.edu/uscode/text/45/231m#a diff --git a/policyengine_us/parameters/gov/states/or/tax/income/subtractions/subtractions.yaml b/policyengine_us/parameters/gov/states/or/tax/income/subtractions/subtractions.yaml index bd8f2fc935a..e244fde3700 100644 --- a/policyengine_us/parameters/gov/states/or/tax/income/subtractions/subtractions.yaml +++ b/policyengine_us/parameters/gov/states/or/tax/income/subtractions/subtractions.yaml @@ -20,6 +20,8 @@ metadata: href: https://www.oregon.gov/dor/forms/FormsPubs/form-or-40-inst_101-040-1_2023.pdf#page=13 - title: 2025 Oregon Income Tax Form OR-40 Instructions href: https://www.oregon.gov/dor/forms/FormsPubs/form-or-40-inst_101-040-1_2025.pdf#page=14 + - title: 45 U.S. Code § 231m(a) - Railroad Retirement Act annuities are exempt from state tax + href: https://www.law.cornell.edu/uscode/text/45/231m#a values: 2020-01-01: - or_federal_tax_liability_subtraction @@ -28,3 +30,4 @@ values: - salt_refund_income - or_federal_pension_subtraction - or_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) diff --git a/policyengine_us/parameters/gov/states/pa/tax/income/nontaxable_income_sources.yaml b/policyengine_us/parameters/gov/states/pa/tax/income/nontaxable_income_sources.yaml index 4f20466d5ad..0d3981c4bda 100644 --- a/policyengine_us/parameters/gov/states/pa/tax/income/nontaxable_income_sources.yaml +++ b/policyengine_us/parameters/gov/states/pa/tax/income/nontaxable_income_sources.yaml @@ -6,6 +6,7 @@ values: - pa_nontaxable_roth_conversions - taxable_unemployment_compensation - taxable_social_security + - taxable_railroad_benefits # 45 U.S.C. 231m(a) - us_govt_interest - salt_refund_income - military_retirement_income @@ -23,3 +24,5 @@ metadata: href: https://www.pacodeandbulletin.gov/Display/pacode?file=/secure/pacode/data/061/chapter101/s101.6.html - title: "2024 Pennsylvania PIT Return Instructions (PA-40 IN), nontaxable sick pay and disability benefits and Code 3 disability distributions #page 8,13" href: https://www.pa.gov/content/dam/copapwp-pagov/en/revenue/documents/formsandpublications/formsforindividuals/pit/documents/2024/2024_pa-40in.pdf#page=8 + - title: 45 U.S. Code § 231m(a) - Railroad Retirement Act annuities are exempt from state tax + href: https://www.law.cornell.edu/uscode/text/45/231m#a diff --git a/policyengine_us/parameters/gov/states/pa/tax/property/property_tax_or_rent_rebate/benefit_income_rate.yaml b/policyengine_us/parameters/gov/states/pa/tax/property/property_tax_or_rent_rebate/benefit_income_rate.yaml index 6ccaa1fc0cd..01ddc05dfbe 100644 --- a/policyengine_us/parameters/gov/states/pa/tax/property/property_tax_or_rent_rebate/benefit_income_rate.yaml +++ b/policyengine_us/parameters/gov/states/pa/tax/property/property_tax_or_rent_rebate/benefit_income_rate.yaml @@ -1,4 +1,4 @@ -description: Pennsylvania counts this share of Social Security, Supplemental Security Income, State Supplementary Payment, and Railroad Retirement Tier 1 benefits under the Property Tax/Rent Rebate program. +description: Pennsylvania counts this share of Social Security, Supplemental Security Income, State Supplementary Payment, and Railroad Retirement Act benefits under the Property Tax/Rent Rebate program. values: 2025-01-01: 0.5 metadata: @@ -6,5 +6,7 @@ metadata: period: year label: Pennsylvania Property Tax/Rent Rebate benefit income rate reference: + - title: 'Taxpayer Relief Act (Act 1 of 2006, Special Session 1), section 1303, "Income" (2) and (3)(i)' + href: https://www.legis.state.pa.us/WU01/LI/LI/US/PDF/2006/1/0001..PDF#page=38 - title: "2025 PA-1000 Pennsylvania Property Tax or Rent Rebate Program Instruction Booklet, State Supplementary Payment recipients and Lines 4 and 5 #page 3,7" href: https://www.pa.gov/content/dam/copapwp-pagov/en/revenue/documents/formsandpublications/formsforindividuals/ptrr/documents/2025_pa-1000_inst.pdf#page=3 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 afb8cd1d13c..49df6b94c83 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 @@ -7,6 +7,7 @@ values: - ri_retirement_income_subtraction - us_govt_interest - ri_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) 2023-01-01: - ri_social_security_modification @@ -15,6 +16,7 @@ values: - us_govt_interest - ri_military_retirement_subtraction - ri_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) metadata: unit: list @@ -33,3 +35,5 @@ metadata: href: https://tax.ri.gov/sites/g/files/xkgbur541/files/2025-12/2025%201040R%20Instructions%20122025.pdf#page=9 - title: 2025 RI Schedule M - page 1 RI Modifications to Federal AGI href: https://tax.ri.gov/sites/g/files/xkgbur541/files/2026-01/2025%20RI%20Schedule%20M_w.pdf#page=1 + - title: 45 U.S. Code § 231m(a) - Railroad Retirement Act annuities are exempt from state tax + href: https://www.law.cornell.edu/uscode/text/45/231m#a diff --git a/policyengine_us/parameters/gov/states/sc/tax/income/subtractions/subtractions.yaml b/policyengine_us/parameters/gov/states/sc/tax/income/subtractions/subtractions.yaml index f8e3ee742e5..d48db927302 100644 --- a/policyengine_us/parameters/gov/states/sc/tax/income/subtractions/subtractions.yaml +++ b/policyengine_us/parameters/gov/states/sc/tax/income/subtractions/subtractions.yaml @@ -13,6 +13,7 @@ values: - sc_young_child_deduction - sc_dependent_exemption - sc_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) # - Out of state income/gain # - Volunteer deductions # - Active Trade or Business Income deduction @@ -36,6 +37,7 @@ values: - sc_dependent_exemption - sc_sciad - sc_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) metadata: unit: list @@ -60,3 +62,5 @@ metadata: href: https://dor.sc.gov/forms-site/Forms/IITPacket_2023.pdf#page=43 - title: SC 1040 tax form instructions (2024) href: https://dor.sc.gov/forms-site/Forms/SC1040Instr_2024.pdf#page=8 + - title: 45 U.S. Code § 231m(a) - Railroad Retirement Act annuities are exempt from state tax + href: https://www.law.cornell.edu/uscode/text/45/231m#a diff --git a/policyengine_us/parameters/gov/states/ut/tax/property/homeowner_renter_relief/nontaxable_income/sources.yaml b/policyengine_us/parameters/gov/states/ut/tax/property/homeowner_renter_relief/nontaxable_income/sources.yaml index 409a999269a..76565be0ed5 100644 --- a/policyengine_us/parameters/gov/states/ut/tax/property/homeowner_renter_relief/nontaxable_income/sources.yaml +++ b/policyengine_us/parameters/gov/states/ut/tax/property/homeowner_renter_relief/nontaxable_income/sources.yaml @@ -3,7 +3,6 @@ values: 2024-01-01: - alimony_income - child_support_received - - railroad_benefits - strike_benefits - tax_exempt_interest_income - tax_exempt_pension_income diff --git a/policyengine_us/parameters/gov/states/va/tax/income/subtractions/subtractions.yaml b/policyengine_us/parameters/gov/states/va/tax/income/subtractions/subtractions.yaml index 32dd8cb982d..19c30a6ff86 100644 --- a/policyengine_us/parameters/gov/states/va/tax/income/subtractions/subtractions.yaml +++ b/policyengine_us/parameters/gov/states/va/tax/income/subtractions/subtractions.yaml @@ -6,7 +6,10 @@ values: - us_govt_interest_person - va_military_basic_pay_subtraction_person # Special instructions for members of the military - va_disability_income_subtraction_person # Certification Number 5 - - railroad_benefits # Certification Number 22 + # Tier 1 railroad benefits in federal AGI (Form 760 Line 5) and the tier 2, + # vested dual and other Railroad Retirement Act benefits in it (Schedule + # ADJ code 22); 45 U.S.C. 231m(a). + - taxable_railroad_benefits - va_national_guard_subtraction_person # Certification Number 28 - taxable_unemployment_compensation # Certification Number 37; Va. Code § 58.1-322.02(17) - va_federal_state_employees_subtraction_person # Certification Number 39 @@ -23,8 +26,8 @@ metadata: reference: - title: 2024 Form 760 Instructions, What's New href: https://www.tax.virginia.gov/sites/default/files/vatax-pdf/2024-760-instructions.pdf#page=7 - - title: 2024 Form 760 Instructions, Subtractions from Income # latest list of subtractions as of 2025-02-14 - href: https://www.tax.virginia.gov/sites/default/files/vatax-pdf/2024-760-instructions.pdf#page=24 + - title: "2024 Form 760 Instructions, Line 5 and Subtractions from Income #page 17,24-25" # latest list of subtractions as of 2025-02-14 + href: https://www.tax.virginia.gov/sites/default/files/vatax-pdf/2024-760-instructions.pdf#page=17 - title: 2023 Virginia Schedule ADJ (Form 760-ADJ) href: https://www.tax.virginia.gov/sites/default/files/taxforms/individual-income-tax/2023/schedule-adj-2023.pdf#page=1 - title: 2022 Virginia Schedule ADJ (Form 760-ADJ) @@ -35,3 +38,5 @@ metadata: href: https://www.tax.virginia.gov/sites/default/files/vatax-pdf/2022-760-instructions.pdf#page=25 - title: Code of Virginia § 58.1-322.02 href: https://law.lis.virginia.gov/vacodefull/title58.1/chapter3/article2/ + - title: 45 U.S. Code § 231m(a) - Railroad Retirement Act annuities are exempt from state tax + href: https://www.law.cornell.edu/uscode/text/45/231m#a diff --git a/policyengine_us/parameters/gov/states/vt/tax/income/agi/subtractions.yaml b/policyengine_us/parameters/gov/states/vt/tax/income/agi/subtractions.yaml index 4453105079c..36e086a365d 100644 --- a/policyengine_us/parameters/gov/states/vt/tax/income/agi/subtractions.yaml +++ b/policyengine_us/parameters/gov/states/vt/tax/income/agi/subtractions.yaml @@ -17,6 +17,7 @@ values: - vt_retirement_income_exemption - salt_refund_income - vt_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) metadata: unit: list label: Vermont subtractions @@ -33,3 +34,5 @@ metadata: href: https://tax.vermont.gov/sites/tax/files/documents/IN-112-2021.pdf#page=1 - title: Titl. 32 V.S.A. § 5811(21)(B)(i), (C)(iv), (B)(vi), (B)(ii), (B)(iv) href: https://legislature.vermont.gov/statutes/section/32/151/05811 + - title: 45 U.S. Code § 231m(a) - Railroad Retirement Act annuities are exempt from state tax + href: https://www.law.cornell.edu/uscode/text/45/231m#a diff --git a/policyengine_us/parameters/gov/states/wi/tax/income/subtractions/sources.yaml b/policyengine_us/parameters/gov/states/wi/tax/income/subtractions/sources.yaml index 9cf8f3fb2e5..eca24b0111b 100644 --- a/policyengine_us/parameters/gov/states/wi/tax/income/subtractions/sources.yaml +++ b/policyengine_us/parameters/gov/states/wi/tax/income/subtractions/sources.yaml @@ -12,6 +12,7 @@ values: - salt_refund_income - wi_disability_income_exclusion # Schedule SB, Schedule 2440W - wi_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) 2022-01-01: - wi_unemployment_compensation_subtraction - tax_unit_taxable_social_security @@ -23,6 +24,7 @@ values: - salt_refund_income - wi_disability_income_exclusion # Schedule SB, Schedule 2440W - wi_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) metadata: unit: list @@ -42,3 +44,5 @@ metadata: href: https://www.revenue.wi.gov/TaxForms2024/2024-Schedule2440Wf.pdf#page=1 - title: 2025 Wisconsin Form 1 Schedule SB instructions href: https://www.revenue.wi.gov/TaxForms2025/2025-ScheduleSB-inst.pdf + - title: 45 U.S. Code § 231m(a) - Railroad Retirement Act annuities are exempt from state tax + href: https://www.law.cornell.edu/uscode/text/45/231m#a diff --git a/policyengine_us/parameters/gov/states/wv/tax/income/subtractions/senior_citizen_disability_deduction/excluded_income_sources.yaml b/policyengine_us/parameters/gov/states/wv/tax/income/subtractions/senior_citizen_disability_deduction/excluded_income_sources.yaml index 63c9c52dc00..39887cb8543 100644 --- a/policyengine_us/parameters/gov/states/wv/tax/income/subtractions/senior_citizen_disability_deduction/excluded_income_sources.yaml +++ b/policyengine_us/parameters/gov/states/wv/tax/income/subtractions/senior_citizen_disability_deduction/excluded_income_sources.yaml @@ -12,6 +12,8 @@ values: # removes income within the form's excluded range. Use the contributor's # own amount rather than projecting the tax-unit 529 deduction to spouses. - investment_in_529_plan_indv # Line 39 + # Line 40 subtracts the Railroad Retirement Act benefits in federal AGI. + - taxable_railroad_benefits # Line 40 metadata: unit: list diff --git a/policyengine_us/parameters/gov/states/wv/tax/income/subtractions/subtractions.yaml b/policyengine_us/parameters/gov/states/wv/tax/income/subtractions/subtractions.yaml index 2a742d9ee51..2ce49ee45db 100644 --- a/policyengine_us/parameters/gov/states/wv/tax/income/subtractions/subtractions.yaml +++ b/policyengine_us/parameters/gov/states/wv/tax/income/subtractions/subtractions.yaml @@ -12,6 +12,7 @@ values: - wv_claim_of_right_subtraction # Line 42 - wv_senior_citizen_disability_deduction # Line 47 - wv_other_subtractions + - taxable_railroad_benefits # 45 U.S.C. 231m(a) metadata: unit: list @@ -30,3 +31,5 @@ metadata: href: https://tax.wv.gov/Documents/TaxForms/2021/it140.pdf#page=3 - title: Code of West Virginia §11-21-12 href: https://code.wvlegislature.gov/11-21-12/ + - title: 45 U.S. Code § 231m(a) - Railroad Retirement Act annuities are exempt from state tax + href: https://www.law.cornell.edu/uscode/text/45/231m#a diff --git a/policyengine_us/reforms/congress/tlaib/boost/boost_middle_class_tax_credit.py b/policyengine_us/reforms/congress/tlaib/boost/boost_middle_class_tax_credit.py index 2c201a5d984..ac0a0c02d23 100644 --- a/policyengine_us/reforms/congress/tlaib/boost/boost_middle_class_tax_credit.py +++ b/policyengine_us/reforms/congress/tlaib/boost/boost_middle_class_tax_credit.py @@ -63,6 +63,10 @@ def formula(household, period, parameters): "educational_assistance", "financial_assistance", "survivor_benefits", + # Census money income counts veterans' payments, and railroad + # retirement as a government transfer (P60-200 Appendix A). + "veterans_benefits", + "railroad_benefits", # Broadband subsidies. "acp", "ebb", @@ -122,6 +126,10 @@ def formula(spm_unit, period, parameters): "educational_assistance", "financial_assistance", "survivor_benefits", + # Census money income counts veterans' payments, and railroad + # retirement as a government transfer (P60-200 Appendix A). + "veterans_benefits", + "railroad_benefits", # Broadband subsidies. "acp", "ebb", diff --git a/policyengine_us/reforms/congress/tlaib/economic_dignity_for_all_agenda/edaa_end_child_poverty_act.py b/policyengine_us/reforms/congress/tlaib/economic_dignity_for_all_agenda/edaa_end_child_poverty_act.py index 73c3048bdc9..22a4e54b210 100644 --- a/policyengine_us/reforms/congress/tlaib/economic_dignity_for_all_agenda/edaa_end_child_poverty_act.py +++ b/policyengine_us/reforms/congress/tlaib/economic_dignity_for_all_agenda/edaa_end_child_poverty_act.py @@ -131,6 +131,10 @@ def formula(spm_unit, period, parameters): "educational_assistance", "financial_assistance", "survivor_benefits", + # Census money income counts veterans' payments, and railroad + # retirement as a government transfer (P60-200 Appendix A). + "veterans_benefits", + "railroad_benefits", # Broadband subsidies. "acp", "ebb", diff --git a/policyengine_us/reforms/congress/tlaib/end_child_poverty_act.py b/policyengine_us/reforms/congress/tlaib/end_child_poverty_act.py index 7c4cd0f5f98..97b87f1175b 100644 --- a/policyengine_us/reforms/congress/tlaib/end_child_poverty_act.py +++ b/policyengine_us/reforms/congress/tlaib/end_child_poverty_act.py @@ -99,6 +99,10 @@ def formula(household, period, parameters): "educational_assistance", "financial_assistance", "survivor_benefits", + # Census money income counts veterans' payments, and railroad + # retirement as a government transfer (P60-200 Appendix A). + "veterans_benefits", + "railroad_benefits", # Broadband subsidies. "acp", "ebb", @@ -150,6 +154,10 @@ def formula(spm_unit, period, parameters): "educational_assistance", "financial_assistance", "survivor_benefits", + # Census money income counts veterans' payments, and railroad + # retirement as a government transfer (P60-200 Appendix A). + "veterans_benefits", + "railroad_benefits", # Broadband subsidies. "acp", "ebb", diff --git a/policyengine_us/reforms/congress/watca/working_americans_tax_cut_act.py b/policyengine_us/reforms/congress/watca/working_americans_tax_cut_act.py index 27becb3a73f..e0754486d8c 100644 --- a/policyengine_us/reforms/congress/watca/working_americans_tax_cut_act.py +++ b/policyengine_us/reforms/congress/watca/working_americans_tax_cut_act.py @@ -17,7 +17,12 @@ class watca_alternative_tax_magi(Variable): definition_period = YEAR unit = USD reference = WATCA_REFERENCES - documentation = "Section 1A(d): AGI + foreign income exclusions (sec 911, 931, 933) + non-taxable Social Security." + documentation = ( + "Section 1A(d): AGI + foreign income exclusions (sec 911, 931, " + "933) + the social security benefits, as section 86(d) defines " + "them, that section 86 leaves out of gross income: Social " + "Security and tier 1 railroad retirement benefits." + ) adds = [ "adjusted_gross_income", @@ -25,6 +30,7 @@ class watca_alternative_tax_magi(Variable): "specified_possession_income", "puerto_rico_income", "tax_exempt_social_security", + "tax_exempt_railroad_social_security_equivalent_benefits", ] class watca_surtax_magi(Variable): diff --git a/policyengine_us/reforms/states/tx/rebate/tx_rebate.py b/policyengine_us/reforms/states/tx/rebate/tx_rebate.py index 5779d0789a1..9e1016a4f6f 100644 --- a/policyengine_us/reforms/states/tx/rebate/tx_rebate.py +++ b/policyengine_us/reforms/states/tx/rebate/tx_rebate.py @@ -99,6 +99,10 @@ def formula(spm_unit, period, parameters): "educational_assistance", "financial_assistance", "survivor_benefits", + # Census money income counts veterans' payments, and railroad + # retirement as a government transfer (P60-200 Appendix A). + "veterans_benefits", + "railroad_benefits", "spm_unit_energy_subsidy", "tanf", # Washington (WA) cash-assistance programs. wa_sfa and wa_rca diff --git a/policyengine_us/tests/policy/baseline/gov/hhs/medicaid/income/medicaid_magi_railroad_retirement.yaml b/policyengine_us/tests/policy/baseline/gov/hhs/medicaid/income/medicaid_magi_railroad_retirement.yaml new file mode 100644 index 00000000000..c03a072645a --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/hhs/medicaid/income/medicaid_magi_railroad_retirement.yaml @@ -0,0 +1,47 @@ +# IRC 36B(d)(2)(B)(iii): MAGI adds back social security benefits as section +# 86(d) defines them that section 86 leaves out of gross income, so it adds +# the nontaxable tier 1 railroad benefits just as it adds nontaxable title II +# benefits. Medicaid MAGI follows 36B (42 U.S.C. 1396a(e)(14)(G)). + +- name: Nontaxable tier 1 railroad benefits are in MAGI. + period: 2024 + input: + people: + head: + age: 67 + employment_income: 20_000 + railroad_social_security_equivalent_benefits: 20_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: TX + output: + # Combined income = 30,000, so 2,500 is taxable and 17,500 is not. + adjusted_gross_income: 22_500 + tax_exempt_railroad_social_security_equivalent_benefits: 17_500 + medicaid_magi: 40_000 + aca_magi: 40_000 + medicaid_magi_person: 40_000 + +- name: MAGI is the same whether the benefits are title II or tier 1. + period: 2024 + input: + people: + head: + age: 67 + employment_income: 20_000 + social_security_retirement: 20_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: TX + output: + adjusted_gross_income: 22_500 + medicaid_magi: 40_000 + aca_magi: 40_000 diff --git a/policyengine_us/tests/policy/baseline/gov/irs/credits/elderly_and_disabled/section_22_railroad_retirement.yaml b/policyengine_us/tests/policy/baseline/gov/irs/credits/elderly_and_disabled/section_22_railroad_retirement.yaml new file mode 100644 index 00000000000..c4aca1207ee --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/irs/credits/elderly_and_disabled/section_22_railroad_retirement.yaml @@ -0,0 +1,76 @@ +# Section 22(c)(3)(A)(i)(II): the section 22 amount is reduced by amounts the +# head or spouse receives as a pension or annuity payable under the Railroad +# Retirement Act of 1974 and excluded from gross income. That covers tier 1 +# benefits section 86 leaves out and the tax-free part of the rest of the +# annuity (72(r)(2); 72(d), or 72(b) for some annuities that started before +# November 19, 1996). Initial amounts: $5,000 single, $7,500 joint with +# two qualified individuals (22(c)(2)); AGI thresholds $7,500 and $10,000 +# (22(d)). + +- name: Nontaxable tier 1 benefits and the tax-free tier 2 part both reduce the amount. + period: 2024 + input: + people: + head: + age: 67 + railroad_social_security_equivalent_benefits: 3_000 + taxable_railroad_pension_benefits: 2_000 + tax_exempt_railroad_pension_benefits: 500 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: TX + output: + # Combined income = 2,000 + 1,500 = 3,500, so all 3,000 of tier 1 benefits + # are excluded. AGI = 2,000, under 7,500. + # 5,000 - 3,000 - 500 = 1,500. + section_22_income: 1_500 + +- name: On a joint return title II and railroad exclusions of both spouses reduce the amount. + period: 2024 + input: + people: + head: + age: 67 + railroad_social_security_equivalent_benefits: 2_000 + taxable_railroad_pension_benefits: 4_000 + tax_exempt_railroad_pension_benefits: 300 + spouse: + age: 67 + social_security_retirement: 1_000 + tax_units: + tax_unit: + members: [head, spouse] + households: + household: + members: [head, spouse] + state_code: TX + output: + # Combined income = 4,000 + 1,500 = 5,500, so no benefits are taxable. + # AGI = 4,000, under 10,000. + # 7,500 - 1,000 (title II) - 2,000 (tier 1) - 300 (tier 2) = 4,200. + section_22_income: 4_200 + +- name: The AGI phase-out still applies after the railroad reduction. + period: 2024 + input: + people: + head: + age: 67 + railroad_social_security_equivalent_benefits: 1_000 + taxable_railroad_pension_benefits: 9_500 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: TX + output: + # Combined income = 9,500 + 500 = 10,000: no tier 1 benefits are taxable. + # AGI = 9,500; 22(d): 0.5 * (9,500 - 7,500) = 1,000. + # 5,000 - 1,000 - 1,000 = 3,000. + section_22_income: 3_000 diff --git a/policyengine_us/tests/policy/baseline/gov/irs/deductions/itemized/salt_and_real_estate/state_sales_tax.yaml b/policyengine_us/tests/policy/baseline/gov/irs/deductions/itemized/salt_and_real_estate/state_sales_tax.yaml index 68d0729ca1e..3d9e0fd67a6 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/deductions/itemized/salt_and_real_estate/state_sales_tax.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/deductions/itemized/salt_and_real_estate/state_sales_tax.yaml @@ -1534,3 +1534,62 @@ salt_deduction: 21_728 itemized_taxable_income_deductions_reduction: 2_661 itemized_taxable_income_deductions: 19_067 + +# Form 1040 line 11b plus "any nontaxable items", veterans' benefits among +# them, sets the row (2025 Instructions for Schedule A, page 5). In the 2025 +# table, a Texas family of one may deduct $931 in the $100,000-$120,000 row +# and $992 in the $120,000-$140,000 row (page 16). Veterans' benefits stay out of AGI +# under 38 U.S.C. 5301(a)(1) but raise this deduction for a filer who +# itemizes and deducts sales tax. +- name: A Texas filer with $100,000 of AGI uses the $100,000-$120,000 row, 2025 + period: 2025 + input: + state_code: TX + employment_income: 100_000 + output: + adjusted_gross_income: 100_000 + state_sales_tax_income_bracket: 10 + state_sales_tax: 931 + +- name: $30,000 of veterans' benefits moves the same filer to the $120,000-$140,000 row, 2025 + period: 2025 + input: + state_code: TX + employment_income: 100_000 + veterans_benefits: 30_000 + output: + adjusted_gross_income: 100_000 + state_sales_tax_income_bracket: 11 + state_sales_tax: 992 + +# The same page 5 list adds the "nontaxable part of social security and +# railroad retirement benefits" and the "nontaxable part of IRA, pension, or +# annuity distributions" to line 11b. A Railroad Retirement Act annuity's +# tax-free part and the untaxed part of the social security equivalent benefit +# (SSEB) therefore raise the row, as veterans' benefits do. +- name: $30,000 of tax-free railroad annuity moves the same filer to the $120,000-$140,000 row, 2025 + period: 2025 + input: + state_code: TX + employment_income: 100_000 + tax_exempt_railroad_pension_benefits: 30_000 + output: + adjusted_gross_income: 100_000 + state_sales_tax_income_bracket: 11 + state_sales_tax: 992 + +- name: The untaxed part of a $22,000 SSEB moves the same filer to the $120,000-$140,000 row, 2025 + period: 2025 + input: + state_code: TX + employment_income: 100_000 + railroad_social_security_equivalent_benefits: 22_000 + output: + # Provisional income comes to $100,000 + $22,000 / 2 = $111,000. Section + # 86(a)(2) taxes the lesser of 85% of the benefit ($18,700) and 85% of + # $111,000 - $34,000 plus $4,500 ($69,950), so AGI reaches $118,700, in + # the $100,000-$120,000 row. The untaxed $3,300 lifts the total to $122,000. + adjusted_gross_income: 118_700 + tax_exempt_railroad_social_security_equivalent_benefits: 3_300 + state_sales_tax_income_bracket: 11 + state_sales_tax: 992 diff --git a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/railroad_social_security_equivalent_benefits.yaml b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/railroad_social_security_equivalent_benefits.yaml new file mode 100644 index 00000000000..a957b3cbdba --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/railroad_social_security_equivalent_benefits.yaml @@ -0,0 +1,193 @@ +# Tier 1 railroad retirement benefits under IRC 86. Section 86(d)(1)(B) makes +# the social security equivalent benefit (Form RRB-1099 box 5) a social +# security benefit, so section 86 figures one taxable amount on it and title II +# benefits together (Pub. 915 Worksheet 1). Single filers: base amount $25,000, +# adjusted base amount $34,000 (86(c)(1)); joint: $32,000 and $44,000. + +- name: Tier 1 railroad benefits between the base and adjusted base amounts. + period: 2024 + input: + people: + head: + age: 67 + employment_income: 20_000 + railroad_social_security_equivalent_benefits: 20_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: TX + output: + # Combined income = 20,000 + 0.5 * 20,000 = 30,000 (86(b)(1)). + # 86(a)(1): min(0.5 * 20,000, 0.5 * (30,000 - 25,000)) = 2,500. + tax_unit_section_86_benefits: 20_000 + tax_unit_taxable_section_86_benefits: 2_500 + tax_unit_taxable_social_security: 0 + tax_unit_taxable_railroad_social_security_equivalent_benefits: 2_500 + taxable_railroad_social_security_equivalent_benefits: 2_500 + tax_exempt_railroad_social_security_equivalent_benefits: 17_500 + adjusted_gross_income: 22_500 + +- name: The same amount of title II benefits is taxed the same way. + period: 2024 + input: + people: + head: + age: 67 + employment_income: 20_000 + social_security_retirement: 20_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: TX + output: + tax_unit_section_86_benefits: 20_000 + tax_unit_taxable_section_86_benefits: 2_500 + tax_unit_taxable_social_security: 2_500 + taxable_social_security: 2_500 + tax_unit_taxable_railroad_social_security_equivalent_benefits: 0 + adjusted_gross_income: 22_500 + +- name: Title II and tier 1 benefits share one section 86 amount in proportion. + period: 2024 + input: + people: + head: + age: 67 + employment_income: 20_000 + social_security_retirement: 12_000 + railroad_social_security_equivalent_benefits: 8_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: TX + output: + # Combined income = 20,000 + 0.5 * 20,000 = 30,000, so 2,500 is taxable, + # split 12,000 / 20,000 to title II and 8,000 / 20,000 to tier 1. + tax_unit_taxable_section_86_benefits: 2_500 + tax_unit_taxable_social_security: 1_500 + tax_unit_taxable_railroad_social_security_equivalent_benefits: 1_000 + tax_exempt_social_security: 10_500 + tax_exempt_railroad_social_security_equivalent_benefits: 7_000 + adjusted_gross_income: 22_500 + +- name: Tier 1 railroad benefits above the adjusted base amount reach the 85 percent cap. + period: 2024 + input: + people: + head: + age: 67 + employment_income: 50_000 + railroad_social_security_equivalent_benefits: 24_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: TX + output: + # Combined income = 50,000 + 12,000 = 62,000. + # 86(a)(2)(A): 0.85 * (62,000 - 34,000) + min(12,000, 4,500) = 28,300. + # 86(a)(2)(B): 0.85 * 24,000 = 20,400, the lesser. + tax_unit_taxable_section_86_benefits: 20_400 + taxable_railroad_social_security_equivalent_benefits: 20_400 + adjusted_gross_income: 70_400 + +- name: On a joint return each spouse is allocated the taxable part of their own kind of benefit. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + head: + age: 67 + employment_income: 40_000 + social_security_retirement: 20_000 + spouse: + age: 67 + railroad_social_security_equivalent_benefits: 10_000 + tax_units: + tax_unit: + members: [head, spouse] + households: + household: + members: [head, spouse] + state_code: TX + output: + # Combined income = 40,000 + 0.5 * 30,000 = 55,000. + # 86(a)(1) amount: min(15,000, 0.5 * (55,000 - 32,000)) = 11,500. + # 86(a)(2)(A): 0.85 * (55,000 - 44,000) + min(11,500, 6,000) = 15,350. + # 86(a)(2)(B): 0.85 * 30,000 = 25,500, so 15,350 is taxable: two thirds + # title II (10,233.33) and one third tier 1 (5,116.67). + tax_unit_taxable_section_86_benefits: 15_350 + taxable_social_security: [10_233.33, 0] + taxable_railroad_social_security_equivalent_benefits: [0, 5_116.67] + adjusted_gross_income: 55_350 + +- name: A dependent's tier 1 benefits are taxed on the dependent's own income. + period: 2024 + input: + people: + head: + age: 45 + employment_income: 50_000 + child: + age: 17 + is_tax_unit_dependent: true + employment_income: 30_000 + railroad_social_security_equivalent_benefits: 10_000 + tax_units: + tax_unit: + members: [head, child] + households: + household: + members: [head, child] + state_code: TX + output: + # The child's combined income = 30,000 + 5,000 = 35,000, over the 34,000 + # adjusted base amount for an unmarried individual. + # 86(a)(2)(A): 0.85 * 1,000 + min(5,000, 4,500) = 5,350. + # 86(a)(2)(B): 0.85 * 10,000 = 8,500, so 5,350. + dependent_taxable_ss_magi: [0, 30_000] + dependent_taxable_railroad_social_security_equivalent_benefits: [0, 5_350] + dependent_taxable_social_security: [0, 0] + dependent_gross_income: [0, 35_350] + # The head's return holds none of the child's benefits. + tax_unit_railroad_social_security_equivalent_benefits_for_taxability: 0 + tax_unit_taxable_railroad_social_security_equivalent_benefits: 0 + irs_gross_income: [50_000, 0] + +- name: A dependent's title II and tier 1 benefits share one section 86 amount. + period: 2024 + input: + people: + head: + age: 45 + employment_income: 50_000 + child: + age: 17 + is_tax_unit_dependent: true + employment_income: 30_000 + social_security_survivors: 6_000 + railroad_social_security_equivalent_benefits: 4_000 + tax_units: + tax_unit: + members: [head, child] + households: + household: + members: [head, child] + state_code: TX + output: + # The same 5,350 on 10,000 of benefits: 60 percent title II, 40 percent + # tier 1. + dependent_taxable_social_security: [0, 3_210] + dependent_taxable_railroad_social_security_equivalent_benefits: [0, 2_140] + dependent_gross_income: [0, 35_350] diff --git a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/taxable_railroad_benefits.yaml b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/taxable_railroad_benefits.yaml new file mode 100644 index 00000000000..ec3060aeb8b --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/taxable_railroad_benefits.yaml @@ -0,0 +1,99 @@ +# Railroad Retirement Act benefits in federal gross income. IRC 72(r)(1) +# treats every benefit other than the tier 1 social security equivalent +# benefit as a benefit from a section 401(a) plan, and 72(r)(2) treats tier 2 +# employee taxes as contributions recovered tax-free (Form RRB-1099-R). +# taxable_railroad_benefits is what federal AGI includes from the Act, which +# 45 U.S.C. 231m(a) bars states from taxing. + +- name: The taxable part of the rest of the annuity is gross income, like a private pension. + period: 2024 + input: + people: + head: + age: 67 + railroad_social_security_equivalent_benefits: 18_000 + taxable_railroad_pension_benefits: 12_000 + tax_exempt_railroad_pension_benefits: 1_500 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: TX + output: + # Combined income = 12,000 + 0.5 * 18,000 = 21,000, under the 25,000 base + # amount, so no tier 1 benefits are taxable. + railroad_benefits: 31_500 + tax_unit_taxable_railroad_social_security_equivalent_benefits: 0 + irs_gross_income: 12_000 + adjusted_gross_income: 12_000 + taxable_railroad_benefits: 12_000 + +- name: Taxable tier 1 benefits and the taxable rest of the annuity both reach AGI. + period: 2024 + input: + people: + head: + age: 67 + employment_income: 30_000 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 10_000 + tax_exempt_railroad_pension_benefits: 1_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: TX + output: + # Modified AGI = 30,000 + 10,000 = 40,000; combined income = 50,000. + # 86(a)(2)(A): 0.85 * (50,000 - 34,000) + min(10,000, 4,500) = 18,100. + # 86(a)(2)(B): 0.85 * 20,000 = 17,000, the lesser. + railroad_benefits: 31_000 + taxable_railroad_social_security_equivalent_benefits: 17_000 + adjusted_gross_income: 57_000 + taxable_railroad_benefits: 27_000 + +- name: A dependent's railroad benefits are not in the filer's AGI, so states have nothing to remove. + period: 2024 + input: + people: + head: + age: 45 + employment_income: 50_000 + child: + age: 17 + is_tax_unit_dependent: true + taxable_railroad_pension_benefits: 5_000 + tax_units: + tax_unit: + members: [head, child] + households: + household: + members: [head, child] + state_code: TX + output: + irs_gross_income: [50_000, 0] + dependent_gross_income: [0, 5_000] + taxable_railroad_benefits: [0, 0] + +- name: A railroad_benefits total entered directly reaches no tax variable. + period: 2024 + input: + people: + head: + age: 67 + railroad_benefits: 30_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: TX + output: + tax_unit_section_86_benefits: 0 + irs_gross_income: 0 + taxable_railroad_benefits: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/veterans_benefits.yaml b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/veterans_benefits.yaml new file mode 100644 index 00000000000..8df77dd2719 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/veterans_benefits.yaml @@ -0,0 +1,19 @@ +- name: Veterans' benefits stay out of federal gross income. + period: 2026 + input: + age: 40 + veterans_benefits: 30_000 + output: + # 38 U.S.C. 5301(a)(1): VA benefits are exempt from taxation. + irs_gross_income: 0 + adjusted_gross_income: 0 + +- name: Veterans' benefits add nothing to a worker's federal gross income. + period: 2026 + input: + age: 40 + employment_income: 45_000 + veterans_benefits: 20_000 + output: + irs_gross_income: 45_000 + adjusted_gross_income: 45_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/tax/income/az_property_tax_credit_agi.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/tax/income/az_property_tax_credit_agi.yaml index 35f8565d5f3..cd99c105f5d 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/az/tax/income/az_property_tax_credit_agi.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/az/tax/income/az_property_tax_credit_agi.yaml @@ -186,3 +186,33 @@ az_property_tax_credit_agi: 3_000 az_property_tax_credit_household_income: 3_000 az_property_tax_credit: 212 + +- name: Railroad retirement benefits are excluded, a dependent's too. + period: 2025 + input: + people: + head: + age: 70 + railroad_social_security_equivalent_benefits: 10_000 + taxable_railroad_pension_benefits: 3_000 + taxable_interest_income: 2_000 + child: + age: 15 + taxable_railroad_pension_benefits: 1_000 + taxable_interest_income: 500 + tax_units: + tax_unit: + members: [head, child] + households: + household: + members: [head, child] + state_code: AZ + output: + # Combined income is 3,000 + 2,000 + 0.5 x 10,000 = 10,000, under the + # $25,000 base, so federal AGI holds 3,000 of tier 2 benefits and 2,000 + # of interest. The 2025 Form 140PTC instructions (page 4) leave railroad + # retirement benefits out of household income. + adjusted_gross_income: 5_000 + az_property_tax_credit_agi: 2_000 + # The child's railroad annuity stays out too; the interest counts. + az_property_tax_credit_dependent_income: 500 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mn/tax/income/mn_amt_taxable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/mn/tax/income/mn_amt_taxable_income.yaml index aadc46a547a..e003293078f 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mn/tax/income/mn_amt_taxable_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mn/tax/income/mn_amt_taxable_income.yaml @@ -112,3 +112,56 @@ state_code: MN output: mn_amt_taxable_income: 200_000 + +- name: MN AMT taxable income leaves out railroad retirement benefits + period: 2024 + input: + people: + head: + age: 70 + taxable_railroad_pension_benefits: 64_255 + spouse: + age: 70 + taxable_railroad_pension_benefits: 64_255 + tax_units: + tax_unit: + members: [head, spouse] + marital_units: + marital_unit: + members: [head, spouse] + households: + household: + members: [head, spouse] + state_code: MN + output: + # Federal AGI = 2 x 64,255 = 128,510, all of it the taxable rest of the + # railroad annuity (IRC 72(r)). 2024 Schedule M1MT line 18 subtracts + # Schedule M1M line 17, the Railroad Retirement Board benefits in federal + # AGI, and 45 U.S.C. 231m(a) bars any state tax on them: + # 128,510 - 128,510 = 0, so no AMT. + filing_status: JOINT + adjusted_gross_income: 128_510 + mn_amt_taxable_income: 0 + mn_amt: 0 + mn_income_tax_before_credits: 0 + +- name: MN AMT taxable income subtracts railroad retirement benefits from a wage earner's AGI + period: 2024 + input: + people: + head: + age: 50 + employment_income: 300_000 + taxable_railroad_pension_benefits: 100_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: MN + output: + # Federal AGI = 300,000 + 100,000 = 400,000; M1MT line 18 subtracts the + # 100,000 of railroad benefits: 400,000 - 100,000 = 300,000. + adjusted_gross_income: 400_000 + mn_amt_taxable_income: 300_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mn/tax/income/mn_elderly_disabled_subtraction.yaml b/policyengine_us/tests/policy/baseline/gov/states/mn/tax/income/mn_elderly_disabled_subtraction.yaml index 88fbcdcd603..cfa147adc01 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mn/tax/income/mn_elderly_disabled_subtraction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mn/tax/income/mn_elderly_disabled_subtraction.yaml @@ -120,3 +120,28 @@ state_code: MN output: mn_elderly_disabled_subtraction: 0 # = 12_000 - 0.5 * (42_000 - 18_000) + +- name: Railroad retirement benefits reduce the base and leave AGI. + period: 2025 + input: + people: + person1: + age: 70 + taxable_ira_distributions: 15_000 + railroad_social_security_equivalent_benefits: 1_000 + taxable_railroad_pension_benefits: 2_000 + tax_units: + tax_unit: + members: [person1] + households: + household: + members: [person1] + state_code: MN + output: + # Combined income is 17,000 + 0.5 x 1,000 = 17,500, under the $25,000 + # base, so none of the tier 1 benefits enters federal AGI of 17,000. + adjusted_gross_income: 17_000 + # Minn. Stat. 290.0802, subd. 1(a): AGI = 17,000 - 2,000 = 15,000. + # Subd. 1(c) and 2(b)(2): 9,600 - (1,000 + 2,000) - 0.5 x (15,000 - + # 14,500) = 6,350. + mn_elderly_disabled_subtraction: 6_350 diff --git a/policyengine_us/tests/policy/baseline/gov/states/or/tax/income/credits/retirement_income/or_retirement_credit.yaml b/policyengine_us/tests/policy/baseline/gov/states/or/tax/income/credits/retirement_income/or_retirement_credit.yaml index 59d020855c0..b1806582003 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/or/tax/income/credits/retirement_income/or_retirement_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/or/tax/income/credits/retirement_income/or_retirement_credit.yaml @@ -219,3 +219,36 @@ state_code: OR output: or_retirement_credit: 675 + +- name: Tier 1 railroad benefits count as Social Security on lines 5 and 7. + period: 2025 + input: + people: + head: + age: 66 + taxable_private_pension_income: 25_000 + taxable_interest_income: 5_000 + spouse: + age: 66 + railroad_social_security_equivalent_benefits: 4_000 + taxable_railroad_pension_benefits: 3_000 + tax_units: + tax_unit: + members: [head, spouse] + marital_units: + marital_unit: + members: [head, spouse] + households: + household: + members: [head, spouse] + state_code: OR + output: + # Section 86: combined income is 33,000 + 0.5 x 4,000 = 35,000, which + # exceeds the $32,000 joint base by 3,000, so 1,500 of the tier 1 + # benefits enters federal AGI. + adjusted_gross_income: 34_500 + # ORS 316.157(2)(b) and (f): household income leaves out the 1,500. + or_retirement_credit_household_income: 33_000 + # Line 6: 15,000 - 4,000 = 11,000. Line 10: 11,000 - (33,000 - 30,000) + # = 8,000. Line 11: min(25,000, 8,000) x 9% = 720. + or_retirement_credit: 720 diff --git a/policyengine_us/tests/policy/baseline/gov/states/pa/tax/property/property_tax_or_rent_rebate/pa_property_tax_or_rent_rebate_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/pa/tax/property/property_tax_or_rent_rebate/pa_property_tax_or_rent_rebate_income.yaml index ec86404383c..670b7731ab4 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/pa/tax/property/property_tax_or_rent_rebate/pa_property_tax_or_rent_rebate_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/pa/tax/property/property_tax_or_rent_rebate/pa_property_tax_or_rent_rebate_income.yaml @@ -55,6 +55,79 @@ output: pa_property_tax_or_rent_rebate_income: 6_000 +- name: Case 3, entered as the tier 1 part, gives the same income. + period: 2025 + input: + people: + person1: + age: 65 + ssi: 2_000 + pa_ssp: 0 + railroad_social_security_equivalent_benefits: 10_000 + tax_units: + tax_unit: + members: [person1] + households: + household: + members: [person1] + state_code: PA + output: + # Combined income = 10,000 / 2 = 5,000, below the 25,000 base amount, so + # federal AGI holds none of the tier 1 benefits (IRC 86). Section 1303, + # "Income" (2), and the SSI rule: half of 2,000 + 10,000 = 6,000. + adjusted_gross_income: 0 + pa_property_tax_or_rent_rebate_income: 6_000 + +- name: Case 3a, half of all railroad benefits count, tier 2 included, and once. + period: 2025 + input: + people: + person1: + age: 65 + railroad_social_security_equivalent_benefits: 10_000 + taxable_railroad_pension_benefits: 10_000 + tax_exempt_railroad_pension_benefits: 2_000 + tax_units: + tax_unit: + members: [person1] + households: + household: + members: [person1] + state_code: PA + output: + # Combined income = 10,000 + 10,000 / 2 = 15,000, below the 25,000 base + # amount, so none of the tier 1 benefits is taxable (IRC 86) and federal + # AGI is the 10,000 taxable part of tier 2 (IRC 72(r)). + # Taxpayer Relief Act section 1303, "Income" (2): 50% of railroad + # retirement benefits. The 10,000 in federal AGI comes out, and half of + # 10,000 + 10,000 + 2,000 goes in: 10,000 - 10,000 + 11,000 = 11,000. + adjusted_gross_income: 10_000 + pa_property_tax_or_rent_rebate_income: 11_000 + +- name: Case 3b, taxable railroad benefits in federal AGI give way to half of the total. + period: 2025 + input: + people: + person1: + age: 65 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 30_000 + tax_units: + tax_unit: + members: [person1] + households: + household: + members: [person1] + state_code: PA + output: + # Combined income = 30,000 + 20,000 / 2 = 40,000. IRC 86(a)(2): the lesser + # of 85% x 20,000 = 17,000 and 85% x (40,000 - 34,000) + 4,500 = 9,600, + # so 9,600 of tier 1 is taxable and federal AGI is 39,600. + # Section 1303, "Income" (2): all 39,600 in federal AGI comes out, and half + # of 20,000 + 30,000 goes in: 39,600 - 39,600 + 25,000 = 25,000. + adjusted_gross_income: 39_600 + pa_property_tax_or_rent_rebate_income: 25_000 + - name: Case 4, tax-exempt interest and retirement income are included. period: 2025 input: diff --git a/policyengine_us/tests/policy/baseline/gov/states/railroad_retirement_benefits.yaml b/policyengine_us/tests/policy/baseline/gov/states/railroad_retirement_benefits.yaml new file mode 100644 index 00000000000..95401043afb --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/railroad_retirement_benefits.yaml @@ -0,0 +1,856 @@ +# 45 U.S.C. 231m(a): no State may tax an annuity or supplemental annuity under +# the Railroad Retirement Act. Federal AGI includes the taxable part of tier 1 +# benefits (IRC 86(d)(1)(B)) and of the rest of the annuity (IRC 72(r)(1)), so +# every state that starts from federal AGI or taxable income removes what it +# includes (taxable_railroad_benefits). States that build their own income +# base from other variables never take it in. +# +# Each household: single, 45, railroad benefits only: $20,000 of tier 1 social +# security equivalent benefits and $25,000 of the taxable rest of the annuity. +# Combined income = 25,000 + 10,000 = 35,000, so IRC 86(a)(2) makes +# 0.85 * 1,000 + 4,500 = 5,350 of the tier 1 benefits taxable, and federal AGI +# is 25,000 + 5,350 = 30,350. None of it is state income, and the state's +# withholding estimate, which feeds the federal deduction for state and +# local income taxes, counts none of it. + +- name: Railroad retirement benefits are not AL income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: AL + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 + +- name: Railroad retirement benefits are not AZ income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: AZ + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 + +- name: Railroad retirement benefits are not AR income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: AR + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 + +- name: Railroad retirement benefits are not CA income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: CA + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 + +- name: Railroad retirement benefits are not CO income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: CO + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 + +- name: Railroad retirement benefits are not CT income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: CT + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 + +- name: Railroad retirement benefits are not DE income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: DE + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 + +- name: Railroad retirement benefits are not DC income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: DC + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 + +- name: Railroad retirement benefits are not GA income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: GA + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 + +- name: Railroad retirement benefits are not HI income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: HI + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 + +- name: Railroad retirement benefits are not ID income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: ID + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + # The $10 permanent building fund tax falls on every required filer + # (Idaho Code 63-3082), so the test checks taxable income. + id_taxable_income: 0 + +- name: Railroad retirement benefits are not IL income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: IL + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 + +- name: Railroad retirement benefits are not IN income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: IN + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 + +- name: Railroad retirement benefits are not IA income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: IA + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 + +- name: Railroad retirement benefits are not KS income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: KS + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 + +- name: Railroad retirement benefits are not KY income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: KY + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 + +- name: Railroad retirement benefits are not LA income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: LA + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 + +- name: Railroad retirement benefits are not ME income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: ME + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 + +- name: Railroad retirement benefits are not MD income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: MD + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 + +- name: Railroad retirement benefits are not MA income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: MA + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 + +- name: Railroad retirement benefits are not MI income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: MI + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 + +- name: Railroad retirement benefits are not MN income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: MN + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 + +- name: Railroad retirement benefits are not MS income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: MS + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 + +- name: Railroad retirement benefits are not MO income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: MO + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 + +- name: Railroad retirement benefits are not MT income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: MT + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 + +- name: Railroad retirement benefits are not NE income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: NE + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 + +- name: Railroad retirement benefits are not NJ income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: NJ + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 + +- name: Railroad retirement benefits are not NM income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: NM + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 + +- name: Railroad retirement benefits are not NY income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: NY + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 + +- name: Railroad retirement benefits are not NC income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: NC + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 + +- name: Railroad retirement benefits are not ND income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: ND + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 + +- name: Railroad retirement benefits are not OH income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: OH + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 + +- name: Railroad retirement benefits are not OK income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: OK + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 + +- name: Railroad retirement benefits are not OR income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: OR + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 + +- name: Railroad retirement benefits are not PA income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: PA + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 + +- name: Railroad retirement benefits are not RI income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: RI + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 + +- name: Railroad retirement benefits are not SC income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: SC + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 + +- name: Railroad retirement benefits are not UT income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: UT + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 + +- name: Railroad retirement benefits are not VT income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: VT + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 + +- name: Railroad retirement benefits are not VA income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: VA + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 + +- name: Railroad retirement benefits are not WV income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: WV + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 + +- name: Railroad retirement benefits are not WI income. + period: 2025 + input: + people: + head: + age: 45 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 25_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: WI + output: + adjusted_gross_income: 30_350 + state_withheld_income_tax: 0 + state_income_tax_before_refundable_credits: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ut/tax/property/homeowner_renter_relief/ut_homeowner_renter_relief.yaml b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/property/homeowner_renter_relief/ut_homeowner_renter_relief.yaml index 1647e05fc5d..ed7a2647bab 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ut/tax/property/homeowner_renter_relief/ut_homeowner_renter_relief.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/property/homeowner_renter_relief/ut_homeowner_renter_relief.yaml @@ -241,3 +241,28 @@ # deferrals + the 8,000 catch-up at age 66. self_employed_pension_contributions: 51_087.05 ut_homeowner_renter_relief_nontaxable_income: 51_087.05 + +- name: Railroad retirement benefits count once in household income. + period: 2025 + input: + people: + head: + age: 70 + railroad_social_security_equivalent_benefits: 6_000 + taxable_railroad_pension_benefits: 10_000 + tax_exempt_railroad_pension_benefits: 2_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: UT + output: + # Combined income = 10,000 + 6,000 / 2 = 13,000, below the 25,000 base + # amount, so none of the tier 1 benefits is taxable (IRC 86) and federal + # AGI is the 10,000 taxable rest of the annuity (IRC 72(r)). The + # nontaxable income adds the other 6,000 + 2,000 = 8,000, so household + # income holds each dollar of the 18,000 of benefits once. + adjusted_gross_income: 10_000 + ut_homeowner_renter_relief_household_income: 18_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/va/tax/income/subtractions/va_railroad_retirement.yaml b/policyengine_us/tests/policy/baseline/gov/states/va/tax/income/subtractions/va_railroad_retirement.yaml new file mode 100644 index 00000000000..09f8f4fa74b --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/va/tax/income/subtractions/va_railroad_retirement.yaml @@ -0,0 +1,61 @@ +# Virginia subtracts the tier 1 railroad benefits in federal AGI on Form 760 +# line 5 and the rest of the Railroad Retirement Act benefits in it on Schedule +# ADJ (code 22); 45 U.S.C. 231m(a). Adjusted federal AGI for the age deduction +# removes benefits taxed solely under IRC 86, which include tier 1 but not +# tier 2 (Va. Code 58.1-322.03(5)). A single filer born after January 1, 1939 +# gets $12,000, less each dollar of adjusted federal AGI over $50,000. + +- name: Virginia removes railroad benefits from AGI and the age deduction test. + period: 2024 + input: + people: + head: + age: 67 + employment_income: 20_000 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 10_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: VA + output: + # Federal: combined income = 30,000 + 10,000 = 40,000. + # 86(a)(2)(A): 0.85 * 6,000 + min(7,500, 4,500) = 9,600, under the + # 17,000 cap. AGI = 20,000 + 10,000 + 9,600 = 39,600. + adjusted_gross_income: 39_600 + taxable_railroad_benefits: 19_600 + # 39,600 - 9,600 = 30,000, under 50,000: the full 12,000. + va_age_deduction_agi: 30_000 + va_age_deduction: 12_000 + va_subtractions: 31_600 + va_agi: 8_000 + +- name: Tier 2 benefits stay in adjusted federal AGI for the age deduction. + period: 2024 + input: + people: + head: + age: 67 + employment_income: 45_000 + railroad_social_security_equivalent_benefits: 10_000 + taxable_railroad_pension_benefits: 15_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: VA + output: + # Federal: combined income = 60,000 + 5,000 = 65,000, so the 85 percent + # cap binds: 8,500. AGI = 45,000 + 15,000 + 8,500 = 68,500. + adjusted_gross_income: 68_500 + taxable_railroad_benefits: 23_500 + # 68,500 - 8,500 = 60,000; 12,000 - (60,000 - 50,000) = 2,000. + va_age_deduction_agi: 60_000 + va_age_deduction: 2_000 + va_subtractions: 25_500 + va_agi: 43_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/vt_amt.yaml b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/vt_amt.yaml index 59a67058207..fc5a99442f2 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/vt_amt.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/vt_amt.yaml @@ -15,3 +15,47 @@ state_code: VT output: vt_amt: 0 + +- name: Vermont minimum tax leaves out railroad retirement benefits + period: 2025 + input: + people: + head: + age: 70 + taxable_railroad_pension_benefits: 262_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: VT + output: + # Federal AGI = 262,000 exceeds 150,000, so 32 V.S.A. 5822(a)(6) applies, + # but 45 U.S.C. 231m(a) bars any state tax on the railroad annuity: + # 3% x (262,000 - 262,000) = 0. + adjusted_gross_income: 262_000 + vt_amt: 0 + vt_income_tax_before_non_refundable_credits: 0 + +- name: Vermont minimum tax applies to federal AGI less railroad retirement benefits + period: 2025 + input: + people: + head: + age: 40 + employment_income: 100_000 + taxable_railroad_pension_benefits: 100_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: VT + output: + # Federal AGI = 100,000 + 100,000 = 200,000 exceeds 150,000, so the + # minimum tax applies to the 100,000 that is not railroad benefits: + # 3% x (200,000 - 100,000) = 3,000. + adjusted_gross_income: 200_000 + vt_amt: 3_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/wv/tax/income/subtractions/senior_citizen_disability/wv_senior_citizen_disability_deduction_person.yaml b/policyengine_us/tests/policy/baseline/gov/states/wv/tax/income/subtractions/senior_citizen_disability/wv_senior_citizen_disability_deduction_person.yaml index 0db631f2408..fe277f24e97 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wv/tax/income/subtractions/senior_citizen_disability/wv_senior_citizen_disability_deduction_person.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wv/tax/income/subtractions/senior_citizen_disability/wv_senior_citizen_disability_deduction_person.yaml @@ -53,6 +53,21 @@ wv_subtractions: 6_000 wv_agi: 0 +- name: Railroad retirement benefits on line 40 stay out of box c. + period: 2025 + input: + age: 70 + taxable_private_pension_income: 5_000 + taxable_railroad_pension_benefits: 20_000 + state_code: WV + output: + adjusted_gross_income_person: 25_000 + # Line 40 subtracts the 20,000 of railroad benefits in federal AGI. + # Box (c) = min(25,000 - 20,000, 8,000) = 5,000; box (d) = 0. + wv_senior_citizen_disability_deduction_person: 5_000 + wv_subtractions: 25_000 + wv_agi: 0 + - name: The 2024 line 49 excludes taxable state refunds from box c. period: 2024 input: @@ -82,6 +97,9 @@ adjusted_gross_income_person: 6_000 salt_refund_income: 2_000 wv_senior_citizen_disability_deduction_total_modifications: 1_000 + # The case enters AGI directly. Federal taxable railroad benefits build on + # 401(k) limits that the model holds only from 2018. + taxable_railroad_benefits: 0 output: # Available income = 6,000 - 2,000 - 1,000 = 3,000. # Remaining allowance = 8,000 - 1,000 = 7,000; min = 3,000. diff --git a/policyengine_us/tests/policy/baseline/household/income/household/household_benefits.yaml b/policyengine_us/tests/policy/baseline/household/income/household/household_benefits.yaml index dfbde8229df..c54e53e0b10 100644 --- a/policyengine_us/tests/policy/baseline/household/income/household/household_benefits.yaml +++ b/policyengine_us/tests/policy/baseline/household/income/household/household_benefits.yaml @@ -173,3 +173,33 @@ output: household_benefits: 2_000 spm_unit_benefits: 2_000 + +- name: Veterans' benefits and railroad retirement reach household benefits. + period: 2026 + input: + people: + veteran: + age: 40 + employment_income: 500_000 + veterans_benefits: 12_000 + railroad_annuitant: + age: 40 + employment_income: 500_000 + railroad_benefits: 30_000 + tax_units: + veteran_unit: + members: [veteran] + railroad_unit: + members: [railroad_annuitant] + spm_units: + spm_unit: + members: [veteran, railroad_annuitant] + households: + household: + members: [veteran, railroad_annuitant] + state_code: TX + output: + # Census P60-200 Appendix A: veterans' payments are money income, and + # railroad retirement is a government transfer. 12,000 + 30,000 = 42,000. + household_benefits: 42_000 + spm_unit_benefits: 42_000 diff --git a/policyengine_us/tests/policy/baseline/household/income/spm_unit/spm_unit_benefits.yaml b/policyengine_us/tests/policy/baseline/household/income/spm_unit/spm_unit_benefits.yaml index 2c8a14090b0..2474acb5ac1 100644 --- a/policyengine_us/tests/policy/baseline/household/income/spm_unit/spm_unit_benefits.yaml +++ b/policyengine_us/tests/policy/baseline/household/income/spm_unit/spm_unit_benefits.yaml @@ -302,3 +302,33 @@ output: spm_unit_benefits: [1_000, 1_000] household_benefits: 2_000 + +- name: Each SPM unit counts its own veterans' and railroad retirement benefits. + period: 2026 + input: + people: + veteran: + age: 40 + employment_income: 500_000 + veterans_benefits: 9_000 + railroad_annuitant: + age: 40 + employment_income: 500_000 + railroad_benefits: 24_000 + tax_units: + veteran_unit: + members: [veteran] + railroad_unit: + members: [railroad_annuitant] + spm_units: + veteran_unit: + members: [veteran] + railroad_unit: + members: [railroad_annuitant] + households: + household: + members: [veteran, railroad_annuitant] + state_code: FL + output: + spm_unit_benefits: [9_000, 24_000] + household_benefits: 9_000 + 24_000 diff --git a/policyengine_us/tests/policy/contrib/congress/watca.yaml b/policyengine_us/tests/policy/contrib/congress/watca.yaml index 2b40103ff7f..c4a942e0d9a 100644 --- a/policyengine_us/tests/policy/contrib/congress/watca.yaml +++ b/policyengine_us/tests/policy/contrib/congress/watca.yaml @@ -353,6 +353,32 @@ # MAGI = 50_000 + (20_000 - 5_000) = 65_000 watca_alternative_tax_magi: 65_000 +- name: Alternative tax MAGI includes non-taxable tier 1 railroad retirement benefits + period: 2026 + reforms: policyengine_us.reforms.congress.watca.working_americans_tax_cut_act.watca_reform_object + input: + gov.contrib.congress.watca.in_effect: true + people: + person1: + age: 67 + employment_income: 20_000 + railroad_social_security_equivalent_benefits: 20_000 + tax_units: + tax_unit: + members: [person1] + households: + household: + members: [person1] + state_code: TX + output: + # Section 86(d)(1) counts the benefit as Social Security. Combined income + # is 20,000 + 0.5 x 20,000 = 30,000, which exceeds the $25,000 base by + # 5,000, so 2,500 enters AGI and 17,500 stays out. + adjusted_gross_income: 22_500 + tax_exempt_railroad_social_security_equivalent_benefits: 17_500 + # MAGI = 22,500 + 17,500 = 40,000 + watca_alternative_tax_magi: 40_000 + - name: Alternative tax MAGI includes specified possession income period: 2026 reforms: policyengine_us.reforms.congress.watca.working_americans_tax_cut_act.watca_reform_object diff --git a/policyengine_us/tests/policy/contrib/taxsim/taxsim_v12.yaml b/policyengine_us/tests/policy/contrib/taxsim/taxsim_v12.yaml index 2a385454e38..fc51328c70c 100644 --- a/policyengine_us/tests/policy/contrib/taxsim/taxsim_v12.yaml +++ b/policyengine_us/tests/policy/contrib/taxsim/taxsim_v12.yaml @@ -34,3 +34,28 @@ cohabitating_spouses: true output: taxsim_v12: 4_250 + +- name: Case 5, TAXSIM counts a railroad retiree's social security equivalent benefit as Social Security. + period: 2025 + input: + people: + person1: + age: 67 + employment_income: 20_000 + railroad_social_security_equivalent_benefits: 20_000 + tax_units: + tax_unit: + members: [person1] + households: + household: + members: [person1] + state_code: TX + output: + taxsim_gssi: 20_000 + taxable_ss_magi: 20_000 + # Section 86(d)(1) counts the benefit as Social Security. Combined income + # is 20,000 + 0.5 x 20,000 = 30,000, which exceeds the $25,000 base by + # 5,000. Half of that excess, 2,500, is less than half the benefits, so + # 2,500 enters AGI. + taxsim_v12: 2_500 + adjusted_gross_income: 22_500 diff --git a/policyengine_us/tests/test_aggregate_list_integrity.py b/policyengine_us/tests/test_aggregate_list_integrity.py index 2599fa64000..0fd94fd7981 100644 --- a/policyengine_us/tests/test_aggregate_list_integrity.py +++ b/policyengine_us/tests/test_aggregate_list_integrity.py @@ -290,12 +290,16 @@ def test_reform_aggregates_keep_baseline_contrib_entries( "ma_eaedc", "nj_property_tax_relief", "ny_drive_clean_rebate", + "railroad_benefits", "state_property_tax_refunds", + "veterans_benefits", }, "household_benefits": { "ak_energy_relief", "household_local_benefits", "ny_drive_clean_rebate", + "railroad_benefits", + "veterans_benefits", }, } diff --git a/policyengine_us/tests/test_household_spm_resource_parity.py b/policyengine_us/tests/test_household_spm_resource_parity.py index b3d554e469a..1a0309a8b3c 100644 --- a/policyengine_us/tests/test_household_spm_resource_parity.py +++ b/policyengine_us/tests/test_household_spm_resource_parity.py @@ -4,7 +4,7 @@ cash. Each total keeps its own list of what it adds, in two different files, and the lists drift: a cash program reaches one total and not the other. -Seven invariants hold for every input. +Nine invariants hold for every input. 1. The list difference is exactly the documented one. Every program that one total counts and the other does not appears below with its reason. @@ -24,6 +24,12 @@ same amount. A state income tax credit list or state_property_tax_refunds carries each payment into both totals, and the bill reductions documented below reach neither, since neither total subtracts property tax. +8. For a household with no means-tested benefits to lose, a dollar of veterans' + or railroad benefits adds a dollar to both benefit totals and to market + income plus benefits. +9. Veterans' benefits never change federal gross income or AGI, and they leave + federal income tax unchanged for a filer under 65 without a disability who + doesn't itemize. Child care stays out of this file: household_state_benefits and the SPM treat subsidies differently on purpose, and #9405 and #10569 cover that difference. @@ -107,10 +113,14 @@ "rental_income": "rental_income", "dividend_income": "dividend_income", "taxable_pension_income": "pension_income", + # railroad_benefits holds the whole Railroad Retirement Act annuity; these + # two sources tax its non-SSEB part (IRC 72(r)) and its SSEB (IRC 86). + "taxable_railroad_pension_benefits": "railroad_benefits", "taxable_disability_benefits": "disability_benefits", "estate_income": "estate_income", "taxable_unemployment_compensation": "total_unemployment_compensation", "taxable_social_security": "social_security", + "taxable_railroad_social_security_equivalent_benefits": "railroad_benefits", "illicit_income": "illicit_income", "taxable_retirement_distributions": "retirement_distributions", "miscellaneous_income": "miscellaneous_income", @@ -601,3 +611,76 @@ def change(name): expected = 0 if name in PROPERTY_TAX_BILL_REDUCTIONS else amount assert household_change == pytest.approx(expected, abs=TOLERANCE), name assert spm_change == pytest.approx(expected, abs=TOLERANCE), name + + +# Federal cash benefits, paid in every state to the person who receives them. +# Census P60-200 Appendix A counts veterans' payments as money income and +# railroad retirement as a government transfer, so both totals count them. +FEDERAL_CASH_BENEFITS = ("veterans_benefits", "railroad_benefits") + + +@settings(max_examples=30, deadline=None) +@given( + name=st.sampled_from(FEDERAL_CASH_BENEFITS), + amount=st.integers(min_value=1, max_value=60_000), + state=st.sampled_from(STATES), +) +@example(name="veterans_benefits", amount=12_000, state="TX") +@example(name="railroad_benefits", amount=30_000, state="VA") +def test_a_dollar_of_federal_cash_benefits_adds_a_dollar_to_both_totals( + name, amount, state +): + year = 2026 + # Earnings this high rule out every means-tested response. + zero = Simulation( + situation=one_person(state, 500_000, year, **{name: ("person", 0)}) + ) + paid = Simulation( + situation=one_person(state, 500_000, year, **{name: ("person", amount)}) + ) + for names in ( + ("spm_unit_benefits",), + ("household_benefits",), + SPM_RESOURCES, + HOUSEHOLD_RESOURCES, + ): + change = total(paid, names, year) - total(zero, names, year) + assert change == pytest.approx(amount, abs=TOLERANCE), names + + +@settings(max_examples=20, deadline=None) +@given( + amount=st.integers(min_value=1, max_value=60_000), + earnings=st.integers(min_value=0, max_value=300_000), + state=st.sampled_from(STATES), +) +@example(amount=30_000, earnings=0, state="TX") +@example(amount=20_000, earnings=45_000, state="VT") +def test_veterans_benefits_stay_out_of_federal_gross_income(amount, earnings, state): + """38 U.S.C. 5301(a)(1) exempts VA benefits from taxation. + + The IRS still counts them as income for the optional sales tax tables + (state_sales_tax.yaml), so a filer who itemizes and deducts sales tax can + owe less federal income tax with them. The test checks income tax only + when neither household itemizes. The person is 40 and has no disability, + so the credit for the elderly or disabled doesn't apply. 26 U.S.C. + 22(c)(3)(A)(i)(III) reduces that credit by VA pensions, and the model + doesn't yet. + """ + year = 2026 + zero = Simulation( + situation=one_person(state, earnings, year, veterans_benefits=("person", 0)) + ) + paid = Simulation( + situation=one_person( + state, earnings, year, veterans_benefits=("person", amount) + ) + ) + for name in ("irs_gross_income", "adjusted_gross_income"): + assert total(paid, [name], year) == pytest.approx( + total(zero, [name], year), abs=TOLERANCE + ), name + if not any(total(s, ["tax_unit_itemizes"], year) for s in (zero, paid)): + assert total(paid, ["income_tax"], year) == pytest.approx( + total(zero, ["income_tax"], year), abs=TOLERANCE + ) diff --git a/policyengine_us/tests/test_railroad_retirement_taxation.py b/policyengine_us/tests/test_railroad_retirement_taxation.py new file mode 100644 index 00000000000..5b7c54713e7 --- /dev/null +++ b/policyengine_us/tests/test_railroad_retirement_taxation.py @@ -0,0 +1,508 @@ +"""Property tests for the taxation of Railroad Retirement Act benefits. + +Invariants, for every input the strategies draw: + +I1. IRC 86(d)(1): moving any part of a filer's benefits from title II to the + tier 1 social security equivalent benefit leaves federal AGI, federal + income tax, the section 86 taxable amount, Medicaid MAGI and the section + 22 amount unchanged. Section 22(c)(3)(A)(i) reduces that amount by + nontaxable title II benefits under (I) and by nontaxable Railroad + Retirement Act benefits under (II). +I2. IRC 72(r)(1): moving any amount from the taxable part of a private + pension to the taxable part of the rest of the railroad annuity leaves + federal AGI and federal income tax unchanged. +I3. 45 U.S.C. 231m(a): a household whose only income is railroad benefits + owes no state income tax in any state in 2025 or 2026, alternative and + minimum taxes included, and no state's withholding estimate counts the + benefits. + Idaho's $10 permanent building fund tax falls on every required filer, + so there the test checks that taxable income is zero. +I4. For a wage earner under 65 in 2025, with wages up to $120,000 and + benefits up to $70,000, adding railroad benefits leaves the state's AGI + unchanged, and in states whose reported AGI includes them it leaves state + taxable income unchanged. Colorado and South Carolina start from federal + taxable income, which floors at zero; while the standard deduction + absorbs some of the benefits, their subtraction exceeds what federal + taxable income gained, so there the base can only fall. + STATE_BASE_EXCEPTIONS lists the states with a provision measured with + federal AGI or federal income tax that binds in that range. Outside it, + more such provisions bind: Minnesota reduces its standard deduction with + federal AGI above $238,950 in 2025 and phases out its dependent + exemptions with federal AGI, and from 2026 South Carolina phases out its + income adjusted deduction (SCIAD) with federal AGI. +I5. Totals: when railroad_benefits itself is left unset, it is the sum of + its three parts; the title II and tier 1 shares sum to the section 86 + taxable amount; each spouse carries the share of the unit's taxable tier + 1 amount that their own tier 1 benefits make up; and + taxable_railroad_benefits is what the head's and spouse's federal AGI + includes from the Act, and zero for dependents. +I6. Taxpayer Relief Act section 1303, "Income" (2): each dollar of railroad + benefits a filer or spouse receives, split among the three parts in any + way or entered as a total, raises Pennsylvania Property Tax/Rent Rebate + income by 50 cents, whatever share of it federal AGI holds. +D1. Differential: section 86 runs once on the head and spouse's return and + once on a dependent's own return. For the same income and benefits the + two give the same taxable amount. + +YAML tests pin worked examples; these properties quantify over amounts, +filing status and every state, which YAML cannot. Each example builds one +Simulation that holds every household it compares. + +The model keeps money in float32, whose step grows with the amount: 2^-7 +from $65,536 to $131,072 and 2^-6 from there to $262,144. Two calculations +that add the same dollars in a different order can land a few steps apart, +so the comparisons allow eight steps at the sum of the income inputs, and +never less than a cent. +""" + +import numpy as np +import pytest +from hypothesis import example, given, settings +from hypothesis import strategies as st + +from policyengine_us import Simulation + +YEAR = 2025 +CENT = 0.01 + + +def tolerance(*amounts): + """Eight float32 steps at the largest of the amounts, or a cent.""" + largest = max(abs(float(amount)) for amount in amounts) + return max(CENT, 8 * float(np.spacing(np.float32(largest)))) + + +INCOME_TAX_STATES = ( + "AL AZ AR CA CO CT DE DC GA HI ID IL IN IA KS KY LA ME MD MA MI MN MS MO " + "MT NE NJ NM NY NC ND OH OK OR PA RI SC UT VT VA WV WI" +).split() + +# States whose AGI as taxsim_state_agi reports it includes railroad benefits: +# the states_using_federal_agi list, which reports federal AGI, and Utah, +# which reports TC-40 line 6 total income (federal AGI plus additions) and +# subtracts the benefits on the way to taxable income. I4 compares their +# taxable income. +FEDERAL_AGI_STATES = {"CO", "MI", "MN", "MT", "NC", "ND", "NM", "SC", "UT"} + +# co_taxable_income and, before the SCIAD, sc_taxable_income start from +# taxable_income, which floors at zero, and subtract the benefits federal AGI +# includes. North Dakota starts from AGI less deductions without the floor. +FEDERAL_TAXABLE_INCOME_STATES = {"CO", "SC"} + +# Provisions measured with federal AGI or federal income tax. Railroad +# benefits raise both, so these states' bases move by the state's own rule. +STATE_BASE_EXCEPTIONS = { + # or_agi subtracts the federal income tax subtraction (ORS 316.680(1)(b)). + "OR": "federal income tax subtraction inside Oregon AGI", + # nm_low_and_middle_income_exemption phases out with adjusted_gross_income + # (NMSA 7-2-5.8; 7-2-2 defines adjusted gross income as federal AGI). + "NM": "low- and middle-income exemption phases out on federal AGI", +} +I4_STATES = [s for s in INCOME_TAX_STATES if s not in STATE_BASE_EXCEPTIONS] + +ROLE_FLAGS = ("is_tax_unit_head", "is_tax_unit_spouse", "is_tax_unit_dependent") + + +def situation(households, year=YEAR): + """Build a situation from (state, members) pairs. + + Each member is a dict of person inputs plus an optional "role": the first + member defaults to head and the rest to dependent. The head and spouse + share a marital unit; each dependent has one of its own. + """ + people, units, households_out, marital_units = {}, {}, {}, {} + for i, (state, members) in enumerate(households): + names, couple = [], [] + for j, inputs in enumerate(members): + inputs = dict(inputs) + role = inputs.pop("role", "head" if j == 0 else "dependent") + name = f"h{i}p{j}" + names.append(name) + people[name] = {key: {year: value} for key, value in inputs.items()} + for flag in ROLE_FLAGS: + people[name][flag] = {year: flag == f"is_tax_unit_{role}"} + if role == "dependent": + marital_units[f"{name}m"] = {"members": [name]} + else: + couple.append(name) + marital_units[f"h{i}m"] = {"members": couple} + units[f"h{i}"] = names + households_out[f"h{i}"] = {"members": names, "state_name": {year: state}} + + def group(): + return {key: {"members": list(value)} for key, value in units.items()} + + return { + "people": people, + "tax_units": group(), + "spm_units": group(), + "families": group(), + "marital_units": marital_units, + "households": households_out, + } + + +def calc(simulation, variable, year=YEAR): + return np.asarray(simulation.calculate(variable, year), dtype=float) + + +def section_86_parts(simulation): + return calc(simulation, "tax_unit_taxable_social_security") + calc( + simulation, "tax_unit_taxable_railroad_social_security_equivalent_benefits" + ) + + +amounts = st.integers(min_value=0, max_value=60_000) +wages = st.integers(min_value=0, max_value=150_000) +shares = st.integers(min_value=0, max_value=100).map(lambda x: x / 100) + + +@settings(max_examples=25, deadline=None, derandomize=True) +@given( + wages=wages, + head_benefits=amounts, + spouse_benefits=amounts, + head_share=shares, + spouse_share=shares, + joint=st.booleans(), + age=st.integers(min_value=62, max_value=85), +) +# At $131,072 of wages, float32 rounds the two households' AGI one $0.015625 +# step apart: the railroad household adds its title II and SSEB shares to AGI +# separately. +@example( + wages=131_072, + head_benefits=1, + spouse_benefits=0, + head_share=0.01, + spouse_share=0.0, + joint=False, + age=62, +) +# A 70-year-old with no other income keeps a section 22 amount: $5,000 less +# the $2,000 of nontaxable benefits, whichever kind they are. +@example( + wages=0, + head_benefits=2_000, + spouse_benefits=0, + head_share=1.0, + spouse_share=0.0, + joint=False, + age=70, +) +def test_i1_tier_1_benefits_are_taxed_as_title_ii_benefits( + wages, head_benefits, spouse_benefits, head_share, spouse_share, joint, age +): + def person(benefits, railroad_share, role): + return { + "role": role, + "age": age, + "social_security_retirement": benefits * (1 - railroad_share), + "railroad_social_security_equivalent_benefits": benefits * railroad_share, + } + + def household(head_share, spouse_share): + members = [person(head_benefits, head_share, "head")] + members[0]["employment_income"] = wages + if joint: + members.append(person(spouse_benefits, spouse_share, "spouse")) + return ("TX", members) + + simulation = Simulation( + situation=situation([household(0, 0), household(head_share, spouse_share)]) + ) + tol = tolerance(wages + head_benefits + (spouse_benefits if joint else 0)) + for variable in ( + "adjusted_gross_income", + "income_tax", + "tax_unit_taxable_section_86_benefits", + "medicaid_magi", + "section_22_income", + ): + title_ii, railroad = calc(simulation, variable)[:2] + assert abs(title_ii - railroad) < tol, (variable, title_ii, railroad) + np.testing.assert_allclose( + section_86_parts(simulation), + calc(simulation, "tax_unit_taxable_section_86_benefits"), + atol=tol, + ) + + +@settings(max_examples=25, deadline=None, derandomize=True) +@given( + wages=wages, + pension=st.integers(min_value=0, max_value=80_000), + title_ii=amounts, + age=st.integers(min_value=60, max_value=85), +) +def test_i2_the_rest_of_the_annuity_is_taxed_as_a_pension( + wages, pension, title_ii, age +): + base = { + "age": age, + "employment_income": wages, + "social_security_retirement": title_ii, + } + simulation = Simulation( + situation=situation( + [ + ("TX", [base | {"taxable_pension_income": pension}]), + ("TX", [base | {"taxable_railroad_pension_benefits": pension}]), + ] + ) + ) + tol = tolerance(wages + pension + title_ii) + for variable in ("adjusted_gross_income", "income_tax"): + private, railroad = calc(simulation, variable) + assert abs(private - railroad) < tol, (variable, private, railroad) + + +@settings(max_examples=6, deadline=None, derandomize=True) +@given( + tier_1=st.integers(min_value=0, max_value=60_000), + tier_2=st.integers(min_value=1, max_value=300_000), + tax_free=st.integers(min_value=0, max_value=5_000), + age=st.integers(min_value=18, max_value=90), + joint=st.booleans(), + year=st.sampled_from((2025, 2026)), +) +# Minnesota's AMT starts from federal AGI (Schedule M1MT line 1) and Vermont's +# minimum tax takes 3% of federal AGI over $150,000 (32 V.S.A. 5822(a)(6)). +# Both bases must leave the benefits out. +@example(tier_1=0, tier_2=64_255, tax_free=0, age=70, joint=True, year=2025) +@example(tier_1=0, tier_2=79_918, tax_free=0, age=18, joint=False, year=2025) +@example(tier_1=0, tier_2=262_000, tax_free=0, age=70, joint=False, year=2025) +@example(tier_1=0, tier_2=262_000, tax_free=0, age=70, joint=False, year=2026) +def test_i3_no_state_taxes_railroad_benefits( + tier_1, tier_2, tax_free, age, joint, year +): + head = { + "age": age, + "railroad_social_security_equivalent_benefits": tier_1, + "taxable_railroad_pension_benefits": tier_2, + "tax_exempt_railroad_pension_benefits": tax_free, + } + members = [head] + ([head | {"role": "spouse"}] if joint else []) + simulation = Simulation( + situation=situation( + [(state, members) for state in INCOME_TAX_STATES], year=year + ) + ) + # Tier 2 benefits are always taxable federally, so the test is not vacuous. + assert calc(simulation, "adjusted_gross_income", year).min() >= tier_2 + tax = calc(simulation, "state_income_tax_before_refundable_credits", year) + idaho_taxable_income = calc(simulation, "id_taxable_income", year) + withholding = calc(simulation, "state_withheld_income_tax", year) + for i, state in enumerate(INCOME_TAX_STATES): + if state == "ID": + assert idaho_taxable_income[i] == 0, (state, idaho_taxable_income[i]) + else: + assert abs(tax[i]) < CENT, (state, tax[i]) + assert abs(withholding[i]) < CENT, (state, withholding[i]) + + +@settings(max_examples=6, deadline=None, derandomize=True) +@given( + wages=st.integers(min_value=0, max_value=120_000), + tier_1=st.integers(min_value=0, max_value=30_000), + tier_2=st.integers(min_value=0, max_value=40_000), + age=st.integers(min_value=25, max_value=64), +) +# South Carolina adds back $750 of the 2025 standard deduction for every +# non-itemizer, so a dollar of benefits the standard deduction absorbs still +# lowers its base from 750 to 749. +@example(wages=0, tier_1=0, tier_2=1, age=25) +@example(wages=40_000, tier_1=12_000, tier_2=15_000, age=50) +def test_i4_railroad_benefits_leave_state_bases_unchanged(wages, tier_1, tier_2, age): + worker = {"age": age, "employment_income": wages} + retiree = worker | { + "railroad_social_security_equivalent_benefits": tier_1, + "taxable_railroad_pension_benefits": tier_2, + } + households = [(state, [worker]) for state in I4_STATES] + [ + (state, [retiree]) for state in I4_STATES + ] + simulation = Simulation(situation=situation(households)) + n = len(I4_STATES) + agi = calc(simulation, "taxsim_state_agi") + taxable_income = calc(simulation, "taxsim_state_taxable_income") + federal_taxable_income = calc(simulation, "taxable_income") + tol = tolerance(wages + tier_1 + tier_2) + for i, state in enumerate(I4_STATES): + base = taxable_income if state in FEDERAL_AGI_STATES else agi + before, after = base[i], base[i + n] + floored = federal_taxable_income[i] <= 0 + if state in FEDERAL_TAXABLE_INCOME_STATES and floored: + assert after <= before + tol, (state, before, after) + else: + assert abs(before - after) < tol, (state, before, after) + + +@settings(max_examples=25, deadline=None, derandomize=True) +@given( + tier_1=st.lists(amounts, min_size=3, max_size=3), + tier_2=st.lists(amounts, min_size=3, max_size=3), + tax_free=st.lists( + st.integers(min_value=0, max_value=5_000), min_size=3, max_size=3 + ), + title_ii=st.lists(amounts, min_size=3, max_size=3), + earnings=st.lists(wages, min_size=3, max_size=3), +) +def test_i5_railroad_totals_add_up(tier_1, tier_2, tax_free, title_ii, earnings): + roles, ages = ("head", "spouse", "dependent"), (70, 68, 17) + members = [ + { + "role": roles[i], + "age": ages[i], + "employment_income": earnings[i], + "social_security_retirement": title_ii[i], + "railroad_social_security_equivalent_benefits": tier_1[i], + "taxable_railroad_pension_benefits": tier_2[i], + "tax_exempt_railroad_pension_benefits": tax_free[i], + } + for i in range(3) + ] + simulation = Simulation(situation=situation([("TX", members)])) + tol = tolerance(sum(earnings) + sum(title_ii) + sum(tier_1) + sum(tier_2)) + np.testing.assert_allclose( + calc(simulation, "railroad_benefits"), + np.add(np.add(tier_1, tier_2), tax_free), + ) + np.testing.assert_allclose( + section_86_parts(simulation), + calc(simulation, "tax_unit_taxable_section_86_benefits"), + atol=tol, + ) + taxable_tier_1 = calc( + simulation, "taxable_railroad_social_security_equivalent_benefits" + ) + # The head's and spouse's shares add up to the tax unit's tier 1 amount. + assert taxable_tier_1[2] == 0 + unit_taxable_tier_1 = calc( + simulation, "tax_unit_taxable_railroad_social_security_equivalent_benefits" + )[0] + np.testing.assert_allclose(taxable_tier_1.sum(), unit_taxable_tier_1, atol=tol) + # Each spouse carries the share of it that their own tier 1 benefits make up. + couple_tier_1 = tier_1[0] + tier_1[1] + if couple_tier_1 > 0: + np.testing.assert_allclose( + taxable_tier_1[:2], + unit_taxable_tier_1 * np.array(tier_1[:2], dtype=float) / couple_tier_1, + atol=tol, + ) + np.testing.assert_allclose( + calc(simulation, "taxable_railroad_benefits"), + taxable_tier_1 + np.array(tier_2, dtype=float) * [1, 1, 0], + atol=tol, + ) + + +@settings(max_examples=10, deadline=None, derandomize=True) +@given( + tier_1=amounts, + tier_2=amounts, + tax_free=st.integers(min_value=0, max_value=5_000), + title_ii=amounts, + earnings=wages, + joint=st.booleans(), +) +# YAML Cases 3a and 3b of pa_property_tax_or_rent_rebate_income. +@example( + tier_1=10_000, tier_2=10_000, tax_free=2_000, title_ii=0, earnings=0, joint=False +) +@example(tier_1=20_000, tier_2=30_000, tax_free=0, title_ii=0, earnings=0, joint=False) +def test_i6_pennsylvania_rebate_income_counts_half_of_railroad_benefits( + tier_1, tier_2, tax_free, title_ii, earnings, joint +): + total = tier_1 + tier_2 + tax_free + # SSI and the state supplement would fall as railroad benefits rise, and + # the rebate half-counts them too, so both stay at zero. + head = {"age": 67, "employment_income": earnings, "ssi": 0, "pa_ssp": 0} + spouse = {"role": "spouse", "age": 67, "ssi": 0, "pa_ssp": 0} + if joint: + spouse["social_security_retirement"] = title_ii + else: + head["social_security_retirement"] = title_ii + + def household(railroad): + return ("PA", [head | railroad] + ([spouse] if joint else [])) + + def added_income(railroad): + # A variable that any person enters becomes an input for every person, + # with 0 for the rest, so a household that enters the three tiers and + # one that enters the total cannot share a simulation. Each runs beside + # a household without railroad benefits. + simulation = Simulation( + situation=situation([household({}), household(railroad)]) + ) + income = calc(simulation, "pa_property_tax_or_rent_rebate_income") + return income[1] - income[0] + + tiers = added_income( + { + "railroad_social_security_equivalent_benefits": tier_1, + "taxable_railroad_pension_benefits": tier_2, + "tax_exempt_railroad_pension_benefits": tax_free, + } + ) + entered_total = added_income({"railroad_benefits": total}) + tol = tolerance(earnings + title_ii + total) + assert abs(tiers - total / 2) < tol, tiers + assert abs(entered_total - total / 2) < tol, entered_total + + +@settings(max_examples=25, deadline=None, derandomize=True) +@given(wages=wages, title_ii=amounts, tier_1=amounts) +def test_d1_dependent_and_filer_section_86_agree(wages, title_ii, tier_1): + income = { + "employment_income": wages, + "social_security_survivors": title_ii, + "railroad_social_security_equivalent_benefits": tier_1, + } + simulation = Simulation( + situation=situation( + [ + ("TX", [{"age": 40} | income]), + ("TX", [{"age": 45}, {"age": 17} | income]), + ] + ) + ) + filer = calc(simulation, "tax_unit_taxable_section_86_benefits")[0] + dependent = ( + calc(simulation, "dependent_taxable_social_security") + + calc( + simulation, "dependent_taxable_railroad_social_security_equivalent_benefits" + ) + )[2] + assert abs(filer - dependent) < tolerance(wages + title_ii + tier_1), ( + filer, + dependent, + ) + + +@pytest.mark.parametrize("amount", [-5_000, float("inf")]) +def test_negative_or_infinite_tier_1_benefits_raise(amount): + """Box 5 of Form RRB-1099 can be negative when repayments exceed the + year's benefits. The model doesn't net such amounts as IRS Publication 915 + does, so it rejects them, as it rejects negative title II benefits. It + also rejects an infinite amount. policyengine-core refuses a NaN input + before any formula runs, so that case needs no test here.""" + simulation = Simulation( + situation=situation( + [ + ( + "TX", + [ + { + "age": 70, + "employment_income": 20_000, + "social_security_retirement": 20_000, + "railroad_social_security_equivalent_benefits": amount, + } + ], + ) + ] + ) + ) + with pytest.raises(ValueError, match="finite and nonnegative"): + simulation.calculate("adjusted_gross_income", YEAR) diff --git a/policyengine_us/tests/test_state_sales_tax_table.py b/policyengine_us/tests/test_state_sales_tax_table.py index f23bdeacc93..7bdd0f4164a 100644 --- a/policyengine_us/tests/test_state_sales_tax_table.py +++ b/policyengine_us/tests/test_state_sales_tax_table.py @@ -94,15 +94,17 @@ # the nontaxable part of social security and railroad retirement benefits; the # nontaxable part of IRA, pension, or annuity distributions; and public # assistance payments. -# These are the PolicyEngine variables for those items. Combat pay and -# railroad retirement benefits are not included. +# These are the PolicyEngine variables for those items. Combat pay is not +# included. TABLE_INCOME_SOURCES = [ "adjusted_gross_income", "tax_exempt_interest_income", "veterans_benefits", "workers_compensation", "tax_exempt_social_security", + "tax_exempt_railroad_social_security_equivalent_benefits", "tax_exempt_pension_income", + "tax_exempt_railroad_pension_benefits", "tax_exempt_retirement_distributions", "tanf", "ssi", diff --git a/policyengine_us/variables/contrib/taxsim/taxsim_gssi.py b/policyengine_us/variables/contrib/taxsim/taxsim_gssi.py index b56f9a73a9d..9f8e956fe81 100644 --- a/policyengine_us/variables/contrib/taxsim/taxsim_gssi.py +++ b/policyengine_us/variables/contrib/taxsim/taxsim_gssi.py @@ -6,6 +6,12 @@ class taxsim_gssi(Variable): entity = TaxUnit label = "Gross Social Security Income" unit = USD + documentation = ( + "Social Security benefits and the social security equivalent part of " + "tier 1 railroad retirement benefits, which section 86(d)(1) counts " + "together, so taxsim_v12 runs section 86 on both." + ) + reference = "https://www.law.cornell.edu/uscode/text/26/86#d_1" definition_period = YEAR - adds = ["social_security"] + adds = ["social_security", "railroad_social_security_equivalent_benefits"] diff --git a/policyengine_us/variables/gov/irs/credits/elderly_and_disabled/section_22_income.py b/policyengine_us/variables/gov/irs/credits/elderly_and_disabled/section_22_income.py index 7f37ba286b1..7ca6fd74d24 100644 --- a/policyengine_us/variables/gov/irs/credits/elderly_and_disabled/section_22_income.py +++ b/policyengine_us/variables/gov/irs/credits/elderly_and_disabled/section_22_income.py @@ -11,6 +11,8 @@ class section_22_income(Variable): reference = ( "https://www.law.cornell.edu/uscode/text/26/22#c", "https://www.law.cornell.edu/uscode/text/26/22#e_1", + # 2024 Instructions for Schedule R, lines 13a and 13b. + "https://www.irs.gov/pub/irs-prior/i1040sr--2024.pdf#page=5", ) def formula(tax_unit, period, parameters): @@ -62,8 +64,30 @@ def formula(tax_unit, period, parameters): total_social_security = tax_unit("tax_unit_social_security", period) taxable_social_security = tax_unit("tax_unit_taxable_social_security", period) non_taxable_social_security = total_social_security - taxable_social_security + # Section 22(c)(3)(A)(i)(II) reduces the amount by what the head or + # spouse receives as a pension or annuity payable under the Railroad + # Retirement Act of 1974 and excluded from gross income, whatever + # provision excludes it: the tier 1 benefits section 86 leaves out, + # and the tax-free part of the rest of the annuity, which recovers + # the employee's tier 2 taxes (72(r)(2); 72(d)'s Simplified Method, + # or 72(b) for some annuities that started before November 19, 1996). + # Schedule R's line 13b keeps returns of cost out; the model follows + # the statute's words. + non_taxable_tier_1 = tax_unit( + "tax_unit_railroad_social_security_equivalent_benefits_for_taxability", + period, + ) - tax_unit( + "tax_unit_taxable_railroad_social_security_equivalent_benefits", period + ) + non_taxable_tier_2 = tax_unit_non_dep_add( + tax_unit, period, ["tax_exempt_railroad_pension_benefits"] + ) + non_taxable_railroad = non_taxable_tier_1 + non_taxable_tier_2 capped_reduced_amount = ( - capped_amount - non_taxable_pensions - non_taxable_social_security + capped_amount + - non_taxable_pensions + - non_taxable_social_security + - non_taxable_railroad ) agi = tax_unit("adjusted_gross_income", period) diff --git a/policyengine_us/variables/gov/irs/income/dependent_gross_income.py b/policyengine_us/variables/gov/irs/income/dependent_gross_income.py index f9e7c0859be..78d87e0c04b 100644 --- a/policyengine_us/variables/gov/irs/income/dependent_gross_income.py +++ b/policyengine_us/variables/gov/irs/income/dependent_gross_income.py @@ -14,6 +14,10 @@ # computed in dependent_taxable_social_security, on the dependent's own # income, and modified AGI is figured without it (IRC 86(b)(2)(A)). "taxable_social_security": [], + # Tier 1 railroad retirement benefits are social security benefits under + # IRC 86(d)(1)(B), handled the same way in + # dependent_taxable_railroad_social_security_equivalent_benefits. + "taxable_railroad_social_security_equivalent_benefits": [], # Gross income includes gains but not losses (computed below). Modified # AGI nets them, subject to the IRC 1211(b) limit. "capital_gains": [], @@ -74,6 +78,12 @@ def formula(person, period, parameters): if "taxable_social_security" in sources: gross_income += person("dependent_taxable_social_security", period) + if "taxable_railroad_social_security_equivalent_benefits" in sources: + gross_income += person( + "dependent_taxable_railroad_social_security_equivalent_benefits", + period, + ) + # Only dependents' own income is tested. is_dependent = ~person("is_tax_unit_head_or_spouse", period) return is_dependent * gross_income diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/capped_qualified_tuition_expenses_ald.py b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/capped_qualified_tuition_expenses_ald.py index ac0389d9366..88fc240475e 100644 --- a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/capped_qualified_tuition_expenses_ald.py +++ b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/capped_qualified_tuition_expenses_ald.py @@ -1,4 +1,7 @@ from policyengine_us.model_api import * +from policyengine_us.variables.gov.irs.income.taxable_income.adjusted_gross_income.irs_gross_income.social_security.tax_unit_taxable_section_86_benefits import ( + SECTION_86_GROSS_INCOME_SOURCES, +) class capped_qualified_tuition_expenses_ald(Variable): @@ -49,7 +52,7 @@ def formula(tax_unit, period, parameters): else src ) for src in gross_income_sources - if src != "taxable_social_security" + if src not in SECTION_86_GROSS_INCOME_SOURCES ] magi = 0 for source in safe_sources: diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/ira/dependent_traditional_ira_deduction.py b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/ira/dependent_traditional_ira_deduction.py index 8b896ddd0e6..e97df76a897 100644 --- a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/ira/dependent_traditional_ira_deduction.py +++ b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/ira/dependent_traditional_ira_deduction.py @@ -3,7 +3,7 @@ dependent_income_and_deductions, ) from policyengine_us.variables.gov.irs.income.taxable_income.adjusted_gross_income.irs_gross_income.social_security.dependent_taxable_social_security import ( - dependent_taxable_social_security_amount, + dependent_taxable_section_86_amount, ) from policyengine_us.variables.household.expense.retirement.ira_compensation import ( dependent_ira_compensation, @@ -63,7 +63,7 @@ def formula(person, period, parameters): d for d in deductions if d != "dependent_traditional_ira_deduction" ] agi_before_ira = income - add(person, period, other_deductions) - taxable_social_security = dependent_taxable_social_security_amount( + taxable_social_security = dependent_taxable_section_86_amount( person, period, parameters, diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/ira/ira_219g_magi.py b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/ira/ira_219g_magi.py index 6aa08f7f45e..b34fcb269c6 100644 --- a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/ira/ira_219g_magi.py +++ b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/ira/ira_219g_magi.py @@ -1,4 +1,7 @@ from policyengine_us.model_api import * +from policyengine_us.variables.gov.irs.income.taxable_income.adjusted_gross_income.irs_gross_income.social_security.tax_unit_taxable_section_86_benefits import ( + SECTION_86_GROSS_INCOME_SOURCES, +) class ira_219g_magi(Variable): @@ -17,7 +20,8 @@ def formula(tax_unit, period, parameters): sources = [ s for s in irs.gross_income.sources - if s not in ["taxable_social_security", "taxable_unemployment_compensation"] + if s not in SECTION_86_GROSS_INCOME_SOURCES + and s != "taxable_unemployment_compensation" ] if "taxable_unemployment_compensation" in irs.gross_income.sources: # 219(g)(3)(A)(ii): without regard to 85(c). diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/ira/ira_219g_taxable_social_security.py b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/ira/ira_219g_taxable_social_security.py index 9bf574bb41c..2f1da03d315 100644 --- a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/ira/ira_219g_taxable_social_security.py +++ b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/ira/ira_219g_taxable_social_security.py @@ -1,12 +1,16 @@ from policyengine_us.model_api import * +from policyengine_us.variables.gov.irs.income.taxable_income.adjusted_gross_income.irs_gross_income.social_security.tax_unit_taxable_section_86_benefits import ( + SECTION_86_GROSS_INCOME_SOURCES, +) class ira_219g_taxable_social_security(Variable): """Section 86 taxable benefits figured without the IRA deduction. Pub. 590-A Appendix B, Worksheet 1 lines 1-17. Same inputs and thresholds as - the engine's taxable_ss_magi / tax_unit_taxable_social_security, except that - the IRA deduction is not subtracted (it is what is being computed). + the engine's taxable_ss_magi / tax_unit_taxable_section_86_benefits, except + that the IRA deduction is not subtracted (it is what is being computed). + Like section 86, it covers title II and tier 1 railroad benefits together. """ value_type = float @@ -26,7 +30,8 @@ def formula(tax_unit, period, parameters): sources = [ s for s in irs.gross_income.sources - if s not in ["taxable_social_security", "taxable_unemployment_compensation"] + if s not in SECTION_86_GROSS_INCOME_SOURCES + and s != "taxable_unemployment_compensation" ] if "taxable_unemployment_compensation" in irs.gross_income.sources: sources.append("total_unemployment_compensation") @@ -54,7 +59,7 @@ def formula(tax_unit, period, parameters): ) ss_magi = max_(0, gross - total_deductions) # tax_unit_taxable_social_security, with that MAGI. - gross_ss = tax_unit("tax_unit_social_security_for_taxability", period) + gross_ss = tax_unit("tax_unit_section_86_benefits", period) combined = ss_magi + p.combined_income_ss_fraction * gross_ss filing_status = tax_unit("filing_status", period) separate = filing_status == filing_status.possible_values.SEPARATE diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/dependent_taxable_railroad_social_security_equivalent_benefits.py b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/dependent_taxable_railroad_social_security_equivalent_benefits.py new file mode 100644 index 00000000000..cced20dfe17 --- /dev/null +++ b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/dependent_taxable_railroad_social_security_equivalent_benefits.py @@ -0,0 +1,29 @@ +from policyengine_us.model_api import * +from policyengine_us.variables.gov.irs.income.taxable_income.adjusted_gross_income.irs_gross_income.social_security.dependent_taxable_social_security import ( + dependent_taxable_section_86_share, +) + + +class dependent_taxable_railroad_social_security_equivalent_benefits(Variable): + value_type = float + entity = Person + label = ( + "Dependent's taxable Railroad Retirement social security equivalent benefits" + ) + unit = USD + documentation = ( + "The tier 1 railroad retirement share of the social security benefits " + "a dependent includes in gross income under IRC 86, figured on the " + "dependent's own return as in dependent_taxable_social_security." + ) + definition_period = YEAR + defined_for = "is_tax_unit_dependent" + reference = ( + "https://www.law.cornell.edu/uscode/text/26/86#d_1_B", + "https://www.law.cornell.edu/uscode/text/26/152#b_2", + ) + + def formula(person, period, parameters): + return dependent_taxable_section_86_share( + person, period, parameters, title_ii=False + ) diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/dependent_taxable_social_security.py b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/dependent_taxable_social_security.py index 0301ca340a3..4998f5678c8 100644 --- a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/dependent_taxable_social_security.py +++ b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/dependent_taxable_social_security.py @@ -1,11 +1,21 @@ from policyengine_us.model_api import * -def dependent_taxable_social_security_amount(person, period, parameters, modified_agi): - """A dependent's taxable Social Security under IRC 86 for a given modified - AGI, on the dependent's own marital circumstances.""" +def dependent_section_86_benefits(person, period): + """A dependent's social security benefits as IRC 86(d)(1) defines them: + title II benefits and tier 1 railroad retirement benefits.""" + title_ii = max_(0, person("social_security", period)) + tier_1 = max_(0, person("railroad_social_security_equivalent_benefits", period)) + return title_ii, tier_1 + + +def dependent_taxable_section_86_amount(person, period, parameters, modified_agi): + """A dependent's taxable social security benefits under IRC 86 for a given + modified AGI, on the dependent's own marital circumstances. Section 86 + figures one amount on title II and tier 1 railroad benefits together.""" p = parameters(period).gov.irs.social_security.taxability - gross_ss = max_(0, person("social_security", period)) + title_ii, tier_1 = dependent_section_86_benefits(person, period) + gross_ss = title_ii + tier_1 # IRC 86(b)(1): modified AGI plus one-half of the benefits. combined_income = modified_agi + p.combined_income_ss_fraction * gross_ss lives_with_spouse = person("dependent_lives_with_spouse", period) @@ -61,7 +71,11 @@ class dependent_taxable_social_security(Variable): dependent does not file a joint return (IRC 152(b)(2)), so the joint amounts never apply. A dependent who lives with their spouse has zero base and adjusted base amounts under IRC 86(c)(1)(C) and (c)(2)(C), and - every other dependent has the unmarried amounts. + every other dependent has the unmarried amounts. Section 86 figures one + amount on title II and tier 1 railroad retirement benefits together; this + is the title II share, and + dependent_taxable_railroad_social_security_equivalent_benefits is the + railroad share. """ definition_period = YEAR defined_for = "is_tax_unit_dependent" @@ -76,6 +90,21 @@ class dependent_taxable_social_security(Variable): ) def formula(person, period, parameters): - return dependent_taxable_social_security_amount( - person, period, parameters, person("dependent_taxable_ss_magi", period) + return dependent_taxable_section_86_share( + person, period, parameters, title_ii=True ) + + +def dependent_taxable_section_86_share(person, period, parameters, title_ii): + """The title II or tier 1 railroad share of a dependent's taxable section + 86 benefits, in proportion to the two kinds of benefits.""" + taxable = dependent_taxable_section_86_amount( + person, period, parameters, person("dependent_taxable_ss_magi", period) + ) + title_ii_benefits, tier_1_benefits = dependent_section_86_benefits(person, period) + benefits = title_ii_benefits if title_ii else tier_1_benefits + total = title_ii_benefits + tier_1_benefits + share = np.zeros_like(total) + mask = total > 0 + share[mask] = benefits[mask] / total[mask] + return taxable * share diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/tax_exempt_railroad_social_security_equivalent_benefits.py b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/tax_exempt_railroad_social_security_equivalent_benefits.py new file mode 100644 index 00000000000..a4120a82590 --- /dev/null +++ b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/tax_exempt_railroad_social_security_equivalent_benefits.py @@ -0,0 +1,17 @@ +from policyengine_us.model_api import * + + +class tax_exempt_railroad_social_security_equivalent_benefits(Variable): + value_type = float + entity = TaxUnit + label = "Tax-exempt Railroad Retirement social security equivalent benefits" + unit = USD + documentation = ( + "Tier 1 railroad retirement benefits that section 86 leaves out of " + "AGI, the counterpart of tax_exempt_social_security." + ) + definition_period = YEAR + reference = "https://www.law.cornell.edu/uscode/text/26/86#d_1_B" + + adds = ["tax_unit_railroad_social_security_equivalent_benefits"] + subtracts = ["tax_unit_taxable_railroad_social_security_equivalent_benefits"] diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/tax_unit_combined_income_for_social_security_taxability.py b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/tax_unit_combined_income_for_social_security_taxability.py index c2e13d72751..181a96af356 100644 --- a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/tax_unit_combined_income_for_social_security_taxability.py +++ b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/tax_unit_combined_income_for_social_security_taxability.py @@ -14,7 +14,9 @@ class tax_unit_combined_income_for_social_security_taxability(Variable): def formula(tax_unit, period, parameters): p = parameters(period).gov.irs.social_security.taxability - gross_ss = tax_unit("tax_unit_social_security_for_taxability", period) + # IRC 86(b)(1)(A)(ii) and (d)(1): one-half of the social security + # benefits, title II and tier 1 railroad benefits alike. + gross_ss = tax_unit("tax_unit_section_86_benefits", period) # The legislation directs the usage of an income definition that is # a particularly modified AGI, plus half of gross Social Security diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/tax_unit_railroad_social_security_equivalent_benefits.py b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/tax_unit_railroad_social_security_equivalent_benefits.py new file mode 100644 index 00000000000..db816c3528f --- /dev/null +++ b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/tax_unit_railroad_social_security_equivalent_benefits.py @@ -0,0 +1,33 @@ +from policyengine_us.model_api import * + + +class tax_unit_railroad_social_security_equivalent_benefits(Variable): + value_type = float + entity = TaxUnit + label = "Tax unit Railroad Retirement social security equivalent benefits" + unit = USD + documentation = ( + "The members' social security equivalent benefits. As with reported " + "Social Security benefits, each amount must be finite and " + "nonnegative. When repayments exceed a year's benefits, box 5 of " + "Form RRB-1099 shows a negative amount, which IRS Publication 915 " + "offsets against the box 5 amounts on the filer's other forms and, " + "on a joint return, the spouse's; the model doesn't net them." + ) + definition_period = YEAR + reference = ( + "https://www.law.cornell.edu/uscode/text/26/86#d_1_B", + # Pub. 915 (2024), "Repayments More Than Gross Benefits", PDF page 15. + "https://www.irs.gov/pub/irs-prior/p915--2024.pdf#page=15", + ) + + def formula(tax_unit, period, parameters): + benefits = tax_unit.members( + "railroad_social_security_equivalent_benefits", period + ) + if np.any(~np.isfinite(benefits) | (benefits < 0)): + raise ValueError( + "railroad_social_security_equivalent_benefits must be finite " + "and nonnegative, as reported Social Security benefits must be" + ) + return tax_unit.sum(benefits) diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/tax_unit_railroad_social_security_equivalent_benefits_for_taxability.py b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/tax_unit_railroad_social_security_equivalent_benefits_for_taxability.py new file mode 100644 index 00000000000..77b1a6182d9 --- /dev/null +++ b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/tax_unit_railroad_social_security_equivalent_benefits_for_taxability.py @@ -0,0 +1,28 @@ +from policyengine_us.model_api import * + + +class tax_unit_railroad_social_security_equivalent_benefits_for_taxability(Variable): + value_type = float + entity = TaxUnit + label = ( + "Tax unit Railroad Retirement social security equivalent benefits " + "for taxability" + ) + unit = USD + documentation = ( + "Social security equivalent benefits of the head and spouse. As in " + "tax_unit_social_security_for_taxability, a tax unit dependent's " + "benefits are the dependent's own income." + ) + definition_period = YEAR + reference = "https://www.law.cornell.edu/uscode/text/26/86#d_1_B" + + def formula(tax_unit, period, parameters): + person = tax_unit.members + total = tax_unit( + "tax_unit_railroad_social_security_equivalent_benefits", period + ) + is_dependent = person("is_tax_unit_dependent", period) + benefits = person("railroad_social_security_equivalent_benefits", period) + dependent_benefits = tax_unit.sum(is_dependent * benefits) + return max_(0, total - dependent_benefits) diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/tax_unit_section_86_benefits.py b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/tax_unit_section_86_benefits.py new file mode 100644 index 00000000000..03ab927a72e --- /dev/null +++ b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/tax_unit_section_86_benefits.py @@ -0,0 +1,26 @@ +from policyengine_us.model_api import * + + +class tax_unit_section_86_benefits(Variable): + value_type = float + entity = TaxUnit + label = "Tax unit social security benefits under section 86" + unit = USD + documentation = ( + "The head's and spouse's social security benefits as IRC 86(d)(1) " + "defines them: title II benefits and tier 1 railroad retirement " + "benefits (the social security equivalent benefit). Section 86 figures " + "one taxable amount on their total." + ) + definition_period = YEAR + reference = ( + "https://www.law.cornell.edu/uscode/text/26/86#d_1", + # IRS Pub. 915 (2024), Worksheet A line A: box 5 of all Forms + # SSA-1099 and RRB-1099. + "https://www.irs.gov/pub/irs-prior/p915--2024.pdf#page=4", + ) + + adds = [ + "tax_unit_social_security_for_taxability", + "tax_unit_railroad_social_security_equivalent_benefits_for_taxability", + ] diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/tax_unit_taxable_railroad_social_security_equivalent_benefits.py b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/tax_unit_taxable_railroad_social_security_equivalent_benefits.py new file mode 100644 index 00000000000..113d0c8b302 --- /dev/null +++ b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/tax_unit_taxable_railroad_social_security_equivalent_benefits.py @@ -0,0 +1,32 @@ +from policyengine_us.model_api import * +from policyengine_us.variables.gov.irs.income.taxable_income.adjusted_gross_income.irs_gross_income.social_security.tax_unit_taxable_social_security import ( + section_86_share, +) + + +class tax_unit_taxable_railroad_social_security_equivalent_benefits(Variable): + value_type = float + entity = TaxUnit + definition_period = YEAR + label = "Taxable Railroad Retirement social security equivalent benefits" + documentation = ( + "Tier 1 railroad retirement benefits included in AGI: the share of " + "tax_unit_taxable_section_86_benefits that the head's and spouse's " + "social security equivalent benefits make up. It is figured from the " + "benefits, not as the total less taxable title II benefits, so a " + "change to tax_unit_taxable_social_security leaves it alone." + ) + unit = USD + reference = ( + "https://www.law.cornell.edu/uscode/text/26/86#d_1_B", + "https://www.law.cornell.edu/uscode/text/26/86#d_4", + ) + + def formula(tax_unit, period, parameters): + taxable = tax_unit("tax_unit_taxable_section_86_benefits", period) + share = section_86_share( + tax_unit, + period, + "tax_unit_railroad_social_security_equivalent_benefits_for_taxability", + ) + return taxable * share diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/tax_unit_taxable_section_86_benefits.py b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/tax_unit_taxable_section_86_benefits.py new file mode 100644 index 00000000000..de266f3a07b --- /dev/null +++ b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/tax_unit_taxable_section_86_benefits.py @@ -0,0 +1,104 @@ +from policyengine_us.model_api import * + +# Gross income sources that hold the benefits section 86 includes. Income +# measures that section 86 itself depends on (its modified AGI, and others +# built the same way) leave all of them out. +SECTION_86_GROSS_INCOME_SOURCES = ( + "taxable_social_security", + "taxable_railroad_social_security_equivalent_benefits", +) + + +class tax_unit_taxable_section_86_benefits(Variable): + value_type = float + entity = TaxUnit + definition_period = YEAR + label = "Taxable social security benefits under section 86" + documentation = ( + "The head's and spouse's social security benefits that IRC 86 includes " + "in gross income: title II benefits and tier 1 railroad retirement " + "benefits together, as on Form 1040 line 6b. " + "tax_unit_taxable_social_security and " + "tax_unit_taxable_railroad_social_security_equivalent_benefits split " + "it between the two in proportion to the benefits." + ) + unit = USD + reference = ( + "https://www.law.cornell.edu/uscode/text/26/86", + # IRS Pub. 915 (2024), Worksheet 1. + "https://www.irs.gov/pub/irs-prior/p915--2024.pdf#page=16", + ) + + def formula(tax_unit, period, parameters): + p = parameters(period).gov.irs.social_security.taxability + gross_ss = tax_unit("tax_unit_section_86_benefits", period) + + # The legislation directs the usage of an income definition that is + # a particularly modified AGI, plus half of gross Social Security + # payments. Per IRC Section 86(b)(1), this fraction is always 0.5 + # and is handled by the combined_income_ss_fraction parameter. + + combined_income = tax_unit( + "tax_unit_combined_income_for_social_security_taxability", period + ) + filing_status = tax_unit("filing_status", period) + status = filing_status.possible_values + separate = filing_status == status.SEPARATE + cohabitating = tax_unit("cohabitating_spouses", period) + # Cohabitating married couples filing separately receive a base + # and adjusted base amount of 0 + base_amount = where( + separate & cohabitating, + p.threshold.base.separate_cohabitating, + p.threshold.base.main[filing_status], + ) + adjusted_base_amount = where( + separate & cohabitating, + p.threshold.adjusted_base.separate_cohabitating, + p.threshold.adjusted_base.main[filing_status], + ) + + under_first_threshold = combined_income < base_amount + under_second_threshold = combined_income < adjusted_base_amount + + combined_income_excess = tax_unit("tax_unit_ss_combined_income_excess", period) + excess_over_adjusted_base = max_(0, combined_income - adjusted_base_amount) + + # Tier 1: Between base and adjusted base thresholds + # Per IRC §86(a)(1), "the amount determined under paragraph (1)" + # Taxable amount is lesser of: + # - base.benefit_cap * SS benefits [§86(a)(1)(A)] + # - base.excess * excess over base [§86(a)(1)(B)] + amount_under_paragraph_1 = min_( + p.rate.base.benefit_cap * gross_ss, + p.rate.base.excess * combined_income_excess, + ) + + # Tier 2: Above adjusted base threshold + # Per IRC §86(a)(2)(A)(ii), the lesser of: + # - "the amount determined under paragraph (1)" (calculated above) + # - "one-half of the difference between the adjusted base amount and the base amount" + bracket_amount = min_( + amount_under_paragraph_1, + p.rate.additional.bracket * (adjusted_base_amount - base_amount), + ) + + # Per IRC §86(a)(2), the lesser of: + # (A) 85% of excess over adjusted base + bracket amount, or + # (B) 85% of social security benefits + amount_if_over_second_threshold = min_( + p.rate.additional.excess * excess_over_adjusted_base + bracket_amount, + p.rate.additional.benefit_cap * gross_ss, + ) + + return select( + [ + under_first_threshold, + under_second_threshold, + ], + [ + 0, + amount_under_paragraph_1, + ], + default=amount_if_over_second_threshold, + ) diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/tax_unit_taxable_social_security.py b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/tax_unit_taxable_social_security.py index f948e3cab9d..b59d66b426d 100644 --- a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/tax_unit_taxable_social_security.py +++ b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/tax_unit_taxable_social_security.py @@ -1,85 +1,38 @@ from policyengine_us.model_api import * +def section_86_share(tax_unit, period, benefits_variable): + """Share of the head's and spouse's section 86 benefits that one kind of + benefit makes up. Section 86 figures a single taxable amount on title II + and tier 1 railroad benefits together; each kind carries its share.""" + benefits = tax_unit(benefits_variable, period) + total = tax_unit("tax_unit_section_86_benefits", period) + share = np.zeros_like(total) + mask = total > 0 + share[mask] = benefits[mask] / total[mask] + return share + + class tax_unit_taxable_social_security(Variable): value_type = float entity = TaxUnit definition_period = YEAR label = "Taxable Social Security benefits" - documentation = "Social security (OASDI) benefits included in AGI, including tier 1 railroad retirement benefits." + documentation = ( + "Title II Social Security (OASDI) benefits included in AGI: the title " + "II share of tax_unit_taxable_section_86_benefits. Tier 1 railroad " + "retirement benefits carry the rest, in " + "tax_unit_taxable_railroad_social_security_equivalent_benefits." + ) unit = USD - reference = "https://www.law.cornell.edu/uscode/text/26/86" + reference = ( + "https://www.law.cornell.edu/uscode/text/26/86", + "https://www.law.cornell.edu/uscode/text/26/86#d_1", + ) def formula(tax_unit, period, parameters): - p = parameters(period).gov.irs.social_security.taxability - gross_ss = tax_unit("tax_unit_social_security_for_taxability", period) - - # The legislation directs the usage of an income definition that is - # a particularly modified AGI, plus half of gross Social Security - # payments. Per IRC Section 86(b)(1), this fraction is always 0.5 - # and is handled by the combined_income_ss_fraction parameter. - - combined_income = tax_unit( - "tax_unit_combined_income_for_social_security_taxability", period - ) - filing_status = tax_unit("filing_status", period) - status = filing_status.possible_values - separate = filing_status == status.SEPARATE - cohabitating = tax_unit("cohabitating_spouses", period) - # Cohabitating married couples filing separately receive a base - # and adjusted base amount of 0 - base_amount = where( - separate & cohabitating, - p.threshold.base.separate_cohabitating, - p.threshold.base.main[filing_status], - ) - adjusted_base_amount = where( - separate & cohabitating, - p.threshold.adjusted_base.separate_cohabitating, - p.threshold.adjusted_base.main[filing_status], - ) - - under_first_threshold = combined_income < base_amount - under_second_threshold = combined_income < adjusted_base_amount - - combined_income_excess = tax_unit("tax_unit_ss_combined_income_excess", period) - excess_over_adjusted_base = max_(0, combined_income - adjusted_base_amount) - - # Tier 1: Between base and adjusted base thresholds - # Per IRC §86(a)(1), "the amount determined under paragraph (1)" - # Taxable amount is lesser of: - # - base.benefit_cap * SS benefits [§86(a)(1)(A)] - # - base.excess * excess over base [§86(a)(1)(B)] - amount_under_paragraph_1 = min_( - p.rate.base.benefit_cap * gross_ss, - p.rate.base.excess * combined_income_excess, - ) - - # Tier 2: Above adjusted base threshold - # Per IRC §86(a)(2)(A)(ii), the lesser of: - # - "the amount determined under paragraph (1)" (calculated above) - # - "one-half of the difference between the adjusted base amount and the base amount" - bracket_amount = min_( - amount_under_paragraph_1, - p.rate.additional.bracket * (adjusted_base_amount - base_amount), - ) - - # Per IRC §86(a)(2), the lesser of: - # (A) 85% of excess over adjusted base + bracket amount, or - # (B) 85% of social security benefits - amount_if_over_second_threshold = min_( - p.rate.additional.excess * excess_over_adjusted_base + bracket_amount, - p.rate.additional.benefit_cap * gross_ss, - ) - - return select( - [ - under_first_threshold, - under_second_threshold, - ], - [ - 0, - amount_under_paragraph_1, - ], - default=amount_if_over_second_threshold, + taxable = tax_unit("tax_unit_taxable_section_86_benefits", period) + share = section_86_share( + tax_unit, period, "tax_unit_social_security_for_taxability" ) + return taxable * share diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/taxable_railroad_social_security_equivalent_benefits.py b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/taxable_railroad_social_security_equivalent_benefits.py new file mode 100644 index 00000000000..e07e96d845f --- /dev/null +++ b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/taxable_railroad_social_security_equivalent_benefits.py @@ -0,0 +1,35 @@ +from policyengine_us.model_api import * +from policyengine_us.variables.gov.irs.income.taxable_income.adjusted_gross_income.irs_gross_income.social_security.taxable_social_security import ( + allocate_to_head_and_spouse, +) + + +class taxable_railroad_social_security_equivalent_benefits(Variable): + value_type = float + entity = Person + label = "Taxable Railroad Retirement social security equivalent benefits" + unit = USD + documentation = ( + "The head's or spouse's tier 1 railroad retirement benefits included " + "in gross income under IRC 86, allocated in proportion to their social " + "security equivalent benefits as taxable_social_security allocates " + "title II benefits. 45 U.S.C. 231m(a) bars states from taxing them." + ) + definition_period = YEAR + reference = ( + "https://www.law.cornell.edu/uscode/text/26/86#d_1_B", + "https://www.law.cornell.edu/uscode/text/45/231m#a", + ) + + def formula(person, period, parameters): + unit_taxable = person.tax_unit( + "tax_unit_taxable_railroad_social_security_equivalent_benefits", period + ) + benefits = person("railroad_social_security_equivalent_benefits", period) + unit_benefits = person.tax_unit( + "tax_unit_railroad_social_security_equivalent_benefits_for_taxability", + period, + ) + return allocate_to_head_and_spouse( + person, period, unit_taxable, benefits, unit_benefits + ) diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/taxable_social_security.py b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/taxable_social_security.py index 79d4718a1aa..b8d59e09351 100644 --- a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/taxable_social_security.py +++ b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/taxable_social_security.py @@ -1,6 +1,25 @@ from policyengine_us.model_api import * +def allocate_to_head_and_spouse(person, period, unit_amount, benefits, unit_benefits): + """Split a tax unit amount between the head and spouse in proportion to + their benefits. The spouse takes their share and the head the rest, so + the two parts always sum to the tax unit amount.""" + # Use mask instead of where to avoid divide-by-zero warnings. Default to zero. + person_frac = np.zeros_like(unit_benefits) + mask = unit_benefits > 0 + person_frac[mask] = benefits[mask] / unit_benefits[mask] + is_spouse = person("is_tax_unit_spouse", period) + spouse_frac = min_(1, where(is_spouse, person_frac, 0)) + unit_spouse_frac = person.tax_unit.sum(spouse_frac) + is_head = person("is_tax_unit_head", period) + head_frac = where(is_head, 1 - unit_spouse_frac, 0) + unit_head_frac = person.tax_unit.sum(head_frac) + return unit_amount * select( + [is_head, is_spouse], [unit_head_frac, unit_spouse_frac], default=0 + ) + + class taxable_social_security(Variable): value_type = float entity = Person @@ -13,17 +32,6 @@ def formula(person, period, parameters): # allocate unit_tss to head and spouse in proportion to social_security ind_socsec = person("social_security", period) unit_socsec = person.tax_unit("tax_unit_social_security_for_taxability", period) - # Compute each person's share of tax unit's Social Security benefits. - # Use mask instead of where to avoid divide-by-zero warnings. Default to zero. - person_frac = np.zeros_like(unit_socsec) - mask = unit_socsec > 0 - person_frac[mask] = ind_socsec[mask] / unit_socsec[mask] - is_spouse = person("is_tax_unit_spouse", period) - spouse_frac = min_(1, where(is_spouse, person_frac, 0)) - unit_spouse_frac = person.tax_unit.sum(spouse_frac) - is_head = person("is_tax_unit_head", period) - head_frac = where(is_head, 1 - unit_spouse_frac, 0) - unit_head_frac = person.tax_unit.sum(head_frac) - return unit_tss * select( - [is_head, is_spouse], [unit_head_frac, unit_spouse_frac], default=0 + return allocate_to_head_and_spouse( + person, period, unit_tss, ind_socsec, unit_socsec ) diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/taxable_social_security_tier_1.py b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/taxable_social_security_tier_1.py index 763a428aee4..8d303b7a7cc 100644 --- a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/taxable_social_security_tier_1.py +++ b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/taxable_social_security_tier_1.py @@ -1,4 +1,7 @@ from policyengine_us.model_api import * +from policyengine_us.variables.gov.irs.income.taxable_income.adjusted_gross_income.irs_gross_income.social_security.tax_unit_taxable_social_security import ( + section_86_share, +) class taxable_social_security_tier_1(Variable): @@ -9,14 +12,17 @@ class taxable_social_security_tier_1(Variable): documentation = ( "First-tier component of current-law taxable Social Security. " "Above the adjusted base amount this is the bracket amount in section " - "86(a)(2)(A)(ii), rather than the pre-1993 inclusion used for OASDI revenue." + "86(a)(2)(A)(ii), rather than the pre-1993 inclusion used for OASDI revenue. " + "Section 86 figures it on title II and tier 1 railroad benefits " + "together; this is the title II share, as in " + "tax_unit_taxable_social_security." ) unit = USD reference = "https://www.law.cornell.edu/uscode/text/26/86#a_1" def formula(tax_unit, period, parameters): p = parameters(period).gov.irs.social_security.taxability - gross_ss = tax_unit("tax_unit_social_security_for_taxability", period) + gross_ss = tax_unit("tax_unit_section_86_benefits", period) combined_income = tax_unit( "tax_unit_combined_income_for_social_security_taxability", period ) @@ -53,8 +59,12 @@ def formula(tax_unit, period, parameters): p.rate.additional.bracket * (adjusted_base_amount - base_amount), ) - return select( + tier_1 = select( [under_first_threshold, under_second_threshold], [0, amount_under_paragraph_1], default=bracket_amount, ) + share = section_86_share( + tax_unit, period, "tax_unit_social_security_for_taxability" + ) + return tier_1 * share diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/taxable_ss_magi.py b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/taxable_ss_magi.py index 4f31eff45c2..011873dbe62 100644 --- a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/taxable_ss_magi.py +++ b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/taxable_ss_magi.py @@ -1,4 +1,7 @@ from policyengine_us.model_api import * +from policyengine_us.variables.gov.irs.income.taxable_income.adjusted_gross_income.irs_gross_income.social_security.tax_unit_taxable_section_86_benefits import ( + SECTION_86_GROSS_INCOME_SOURCES, +) class taxable_ss_magi(Variable): @@ -23,7 +26,8 @@ def formula(tax_unit, period, parameters): else income_source ) for income_source in gross_income_sources - if income_source != "taxable_social_security" + # IRC 86(b)(2)(A): determined without regard to section 86. + if income_source not in SECTION_86_GROSS_INCOME_SOURCES ] income_sources_without_ss.append("tax_exempt_interest_income") gross_income = 0 diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/taxable_railroad_benefits.py b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/taxable_railroad_benefits.py new file mode 100644 index 00000000000..4756692bb43 --- /dev/null +++ b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/taxable_railroad_benefits.py @@ -0,0 +1,36 @@ +from policyengine_us.model_api import * + +RAILROAD_GROSS_INCOME_SOURCES = ( + "taxable_railroad_social_security_equivalent_benefits", + "taxable_railroad_pension_benefits", +) + + +class taxable_railroad_benefits(Variable): + value_type = float + entity = Person + label = "Railroad Retirement benefits in federal gross income" + unit = USD + documentation = ( + "The Railroad Retirement Act benefits that federal gross income " + "includes for this person: the taxable tier 1 benefits treated as " + "social security and the taxable rest of the annuity, counted as " + "irs_gross_income counts them. 45 U.S.C. 231m(a) bars states from " + "taxing these annuities, so states that start from federal adjusted " + "gross income or taxable income subtract this amount." + ) + definition_period = YEAR + reference = ( + "https://www.law.cornell.edu/uscode/text/45/231m#a", + "https://www.law.cornell.edu/uscode/text/26/86#d_1_B", + "https://www.law.cornell.edu/uscode/text/26/72#r", + ) + + def formula(person, period, parameters): + sources = parameters(period).gov.irs.gross_income.sources + not_dependent = ~person("is_tax_unit_dependent", period) + total = 0 + for source in RAILROAD_GROSS_INCOME_SOURCES: + if source in sources: + total += max_(0, person(source, period)) + return not_dependent * total diff --git a/policyengine_us/variables/gov/ssa/revenue/tax_unit_taxable_social_security_pre_1993.py b/policyengine_us/variables/gov/ssa/revenue/tax_unit_taxable_social_security_pre_1993.py index 292c4c69f4f..ef1153e1023 100644 --- a/policyengine_us/variables/gov/ssa/revenue/tax_unit_taxable_social_security_pre_1993.py +++ b/policyengine_us/variables/gov/ssa/revenue/tax_unit_taxable_social_security_pre_1993.py @@ -1,4 +1,7 @@ from policyengine_us.model_api import * +from policyengine_us.variables.gov.irs.income.taxable_income.adjusted_gross_income.irs_gross_income.social_security.tax_unit_taxable_social_security import ( + section_86_share, +) class tax_unit_taxable_social_security_pre_1993(Variable): @@ -10,7 +13,10 @@ class tax_unit_taxable_social_security_pre_1993(Variable): "Counterfactual Social Security inclusion under section 86 before the " "1993 amendments, used to allocate taxation-of-benefits revenue to OASDI. " "The original law limits inclusion to half of benefits and half of " - "provisional income above the unindexed base amount." + "provisional income above the unindexed base amount. Section 86 has " + "covered tier 1 railroad retirement benefits since 1984, so the " + "inclusion is figured on both and this is the title II share: the " + "OASDI trust funds receive the tax on title II benefits." ) unit = USD reference = ( @@ -20,7 +26,7 @@ class tax_unit_taxable_social_security_pre_1993(Variable): def formula(tax_unit, period, parameters): p = parameters(period).gov.ssa.revenue.pre_1993 - benefits = tax_unit("tax_unit_social_security_for_taxability", period) + benefits = tax_unit("tax_unit_section_86_benefits", period) # Section 86(b) also used one-half of benefits in provisional income. # Keep the historical counterfactual independent of current-law rates. combined_income = tax_unit("taxable_ss_magi", period) + p.rate * benefits @@ -33,4 +39,7 @@ def formula(tax_unit, period, parameters): p.base_amount.main[filing_status], ) excess = max_(combined_income - base_amount, 0) - return p.rate * min_(benefits, excess) + share = section_86_share( + tax_unit, period, "tax_unit_social_security_for_taxability" + ) + return p.rate * min_(benefits, excess) * share diff --git a/policyengine_us/variables/gov/ssa/revenue/tob_revenue_medicare_hi.py b/policyengine_us/variables/gov/ssa/revenue/tob_revenue_medicare_hi.py index 74372fdab5d..7f931bbed39 100644 --- a/policyengine_us/variables/gov/ssa/revenue/tob_revenue_medicare_hi.py +++ b/policyengine_us/variables/gov/ssa/revenue/tob_revenue_medicare_hi.py @@ -18,13 +18,20 @@ class tob_revenue_medicare_hi(Variable): "retaining negative unit differences for aggregation. The OASDI " "allocation caps old-law inclusion at actual inclusion, so HI gets " "nothing from a unit whose inclusion does not exceed the pre-1993 " - "amount." + "amount. Section 121(e)(3)(C) gives social security benefits the " + "section 86(d)(1) meaning, so section 121(e)(1)(B) also credits HI " + "with the 1993 amendments' increase on the social security " + "equivalent part of tier 1 railroad retirement benefits. The " + "counterfactuals change only title II benefits' taxable share " + "(tax_unit_taxable_social_security), so this variable leaves that " + "railroad amount out." ) unit = USD reference = ( "https://www.law.cornell.edu/uscode/text/42/401", "https://www.law.cornell.edu/uscode/text/26/1411", "https://www.ssa.gov/OP_Home/comp2/F098-021.html", + "https://www.law.cornell.edu/uscode/text/26/86#d_1", ) def formula(tax_unit, period, parameters): diff --git a/policyengine_us/variables/gov/ssa/revenue/tob_revenue_oasdi.py b/policyengine_us/variables/gov/ssa/revenue/tob_revenue_oasdi.py index 4798a41038c..f76b8500713 100644 --- a/policyengine_us/variables/gov/ssa/revenue/tob_revenue_oasdi.py +++ b/policyengine_us/variables/gov/ssa/revenue/tob_revenue_oasdi.py @@ -17,13 +17,18 @@ class tob_revenue_oasdi(Variable): "impact. Negative unit differences are retained when aggregating " "tax liabilities. Actual inclusion caps the old-law inclusion. When " "a reform cuts inclusion below the pre-1993 amount, the 1993 " - "amendments add nothing, and OASDI takes the whole increase." + "amendments add nothing, and OASDI takes the whole increase. It " + "covers title II benefits only: under 45 U.S.C. 231n-1(b)(1)(B), the " + "section 121(e) amount from taxing the social security equivalent " + "part of tier 1 railroad retirement benefits goes to the Social " + "Security Equivalent Benefit Account, not to OASDI." ) unit = USD reference = ( "https://www.law.cornell.edu/uscode/text/42/401", "https://www.law.cornell.edu/uscode/text/26/1411", "https://www.ssa.gov/OP_Home/comp2/F098-021.html", + "https://www.law.cornell.edu/uscode/text/45/231n-1", ) def formula(tax_unit, period, parameters): diff --git a/policyengine_us/variables/gov/ssa/revenue/tob_revenue_total.py b/policyengine_us/variables/gov/ssa/revenue/tob_revenue_total.py index 784a510d5f8..c962ea4c199 100644 --- a/policyengine_us/variables/gov/ssa/revenue/tob_revenue_total.py +++ b/policyengine_us/variables/gov/ssa/revenue/tob_revenue_total.py @@ -12,12 +12,19 @@ class tob_revenue_total(Variable): "benefits, per 42 U.S.C. 401 note. Excludes chapter 2A net investment " "income tax under section 1411. The total and its " "OASDI and Medicare HI shares are signed: negative unit differences " - "are retained for aggregation." + "are retained for aggregation. The counterfactual removes only title " + "II benefits' taxable share (tax_unit_taxable_social_security), so " + "the total leaves out the tax on the social security equivalent part " + "of tier 1 railroad retirement benefits. Section 121(e) and 45 U.S.C. " + "231n-1(b)(1)(B) credit that tax to the Social Security Equivalent " + "Benefit Account, except the part from the 1993 amendments, which " + "goes to HI." ) unit = USD reference = ( "https://www.law.cornell.edu/uscode/text/42/401", "https://www.law.cornell.edu/uscode/text/26/1411", + "https://www.law.cornell.edu/uscode/text/45/231n-1", ) def formula(tax_unit, period, parameters): diff --git a/policyengine_us/variables/gov/states/al/tax/income/al_withheld_income_tax.py b/policyengine_us/variables/gov/states/al/tax/income/al_withheld_income_tax.py index 7828e3d2cd2..2c3e5ce5783 100644 --- a/policyengine_us/variables/gov/states/al/tax/income/al_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/al/tax/income/al_withheld_income_tax.py @@ -10,7 +10,7 @@ class al_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) # Assigning the maximum standard deduction amount p = parameters(period).gov.states.al.tax.income standard_deduction = p.deductions.standard.amount.max["SINGLE"] diff --git a/policyengine_us/variables/gov/states/ar/tax/income/ar_withheld_income_tax.py b/policyengine_us/variables/gov/states/ar/tax/income/ar_withheld_income_tax.py index 2e6cef4ec5e..54dbad02233 100644 --- a/policyengine_us/variables/gov/states/ar/tax/income/ar_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/ar/tax/income/ar_withheld_income_tax.py @@ -10,7 +10,7 @@ class ar_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) p = parameters(period).gov.states.ar.tax.income # We apply the base standard deduction amount standard_deduction = p.deductions.standard["SINGLE"] diff --git a/policyengine_us/variables/gov/states/az/tax/income/az_withheld_income_tax.py b/policyengine_us/variables/gov/states/az/tax/income/az_withheld_income_tax.py index 055b5ddd47e..fe4ac808109 100644 --- a/policyengine_us/variables/gov/states/az/tax/income/az_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/az/tax/income/az_withheld_income_tax.py @@ -10,7 +10,7 @@ class az_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) p = parameters(period).gov.states.az.tax.income standard_deduction = p.deductions.standard.amount["SINGLE"] reduced_agi = max_(agi - standard_deduction, 0) diff --git a/policyengine_us/variables/gov/states/az/tax/income/credits/property_tax_credit/az_property_tax_credit_agi.py b/policyengine_us/variables/gov/states/az/tax/income/credits/property_tax_credit/az_property_tax_credit_agi.py index 026ffb8d2fd..2671303244b 100644 --- a/policyengine_us/variables/gov/states/az/tax/income/credits/property_tax_credit/az_property_tax_credit_agi.py +++ b/policyengine_us/variables/gov/states/az/tax/income/credits/property_tax_credit/az_property_tax_credit_agi.py @@ -11,7 +11,9 @@ class az_property_tax_credit_agi(Variable): "Adjusted gross income as defined for Arizona property tax credit purposes, " "for members who are not dependents. Per ARS 43-1072(H)(6) and ITR 12-1, " "this starts with federal AGI and subtracts taxable Social Security (ARS " - "43-1072(I)) and the unemployment compensation it holds, as (I) excludes " + "43-1072(I)), the taxable railroad retirement benefits, which the Form " + "140PTC instructions also leave out of income, and the unemployment " + "compensation it holds, as (I) excludes " "Arizona unemployment insurance payments; other states' payments are " "added back in az_property_tax_credit_unemployment. Federal AGI already " "leaves out most other subsection (I) items. Federal above-the-line " @@ -43,14 +45,17 @@ def formula(tax_unit, period, parameters): # Per ARS 43-1072(I) and ITR 12-1 "Items Excluded from Income", # we exclude Social Security benefits and Arizona unemployment - # compensation (below). Other excluded items, like railroad - # retirement, workers comp, veterans disability pensions, welfare and - # gifts, are typically not in Federal AGI anyway. + # compensation (below). The 2025 Form 140PTC instructions (page 4) + # also leave out railroad retirement benefits. Other excluded items, + # like workers comp, veterans disability pensions, welfare and gifts, + # are typically not in Federal AGI anyway. # # Federal AGI only includes the TAXABLE portion of Social Security, # but for property tax credit, ALL Social Security should be excluded. - # So we subtract the taxable portion that's in Federal AGI. + # So we subtract the taxable portion that's in Federal AGI. The same + # holds for the railroad retirement benefits federal AGI includes. taxable_social_security = tax_unit("tax_unit_taxable_social_security", period) + taxable_railroad_benefits = add(tax_unit, period, ["taxable_railroad_benefits"]) # NOTE: We do NOT subtract Arizona's regular subtractions here because: # - Pension exclusions (az_public_pension_exclusion, @@ -156,6 +161,7 @@ def formula(tax_unit, period, parameters): return ( federal_agi - taxable_social_security + - taxable_railroad_benefits - federal_capital_gains + federal_capital_loss + line_d diff --git a/policyengine_us/variables/gov/states/az/tax/income/credits/property_tax_credit/az_property_tax_credit_dependent_income.py b/policyengine_us/variables/gov/states/az/tax/income/credits/property_tax_credit/az_property_tax_credit_dependent_income.py index 245bd005964..d88bf8d160f 100644 --- a/policyengine_us/variables/gov/states/az/tax/income/credits/property_tax_credit/az_property_tax_credit_dependent_income.py +++ b/policyengine_us/variables/gov/states/az/tax/income/credits/property_tax_credit/az_property_tax_credit_dependent_income.py @@ -44,13 +44,17 @@ def formula(tax_unit, period, parameters): sources = parameters(period).gov.irs.gross_income.sources income = 0 for source in sources: - # ARS 43-1072(I): Social Security benefits are not income. - # Capital gains and losses, including capital gain distributions, - # are added below as line D. ARS 43-1072(I): Arizona unemployment - # insurance payments are not income; other states' payments are - # in az_property_tax_credit_unemployment. + # ARS 43-1072(I): Social Security benefits are not income, and + # the 2025 Form 140PTC instructions (page 4) leave out railroad + # retirement benefits too. Capital gains and losses, including + # capital gain distributions, are added below as line D. ARS + # 43-1072(I): Arizona unemployment insurance payments are not + # income; other states' payments are in + # az_property_tax_credit_unemployment. if source in ( "taxable_social_security", + "taxable_railroad_social_security_equivalent_benefits", + "taxable_railroad_pension_benefits", "taxable_unemployment_compensation", "capital_gains", "non_sch_d_capital_gains", diff --git a/policyengine_us/variables/gov/states/ca/tax/income/ca_withheld_income_tax.py b/policyengine_us/variables/gov/states/ca/tax/income/ca_withheld_income_tax.py index 7d097cea141..c1ddda9d377 100644 --- a/policyengine_us/variables/gov/states/ca/tax/income/ca_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/ca/tax/income/ca_withheld_income_tax.py @@ -10,7 +10,7 @@ class ca_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) p = parameters(period).gov.states.ca.tax.income standard_deduction = p.deductions.standard.amount["SINGLE"] reduced_agi = max_(agi - standard_deduction, 0) diff --git a/policyengine_us/variables/gov/states/co/tax/income/co_withheld_income_tax.py b/policyengine_us/variables/gov/states/co/tax/income/co_withheld_income_tax.py index 43e14fe0652..c29fe19325e 100644 --- a/policyengine_us/variables/gov/states/co/tax/income/co_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/co/tax/income/co_withheld_income_tax.py @@ -10,7 +10,7 @@ class co_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) # Colorado adopts federal taxable income, so use federal standard deduction. p_irs = parameters(period).gov.irs.deductions.standard standard_deduction = p_irs.amount["SINGLE"] diff --git a/policyengine_us/variables/gov/states/ct/tax/income/ct_withheld_income_tax.py b/policyengine_us/variables/gov/states/ct/tax/income/ct_withheld_income_tax.py index 8ef2e232e3f..bb2e78d427d 100644 --- a/policyengine_us/variables/gov/states/ct/tax/income/ct_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/ct/tax/income/ct_withheld_income_tax.py @@ -10,7 +10,7 @@ class ct_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) p = parameters(period).gov.states.ct.tax.income # Since Connecticut does not have a standard deduction, we apply the maximum # personal exemption amount diff --git a/policyengine_us/variables/gov/states/dc/tax/income/dc_withheld_income_tax.py b/policyengine_us/variables/gov/states/dc/tax/income/dc_withheld_income_tax.py index 2a0279c875b..3f9226990e2 100644 --- a/policyengine_us/variables/gov/states/dc/tax/income/dc_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/dc/tax/income/dc_withheld_income_tax.py @@ -10,7 +10,7 @@ class dc_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) p_irs = parameters(period).gov.irs.deductions.standard # We apply the maximum standard deduction amount standard_deduction = p_irs.amount["SINGLE"] diff --git a/policyengine_us/variables/gov/states/de/tax/income/de_withheld_income_tax.py b/policyengine_us/variables/gov/states/de/tax/income/de_withheld_income_tax.py index 74c6a53c855..682b27d83b0 100644 --- a/policyengine_us/variables/gov/states/de/tax/income/de_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/de/tax/income/de_withheld_income_tax.py @@ -10,7 +10,7 @@ class de_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) p = parameters(period).gov.states.de.tax.income # We apply the base standard deduction amount standard_deduction = p.deductions.standard.amount["SINGLE"] diff --git a/policyengine_us/variables/gov/states/ga/tax/income/ga_withheld_income_tax.py b/policyengine_us/variables/gov/states/ga/tax/income/ga_withheld_income_tax.py index e824290e144..fb0114f7c14 100644 --- a/policyengine_us/variables/gov/states/ga/tax/income/ga_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/ga/tax/income/ga_withheld_income_tax.py @@ -10,7 +10,7 @@ class ga_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) p = parameters(period).gov.states.ga.tax.income # We apply the base standard deduction amount personal_exemptions = p.deductions.standard.amount["SINGLE"] diff --git a/policyengine_us/variables/gov/states/hi/tax/income/hi_withheld_income_tax.py b/policyengine_us/variables/gov/states/hi/tax/income/hi_withheld_income_tax.py index 1f8b2a819ba..1a20c3a640f 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/hi_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/hi_withheld_income_tax.py @@ -10,7 +10,7 @@ class hi_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) p = parameters(period).gov.states.hi.tax.income # We apply the base standard deduction amount standard_deduction = p.deductions.standard.amount["SINGLE"] diff --git a/policyengine_us/variables/gov/states/ia/tax/income/ia_withheld_income_tax.py b/policyengine_us/variables/gov/states/ia/tax/income/ia_withheld_income_tax.py index 7709214b199..b292d2d58de 100644 --- a/policyengine_us/variables/gov/states/ia/tax/income/ia_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/ia/tax/income/ia_withheld_income_tax.py @@ -10,7 +10,7 @@ class ia_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) p = parameters(period).gov.states.ia.tax.income # We apply the base standard deduction amount if p.deductions.standard.applies_federal: diff --git a/policyengine_us/variables/gov/states/id/tax/income/id_withheld_income_tax.py b/policyengine_us/variables/gov/states/id/tax/income/id_withheld_income_tax.py index 45760166678..c7a872aa7c0 100644 --- a/policyengine_us/variables/gov/states/id/tax/income/id_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/id/tax/income/id_withheld_income_tax.py @@ -10,7 +10,7 @@ class id_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) p_irs = parameters(period).gov.irs.deductions.standard # We apply the base IRS standard deduction amount standard_deduction = p_irs.amount["SINGLE"] diff --git a/policyengine_us/variables/gov/states/il/tax/income/il_withheld_income_tax.py b/policyengine_us/variables/gov/states/il/tax/income/il_withheld_income_tax.py index 04838f8fbcd..bab78b41b43 100644 --- a/policyengine_us/variables/gov/states/il/tax/income/il_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/il/tax/income/il_withheld_income_tax.py @@ -10,7 +10,7 @@ class il_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) p = parameters(period).gov.states.il.tax.income # Since Illinois does not have a standard deduction # we apply the personal exemption amount diff --git a/policyengine_us/variables/gov/states/in/tax/income/in_withheld_income_tax.py b/policyengine_us/variables/gov/states/in/tax/income/in_withheld_income_tax.py index e603b2abbb1..2ec48eb60b3 100644 --- a/policyengine_us/variables/gov/states/in/tax/income/in_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/in/tax/income/in_withheld_income_tax.py @@ -10,7 +10,7 @@ class in_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) p = parameters(period).gov.states["in"].tax.income # Since Indiana does not have a standard deduction, we apply the maximum # personal exemption amount diff --git a/policyengine_us/variables/gov/states/ks/tax/income/ks_agi_subtractions.py b/policyengine_us/variables/gov/states/ks/tax/income/ks_agi_subtractions.py index 773a9b6b8d3..67527c42859 100644 --- a/policyengine_us/variables/gov/states/ks/tax/income/ks_agi_subtractions.py +++ b/policyengine_us/variables/gov/states/ks/tax/income/ks_agi_subtractions.py @@ -10,6 +10,7 @@ class ks_agi_subtractions(Variable): reference = ( "https://ksrevisor.gov/statutes/chapters/ch79/079_032_0117.html", "https://www.ksrevenue.gov/pdf/ip25.pdf#page=12", + "https://www.law.cornell.edu/uscode/text/45/231m#a", ) defined_for = StateCode.KS @@ -39,11 +40,16 @@ def formula(tax_unit, period, parameters): "military_retirement_income", ], ) + # 45 U.S.C. 231m(a) bars state tax on Railroad Retirement Act + # annuities, so the part federal AGI includes comes back out, + # whatever the federal AGI. + railroad = add(tax_unit, period, ["taxable_railroad_benefits"]) return ( oasdi_subtraction + us_govt_interest + plan_529 + salt_refund_income + public_and_military_pension + + railroad + tax_unit("ks_other_subtractions", period) ) diff --git a/policyengine_us/variables/gov/states/ks/tax/income/ks_withheld_income_tax.py b/policyengine_us/variables/gov/states/ks/tax/income/ks_withheld_income_tax.py index 0ec33ebccad..541bec5eaf3 100644 --- a/policyengine_us/variables/gov/states/ks/tax/income/ks_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/ks/tax/income/ks_withheld_income_tax.py @@ -10,7 +10,7 @@ class ks_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) p = parameters(period).gov.states.ks.tax.income # We apply the base standard deduction amount standard_deduction = p.deductions.standard.base_amount["SINGLE"] diff --git a/policyengine_us/variables/gov/states/ky/tax/income/ky_withheld_income_tax.py b/policyengine_us/variables/gov/states/ky/tax/income/ky_withheld_income_tax.py index b82b31651f0..d17df6ad5aa 100644 --- a/policyengine_us/variables/gov/states/ky/tax/income/ky_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/ky/tax/income/ky_withheld_income_tax.py @@ -10,7 +10,7 @@ class ky_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) p = parameters(period).gov.states.ky.tax.income # We apply the base standard deduction amount standard_deduction = p.deductions.standard diff --git a/policyengine_us/variables/gov/states/la/tax/income/la_withheld_income_tax.py b/policyengine_us/variables/gov/states/la/tax/income/la_withheld_income_tax.py index ea7f5634908..74a1c39f3b2 100644 --- a/policyengine_us/variables/gov/states/la/tax/income/la_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/la/tax/income/la_withheld_income_tax.py @@ -10,7 +10,7 @@ class la_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) p_irs = parameters(period).gov.irs.deductions.standard # We apply the base IRS standard deduction amount standard_deduction = p_irs.amount["SINGLE"] diff --git a/policyengine_us/variables/gov/states/ma/tax/income/gross_income/ma_gross_income.py b/policyengine_us/variables/gov/states/ma/tax/income/gross_income/ma_gross_income.py index 67f1f1b4b20..3eec2e26cd3 100644 --- a/policyengine_us/variables/gov/states/ma/tax/income/gross_income/ma_gross_income.py +++ b/policyengine_us/variables/gov/states/ma/tax/income/gross_income/ma_gross_income.py @@ -12,6 +12,7 @@ class ma_gross_income(Variable): # c.62C s.6(a): each individual with Massachusetts gross income over # $8,000 files a return, so a dependent reports their own income. "https://malegislature.gov/Laws/GeneralLaws/PartI/TitleIX/Chapter62C/Section6", + "https://www.law.cornell.edu/uscode/text/45/231m#a", ) defined_for = StateCode.MA @@ -52,7 +53,15 @@ def formula(tax_unit, period, parameters): "military_retirement_income", ], ) - deductions = social_security_in_agi + salt_refund_income + public_pension + # 45 U.S.C. 231m(a) bars state tax on Railroad Retirement Act + # annuities, so the part federal gross income includes comes out. + railroad_in_agi = add(tax_unit, period, ["taxable_railroad_benefits"]) + deductions = ( + social_security_in_agi + + railroad_in_agi + + salt_refund_income + + public_pension + ) return max_( 0, federal_gross_income diff --git a/policyengine_us/variables/gov/states/ma/tax/income/ma_withheld_income_tax.py b/policyengine_us/variables/gov/states/ma/tax/income/ma_withheld_income_tax.py index ed52c9253fe..05ab497c58b 100644 --- a/policyengine_us/variables/gov/states/ma/tax/income/ma_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/ma/tax/income/ma_withheld_income_tax.py @@ -10,7 +10,7 @@ class ma_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) p = parameters(period).gov.states.ma.tax.income # Since Massachusetts does not have a standard deduction, we apply the # base personal exemption amount diff --git a/policyengine_us/variables/gov/states/md/tax/income/md_withheld_income_tax.py b/policyengine_us/variables/gov/states/md/tax/income/md_withheld_income_tax.py index d42abd83fc0..f4049f3fa97 100644 --- a/policyengine_us/variables/gov/states/md/tax/income/md_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/md/tax/income/md_withheld_income_tax.py @@ -10,7 +10,7 @@ class md_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) p = parameters(period).gov.states.md.tax.income # We apply the maximum standard deduction amount standard_deduction = p.deductions.standard.max["SINGLE"] diff --git a/policyengine_us/variables/gov/states/me/tax/income/adjusted_gross_income/me_agi_subtractions.py b/policyengine_us/variables/gov/states/me/tax/income/adjusted_gross_income/me_agi_subtractions.py index 8c1f18180ca..7c3a6a71241 100644 --- a/policyengine_us/variables/gov/states/me/tax/income/adjusted_gross_income/me_agi_subtractions.py +++ b/policyengine_us/variables/gov/states/me/tax/income/adjusted_gross_income/me_agi_subtractions.py @@ -9,10 +9,15 @@ class me_agi_subtractions(Variable): documentation = "Subtractions from ME AGI over federal AGI." definition_period = YEAR defined_for = StateCode.ME - reference = "https://www.maine.gov/revenue/sites/maine.gov.revenue/files/inline-files/22_1040me_sched_1s_ff.pdf" + reference = ( + "https://www.maine.gov/revenue/sites/maine.gov.revenue/files/inline-files/22_1040me_sched_1s_ff.pdf", + "https://www.law.cornell.edu/uscode/text/45/231m#a", + ) adds = [ "tax_unit_taxable_social_security", + # 45 U.S.C. 231m(a): railroad retirement benefits in federal AGI. + "taxable_railroad_benefits", "us_govt_interest", "me_pension_income_deduction", "me_529_plan_subtraction", diff --git a/policyengine_us/variables/gov/states/me/tax/income/me_withheld_income_tax.py b/policyengine_us/variables/gov/states/me/tax/income/me_withheld_income_tax.py index 141b7bdd8a4..0820c721c03 100644 --- a/policyengine_us/variables/gov/states/me/tax/income/me_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/me/tax/income/me_withheld_income_tax.py @@ -10,7 +10,7 @@ class me_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) p_irs = parameters(period).gov.irs.deductions.standard # We apply the base IRS standard deduction amount standard_deduction = p_irs.amount["SINGLE"] diff --git a/policyengine_us/variables/gov/states/mi/tax/income/mi_withheld_income_tax.py b/policyengine_us/variables/gov/states/mi/tax/income/mi_withheld_income_tax.py index 7d3b59eeb26..518b476fb39 100644 --- a/policyengine_us/variables/gov/states/mi/tax/income/mi_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/mi/tax/income/mi_withheld_income_tax.py @@ -10,7 +10,7 @@ class mi_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) p = parameters(period).gov.states.mi.tax.income # The MI standard deduction only applys for elderly # Wo do not apply deductions here diff --git a/policyengine_us/variables/gov/states/mn/tax/income/amt/mn_amt_taxable_income.py b/policyengine_us/variables/gov/states/mn/tax/income/amt/mn_amt_taxable_income.py index 874312cee15..5ad12cf893a 100644 --- a/policyengine_us/variables/gov/states/mn/tax/income/amt/mn_amt_taxable_income.py +++ b/policyengine_us/variables/gov/states/mn/tax/income/amt/mn_amt_taxable_income.py @@ -12,12 +12,17 @@ class mn_amt_taxable_income(Variable): "https://www.revenue.state.mn.us/sites/default/files/2023-01/m1mt_22.pdf", # 2024 Schedule M1MT, line 12 "https://www.revenue.state.mn.us/sites/default/files/2025-07/m1mt-24.pdf#page=1", + # 2024 Schedule M1MT instructions, line 18: adds Schedule M1M line 17, + # Railroad Retirement Board benefits. + "https://www.revenue.state.mn.us/sites/default/files/2025-07/m1mt-24.pdf#page=2", # 2020 Schedule M1MT instructions, line 4: includes Schedule M1NC # line 6, the federal charitable deduction for non-itemizers. "https://www.revenue.state.mn.us/sites/default/files/2023-02/m1mt_20.pdf#page=2", # Laws 2023, ch. 1, sec. 22(c)(2) and (d)(2): the addition applies # to alternative minimum taxable income. "https://www.revisor.mn.gov/laws/2023/0/Session+Law/Chapter/1/", + # No state may tax Railroad Retirement Act annuities. + "https://www.law.cornell.edu/uscode/text/45/231m#a", ) defined_for = StateCode.MN @@ -49,6 +54,10 @@ def formula(tax_unit, period, parameters): "mn_charity_subtraction", "mn_social_security_subtraction", "mn_military_pension_subtraction", + # M1MT line 18 takes Schedule M1M line 17, the Railroad + # Retirement Board benefits in federal AGI; 45 U.S.C. 231m(a) + # bars any state tax on them. + "taxable_railroad_benefits", ] amt_subs = add(tax_unit, period, AMT_SUBTRACTIONS) return fagi + charitable_addition - some_itm_deds - amt_subs diff --git a/policyengine_us/variables/gov/states/mn/tax/income/mn_withheld_income_tax.py b/policyengine_us/variables/gov/states/mn/tax/income/mn_withheld_income_tax.py index 02de7f721c0..005ba430a30 100644 --- a/policyengine_us/variables/gov/states/mn/tax/income/mn_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/mn/tax/income/mn_withheld_income_tax.py @@ -10,7 +10,7 @@ class mn_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) p = parameters(period).gov.states.mn.tax.income # We apply the base standard deduction amount standard_deduction = p.deductions.standard.base["SINGLE"] diff --git a/policyengine_us/variables/gov/states/mn/tax/income/subtractions/mn_elderly_disabled_subtraction.py b/policyengine_us/variables/gov/states/mn/tax/income/subtractions/mn_elderly_disabled_subtraction.py index 0f2226338c4..6c5ab403764 100644 --- a/policyengine_us/variables/gov/states/mn/tax/income/subtractions/mn_elderly_disabled_subtraction.py +++ b/policyengine_us/variables/gov/states/mn/tax/income/subtractions/mn_elderly_disabled_subtraction.py @@ -10,6 +10,7 @@ class mn_elderly_disabled_subtraction(Variable): reference = ( "https://www.revenue.state.mn.us/sites/default/files/2023-01/m1r_21.pdf", "https://www.revenue.state.mn.us/sites/default/files/2023-01/m1r_22.pdf", + "https://www.revisor.mn.gov/statutes/cite/290.0802", ) defined_for = StateCode.MN @@ -45,13 +46,29 @@ def formula(tax_unit, period, parameters): unit_socsec = add(tax_unit, period, ["social_security"]) unit_taxed_socsec = add(tax_unit, period, ["taxable_social_security"]) untaxed_social_security = unit_socsec - unit_taxed_socsec - # ... determine total disability and social security benefits - benefits = disability_income + untaxed_social_security + # Minn. Stat. 290.0802, subd. 1(c), counts as nontaxable retirement + # benefits both the section 22(c)(3) reduction, which takes in the + # Railroad Retirement Act benefits that gross income leaves out, and + # the benefits in federal gross income that Minnesota doesn't tax, + # which take in the rest (45 U.S.C. 231m(a)). So all of the head's + # and spouse's railroad benefits reduce the base. + head_or_spouse = person("is_tax_unit_head_or_spouse", period) + railroad = tax_unit.sum(head_or_spouse * person("railroad_benefits", period)) + # ... determine total disability, social security and railroad benefits + benefits = disability_income + untaxed_social_security + railroad # ... subtract benefits from base amount amount = max_(0, base_amount - benefits) # ... determine net agi fed_agi = tax_unit("adjusted_gross_income", period) - agi = fed_agi + tax_unit("form_4972_lumpsum_distributions", period) + # Subd. 1(a) takes out of AGI the pension and annuity benefits in + # federal gross income that Minnesota doesn't tax: the taxable + # railroad benefits. + taxable_railroad = add(tax_unit, period, ["taxable_railroad_benefits"]) + agi = ( + fed_agi + + tax_unit("form_4972_lumpsum_distributions", period) + - taxable_railroad + ) agi_offset_base = p.agi_offset_base[filing_status] joint = filing_status == filing_status.possible_values.JOINT head_eligible = tax_unit.any(elderly_head | disabled_head) diff --git a/policyengine_us/variables/gov/states/mo/tax/income/mo_withheld_income_tax.py b/policyengine_us/variables/gov/states/mo/tax/income/mo_withheld_income_tax.py index e2d8617e238..78bd9f7ed4b 100644 --- a/policyengine_us/variables/gov/states/mo/tax/income/mo_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/mo/tax/income/mo_withheld_income_tax.py @@ -10,7 +10,7 @@ class mo_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) p_irs = parameters(period).gov.irs.deductions.standard # We apply the base IRS standard deduction amount standard_deduction = p_irs.amount["SINGLE"] diff --git a/policyengine_us/variables/gov/states/ms/tax/income/ms_withheld_income_tax.py b/policyengine_us/variables/gov/states/ms/tax/income/ms_withheld_income_tax.py index 5385c9c176d..a072bbfec22 100644 --- a/policyengine_us/variables/gov/states/ms/tax/income/ms_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/ms/tax/income/ms_withheld_income_tax.py @@ -10,7 +10,7 @@ class ms_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) p = parameters(period).gov.states.ms.tax.income # We apply the base standard deduction amount standard_deduction = p.deductions.standard.amount["SINGLE"] diff --git a/policyengine_us/variables/gov/states/mt/tax/income/base/mt_withheld_income_tax.py b/policyengine_us/variables/gov/states/mt/tax/income/base/mt_withheld_income_tax.py index cdc6b807737..6a734540c74 100644 --- a/policyengine_us/variables/gov/states/mt/tax/income/base/mt_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/mt/tax/income/base/mt_withheld_income_tax.py @@ -10,7 +10,7 @@ class mt_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) p = parameters(period).gov.states.mt.tax.income # We apply the maximum standard deduction amount standard_deduction = p.deductions.standard.cap["SINGLE"] diff --git a/policyengine_us/variables/gov/states/nc/tax/income/nc_withheld_income_tax.py b/policyengine_us/variables/gov/states/nc/tax/income/nc_withheld_income_tax.py index b6ee21caa6d..00a93bce26f 100644 --- a/policyengine_us/variables/gov/states/nc/tax/income/nc_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/nc/tax/income/nc_withheld_income_tax.py @@ -10,7 +10,7 @@ class nc_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) p = parameters(period).gov.states.nc.tax.income # We apply the base standard deduction amount standard_deduction = p.deductions.standard.amount["SINGLE"] diff --git a/policyengine_us/variables/gov/states/nd/tax/income/nd_withheld_income_tax.py b/policyengine_us/variables/gov/states/nd/tax/income/nd_withheld_income_tax.py index c74dd0caff1..171617f7470 100644 --- a/policyengine_us/variables/gov/states/nd/tax/income/nd_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/nd/tax/income/nd_withheld_income_tax.py @@ -10,7 +10,7 @@ class nd_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) p_irs = parameters(period).gov.irs.deductions.standard # We apply the base IRS standard deduction amount standard_deduction = p_irs.amount["SINGLE"] diff --git a/policyengine_us/variables/gov/states/ne/tax/income/ne_withheld_income_tax.py b/policyengine_us/variables/gov/states/ne/tax/income/ne_withheld_income_tax.py index a3a5e2dfdb8..0d8b62b782f 100644 --- a/policyengine_us/variables/gov/states/ne/tax/income/ne_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/ne/tax/income/ne_withheld_income_tax.py @@ -10,7 +10,7 @@ class ne_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) p = parameters(period).gov.states.ne.tax.income # We apply the base standard deduction amount standard_deduction = p.deductions.standard.base_amount["SINGLE"] diff --git a/policyengine_us/variables/gov/states/nj/tax/income/nj_withheld_income_tax.py b/policyengine_us/variables/gov/states/nj/tax/income/nj_withheld_income_tax.py index d363def3e0f..d0627b21b4a 100644 --- a/policyengine_us/variables/gov/states/nj/tax/income/nj_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/nj/tax/income/nj_withheld_income_tax.py @@ -10,7 +10,7 @@ class nj_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) p = parameters(period).gov.states.nj.tax.income # Since New Jersey does not have a standard deduction # we apply the regular personal exemption amount diff --git a/policyengine_us/variables/gov/states/nm/tax/income/nm_withheld_income_tax.py b/policyengine_us/variables/gov/states/nm/tax/income/nm_withheld_income_tax.py index 2913ee8ef32..1463423a1ff 100644 --- a/policyengine_us/variables/gov/states/nm/tax/income/nm_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/nm/tax/income/nm_withheld_income_tax.py @@ -10,7 +10,7 @@ class nm_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) p_irs = parameters(period).gov.irs.deductions.standard # We apply the base IRS standard deduction amount standard_deduction = p_irs.amount["SINGLE"] diff --git a/policyengine_us/variables/gov/states/ny/tax/income/ny_withheld_income_tax.py b/policyengine_us/variables/gov/states/ny/tax/income/ny_withheld_income_tax.py index 45719a0dba5..3265cd264ff 100644 --- a/policyengine_us/variables/gov/states/ny/tax/income/ny_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/ny/tax/income/ny_withheld_income_tax.py @@ -10,7 +10,7 @@ class ny_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) p = parameters(period).gov.states.ny.tax.income # We apply the base standard deduction amount standard_deduction = p.deductions.standard.amount["SINGLE"] diff --git a/policyengine_us/variables/gov/states/oh/tax/income/oh_withheld_income_tax.py b/policyengine_us/variables/gov/states/oh/tax/income/oh_withheld_income_tax.py index ea9f3888835..50242c4984a 100644 --- a/policyengine_us/variables/gov/states/oh/tax/income/oh_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/oh/tax/income/oh_withheld_income_tax.py @@ -10,7 +10,7 @@ class oh_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) p = parameters(period).gov.states.oh.tax.income exempt = agi <= p.agi_threshold # Since Ohio does not have a standard deduction, we apply the diff --git a/policyengine_us/variables/gov/states/ok/tax/income/ok_withheld_income_tax.py b/policyengine_us/variables/gov/states/ok/tax/income/ok_withheld_income_tax.py index c897bb2f260..f5b05b90334 100644 --- a/policyengine_us/variables/gov/states/ok/tax/income/ok_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/ok/tax/income/ok_withheld_income_tax.py @@ -10,7 +10,7 @@ class ok_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) p = parameters(period).gov.states.ok.tax.income standard_deduction = p.deductions.standard.amount["SINGLE"] reduced_agi = max_(agi - standard_deduction, 0) diff --git a/policyengine_us/variables/gov/states/or/tax/income/credits/retirement_credit/or_retirement_credit.py b/policyengine_us/variables/gov/states/or/tax/income/credits/retirement_credit/or_retirement_credit.py index deac860a046..5ffe3b92276 100644 --- a/policyengine_us/variables/gov/states/or/tax/income/credits/retirement_credit/or_retirement_credit.py +++ b/policyengine_us/variables/gov/states/or/tax/income/credits/retirement_credit/or_retirement_credit.py @@ -30,8 +30,13 @@ def formula(tax_unit, period, parameters): 0, total_retirement_income - federal_pension_subtraction ) - # Line 5, total social security - social_security = add(tax_unit, period, ["social_security"]) + # Line 5, total social security. ORS 316.157(2)(f) counts tier 1 + # railroad retirement benefits as Social Security benefits. + social_security = add( + tax_unit, + period, + ["social_security", "railroad_social_security_equivalent_benefits"], + ) # Line 6, base amount reduced by social security benefits received p = parameters(period).gov.states["or"].tax.income.credits.retirement_income diff --git a/policyengine_us/variables/gov/states/or/tax/income/credits/retirement_credit/or_retirement_credit_household_income.py b/policyengine_us/variables/gov/states/or/tax/income/credits/retirement_credit/or_retirement_credit_household_income.py index fc76438a361..ffaa40722ff 100644 --- a/policyengine_us/variables/gov/states/or/tax/income/credits/retirement_credit/or_retirement_credit_household_income.py +++ b/policyengine_us/variables/gov/states/or/tax/income/credits/retirement_credit/or_retirement_credit_household_income.py @@ -7,10 +7,20 @@ class or_retirement_credit_household_income(Variable): label = "Household income for the Oregon Retirement Income Tax Credit" unit = USD definition_period = YEAR + documentation = ( + "Federal AGI plus tax-exempt interest, less the Social Security " + "benefits AGI holds. ORS 316.157(2)(b) leaves Social Security " + "benefits out of household income, and (2)(f) defines them as " + "section 86 benefits: title II benefits and tier 1 railroad " + "retirement benefits." + ) reference = ( "https://www.oregonlegislature.gov/bills_laws/ors/ors316.html" # ORS 316.157 ) defined_for = StateCode.OR adds = ["adjusted_gross_income", "tax_exempt_interest_income"] - subtracts = ["taxable_social_security"] + subtracts = [ + "taxable_social_security", + "taxable_railroad_social_security_equivalent_benefits", + ] diff --git a/policyengine_us/variables/gov/states/or/tax/income/or_withheld_income_tax.py b/policyengine_us/variables/gov/states/or/tax/income/or_withheld_income_tax.py index 955ff3ae4ab..179244e123f 100644 --- a/policyengine_us/variables/gov/states/or/tax/income/or_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/or/tax/income/or_withheld_income_tax.py @@ -10,7 +10,7 @@ class or_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) p = parameters(period).gov.states["or"].tax.income # We apply the maximum standard deduction standard_deduction = p.deductions.standard.amount["SINGLE"] diff --git a/policyengine_us/variables/gov/states/pa/tax/income/pa_withheld_income_tax.py b/policyengine_us/variables/gov/states/pa/tax/income/pa_withheld_income_tax.py index fda42e111ac..93080f61b81 100644 --- a/policyengine_us/variables/gov/states/pa/tax/income/pa_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/pa/tax/income/pa_withheld_income_tax.py @@ -10,7 +10,7 @@ class pa_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) # Pennsylvania does not have standard deductions, personal exemptions, or itemized deductions. # Also, They do not use the federal standard deduction amounts. p = parameters(period).gov.states.pa.tax.income diff --git a/policyengine_us/variables/gov/states/pa/tax/property/property_tax_or_rent_rebate/pa_property_tax_or_rent_rebate_income.py b/policyengine_us/variables/gov/states/pa/tax/property/property_tax_or_rent_rebate/pa_property_tax_or_rent_rebate_income.py index 13dd8cab593..ccb22ee27ac 100644 --- a/policyengine_us/variables/gov/states/pa/tax/property/property_tax_or_rent_rebate/pa_property_tax_or_rent_rebate_income.py +++ b/policyengine_us/variables/gov/states/pa/tax/property/property_tax_or_rent_rebate/pa_property_tax_or_rent_rebate_income.py @@ -12,6 +12,8 @@ class pa_property_tax_or_rent_rebate_income(Variable): "https://www.pa.gov/content/dam/copapwp-pagov/en/revenue/documents/" "formsandpublications/formsforindividuals/ptrr/documents/" "2025_pa-1000_inst.pdf#page=3", + # Taxpayer Relief Act section 1303, "Income" (2) and (3)(i) + "https://www.legis.state.pa.us/WU01/LI/LI/US/PDF/2006/1/0001..PDF#page=38", ) defined_for = StateCode.PA @@ -22,6 +24,9 @@ def formula(tax_unit, period, parameters): adjusted_gross_income = tax_unit("adjusted_gross_income", period) above_the_line_deductions = tax_unit("above_the_line_deductions", period) taxable_social_security = tax_unit("tax_unit_taxable_social_security", period) + # Section 1303 counts half of all railroad retirement benefits, so the + # part that federal AGI holds comes out before the half goes in. + taxable_railroad_benefits = add(tax_unit, period, ["taxable_railroad_benefits"]) half_counted_benefits = tax_unit.sum( head_or_spouse * add( @@ -34,6 +39,8 @@ def formula(tax_unit, period, parameters): # Medicaid facility residents; the PA-1000 does not say # whether the Department of Revenue treats it as SSP. "pa_ssp", + # Section 1303 "Income" (2): 50% of railroad retirement + # benefits, every tier, from 1999. "railroad_benefits", ], ) @@ -81,6 +88,7 @@ def formula(tax_unit, period, parameters): adjusted_gross_income + above_the_line_deductions - taxable_social_security + - taxable_railroad_benefits + p.benefit_income_rate * half_counted_benefits + tax_exempt_income + pa_1000_line_11_income diff --git a/policyengine_us/variables/gov/states/ri/tax/income/ri_withheld_income_tax.py b/policyengine_us/variables/gov/states/ri/tax/income/ri_withheld_income_tax.py index 1de8c9c8d11..7ecd9958bd1 100644 --- a/policyengine_us/variables/gov/states/ri/tax/income/ri_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/ri/tax/income/ri_withheld_income_tax.py @@ -10,7 +10,7 @@ class ri_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) p = parameters(period).gov.states.ri.tax.income # We apply the maximum standard deduction standard_deduction = p.deductions.standard.amount["SINGLE"] diff --git a/policyengine_us/variables/gov/states/sc/tax/income/sc_withheld_income_tax.py b/policyengine_us/variables/gov/states/sc/tax/income/sc_withheld_income_tax.py index f1be877f69a..f9800f2c35b 100644 --- a/policyengine_us/variables/gov/states/sc/tax/income/sc_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/sc/tax/income/sc_withheld_income_tax.py @@ -10,7 +10,7 @@ class sc_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) p_irs = parameters(period).gov.irs.deductions.standard # We apply the base IRS standard deduction amount standard_deduction = p_irs.amount["SINGLE"] diff --git a/policyengine_us/variables/gov/states/tax/income/state_withholding_agi.py b/policyengine_us/variables/gov/states/tax/income/state_withholding_agi.py new file mode 100644 index 00000000000..dd603ee2727 --- /dev/null +++ b/policyengine_us/variables/gov/states/tax/income/state_withholding_agi.py @@ -0,0 +1,24 @@ +from policyengine_us.model_api import * + + +class state_withholding_agi(Variable): + value_type = float + entity = Person + label = "Federal AGI that state income tax withholding reaches" + unit = USD + documentation = ( + "Each person's federal adjusted gross income " + "(adjusted_gross_income_person) less the Railroad Retirement Act " + "benefits it includes (taxable_railroad_benefits). 45 U.S.C. 231m(a) " + "bars any state tax on those annuities. The state withholding " + "estimates (the *_withheld_income_tax variables) start from this " + "amount, and they feed the federal deduction for state and local " + "income taxes." + ) + definition_period = YEAR + reference = "https://www.law.cornell.edu/uscode/text/45/231m#a" + + def formula(person, period, parameters): + agi = person("adjusted_gross_income_person", period) + railroad = person("taxable_railroad_benefits", period) + return agi - railroad diff --git a/policyengine_us/variables/gov/states/ut/tax/income/taxable_income/ut_subtractions.py b/policyengine_us/variables/gov/states/ut/tax/income/taxable_income/ut_subtractions.py index de72d5683fa..450a9c6c27e 100644 --- a/policyengine_us/variables/gov/states/ut/tax/income/taxable_income/ut_subtractions.py +++ b/policyengine_us/variables/gov/states/ut/tax/income/taxable_income/ut_subtractions.py @@ -9,6 +9,14 @@ class ut_subtractions(Variable): documentation = "Form TC-40, line 8" definition_period = YEAR defined_for = StateCode.UT - reference = "https://le.utah.gov/xcode/Title59/Chapter10/59-10-S114.html?v=C59-10-S114_2022032320220323" + reference = ( + "https://le.utah.gov/xcode/Title59/Chapter10/59-10-S114.html?v=C59-10-S114_2022032320220323", + "https://www.law.cornell.edu/uscode/text/45/231m#a", + ) - adds = ["us_govt_interest", "ut_other_subtractions"] + adds = [ + "us_govt_interest", + # 45 U.S.C. 231m(a): railroad retirement benefits in federal AGI. + "taxable_railroad_benefits", + "ut_other_subtractions", + ] diff --git a/policyengine_us/variables/gov/states/ut/tax/income/ut_withheld_income_tax.py b/policyengine_us/variables/gov/states/ut/tax/income/ut_withheld_income_tax.py index a0be0b6cb4f..7e009522549 100644 --- a/policyengine_us/variables/gov/states/ut/tax/income/ut_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/ut/tax/income/ut_withheld_income_tax.py @@ -10,7 +10,7 @@ class ut_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) p_irs = parameters(period).gov.irs.deductions.standard # We apply the maximum standard deduction amount standard_deduction = p_irs.amount["SINGLE"] diff --git a/policyengine_us/variables/gov/states/ut/tax/property/homeowner_renter_relief/ut_homeowner_renter_relief_nontaxable_income.py b/policyengine_us/variables/gov/states/ut/tax/property/homeowner_renter_relief/ut_homeowner_renter_relief_nontaxable_income.py index 3ea3d1d381b..eb6f8eaf235 100644 --- a/policyengine_us/variables/gov/states/ut/tax/property/homeowner_renter_relief/ut_homeowner_renter_relief_nontaxable_income.py +++ b/policyengine_us/variables/gov/states/ut/tax/property/homeowner_renter_relief/ut_homeowner_renter_relief_nontaxable_income.py @@ -8,7 +8,7 @@ class ut_homeowner_renter_relief_nontaxable_income(Variable): unit = USD definition_period = YEAR reference = ( - "https://le.utah.gov/xcode/Title59/Chapter2A/C59-2a_2026010120250507.pdf#page=5" + "https://le.utah.gov/xcode/Title59/Chapter2A/C59-2a_2026010120250507.pdf#page=4" ) defined_for = StateCode.UT @@ -21,6 +21,13 @@ def formula(person, period, parameters): nontaxable_social_security = max_( 0, social_security - ssdi - taxable_social_security ) + # Federal AGI includes the head's and spouse's taxable railroad + # retirement benefits, so the rest of the benefits is added here. + nontaxable_railroad_benefits = max_( + 0, + person("railroad_benefits", period) + - person("taxable_railroad_benefits", period), + ) total_unemployment_compensation = person( "total_unemployment_compensation", period ) @@ -34,5 +41,6 @@ def formula(person, period, parameters): return ( listed_sources + nontaxable_social_security + + nontaxable_railroad_benefits + nontaxable_unemployment_compensation ) diff --git a/policyengine_us/variables/gov/states/va/tax/income/subtractions/age_deduction/va_age_deduction_agi.py b/policyengine_us/variables/gov/states/va/tax/income/subtractions/age_deduction/va_age_deduction_agi.py index f3047afbccb..ccae9317068 100644 --- a/policyengine_us/variables/gov/states/va/tax/income/subtractions/age_deduction/va_age_deduction_agi.py +++ b/policyengine_us/variables/gov/states/va/tax/income/subtractions/age_deduction/va_age_deduction_agi.py @@ -10,8 +10,14 @@ class va_age_deduction_agi(Variable): reference = [ "https://law.lis.virginia.gov/vacodefull/title58.1/chapter3/article2/", # § 58.1-322.03.(5)(b) "https://www.tax.virginia.gov/sites/default/files/vatax-pdf/2022-760-instructions.pdf#page=17", + "https://www.tax.virginia.gov/sites/default/files/vatax-pdf/2024-760-instructions.pdf#page=17", ] defined_for = StateCode.VA adds = ["adjusted_gross_income"] - subtracts = ["tax_unit_taxable_social_security"] + # Adjusted federal AGI is reduced by taxable Social Security and Tier 1 + # Railroad Retirement benefits (2024 Form 760 instructions, Line 4). + subtracts = [ + "tax_unit_taxable_social_security", + "tax_unit_taxable_railroad_social_security_equivalent_benefits", + ] diff --git a/policyengine_us/variables/gov/states/va/tax/income/va_withheld_income_tax.py b/policyengine_us/variables/gov/states/va/tax/income/va_withheld_income_tax.py index 2a60af817ab..442ea82e849 100644 --- a/policyengine_us/variables/gov/states/va/tax/income/va_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/va/tax/income/va_withheld_income_tax.py @@ -10,7 +10,7 @@ class va_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) p = parameters(period).gov.states.va.tax.income standard_deduction = p.deductions.standard["SINGLE"] reduced_agi = max_(agi - standard_deduction, 0) diff --git a/policyengine_us/variables/gov/states/vt/tax/income/vt_amt.py b/policyengine_us/variables/gov/states/vt/tax/income/vt_amt.py index 30ad06cf944..95563a5a1a4 100644 --- a/policyengine_us/variables/gov/states/vt/tax/income/vt_amt.py +++ b/policyengine_us/variables/gov/states/vt/tax/income/vt_amt.py @@ -11,6 +11,7 @@ class vt_amt(Variable): "https://legislature.vermont.gov/statutes/section/32/151/05822", # Vermont §5822. Tax on income of individuals, estates, and trusts (a) "https://tax.vermont.gov/sites/tax/files/documents/RateSched-2021.pdf#page=1", # Vermont 2021 Income Tax Rate Schedules "https://tax.vermont.gov/sites/tax/files/documents/RateSched-2022.pdf#page=1", # Vermont 2022 Income Tax Return Booklet Forms and Instructions + "https://www.law.cornell.edu/uscode/text/45/231m#a", # No state tax on Railroad Retirement Act annuities ) defined_for = StateCode.VT @@ -23,6 +24,11 @@ def formula(tax_unit, period, parameters): # not in the legal code (Vermont §5822 (a)(6)). federal_agi = tax_unit("adjusted_gross_income", period) us_govt_interest = tax_unit("us_govt_interest", period) + # 45 U.S.C. 231m(a) bars any state tax on Railroad Retirement Act + # annuities, so the percentage applies to federal AGI less the + # benefits it includes. The threshold test still uses federal AGI, + # as section 5822(a)(6) writes it. + railroad = add(tax_unit, period, ["taxable_railroad_benefits"]) amt_rate = p.amt.calc(federal_agi) - base_amt = amt_rate * federal_agi + base_amt = amt_rate * max_(federal_agi - railroad, 0) return max_(base_amt - us_govt_interest, 0) diff --git a/policyengine_us/variables/gov/states/vt/tax/income/vt_withheld_income_tax.py b/policyengine_us/variables/gov/states/vt/tax/income/vt_withheld_income_tax.py index 222be10482b..90549b62d03 100644 --- a/policyengine_us/variables/gov/states/vt/tax/income/vt_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/vt/tax/income/vt_withheld_income_tax.py @@ -10,7 +10,7 @@ class vt_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) p = parameters(period).gov.states.vt.tax.income # We apply the base standard deduction amount personal_exmptions = p.deductions.standard.base["SINGLE"] diff --git a/policyengine_us/variables/gov/states/wi/tax/income/wi_withheld_income_tax.py b/policyengine_us/variables/gov/states/wi/tax/income/wi_withheld_income_tax.py index a55ad1d531d..d9b3794cd6c 100644 --- a/policyengine_us/variables/gov/states/wi/tax/income/wi_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/wi/tax/income/wi_withheld_income_tax.py @@ -10,7 +10,7 @@ class wi_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) p = parameters(period).gov.states.wi.tax.income # We apply the maximum standard deduction amount standard_deduction = p.deductions.standard.max["SINGLE"] diff --git a/policyengine_us/variables/gov/states/wv/tax/income/wv_withheld_income_tax.py b/policyengine_us/variables/gov/states/wv/tax/income/wv_withheld_income_tax.py index 2a26dbffea1..92373af2533 100644 --- a/policyengine_us/variables/gov/states/wv/tax/income/wv_withheld_income_tax.py +++ b/policyengine_us/variables/gov/states/wv/tax/income/wv_withheld_income_tax.py @@ -10,7 +10,7 @@ class wv_withheld_income_tax(Variable): definition_period = YEAR def formula(person, period, parameters): - agi = person("adjusted_gross_income_person", period) + agi = person("state_withholding_agi", period) p = parameters(period).gov.states.wv.tax.income # Since West Virginia does not have a standard deduction, we apply the maximum # personal exemption amount diff --git a/policyengine_us/variables/household/income/person/general/disability_benefits.py b/policyengine_us/variables/household/income/person/general/disability_benefits.py index c7d6b7624e8..b30e82abe62 100644 --- a/policyengine_us/variables/household/income/person/general/disability_benefits.py +++ b/policyengine_us/variables/household/income/person/general/disability_benefits.py @@ -9,7 +9,8 @@ class disability_benefits(Variable): documentation = ( "Disability benefits from employment (not Social Security), except for " "worker's compensation. Includes both the taxable and the tax-exempt part; " - "see taxable_disability_benefits and tax_exempt_disability_benefits." + "see taxable_disability_benefits and tax_exempt_disability_benefits. " + "VA disability compensation goes in veterans_benefits." ) definition_period = YEAR uprating = "calibration.gov.irs.soi.employment_income" diff --git a/policyengine_us/variables/household/income/person/general/survivor_benefits.py b/policyengine_us/variables/household/income/person/general/survivor_benefits.py index 62c6b897960..e3b3ac9ee1c 100644 --- a/policyengine_us/variables/household/income/person/general/survivor_benefits.py +++ b/policyengine_us/variables/household/income/person/general/survivor_benefits.py @@ -5,6 +5,10 @@ class survivor_benefits(Variable): value_type = float entity = Person label = "survivor benefits" - documentation = "Survivor benefits other than Social Security survivor benefits." + documentation = ( + "Survivor benefits other than Social Security survivor benefits. VA " + "dependency and indemnity compensation goes in veterans_benefits, and " + "Railroad Retirement Board survivor annuities in railroad_benefits." + ) unit = USD definition_period = YEAR diff --git a/policyengine_us/variables/household/income/person/general/veterans_benefits.py b/policyengine_us/variables/household/income/person/general/veterans_benefits.py index a198c30f3be..631bc7b0ab4 100644 --- a/policyengine_us/variables/household/income/person/general/veterans_benefits.py +++ b/policyengine_us/variables/household/income/person/general/veterans_benefits.py @@ -6,6 +6,26 @@ class veterans_benefits(Variable): entity = Person label = "Veterans benefits" unit = USD - documentation = "Veterans benefits from past military service." + documentation = ( + "Tax-exempt payments for past military service: benefits the " + "Department of Veterans Affairs pays, such as disability compensation, " + "pensions, dependency and indemnity compensation to survivors, and " + "education allowances, and Combat-Related Special Compensation (CRSC), " + "which the military departments pay under 10 U.S.C. 1413a. " + "38 U.S.C. 5301(a)(1) exempts VA benefits from taxation, and IRS " + "Publication 525 lets retirees exclude CRSC from income, so federal " + "gross income leaves both out. Census money income counts VA payments " + "as veterans' payments, and household and SPM benefits include this " + "variable. Taxable military retirement pay goes in " + "military_retirement_pay instead. The model marks anyone with " + "veterans_benefits as a veteran (is_veteran), so set is_veteran to " + "false for a survivor or dependent whose VA payments go here." + ) definition_period = YEAR uprating = "gov.ssa.uprating" + reference = ( + "https://www.law.cornell.edu/uscode/text/38/5301#a_1", + "https://www.law.cornell.edu/uscode/text/10/1413a", + "https://www.irs.gov/pub/irs-prior/p525--2024.pdf#page=19", + "https://www2.census.gov/programs-surveys/cps/methodology/Appendix%20A%20P60-200%20income%20measurement%20definition.pdf#page=3", + ) diff --git a/policyengine_us/variables/household/income/person/misc/educational_assistance.py b/policyengine_us/variables/household/income/person/misc/educational_assistance.py index ec72d09ce29..f822902fccf 100644 --- a/policyengine_us/variables/household/income/person/misc/educational_assistance.py +++ b/policyengine_us/variables/household/income/person/misc/educational_assistance.py @@ -5,6 +5,9 @@ class educational_assistance(Variable): value_type = float entity = Person label = "educational assistance" - documentation = "Scholarships, grants, and other educational assistance." + documentation = ( + "Scholarships, grants, and other educational assistance. VA education " + "benefits go in veterans_benefits." + ) unit = USD definition_period = YEAR diff --git a/policyengine_us/variables/household/income/person/retirement/railroad_benefits.py b/policyengine_us/variables/household/income/person/retirement/railroad_benefits.py index 65278a26348..becc13dfb01 100644 --- a/policyengine_us/variables/household/income/person/retirement/railroad_benefits.py +++ b/policyengine_us/variables/household/income/person/retirement/railroad_benefits.py @@ -10,8 +10,23 @@ class railroad_benefits(Variable): documentation = ( "Gross annual benefits paid by the Railroad Retirement Board, such as " "retirement, disability, spouse and survivor annuities, before Medicare " - "premiums or other amounts are withheld. Railroad Unemployment Insurance " - "Act benefits are reported elsewhere: unemployment benefits in " - "unemployment_compensation and sickness benefits in disability_benefits." + "premiums or other amounts are withheld: the social security " + "equivalent benefit (Form RRB-1099) plus the rest of the annuity " + "(Form RRB-1099-R). Railroad Unemployment Insurance Act benefits are " + "reported elsewhere: unemployment benefits in " + "unemployment_compensation and sickness benefits in " + "disability_benefits. Census counts railroad retirement as a " + "government transfer, and household and SPM benefits include these " + "benefits. Enter the parts, not this total, so that taxes see them. " + "A total entered here replaces the sum of the parts and reaches " + "income totals and benefit programs but no tax, and it applies only " + "to the year it is entered for, while the parts carry into later " + "years. Do not also enter them as social_security, pension_income, " + "disability_benefits or survivor_benefits." ) - default_value = 0 + adds = [ + "railroad_social_security_equivalent_benefits", + "taxable_railroad_pension_benefits", + "tax_exempt_railroad_pension_benefits", + ] + reference = "https://www2.census.gov/programs-surveys/cps/methodology/Appendix%20A%20P60-200%20income%20measurement%20definition.pdf#page=4" diff --git a/policyengine_us/variables/household/income/person/retirement/railroad_social_security_equivalent_benefits.py b/policyengine_us/variables/household/income/person/retirement/railroad_social_security_equivalent_benefits.py new file mode 100644 index 00000000000..a1c820d5211 --- /dev/null +++ b/policyengine_us/variables/household/income/person/retirement/railroad_social_security_equivalent_benefits.py @@ -0,0 +1,30 @@ +from policyengine_us.model_api import * + + +class railroad_social_security_equivalent_benefits(Variable): + value_type = float + entity = Person + label = "Railroad Retirement social security equivalent benefits" + unit = USD + definition_period = YEAR + documentation = ( + "The part of a tier 1 Railroad Retirement Act annuity equal to the " + "Social Security benefit the same work record would earn: the social " + "security equivalent benefit (SSEB) on Form RRB-1099, box 5, net of " + "repayments. The model takes only nonnegative amounts, as it does for " + "reported Social Security benefits. IRC 86(d)(1)(B) counts it as a " + "social security benefit, " + "so section 86 taxes it together with title II benefits. The rest of " + "the annuity, reported on Form RRB-1099-R, goes in " + "taxable_railroad_pension_benefits and " + "tax_exempt_railroad_pension_benefits." + ) + uprating = "gov.ssa.uprating" + reference = ( + "https://www.law.cornell.edu/uscode/text/26/86#d_1_B", + "https://www.law.cornell.edu/uscode/text/26/86#d_4", + # IRS Pub. 915 (2024): the SSEB portion of tier 1 is treated as a + # social security benefit; Worksheet A line A adds box 5 of + # Forms SSA-1099 and RRB-1099. PDF pages 2, 4. + "https://www.irs.gov/pub/irs-prior/p915--2024.pdf#page=2", + ) diff --git a/policyengine_us/variables/household/income/person/retirement/tax_exempt_railroad_pension_benefits.py b/policyengine_us/variables/household/income/person/retirement/tax_exempt_railroad_pension_benefits.py new file mode 100644 index 00000000000..9780f5b10e2 --- /dev/null +++ b/policyengine_us/variables/household/income/person/retirement/tax_exempt_railroad_pension_benefits.py @@ -0,0 +1,23 @@ +from policyengine_us.model_api import * + + +class tax_exempt_railroad_pension_benefits(Variable): + value_type = float + entity = Person + label = "Tax-exempt Railroad Retirement pension benefits" + unit = USD + definition_period = YEAR + uprating = "calibration.gov.irs.soi.tax_exempt_pension_income" + documentation = ( + "The part of Form RRB-1099-R benefits that recovers the employee's " + "own contributions tax free. IRC 72(r)(2) treats tier 2 employee " + "taxes as employee contributions to the plan, and the annuitant " + "recovers them over the annuity's expected life, usually under the " + "Simplified Method." + ) + reference = ( + "https://www.law.cornell.edu/uscode/text/26/72#r_2", + # IRS Pub. 575 (2024): tax-free recovery of employee contributions + # from tier 2 and the non-social security equivalent part of tier 1. + "https://www.irs.gov/pub/irs-prior/p575--2024.pdf#page=7", + ) diff --git a/policyengine_us/variables/household/income/person/retirement/taxable_railroad_pension_benefits.py b/policyengine_us/variables/household/income/person/retirement/taxable_railroad_pension_benefits.py new file mode 100644 index 00000000000..2db4b8c4a5b --- /dev/null +++ b/policyengine_us/variables/household/income/person/retirement/taxable_railroad_pension_benefits.py @@ -0,0 +1,27 @@ +from policyengine_us.model_api import * + + +class taxable_railroad_pension_benefits(Variable): + value_type = float + entity = Person + label = "Taxable Railroad Retirement pension benefits" + unit = USD + definition_period = YEAR + uprating = "calibration.gov.irs.soi.taxable_pension_income" + documentation = ( + "Railroad Retirement Act benefits other than the social security " + "equivalent benefit, less the employee contributions recovered tax " + "free: the non-social security equivalent part of tier 1, tier 2, " + "vested dual benefits and supplemental annuities reported on Form " + "RRB-1099-R. IRC 72(r)(1) treats these benefits as paid from a " + "section 401(a) employer plan, so the taxable part is gross income " + "like any other pension. 45 U.S.C. 231m(a) bars states from taxing " + "them." + ) + reference = ( + "https://www.law.cornell.edu/uscode/text/26/72#r", + "https://www.law.cornell.edu/uscode/text/45/231m#a", + # IRS Pub. 575 (2024): the second category of railroad retirement + # benefits and the Form RRB-1099-R boxes. PDF pages 7-8. + "https://www.irs.gov/pub/irs-prior/p575--2024.pdf#page=7", + ) diff --git a/policyengine_us/variables/household/income/spm_unit/spm_unit_benefits.py b/policyengine_us/variables/household/income/spm_unit/spm_unit_benefits.py index 1c4aba748a1..38d2c7aff6a 100644 --- a/policyengine_us/variables/household/income/spm_unit/spm_unit_benefits.py +++ b/policyengine_us/variables/household/income/spm_unit/spm_unit_benefits.py @@ -80,6 +80,10 @@ def formula(spm_unit, period, parameters): "educational_assistance", "financial_assistance", "survivor_benefits", + # Census money income counts veterans' payments, and railroad + # retirement as a government transfer (P60-200 Appendix A). + "veterans_benefits", + "railroad_benefits", "spm_unit_energy_subsidy", "tanf", # Washington (WA) cash-assistance programs. wa_sfa and wa_rca diff --git a/source_conflicts/candidates.yaml b/source_conflicts/candidates.yaml index 2674b4e9be5..215e02bb073 100644 --- a/source_conflicts/candidates.yaml +++ b/source_conflicts/candidates.yaml @@ -9032,6 +9032,41 @@ candidates: state: unreviewed note: PE-US applies the floor to the prior year's guideline, rounded to cents, with the statute's strict "less than" (or_liheap_eligible.py). +- id: us-or:retirement-credit-household-income-railroad-tier-2 + program: income-tax + years: + from: 2025 + type: tax + question: Does household income for Oregon's retirement income credit count the tier 2 railroad annuity, or leave + out every Railroad Retirement benefit? + sources: + - kind: statute + citation: ORS 316.157(2)(b), (c) and (f) + url: https://www.oregonlegislature.gov/bills_laws/ors/ors316.html + - kind: regulation + citation: OAR 150-316-0225(3) + url: https://secure.sos.state.or.us/oard/view.action?ruleNumber=150-316-0225 + - kind: guidance + citation: 2025 Publication OR-17, retirement income credit + url: https://www.oregon.gov/dor/forms/FormsPubs/publication-or-17_101-431_2025.pdf#page=107 + readings: + statute: Household income leaves out Social Security benefits, which (2)(f) defines as section 86 benefits, + "Title II Social Security or tier 1 railroad retirement benefits", so the tier 2 annuity in federal AGI stays + in. OR-17 lists "RRB benefits (Tier 2 only)" as household income. + other: The rule says that "benefits received from Social Security or Railroad Retirement are not included in + computing the household income limitation", which, read literally, leaves tier 2 out as well. + example: Joint filers aged 66 with $25,000 of pension income, $5,000 of interest, $4,000 of tier 1 benefits and a + $3,000 taxable tier 2 annuity have household income of $33,000 and a credit of $720 under the statute. Leaving + tier 2 out gives $30,000 and $990. + pe_us: + follows: statute + code: + - policyengine_us/variables/gov/states/or/tax/income/credits/retirement_credit/or_retirement_credit_household_income.py + - policyengine_us/variables/gov/states/or/tax/income/credits/retirement_credit/or_retirement_credit.py + prs: + - https://github.com/PolicyEngine/policyengine-us/pull/10696 + state: ruled + note: The statute comes first, and the department's own publication reads it the same way. - id: us-pa:liheap-interest-dividend-threshold program: liheap years: @@ -9136,6 +9171,37 @@ candidates: state: ruled note: PE-US follows the memoranda, the issuance directives for a payment the plan says "may" be issued (ruled 2026-10-28). The memoranda are served only over http. +- id: us-pa:ptrr-railroad-retirement-tier-2-share + program: property-tax-credit + years: + from: 2025 + type: calendar + question: Does Pennsylvania Property Tax/Rent Rebate income count half of all railroad retirement benefits, or half + of tier 1 and the whole gross tier 2 annuity? + sources: + - kind: statute + citation: Taxpayer Relief Act (Act 1 of 2006, Special Session 1), section 1303, "Income" (2) + url: https://www.legis.state.pa.us/WU01/LI/LI/US/PDF/2006/1/0001..PDF#page=38 + - kind: instructions + citation: 2025 PA-1000 instructions, lines 5 and 6 + url: https://www.pa.gov/content/dam/copapwp-pagov/en/revenue/documents/formsandpublications/formsforindividuals/ptrr/documents/2025_pa-1000_inst.pdf#page=7 + readings: + statute: Income includes "50% of railroad retirement benefits for calendar years 1999 and thereafter". The act + names no tiers. + other: Line 5 takes one-half of Railroad Retirement Tier 1 Benefits, and line 6 takes the gross amount, not the + taxable amount, of Tier 2 Railroad Retirement Benefits. + example: A claimant aged 65 with $10,000 of tier 1 benefits, a $10,000 taxable tier 2 annuity and a $2,000 tax-free + recovery has income of $11,000 under the act and $17,000 under the instructions. + pe_us: + follows: statute + code: + - policyengine_us/variables/gov/states/pa/tax/property/property_tax_or_rent_rebate/pa_property_tax_or_rent_rebate_income.py + - policyengine_us/parameters/gov/states/pa/tax/property/property_tax_or_rent_rebate/benefit_income_rate.yaml + prs: + - https://github.com/PolicyEngine/policyengine-us/pull/10696 + state: ruled + note: The act's only railroad words sit in "Income" (2). PE-US takes the railroad benefits in federal AGI out of + income and adds half of all of them, so the tier 2 annuity counts once, at half. - id: us-pa:snap-child-support-deduction program: snap years: @@ -13232,6 +13298,40 @@ candidates: zero and kept apart from the passive basket, so these royalties count although Worksheet 1 line 8 reads only Schedule E line 23b. Where rental real estate goes is a separate reading (us:eitc-rental-real-estate-basket). Register the entry and add the marker once #10297 is on main. +- id: us:elderly-credit-railroad-tier-2-return-of-cost + program: income-tax + years: + from: 2023 + type: tax + question: Does the tax-free recovery of tier 2 employee contributions from a Railroad Retirement Act annuity reduce + the section 22 amount for the credit for the elderly or disabled? + sources: + - kind: statute + citation: 26 U.S.C. 22(c)(3)(A)(i)(II); 72(b), (d) and (r) + url: https://www.law.cornell.edu/uscode/text/26/22#c_3_A + - kind: instructions + citation: 2026 Instructions for Schedule R (Form 1040), lines 13a and 13b + url: https://www.irs.gov/pub/irs-pdf/i1040sr.pdf#page=4 + readings: + statute: Section 22(c)(3)(A)(i)(II) reduces the section 22 amount by amounts the individual, or either spouse on a + joint return, receives as a pension or annuity "which is excluded from gross income and payable under" the + Railroad Retirement Act of 1974. The tax-free part of the annuity other than the social security equivalent + benefit, which recovers tier 2 employee taxes under 72(r)(2) and 72(d) (72(b) for some annuities that started + before November 19, 1996), meets both conditions. + other: Line 13a takes nontaxable tier 1 benefits treated as social security. Line 13b says "Don't include amounts + that are treated as a return of your cost of a pension or annuity," so the tax-free recovery reduces nothing. + example: A single filer aged 67 with $3,000 of social security equivalent benefits, all nontaxable, $2,000 of taxable + tier 2 benefits and $500 recovered tax free has a section 22 amount of $1,500 under the statute ($5,000 - $3,000 - + $500) and $2,000 under the instructions ($5,000 - $3,000); the model gives $1,500. + pe_us: + follows: statute + code: + - policyengine_us/variables/gov/irs/credits/elderly_and_disabled/section_22_income.py + prs: + - https://github.com/PolicyEngine/policyengine-us/pull/10696 + state: ruled + note: The 2023, 2024 and 2025 instructions carry the same line 13b sentence. The model takes the tax-free recovery + from tax_exempt_railroad_pension_benefits for the head and spouse. - id: us:filing-chart-a-blindness-and-equality program: income-tax years: