From 4b2bad4c3d133b731ac3d90d8d8be774bfc6b0b8 Mon Sep 17 00:00:00 2001 From: PavelMakarchuk Date: Tue, 6 Oct 2026 08:44:52 -0400 Subject: [PATCH 1/3] Apply the California YCTC wage and net loss limits to wages and unlimited losses Fixes #9880. Co-Authored-By: Claude Opus 5.5 --- .../ca-yctc-wage-and-loss-tests.fixed.md | 1 + .../credits/young_child/loss_threshold.yaml | 2 +- .../income/credits/young_child/ca_yctc.yaml | 74 ++++++++++++++++++- .../tax/income/credits/young_child/ca_yctc.py | 29 ++++++-- 4 files changed, 95 insertions(+), 11 deletions(-) create mode 100644 changelog.d/ca-yctc-wage-and-loss-tests.fixed.md diff --git a/changelog.d/ca-yctc-wage-and-loss-tests.fixed.md b/changelog.d/ca-yctc-wage-and-loss-tests.fixed.md new file mode 100644 index 00000000000..0d502ab4bdf --- /dev/null +++ b/changelog.d/ca-yctc-wage-and-loss-tests.fixed.md @@ -0,0 +1 @@ +California Young Child Tax Credit for filers with zero or negative earned income now applies the wage limit to wages and measures net losses without the capital loss limitation. diff --git a/policyengine_us/parameters/gov/states/ca/tax/income/credits/young_child/loss_threshold.yaml b/policyengine_us/parameters/gov/states/ca/tax/income/credits/young_child/loss_threshold.yaml index e39ff5574af..bcced7bf5f4 100644 --- a/policyengine_us/parameters/gov/states/ca/tax/income/credits/young_child/loss_threshold.yaml +++ b/policyengine_us/parameters/gov/states/ca/tax/income/credits/young_child/loss_threshold.yaml @@ -1,4 +1,4 @@ -description: California limits its Young Child Tax Credit to filers with losses not exceeding this value. +description: California limits its Young Child Tax Credit for filers with zero or negative earned income to those whose net losses, and whose wages, do not exceed this value. values: 2021-01-01: 0 2022-01-01: 32_490 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ca/tax/income/credits/young_child/ca_yctc.yaml b/policyengine_us/tests/policy/baseline/gov/states/ca/tax/income/credits/young_child/ca_yctc.yaml index c26330a541e..f907adce7b8 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ca/tax/income/credits/young_child/ca_yctc.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ca/tax/income/credits/young_child/ca_yctc.yaml @@ -148,7 +148,7 @@ people: parent: age: 30 - irs_gross_income: -20_000 # modest capital losses + short_term_capital_gains: -20_000 # modest capital losses child: age: 5 ca_is_qualifying_child_for_caleitc: true @@ -169,7 +169,7 @@ people: parent: age: 30 - irs_gross_income: -40_000 # large capital losses + short_term_capital_gains: -40_000 # large capital losses child: age: 5 ca_is_qualifying_child_for_caleitc: true @@ -183,3 +183,73 @@ output: # ca_eitc: 0 # because of zero earnings ca_yctc: 0 # not eligible for YCTC in 2022 because of large losses + +# R&TC 17052.1(b)(1)(B) and the 2022 FTB 3514 instructions, Step 8 (lines 23a +# and 23b): a filer with zero or negative earned income qualifies only if net +# losses (Form 540 line 17 without utilization limitations) and wages are each +# $32,490 or less in 2022. +- name: Self-employment loss above wages, modest net loss, so eligible in 2022. + period: 2022 + input: + people: + parent: + age: 30 + employment_income: 20_000 + self_employment_income: -30_000 + child: + age: 3 + ca_is_qualifying_child_for_caleitc: true + tax_units: + tax_unit: + members: [parent, child] + households: + household: + members: [parent, child] + state_code: CA + output: + # Earned income -10,000; net loss 10,000; wages 20,000. + ca_yctc: 1_083 + +- name: Wages above $32,490 with negative earned income, so not eligible in 2022. + period: 2022 + input: + people: + parent: + age: 30 + employment_income: 71_846 + self_employment_income: -92_009 + child: + age: 3 + ca_is_qualifying_child_for_caleitc: true + tax_units: + tax_unit: + members: [parent, child] + households: + household: + members: [parent, child] + state_code: CA + output: + # Line 23a: wages of $71,846 exceed $32,490. + ca_yctc: 0 + +- name: Self-employment net loss above $32,490, so not eligible in 2022. + period: 2022 + input: + people: + parent: + age: 30 + employment_income: 20_000 + self_employment_income: -80_000 + child: + age: 3 + ca_is_qualifying_child_for_caleitc: true + tax_units: + tax_unit: + members: [parent, child] + households: + household: + members: [parent, child] + state_code: CA + output: + # Line 23b: net loss of $60,000 exceeds $32,490. + ca_yctc: 0 diff --git a/policyengine_us/variables/gov/states/ca/tax/income/credits/young_child/ca_yctc.py b/policyengine_us/variables/gov/states/ca/tax/income/credits/young_child/ca_yctc.py index d7501fd6104..b1b3b1fc53c 100644 --- a/policyengine_us/variables/gov/states/ca/tax/income/credits/young_child/ca_yctc.py +++ b/policyengine_us/variables/gov/states/ca/tax/income/credits/young_child/ca_yctc.py @@ -34,17 +34,30 @@ def formula(tax_unit, period, parameters): gets_caleitc = tax_unit("ca_eitc", period) > 0 # ... determine (b) part two is_caleitc_eligible = tax_unit("ca_eitc_eligible", period) - # ... ... determine if losses are limited to modest amount - federal_gross_income = add(tax_unit, period, ["irs_gross_income"]) - total_federal_net_loss = max_(0, -federal_gross_income) - has_limited_losses = total_federal_net_loss <= p.loss_threshold - # ... ... determine if earnings are limited to modest amount - total_earnings = tax_unit("tax_unit_earned_income", period) - has_limited_earnings = total_earnings <= p.loss_threshold + # ... ... R&TC 17052.1(b)(1)(B)(i): earned income of zero or less + has_no_earned_income = tax_unit("tax_unit_earned_income", period) <= 0 + # ... ... (B)(ii): net losses within the threshold. FTB 3514 line 23b + # measures the net loss from Form 540 line 17 without utilization + # limitations, so the capital loss limit does not apply. + limited_capital_gains = tax_unit("loss_limited_net_capital_gains", period) + distributions = tax_unit.sum(max_(0, person("non_sch_d_capital_gains", period))) + unlimited_capital_gains = tax_unit("net_capital_gains", period) + distributions + total_income = ( + tax_unit("ca_agi", period) - limited_capital_gains + unlimited_capital_gains + ) + total_net_loss = max_(0, -total_income) + has_limited_losses = total_net_loss <= p.loss_threshold + # ... ... (B)(iii): wages, salaries, tips, and other employee + # compensation within the same threshold (FTB 3514 line 23a) + wages = add(tax_unit, period, ["employment_income"]) + has_limited_wages = wages <= p.loss_threshold # ... ... combine all the (b) elements where appropriate is_loss_eligible = where( p.loss_threshold > 0, - is_caleitc_eligible & has_limited_losses & has_limited_earnings, + is_caleitc_eligible + & has_no_earned_income + & has_limited_losses + & has_limited_wages, False, ) # ... combine (a) and (b) parts to determine eligibility From 6c7231f421eccb8b622dbf253a4497c4031cae87 Mon Sep 17 00:00:00 2001 From: PavelMakarchuk Date: Tue, 6 Oct 2026 16:26:30 -0400 Subject: [PATCH 2/3] Count only the filers' wages in the YCTC wage limit and pin capital gain distributions in the net loss Co-Authored-By: Claude Opus 5.5 --- .../income/credits/young_child/ca_yctc.yaml | 49 +++++++++++++++++++ .../tax/income/credits/young_child/ca_yctc.py | 10 ++-- 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/policyengine_us/tests/policy/baseline/gov/states/ca/tax/income/credits/young_child/ca_yctc.yaml b/policyengine_us/tests/policy/baseline/gov/states/ca/tax/income/credits/young_child/ca_yctc.yaml index f907adce7b8..5d2dfbad03b 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ca/tax/income/credits/young_child/ca_yctc.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ca/tax/income/credits/young_child/ca_yctc.yaml @@ -253,3 +253,52 @@ output: # Line 23b: net loss of $60,000 exceeds $32,490. ca_yctc: 0 + +- name: Capital gain distributions reduce a self-employment loss to within $32,490, so eligible in 2022. + period: 2022 + input: + people: + parent: + age: 30 + self_employment_income: -35_000 + non_sch_d_capital_gains: 3_000 + child: + age: 3 + ca_is_qualifying_child_for_caleitc: true + tax_units: + tax_unit: + members: [parent, child] + households: + household: + members: [parent, child] + state_code: CA + output: + # Line 23b: Form 540 line 17 is -35,000 + 3,000 = -32,000, a net loss of + # $32,000, within $32,490. Investment income of $3,000 is within $4,389. + ca_yctc: 1_083 + +- name: A dependent's wages do not count toward the $32,490 wage limit in 2022. + period: 2022 + input: + people: + parent: + age: 30 + self_employment_income: -10_000 + child: + age: 3 + ca_is_qualifying_child_for_caleitc: true + teen: + age: 17 + employment_income: 40_000 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [parent, child, teen] + households: + household: + members: [parent, child, teen] + state_code: CA + output: + # Line 23a carries the filer's wages (line 13), which are $0; the + # dependent's $40,000 is on the dependent's own return. + ca_yctc: 1_083 diff --git a/policyengine_us/variables/gov/states/ca/tax/income/credits/young_child/ca_yctc.py b/policyengine_us/variables/gov/states/ca/tax/income/credits/young_child/ca_yctc.py index b1b3b1fc53c..79ef16828b7 100644 --- a/policyengine_us/variables/gov/states/ca/tax/income/credits/young_child/ca_yctc.py +++ b/policyengine_us/variables/gov/states/ca/tax/income/credits/young_child/ca_yctc.py @@ -38,7 +38,10 @@ def formula(tax_unit, period, parameters): has_no_earned_income = tax_unit("tax_unit_earned_income", period) <= 0 # ... ... (B)(ii): net losses within the threshold. FTB 3514 line 23b # measures the net loss from Form 540 line 17 without utilization - # limitations, so the capital loss limit does not apply. + # limitations, so the capital loss limit does not apply. AGI carries + # loss_limited_net_capital_gains, which already includes capital gain + # distributions (they net against losses on Schedule D line 13), so the + # unlimited amount adds them back to net_capital_gains as well. limited_capital_gains = tax_unit("loss_limited_net_capital_gains", period) distributions = tax_unit.sum(max_(0, person("non_sch_d_capital_gains", period))) unlimited_capital_gains = tax_unit("net_capital_gains", period) + distributions @@ -48,8 +51,9 @@ def formula(tax_unit, period, parameters): total_net_loss = max_(0, -total_income) has_limited_losses = total_net_loss <= p.loss_threshold # ... ... (B)(iii): wages, salaries, tips, and other employee - # compensation within the same threshold (FTB 3514 line 23a) - wages = add(tax_unit, period, ["employment_income"]) + # compensation within the same threshold (FTB 3514 line 23a, which + # carries the filers' wages from line 13) + wages = tax_unit_non_dep_sum("employment_income", tax_unit, period) has_limited_wages = wages <= p.loss_threshold # ... ... combine all the (b) elements where appropriate is_loss_eligible = where( From acedceb1745447a393620b394a2e83178aaaf8e1 Mon Sep 17 00:00:00 2001 From: PavelMakarchuk Date: Thu, 8 Oct 2026 09:05:44 -0400 Subject: [PATCH 3/3] Test YCTC zero earned income on the CalEITC line 19 amount After PR 9818, FTB 3514 line 19 is the floored federal EITC earned income, so a business loss that offsets wages gives zero earned income. Use ca_eitc_earned_income for the R&TC 17052.1(b)(1)(B)(i) test, as the review asked, and add the 2022 wages-plus-farm-loss case. Co-Authored-By: Claude Opus 5.5 --- .../income/credits/young_child/ca_yctc.yaml | 25 +++++++++++++++++++ .../tax/income/credits/young_child/ca_yctc.py | 6 +++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/policyengine_us/tests/policy/baseline/gov/states/ca/tax/income/credits/young_child/ca_yctc.yaml b/policyengine_us/tests/policy/baseline/gov/states/ca/tax/income/credits/young_child/ca_yctc.yaml index 70112af0d5b..62833e2fc63 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ca/tax/income/credits/young_child/ca_yctc.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ca/tax/income/credits/young_child/ca_yctc.yaml @@ -302,3 +302,28 @@ # Line 23a carries the filer's wages (line 13), which are $0; the # dependent's $40,000 is on the dependent's own return. ca_yctc: 1_083 + +- name: Wages offset by a farm loss give zero CalEITC earned income, 2022 + period: 2022 + absolute_error_margin: 0.01 + input: + people: + head: + age: 30 + employment_income: 10_000 + farm_operations_income: -15_000 + child: + age: 3 + tax_units: + tax_unit: + members: [head, child] + households: + household: + members: [head, child] + state_code: CA + output: + # FTB 3514 line 19: 10,000 wages - 15,000 farm loss, floored at 0. + ca_eitc_earned_income: 0 + # Step 8: earned income of zero or less; the 5,000 net loss and the + # 10,000 wages are within the 2022 $32,490 limits, so the full $1,083. + ca_yctc: 1_083 diff --git a/policyengine_us/variables/gov/states/ca/tax/income/credits/young_child/ca_yctc.py b/policyengine_us/variables/gov/states/ca/tax/income/credits/young_child/ca_yctc.py index 7d7c1d98a3b..3705e68b1df 100644 --- a/policyengine_us/variables/gov/states/ca/tax/income/credits/young_child/ca_yctc.py +++ b/policyengine_us/variables/gov/states/ca/tax/income/credits/young_child/ca_yctc.py @@ -34,8 +34,10 @@ def formula(tax_unit, period, parameters): gets_caleitc = tax_unit("ca_eitc", period) > 0 # ... determine (b) part two is_caleitc_eligible = tax_unit("ca_eitc_eligible", period) - # ... ... R&TC 17052.1(b)(1)(B)(i): earned income of zero or less - has_no_earned_income = tax_unit("tax_unit_earned_income", period) <= 0 + # ... ... R&TC 17052.1(b)(1)(B)(i): earned income of zero or less, + # measured as the CalEITC earned income on FTB 3514 line 19, so a + # business loss that offsets wages counts (Step 8). + has_no_earned_income = tax_unit("ca_eitc_earned_income", period) <= 0 # ... ... (B)(ii): net losses within the threshold. FTB 3514 line 23b # measures the net loss from Form 540 line 17 without utilization # limitations, so the capital loss limit does not apply. AGI carries