Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/issue-8301-state-ui-income-flow.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Integrated modeled Alabama, New York, Oklahoma, Pennsylvania, and Utah unemployment insurance into unemployment compensation income flows.
Original file line number Diff line number Diff line change
Expand Up @@ -367,3 +367,73 @@
# 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 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:
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
# 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
Original file line number Diff line number Diff line change
Expand Up @@ -246,3 +246,68 @@
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 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:
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
# 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
Original file line number Diff line number Diff line change
Expand Up @@ -213,3 +213,68 @@
# 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 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:
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
# 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
Original file line number Diff line number Diff line change
Expand Up @@ -263,3 +263,67 @@
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
# 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
Original file line number Diff line number Diff line change
Expand Up @@ -266,3 +266,68 @@
# 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 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:
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
# 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
2 changes: 1 addition & 1 deletion policyengine_us/variables/contrib/taxsim/taxsim_pui.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
2 changes: 1 addition & 1 deletion policyengine_us/variables/contrib/taxsim/taxsim_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Original file line number Diff line number Diff line change
Expand Up @@ -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",
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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]))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Loading
Loading