diff --git a/changelog.d/fix-tips-overtime-2029-termination.fixed.md b/changelog.d/fix-tips-overtime-2029-termination.fixed.md new file mode 100644 index 00000000000..7434af91548 --- /dev/null +++ b/changelog.d/fix-tips-overtime-2029-termination.fixed.md @@ -0,0 +1 @@ +Limit the tips and overtime deductions to 2025-2028, as 26 U.S.C. 224(h) and 225(g) and their effective dates require, with new in_effect parameters; the federal deduction lists now name both deductions in every year so those parameters alone set the years. From 2029 Montana and Arizona no longer subtract them and South Carolina's OBBBA add-back no longer includes them. diff --git a/policyengine_us/parameters/gov/irs/deductions/deductions_if_itemizing.yaml b/policyengine_us/parameters/gov/irs/deductions/deductions_if_itemizing.yaml index 9d055329b32..87159339586 100644 --- a/policyengine_us/parameters/gov/irs/deductions/deductions_if_itemizing.yaml +++ b/policyengine_us/parameters/gov/irs/deductions/deductions_if_itemizing.yaml @@ -1,9 +1,14 @@ description: Deductions from taxable income if itemizing. values: + # The tips and overtime deductions (26 U.S.C. 224 and 225) are listed in every + # year. Their own in_effect parameters zero them outside 2025-2028, so a reform + # that switches them on in another year also reaches taxable income. 2018-01-01: - qualified_business_income_deduction - wagering_losses_deduction - itemized_taxable_income_deductions + - tip_income_deduction + - overtime_income_deduction 2025-01-01: - qualified_business_income_deduction @@ -18,6 +23,8 @@ values: - qualified_business_income_deduction - wagering_losses_deduction - itemized_taxable_income_deductions + - tip_income_deduction + - overtime_income_deduction metadata: unit: list diff --git a/policyengine_us/parameters/gov/irs/deductions/deductions_if_not_itemizing.yaml b/policyengine_us/parameters/gov/irs/deductions/deductions_if_not_itemizing.yaml index fc58295ca71..27e158096e0 100644 --- a/policyengine_us/parameters/gov/irs/deductions/deductions_if_not_itemizing.yaml +++ b/policyengine_us/parameters/gov/irs/deductions/deductions_if_not_itemizing.yaml @@ -1,19 +1,28 @@ description: Deductions from taxable income if not itemizing. values: + # The tips and overtime deductions (26 U.S.C. 224 and 225) are listed in every + # year. Their own in_effect parameters zero them outside 2025-2028, so a reform + # that switches them on in another year also reaches taxable income. 2018-01-01: - standard_deduction - qualified_business_income_deduction - tuition_and_fees_deduction + - tip_income_deduction + - overtime_income_deduction # The 2020 charitable deduction for non-itemizers is above the line # (charitable_contributions_ald). From 2021 it is taken from adjusted gross # income under 26 U.S.C. 170(p) and 63(b)(4). 2020-01-01: - standard_deduction - qualified_business_income_deduction + - tip_income_deduction + - overtime_income_deduction 2021-01-01: - charitable_deduction_for_non_itemizers - standard_deduction - qualified_business_income_deduction + - tip_income_deduction + - overtime_income_deduction 2025-01-01: - charitable_deduction_for_non_itemizers - standard_deduction @@ -26,6 +35,8 @@ values: - charitable_deduction_for_non_itemizers - standard_deduction - qualified_business_income_deduction + - tip_income_deduction + - overtime_income_deduction metadata: unit: list diff --git a/policyengine_us/parameters/gov/irs/deductions/overtime_income/in_effect.yaml b/policyengine_us/parameters/gov/irs/deductions/overtime_income/in_effect.yaml new file mode 100644 index 00000000000..069db9862d1 --- /dev/null +++ b/policyengine_us/parameters/gov/irs/deductions/overtime_income/in_effect.yaml @@ -0,0 +1,19 @@ +description: The IRS allows the overtime income deduction when this is true. +values: + 0000-01-01: false + # Pub. L. 119-21, sec. 70202(g): section 225 applies to taxable years + # beginning after December 31, 2024. + 2025-01-01: true + # 26 U.S.C. 225(g): no deduction is allowed for any taxable year beginning + # after December 31, 2028. + 2029-01-01: false + +metadata: + unit: bool + period: year + label: Overtime income deduction in effect + reference: + - title: 26 U.S.C. 225(g) + href: https://www.law.cornell.edu/uscode/text/26/225#g + - title: "One Big Beautiful Bill Act, Pub. L. No. 119-21, secs. 70202(a) (adding 26 U.S.C. 225(g)) and 70202(g) (effective date) #page 103-104" + href: https://www.congress.gov/119/bills/hr1/BILLS-119hr1enr.pdf#page=103 diff --git a/policyengine_us/parameters/gov/irs/deductions/tip_income/in_effect.yaml b/policyengine_us/parameters/gov/irs/deductions/tip_income/in_effect.yaml new file mode 100644 index 00000000000..216f1110c0b --- /dev/null +++ b/policyengine_us/parameters/gov/irs/deductions/tip_income/in_effect.yaml @@ -0,0 +1,19 @@ +description: The IRS allows the tip income deduction when this is true. +values: + 0000-01-01: false + # Pub. L. 119-21, sec. 70201(j): section 224 applies to taxable years + # beginning after December 31, 2024. + 2025-01-01: true + # 26 U.S.C. 224(h): no deduction is allowed for any taxable year beginning + # after December 31, 2028. + 2029-01-01: false + +metadata: + unit: bool + period: year + label: Tip income deduction in effect + reference: + - title: 26 U.S.C. 224(h) + href: https://www.law.cornell.edu/uscode/text/26/224#h + - title: "One Big Beautiful Bill Act, Pub. L. No. 119-21, secs. 70201(a) (adding 26 U.S.C. 224(h)) and 70201(j) (effective date) #page 100,102" + href: https://www.congress.gov/119/bills/hr1/BILLS-119hr1enr.pdf#page=100 diff --git a/policyengine_us/parameters/gov/states/co/tax/income/additions/qualified_overtime/in_effect.yaml b/policyengine_us/parameters/gov/states/co/tax/income/additions/qualified_overtime/in_effect.yaml index b902d60fbe0..2b0c5947e3b 100644 --- a/policyengine_us/parameters/gov/states/co/tax/income/additions/qualified_overtime/in_effect.yaml +++ b/policyengine_us/parameters/gov/states/co/tax/income/additions/qualified_overtime/in_effect.yaml @@ -2,7 +2,6 @@ description: Colorado adds back the federal overtime compensation deduction in t values: 2025-01-01: false 2026-01-01: true - 2029-01-01: false metadata: unit: bool @@ -17,12 +16,11 @@ metadata: # effective for tax years beginning on or after January 1, 2026. Colorado continues # to conform to the federal tips deduction (IRC 224), so tips are not added back. # Colorado conformed for tax year 2025 (Section 6 was not yet effective). -# The federal IRC 225 overtime deduction sunsets after 2028: it is dropped from -# the deductions_if_itemizing and deductions_if_not_itemizing lists at 2029, so -# federal taxable income no longer reflects it. The overtime_income_deduction -# variable is not itself year-gated (it still computes a nonzero amount), so the -# add-back is turned off here from 2029 -- otherwise Colorado would add back a -# deduction that was never taken federally, over-taxing filers. +# C.R.S. 39-22-104(3)(u) adds back "the amount of any overtime compensation +# excluded or deducted from federal gross income" and has no end date. The +# federal IRC 225 deduction ends after 2028 (225(g)), and overtime_income_deduction +# is zero from 2029 (gov.irs.deductions.overtime_income.in_effect), so the +# add-back falls to zero with it and needs no end date here. # The 2026 start is enforced both here and by the year block in additions.yaml # (co_overtime_income_addback appears only from the 2026 list); update both if # the effective dates change. diff --git a/policyengine_us/reforms/crfb/senior_deduction_extension.py b/policyengine_us/reforms/crfb/senior_deduction_extension.py index 56bd3d19c4f..d686b464cdd 100644 --- a/policyengine_us/reforms/crfb/senior_deduction_extension.py +++ b/policyengine_us/reforms/crfb/senior_deduction_extension.py @@ -5,13 +5,17 @@ def create_senior_deduction_extension() -> Reform: def modify_parameters(parameters): - # Update deductions_if_itemizing to include additional_senior_deduction after 2028 + # Update deductions_if_itemizing to include additional_senior_deduction after 2028. + # The tips and overtime deductions stay listed, as in the baseline lists; + # their own in_effect parameters zero them after 2028. parameters.gov.irs.deductions.deductions_if_itemizing.update( start=instant("2029-01-01"), value=[ "qualified_business_income_deduction", "wagering_losses_deduction", "itemized_taxable_income_deductions", + "tip_income_deduction", + "overtime_income_deduction", "additional_senior_deduction", ], ) @@ -22,6 +26,8 @@ def modify_parameters(parameters): value=[ "standard_deduction", "qualified_business_income_deduction", + "tip_income_deduction", + "overtime_income_deduction", "additional_senior_deduction", "charitable_deduction_for_non_itemizers", ], diff --git a/policyengine_us/tests/core/test_tip_and_overtime_deduction_years.py b/policyengine_us/tests/core/test_tip_and_overtime_deduction_years.py new file mode 100644 index 00000000000..9c9b3048a4c --- /dev/null +++ b/policyengine_us/tests/core/test_tip_and_overtime_deduction_years.py @@ -0,0 +1,171 @@ +"""Years in which the tips and overtime deductions apply (26 U.S.C. 224, 225). + +Sections 224 and 225 apply to taxable years beginning after December 31, 2024 +(Pub. L. 119-21, secs. 70201(j) and 70202(g)), and 224(h) and 225(g) say "No +deduction shall be allowed under this section for any taxable year beginning +after December 31, 2028." + +The first test builds two simulations of many tax units (current law, and +both deductions switched on in every year); the second builds one. Inputs are +the same in every year. The tests check: + +1. Outside 2025-2028 both deductions are zero for every return. +2. The year switch is the only thing that changes across years. With both + deductions switched on in every year, each return gets in every year what + it gets in 2025. Inside 2025-2028 that is also the current-law amount. This + holds because the caps, thresholds and phase-out parameters each carry one + value, dated 2025; if any is later indexed or amended, compare against + that year's value instead. +3. South Carolina's add-back, Montana's Schedule 1-A subtraction and + Arizona's subtractions pass the federal deductions through exactly. Giving + a return its tips and overtime moves each by the federal tips and overtime + deductions in 2026-2028, and not at all from 2029 (Hypothesis). +""" + +import numpy as np +from hypothesis import Phase, example, given, settings +from hypothesis import strategies as st + +from policyengine_us import Simulation +from policyengine_us.system import system + +YEARS = range(2015, 2036) +FIRST_YEAR, LAST_YEAR = 2025, 2028 +STATUSES = ("SINGLE", "JOINT", "SEPARATE", "HEAD_OF_HOUSEHOLD", "SURVIVING_SPOUSE") +OUTPUTS = ("tip_income_deduction", "overtime_income_deduction") +SWITCHES = ( + "gov.irs.deductions.tip_income.in_effect", + "gov.irs.deductions.overtime_income.in_effect", +) +# Each state's channel for the federal deductions, as a tax unit amount. +STATE_OUTPUTS = { + "SC": "sc_obbba_addback", + "MT": "mt_federal_obbba_subtraction", + "AZ": "az_subtractions", +} +STATE_YEARS = (2026, 2027, 2028, 2029, 2030, 2035) + + +def _situation(units, years, state_of=None): + """One tax unit per (status, magi, tips, overtime); joint units get a + spouse with the same tips and overtime, others one person.""" + people, tax_units, marital_units, households = {}, {}, {}, {} + for i, (status, magi, tips, overtime) in enumerate(units): + members = [f"head{i}"] + ([f"spouse{i}"] if status == "JOINT" else []) + for name in members: + people[name] = { + "age": {year: 40 for year in years}, + "ssn_card_type": {year: "CITIZEN" for year in years}, + "treasury_tipped_occupation_code": {year: 102 for year in years}, + "tip_income": {year: float(tips) for year in years}, + "fsla_overtime_premium": {year: float(overtime) for year in years}, + } + tax_units[f"t{i}"] = { + "members": members, + "filing_status": {year: status for year in years}, + "adjusted_gross_income": {year: float(magi) for year in years}, + # Fixed, so that adding tips cannot change South Carolina's + # standard deduction add-back through the itemizing choice. + "tax_unit_itemizes": {year: False for year in years}, + } + marital_units[f"m{i}"] = {"members": members} + state = state_of[i] if state_of else "TX" + households[f"h{i}"] = { + "members": members, + "state_code": {year: state for year in years}, + } + return { + "people": people, + "tax_units": tax_units, + "marital_units": marital_units, + "households": households, + } + + +def _draw(seed, n=120): + rng = np.random.default_rng(seed) + units = [] + for _ in range(n): + status = STATUSES[int(rng.integers(len(STATUSES)))] + magi = float(rng.choice([0, 60_000, 150_000, int(rng.integers(0, 500_001))])) + tips = int(rng.choice([0, 25_000, int(rng.integers(0, 40_001))])) + overtime = int(rng.choice([0, 12_500, int(rng.integers(0, 30_001))])) + units.append((status, magi, tips, overtime)) + return units + + +def test_deductions_apply_only_from_2025_through_2028(): + # One draw, so the switched-on reform builds one model. + units = _draw(seed=0, n=240) + situation = _situation(units, YEARS) + baseline = Simulation(tax_benefit_system=system, situation=situation) + always = Simulation( + situation=situation, + reform={switch: {"2015-01-01.2100-12-31": True} for switch in SWITCHES}, + ) + for name in OUTPUTS: + first_year = baseline.calculate(name, FIRST_YEAR) + # Guard: the draw must hold returns with a positive deduction. + assert np.any(first_year > 0), name + for year in YEARS: + current_law = baseline.calculate(name, year) + switched_on = always.calculate(name, year) + # 2. The switch alone changes the amount across years. + np.testing.assert_array_equal(switched_on, first_year, (name, year)) + if FIRST_YEAR <= year <= LAST_YEAR: + np.testing.assert_array_equal(current_law, first_year, (name, year)) + else: + # 1. Nothing outside 2025-2028. + assert np.all(current_law == 0), (name, year) + + +amounts = st.one_of( + st.sampled_from([0, 12_500, 25_000]), st.integers(min_value=0, max_value=40_000) +) +cases = st.tuples( + st.sampled_from(STATUSES), + st.one_of( + st.sampled_from([60_000, 150_000, 300_000]), + st.integers(min_value=0, max_value=500_000), + ), + amounts, + amounts, +) + + +# Shrinking a failure takes minutes on a simulation this size; the failing +# example is still reported. +@settings( + max_examples=5, + deadline=None, + derandomize=True, + phases=(Phase.explicit, Phase.generate), +) +@given(drawn=st.lists(cases, min_size=8, max_size=8)) +# Generation starts from the minimal draw (no tips or overtime), so one +# explicit draw makes sure positive deductions reach every state. +@example(drawn=[(status, 60_000, 10_000, 5_000) for status in STATUSES] * 2) +def test_states_pass_the_federal_deductions_through_only_while_they_apply(drawn): + # Each drawn return appears in each state twice: with its tips and + # overtime, and with none. + units, state_of = [], [] + for status, magi, tips, overtime in drawn: + for state in STATE_OUTPUTS: + units += [(status, magi, tips, overtime), (status, magi, 0, 0)] + state_of += [state, state] + simulation = Simulation( + tax_benefit_system=system, + situation=_situation(units, STATE_YEARS, state_of), + ) + state_of = np.array(state_of) + for year in STATE_YEARS: + federal = sum(simulation.calculate(name, year) for name in OUTPUTS) + for state, name in STATE_OUTPUTS.items(): + amount = simulation.calculate(name, year, map_to="tax_unit") + rows = np.flatnonzero(state_of == state) + with_pay, without_pay = rows[0::2], rows[1::2] + moved = amount[with_pay] - amount[without_pay] + # 3. Exactly the federal deductions while they apply, else nothing. + expected = federal[with_pay] if year <= LAST_YEAR else 0 + np.testing.assert_allclose(moved, expected, atol=0.01, err_msg=(name, year)) + assert np.all(federal[without_pay] == 0), (name, year) diff --git a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/itemizing/taxable_income_before_itemized_deductions.yaml b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/itemizing/taxable_income_before_itemized_deductions.yaml index 011a2e9d3bb..88f98f9a334 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/itemizing/taxable_income_before_itemized_deductions.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/itemizing/taxable_income_before_itemized_deductions.yaml @@ -54,15 +54,29 @@ - name: 2030, after the tip, overtime, senior and vehicle loan deductions end period: 2030 input: - adjusted_gross_income: 500_000 - filing_status: SINGLE - qualified_business_income_deduction: 1_000 - tip_income_deduction: 2_000 - overtime_income_deduction: 3_000 - additional_senior_deduction: 4_000 - auto_loan_interest_deduction: 5_000 + people: + person1: + age: 40 + tip_income: 2_000 + treasury_tipped_occupation_code: 102 + fsla_overtime_premium: 3_000 + tax_units: + tax_unit: + members: [person1] + adjusted_gross_income: 100_000 + filing_status: SINGLE + qualified_business_income_deduction: 1_000 + additional_senior_deduction: 4_000 + auto_loan_interest_deduction: 5_000 + households: + household: + members: [person1] output: # Sections 224(h), 225(g), 151(d)(5)(C)(i) and 163(h)(4)(A), as added by # P.L. 119-21, allow these deductions only for taxable years beginning - # before 2029, so only the QBI deduction is subtracted. - taxable_income_before_itemized_deductions: 499_000 + # before 2029, so only the QBI deduction is subtracted. The tips and + # overtime deductions are zero from 2029 (their in_effect parameters); + # the senior and vehicle loan deductions are off the 2029 deduction list. + tip_income_deduction: 0 + overtime_income_deduction: 0 + taxable_income_before_itemized_deductions: 99_000 diff --git a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/overtime_income/overtime_income_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/overtime_income/overtime_income_deduction.yaml index 1fb261b4b9a..6a45a5e5ab3 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/overtime_income/overtime_income_deduction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/overtime_income/overtime_income_deduction.yaml @@ -1,3 +1,103 @@ +# 26 U.S.C. 225(g) allows no deduction for any taxable year beginning after +# December 31, 2028, and Pub. L. 119-21 sec. 70202(g) applies the section to +# taxable years beginning after December 31, 2024. The same single filer, below +# the $150,000 phase-out start, is computed in each year. + +- name: 2024, before the deduction takes effect, no deduction is allowed. + period: 2024 + input: + people: + person1: + age: 40 + fsla_overtime_premium: 5_000 + tax_units: + tax_unit: + members: [person1] + filing_status: SINGLE + adjusted_gross_income: 60_000 + households: + household: + members: [person1] + state_code: TX + output: + overtime_income_deduction: 0 + +- name: 2025, the first year, the deduction is allowed. + period: 2025 + input: + people: + person1: + age: 40 + fsla_overtime_premium: 5_000 + tax_units: + tax_unit: + members: [person1] + filing_status: SINGLE + adjusted_gross_income: 60_000 + households: + household: + members: [person1] + state_code: TX + output: + overtime_income_deduction: 5_000 + +- name: 2028, the last year, the deduction is allowed. + period: 2028 + input: + people: + person1: + age: 40 + fsla_overtime_premium: 5_000 + tax_units: + tax_unit: + members: [person1] + filing_status: SINGLE + adjusted_gross_income: 60_000 + households: + household: + members: [person1] + state_code: TX + output: + overtime_income_deduction: 5_000 + +- name: 2029, after 225(g) ends it, no deduction is allowed. + period: 2029 + input: + people: + person1: + age: 40 + fsla_overtime_premium: 5_000 + tax_units: + tax_unit: + members: [person1] + filing_status: SINGLE + adjusted_gross_income: 60_000 + households: + household: + members: [person1] + state_code: TX + output: + overtime_income_deduction: 0 + +- name: 2030, later years stay at zero. + period: 2030 + input: + people: + person1: + age: 40 + fsla_overtime_premium: 5_000 + tax_units: + tax_unit: + members: [person1] + filing_status: SINGLE + adjusted_gross_income: 60_000 + households: + household: + members: [person1] + state_code: TX + output: + overtime_income_deduction: 0 + - name: Ineligible cardholder present period: 2025 input: diff --git a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/tip_and_overtime_income_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/tip_and_overtime_income_deduction.yaml index e9fef1e79cd..200d47d5e51 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/tip_and_overtime_income_deduction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/tip_and_overtime_income_deduction.yaml @@ -396,3 +396,142 @@ output: taxable_income_deductions_if_itemizing: 0 taxable_income_deductions_if_not_itemizing: 0 + +# Both deduction lists name the tips and overtime deductions in every year; the +# deductions' in_effect parameters (26 U.S.C. 224(h) and 225(g)) set the years. + +- name: 2028, both deductions reach the federal deduction lists. + period: 2028 + input: + people: + person1: + age: 40 + tip_income: 10_000 + treasury_tipped_occupation_code: 102 + fsla_overtime_premium: 5_000 + tax_units: + tax_unit: + members: [person1] + filing_status: SINGLE + adjusted_gross_income: 60_000 + # Every other listed deduction is set to zero, so each list sums to + # the tips and overtime deductions alone. + standard_deduction: 0 + qualified_business_income_deduction: 0 + charitable_deduction_for_non_itemizers: 0 + itemized_taxable_income_deductions: 0 + wagering_losses_deduction: 0 + additional_senior_deduction: 0 + auto_loan_interest_deduction: 0 + households: + household: + members: [person1] + state_code: TX + output: + tip_income_deduction: 10_000 + overtime_income_deduction: 5_000 + taxable_income_deductions_if_not_itemizing: 15_000 + taxable_income_deductions_if_itemizing: 15_000 + +- name: 2029, neither deduction reaches the federal deduction lists. + period: 2029 + input: + people: + person1: + age: 40 + tip_income: 10_000 + treasury_tipped_occupation_code: 102 + fsla_overtime_premium: 5_000 + tax_units: + tax_unit: + members: [person1] + filing_status: SINGLE + adjusted_gross_income: 60_000 + # Every other listed deduction is set to zero, so each list sums to + # the tips and overtime deductions alone. + standard_deduction: 0 + qualified_business_income_deduction: 0 + charitable_deduction_for_non_itemizers: 0 + itemized_taxable_income_deductions: 0 + wagering_losses_deduction: 0 + additional_senior_deduction: 0 + auto_loan_interest_deduction: 0 + households: + household: + members: [person1] + state_code: TX + output: + tip_income_deduction: 0 + overtime_income_deduction: 0 + taxable_income_deductions_if_not_itemizing: 0 + taxable_income_deductions_if_itemizing: 0 + +- name: 2029, a reform that keeps both switches on restores the deductions in taxable income. + period: 2029 + input: + gov.irs.deductions.tip_income.in_effect: true + gov.irs.deductions.overtime_income.in_effect: true + people: + person1: + age: 40 + tip_income: 10_000 + treasury_tipped_occupation_code: 102 + fsla_overtime_premium: 5_000 + tax_units: + tax_unit: + members: [person1] + filing_status: SINGLE + adjusted_gross_income: 60_000 + # Every other listed deduction is set to zero, so each list sums to + # the tips and overtime deductions alone. + standard_deduction: 0 + qualified_business_income_deduction: 0 + charitable_deduction_for_non_itemizers: 0 + itemized_taxable_income_deductions: 0 + wagering_losses_deduction: 0 + additional_senior_deduction: 0 + auto_loan_interest_deduction: 0 + households: + household: + members: [person1] + state_code: TX + output: + tip_income_deduction: 10_000 + overtime_income_deduction: 5_000 + taxable_income_deductions_if_not_itemizing: 15_000 + taxable_income_deductions_if_itemizing: 15_000 + +- name: 2024, a reform that switches both deductions on applies them in taxable income. + period: 2024 + input: + gov.irs.deductions.tip_income.in_effect: true + gov.irs.deductions.overtime_income.in_effect: true + people: + person1: + age: 40 + tip_income: 10_000 + treasury_tipped_occupation_code: 102 + fsla_overtime_premium: 5_000 + tax_units: + tax_unit: + members: [person1] + filing_status: SINGLE + adjusted_gross_income: 60_000 + # Every other listed deduction is set to zero, so each list sums to + # the tips and overtime deductions alone. + standard_deduction: 0 + qualified_business_income_deduction: 0 + charitable_deduction_for_non_itemizers: 0 + itemized_taxable_income_deductions: 0 + wagering_losses_deduction: 0 + additional_senior_deduction: 0 + auto_loan_interest_deduction: 0 + households: + household: + members: [person1] + state_code: TX + output: + tip_income_deduction: 10_000 + overtime_income_deduction: 5_000 + taxable_income_deductions_if_not_itemizing: 15_000 + taxable_income_deductions_if_itemizing: 15_000 diff --git a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/tip_income/tip_income_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/tip_income/tip_income_deduction.yaml index ee772b95d6e..d785b4e9d66 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/tip_income/tip_income_deduction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/tip_income/tip_income_deduction.yaml @@ -1,3 +1,108 @@ +# 26 U.S.C. 224(h) allows no deduction for any taxable year beginning after +# December 31, 2028, and Pub. L. 119-21 sec. 70201(j) applies the section to +# taxable years beginning after December 31, 2024. The same single filer, below +# the $150,000 phase-out start, is computed in each year. + +- name: 2024, before the deduction takes effect, no deduction is allowed. + period: 2024 + input: + people: + person1: + age: 40 + tip_income: 10_000 + treasury_tipped_occupation_code: 102 + tax_units: + tax_unit: + members: [person1] + filing_status: SINGLE + adjusted_gross_income: 60_000 + households: + household: + members: [person1] + state_code: TX + output: + tip_income_deduction: 0 + +- name: 2025, the first year, the deduction is allowed. + period: 2025 + input: + people: + person1: + age: 40 + tip_income: 10_000 + treasury_tipped_occupation_code: 102 + tax_units: + tax_unit: + members: [person1] + filing_status: SINGLE + adjusted_gross_income: 60_000 + households: + household: + members: [person1] + state_code: TX + output: + tip_income_deduction: 10_000 + +- name: 2028, the last year, the deduction is allowed. + period: 2028 + input: + people: + person1: + age: 40 + tip_income: 10_000 + treasury_tipped_occupation_code: 102 + tax_units: + tax_unit: + members: [person1] + filing_status: SINGLE + adjusted_gross_income: 60_000 + households: + household: + members: [person1] + state_code: TX + output: + tip_income_deduction: 10_000 + +- name: 2029, after 224(h) ends it, no deduction is allowed. + period: 2029 + input: + people: + person1: + age: 40 + tip_income: 10_000 + treasury_tipped_occupation_code: 102 + tax_units: + tax_unit: + members: [person1] + filing_status: SINGLE + adjusted_gross_income: 60_000 + households: + household: + members: [person1] + state_code: TX + output: + tip_income_deduction: 0 + +- name: 2030, later years stay at zero. + period: 2030 + input: + people: + person1: + age: 40 + tip_income: 10_000 + treasury_tipped_occupation_code: 102 + tax_units: + tax_unit: + members: [person1] + filing_status: SINGLE + adjusted_gross_income: 60_000 + households: + household: + members: [person1] + state_code: TX + output: + tip_income_deduction: 0 + - name: Ineligible cardholder present period: 2025 input: diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/tax/income/az_subtractions.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/tax/income/az_subtractions.yaml new file mode 100644 index 00000000000..023996e74df --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/az/tax/income/az_subtractions.yaml @@ -0,0 +1,50 @@ +# A.R.S. 43-1022(31) and (32) subtract the qualified tips and overtime +# compensation "deducted under section 224" and "section 225" of the Internal +# Revenue Code. Sections 224(h) and 225(g) allow no deduction for any taxable +# year beginning after December 31, 2028, so nothing is subtracted from 2029. + +- name: 2028 Arizona subtracts the computed federal tips and overtime deductions. + period: 2028 + input: + people: + person1: + age: 40 + tip_income: 10_000 + treasury_tipped_occupation_code: 102 + fsla_overtime_premium: 5_000 + tax_units: + tax_unit: + members: [person1] + filing_status: SINGLE + adjusted_gross_income: 60_000 + households: + household: + members: [person1] + state_code: AZ + output: + tip_income_deduction: 10_000 + overtime_income_deduction: 5_000 + az_subtractions: 15_000 + +- name: 2029 Arizona subtracts nothing after the federal deductions end. + period: 2029 + input: + people: + person1: + age: 40 + tip_income: 10_000 + treasury_tipped_occupation_code: 102 + fsla_overtime_premium: 5_000 + tax_units: + tax_unit: + members: [person1] + filing_status: SINGLE + adjusted_gross_income: 60_000 + households: + household: + members: [person1] + state_code: AZ + output: + tip_income_deduction: 0 + overtime_income_deduction: 0 + az_subtractions: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/co/tax/income/additions/co_overtime_income_addback.yaml b/policyengine_us/tests/policy/baseline/gov/states/co/tax/income/additions/co_overtime_income_addback.yaml index bdeb383abc3..9d26ae4cc01 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/co/tax/income/additions/co_overtime_income_addback.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/co/tax/income/additions/co_overtime_income_addback.yaml @@ -66,16 +66,29 @@ co_overtime_income_addback: 0 co_additions: 0 -- name: Case 7, 2029 the add-back ends when the federal overtime deduction sunsets. +- name: Case 7, 2029 the add-back ends when the federal overtime deduction ends. period: 2029 absolute_error_margin: 0.01 input: - overtime_income_deduction: 3_000 - state_code: CO + people: + person1: + age: 40 + fsla_overtime_premium: 3_000 + tax_units: + tax_unit: + members: [person1] + filing_status: SINGLE + adjusted_gross_income: 60_000 + households: + household: + members: [person1] + state_code: CO output: - # The federal IRC 225 deduction is dropped from the federal deduction lists - # at 2029, so Colorado must not add it back; in_effect is false from 2029. + # 26 U.S.C. 225(g) allows no federal deduction after 2028, so Colorado + # has nothing to add back. + overtime_income_deduction: 0 co_overtime_income_addback: 0 + co_additions: 0 - name: Case 8, 2026 the add-back mirrors the capped federal deduction (joint filer above the cap). period: 2026 @@ -129,3 +142,37 @@ # 7,500 -- which the Colorado add-back mirrors. overtime_income_deduction: 7_500 co_overtime_income_addback: 7_500 + +- name: Case 10, 2028 the add-back mirrors the computed federal overtime deduction. + period: 2028 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + fsla_overtime_premium: 3_000 + tax_units: + tax_unit: + members: [person1] + filing_status: SINGLE + adjusted_gross_income: 60_000 + households: + household: + members: [person1] + state_code: CO + output: + overtime_income_deduction: 3_000 + co_overtime_income_addback: 3_000 + co_additions: 3_000 + +- name: Case 11, 2029 Colorado still adds back any federal overtime deduction taken. + period: 2029 + absolute_error_margin: 0.01 + input: + # Entered directly, as under a reform that extends section 225 past 2028. + overtime_income_deduction: 3_000 + state_code: CO + output: + # C.R.S. 39-22-104(3)(u) adds back "the amount of any overtime compensation + # excluded or deducted from federal gross income", with no end date. + co_overtime_income_addback: 3_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/id/tax/income/deductions/id_qualified_business_income_and_federal_schedule_1a_deductions.yaml b/policyengine_us/tests/policy/baseline/gov/states/id/tax/income/deductions/id_qualified_business_income_and_federal_schedule_1a_deductions.yaml index d5f535d96d8..cbdf295acfa 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/id/tax/income/deductions/id_qualified_business_income_and_federal_schedule_1a_deductions.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/id/tax/income/deductions/id_qualified_business_income_and_federal_schedule_1a_deductions.yaml @@ -31,8 +31,32 @@ state_code: ID qualified_passenger_vehicle_loan_interest: 10_000 output: - tip_income_deduction: 10_000 - overtime_income_deduction: 10_000 + # 26 U.S.C. 224(h) and 225(g) allow no tips or overtime deduction after 2028. + tip_income_deduction: 0 + overtime_income_deduction: 0 additional_senior_deduction: 3_300 auto_loan_interest_deduction: 6_000 id_qualified_business_income_and_federal_schedule_1a_deductions: 0 + +- name: Idaho includes the computed federal tips and overtime deductions in 2028. + period: 2028 + input: + people: + person1: + age: 40 + tip_income: 10_000 + treasury_tipped_occupation_code: 102 + fsla_overtime_premium: 5_000 + tax_units: + tax_unit: + members: [person1] + filing_status: SINGLE + adjusted_gross_income: 60_000 + households: + household: + members: [person1] + state_code: ID + output: + tip_income_deduction: 10_000 + overtime_income_deduction: 5_000 + id_qualified_business_income_and_federal_schedule_1a_deductions: 15_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/deductions/qualified_tips_and_overtime/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/deductions/qualified_tips_and_overtime/integration.yaml index 20835913c55..2a8baa3b937 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/deductions/qualified_tips_and_overtime/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/deductions/qualified_tips_and_overtime/integration.yaml @@ -46,3 +46,55 @@ # AGI 50,000 - 5,000 tips/overtime subtraction - 0 exemptions = 45,000. mi_subtractions: 5_000 mi_taxable_income: 45_000 + +- name: 2028 the computed federal deductions flow into Michigan subtractions + period: 2028 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + tip_income: 2_000 + treasury_tipped_occupation_code: 102 + fsla_overtime_premium: 3_000 + tax_units: + tax_unit: + members: [person1] + filing_status: SINGLE + adjusted_gross_income: 60_000 + households: + household: + members: [person1] + state_code: MI + output: + tip_income_deduction: 2_000 + overtime_income_deduction: 3_000 + mi_qualified_tips_deduction: 2_000 + mi_qualified_overtime_deduction: 3_000 + +- name: 2029 no federal deduction and no Michigan deduction + period: 2029 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + tip_income: 2_000 + treasury_tipped_occupation_code: 102 + fsla_overtime_premium: 3_000 + tax_units: + tax_unit: + members: [person1] + filing_status: SINGLE + adjusted_gross_income: 60_000 + households: + household: + members: [person1] + state_code: MI + output: + # MCL 206.30(1)(ee) ends after 2028 and reaches only deductions allowed on + # the federal return; 26 U.S.C. 224(h) and 225(g) allow none after 2028. + tip_income_deduction: 0 + overtime_income_deduction: 0 + mi_qualified_tips_deduction: 0 + mi_qualified_overtime_deduction: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/subtractions/mt_federal_obbba_subtraction.yaml b/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/subtractions/mt_federal_obbba_subtraction.yaml index 0824cf0bbb2..f2b6df93e14 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/subtractions/mt_federal_obbba_subtraction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/subtractions/mt_federal_obbba_subtraction.yaml @@ -158,3 +158,49 @@ additional_senior_deduction: 0 tip_income_deduction: 8_000 mt_federal_obbba_subtraction: 8_000 + +- name: 2028 tipped worker — the federal tips and overtime deductions flow through + period: 2028 + input: + people: + head: + age: 30 + employment_income: 30_000 + tip_income: 8_000 + treasury_tipped_occupation_code: 102 + fsla_overtime_premium: 2_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_name: MT + output: + tip_income_deduction: 8_000 + overtime_income_deduction: 2_000 + mt_federal_obbba_subtraction: 10_000 + +- name: 2029 tipped worker — nothing flows through after the federal deductions end + # Montana taxable income starts from federal taxable income (15-30-2101(22)), + # and 26 U.S.C. 224(h) and 225(g) allow no deduction after 2028. + period: 2029 + input: + people: + head: + age: 30 + employment_income: 30_000 + tip_income: 8_000 + treasury_tipped_occupation_code: 102 + fsla_overtime_premium: 2_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_name: MT + output: + tip_income_deduction: 0 + overtime_income_deduction: 0 + mt_federal_obbba_subtraction: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/sc_obbba_addback.yaml b/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/sc_obbba_addback.yaml index 8d4d10a7280..fe844eca782 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/sc_obbba_addback.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/sc_obbba_addback.yaml @@ -66,3 +66,55 @@ output: # Only the $1,500 standard deduction increase remains. sc_obbba_addback: 1_500 + +- name: 2028 itemizer — the computed federal tips and overtime deductions are added back + period: 2028 + input: + people: + person1: + age: 40 + tip_income: 10_000 + treasury_tipped_occupation_code: 102 + fsla_overtime_premium: 5_000 + tax_units: + tax_unit: + members: [person1] + filing_status: SINGLE + adjusted_gross_income: 60_000 + # Itemizing removes the standard deduction add-back, isolating + # the Schedule 1-A deductions. + tax_unit_itemizes: true + households: + household: + members: [person1] + state_code: SC + output: + tip_income_deduction: 10_000 + overtime_income_deduction: 5_000 + sc_obbba_addback: 15_000 + +- name: 2029 itemizer — no tips or overtime add-back after the federal deductions end + period: 2029 + input: + people: + person1: + age: 40 + tip_income: 10_000 + treasury_tipped_occupation_code: 102 + fsla_overtime_premium: 5_000 + tax_units: + tax_unit: + members: [person1] + filing_status: SINGLE + adjusted_gross_income: 60_000 + tax_unit_itemizes: true + households: + household: + members: [person1] + state_code: SC + output: + # 26 U.S.C. 224(h) and 225(g) allow no deduction after 2028, so there + # is nothing claimed on the federal return for South Carolina to add back. + tip_income_deduction: 0 + overtime_income_deduction: 0 + sc_obbba_addback: 0 diff --git a/policyengine_us/tests/policy/baseline/parameters/test_reform_backdating.py b/policyengine_us/tests/policy/baseline/parameters/test_reform_backdating.py index 42dee090530..d040329bfa0 100644 --- a/policyengine_us/tests/policy/baseline/parameters/test_reform_backdating.py +++ b/policyengine_us/tests/policy/baseline/parameters/test_reform_backdating.py @@ -52,6 +52,9 @@ RANGE_FILE = "gov/irs/deductions/tip_income/cap.yaml" RANGE_START_YEAR = 2025 RANGE_VALUE = 50_000 +# Section 224 applies only from 2025 (Pub. L. 119-21, sec. 70201(j)), so the +# deduction is switched on for the year before the cap's first dated value. +TIP_DEDUCTION_SWITCH = "gov.irs.deductions.tip_income.in_effect" # App-style reform starting before a breakdown parameter's first dated value. BEFORE_PARAMETER = "gov.irs.deductions.overtime_income.cap.SINGLE" @@ -291,10 +294,13 @@ def test_simulation_reform_leaves_earlier_years(): } }, } - baseline = Simulation(situation=situation) + before = RANGE_START_YEAR - 1 + tip_deduction_before = {TIP_DEDUCTION_SWITCH: {f"year:{before}:1": True}} + baseline = Simulation(situation=situation, reform=tip_deduction_before) reformed_simulation = Simulation( situation=situation, reform={ + **tip_deduction_before, RANGE_PARAMETER: {f"{RANGE_START_YEAR}-01-01.2100-12-31": RANGE_VALUE}, EARLY_SCALAR: {f"year:{EARLY_SCALAR_YEAR}:1": EARLY_SCALAR_VALUE}, }, @@ -303,7 +309,6 @@ def test_simulation_reform_leaves_earlier_years(): # The simulation's own tree, independent of how any formula gates by year. cap = reformed_simulation.tax_benefit_system.parameters.get_child(RANGE_PARAMETER) baseline_cap = system.parameters.get_child(RANGE_PARAMETER) - before = RANGE_START_YEAR - 1 assert cap(f"{before}-01-01") == baseline_cap(f"{before}-01-01") def tip_deduction(simulation, year): diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/deductions/overtime_income/overtime_income_deduction.py b/policyengine_us/variables/gov/irs/income/taxable_income/deductions/overtime_income/overtime_income_deduction.py index e998f477de4..6754964cc1a 100644 --- a/policyengine_us/variables/gov/irs/income/taxable_income/deductions/overtime_income/overtime_income_deduction.py +++ b/policyengine_us/variables/gov/irs/income/taxable_income/deductions/overtime_income/overtime_income_deduction.py @@ -14,6 +14,11 @@ class overtime_income_deduction(Variable): ) def formula(tax_unit, period, parameters): + # Section 225 applies to taxable years beginning after December 31, + # 2024, and 225(g) allows no deduction for any taxable year beginning + # after December 31, 2028. + if not parameters(period).gov.irs.deductions.overtime_income.in_effect: + return 0 person = tax_unit.members overtime_income = person("fsla_overtime_premium", period) # 26 U.S.C. 225(b)(2)(B): modified adjusted gross income adds back diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/deductions/tip_income/tip_income_deduction.py b/policyengine_us/variables/gov/irs/income/taxable_income/deductions/tip_income/tip_income_deduction.py index 7e61bb1a85a..5f052c4d763 100644 --- a/policyengine_us/variables/gov/irs/income/taxable_income/deductions/tip_income/tip_income_deduction.py +++ b/policyengine_us/variables/gov/irs/income/taxable_income/deductions/tip_income/tip_income_deduction.py @@ -14,6 +14,11 @@ class tip_income_deduction(Variable): ) def formula(tax_unit, period, parameters): + # Section 224 applies to taxable years beginning after December 31, + # 2024, and 224(h) allows no deduction for any taxable year beginning + # after December 31, 2028. + if not parameters(period).gov.irs.deductions.tip_income.in_effect: + return 0 person = tax_unit.members tip_income = person("tip_income", period) occupation_requirement_met = person(