From 94222017ae393c184fa6968101cbf84df74782a2 Mon Sep 17 00:00:00 2001 From: PavelMakarchuk Date: Tue, 27 Oct 2026 14:01:59 -0400 Subject: [PATCH] Net spouses' self-employment losses in OK EITC, WA WFTC and NY WFTC reform earned income ok_federal_eitc_phased_in, ok_federal_eitc_reduction, the WA Working Families Tax Credit (and its age-expansion eligibility) and the NY WFTC contributed reform read filer_adjusted_earnings, which floors each spouse at zero before summing. Federal earned income (IRC 32(c)(2)(A), EIC Worksheet B; Form 511-EIC line 15; RCW 82.08.0206(2)(b)) combines both spouses on a joint return, so a Schedule C loss offsets wages. Read eitc_earned_income instead, as #9820 did for the state EITC helper. Fixes #10448 Co-Authored-By: Claude Opus 5.5 --- ...10448-eitc-earned-income-ok-wa-ny.fixed.md | 1 + .../ny/wftc/ny_working_families_tax_credit.py | 8 +- .../ok/tax/income/credits/eitc/ok_eitc.yaml | 67 ++++++++++++++ .../wa_working_families_tax_credit.yaml | 91 +++++++++++++++++++ .../ny/ny_working_families_tax_credit.yaml | 33 +++++++ .../ok_federal_eitc_phased_in.py | 6 +- .../ok_federal_eitc_reduction.py | 3 +- .../credits/wa_working_families_tax_credit.py | 5 +- ...ilies_tax_credit_age_expansion_eligible.py | 3 +- 9 files changed, 208 insertions(+), 9 deletions(-) create mode 100644 changelog.d/fix-10448-eitc-earned-income-ok-wa-ny.fixed.md diff --git a/changelog.d/fix-10448-eitc-earned-income-ok-wa-ny.fixed.md b/changelog.d/fix-10448-eitc-earned-income-ok-wa-ny.fixed.md new file mode 100644 index 00000000000..b797d34483b --- /dev/null +++ b/changelog.d/fix-10448-eitc-earned-income-ok-wa-ny.fixed.md @@ -0,0 +1 @@ +Oklahoma's EITC, Washington's Working Families Tax Credit and the New York WFTC contributed reform now net a spouse's self-employment loss against the other spouse's wages when they compute earned income. diff --git a/policyengine_us/reforms/states/ny/wftc/ny_working_families_tax_credit.py b/policyengine_us/reforms/states/ny/wftc/ny_working_families_tax_credit.py index a9fe23a6548..b26290aa35e 100644 --- a/policyengine_us/reforms/states/ny/wftc/ny_working_families_tax_credit.py +++ b/policyengine_us/reforms/states/ny/wftc/ny_working_families_tax_credit.py @@ -117,7 +117,7 @@ class eitc_younger_phased_in(Variable): def formula(tax_unit, period, parameters): maximum = tax_unit("eitc_younger_maximum", period) phase_in_rate = tax_unit("eitc_younger_phase_in_rate", period) - earnings = tax_unit("filer_adjusted_earnings", period) + earnings = tax_unit("eitc_earned_income", period) phased_in_amount = earnings * phase_in_rate return min_(maximum, phased_in_amount) @@ -161,7 +161,7 @@ class eitc_younger_reduction(Variable): reference = "https://www.law.cornell.edu/uscode/text/26/32#a_2" def formula(tax_unit, period, parameters): - earnings = tax_unit("filer_adjusted_earnings", period) + earnings = tax_unit("eitc_earned_income", period) agi = tax_unit("adjusted_gross_income", period) highest_income_variable = max_(earnings, agi) phase_out_start = tax_unit("eitc_younger_phase_out_start", period) @@ -298,7 +298,7 @@ class eitc_older_phased_in(Variable): def formula(tax_unit, period, parameters): maximum = tax_unit("eitc_older_maximum", period) phase_in_rate = tax_unit("eitc_older_phase_in_rate", period) - earnings = tax_unit("filer_adjusted_earnings", period) + earnings = tax_unit("eitc_earned_income", period) phased_in_amount = earnings * phase_in_rate return min_(maximum, phased_in_amount) @@ -346,7 +346,7 @@ class eitc_older_reduction(Variable): reference = "https://www.law.cornell.edu/uscode/text/26/32#a_2" def formula(tax_unit, period, parameters): - earnings = tax_unit("filer_adjusted_earnings", period) + earnings = tax_unit("eitc_earned_income", period) agi = tax_unit("adjusted_gross_income", period) highest_income_variable = max_(earnings, agi) phase_out_start = tax_unit("eitc_older_phase_out_start", period) diff --git a/policyengine_us/tests/policy/baseline/gov/states/ok/tax/income/credits/eitc/ok_eitc.yaml b/policyengine_us/tests/policy/baseline/gov/states/ok/tax/income/credits/eitc/ok_eitc.yaml index 4886da88445..0098018092c 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ok/tax/income/credits/eitc/ok_eitc.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ok/tax/income/credits/eitc/ok_eitc.yaml @@ -144,3 +144,70 @@ output: # No federal EITC = no OK EITC ok_eitc: 0 + +- name: OK EITC - 2025 - a spouse's self-employment loss reduces the phase-in base + period: 2025 + absolute_error_margin: 0.01 + input: + people: + head: + age: 40 + employment_income: 10_000 + spouse: + age: 40 + self_employment_income: -4_000 + child1: + age: 7 + is_tax_unit_dependent: true + child2: + age: 10 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [head, spouse, child1, child2] + households: + household: + members: [head, spouse, child1, child2] + state_code: OK + output: + # Form 511-EIC: line 1 wages 10,000; line 12 Schedule C loss -4,000; + # line 15 total earned income 6,000 (lines 13 and 15 combine them). AGI is + # also 6,000. Under the 2020 rules the two-child phase-in rate is 40%: + # 0.40 x 6,000 = 2,400 (the 2020 EIC table row 6,000-6,050 gives 2,410 at + # the row midpoint). OK EITC = 5% x 2,400 = 120. Flooring each spouse at + # zero would count 10,000 and give 4,000 and 200. + ok_federal_eitc: 2_400 + ok_eitc: 120 + +- name: OK EITC - 2025 - a spouse's self-employment loss lowers the phase-out base + period: 2025 + absolute_error_margin: 0.01 + input: + people: + head: + age: 40 + employment_income: 40_000 + spouse: + age: 40 + self_employment_income: -10_000 + child1: + age: 7 + is_tax_unit_dependent: true + child2: + age: 10 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [head, spouse, child1, child2] + households: + household: + members: [head, spouse, child1, child2] + state_code: OK + output: + # Earned income (511-EIC line 15) = 40,000 + (-10,000) = 30,000 = AGI. + # 2020 joint two-child credit: 5,920 - 21.06% x (30,000 - 25,220) = + # 4,913.33 (the 2020 EIC table row 30,000-30,050 gives 4,908 at the row + # midpoint). OK EITC = 5% x 4,913.33 = 245.67. Measuring from 40,000 would + # give 5,920 - 21.06% x 14,780 = 2,807.33. + ok_federal_eitc: 4_913.33 + ok_eitc: 245.67 diff --git a/policyengine_us/tests/policy/baseline/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.yaml b/policyengine_us/tests/policy/baseline/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.yaml index 1231731596e..245657d5606 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.yaml @@ -1566,3 +1566,94 @@ # full 2024 one-child maximum ($640). This end-to-end case confirms the # WFTC flows through to household_net_income (it is a positive component). wa_working_families_tax_credit: 640 + +- name: Case 37, a spouse's self-employment loss that offsets the wages leaves no earned income. + period: 2025 + absolute_error_margin: 0.01 + input: + people: + head: + age: 40 + employment_income: 3_000 + has_tin: true + ssn_card_type: CITIZEN + is_tax_unit_head: true + spouse: + age: 40 + self_employment_income: -5_000 + has_tin: true + ssn_card_type: CITIZEN + is_tax_unit_spouse: true + child: + age: 7 + has_tin: true + ssn_card_type: CITIZEN + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [head, spouse, child] + filing_status: JOINT + would_file_taxes_voluntarily: true + takes_up_eitc: true + spm_units: + spm_unit: + members: [head, spouse, child] + households: + household: + members: [head, spouse, child] + state_code: WA + output: + # RCW 82.08.0206(2)(b): income is earned income under 26 U.S.C. 32. On a + # joint return, federal EIC Worksheet B combines both spouses' amounts: + # line 4b = 3,000 + (-5,000) < 0, so "You can't take the credit", and + # (2)(a)(i)(A) requires eligibility for the federal credit. Flooring each + # spouse at zero would count 3,000 and pay the one-child $660. + wa_working_families_tax_credit: 0 + +- name: Case 38, a spouse's self-employment loss lowers the phase-out base. + period: 2025 + absolute_error_margin: 0.01 + input: + people: + head: + age: 40 + employment_income: 62_000 + has_tin: true + ssn_card_type: CITIZEN + is_tax_unit_head: true + spouse: + age: 40 + self_employment_income: -10_000 + has_tin: true + ssn_card_type: CITIZEN + is_tax_unit_spouse: true + child1: + age: 7 + has_tin: true + ssn_card_type: CITIZEN + is_tax_unit_dependent: true + child2: + age: 10 + has_tin: true + ssn_card_type: CITIZEN + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [head, spouse, child1, child2] + filing_status: JOINT + would_file_taxes_voluntarily: true + takes_up_eitc: true + spm_units: + spm_unit: + members: [head, spouse, child1, child2] + households: + household: + members: [head, spouse, child1, child2] + state_code: WA + output: + # Earned income (Worksheet B line 4b) = 62,000 + (-10,000) = 52,000, and AGI + # is also 52,000. The 2025 joint two-child limit is $64,430 (Form 1040 + # instructions), so the reduction starts at 59,430 and does not apply. + # ETA 3240.2026 gives the 2025 two-child maximum, $995. Measuring from + # 62,000 would give 995 - 0.199 x (62,000 - 59,430) = 484. + wa_working_families_tax_credit: 995 diff --git a/policyengine_us/tests/policy/contrib/states/ny/ny_working_families_tax_credit.yaml b/policyengine_us/tests/policy/contrib/states/ny/ny_working_families_tax_credit.yaml index ea750e30421..918181f7702 100644 --- a/policyengine_us/tests/policy/contrib/states/ny/ny_working_families_tax_credit.yaml +++ b/policyengine_us/tests/policy/contrib/states/ny/ny_working_families_tax_credit.yaml @@ -344,3 +344,36 @@ state_code: NY output: ny_exemptions: 0 + +- name: Couple, one younger child, a spouse's self-employment loss reduces the federal credit - reform + period: 2025 + absolute_error_margin: 0.01 + reforms: policyengine_us.reforms.states.ny.wftc.ny_working_families_tax_credit.ny_working_families_tax_credit + input: + gov.contrib.states.ny.wftc.in_effect: true + people: + person1: + age: 40 + employment_income: 10_000 + person2: + age: 40 + self_employment_income: -4_000 + person3: + age: 7 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [person1, person2, person3] + households: + household: + members: [person1, person2, person3] + state_code: NY + output: + # Federal earned income (EIC Worksheet B, both spouses combined) is + # 10,000 + (-4,000) = 6,000. The one-child federal credit is 34% x 6,000 = + # 2,040, the same as eitc. S277C's 2025 match is 25%, so ny_eitc is + # 0.25 x 2,040 = 510 (no household credit at this income). Flooring each + # spouse at zero would give 34% x 10,000 = 3,400 and 850. + eitc: 2_040 + younger_eitc: 2_040 + ny_eitc: 510 diff --git a/policyengine_us/variables/gov/states/ok/tax/income/credits/eitc/federal_credit/ok_federal_eitc_phased_in.py b/policyengine_us/variables/gov/states/ok/tax/income/credits/eitc/federal_credit/ok_federal_eitc_phased_in.py index 5df2df4a616..ca11ffb9588 100644 --- a/policyengine_us/variables/gov/states/ok/tax/income/credits/eitc/federal_credit/ok_federal_eitc_phased_in.py +++ b/policyengine_us/variables/gov/states/ok/tax/income/credits/eitc/federal_credit/ok_federal_eitc_phased_in.py @@ -12,7 +12,7 @@ class ok_federal_eitc_phased_in(Variable): EITC phase-in amount using FROZEN 2020 parameters. The phase-in amount represents how much credit has been "earned" based - on the filer's adjusted earnings. It equals: + on earned income (Form 511-EIC line 15). It equals: min(maximum_credit, earnings * phase_in_rate) This creates the rising portion of the EITC where the credit increases @@ -36,7 +36,9 @@ def formula(tax_unit, period, parameters): maximum = tax_unit("ok_federal_eitc_maximum", period) # Get the phase-in rate (frozen at 2020 values) phase_in_rate = tax_unit("ok_federal_eitc_phase_in_rate", period) - earnings = tax_unit("filer_adjusted_earnings", period) + # Form 511-EIC line 15: both spouses' earned income combined, with + # a net self-employment loss subtracted (lines 12 and 13). + earnings = tax_unit("eitc_earned_income", period) # Calculate phased-in amount, capped at maximum phased_in_amount = earnings * phase_in_rate return min_(maximum, phased_in_amount) diff --git a/policyengine_us/variables/gov/states/ok/tax/income/credits/eitc/federal_credit/ok_federal_eitc_reduction.py b/policyengine_us/variables/gov/states/ok/tax/income/credits/eitc/federal_credit/ok_federal_eitc_reduction.py index 2db6f93ca20..68c46aa624c 100644 --- a/policyengine_us/variables/gov/states/ok/tax/income/credits/eitc/federal_credit/ok_federal_eitc_reduction.py +++ b/policyengine_us/variables/gov/states/ok/tax/income/credits/eitc/federal_credit/ok_federal_eitc_reduction.py @@ -36,7 +36,8 @@ class ok_federal_eitc_reduction(Variable): """ def formula(tax_unit, period, parameters): - earnings = tax_unit("filer_adjusted_earnings", period) + # Form 511-EIC line 15 earned income, as in ok_federal_eitc_phased_in. + earnings = tax_unit("eitc_earned_income", period) agi = tax_unit("adjusted_gross_income", period) # Use the greater of earnings or AGI for phase-out highest_income_variable = max_(earnings, agi) diff --git a/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.py b/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.py index 642ef1a6b77..694073c770d 100644 --- a/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.py +++ b/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.py @@ -80,7 +80,10 @@ def formula(tax_unit, period, parameters): tax_unit("eitc_relevant_investment_income", period) <= eitc.phase_out.max_investment_income ) - earnings = tax_unit("filer_adjusted_earnings", period) + # RCW 82.08.0206(2)(b): income is earned income under 26 U.S.C. 32, + # both spouses combined on a joint return, net of a self-employment + # loss. + earnings = tax_unit("eitc_earned_income", period) agi = tax_unit("adjusted_gross_income", period) higher_income = max_(earnings, agi) maximum_qualifying_income = tax_unit( diff --git a/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit_age_expansion_eligible.py b/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit_age_expansion_eligible.py index d890a4c84b2..a0d1ee9e6be 100644 --- a/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit_age_expansion_eligible.py +++ b/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit_age_expansion_eligible.py @@ -56,7 +56,8 @@ def formula(tax_unit, period, parameters): tax_unit("eitc_relevant_investment_income", period) <= eitc.phase_out.max_investment_income ) - earnings = tax_unit("filer_adjusted_earnings", period) + # RCW 82.08.0206(2)(b) earned income, as in wa_working_families_tax_credit. + earnings = tax_unit("eitc_earned_income", period) agi = tax_unit("adjusted_gross_income", period) # ESSB 6346 Sec. 901(2)(a)(ii)(C) makes the age-expansion path eligible # when income is "equal to or less than" the maximum qualifying income,