From 6f00a2838a478e7f39157c7387edaa638acecb82 Mon Sep 17 00:00:00 2001 From: Daphne Hansell <128793799+daphnehanse11@users.noreply.github.com> Date: Thu, 14 May 2026 13:45:45 -0400 Subject: [PATCH 1/3] Integrate PA UC into unemployment compensation (cherry picked from commit 6198da9b844ed1dba23985b530ba2b56bcc77c62) --- .../issue-8301-state-ui-income-flow.added.md | 1 + .../integration.yaml | 22 +++++++++++++++++++ .../gov/states/unemployment_compensation.py | 1 + 3 files changed, 24 insertions(+) create mode 100644 changelog.d/issue-8301-state-ui-income-flow.added.md diff --git a/changelog.d/issue-8301-state-ui-income-flow.added.md b/changelog.d/issue-8301-state-ui-income-flow.added.md new file mode 100644 index 00000000000..e2f441e862f --- /dev/null +++ b/changelog.d/issue-8301-state-ui-income-flow.added.md @@ -0,0 +1 @@ +Integrated modeled Pennsylvania unemployment compensation into unemployment compensation income flows. diff --git a/policyengine_us/tests/policy/baseline/gov/states/pa/dli/unemployment_compensation/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/pa/dli/unemployment_compensation/integration.yaml index bf6f9384e27..ff8b2073b05 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/pa/dli/unemployment_compensation/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/pa/dli/unemployment_compensation/integration.yaml @@ -263,3 +263,25 @@ person1: # Household is outside PA -> defined_for filters the benefit. pa_uc: 0 + +- name: Case 8, PA UC feeds unemployment compensation income flows. + period: 2025 + absolute_error_margin: 0.01 + input: + age: 35 + state_code: PA + employment_income: 100_000 + is_tax_unit_dependent: false + pa_uc_highest_quarter_wages: 10_000 + pa_uc_base_year_wages: 28_000 + pa_uc_credit_weeks: 26 + weeks_unemployed: 26 + pa_uc_gross_weekly_earnings: 0 + output: + pa_uc: 10_218 + unemployment_compensation: 10_218 + tax_unit_unemployment_compensation: 10_218 + taxable_unemployment_compensation: 10_218 + tax_unit_taxable_unemployment_compensation: 10_218 + spm_unit_benefits: 10_218 + household_benefits: 10_218 diff --git a/policyengine_us/variables/gov/states/unemployment_compensation.py b/policyengine_us/variables/gov/states/unemployment_compensation.py index 0857864ab17..834fbc05c49 100644 --- a/policyengine_us/variables/gov/states/unemployment_compensation.py +++ b/policyengine_us/variables/gov/states/unemployment_compensation.py @@ -9,3 +9,4 @@ class unemployment_compensation(Variable): documentation = "Income from unemployment compensation programs." definition_period = YEAR uprating = "calibration.gov.irs.soi.unemployment_compensation" + adds = ["pa_uc"] From 926a7f2f6ea5d9dd825ec49b72b722af98ab83b3 Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Tue, 18 Aug 2026 15:53:52 -0400 Subject: [PATCH 2/3] Extend state UI income-flow wiring to AL, NY, OK, UT Builds on the PA UC integration: unemployment_compensation now adds the modeled state UI benefits for all currently-merged programs (al_ui, ny_ui, ok_ui, pa_uc, ut_ui). Adds a 'feeds unemployment compensation income flows' integration case to each state's UI suite (mirroring PA Case 8), asserting the benefit flows to unemployment_compensation / tax_unit_unemployment_compensation and the taxable_* pair. Rebased onto current main. State UI suites 47/47; taxable-UC + my_friend_ben and analytics-coverage partner signature tests unaffected (adds is inert where unemployment_compensation is a set input). See PR comment re: the baseline double-count / microsim question. Co-Authored-By: Claude Opus 4.8 --- .../issue-8301-state-ui-income-flow.added.md | 2 +- .../unemployment_insurance/integration.yaml | 24 +++++++++++++++++++ .../unemployment_insurance/integration.yaml | 24 +++++++++++++++++++ .../unemployment_insurance/integration.yaml | 24 +++++++++++++++++++ .../unemployment_insurance/integration.yaml | 24 +++++++++++++++++++ .../gov/states/unemployment_compensation.py | 2 +- 6 files changed, 98 insertions(+), 2 deletions(-) diff --git a/changelog.d/issue-8301-state-ui-income-flow.added.md b/changelog.d/issue-8301-state-ui-income-flow.added.md index e2f441e862f..4df888cf91a 100644 --- a/changelog.d/issue-8301-state-ui-income-flow.added.md +++ b/changelog.d/issue-8301-state-ui-income-flow.added.md @@ -1 +1 @@ -Integrated modeled Pennsylvania unemployment compensation into unemployment compensation income flows. +Integrated modeled Alabama, New York, Oklahoma, Pennsylvania, and Utah unemployment insurance into unemployment compensation income flows. diff --git a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/integration.yaml index 8d2581e2aae..54d92e1fa63 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/integration.yaml @@ -367,3 +367,27 @@ # so the clamp binds. Annual = 275 * 14 = 3,850 (equals MBA, so the # MBA cap is not additionally binding here). al_ui: 3_850 + +- name: Case 13, AL UI feeds unemployment compensation income flows. + # Reuses Case 1's monetarily-eligible input set (al_ui = $3,850) and asserts + # the state benefit also surfaces through the federal unemployment + # compensation income chain (unemployment_compensation adds al_ui). + period: 2024 + absolute_error_margin: 0.01 + input: + age: 35 + state_code: AL + employment_income: 100_000 + is_tax_unit_dependent: false + al_ui_high_quarter_wages: 10_000 + al_ui_second_high_quarter_wages: 10_000 + al_ui_base_period_wages: 40_000 + al_ui_quarters_with_wages: 4 + al_ui_weekly_earnings: 0 + weeks_unemployed: 20 + output: + al_ui: 3_850 + unemployment_compensation: 3_850 + tax_unit_unemployment_compensation: 3_850 + taxable_unemployment_compensation: 3_850 + tax_unit_taxable_unemployment_compensation: 3_850 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ny/dol/unemployment_insurance/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/ny/dol/unemployment_insurance/integration.yaml index 028c782f6d0..2407a921a0b 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ny/dol/unemployment_insurance/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ny/dol/unemployment_insurance/integration.yaml @@ -246,3 +246,27 @@ ny_ui_weekly_payable: 71.5 # Annual = $71.50 * 10 = $715; MBA = $143 * 26 = $3,718 (not binding). ny_ui: 715 + +- name: Case 9, NY UI feeds unemployment compensation income flows. + # Reuses Case 1's monetarily-eligible input set (ny_ui = $7,982) and asserts + # the state benefit also surfaces through the federal unemployment + # compensation income chain (unemployment_compensation adds ny_ui). + period: 2025 + absolute_error_margin: 0.01 + input: + age: 35 + state_code: NY + employment_income: 100_000 + is_tax_unit_dependent: false + ny_ui_high_quarter_wages: 8_000 + ny_ui_base_period_wages: 28_000 + ny_ui_quarters_with_wages: 4 + weeks_unemployed: 26 + ny_ui_weekly_hours_worked: 0 + ny_ui_gross_weekly_earnings: 0 + output: + ny_ui: 7_982 + unemployment_compensation: 7_982 + tax_unit_unemployment_compensation: 7_982 + taxable_unemployment_compensation: 7_982 + tax_unit_taxable_unemployment_compensation: 7_982 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ok/oesc/unemployment_insurance/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/ok/oesc/unemployment_insurance/integration.yaml index c776e6375d4..8161ab872ae 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ok/oesc/unemployment_insurance/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ok/oesc/unemployment_insurance/integration.yaml @@ -213,3 +213,27 @@ # Note: intermediate variables (eligibility, WBA, etc.) may still # compute, but the top-level ok_ui is gated to OK households only. ok_ui: 0 + +- name: Case 8, OK UI feeds unemployment compensation income flows. + # Reuses Case 1's monetarily-eligible input set (ok_ui = $8,000) and asserts + # the state benefit also surfaces through the federal unemployment + # compensation income chain (unemployment_compensation adds ok_ui). + period: 2024 + absolute_error_margin: 0.01 + input: + age: 35 + state_code: OK + employment_income: 100_000 + is_tax_unit_dependent: false + ok_ui_high_quarter_taxable_wages: 11_500 + ok_ui_high_quarter_total_wages: 11_500 + ok_ui_base_period_taxable_wages: 11_500 + ok_ui_base_period_total_wages: 35_000 + ok_ui_gross_weekly_earnings: 0 + weeks_unemployed: 16 + output: + ok_ui: 8_000 + unemployment_compensation: 8_000 + tax_unit_unemployment_compensation: 8_000 + taxable_unemployment_compensation: 8_000 + tax_unit_taxable_unemployment_compensation: 8_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ut/dws/unemployment_insurance/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/ut/dws/unemployment_insurance/integration.yaml index 36f811ebc1c..04592a1fa42 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ut/dws/unemployment_insurance/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ut/dws/unemployment_insurance/integration.yaml @@ -266,3 +266,27 @@ # weeks_unemployed (17) == duration (17): annual = $395 * 17 = $6,715. # min($6,715, MBA $6,715) = $6,715 -> cap binds exactly at equality. ut_ui: 6_715 + +- name: Case 9, UT UI feeds unemployment compensation income flows. + # Reuses Case 2's monetarily-eligible input set (ut_ui = $19,396) and asserts + # the state benefit also surfaces through the federal unemployment + # compensation income chain (unemployment_compensation adds ut_ui). + period: 2024 + absolute_error_margin: 0.01 + input: + age: 35 + state_code: UT + employment_income: 100_000 + is_tax_unit_dependent: false + ut_ui_base_period_wages: 80_000 + ut_ui_high_quarter_wages: 25_000 + ut_ui_quarters_with_wages: 4 + ut_ui_weeks_unemployed: 26 + ut_ui_gross_weekly_earnings: 0 + ut_ui_weekly_hours_worked: 0 + output: + ut_ui: 19_396 + unemployment_compensation: 19_396 + tax_unit_unemployment_compensation: 19_396 + taxable_unemployment_compensation: 19_396 + tax_unit_taxable_unemployment_compensation: 19_396 diff --git a/policyengine_us/variables/gov/states/unemployment_compensation.py b/policyengine_us/variables/gov/states/unemployment_compensation.py index 834fbc05c49..229ee61fcb1 100644 --- a/policyengine_us/variables/gov/states/unemployment_compensation.py +++ b/policyengine_us/variables/gov/states/unemployment_compensation.py @@ -9,4 +9,4 @@ class unemployment_compensation(Variable): documentation = "Income from unemployment compensation programs." definition_period = YEAR uprating = "calibration.gov.irs.soi.unemployment_compensation" - adds = ["pa_uc"] + adds = ["al_ui", "ny_ui", "ok_ui", "pa_uc", "ut_ui"] From f4aa5e9b59c7654ac99559e924b712096bec2571 Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Mon, 21 Sep 2026 12:21:51 -0400 Subject: [PATCH 3/3] Fix computation-mode conflict: split modeled UI from the uprated input Restores unemployment_compensation as the uprated reported input, adds modeled_state_unemployment_compensation aggregating the five state UI programs, and routes income flows through a total_unemployment_ compensation fallback (reported where present, else modeled), per review. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01LqCGPgxDukji8Bhs57c5rH --- .../unemployment_insurance/integration.yaml | 52 +++++++++++++++++-- .../unemployment_insurance/integration.yaml | 47 +++++++++++++++-- .../unemployment_insurance/integration.yaml | 47 +++++++++++++++-- .../integration.yaml | 44 +++++++++++++++- .../unemployment_insurance/integration.yaml | 47 +++++++++++++++-- .../variables/contrib/taxsim/taxsim_pui.py | 2 +- .../variables/contrib/taxsim/taxsim_ui.py | 2 +- .../gov/irs/income/dependent_gross_income.py | 2 +- .../capped_qualified_tuition_expenses_ald.py | 2 +- .../social_security/taxable_ss_magi.py | 2 +- .../tax_exempt_unemployment_compensation.py | 2 +- .../tax_unit_unemployment_compensation.py | 2 +- .../taxable_unemployment_insurance.py | 6 +-- ...a_cc_general_assistance_eligible_person.py | 2 +- .../ar_medicaid_work_requirement_eligible.py | 2 +- ...modeled_state_unemployment_compensation.py | 14 +++++ .../gov/states/unemployment_compensation.py | 1 - ...meowner_renter_relief_nontaxable_income.py | 2 +- ...s_snap_work_registration_exempt_non_age.py | 2 +- .../person/total_unemployment_compensation.py | 19 +++++++ .../income/spm_unit/spm_unit_benefits.py | 2 +- 21 files changed, 272 insertions(+), 29 deletions(-) create mode 100644 policyengine_us/variables/gov/states/modeled_state_unemployment_compensation.py create mode 100644 policyengine_us/variables/household/income/person/total_unemployment_compensation.py diff --git a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/integration.yaml index 54d92e1fa63..a7629833204 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/integration.yaml @@ -370,8 +370,8 @@ - name: Case 13, AL UI feeds unemployment compensation income flows. # Reuses Case 1's monetarily-eligible input set (al_ui = $3,850) and asserts - # the state benefit also surfaces through the federal unemployment - # compensation income chain (unemployment_compensation adds al_ui). + # the state benefit surfaces through the total unemployment compensation + # income chain (modeled fallback: no reported UC, so total = modeled al_ui). period: 2024 absolute_error_margin: 0.01 input: @@ -387,7 +387,53 @@ weeks_unemployed: 20 output: al_ui: 3_850 - unemployment_compensation: 3_850 + # No reported unemployment_compensation input -> fallback to modeled. + modeled_state_unemployment_compensation: 3_850 + total_unemployment_compensation: 3_850 tax_unit_unemployment_compensation: 3_850 taxable_unemployment_compensation: 3_850 tax_unit_taxable_unemployment_compensation: 3_850 + +- name: Case 14, reported UC preserved when present (no state UI inputs). + # Reported unemployment_compensation is supplied and no AL UI wage inputs are + # given, so total_unemployment_compensation equals the reported amount. + period: 2024 + absolute_error_margin: 0.01 + input: + age: 35 + state_code: AL + employment_income: 100_000 + is_tax_unit_dependent: false + unemployment_compensation: 5_000 + output: + al_ui: 0 + modeled_state_unemployment_compensation: 0 + unemployment_compensation: 5_000 + total_unemployment_compensation: 5_000 + tax_unit_unemployment_compensation: 5_000 + +- name: Case 15, reported UC wins over modeled - no double count. + # Both reported unemployment_compensation and AL UI wage inputs are present. + # total_unemployment_compensation uses the reported amount only (does not + # add reported + modeled), proving the fallback prevents double counting. + period: 2024 + absolute_error_margin: 0.01 + input: + age: 35 + state_code: AL + employment_income: 100_000 + is_tax_unit_dependent: false + unemployment_compensation: 5_000 + al_ui_high_quarter_wages: 10_000 + al_ui_second_high_quarter_wages: 10_000 + al_ui_base_period_wages: 40_000 + al_ui_quarters_with_wages: 4 + al_ui_weekly_earnings: 0 + weeks_unemployed: 20 + output: + al_ui: 3_850 + modeled_state_unemployment_compensation: 3_850 + unemployment_compensation: 5_000 + # Reported wins; total is 5,000 (not 5,000 + 3,850). + total_unemployment_compensation: 5_000 + tax_unit_unemployment_compensation: 5_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ny/dol/unemployment_insurance/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/ny/dol/unemployment_insurance/integration.yaml index 2407a921a0b..1828677f567 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ny/dol/unemployment_insurance/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ny/dol/unemployment_insurance/integration.yaml @@ -249,8 +249,8 @@ - name: Case 9, NY UI feeds unemployment compensation income flows. # Reuses Case 1's monetarily-eligible input set (ny_ui = $7,982) and asserts - # the state benefit also surfaces through the federal unemployment - # compensation income chain (unemployment_compensation adds ny_ui). + # the state benefit surfaces through the total unemployment compensation + # income chain (modeled fallback: no reported UC, so total = modeled ny_ui). period: 2025 absolute_error_margin: 0.01 input: @@ -266,7 +266,48 @@ ny_ui_gross_weekly_earnings: 0 output: ny_ui: 7_982 - unemployment_compensation: 7_982 + # No reported unemployment_compensation input -> fallback to modeled. + modeled_state_unemployment_compensation: 7_982 + total_unemployment_compensation: 7_982 tax_unit_unemployment_compensation: 7_982 taxable_unemployment_compensation: 7_982 tax_unit_taxable_unemployment_compensation: 7_982 + +- name: Case 10, reported UC preserved when present (no NY UI inputs). + period: 2025 + absolute_error_margin: 0.01 + input: + age: 35 + state_code: NY + employment_income: 100_000 + is_tax_unit_dependent: false + unemployment_compensation: 4_500 + output: + ny_ui: 0 + modeled_state_unemployment_compensation: 0 + unemployment_compensation: 4_500 + total_unemployment_compensation: 4_500 + tax_unit_unemployment_compensation: 4_500 + +- name: Case 11, reported UC wins over modeled NY UI - no double count. + period: 2025 + absolute_error_margin: 0.01 + input: + age: 35 + state_code: NY + employment_income: 100_000 + is_tax_unit_dependent: false + unemployment_compensation: 4_500 + ny_ui_high_quarter_wages: 8_000 + ny_ui_base_period_wages: 28_000 + ny_ui_quarters_with_wages: 4 + weeks_unemployed: 26 + ny_ui_weekly_hours_worked: 0 + ny_ui_gross_weekly_earnings: 0 + output: + ny_ui: 7_982 + modeled_state_unemployment_compensation: 7_982 + unemployment_compensation: 4_500 + # Reported wins; total is 4,500 (not 4,500 + 7,982). + total_unemployment_compensation: 4_500 + tax_unit_unemployment_compensation: 4_500 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ok/oesc/unemployment_insurance/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/ok/oesc/unemployment_insurance/integration.yaml index 8161ab872ae..3d1996a506d 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ok/oesc/unemployment_insurance/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ok/oesc/unemployment_insurance/integration.yaml @@ -216,8 +216,8 @@ - name: Case 8, OK UI feeds unemployment compensation income flows. # Reuses Case 1's monetarily-eligible input set (ok_ui = $8,000) and asserts - # the state benefit also surfaces through the federal unemployment - # compensation income chain (unemployment_compensation adds ok_ui). + # the state benefit surfaces through the total unemployment compensation + # income chain (modeled fallback: no reported UC, so total = modeled ok_ui). period: 2024 absolute_error_margin: 0.01 input: @@ -233,7 +233,48 @@ weeks_unemployed: 16 output: ok_ui: 8_000 - unemployment_compensation: 8_000 + # No reported unemployment_compensation input -> fallback to modeled. + modeled_state_unemployment_compensation: 8_000 + total_unemployment_compensation: 8_000 tax_unit_unemployment_compensation: 8_000 taxable_unemployment_compensation: 8_000 tax_unit_taxable_unemployment_compensation: 8_000 + +- name: Case 9, reported UC preserved when present (no OK UI inputs). + period: 2024 + absolute_error_margin: 0.01 + input: + age: 35 + state_code: OK + employment_income: 100_000 + is_tax_unit_dependent: false + unemployment_compensation: 3_200 + output: + ok_ui: 0 + modeled_state_unemployment_compensation: 0 + unemployment_compensation: 3_200 + total_unemployment_compensation: 3_200 + tax_unit_unemployment_compensation: 3_200 + +- name: Case 10, reported UC wins over modeled OK UI - no double count. + period: 2024 + absolute_error_margin: 0.01 + input: + age: 35 + state_code: OK + employment_income: 100_000 + is_tax_unit_dependent: false + unemployment_compensation: 3_200 + ok_ui_high_quarter_taxable_wages: 11_500 + ok_ui_high_quarter_total_wages: 11_500 + ok_ui_base_period_taxable_wages: 11_500 + ok_ui_base_period_total_wages: 35_000 + ok_ui_gross_weekly_earnings: 0 + weeks_unemployed: 16 + output: + ok_ui: 8_000 + modeled_state_unemployment_compensation: 8_000 + unemployment_compensation: 3_200 + # Reported wins; total is 3,200 (not 3,200 + 8,000). + total_unemployment_compensation: 3_200 + tax_unit_unemployment_compensation: 3_200 diff --git a/policyengine_us/tests/policy/baseline/gov/states/pa/dli/unemployment_compensation/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/pa/dli/unemployment_compensation/integration.yaml index ff8b2073b05..d7c5becc519 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/pa/dli/unemployment_compensation/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/pa/dli/unemployment_compensation/integration.yaml @@ -279,9 +279,51 @@ pa_uc_gross_weekly_earnings: 0 output: pa_uc: 10_218 - unemployment_compensation: 10_218 + # No reported unemployment_compensation input -> fallback to modeled. + modeled_state_unemployment_compensation: 10_218 + total_unemployment_compensation: 10_218 tax_unit_unemployment_compensation: 10_218 taxable_unemployment_compensation: 10_218 tax_unit_taxable_unemployment_compensation: 10_218 spm_unit_benefits: 10_218 household_benefits: 10_218 + +- name: Case 9, reported UC preserved when present (no PA UC inputs). + period: 2025 + absolute_error_margin: 0.01 + input: + age: 35 + state_code: PA + employment_income: 100_000 + is_tax_unit_dependent: false + unemployment_compensation: 6_000 + output: + pa_uc: 0 + modeled_state_unemployment_compensation: 0 + unemployment_compensation: 6_000 + total_unemployment_compensation: 6_000 + tax_unit_unemployment_compensation: 6_000 + spm_unit_benefits: 6_000 + +- name: Case 10, reported UC wins over modeled PA UC - no double count. + period: 2025 + absolute_error_margin: 0.01 + input: + age: 35 + state_code: PA + employment_income: 100_000 + is_tax_unit_dependent: false + unemployment_compensation: 6_000 + pa_uc_highest_quarter_wages: 10_000 + pa_uc_base_year_wages: 28_000 + pa_uc_credit_weeks: 26 + weeks_unemployed: 26 + pa_uc_gross_weekly_earnings: 0 + output: + pa_uc: 10_218 + modeled_state_unemployment_compensation: 10_218 + unemployment_compensation: 6_000 + # Reported wins; total is 6,000 (not 6,000 + 10,218). + total_unemployment_compensation: 6_000 + tax_unit_unemployment_compensation: 6_000 + spm_unit_benefits: 6_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ut/dws/unemployment_insurance/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/ut/dws/unemployment_insurance/integration.yaml index 04592a1fa42..28ae0891c86 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ut/dws/unemployment_insurance/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ut/dws/unemployment_insurance/integration.yaml @@ -269,8 +269,8 @@ - name: Case 9, UT UI feeds unemployment compensation income flows. # Reuses Case 2's monetarily-eligible input set (ut_ui = $19,396) and asserts - # the state benefit also surfaces through the federal unemployment - # compensation income chain (unemployment_compensation adds ut_ui). + # the state benefit surfaces through the total unemployment compensation + # income chain (modeled fallback: no reported UC, so total = modeled ut_ui). period: 2024 absolute_error_margin: 0.01 input: @@ -286,7 +286,48 @@ ut_ui_weekly_hours_worked: 0 output: ut_ui: 19_396 - unemployment_compensation: 19_396 + # No reported unemployment_compensation input -> fallback to modeled. + modeled_state_unemployment_compensation: 19_396 + total_unemployment_compensation: 19_396 tax_unit_unemployment_compensation: 19_396 taxable_unemployment_compensation: 19_396 tax_unit_taxable_unemployment_compensation: 19_396 + +- name: Case 10, reported UC preserved when present (no UT UI inputs). + period: 2024 + absolute_error_margin: 0.01 + input: + age: 35 + state_code: UT + employment_income: 100_000 + is_tax_unit_dependent: false + unemployment_compensation: 12_000 + output: + ut_ui: 0 + modeled_state_unemployment_compensation: 0 + unemployment_compensation: 12_000 + total_unemployment_compensation: 12_000 + tax_unit_unemployment_compensation: 12_000 + +- name: Case 11, reported UC wins over modeled UT UI - no double count. + period: 2024 + absolute_error_margin: 0.01 + input: + age: 35 + state_code: UT + employment_income: 100_000 + is_tax_unit_dependent: false + unemployment_compensation: 12_000 + ut_ui_base_period_wages: 80_000 + ut_ui_high_quarter_wages: 25_000 + ut_ui_quarters_with_wages: 4 + ut_ui_weeks_unemployed: 26 + ut_ui_gross_weekly_earnings: 0 + ut_ui_weekly_hours_worked: 0 + output: + ut_ui: 19_396 + modeled_state_unemployment_compensation: 19_396 + unemployment_compensation: 12_000 + # Reported wins; total is 12,000 (not 12,000 + 19,396). + total_unemployment_compensation: 12_000 + tax_unit_unemployment_compensation: 12_000 diff --git a/policyengine_us/variables/contrib/taxsim/taxsim_pui.py b/policyengine_us/variables/contrib/taxsim/taxsim_pui.py index 4c2e77569d6..cad57b42fdb 100644 --- a/policyengine_us/variables/contrib/taxsim/taxsim_pui.py +++ b/policyengine_us/variables/contrib/taxsim/taxsim_pui.py @@ -12,5 +12,5 @@ class taxsim_pui(Variable): def formula(tax_unit, period, parameters): person = tax_unit.members is_primary = person("is_tax_unit_head", period) - unemployment_compensation = person("unemployment_compensation", period) + unemployment_compensation = person("total_unemployment_compensation", period) return tax_unit.sum(unemployment_compensation * is_primary) diff --git a/policyengine_us/variables/contrib/taxsim/taxsim_ui.py b/policyengine_us/variables/contrib/taxsim/taxsim_ui.py index c92f53b750d..75efacb838a 100644 --- a/policyengine_us/variables/contrib/taxsim/taxsim_ui.py +++ b/policyengine_us/variables/contrib/taxsim/taxsim_ui.py @@ -12,5 +12,5 @@ class taxsim_ui(Variable): def formula(tax_unit, period, parameters): person = tax_unit.members is_spouse = person("is_tax_unit_spouse", period) - unemployment_compensation = person("unemployment_compensation", period) + unemployment_compensation = person("total_unemployment_compensation", period) return tax_unit.sum(unemployment_compensation * is_spouse) diff --git a/policyengine_us/variables/gov/irs/income/dependent_gross_income.py b/policyengine_us/variables/gov/irs/income/dependent_gross_income.py index 011fadabc3d..d0193c97ffd 100644 --- a/policyengine_us/variables/gov/irs/income/dependent_gross_income.py +++ b/policyengine_us/variables/gov/irs/income/dependent_gross_income.py @@ -2,7 +2,7 @@ # Sources that depend on filing_status and would cause circular dependencies CIRCULAR_SOURCES = { - "taxable_unemployment_compensation": "unemployment_compensation", + "taxable_unemployment_compensation": "total_unemployment_compensation", "taxable_social_security": "social_security", } diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/capped_qualified_tuition_expenses_ald.py b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/capped_qualified_tuition_expenses_ald.py index 5ec84daea9a..7270a9cce57 100644 --- a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/capped_qualified_tuition_expenses_ald.py +++ b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/capped_qualified_tuition_expenses_ald.py @@ -44,7 +44,7 @@ def formula(tax_unit, period, parameters): } safe_sources = [src for src in gross_income_sources if src not in cycle_sources] if "taxable_unemployment_compensation" in gross_income_sources: - safe_sources.append("unemployment_compensation") + safe_sources.append("total_unemployment_compensation") magi = 0 for source in safe_sources: magi += not_dependent * max_(0, add(person, period, [source])) diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/taxable_ss_magi.py b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/taxable_ss_magi.py index 0e8a6beb1fb..6ca9ee87251 100644 --- a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/taxable_ss_magi.py +++ b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/taxable_ss_magi.py @@ -26,7 +26,7 @@ def formula(tax_unit, period, parameters): if "taxable_unemployment_compensation" in gross_income_sources: # Reforms which drop the UI variable from gross income should # trigger SS-related MAGI to drop it too. - income_sources_without_ss.append("unemployment_compensation") + income_sources_without_ss.append("total_unemployment_compensation") income_sources_without_ss.append("tax_exempt_interest_income") gross_income = 0 person = tax_unit.members diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/unemployment_insurance/tax_exempt_unemployment_compensation.py b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/unemployment_insurance/tax_exempt_unemployment_compensation.py index 6a0cfa3542d..3aac62db317 100644 --- a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/unemployment_insurance/tax_exempt_unemployment_compensation.py +++ b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/unemployment_insurance/tax_exempt_unemployment_compensation.py @@ -8,5 +8,5 @@ class tax_exempt_unemployment_compensation(Variable): unit = USD definition_period = YEAR - adds = ["unemployment_compensation"] + adds = ["total_unemployment_compensation"] subtracts = ["taxable_unemployment_compensation"] diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/unemployment_insurance/tax_unit_unemployment_compensation.py b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/unemployment_insurance/tax_unit_unemployment_compensation.py index 5a89006fb1f..9f32516cc60 100644 --- a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/unemployment_insurance/tax_unit_unemployment_compensation.py +++ b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/unemployment_insurance/tax_unit_unemployment_compensation.py @@ -9,4 +9,4 @@ class tax_unit_unemployment_compensation(Variable): documentation = "Combined unemployment compensation for the tax unit." definition_period = YEAR - adds = ["unemployment_compensation"] + adds = ["total_unemployment_compensation"] diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/unemployment_insurance/taxable_unemployment_insurance.py b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/unemployment_insurance/taxable_unemployment_insurance.py index 707baf719c0..e4d4aa53eed 100644 --- a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/unemployment_insurance/taxable_unemployment_insurance.py +++ b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/unemployment_insurance/taxable_unemployment_insurance.py @@ -12,12 +12,12 @@ def formula(person, period, parameters): # The taxable amount of unemployment compensation is decided at the tax # unit level, but gross income (which contains taxable UI) is person-level. # Allocate the tax unit's taxable UC to each person in proportion to their - # unemployment_compensation, so that per-person AGI reflects the actual - # recipient (required by states with combined-return optimization). + # total_unemployment_compensation, so that per-person AGI reflects the + # actual recipient (required by states with combined-return optimization). tax_unit_taxable_uc = person.tax_unit( "tax_unit_taxable_unemployment_compensation", period ) - person_uc = person("unemployment_compensation", period) + person_uc = person("total_unemployment_compensation", period) tax_unit_uc = person.tax_unit.sum(person_uc) share = where(tax_unit_uc > 0, person_uc / tax_unit_uc, 0) return tax_unit_taxable_uc * share diff --git a/policyengine_us/variables/gov/local/ca/cc/ga/eligibility/ca_cc_general_assistance_eligible_person.py b/policyengine_us/variables/gov/local/ca/cc/ga/eligibility/ca_cc_general_assistance_eligible_person.py index fb272acefb0..9291a31648c 100644 --- a/policyengine_us/variables/gov/local/ca/cc/ga/eligibility/ca_cc_general_assistance_eligible_person.py +++ b/policyengine_us/variables/gov/local/ca/cc/ga/eligibility/ca_cc_general_assistance_eligible_person.py @@ -28,7 +28,7 @@ def formula(person, period, parameters): (person("ssi", period) > 0) | person("receives_ssi", period) | (person("social_security_disability", period.this_year) > 0) - | (person("unemployment_compensation", period.this_year) > 0) + | (person("total_unemployment_compensation", period.this_year) > 0) | (person("ca_state_disability_insurance", period.this_year) > 0) ) is_head_or_spouse = person("is_tax_unit_head_or_spouse", period) diff --git a/policyengine_us/variables/gov/states/ar/dhs/medicaid/eligibility/ar_medicaid_work_requirement_eligible.py b/policyengine_us/variables/gov/states/ar/dhs/medicaid/eligibility/ar_medicaid_work_requirement_eligible.py index c19d822191b..33d9cba04ba 100644 --- a/policyengine_us/variables/gov/states/ar/dhs/medicaid/eligibility/ar_medicaid_work_requirement_eligible.py +++ b/policyengine_us/variables/gov/states/ar/dhs/medicaid/eligibility/ar_medicaid_work_requirement_eligible.py @@ -42,7 +42,7 @@ def formula(person, period, parameters): is_dependent & (is_disabled | is_blind | is_incapable_of_self_care) ) receives_unemployment_compensation = ( - person("unemployment_compensation", period) > 0 + person("total_unemployment_compensation", period) > 0 ) eligible_disabled = is_disabled | is_blind | is_incapable_of_self_care diff --git a/policyengine_us/variables/gov/states/modeled_state_unemployment_compensation.py b/policyengine_us/variables/gov/states/modeled_state_unemployment_compensation.py new file mode 100644 index 00000000000..e04236ad2e4 --- /dev/null +++ b/policyengine_us/variables/gov/states/modeled_state_unemployment_compensation.py @@ -0,0 +1,14 @@ +from policyengine_us.model_api import * + + +class modeled_state_unemployment_compensation(Variable): + value_type = float + entity = Person + label = "modeled state unemployment compensation" + unit = USD + documentation = ( + "Unemployment compensation computed from the modeled state " + "unemployment insurance programs." + ) + definition_period = YEAR + adds = ["al_ui", "ny_ui", "ok_ui", "pa_uc", "ut_ui"] diff --git a/policyengine_us/variables/gov/states/unemployment_compensation.py b/policyengine_us/variables/gov/states/unemployment_compensation.py index 229ee61fcb1..0857864ab17 100644 --- a/policyengine_us/variables/gov/states/unemployment_compensation.py +++ b/policyengine_us/variables/gov/states/unemployment_compensation.py @@ -9,4 +9,3 @@ class unemployment_compensation(Variable): documentation = "Income from unemployment compensation programs." definition_period = YEAR uprating = "calibration.gov.irs.soi.unemployment_compensation" - adds = ["al_ui", "ny_ui", "ok_ui", "pa_uc", "ut_ui"] diff --git a/policyengine_us/variables/gov/states/ut/tax/property/homeowner_renter_relief/ut_homeowner_renter_relief_nontaxable_income.py b/policyengine_us/variables/gov/states/ut/tax/property/homeowner_renter_relief/ut_homeowner_renter_relief_nontaxable_income.py index c883faddaf8..e658c62eba4 100644 --- a/policyengine_us/variables/gov/states/ut/tax/property/homeowner_renter_relief/ut_homeowner_renter_relief_nontaxable_income.py +++ b/policyengine_us/variables/gov/states/ut/tax/property/homeowner_renter_relief/ut_homeowner_renter_relief_nontaxable_income.py @@ -21,7 +21,7 @@ def formula(person, period, parameters): nontaxable_social_security = max_( 0, social_security - ssdi - taxable_social_security ) - unemployment_compensation = person("unemployment_compensation", period) + unemployment_compensation = person("total_unemployment_compensation", period) taxable_unemployment_compensation = person( "taxable_unemployment_compensation", period ) diff --git a/policyengine_us/variables/gov/usda/snap/eligibility/work_requirements/is_snap_work_registration_exempt_non_age.py b/policyengine_us/variables/gov/usda/snap/eligibility/work_requirements/is_snap_work_registration_exempt_non_age.py index 257b5ee023e..5fea24b639e 100644 --- a/policyengine_us/variables/gov/usda/snap/eligibility/work_requirements/is_snap_work_registration_exempt_non_age.py +++ b/policyengine_us/variables/gov/usda/snap/eligibility/work_requirements/is_snap_work_registration_exempt_non_age.py @@ -45,7 +45,7 @@ def formula(person, period, parameters): # Simplification: any UC receipt during the year exempts the person # in all months of that year, since survey data lack monthly UC # receipt histories. - receiving_ui = person("unemployment_compensation", period.this_year) > 0 + receiving_ui = person("total_unemployment_compensation", period.this_year) > 0 applied_for_ui = person("has_applied_for_unemployment_compensation", period) # (vi) Regular participant in a drug addiction or alcoholic # treatment and rehabilitation program — 7 CFR 273.7(b)(1)(vi). diff --git a/policyengine_us/variables/household/income/person/total_unemployment_compensation.py b/policyengine_us/variables/household/income/person/total_unemployment_compensation.py new file mode 100644 index 00000000000..1d65430c02c --- /dev/null +++ b/policyengine_us/variables/household/income/person/total_unemployment_compensation.py @@ -0,0 +1,19 @@ +from policyengine_us.model_api import * + + +class total_unemployment_compensation(Variable): + value_type = float + entity = Person + label = "total unemployment compensation" + unit = USD + documentation = ( + "Unemployment compensation used in downstream income flows, using " + "the reported (uprated) amount when present and otherwise falling " + "back to the modeled state unemployment insurance programs." + ) + definition_period = YEAR + + def formula(person, period, parameters): + reported = person("unemployment_compensation", period) + modeled = person("modeled_state_unemployment_compensation", period) + return where(reported > 0, reported, modeled) diff --git a/policyengine_us/variables/household/income/spm_unit/spm_unit_benefits.py b/policyengine_us/variables/household/income/spm_unit/spm_unit_benefits.py index 78437a73d86..0c6967eab3d 100644 --- a/policyengine_us/variables/household/income/spm_unit/spm_unit_benefits.py +++ b/policyengine_us/variables/household/income/spm_unit/spm_unit_benefits.py @@ -68,7 +68,7 @@ def formula(spm_unit, period, parameters): "wa_rca", "high_efficiency_electric_home_rebate", "residential_efficiency_electrification_rebate", - "unemployment_compensation", + "total_unemployment_compensation", # One-time energy relief payments. # Paid at the same time as the Alaska Permanent Fund Dividend, # which is part of IRS gross income.