From 7ba9a34a34ade1e717fb8024b3c8144a449ecd23 Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Thu, 2 Apr 2026 15:51:01 -0400 Subject: [PATCH 1/5] Initial commit for SC H4216 baseline implementation Moving SC H4216 income tax reform from contributed reform to baseline. This reform takes effect for tax years beginning after 2025. From 160955e4dba47fe4a1590be8fe7637a429e691c0 Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Thu, 2 Apr 2026 16:04:58 -0400 Subject: [PATCH 2/5] Move SC H4216 income tax reform to baseline (2026+) This moves the South Carolina H.4216 income tax reform from contributed reform status to baseline, effective for tax years beginning after 2025. Key changes: - New tax brackets: 1.99% for income up to $30k, 5.21% above - SC Income Adjusted Deduction (SCIAD) replaces federal std/itemized deductions - SCIAD phases out based on AGI by filing status - SC EITC capped at $200 Closes #7916 Co-Authored-By: Claude Opus 4.5 --- changelog.d/sc-h4216-baseline.added.md | 1 + .../sc/tax/income/credits/eitc/max.yaml | 13 + .../tax/income/deductions/sciad/amount.yaml | 31 ++ .../deductions/sciad/phase_out/start.yaml | 31 ++ .../deductions/sciad/phase_out/width.yaml | 32 ++ .../gov/states/sc/tax/income/rates.yaml | 9 + .../tax/income/subtractions/subtractions.yaml | 28 +- .../sc/tax/income/sc_h4216_baseline.yaml | 326 ++++++++++++++++++ .../sc/tax/income/credits/eitc/sc_eitc.py | 11 +- .../tax/income/deductions/sciad/sc_sciad.py | 33 ++ .../states/sc/tax/income/sc_taxable_income.py | 24 +- 11 files changed, 526 insertions(+), 13 deletions(-) create mode 100644 changelog.d/sc-h4216-baseline.added.md create mode 100644 policyengine_us/parameters/gov/states/sc/tax/income/credits/eitc/max.yaml create mode 100644 policyengine_us/parameters/gov/states/sc/tax/income/deductions/sciad/amount.yaml create mode 100644 policyengine_us/parameters/gov/states/sc/tax/income/deductions/sciad/phase_out/start.yaml create mode 100644 policyengine_us/parameters/gov/states/sc/tax/income/deductions/sciad/phase_out/width.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/sc_h4216_baseline.yaml create mode 100644 policyengine_us/variables/gov/states/sc/tax/income/deductions/sciad/sc_sciad.py diff --git a/changelog.d/sc-h4216-baseline.added.md b/changelog.d/sc-h4216-baseline.added.md new file mode 100644 index 00000000000..420e050bcf4 --- /dev/null +++ b/changelog.d/sc-h4216-baseline.added.md @@ -0,0 +1 @@ +Move SC H4216 income tax reform from contributed reform to baseline, effective for tax years 2026 and later. This implements new tax rates (1.99%/5.21%), the SCIAD deduction with phaseout, and the 00 EITC cap. diff --git a/policyengine_us/parameters/gov/states/sc/tax/income/credits/eitc/max.yaml b/policyengine_us/parameters/gov/states/sc/tax/income/credits/eitc/max.yaml new file mode 100644 index 00000000000..7e10dc655b3 --- /dev/null +++ b/policyengine_us/parameters/gov/states/sc/tax/income/credits/eitc/max.yaml @@ -0,0 +1,13 @@ +description: South Carolina caps the state EITC at this amount. + +metadata: + unit: currency-USD + period: year + label: South Carolina EITC maximum cap + reference: + - title: SC H.4216 Section 7 - Section 12-6-3632 + href: https://www.scstatehouse.gov/sess126_2025-2026/bills/4216.htm + +values: + 0001-01-01: .inf + 2026-01-01: 200 diff --git a/policyengine_us/parameters/gov/states/sc/tax/income/deductions/sciad/amount.yaml b/policyengine_us/parameters/gov/states/sc/tax/income/deductions/sciad/amount.yaml new file mode 100644 index 00000000000..99d1c65d479 --- /dev/null +++ b/policyengine_us/parameters/gov/states/sc/tax/income/deductions/sciad/amount.yaml @@ -0,0 +1,31 @@ +description: South Carolina provides the SC Income Adjusted Deduction (SCIAD) at these base amounts by filing status. + +metadata: + label: South Carolina SCIAD base amount + period: year + unit: currency-USD + breakdown: + - filing_status + reference: + - title: SC H.4216 Section 3 - Section 12-6-1140(15)(a) + href: https://www.scstatehouse.gov/sess126_2025-2026/bills/4216.htm + +SINGLE: + 0001-01-01: 0 + 2026-01-01: 15_000 + +SEPARATE: + 0001-01-01: 0 + 2026-01-01: 15_000 + +HEAD_OF_HOUSEHOLD: + 0001-01-01: 0 + 2026-01-01: 22_500 + +JOINT: + 0001-01-01: 0 + 2026-01-01: 30_000 + +SURVIVING_SPOUSE: + 0001-01-01: 0 + 2026-01-01: 30_000 diff --git a/policyengine_us/parameters/gov/states/sc/tax/income/deductions/sciad/phase_out/start.yaml b/policyengine_us/parameters/gov/states/sc/tax/income/deductions/sciad/phase_out/start.yaml new file mode 100644 index 00000000000..c879ba4e172 --- /dev/null +++ b/policyengine_us/parameters/gov/states/sc/tax/income/deductions/sciad/phase_out/start.yaml @@ -0,0 +1,31 @@ +description: South Carolina begins phasing out the SCIAD when federal AGI exceeds this threshold. + +metadata: + label: South Carolina SCIAD phase-out start + period: year + unit: currency-USD + breakdown: + - filing_status + reference: + - title: SC H.4216 Section 3 - Section 12-6-1140(15)(b) + href: https://www.scstatehouse.gov/sess126_2025-2026/bills/4216.htm + +SINGLE: + 0001-01-01: 0 + 2026-01-01: 40_000 + +SEPARATE: + 0001-01-01: 0 + 2026-01-01: 40_000 + +HEAD_OF_HOUSEHOLD: + 0001-01-01: 0 + 2026-01-01: 60_000 + +JOINT: + 0001-01-01: 0 + 2026-01-01: 80_000 + +SURVIVING_SPOUSE: + 0001-01-01: 0 + 2026-01-01: 80_000 diff --git a/policyengine_us/parameters/gov/states/sc/tax/income/deductions/sciad/phase_out/width.yaml b/policyengine_us/parameters/gov/states/sc/tax/income/deductions/sciad/phase_out/width.yaml new file mode 100644 index 00000000000..f6e94b4c48a --- /dev/null +++ b/policyengine_us/parameters/gov/states/sc/tax/income/deductions/sciad/phase_out/width.yaml @@ -0,0 +1,32 @@ +description: South Carolina fully phases out the SCIAD over this income range (the denominator in the phase-out formula). + +metadata: + label: South Carolina SCIAD phase-out width + period: year + unit: currency-USD + breakdown: + - filing_status + reference: + - title: SC H.4216 Section 3 - Section 12-6-1140(15)(b) + href: https://www.scstatehouse.gov/sess126_2025-2026/bills/4216.htm + +# Use 1 as default to avoid division by zero (amount is 0 before 2026 anyway) +SINGLE: + 0001-01-01: 1 + 2026-01-01: 55_000 + +SEPARATE: + 0001-01-01: 1 + 2026-01-01: 55_000 + +HEAD_OF_HOUSEHOLD: + 0001-01-01: 1 + 2026-01-01: 82_500 + +JOINT: + 0001-01-01: 1 + 2026-01-01: 110_000 + +SURVIVING_SPOUSE: + 0001-01-01: 1 + 2026-01-01: 110_000 diff --git a/policyengine_us/parameters/gov/states/sc/tax/income/rates.yaml b/policyengine_us/parameters/gov/states/sc/tax/income/rates.yaml index b96a0427263..916aee34dc3 100644 --- a/policyengine_us/parameters/gov/states/sc/tax/income/rates.yaml +++ b/policyengine_us/parameters/gov/states/sc/tax/income/rates.yaml @@ -1,10 +1,14 @@ description: South Carolina taxes the income of individuals at this rate. +# Note: H.4216 Items 2-3 also include automatic rate reductions beginning +# in 2027 if individual income tax revenues project to grow at least 5% +# year-over-year. These future rate reductions are not modeled. brackets: - threshold: 1994-01-01: 0 rate: 1994-01-01: 0.025 2021-01-01: 0 + 2026-01-01: 0.0199 - threshold: 1994-01-01: 2_220 2021-01-01: 3_110 @@ -12,6 +16,7 @@ brackets: 2023-01-01: 3_330 2024-01-01: 3_460 2025-01-01: 3_560 + 2026-01-01: 30_000 metadata: uprating: gov.irs.uprating rounding: @@ -19,6 +24,7 @@ brackets: interval: 10 rate: 1994-01-01: 0.03 + 2026-01-01: 0.0521 - threshold: 1994-01-01: 4_440 2021-01-01: 6_220 @@ -26,6 +32,7 @@ brackets: 2023-01-01: 16_680 2024-01-01: 17_330 2025-01-01: 17_830 + 2026-01-01: .inf metadata: uprating: gov.irs.uprating rounding: @@ -63,6 +70,8 @@ brackets: metadata: reference: + - title: SC H.4216 Section 1 - Section 12-6-510(C) (2026+) + href: https://www.scstatehouse.gov/sess126_2025-2026/bills/4216.htm - title: SC 1040 Instructions 2025 href: https://dor.sc.gov/sites/dor/files/forms/SC1040Instr_2025.pdf#page=1 - title: SC 2024 Income Tax Chart 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 45e7c22ec84..6cafb26addd 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 @@ -4,28 +4,44 @@ values: - salt_refund_income - disability_benefits #include tax disability benefits - sc_net_capital_gain_deduction - - investment_in_529_plan + - investment_in_529_plan - us_govt_interest - taxable_social_security - sc_military_deduction - sc_retirement_deduction - sc_senior_exemption - sc_young_child_deduction - - sc_dependent_exemption + - sc_dependent_exemption # - Out of state income/gain - # - Volunteer deductions + # - Volunteer deductions # - Active Trade or Business Income deduction # - Certain nontaxable National Guard or Reserve pay # - Negative amount of federal taxable income - # - Subsistence allowance + # - Subsistence allowance # - Consumer Protection Services - # - Other subtractions + # - Other subtractions + # H.4216 adds the SCIAD starting 2026 + 2026-01-01: + - salt_refund_income + - disability_benefits + - sc_net_capital_gain_deduction + - investment_in_529_plan + - us_govt_interest + - taxable_social_security + - sc_military_deduction + - sc_retirement_deduction + - sc_senior_exemption + - sc_young_child_deduction + - sc_dependent_exemption + - sc_sciad metadata: unit: list period: year - label: South Carolina federal taxable income subtractions + label: South Carolina federal taxable income subtractions reference: + - title: SC H.4216 Section 3 - Section 12-6-1140(15) (2026+) + href: https://www.scstatehouse.gov/sess126_2025-2026/bills/4216.htm - title: SC 1040 tax form instructions (2025) href: https://dor.sc.gov/sites/dor/files/forms/SC1040Instr_2025.pdf#page=8 - title: SC 1040 tax form (2021) diff --git a/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/sc_h4216_baseline.yaml b/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/sc_h4216_baseline.yaml new file mode 100644 index 00000000000..e8c839c01c0 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/sc_h4216_baseline.yaml @@ -0,0 +1,326 @@ +# SC H.4216 Baseline Tests (2026+) +# Tests the comprehensive tax reform in baseline: +# 1. EITC cap at $200 +# 2. SC Income Adjusted Deduction (SCIAD) +# 3. New tax rates (1.99% / 5.21%) +# Reference: https://www.scstatehouse.gov/sess126_2025-2026/bills/4216.htm + +# EITC Cap Tests + +- name: SC EITC capped at $200 for high federal EITC (2026) + period: 2026 + absolute_error_margin: 0.1 + input: + people: + person1: + age: 30 + is_tax_unit_head: true + tax_units: + tax_unit: + members: [person1] + eitc: 500 + households: + household: + members: [person1] + state_code: SC + output: + # Federal EITC = $500 + # SC EITC = min($500 * 1.25, $200) = min($625, $200) = $200 + sc_eitc: 200 + +- name: SC EITC below cap (2026) + period: 2026 + absolute_error_margin: 0.1 + input: + people: + person1: + age: 30 + is_tax_unit_head: true + tax_units: + tax_unit: + members: [person1] + eitc: 100 + households: + household: + members: [person1] + state_code: SC + output: + # Federal EITC = $100 + # SC EITC = min($100 * 1.25, $200) = min($125, $200) = $125 + sc_eitc: 125 + +- name: SC EITC has no cap before 2026 + period: 2025 + absolute_error_margin: 0.1 + input: + people: + person1: + age: 30 + is_tax_unit_head: true + tax_units: + tax_unit: + members: [person1] + eitc: 500 + households: + household: + members: [person1] + state_code: SC + output: + # Federal EITC = $500 + # SC EITC = $500 * 1.25 = $625 (no cap in 2025) + sc_eitc: 625 + +# SCIAD Tests + +- name: SCIAD for single filer below phase-out (2026) + period: 2026 + absolute_error_margin: 0.1 + input: + people: + person1: + age: 30 + is_tax_unit_head: true + employment_income: 35_000 + tax_units: + tax_unit: + members: [person1] + households: + household: + members: [person1] + state_code: SC + output: + # AGI $35k < phase-out start $40k, full SCIAD + sc_sciad: 15_000 + +- name: SCIAD for single filer in phase-out range (2026) + period: 2026 + absolute_error_margin: 0.1 + input: + people: + person1: + age: 30 + is_tax_unit_head: true + employment_income: 67_500 + tax_units: + tax_unit: + members: [person1] + households: + household: + members: [person1] + state_code: SC + output: + # AGI $67,500, phase-out start $40k, width $55k + # Excess = $67,500 - $40,000 = $27,500 + # Phase-out fraction = $27,500 / $55,000 = 0.5 + # Reduction = $15,000 * 0.5 = $7,500 + # SCIAD = $15,000 - $7,500 = $7,500 + sc_sciad: 7_500 + +- name: SCIAD fully phased out for high income single (2026) + period: 2026 + absolute_error_margin: 0.1 + input: + people: + person1: + age: 30 + is_tax_unit_head: true + employment_income: 100_000 + tax_units: + tax_unit: + members: [person1] + households: + household: + members: [person1] + state_code: SC + output: + # AGI $100k > phase-out end $95k ($40k + $55k) + # SCIAD fully phased out + sc_sciad: 0 + +- name: SCIAD for joint filer below phase-out (2026) + period: 2026 + absolute_error_margin: 0.1 + input: + people: + person1: + age: 35 + is_tax_unit_head: true + employment_income: 40_000 + person2: + age: 33 + is_tax_unit_spouse: true + employment_income: 35_000 + tax_units: + tax_unit: + members: [person1, person2] + filing_status: JOINT + households: + household: + members: [person1, person2] + state_code: SC + output: + # AGI $75k < phase-out start $80k, full SCIAD + sc_sciad: 30_000 + +- name: SCIAD for head of household (2026) + period: 2026 + absolute_error_margin: 0.1 + input: + people: + person1: + age: 35 + is_tax_unit_head: true + employment_income: 55_000 + person2: + age: 10 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [person1, person2] + filing_status: HEAD_OF_HOUSEHOLD + households: + household: + members: [person1, person2] + state_code: SC + output: + # AGI $55k < phase-out start $60k, full SCIAD + sc_sciad: 22_500 + +- name: SCIAD is zero before 2026 + period: 2025 + absolute_error_margin: 0.1 + input: + people: + person1: + age: 30 + is_tax_unit_head: true + employment_income: 35_000 + tax_units: + tax_unit: + members: [person1] + households: + household: + members: [person1] + state_code: SC + output: + # SCIAD doesn't exist before 2026 + sc_sciad: 0 + +# Tax Rate Tests + +- name: Tax at 1.99% rate for income under $30k (2026) + period: 2026 + absolute_error_margin: 0.1 + input: + people: + person1: + age: 30 + is_tax_unit_head: true + tax_units: + tax_unit: + members: [person1] + adjusted_gross_income: 20_000 + households: + household: + members: [person1] + state_code: SC + output: + # AGI $20k < SCIAD $15k phase-out, so SCIAD = $15k (below $40k AGI) + # Actually AGI $20k means full SCIAD of $15k + # SC taxable income = $20k - $15k = $5k + # Tax = $5k * 1.99% = $99.50 + sc_income_tax_before_non_refundable_credits: 99.5 + +- name: Tax calculation with new rates above $30k threshold (2026) + period: 2026 + absolute_error_margin: 1 + input: + people: + person1: + age: 30 + is_tax_unit_head: true + employment_income: 100_000 + tax_units: + tax_unit: + members: [person1] + households: + household: + members: [person1] + state_code: SC + output: + # AGI $100k, SCIAD $0 (fully phased out) + # Taxable income = $100k + # Tax = $30k * 1.99% + $70k * 5.21% = $597 + $3,647 = $4,244 + sc_income_tax_before_non_refundable_credits: 4_244 + +# Taxable Income Tests + +- name: SC taxable income starts from AGI in 2026 + period: 2026 + absolute_error_margin: 0.1 + input: + people: + person1: + age: 30 + is_tax_unit_head: true + employment_income: 50_000 + tax_units: + tax_unit: + members: [person1] + households: + household: + members: [person1] + state_code: SC + output: + # AGI $50k, SCIAD phased out partially + # Excess = $50k - $40k = $10k + # Phase-out fraction = $10k / $55k = 0.18182 + # Reduction = $15,000 * 0.18182 = $2,727.27, rounded to $2,720 + # SCIAD = $15,000 - $2,720 = $12,280 + # But wait - formula is: reduction = base * fraction, then round DOWN to $10 + # reduction = $15,000 * 0.18182 = $2,727.27 -> floor($2,727.27/10)*10 = $2,720 + # SCIAD = max(0, $15,000 - $2,720) = $12,280 + sc_sciad: 12_280 + +- name: Zero income results in zero tax (2026) + period: 2026 + absolute_error_margin: 0.1 + input: + people: + person1: + age: 30 + is_tax_unit_head: true + employment_income: 0 + tax_units: + tax_unit: + members: [person1] + households: + household: + members: [person1] + state_code: SC + output: + sc_sciad: 15_000 + sc_taxable_income: 0 + sc_income_tax_before_non_refundable_credits: 0 + +- name: Non-SC resident gets zero for SC variables + period: 2026 + absolute_error_margin: 0.1 + input: + people: + person1: + age: 30 + is_tax_unit_head: true + employment_income: 50_000 + tax_units: + tax_unit: + members: [person1] + households: + household: + members: [person1] + state_code: NY + output: + sc_sciad: 0 + sc_eitc: 0 + sc_taxable_income: 0 + sc_income_tax_before_non_refundable_credits: 0 diff --git a/policyengine_us/variables/gov/states/sc/tax/income/credits/eitc/sc_eitc.py b/policyengine_us/variables/gov/states/sc/tax/income/credits/eitc/sc_eitc.py index 8e5ea5d3b07..4d5bd51f565 100644 --- a/policyengine_us/variables/gov/states/sc/tax/income/credits/eitc/sc_eitc.py +++ b/policyengine_us/variables/gov/states/sc/tax/income/credits/eitc/sc_eitc.py @@ -7,10 +7,15 @@ class sc_eitc(Variable): label = "South Carolina EITC" unit = USD definition_period = YEAR - reference = "https://dor.sc.gov/forms-site/Forms/TC60_2021.pdf" + reference = ( + "https://dor.sc.gov/forms-site/Forms/TC60_2021.pdf", + "https://www.scstatehouse.gov/sess126_2025-2026/bills/4216.htm", + ) defined_for = StateCode.SC def formula(tax_unit, period, parameters): federal_eitc = tax_unit("eitc", period) - rate = parameters(period).gov.states.sc.tax.income.credits.eitc.rate - return np.round(federal_eitc * rate, 1) + p = parameters(period).gov.states.sc.tax.income.credits.eitc + uncapped = np.round(federal_eitc * p.rate, 1) + # Apply cap (infinite before 2026, $200 from 2026+) + return min_(uncapped, p.max) diff --git a/policyengine_us/variables/gov/states/sc/tax/income/deductions/sciad/sc_sciad.py b/policyengine_us/variables/gov/states/sc/tax/income/deductions/sciad/sc_sciad.py new file mode 100644 index 00000000000..9c0c659883a --- /dev/null +++ b/policyengine_us/variables/gov/states/sc/tax/income/deductions/sciad/sc_sciad.py @@ -0,0 +1,33 @@ +from policyengine_us.model_api import * + + +class sc_sciad(Variable): + value_type = float + entity = TaxUnit + label = "South Carolina Income Adjusted Deduction (SCIAD)" + unit = USD + definition_period = YEAR + reference = ("https://www.scstatehouse.gov/sess126_2025-2026/bills/4216.htm",) + defined_for = StateCode.SC + + def formula(tax_unit, period, parameters): + p = parameters(period).gov.states.sc.tax.income.deductions.sciad + filing_status = tax_unit("filing_status", period) + # Get the base amount by filing status + base_amount = p.amount[filing_status] + # Get federal AGI for phase-out calculation + agi = tax_unit("adjusted_gross_income", period) + # Get phase-out parameters by filing status + phase_out_start = p.phase_out.start[filing_status] + phase_out_width = p.phase_out.width[filing_status] + # Calculate phase-out fraction + # fraction = (AGI - start) / width + excess_agi = max_(0, agi - phase_out_start) + phase_out_fraction = min_(1, excess_agi / phase_out_width) + # Calculate reduction + reduction = base_amount * phase_out_fraction + # Round reduction down to nearest $10 + reduction_rounded = np.floor(reduction / 10) * 10 + # Calculate deduction + deduction = max_(0, base_amount - reduction_rounded) + return deduction diff --git a/policyengine_us/variables/gov/states/sc/tax/income/sc_taxable_income.py b/policyengine_us/variables/gov/states/sc/tax/income/sc_taxable_income.py index 433fc8d0d16..d0281787d5e 100644 --- a/policyengine_us/variables/gov/states/sc/tax/income/sc_taxable_income.py +++ b/policyengine_us/variables/gov/states/sc/tax/income/sc_taxable_income.py @@ -8,10 +8,26 @@ class sc_taxable_income(Variable): defined_for = StateCode.SC unit = USD definition_period = YEAR - reference = "https://dor.sc.gov/forms-site/Forms/IITPacket_2022.pdf#page=33" + reference = ( + "https://dor.sc.gov/forms-site/Forms/IITPacket_2022.pdf#page=33", + "https://www.scstatehouse.gov/sess126_2025-2026/bills/4216.htm", + ) def formula(tax_unit, period, parameters): - taxable_income = tax_unit("taxable_income", period) - additions = tax_unit("sc_additions", period) + # Check if we're in H4216 era (2026+) + # H4216 Section 2: SC does not adopt federal standard/itemized deductions + # H4216 Section 3: SC uses SCIAD instead, starting from AGI + year = period.start.year subtractions = tax_unit("sc_subtractions", period) - return max_(0, taxable_income + additions - subtractions) + + if year >= 2026: + # For 2026+: Start from AGI (SC ignores federal std/itemized deductions) + # Additions are not needed since they correct for federal deductions + # that are no longer relevant when starting from AGI + agi = tax_unit("adjusted_gross_income", period) + return max_(0, agi - subtractions) + else: + # Before 2026: Traditional calculation from federal taxable income + taxable_income = tax_unit("taxable_income", period) + additions = tax_unit("sc_additions", period) + return max_(0, taxable_income + additions - subtractions) From 50e37224d2ada66d95f25106fa918613be3b7eb7 Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Thu, 2 Apr 2026 16:09:35 -0400 Subject: [PATCH 3/5] Remove SC H.4216 contributed reform files The reform is now in baseline, so the contributed reform is no longer needed. Co-Authored-By: Claude Opus 4.5 --- .../gov/contrib/states/sc/h4216/eitc/max.yaml | 12 - .../contrib/states/sc/h4216/in_effect.yaml | 12 - .../gov/contrib/states/sc/h4216/rates.yaml | 24 - .../contrib/states/sc/h4216/sciad/amount.yaml | 26 - .../sc/h4216/sciad/phase_out/start.yaml | 26 - .../sc/h4216/sciad/phase_out/width.yaml | 26 - policyengine_us/reforms/reforms.py | 3 - .../reforms/states/sc/h4216/__init__.py | 1 - .../reforms/states/sc/h4216/sc_h4216.py | 137 --- .../contrib/states/sc/h4216/sc_h4216.yaml | 1020 ----------------- 10 files changed, 1287 deletions(-) delete mode 100644 policyengine_us/parameters/gov/contrib/states/sc/h4216/eitc/max.yaml delete mode 100644 policyengine_us/parameters/gov/contrib/states/sc/h4216/in_effect.yaml delete mode 100644 policyengine_us/parameters/gov/contrib/states/sc/h4216/rates.yaml delete mode 100644 policyengine_us/parameters/gov/contrib/states/sc/h4216/sciad/amount.yaml delete mode 100644 policyengine_us/parameters/gov/contrib/states/sc/h4216/sciad/phase_out/start.yaml delete mode 100644 policyengine_us/parameters/gov/contrib/states/sc/h4216/sciad/phase_out/width.yaml delete mode 100644 policyengine_us/reforms/states/sc/h4216/__init__.py delete mode 100644 policyengine_us/reforms/states/sc/h4216/sc_h4216.py delete mode 100644 policyengine_us/tests/policy/contrib/states/sc/h4216/sc_h4216.yaml diff --git a/policyengine_us/parameters/gov/contrib/states/sc/h4216/eitc/max.yaml b/policyengine_us/parameters/gov/contrib/states/sc/h4216/eitc/max.yaml deleted file mode 100644 index 09cdbfde892..00000000000 --- a/policyengine_us/parameters/gov/contrib/states/sc/h4216/eitc/max.yaml +++ /dev/null @@ -1,12 +0,0 @@ -description: South Carolina H.4216 caps the state EITC at this amount. - -metadata: - unit: currency-USD - period: year - label: South Carolina H.4216 EITC maximum cap - reference: - - title: South Carolina H.4216 Section 7 - Section 12-6-3632 - href: https://www.scstatehouse.gov/sess126_2025-2026/prever/4216_20260224.htm - -values: - 2026-01-01: 200 diff --git a/policyengine_us/parameters/gov/contrib/states/sc/h4216/in_effect.yaml b/policyengine_us/parameters/gov/contrib/states/sc/h4216/in_effect.yaml deleted file mode 100644 index e4965bb07e9..00000000000 --- a/policyengine_us/parameters/gov/contrib/states/sc/h4216/in_effect.yaml +++ /dev/null @@ -1,12 +0,0 @@ -description: The South Carolina H.4216 tax reform applies if this is true. - -metadata: - unit: bool - period: year - label: South Carolina H.4216 tax reform in effect - reference: - - title: South Carolina H.4216 (126th Session, 2025-2026) - href: https://www.scstatehouse.gov/sess126_2025-2026/prever/4216_20260224.htm - -values: - 0000-01-01: false diff --git a/policyengine_us/parameters/gov/contrib/states/sc/h4216/rates.yaml b/policyengine_us/parameters/gov/contrib/states/sc/h4216/rates.yaml deleted file mode 100644 index 31fbf8e8c8f..00000000000 --- a/policyengine_us/parameters/gov/contrib/states/sc/h4216/rates.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# Note: H.4216 Items 2-3 also include automatic rate reductions beginning -# in 2027 if individual income tax revenues project to grow at least 5% -# year-over-year. These future rate reductions are not modeled. -description: South Carolina H.4216 establishes new income tax rates. - -metadata: - reference: - - title: South Carolina H.4216 Section 1 - Section 12-6-510(C) - href: https://www.scstatehouse.gov/sess126_2025-2026/prever/4216_20260224.htm - label: South Carolina H.4216 income tax rates - type: marginal_rate - threshold_unit: currency-USD - rate_unit: /1 - period: year - -brackets: - - threshold: - 2026-01-01: 0 - rate: - 2026-01-01: 0.0199 - - threshold: - 2026-01-01: 30_000 - rate: - 2026-01-01: 0.0521 diff --git a/policyengine_us/parameters/gov/contrib/states/sc/h4216/sciad/amount.yaml b/policyengine_us/parameters/gov/contrib/states/sc/h4216/sciad/amount.yaml deleted file mode 100644 index a24c8cc57df..00000000000 --- a/policyengine_us/parameters/gov/contrib/states/sc/h4216/sciad/amount.yaml +++ /dev/null @@ -1,26 +0,0 @@ -description: South Carolina H.4216 provides the SC Income Adjusted Deduction (SCIAD) at these base amounts by filing status. - -metadata: - label: South Carolina H.4216 SCIAD base amount - period: year - unit: currency-USD - breakdown: - - filing_status - reference: - - title: South Carolina H.4216 Section 3 - Section 12-6-1140(15)(a) - href: https://www.scstatehouse.gov/sess126_2025-2026/prever/4216_20260224.htm - -SINGLE: - 2026-01-01: 15_000 - -SEPARATE: - 2026-01-01: 15_000 - -HEAD_OF_HOUSEHOLD: - 2026-01-01: 22_500 - -JOINT: - 2026-01-01: 30_000 - -SURVIVING_SPOUSE: - 2026-01-01: 30_000 diff --git a/policyengine_us/parameters/gov/contrib/states/sc/h4216/sciad/phase_out/start.yaml b/policyengine_us/parameters/gov/contrib/states/sc/h4216/sciad/phase_out/start.yaml deleted file mode 100644 index ceaa430ce56..00000000000 --- a/policyengine_us/parameters/gov/contrib/states/sc/h4216/sciad/phase_out/start.yaml +++ /dev/null @@ -1,26 +0,0 @@ -description: South Carolina H.4216 begins phasing out the SCIAD when AGI exceeds this threshold. - -metadata: - label: South Carolina H.4216 SCIAD phase-out start - period: year - unit: currency-USD - breakdown: - - filing_status - reference: - - title: South Carolina H.4216 Section 3 - Section 12-6-1140(15)(b) - href: https://www.scstatehouse.gov/sess126_2025-2026/prever/4216_20260224.htm - -SINGLE: - 2026-01-01: 40_000 - -SEPARATE: - 2026-01-01: 40_000 - -HEAD_OF_HOUSEHOLD: - 2026-01-01: 60_000 - -JOINT: - 2026-01-01: 80_000 - -SURVIVING_SPOUSE: - 2026-01-01: 80_000 diff --git a/policyengine_us/parameters/gov/contrib/states/sc/h4216/sciad/phase_out/width.yaml b/policyengine_us/parameters/gov/contrib/states/sc/h4216/sciad/phase_out/width.yaml deleted file mode 100644 index deae09c68c0..00000000000 --- a/policyengine_us/parameters/gov/contrib/states/sc/h4216/sciad/phase_out/width.yaml +++ /dev/null @@ -1,26 +0,0 @@ -description: South Carolina H.4216 fully phases out the SCIAD over this income range (the denominator in the phase-out formula). - -metadata: - label: South Carolina H.4216 SCIAD phase-out width - period: year - unit: currency-USD - breakdown: - - filing_status - reference: - - title: South Carolina H.4216 Section 3 - Section 12-6-1140(15)(b) - href: https://www.scstatehouse.gov/sess126_2025-2026/prever/4216_20260224.htm - -SINGLE: - 2026-01-01: 55_000 - -SEPARATE: - 2026-01-01: 55_000 - -HEAD_OF_HOUSEHOLD: - 2026-01-01: 82_500 - -JOINT: - 2026-01-01: 110_000 - -SURVIVING_SPOUSE: - 2026-01-01: 110_000 diff --git a/policyengine_us/reforms/reforms.py b/policyengine_us/reforms/reforms.py index f774002d35c..2c2b6d2029a 100644 --- a/policyengine_us/reforms/reforms.py +++ b/policyengine_us/reforms/reforms.py @@ -25,7 +25,6 @@ from .states.ny.a04038 import create_ny_a04038_enhanced_escc_infants_reform from .states.ny.s9110 import create_ny_s9110_reform from .states.sc.h3492 import create_sc_h3492_eitc_refundable_reform -from .states.sc.h4216 import create_sc_h4216_reform from .states.ny.a06774 import create_ny_a06774_enhanced_cdcc_reform from .states.ny.s04487 import create_ny_s04487_newborn_credit_reform from .harris.lift.middle_class_tax_credit import ( @@ -280,7 +279,6 @@ def create_structural_reforms_from_parameters(parameters, period): sc_h3492_eitc_refundable = create_sc_h3492_eitc_refundable_reform( parameters, period ) - sc_h4216 = create_sc_h4216_reform(parameters, period) ny_a06774_enhanced_cdcc = create_ny_a06774_enhanced_cdcc_reform(parameters, period) ny_s04487_newborn_credit = create_ny_s04487_newborn_credit_reform( parameters, period @@ -445,7 +443,6 @@ def create_structural_reforms_from_parameters(parameters, period): ny_a04038_enhanced_escc_infants, ny_s9110, sc_h3492_eitc_refundable, - sc_h4216, ny_a06774_enhanced_cdcc, ny_s04487_newborn_credit, middle_class_tax_credit, diff --git a/policyengine_us/reforms/states/sc/h4216/__init__.py b/policyengine_us/reforms/states/sc/h4216/__init__.py deleted file mode 100644 index 3dba992e8d2..00000000000 --- a/policyengine_us/reforms/states/sc/h4216/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .sc_h4216 import create_sc_h4216_reform, sc_h4216 diff --git a/policyengine_us/reforms/states/sc/h4216/sc_h4216.py b/policyengine_us/reforms/states/sc/h4216/sc_h4216.py deleted file mode 100644 index f65bfd1ebe3..00000000000 --- a/policyengine_us/reforms/states/sc/h4216/sc_h4216.py +++ /dev/null @@ -1,137 +0,0 @@ -from policyengine_us.model_api import * -from policyengine_core.periods import period as period_ - - -def create_sc_h4216() -> Reform: - """ - South Carolina H.4216 - Comprehensive Income Tax Reform - - This bill makes several changes to SC income tax: - 1. Caps the SC EITC at $200 (Section 7) - 2. Creates the SC Income Adjusted Deduction (SCIAD) replacing federal - standard deduction (Section 3) - 3. Establishes new tax rates: 1.99% up to $30k, 5.21% above (Section 1) - - Reference: https://www.scstatehouse.gov/sess126_2025-2026/prever/4216_20260224.htm - """ - - class sc_eitc(Variable): - value_type = float - entity = TaxUnit - label = "South Carolina EITC under H.4216" - unit = USD - definition_period = YEAR - reference = ( - "https://www.scstatehouse.gov/sess126_2025-2026/prever/4216_20260224.htm", - "https://dor.sc.gov/forms-site/Forms/TC60_2021.pdf", - ) - defined_for = StateCode.SC - - def formula(tax_unit, period, parameters): - federal_eitc = tax_unit("eitc", period) - p = parameters(period).gov.states.sc.tax.income.credits.eitc - p_h4216 = parameters(period).gov.contrib.states.sc.h4216.eitc - # Calculate 125% of federal EITC, then cap at $200 - uncapped = np.round(federal_eitc * p.rate, 1) - return min_(uncapped, p_h4216.max) - - class sc_sciad(Variable): - value_type = float - entity = TaxUnit - label = "South Carolina Income Adjusted Deduction (SCIAD)" - unit = USD - definition_period = YEAR - reference = ( - "https://www.scstatehouse.gov/sess126_2025-2026/prever/4216_20260224.htm" - ) - defined_for = StateCode.SC - - def formula(tax_unit, period, parameters): - p = parameters(period).gov.contrib.states.sc.h4216.sciad - filing_status = tax_unit("filing_status", period) - agi = tax_unit("adjusted_gross_income", period) - - # Get base amount and phase-out parameters by filing status - base_amount = p.amount[filing_status] - phase_out_start = p.phase_out.start[filing_status] - phase_out_width = p.phase_out.width[filing_status] - - # Calculate phase-out reduction - # Formula: deduction * (1 - (AGI - phase_out_start) / width) - excess_agi = max_(agi - phase_out_start, 0) - phase_out_fraction = min_(excess_agi / phase_out_width, 1) - raw_sciad = max_(base_amount * (1 - phase_out_fraction), 0) - # Per H.4216 Section 3, round down to nearest $10 - return np.floor(raw_sciad / 10) * 10 - - class sc_taxable_income(Variable): - value_type = float - entity = TaxUnit - label = "South Carolina taxable income under H.4216" - defined_for = StateCode.SC - unit = USD - definition_period = YEAR - reference = ( - "https://www.scstatehouse.gov/sess126_2025-2026/prever/4216_20260224.htm" - ) - - def formula(tax_unit, period, parameters): - # Start with federal AGI (H.4216 replaces federal std/itemized - # with SCIAD). Baseline sc_additions (QBI addback, SALT addback) - # exist to undo federal deductions already embedded in federal - # taxable_income; since we start from AGI those deductions were - # never taken, so additions must be excluded. - agi = tax_unit("adjusted_gross_income", period) - subtractions = tax_unit("sc_subtractions", period) - sciad = tax_unit("sc_sciad", period) - return max_(0, agi - subtractions - sciad) - - class sc_income_tax_before_non_refundable_credits(Variable): - value_type = float - entity = TaxUnit - label = "South Carolina income tax before non-refundable credits under H.4216" - defined_for = StateCode.SC - unit = USD - definition_period = YEAR - reference = ( - "https://www.scstatehouse.gov/sess126_2025-2026/prever/4216_20260224.htm" - ) - - def formula(tax_unit, period, parameters): - taxable_income = tax_unit("sc_taxable_income", period) - # Use H.4216 rates instead of baseline rates - p = parameters(period).gov.contrib.states.sc.h4216.rates - return p.calc(taxable_income) - - class reform(Reform): - def apply(self): - self.update_variable(sc_eitc) - self.update_variable(sc_sciad) - self.update_variable(sc_taxable_income) - self.update_variable(sc_income_tax_before_non_refundable_credits) - - return reform - - -def create_sc_h4216_reform(parameters, period, bypass: bool = False): - if bypass: - return create_sc_h4216() - - p = parameters.gov.contrib.states.sc.h4216 - - reform_active = False - current_period = period_(period) - - for _ in range(5): - if p(current_period).in_effect: - reform_active = True - break - current_period = current_period.offset(1, "year") - - if reform_active: - return create_sc_h4216() - else: - return None - - -sc_h4216 = create_sc_h4216_reform(None, None, bypass=True) diff --git a/policyengine_us/tests/policy/contrib/states/sc/h4216/sc_h4216.yaml b/policyengine_us/tests/policy/contrib/states/sc/h4216/sc_h4216.yaml deleted file mode 100644 index 46d544f0668..00000000000 --- a/policyengine_us/tests/policy/contrib/states/sc/h4216/sc_h4216.yaml +++ /dev/null @@ -1,1020 +0,0 @@ -# SC H.4216 Tax Reform Tests -# Tests the comprehensive tax reform including: -# 1. EITC cap at $200 -# 2. SC Income Adjusted Deduction (SCIAD) -# 3. New tax rates (1.99% / 5.21%) - -# EITC Cap Tests - -- name: Case 1, EITC capped at $200 for high federal EITC. - period: 2026 - absolute_error_margin: 0.1 - reforms: policyengine_us.reforms.states.sc.h4216.sc_h4216.sc_h4216 - input: - gov.contrib.states.sc.h4216.in_effect: true - people: - person1: - age: 30 - is_tax_unit_head: true - employment_income: 20_000 - person2: - age: 5 - is_tax_unit_dependent: true - tax_units: - tax_unit: - members: [person1, person2] - households: - household: - members: [person1, person2] - state_code: SC - output: - # Federal EITC at $20k with 1 child is substantial (~$3,733) - # SC EITC = min($3,733 * 1.25, $200) = min($4,666, $200) = $200 - sc_eitc: 200 - -- name: Case 2, EITC genuinely below cap with direct eitc input. - period: 2026 - absolute_error_margin: 0.1 - reforms: policyengine_us.reforms.states.sc.h4216.sc_h4216.sc_h4216 - input: - gov.contrib.states.sc.h4216.in_effect: true - people: - person1: - age: 30 - is_tax_unit_head: true - tax_units: - tax_unit: - members: [person1] - eitc: 100 - households: - household: - members: [person1] - state_code: SC - output: - # Federal EITC = $100 (set directly) - # SC EITC = min($100 * 1.25, $200) = min($125, $200) = $125 - sc_eitc: 125 - -- name: Case 3, zero federal EITC produces zero state EITC. - period: 2026 - absolute_error_margin: 0.1 - reforms: policyengine_us.reforms.states.sc.h4216.sc_h4216.sc_h4216 - input: - gov.contrib.states.sc.h4216.in_effect: true - people: - person1: - age: 30 - is_tax_unit_head: true - tax_units: - tax_unit: - members: [person1] - eitc: 0 - households: - household: - members: [person1] - state_code: SC - output: - # Federal EITC = $0 - # SC EITC = min($0 * 1.25, $200) = $0 - sc_eitc: 0 - -- name: Case 4, EITC exactly at cap boundary. - period: 2026 - absolute_error_margin: 0.1 - reforms: policyengine_us.reforms.states.sc.h4216.sc_h4216.sc_h4216 - input: - gov.contrib.states.sc.h4216.in_effect: true - people: - person1: - age: 30 - is_tax_unit_head: true - tax_units: - tax_unit: - members: [person1] - eitc: 160 - households: - household: - members: [person1] - state_code: SC - output: - # Federal EITC = $160 - # SC EITC = min($160 * 1.25, $200) = min($200, $200) = $200 - # Exactly at cap - sc_eitc: 200 - -# SCIAD Tests - -- name: Case 5, SCIAD for single filer below phase-out. - period: 2026 - absolute_error_margin: 0.1 - reforms: policyengine_us.reforms.states.sc.h4216.sc_h4216.sc_h4216 - input: - gov.contrib.states.sc.h4216.in_effect: true - people: - person1: - age: 30 - is_tax_unit_head: true - employment_income: 35_000 - tax_units: - tax_unit: - members: [person1] - households: - household: - members: [person1] - state_code: SC - output: - # AGI $35k < phase-out start $40k, full SCIAD - sc_sciad: 15_000 - -- name: Case 6, SCIAD for single filer in phase-out range. - period: 2026 - absolute_error_margin: 0.1 - reforms: policyengine_us.reforms.states.sc.h4216.sc_h4216.sc_h4216 - input: - gov.contrib.states.sc.h4216.in_effect: true - people: - person1: - age: 30 - is_tax_unit_head: true - employment_income: 67_500 - tax_units: - tax_unit: - members: [person1] - households: - household: - members: [person1] - state_code: SC - output: - # AGI $67,500, phase-out start $40k, width $55k - # Excess = $67,500 - $40,000 = $27,500 - # Phase-out fraction = $27,500 / $55,000 = 0.5 - # SCIAD = $15,000 * (1 - 0.5) = $7,500 - sc_sciad: 7_500 - -- name: Case 7, SCIAD fully phased out for high income single. - period: 2026 - absolute_error_margin: 0.1 - reforms: policyengine_us.reforms.states.sc.h4216.sc_h4216.sc_h4216 - input: - gov.contrib.states.sc.h4216.in_effect: true - people: - person1: - age: 30 - is_tax_unit_head: true - employment_income: 100_000 - tax_units: - tax_unit: - members: [person1] - households: - household: - members: [person1] - state_code: SC - output: - # AGI $100k > phase-out end $95k ($40k + $55k) - # SCIAD fully phased out - sc_sciad: 0 - -- name: Case 8, SCIAD for joint filer below phase-out. - period: 2026 - absolute_error_margin: 0.1 - reforms: policyengine_us.reforms.states.sc.h4216.sc_h4216.sc_h4216 - input: - gov.contrib.states.sc.h4216.in_effect: true - people: - person1: - age: 35 - is_tax_unit_head: true - employment_income: 40_000 - person2: - age: 33 - is_tax_unit_spouse: true - employment_income: 35_000 - tax_units: - tax_unit: - members: [person1, person2] - filing_status: JOINT - households: - household: - members: [person1, person2] - state_code: SC - output: - # AGI $75k < phase-out start $80k, full SCIAD - sc_sciad: 30_000 - -- name: Case 9, SCIAD for joint filer in phase-out range. - period: 2026 - absolute_error_margin: 0.1 - reforms: policyengine_us.reforms.states.sc.h4216.sc_h4216.sc_h4216 - input: - gov.contrib.states.sc.h4216.in_effect: true - people: - person1: - age: 35 - is_tax_unit_head: true - employment_income: 75_000 - person2: - age: 33 - is_tax_unit_spouse: true - employment_income: 60_000 - tax_units: - tax_unit: - members: [person1, person2] - filing_status: JOINT - households: - household: - members: [person1, person2] - state_code: SC - output: - # AGI $135k, phase-out start $80k, width $110k - # Excess = $135,000 - $80,000 = $55,000 - # Phase-out fraction = $55,000 / $110,000 = 0.5 - # SCIAD = $30,000 * (1 - 0.5) = $15,000 - sc_sciad: 15_000 - -- name: Case 10, SCIAD for head of household in phase-out range. - period: 2026 - absolute_error_margin: 0.1 - reforms: policyengine_us.reforms.states.sc.h4216.sc_h4216.sc_h4216 - input: - gov.contrib.states.sc.h4216.in_effect: true - people: - person1: - age: 35 - is_tax_unit_head: true - employment_income: 101_250 - person2: - age: 10 - is_tax_unit_dependent: true - tax_units: - tax_unit: - members: [person1, person2] - filing_status: HEAD_OF_HOUSEHOLD - households: - household: - members: [person1, person2] - state_code: SC - output: - # AGI $101,250, phase-out start $60k, width $82,500 - # Excess = $101,250 - $60,000 = $41,250 - # Phase-out fraction = $41,250 / $82,500 = 0.5 - # SCIAD = $22,500 * (1 - 0.5) = $11,250 - sc_sciad: 11_250 - -# Tax Rate Tests - -- name: Case 11, tax calculation with new rates below $30k threshold. - period: 2026 - absolute_error_margin: 0.1 - reforms: policyengine_us.reforms.states.sc.h4216.sc_h4216.sc_h4216 - input: - gov.contrib.states.sc.h4216.in_effect: true - people: - person1: - age: 30 - is_tax_unit_head: true - employment_income: 35_000 - tax_units: - tax_unit: - members: [person1] - households: - household: - members: [person1] - state_code: SC - output: - # AGI $35k, SCIAD $15k (below phase-out) - # Taxable income = $35k - $15k = $20k - # Tax = $20k * 1.99% = $398 - sc_income_tax_before_non_refundable_credits: 398 - -- name: Case 12, tax calculation with new rates above $30k threshold. - period: 2026 - absolute_error_margin: 0.1 - reforms: policyengine_us.reforms.states.sc.h4216.sc_h4216.sc_h4216 - input: - gov.contrib.states.sc.h4216.in_effect: true - people: - person1: - age: 30 - is_tax_unit_head: true - employment_income: 100_000 - tax_units: - tax_unit: - members: [person1] - households: - household: - members: [person1] - state_code: SC - output: - # AGI $100k, SCIAD $0 (fully phased out) - # Taxable income = $100k - # Tax = $30k * 1.99% + $70k * 5.21% = $597 + $3,647 = $4,244 - sc_income_tax_before_non_refundable_credits: 4_244 - -- name: Case 13, non-SC resident gets zero for all reform variables. - period: 2026 - absolute_error_margin: 0.1 - reforms: policyengine_us.reforms.states.sc.h4216.sc_h4216.sc_h4216 - input: - gov.contrib.states.sc.h4216.in_effect: true - people: - person1: - age: 30 - is_tax_unit_head: true - employment_income: 50_000 - tax_units: - tax_unit: - members: [person1] - households: - household: - members: [person1] - state_code: NY - output: - sc_sciad: 0 - sc_eitc: 0 - sc_taxable_income: 0 - sc_income_tax_before_non_refundable_credits: 0 - -# Boundary Tests - SCIAD Phase-out - -- name: Case 14, SCIAD at exactly phase-out start (single). - period: 2026 - absolute_error_margin: 0.1 - reforms: policyengine_us.reforms.states.sc.h4216.sc_h4216.sc_h4216 - input: - gov.contrib.states.sc.h4216.in_effect: true - people: - person1: - age: 30 - is_tax_unit_head: true - employment_income: 40_000 - tax_units: - tax_unit: - members: [person1] - households: - household: - members: [person1] - state_code: SC - output: - # AGI $40k = phase-out start, full SCIAD - sc_sciad: 15_000 - -- name: Case 15, SCIAD one dollar above phase-out start (single). - period: 2026 - absolute_error_margin: 0.1 - reforms: policyengine_us.reforms.states.sc.h4216.sc_h4216.sc_h4216 - input: - gov.contrib.states.sc.h4216.in_effect: true - people: - person1: - age: 30 - is_tax_unit_head: true - employment_income: 40_001 - tax_units: - tax_unit: - members: [person1] - households: - household: - members: [person1] - state_code: SC - output: - # AGI $40,001, excess = $1 - # Phase-out fraction = $1 / $55,000 = 0.0000182 - # Raw SCIAD = $15,000 * (1 - 0.0000182) = $14,999.73 - # Rounded down to nearest $10: $14,990 - sc_sciad: 14_990 - -- name: Case 16, SCIAD at exactly phase-out end (single). - period: 2026 - absolute_error_margin: 0.1 - reforms: policyengine_us.reforms.states.sc.h4216.sc_h4216.sc_h4216 - input: - gov.contrib.states.sc.h4216.in_effect: true - people: - person1: - age: 30 - is_tax_unit_head: true - employment_income: 95_000 - tax_units: - tax_unit: - members: [person1] - households: - household: - members: [person1] - state_code: SC - output: - # AGI $95k = phase-out end ($40k + $55k) - # Phase-out fraction = $55,000 / $55,000 = 1.0 - # SCIAD = $15,000 * (1 - 1) = $0 - sc_sciad: 0 - -- name: Case 17, SCIAD one dollar below phase-out end (single). - period: 2026 - absolute_error_margin: 0.1 - reforms: policyengine_us.reforms.states.sc.h4216.sc_h4216.sc_h4216 - input: - gov.contrib.states.sc.h4216.in_effect: true - people: - person1: - age: 30 - is_tax_unit_head: true - employment_income: 94_999 - tax_units: - tax_unit: - members: [person1] - households: - household: - members: [person1] - state_code: SC - output: - # AGI $94,999, excess = $94,999 - $40,000 = $54,999 - # Phase-out fraction = $54,999 / $55,000 = 0.9999818 - # Raw SCIAD = $15,000 * (1 - 0.9999818) = $0.27 - # Rounded down to nearest $10: $0 - sc_sciad: 0 - -- name: Case 18, SCIAD at exactly phase-out start (joint). - period: 2026 - absolute_error_margin: 0.1 - reforms: policyengine_us.reforms.states.sc.h4216.sc_h4216.sc_h4216 - input: - gov.contrib.states.sc.h4216.in_effect: true - people: - person1: - age: 35 - is_tax_unit_head: true - employment_income: 50_000 - person2: - age: 33 - is_tax_unit_spouse: true - employment_income: 30_000 - tax_units: - tax_unit: - members: [person1, person2] - filing_status: JOINT - households: - household: - members: [person1, person2] - state_code: SC - output: - # AGI $80k = phase-out start for joint, full SCIAD - sc_sciad: 30_000 - -- name: Case 19, SCIAD at exactly phase-out end (joint). - period: 2026 - absolute_error_margin: 0.1 - reforms: policyengine_us.reforms.states.sc.h4216.sc_h4216.sc_h4216 - input: - gov.contrib.states.sc.h4216.in_effect: true - people: - person1: - age: 35 - is_tax_unit_head: true - employment_income: 100_000 - person2: - age: 33 - is_tax_unit_spouse: true - employment_income: 90_000 - tax_units: - tax_unit: - members: [person1, person2] - filing_status: JOINT - households: - household: - members: [person1, person2] - state_code: SC - output: - # AGI $190k = phase-out end ($80k + $110k) - # SCIAD fully phased out - sc_sciad: 0 - -- name: Case 20, SCIAD at exactly phase-out start (HOH). - period: 2026 - absolute_error_margin: 0.1 - reforms: policyengine_us.reforms.states.sc.h4216.sc_h4216.sc_h4216 - input: - gov.contrib.states.sc.h4216.in_effect: true - people: - person1: - age: 35 - is_tax_unit_head: true - employment_income: 60_000 - person2: - age: 10 - is_tax_unit_dependent: true - tax_units: - tax_unit: - members: [person1, person2] - filing_status: HEAD_OF_HOUSEHOLD - households: - household: - members: [person1, person2] - state_code: SC - output: - # AGI $60k = phase-out start for HOH, full SCIAD - sc_sciad: 22_500 - -- name: Case 21, SCIAD at exactly phase-out end (HOH). - period: 2026 - absolute_error_margin: 0.1 - reforms: policyengine_us.reforms.states.sc.h4216.sc_h4216.sc_h4216 - input: - gov.contrib.states.sc.h4216.in_effect: true - people: - person1: - age: 35 - is_tax_unit_head: true - employment_income: 142_500 - person2: - age: 10 - is_tax_unit_dependent: true - tax_units: - tax_unit: - members: [person1, person2] - filing_status: HEAD_OF_HOUSEHOLD - households: - household: - members: [person1, person2] - state_code: SC - output: - # AGI $142,500 = phase-out end ($60k + $82,500) - # SCIAD fully phased out - sc_sciad: 0 - -# Filing Status Tests - -- name: Case 22, SCIAD for married filing separately. - period: 2026 - absolute_error_margin: 0.1 - reforms: policyengine_us.reforms.states.sc.h4216.sc_h4216.sc_h4216 - input: - gov.contrib.states.sc.h4216.in_effect: true - people: - person1: - age: 35 - is_tax_unit_head: true - employment_income: 35_000 - tax_units: - tax_unit: - members: [person1] - filing_status: SEPARATE - households: - household: - members: [person1] - state_code: SC - output: - # AGI $35k < phase-out start $40k for SEPARATE - # Full SCIAD = $15,000 - sc_sciad: 15_000 - -- name: Case 23, SCIAD for separate filer in phase-out range. - period: 2026 - absolute_error_margin: 0.1 - reforms: policyengine_us.reforms.states.sc.h4216.sc_h4216.sc_h4216 - input: - gov.contrib.states.sc.h4216.in_effect: true - people: - person1: - age: 35 - is_tax_unit_head: true - employment_income: 67_500 - tax_units: - tax_unit: - members: [person1] - filing_status: SEPARATE - households: - household: - members: [person1] - state_code: SC - output: - # AGI $67,500, phase-out start $40k, width $55k - # Excess = $67,500 - $40,000 = $27,500 - # Phase-out fraction = $27,500 / $55,000 = 0.5 - # SCIAD = $15,000 * (1 - 0.5) = $7,500 - sc_sciad: 7_500 - -- name: Case 24, SCIAD fully phased out for separate filer. - period: 2026 - absolute_error_margin: 0.1 - reforms: policyengine_us.reforms.states.sc.h4216.sc_h4216.sc_h4216 - input: - gov.contrib.states.sc.h4216.in_effect: true - people: - person1: - age: 35 - is_tax_unit_head: true - employment_income: 100_000 - tax_units: - tax_unit: - members: [person1] - filing_status: SEPARATE - households: - household: - members: [person1] - state_code: SC - output: - # AGI $100k > phase-out end $95k ($40k + $55k) - # SCIAD fully phased out - sc_sciad: 0 - -- name: Case 25, SCIAD for surviving spouse below phase-out. - period: 2026 - absolute_error_margin: 0.1 - reforms: policyengine_us.reforms.states.sc.h4216.sc_h4216.sc_h4216 - input: - gov.contrib.states.sc.h4216.in_effect: true - people: - person1: - age: 35 - is_tax_unit_head: true - employment_income: 75_000 - person2: - age: 10 - is_tax_unit_dependent: true - tax_units: - tax_unit: - members: [person1, person2] - filing_status: SURVIVING_SPOUSE - households: - household: - members: [person1, person2] - state_code: SC - output: - # AGI $75k < phase-out start $80k for SURVIVING_SPOUSE - # Full SCIAD = $30,000 - sc_sciad: 30_000 - -- name: Case 26, SCIAD for surviving spouse in phase-out range. - period: 2026 - absolute_error_margin: 0.1 - reforms: policyengine_us.reforms.states.sc.h4216.sc_h4216.sc_h4216 - input: - gov.contrib.states.sc.h4216.in_effect: true - people: - person1: - age: 35 - is_tax_unit_head: true - employment_income: 135_000 - person2: - age: 10 - is_tax_unit_dependent: true - tax_units: - tax_unit: - members: [person1, person2] - filing_status: SURVIVING_SPOUSE - households: - household: - members: [person1, person2] - state_code: SC - output: - # AGI $135k, phase-out start $80k, width $110k - # Excess = $135,000 - $80,000 = $55,000 - # Phase-out fraction = $55,000 / $110,000 = 0.5 - # SCIAD = $30,000 * (1 - 0.5) = $15,000 - sc_sciad: 15_000 - -- name: Case 27, SCIAD fully phased out for surviving spouse. - period: 2026 - absolute_error_margin: 0.1 - reforms: policyengine_us.reforms.states.sc.h4216.sc_h4216.sc_h4216 - input: - gov.contrib.states.sc.h4216.in_effect: true - people: - person1: - age: 35 - is_tax_unit_head: true - employment_income: 200_000 - person2: - age: 10 - is_tax_unit_dependent: true - tax_units: - tax_unit: - members: [person1, person2] - filing_status: SURVIVING_SPOUSE - households: - household: - members: [person1, person2] - state_code: SC - output: - # AGI $200k > phase-out end $190k ($80k + $110k) - # SCIAD fully phased out - sc_sciad: 0 - -# Standard/Itemized Deduction Elimination Tests - -- name: Case 28, federal standard deduction ignored - uses SCIAD not federal deductions. - period: 2026 - absolute_error_margin: 0.1 - reforms: policyengine_us.reforms.states.sc.h4216.sc_h4216.sc_h4216 - input: - gov.contrib.states.sc.h4216.in_effect: true - people: - person1: - age: 30 - is_tax_unit_head: true - employment_income: 50_000 - tax_units: - tax_unit: - members: [person1] - households: - household: - members: [person1] - state_code: SC - output: - # AGI = $50,000 - # Federal standard deduction would be ~$15,200 (2026) - # But H.4216 ignores federal deductions and uses SCIAD instead - # SCIAD at $50k AGI: excess = $50k - $40k = $10k - # Phase-out fraction = $10k / $55k = 0.18182 - # Raw SCIAD = $15,000 * (1 - 0.18182) = $12,272.73 - # Rounded down to nearest $10: $12,270 - # SC taxable income = $50,000 - $12,270 = $37,730 - sc_sciad: 12_270 - sc_taxable_income: 37_730 - -- name: Case 29, high income with no SCIAD (fully phased out). - period: 2026 - absolute_error_margin: 0.1 - reforms: policyengine_us.reforms.states.sc.h4216.sc_h4216.sc_h4216 - input: - gov.contrib.states.sc.h4216.in_effect: true - people: - person1: - age: 30 - is_tax_unit_head: true - employment_income: 150_000 - tax_units: - tax_unit: - members: [person1] - households: - household: - members: [person1] - state_code: SC - output: - # AGI = $150,000 - # H.4216 starts from AGI (ignores federal standard deduction) - # SCIAD at $150k AGI: AGI > phase-out end ($95k), SCIAD = $0 - # SC taxable income = AGI - SCIAD = $150,000 - sc_sciad: 0 - sc_taxable_income: 150_000 - -# Rate Bracket Boundary Tests - -- name: Case 30, taxable income exactly at $30k bracket boundary. - period: 2026 - absolute_error_margin: 0.1 - reforms: policyengine_us.reforms.states.sc.h4216.sc_h4216.sc_h4216 - input: - gov.contrib.states.sc.h4216.in_effect: true - people: - person1: - age: 30 - is_tax_unit_head: true - tax_units: - tax_unit: - members: [person1] - adjusted_gross_income: 30_000 - sc_additions: 0 - sc_subtractions: 0 - sc_sciad: 0 - households: - household: - members: [person1] - state_code: SC - output: - # SC taxable income = $30,000 (directly set via AGI with no deductions) - # Tax = $30,000 * 1.99% = $597 - # All income in first bracket only - sc_taxable_income: 30_000 - sc_income_tax_before_non_refundable_credits: 597 - -- name: Case 31, taxable income one dollar above $30k bracket. - period: 2026 - absolute_error_margin: 0.1 - reforms: policyengine_us.reforms.states.sc.h4216.sc_h4216.sc_h4216 - input: - gov.contrib.states.sc.h4216.in_effect: true - people: - person1: - age: 30 - is_tax_unit_head: true - tax_units: - tax_unit: - members: [person1] - adjusted_gross_income: 30_001 - sc_additions: 0 - sc_subtractions: 0 - sc_sciad: 0 - households: - household: - members: [person1] - state_code: SC - output: - # SC taxable income = $30,001 - # Tax = $30,000 * 1.99% + $1 * 5.21% = $597 + $0.0521 = $597.05 - sc_taxable_income: 30_001 - sc_income_tax_before_non_refundable_credits: 597.1 - -- name: Case 32, high income tax calculation. - period: 2026 - absolute_error_margin: 0.1 - reforms: policyengine_us.reforms.states.sc.h4216.sc_h4216.sc_h4216 - input: - gov.contrib.states.sc.h4216.in_effect: true - people: - person1: - age: 30 - is_tax_unit_head: true - employment_income: 125_000 - tax_units: - tax_unit: - members: [person1] - households: - household: - members: [person1] - state_code: SC - output: - # AGI $125k, SCIAD $0 (fully phased out above $95k) - # SC taxable income = $125k - # Tax = $30k * 1.99% + $95k * 5.21% = $597 + $4,949.50 = $5,546.50 - sc_income_tax_before_non_refundable_credits: 5_546.5 - -# Zero/Low Income Edge Cases - -- name: Case 33, zero income results in zero tax. - period: 2026 - absolute_error_margin: 0.1 - reforms: policyengine_us.reforms.states.sc.h4216.sc_h4216.sc_h4216 - input: - gov.contrib.states.sc.h4216.in_effect: true - people: - person1: - age: 30 - is_tax_unit_head: true - employment_income: 0 - tax_units: - tax_unit: - members: [person1] - households: - household: - members: [person1] - state_code: SC - output: - sc_sciad: 15_000 - sc_taxable_income: 0 - sc_income_tax_before_non_refundable_credits: 0 - -- name: Case 34, income below SCIAD results in zero taxable income. - period: 2026 - absolute_error_margin: 0.1 - reforms: policyengine_us.reforms.states.sc.h4216.sc_h4216.sc_h4216 - input: - gov.contrib.states.sc.h4216.in_effect: true - people: - person1: - age: 30 - is_tax_unit_head: true - employment_income: 10_000 - tax_units: - tax_unit: - members: [person1] - households: - household: - members: [person1] - state_code: SC - output: - # AGI $10k < SCIAD $15k - # SC taxable income = max(0, $10k - $15k) = $0 - sc_sciad: 15_000 - sc_taxable_income: 0 - sc_income_tax_before_non_refundable_credits: 0 - -# Negative AGI Edge Case - -- name: Case 35, negative AGI with self-employment loss. - period: 2026 - absolute_error_margin: 0.1 - reforms: policyengine_us.reforms.states.sc.h4216.sc_h4216.sc_h4216 - input: - gov.contrib.states.sc.h4216.in_effect: true - people: - person1: - age: 30 - is_tax_unit_head: true - self_employment_income: -20_000 - tax_units: - tax_unit: - members: [person1] - households: - household: - members: [person1] - state_code: SC - output: - # AGI is negative from SE loss - # SCIAD: excess_agi = max_(negative - $40k, 0) = 0, full SCIAD = $15,000 - # SC taxable income = max_(0, negative - $15,000) = $0 - sc_sciad: 15_000 - sc_taxable_income: 0 - sc_income_tax_before_non_refundable_credits: 0 - sc_eitc: 0 - -# SC Subtractions Interaction -# Note: sc_additions (QBI addback, SALT addback) are excluded from the -# reform formula because it starts from AGI where those federal deductions -# were never taken. Only sc_subtractions (retirement, military) apply. - -- name: Case 36, SC subtractions reduce taxable income (additions excluded). - period: 2026 - absolute_error_margin: 0.1 - reforms: policyengine_us.reforms.states.sc.h4216.sc_h4216.sc_h4216 - input: - gov.contrib.states.sc.h4216.in_effect: true - people: - person1: - age: 30 - is_tax_unit_head: true - tax_units: - tax_unit: - members: [person1] - adjusted_gross_income: 50_000 - sc_additions: 5_000 - sc_subtractions: 2_000 - sc_sciad: 0 - households: - household: - members: [person1] - state_code: SC - output: - # sc_additions ignored (AGI base already excludes federal deductions) - # SC taxable income = $50,000 - $2,000 - $0 = $48,000 - # Tax = $30k * 1.99% + $18k * 5.21% = $597 + $937.80 = $1,534.80 - sc_taxable_income: 48_000 - sc_income_tax_before_non_refundable_credits: 1_534.8 - -# Joint Filer SCIAD Greater Than AGI - -- name: Case 37, joint filer SCIAD exceeds AGI. - period: 2026 - absolute_error_margin: 0.1 - reforms: policyengine_us.reforms.states.sc.h4216.sc_h4216.sc_h4216 - input: - gov.contrib.states.sc.h4216.in_effect: true - people: - person1: - age: 35 - is_tax_unit_head: true - employment_income: 15_000 - person2: - age: 33 - is_tax_unit_spouse: true - employment_income: 5_000 - tax_units: - tax_unit: - members: [person1, person2] - filing_status: JOINT - households: - household: - members: [person1, person2] - state_code: SC - output: - # AGI = $20,000 < phase-out start $80k - # Full SCIAD = $30,000 - # SC taxable income = max(0, $20,000 - $30,000) = $0 - sc_sciad: 30_000 - sc_taxable_income: 0 - sc_income_tax_before_non_refundable_credits: 0 - -# End-to-End Tax Calculation with EITC - -- name: Case 38, end-to-end tax with EITC and all components. - period: 2026 - absolute_error_margin: 1 - reforms: policyengine_us.reforms.states.sc.h4216.sc_h4216.sc_h4216 - input: - gov.contrib.states.sc.h4216.in_effect: true - people: - person1: - age: 30 - is_tax_unit_head: true - tax_units: - tax_unit: - members: [person1] - adjusted_gross_income: 50_000 - eitc: 500 - sc_additions: 0 - sc_subtractions: 0 - households: - household: - members: [person1] - state_code: SC - output: - # EITC: min($500 * 1.25, $200) = min($625, $200) = $200 - sc_eitc: 200 - # SCIAD for SINGLE: AGI $50k, phase-out start $40k, width $55k - # Excess = $10k, fraction = $10k/$55k = 0.18182 - # Raw SCIAD = $15,000 * (1 - 0.18182) = $12,272.73 - # Rounded down to nearest $10: $12,270 - sc_sciad: 12_270 - # SC taxable income = $50,000 - $12,270 = $37,730 - sc_taxable_income: 37_730 - # Tax = $30k * 1.99% + $7,730 * 5.21% = $597 + $402.73 = $999.73 - sc_income_tax_before_non_refundable_credits: 1_000 From d77d41571a01514e2c8617c547dc7dbed63c02c3 Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Mon, 6 Apr 2026 09:50:26 -0400 Subject: [PATCH 4/5] Address review feedback: use in_effect parameter instead of hardcoded year - Add in_effect parameter for SCIAD - Use in_effect in sc_taxable_income and sc_sciad instead of hardcoded year - Remove unnecessary 0001-01-01 default values from parameters - Simplify sc_sciad return statement Co-Authored-By: Claude Opus 4.5 --- .../sc/tax/income/deductions/sciad/amount.yaml | 5 ----- .../sc/tax/income/deductions/sciad/in_effect.yaml | 13 +++++++++++++ .../income/deductions/sciad/phase_out/start.yaml | 5 ----- .../income/deductions/sciad/phase_out/width.yaml | 6 ------ .../sc/tax/income/deductions/sciad/sc_sciad.py | 7 ++++--- .../gov/states/sc/tax/income/sc_taxable_income.py | 11 ++++------- 6 files changed, 21 insertions(+), 26 deletions(-) create mode 100644 policyengine_us/parameters/gov/states/sc/tax/income/deductions/sciad/in_effect.yaml diff --git a/policyengine_us/parameters/gov/states/sc/tax/income/deductions/sciad/amount.yaml b/policyengine_us/parameters/gov/states/sc/tax/income/deductions/sciad/amount.yaml index 99d1c65d479..d0aa6750f1e 100644 --- a/policyengine_us/parameters/gov/states/sc/tax/income/deductions/sciad/amount.yaml +++ b/policyengine_us/parameters/gov/states/sc/tax/income/deductions/sciad/amount.yaml @@ -11,21 +11,16 @@ metadata: href: https://www.scstatehouse.gov/sess126_2025-2026/bills/4216.htm SINGLE: - 0001-01-01: 0 2026-01-01: 15_000 SEPARATE: - 0001-01-01: 0 2026-01-01: 15_000 HEAD_OF_HOUSEHOLD: - 0001-01-01: 0 2026-01-01: 22_500 JOINT: - 0001-01-01: 0 2026-01-01: 30_000 SURVIVING_SPOUSE: - 0001-01-01: 0 2026-01-01: 30_000 diff --git a/policyengine_us/parameters/gov/states/sc/tax/income/deductions/sciad/in_effect.yaml b/policyengine_us/parameters/gov/states/sc/tax/income/deductions/sciad/in_effect.yaml new file mode 100644 index 00000000000..57ddf1c5d92 --- /dev/null +++ b/policyengine_us/parameters/gov/states/sc/tax/income/deductions/sciad/in_effect.yaml @@ -0,0 +1,13 @@ +description: Whether the SC Income Adjusted Deduction (SCIAD) is in effect. + +metadata: + unit: bool + period: year + label: SC SCIAD in effect + reference: + - title: SC H.4216 Section 3 - Section 12-6-1140(15) + href: https://www.scstatehouse.gov/sess126_2025-2026/bills/4216.htm + +values: + 0000-01-01: false + 2026-01-01: true diff --git a/policyengine_us/parameters/gov/states/sc/tax/income/deductions/sciad/phase_out/start.yaml b/policyengine_us/parameters/gov/states/sc/tax/income/deductions/sciad/phase_out/start.yaml index c879ba4e172..662f2f1bbd8 100644 --- a/policyengine_us/parameters/gov/states/sc/tax/income/deductions/sciad/phase_out/start.yaml +++ b/policyengine_us/parameters/gov/states/sc/tax/income/deductions/sciad/phase_out/start.yaml @@ -11,21 +11,16 @@ metadata: href: https://www.scstatehouse.gov/sess126_2025-2026/bills/4216.htm SINGLE: - 0001-01-01: 0 2026-01-01: 40_000 SEPARATE: - 0001-01-01: 0 2026-01-01: 40_000 HEAD_OF_HOUSEHOLD: - 0001-01-01: 0 2026-01-01: 60_000 JOINT: - 0001-01-01: 0 2026-01-01: 80_000 SURVIVING_SPOUSE: - 0001-01-01: 0 2026-01-01: 80_000 diff --git a/policyengine_us/parameters/gov/states/sc/tax/income/deductions/sciad/phase_out/width.yaml b/policyengine_us/parameters/gov/states/sc/tax/income/deductions/sciad/phase_out/width.yaml index f6e94b4c48a..c5828c7373f 100644 --- a/policyengine_us/parameters/gov/states/sc/tax/income/deductions/sciad/phase_out/width.yaml +++ b/policyengine_us/parameters/gov/states/sc/tax/income/deductions/sciad/phase_out/width.yaml @@ -10,23 +10,17 @@ metadata: - title: SC H.4216 Section 3 - Section 12-6-1140(15)(b) href: https://www.scstatehouse.gov/sess126_2025-2026/bills/4216.htm -# Use 1 as default to avoid division by zero (amount is 0 before 2026 anyway) SINGLE: - 0001-01-01: 1 2026-01-01: 55_000 SEPARATE: - 0001-01-01: 1 2026-01-01: 55_000 HEAD_OF_HOUSEHOLD: - 0001-01-01: 1 2026-01-01: 82_500 JOINT: - 0001-01-01: 1 2026-01-01: 110_000 SURVIVING_SPOUSE: - 0001-01-01: 1 2026-01-01: 110_000 diff --git a/policyengine_us/variables/gov/states/sc/tax/income/deductions/sciad/sc_sciad.py b/policyengine_us/variables/gov/states/sc/tax/income/deductions/sciad/sc_sciad.py index 9c0c659883a..d75354614fa 100644 --- a/policyengine_us/variables/gov/states/sc/tax/income/deductions/sciad/sc_sciad.py +++ b/policyengine_us/variables/gov/states/sc/tax/income/deductions/sciad/sc_sciad.py @@ -12,6 +12,9 @@ class sc_sciad(Variable): def formula(tax_unit, period, parameters): p = parameters(period).gov.states.sc.tax.income.deductions.sciad + # SCIAD only exists starting 2026 + if not p.in_effect: + return tax_unit("filing_status", period) * 0 filing_status = tax_unit("filing_status", period) # Get the base amount by filing status base_amount = p.amount[filing_status] @@ -28,6 +31,4 @@ def formula(tax_unit, period, parameters): reduction = base_amount * phase_out_fraction # Round reduction down to nearest $10 reduction_rounded = np.floor(reduction / 10) * 10 - # Calculate deduction - deduction = max_(0, base_amount - reduction_rounded) - return deduction + return max_(0, base_amount - reduction_rounded) diff --git a/policyengine_us/variables/gov/states/sc/tax/income/sc_taxable_income.py b/policyengine_us/variables/gov/states/sc/tax/income/sc_taxable_income.py index d0281787d5e..3bba844b471 100644 --- a/policyengine_us/variables/gov/states/sc/tax/income/sc_taxable_income.py +++ b/policyengine_us/variables/gov/states/sc/tax/income/sc_taxable_income.py @@ -14,20 +14,17 @@ class sc_taxable_income(Variable): ) def formula(tax_unit, period, parameters): - # Check if we're in H4216 era (2026+) - # H4216 Section 2: SC does not adopt federal standard/itemized deductions - # H4216 Section 3: SC uses SCIAD instead, starting from AGI - year = period.start.year + p = parameters(period).gov.states.sc.tax.income.deductions.sciad subtractions = tax_unit("sc_subtractions", period) - if year >= 2026: - # For 2026+: Start from AGI (SC ignores federal std/itemized deductions) + if p.in_effect: + # H4216: Start from AGI (SC ignores federal std/itemized deductions) # Additions are not needed since they correct for federal deductions # that are no longer relevant when starting from AGI agi = tax_unit("adjusted_gross_income", period) return max_(0, agi - subtractions) else: - # Before 2026: Traditional calculation from federal taxable income + # Traditional calculation from federal taxable income taxable_income = tax_unit("taxable_income", period) additions = tax_unit("sc_additions", period) return max_(0, taxable_income + additions - subtractions) From a982df13d469ad922bb8af2d8f670847d6a173bd Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Mon, 6 Apr 2026 10:26:26 -0400 Subject: [PATCH 5/5] Fix EnumArray multiplication error in sc_sciad Use adjusted_gross_income instead of filing_status for zero return, since EnumArrays don't support multiplication. Co-Authored-By: Claude Opus 4.5 --- .../gov/states/sc/tax/income/deductions/sciad/sc_sciad.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/policyengine_us/variables/gov/states/sc/tax/income/deductions/sciad/sc_sciad.py b/policyengine_us/variables/gov/states/sc/tax/income/deductions/sciad/sc_sciad.py index d75354614fa..b5350fd49ab 100644 --- a/policyengine_us/variables/gov/states/sc/tax/income/deductions/sciad/sc_sciad.py +++ b/policyengine_us/variables/gov/states/sc/tax/income/deductions/sciad/sc_sciad.py @@ -14,7 +14,7 @@ def formula(tax_unit, period, parameters): p = parameters(period).gov.states.sc.tax.income.deductions.sciad # SCIAD only exists starting 2026 if not p.in_effect: - return tax_unit("filing_status", period) * 0 + return tax_unit("adjusted_gross_income", period) * 0 filing_status = tax_unit("filing_status", period) # Get the base amount by filing status base_amount = p.amount[filing_status]