diff --git a/changelog_entry.yaml b/changelog_entry.yaml index e69de29bb2d..9a320807cb1 100644 --- a/changelog_entry.yaml +++ b/changelog_entry.yaml @@ -0,0 +1,4 @@ +- bump: minor + changes: + added: + - Utah refundable EITC contributed reform. diff --git a/policyengine_us/parameters/gov/contrib/states/ut/eitc/in_effect.yaml b/policyengine_us/parameters/gov/contrib/states/ut/eitc/in_effect.yaml new file mode 100644 index 00000000000..9eec3ac00a2 --- /dev/null +++ b/policyengine_us/parameters/gov/contrib/states/ut/eitc/in_effect.yaml @@ -0,0 +1,9 @@ +description: The Utah EITC is refundable for households with children below a certain age when this parameter is in effect. + +values: + 0000-01-01: false + +metadata: + unit: bool + period: year + label: Utah refundable EITC in effect diff --git a/policyengine_us/parameters/gov/contrib/states/ut/eitc/max_age.yaml b/policyengine_us/parameters/gov/contrib/states/ut/eitc/max_age.yaml new file mode 100644 index 00000000000..f36387177a2 --- /dev/null +++ b/policyengine_us/parameters/gov/contrib/states/ut/eitc/max_age.yaml @@ -0,0 +1,9 @@ +description: The maximum age for a child to qualify the household for the refundable Utah EITC. + +values: + 0000-01-01: 2 + +metadata: + unit: year + period: year + label: Utah refundable EITC maximum child age diff --git a/policyengine_us/reforms/reforms.py b/policyengine_us/reforms/reforms.py index 3d2f0bd900f..3dd9cc40e85 100644 --- a/policyengine_us/reforms/reforms.py +++ b/policyengine_us/reforms/reforms.py @@ -100,6 +100,9 @@ from .states.mi.surtax import ( create_mi_surtax_reform, ) +from .states.ut import ( + create_ut_refundable_eitc_reform, +) from .additional_tax_bracket import ( create_additional_tax_bracket_reform, ) @@ -248,6 +251,7 @@ def create_structural_reforms_from_parameters(parameters, period): parameters, period ) mi_surtax = create_mi_surtax_reform(parameters, period) + ut_refundable_eitc = create_ut_refundable_eitc_reform(parameters, period) american_worker_rebate_act = create_american_worker_rebate_act_reform( parameters, period @@ -315,6 +319,7 @@ def create_structural_reforms_from_parameters(parameters, period): reconciled_ssn_for_llc_and_aoc, ctc_additional_bracket, mi_surtax, + ut_refundable_eitc, additional_tax_bracket, american_worker_rebate_act, ctc_per_child_phase_out, diff --git a/policyengine_us/reforms/states/ut/__init__.py b/policyengine_us/reforms/states/ut/__init__.py new file mode 100644 index 00000000000..ea904eaf08d --- /dev/null +++ b/policyengine_us/reforms/states/ut/__init__.py @@ -0,0 +1,3 @@ +from .ut_refundable_eitc import ( + create_ut_refundable_eitc_reform, +) diff --git a/policyengine_us/reforms/states/ut/ut_refundable_eitc.py b/policyengine_us/reforms/states/ut/ut_refundable_eitc.py new file mode 100644 index 00000000000..fd1b4303655 --- /dev/null +++ b/policyengine_us/reforms/states/ut/ut_refundable_eitc.py @@ -0,0 +1,118 @@ +from policyengine_us.model_api import * +from policyengine_core.periods import period as period_ + + +def create_ut_refundable_eitc() -> Reform: + class ut_has_qualifying_child_for_refundable_eitc(Variable): + value_type = bool + entity = TaxUnit + label = "Utah tax unit has qualifying child for refundable EITC" + defined_for = StateCode.UT + definition_period = YEAR + + def formula(tax_unit, period, parameters): + person = tax_unit.members + p = parameters(period).gov.contrib.states.ut.eitc + age = person("age", period) + is_dependent = person("is_tax_unit_dependent", period) + is_qualifying_child = (age <= p.max_age) & is_dependent + return tax_unit.any(is_qualifying_child) + + class ut_refundable_eitc(Variable): + value_type = float + entity = TaxUnit + label = "Utah refundable EITC" + unit = USD + definition_period = YEAR + defined_for = "ut_has_qualifying_child_for_refundable_eitc" + + adds = ["ut_eitc"] + + class ut_eitc(Variable): + value_type = float + entity = TaxUnit + label = "Utah Earned Income Tax Credit" + unit = USD + documentation = "This credit is a fraction of the federal EITC." + definition_period = YEAR + defined_for = StateCode.UT + reference = "https://le.utah.gov/xcode/Title59/Chapter10/59-10-S1044.html?v=C59-10-S1044_2022050420220504" + + def formula(tax_unit, period, parameters): + p = parameters( + period + ).gov.states.ut.tax.income.credits.earned_income + federal_eitc = tax_unit("eitc", period) + return p.rate * federal_eitc + + class ut_non_refundable_eitc(Variable): + value_type = float + entity = TaxUnit + label = "Utah non-refundable EITC" + unit = USD + definition_period = YEAR + defined_for = StateCode.UT + + adds = ["ut_eitc"] + subtracts = ["ut_refundable_eitc"] + + class ut_non_refundable_credits(Variable): + value_type = float + entity = TaxUnit + label = "Utah non-refundable tax credits" + unit = USD + definition_period = YEAR + defined_for = StateCode.UT + + adds = [ + "ut_non_refundable_eitc", + "ut_retirement_credit", + "ut_ss_benefits_credit", + "ut_at_home_parent_credit", + "ut_ctc", + ] + + class ut_refundable_credits(Variable): + value_type = float + entity = TaxUnit + label = "Utah refundable credits" + unit = USD + definition_period = YEAR + defined_for = StateCode.UT + + adds = ["ut_refundable_eitc"] + + class reform(Reform): + def apply(self): + self.add_variable(ut_has_qualifying_child_for_refundable_eitc) + self.add_variable(ut_refundable_eitc) + self.add_variable(ut_non_refundable_eitc) + self.update_variable(ut_eitc) + self.update_variable(ut_non_refundable_credits) + self.update_variable(ut_refundable_credits) + + return reform + + +def create_ut_refundable_eitc_reform(parameters, period, bypass: bool = False): + if bypass: + return create_ut_refundable_eitc() + + p = parameters.gov.contrib.states.ut.eitc + + reform_active = False + current_period = period_(period) + + for i 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_ut_refundable_eitc() + else: + return None + + +ut_refundable_eitc = create_ut_refundable_eitc_reform(None, None, bypass=True) diff --git a/policyengine_us/tests/policy/contrib/states/ut/ut_refundable_eitc.yaml b/policyengine_us/tests/policy/contrib/states/ut/ut_refundable_eitc.yaml new file mode 100644 index 00000000000..b8ec489fc5d --- /dev/null +++ b/policyengine_us/tests/policy/contrib/states/ut/ut_refundable_eitc.yaml @@ -0,0 +1,214 @@ +- name: Single parent with child under max age - full EITC refundable + period: 2024 + reforms: policyengine_us.reforms.states.ut.ut_refundable_eitc.ut_refundable_eitc + input: + gov.contrib.states.ut.eitc.in_effect: true + people: + parent: + age: 30 + child: + age: 2 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [parent, child] + eitc: 5_000 + households: + household: + members: [parent, child] + state_code: UT + output: + # Utah EITC = 20% of federal EITC = 0.2 * 5_000 = 1_000 + # Since child is age 2 (at max_age threshold), full amount is refundable + ut_eitc: 1_000 # Total EITC + ut_refundable_eitc: 1_000 + ut_non_refundable_eitc: 0 # Non-refundable portion is reduced to zero + ut_has_qualifying_child_for_refundable_eitc: true + +- name: Single parent with child at age 1 + period: 2024 + reforms: policyengine_us.reforms.states.ut.ut_refundable_eitc.ut_refundable_eitc + input: + gov.contrib.states.ut.eitc.in_effect: true + people: + parent: + age: 30 + child: + age: 1 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [parent, child] + eitc: 3_000 + households: + household: + members: [parent, child] + state_code: UT + output: + # Utah EITC = 20% of federal EITC = 0.2 * 3_000 = 600 + ut_eitc: 600 + ut_refundable_eitc: 600 + ut_non_refundable_eitc: 0 + ut_has_qualifying_child_for_refundable_eitc: true + +- name: Single parent with child age 3 - not eligible for refundable EITC + period: 2024 + reforms: policyengine_us.reforms.states.ut.ut_refundable_eitc.ut_refundable_eitc + input: + gov.contrib.states.ut.eitc.in_effect: true + people: + parent: + age: 30 + child: + age: 3 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [parent, child] + eitc: 4_000 + households: + household: + members: [parent, child] + state_code: UT + output: + # Child is too old (max_age is 2) + # Utah EITC = 0.2 * 4_000 = 800 + ut_eitc: 800 + ut_refundable_eitc: 0 + ut_non_refundable_eitc: 800 # Remains fully non-refundable + ut_has_qualifying_child_for_refundable_eitc: false + +- name: Single parent with multiple children, one qualifying + period: 2024 + reforms: policyengine_us.reforms.states.ut.ut_refundable_eitc.ut_refundable_eitc + input: + gov.contrib.states.ut.eitc.in_effect: true + people: + parent: + age: 35 + child1: + age: 1 + is_tax_unit_dependent: true + child2: + age: 5 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [parent, child1, child2] + eitc: 6_000 + households: + household: + members: [parent, child1, child2] + state_code: UT + output: + # Has at least one qualifying child, so full EITC is refundable + # Utah EITC = 0.2 * 6_000 = 1_200 + ut_eitc: 1_200 + ut_refundable_eitc: 1_200 + ut_non_refundable_eitc: 0 + ut_has_qualifying_child_for_refundable_eitc: true + +- name: Childless tax unit - not eligible + period: 2024 + reforms: policyengine_us.reforms.states.ut.ut_refundable_eitc.ut_refundable_eitc + input: + gov.contrib.states.ut.eitc.in_effect: true + people: + person: + age: 30 + tax_units: + tax_unit: + members: [person] + eitc: 500 + households: + household: + members: [person] + state_code: UT + output: + # Utah EITC = 0.2 * 500 = 100 (all non-refundable) + ut_eitc: 100 + ut_refundable_eitc: 0 + ut_non_refundable_eitc: 100 + ut_has_qualifying_child_for_refundable_eitc: false + +- name: Married couple with infant (age 0) + period: 2024 + reforms: policyengine_us.reforms.states.ut.ut_refundable_eitc.ut_refundable_eitc + input: + gov.contrib.states.ut.eitc.in_effect: true + people: + parent1: + age: 28 + parent2: + age: 30 + child: + age: 0 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [parent1, parent2, child] + eitc: 7_000 + households: + household: + members: [parent1, parent2, child] + state_code: UT + output: + # Utah EITC = 0.2 * 7_000 = 1_400 + ut_eitc: 1_400 + ut_refundable_eitc: 1_400 + ut_non_refundable_eitc: 0 + ut_has_qualifying_child_for_refundable_eitc: true + +- name: Tax unit with qualifying child but no federal EITC + period: 2024 + reforms: policyengine_us.reforms.states.ut.ut_refundable_eitc.ut_refundable_eitc + input: + gov.contrib.states.ut.eitc.in_effect: true + people: + parent: + age: 30 + child: + age: 2 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [parent, child] + eitc: 0 + households: + household: + members: [parent, child] + state_code: UT + output: + # No federal EITC means no Utah EITC + ut_eitc: 0 + ut_refundable_eitc: 0 + ut_non_refundable_eitc: 0 + ut_has_qualifying_child_for_refundable_eitc: true + +- name: Custom max_age parameter - child at age 4 qualifies + period: 2024 + reforms: policyengine_us.reforms.states.ut.ut_refundable_eitc.ut_refundable_eitc + input: + gov.contrib.states.ut.eitc.in_effect: true + gov.contrib.states.ut.eitc.max_age: 4 + people: + parent: + age: 30 + child: + age: 4 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [parent, child] + eitc: 5_000 + households: + household: + members: [parent, child] + state_code: UT + output: + # With max_age = 4, child age 4 qualifies + # Utah EITC = 0.2 * 5_000 = 1_000 + ut_eitc: 1_000 + ut_refundable_eitc: 1_000 + ut_non_refundable_eitc: 0 + ut_has_qualifying_child_for_refundable_eitc: true