diff --git a/changelog_entry.yaml b/changelog_entry.yaml index e69de29bb2d..73c27a25794 100644 --- a/changelog_entry.yaml +++ b/changelog_entry.yaml @@ -0,0 +1,4 @@ +- bump: minor + changes: + added: + - Michigan Family Independence Program (FIP/TANF) diff --git a/policyengine_us/parameters/gov/states/mi/mdhhs/fip/income/disregard/flat.yaml b/policyengine_us/parameters/gov/states/mi/mdhhs/fip/income/disregard/flat.yaml new file mode 100644 index 00000000000..7a08c18642b --- /dev/null +++ b/policyengine_us/parameters/gov/states/mi/mdhhs/fip/income/disregard/flat.yaml @@ -0,0 +1,14 @@ +description: Michigan deducts this amount from earned income under the Family Independence Program. + +values: + 2008-10-01: 200 + +metadata: + unit: currency-USD + period: month + label: Michigan FIP earned income flat deduction + reference: + - title: Michigan DHHS BEM 518 - FIP/RCA/SDA Income Budgeting + href: https://mdhhs-pres-prod.michigan.gov/olmweb/ex/BP/Public/BEM/518.pdf#page=5 + - title: Michigan TANF State Plan 2023 + href: https://www.michigan.gov/mdhhs/-/media/Project/Websites/mdhhs/Inside-MDHHS/Reports-and-Statistics---Human-Services/State-Plans-and-Federal-Regulations/TANF_State_Plan_2023.pdf diff --git a/policyengine_us/parameters/gov/states/mi/mdhhs/fip/income/disregard/initial_rate.yaml b/policyengine_us/parameters/gov/states/mi/mdhhs/fip/income/disregard/initial_rate.yaml new file mode 100644 index 00000000000..7bf5e56503f --- /dev/null +++ b/policyengine_us/parameters/gov/states/mi/mdhhs/fip/income/disregard/initial_rate.yaml @@ -0,0 +1,14 @@ +description: Michigan deducts this share of remaining earned income for initial eligibility under the Family Independence Program. + +values: + 2008-10-01: 0.2 + +metadata: + unit: /1 + period: month + label: Michigan FIP initial earned income percentage deduction + reference: + - title: Michigan DHHS BEM 518 - FIP/RCA/SDA Income Budgeting + href: https://mdhhs-pres-prod.michigan.gov/olmweb/ex/BP/Public/BEM/518.pdf#page=5 + - title: Michigan DHHS BEM 520 - Computing the FIP/RCA/SDA Budget + href: https://mdhhs-pres-prod.michigan.gov/olmweb/EX/BP/Public/BEM/520.pdf#page=3 diff --git a/policyengine_us/parameters/gov/states/mi/mdhhs/fip/income/disregard/ongoing_rate.yaml b/policyengine_us/parameters/gov/states/mi/mdhhs/fip/income/disregard/ongoing_rate.yaml new file mode 100644 index 00000000000..a02486c1ab2 --- /dev/null +++ b/policyengine_us/parameters/gov/states/mi/mdhhs/fip/income/disregard/ongoing_rate.yaml @@ -0,0 +1,15 @@ +description: Michigan deducts this share of remaining earned income for benefit calculation under the Family Independence Program. + +values: + 2008-10-01: 0.2 + 2011-01-01: 0.5 + +metadata: + unit: /1 + period: month + label: Michigan FIP ongoing earned income percentage deduction + reference: + - title: Michigan DHHS BEM 518 - FIP/RCA/SDA Income Budgeting + href: https://mdhhs-pres-prod.michigan.gov/olmweb/ex/BP/Public/BEM/518.pdf#page=5 + - title: Michigan DHHS BEM 520 - Computing the FIP/RCA/SDA Budget + href: https://mdhhs-pres-prod.michigan.gov/olmweb/EX/BP/Public/BEM/520.pdf#page=4 diff --git a/policyengine_us/parameters/gov/states/mi/mdhhs/fip/payment_standard/additional.yaml b/policyengine_us/parameters/gov/states/mi/mdhhs/fip/payment_standard/additional.yaml new file mode 100644 index 00000000000..de653ef1456 --- /dev/null +++ b/policyengine_us/parameters/gov/states/mi/mdhhs/fip/payment_standard/additional.yaml @@ -0,0 +1,17 @@ +description: Michigan adds this amount to the payment standard for each additional person under the Family Independence Program. + +values: + 2017-01-01: 80 + 2024-12-01: 95 + +metadata: + unit: currency-USD + period: month + label: Michigan FIP additional person increment + reference: + - title: Michigan TANF State Plan 2017 - Payment Standards (frozen 2008-2024) + href: https://www.michigan.gov/-/media/Project/Websites/mdhhs/Folder1/Folder33/TANF_State_Plan_2017.pdf#page=7 + - title: Michigan TANF State Plan Effective 01-01-23 Revised 07-01-25 - Updated Payment Standards + href: https://www.michigan.gov/mdhhs/-/media/Project/Websites/mdhhs/Inside-MDHHS/Reports-and-Statistics---Human-Services/State-Plans-and-Federal-Regulations/TANF_State_Plan_Effective_01-01-23_Rev-07-01-25.pdf + - title: Michigan DHHS Reference Table 210 - FIP Monthly Assistance Payment Standard + href: https://mdhhs-pres-prod.michigan.gov/olmweb/ex/RF/Public/RFT/210.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/mi/mdhhs/fip/payment_standard/base.yaml b/policyengine_us/parameters/gov/states/mi/mdhhs/fip/payment_standard/base.yaml new file mode 100644 index 00000000000..cc9073f6c4d --- /dev/null +++ b/policyengine_us/parameters/gov/states/mi/mdhhs/fip/payment_standard/base.yaml @@ -0,0 +1,52 @@ +description: Michigan provides this amount as the payment standard under the Family Independence Program. + +metadata: + type: single_amount + threshold_unit: int + amount_unit: currency-USD + period: month + label: Michigan FIP payment standard + reference: + - title: Michigan TANF State Plan 2017 - Payment Standards (frozen 2008-2024) + href: https://www.michigan.gov/-/media/Project/Websites/mdhhs/Folder1/Folder33/TANF_State_Plan_2017.pdf#page=7 + - title: Michigan TANF State Plan Effective 01-01-23 Revised 07-01-25 - Updated Payment Standards + href: https://www.michigan.gov/mdhhs/-/media/Project/Websites/mdhhs/Inside-MDHHS/Reports-and-Statistics---Human-Services/State-Plans-and-Federal-Regulations/TANF_State_Plan_Effective_01-01-23_Rev-07-01-25.pdf + - title: Michigan DHHS Reference Table 210 - FIP Monthly Assistance Payment Standard + href: https://mdhhs-pres-prod.michigan.gov/olmweb/ex/RF/Public/RFT/210.pdf#page=1 + +brackets: + - threshold: + 2017-01-01: 1 + amount: + 2017-01-01: 306 + 2024-12-01: 363 + - threshold: + 2017-01-01: 2 + amount: + 2017-01-01: 403 + 2024-12-01: 478 + - threshold: + 2017-01-01: 3 + amount: + 2017-01-01: 492 + 2024-12-01: 583 + - threshold: + 2017-01-01: 4 + amount: + 2017-01-01: 597 + 2024-12-01: 707 + - threshold: + 2017-01-01: 5 + amount: + 2017-01-01: 694 + 2024-12-01: 822 + - threshold: + 2017-01-01: 6 + amount: + 2017-01-01: 828 + 2024-12-01: 981 + - threshold: + 2017-01-01: 7 + amount: + 2017-01-01: 905 + 2024-12-01: 1_072 diff --git a/policyengine_us/parameters/gov/states/mi/mdhhs/fip/payment_standard/max_household_size.yaml b/policyengine_us/parameters/gov/states/mi/mdhhs/fip/payment_standard/max_household_size.yaml new file mode 100644 index 00000000000..2e58b19cdc2 --- /dev/null +++ b/policyengine_us/parameters/gov/states/mi/mdhhs/fip/payment_standard/max_household_size.yaml @@ -0,0 +1,12 @@ +description: Michigan sets this threshold as the maximum household size under the Family Independence Program. + +values: + 2017-01-01: 7 + +metadata: + unit: person + period: year + label: Michigan FIP maximum payment standard table size + reference: + - title: Michigan DHHS Reference Table 210 - FIP Monthly Assistance Payment Standard + href: https://mdhhs-pres-prod.michigan.gov/olmweb/ex/RF/Public/RFT/210.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/mi/mdhhs/fip/resources/limit.yaml b/policyengine_us/parameters/gov/states/mi/mdhhs/fip/resources/limit.yaml new file mode 100644 index 00000000000..9edc921e5f5 --- /dev/null +++ b/policyengine_us/parameters/gov/states/mi/mdhhs/fip/resources/limit.yaml @@ -0,0 +1,13 @@ +description: Michigan limits countable resources to this amount under the Family Independence Program. + +values: + 2008-10-01: 3_000 + 2019-01-01: 15_000 + +metadata: + unit: currency-USD + label: Michigan FIP resource limit + period: year + reference: + - title: Michigan DHHS BEM 400 - Assets + href: https://mdhhs-pres-prod.michigan.gov/olmweb/EX/BP/Public/BEM/400.pdf#page=1 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/fip/eligibility/mi_fip_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/fip/eligibility/mi_fip_eligible.yaml new file mode 100644 index 00000000000..eafec7dc9f5 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/fip/eligibility/mi_fip_eligible.yaml @@ -0,0 +1,76 @@ +- name: All requirements met + period: 2025-01 + input: + state_code: MI + is_demographic_tanf_eligible: true + is_citizen_or_legal_immigrant: true + mi_fip_income_eligible: true + mi_fip_resources_eligible: true + output: + mi_fip_eligible: true + +- name: Demographic not met + period: 2025-01 + input: + state_code: MI + is_demographic_tanf_eligible: false + is_citizen_or_legal_immigrant: true + mi_fip_income_eligible: true + mi_fip_resources_eligible: true + output: + mi_fip_eligible: false + +- name: Citizenship not met + period: 2025-01 + input: + state_code: MI + is_demographic_tanf_eligible: true + is_citizen_or_legal_immigrant: false + mi_fip_income_eligible: true + mi_fip_resources_eligible: true + output: + mi_fip_eligible: false + +- name: Income not met + period: 2025-01 + input: + state_code: MI + is_demographic_tanf_eligible: true + is_citizen_or_legal_immigrant: true + mi_fip_income_eligible: false + mi_fip_resources_eligible: true + output: + mi_fip_eligible: false + +- name: Resources not met + period: 2025-01 + input: + state_code: MI + is_demographic_tanf_eligible: true + is_citizen_or_legal_immigrant: true + mi_fip_income_eligible: true + mi_fip_resources_eligible: false + output: + mi_fip_eligible: false + +- name: No requirements met + period: 2025-01 + input: + state_code: MI + is_demographic_tanf_eligible: false + is_citizen_or_legal_immigrant: false + mi_fip_income_eligible: false + mi_fip_resources_eligible: false + output: + mi_fip_eligible: false + +- name: Household outside Michigan is not eligible + period: 2025-01 + input: + state_code: CA + is_demographic_tanf_eligible: true + is_citizen_or_legal_immigrant: true + mi_fip_income_eligible: true + mi_fip_resources_eligible: true + output: + mi_fip_eligible: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/fip/eligibility/mi_fip_income_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/fip/eligibility/mi_fip_income_eligible.yaml new file mode 100644 index 00000000000..7bd0a3ef29d --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/fip/eligibility/mi_fip_income_eligible.yaml @@ -0,0 +1,104 @@ +- name: Enrolled recipient with income below payment standard is eligible + period: 2025-01 + input: + employment_income_before_lsr: 6_000 # $500/month + is_tanf_enrolled: true + state_code: MI + output: + # Enrolled uses Issuance Deficit Test (50% rate): + # Countable = $500 - [$200 + 50%×$300] = $500 - $350 = $150 + # Payment standard (size 1) = $363 + # $150 < $363 → ELIGIBLE + mi_fip_income_eligible: true + +- name: New applicant with low income passes initial eligibility (20% test) + period: 2025-01 + input: + employment_income_before_lsr: 4_800 # $400/month + is_tanf_enrolled: false + state_code: MI + output: + # Initial eligibility uses 20% deduction: + # Countable (20%) = $400 - [$200 + 20%×$200] = $400 - $240 = $160 + # Payment standard (size 1) = $363 + # $160 < $363 → ELIGIBLE + mi_fip_income_eligible: true + +- name: New applicant with high income fails initial eligibility (20% test) + period: 2025-01 + input: + employment_income_before_lsr: 12_000 # $1,000/month + is_tanf_enrolled: false + state_code: MI + output: + # Initial eligibility uses 20% deduction: + # Countable (20%) = $1,000 - [$200 + 20%×$800] = $1,000 - $360 = $640 + # Payment standard (size 1) = $363 + # $640 > $363 → NOT ELIGIBLE + mi_fip_income_eligible: false + +- name: New applicant family of 3 at initial eligibility threshold + period: 2025-01 + input: + people: + parent: + employment_income_before_lsr: 9_768 # $814/month + age: 35 + child1: + age: 8 + child2: + age: 4 + spm_units: + spm_unit: + members: [parent, child1, child2] + households: + household: + members: [parent, child1, child2] + state_code: MI + output: + # Initial eligibility (20% deduction): + # Countable = $814 - [$200 + 20%×$614] = $814 - $322.80 = $491.20 + # Payment standard (size 3) = $583 + # $491.20 < $583 → ELIGIBLE + mi_fip_income_eligible: true + +- name: New applicant family of 3 just above initial threshold + period: 2025-01 + input: + people: + parent: + employment_income_before_lsr: 9_900 # $825/month + age: 35 + child1: + age: 8 + child2: + age: 4 + spm_units: + spm_unit: + members: [parent, child1, child2] + households: + household: + members: [parent, child1, child2] + state_code: MI + output: + # Initial eligibility (20% deduction): + # Countable = $825 - [$200 + 20%×$625] = $825 - $325 = $500 + # Payment standard (size 3) = $583 + # $500 < $583 → ELIGIBLE + mi_fip_income_eligible: true + +- name: Household with zero income is eligible + period: 2025-01 + input: + employment_income_before_lsr: 0 + state_code: MI + output: + mi_fip_income_eligible: true + +- name: Household outside Michigan returns false + period: 2025-01 + input: + employment_income_before_lsr: 12_000 + state_code: CA + output: + mi_fip_income_eligible: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/fip/eligibility/mi_fip_resources_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/fip/eligibility/mi_fip_resources_eligible.yaml new file mode 100644 index 00000000000..f822fffe2ff --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/fip/eligibility/mi_fip_resources_eligible.yaml @@ -0,0 +1,47 @@ +- name: Household with assets below limit is eligible + period: 2025-01 + input: + state_code: MI + spm_unit_assets: 10_000 + output: + mi_fip_resources_eligible: true + +- name: Household with zero assets is eligible + period: 2025-01 + input: + state_code: MI + spm_unit_assets: 0 + output: + mi_fip_resources_eligible: true + +- name: Household with assets exactly at limit is eligible + period: 2025-01 + input: + state_code: MI + spm_unit_assets: 15_000 + output: + mi_fip_resources_eligible: true + +- name: Household with assets exceeding limit is not eligible + period: 2025-01 + input: + state_code: MI + spm_unit_assets: 20_000 + output: + mi_fip_resources_eligible: false + +- name: Household with assets one dollar over limit is not eligible + period: 2025-01 + input: + state_code: MI + spm_unit_assets: 15_001 + output: + mi_fip_resources_eligible: false + +- name: Household outside Michigan returns zero + period: 2025-01 + input: + state_code: CA + spm_unit_assets: 10_000 + output: + mi_fip_resources_eligible: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/fip/income/benefit/mi_fip_earned_income_after_deductions_for_benefit_person.yaml b/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/fip/income/benefit/mi_fip_earned_income_after_deductions_for_benefit_person.yaml new file mode 100644 index 00000000000..045c00b9094 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/fip/income/benefit/mi_fip_earned_income_after_deductions_for_benefit_person.yaml @@ -0,0 +1,80 @@ +- name: Zero earned income + period: 2025-01 + input: + tanf_gross_earned_income: 0 + state_code: MI + output: + mi_fip_earned_income_after_deductions_for_benefit_person: 0 + +- name: Earned income below flat deduction + period: 2025-01 + input: + tanf_gross_earned_income: 150 + state_code: MI + output: + # $150 gross + # Flat deduction = $200, but capped at gross + # Remainder = max($150 - $200, 0) = $0 + # 50% deduction = $0 + # Countable = max($150 - $200 - $0, 0) = $0 + mi_fip_earned_income_after_deductions_for_benefit_person: 0 + +- name: Earned income equal to flat deduction + period: 2025-01 + input: + tanf_gross_earned_income: 200 + state_code: MI + output: + # $200 gross + # Flat deduction = $200 + # Remainder = max($200 - $200, 0) = $0 + # 50% deduction = $0 + # Countable = max($200 - $200 - $0, 0) = $0 + mi_fip_earned_income_after_deductions_for_benefit_person: 0 + +- name: Earned income above flat deduction + period: 2025-01 + input: + tanf_gross_earned_income: 500 + state_code: MI + output: + # $500 gross + # Flat deduction = $200 + # Remainder = $500 - $200 = $300 + # 50% deduction = $300 * 0.5 = $150 + # Countable = $500 - $200 - $150 = $150 + mi_fip_earned_income_after_deductions_for_benefit_person: 150 + +- name: Moderate earned income + period: 2025-01 + input: + tanf_gross_earned_income: 1_000 + state_code: MI + output: + # $1,000 gross + # Flat deduction = $200 + # Remainder = $1,000 - $200 = $800 + # 50% deduction = $800 * 0.5 = $400 + # Countable = $1,000 - $200 - $400 = $400 + mi_fip_earned_income_after_deductions_for_benefit_person: 400 + +- name: High earned income + period: 2025-01 + input: + tanf_gross_earned_income: 2_000 + state_code: MI + output: + # $2,000 gross + # Flat deduction = $200 + # Remainder = $2,000 - $200 = $1,800 + # 50% deduction = $1,800 * 0.5 = $900 + # Countable = $2,000 - $200 - $900 = $900 + mi_fip_earned_income_after_deductions_for_benefit_person: 900 + +- name: Outside Michigan returns zero + period: 2025-01 + input: + tanf_gross_earned_income: 1_000 + state_code: CA + output: + mi_fip_earned_income_after_deductions_for_benefit_person: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/fip/income/eligibility/mi_fip_earned_income_after_deductions_for_eligibility_person.yaml b/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/fip/income/eligibility/mi_fip_earned_income_after_deductions_for_eligibility_person.yaml new file mode 100644 index 00000000000..2dfce926c83 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/fip/income/eligibility/mi_fip_earned_income_after_deductions_for_eligibility_person.yaml @@ -0,0 +1,85 @@ +- name: Zero earned income + period: 2025-01 + input: + tanf_gross_earned_income: 0 + state_code: MI + output: + mi_fip_earned_income_after_deductions_for_eligibility_person: 0 + +- name: Earned income below flat deduction + period: 2025-01 + input: + tanf_gross_earned_income: 150 + state_code: MI + output: + # $150 gross + # Flat deduction = $200, but capped at gross + # Remainder = max($150 - $200, 0) = $0 + # 20% deduction = $0 + # Total deduction = $200 + $0 = $200 + # Countable = max($150 - $200, 0) = $0 + mi_fip_earned_income_after_deductions_for_eligibility_person: 0 + +- name: Earned income equal to flat deduction + period: 2025-01 + input: + tanf_gross_earned_income: 200 + state_code: MI + output: + # $200 gross + # Flat deduction = $200 + # Remainder = max($200 - $200, 0) = $0 + # 20% deduction = $0 + # Total deduction = $200 + $0 = $200 + # Countable = max($200 - $200, 0) = $0 + mi_fip_earned_income_after_deductions_for_eligibility_person: 0 + +- name: Earned income above flat deduction + period: 2025-01 + input: + tanf_gross_earned_income: 500 + state_code: MI + output: + # $500 gross + # Flat deduction = $200 + # Remainder = $500 - $200 = $300 + # 20% deduction = $300 * 0.2 = $60 + # Total deduction = $200 + $60 = $260 + # Countable = max($500 - $260, 0) = $240 + mi_fip_earned_income_after_deductions_for_eligibility_person: 240 + +- name: Moderate earned income + period: 2025-01 + input: + tanf_gross_earned_income: 1_000 + state_code: MI + output: + # $1,000 gross + # Flat deduction = $200 + # Remainder = $1,000 - $200 = $800 + # 20% deduction = $800 * 0.2 = $160 + # Total deduction = $200 + $160 = $360 + # Countable = max($1,000 - $360, 0) = $640 + mi_fip_earned_income_after_deductions_for_eligibility_person: 640 + +- name: High earned income + period: 2025-01 + input: + tanf_gross_earned_income: 2_000 + state_code: MI + output: + # $2,000 gross + # Flat deduction = $200 + # Remainder = $2,000 - $200 = $1,800 + # 20% deduction = $1,800 * 0.2 = $360 + # Total deduction = $200 + $360 = $560 + # Countable = max($2,000 - $560, 0) = $1,440 + mi_fip_earned_income_after_deductions_for_eligibility_person: 1_440 + +- name: Outside Michigan returns zero + period: 2025-01 + input: + tanf_gross_earned_income: 1_000 + state_code: CA + output: + mi_fip_earned_income_after_deductions_for_eligibility_person: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/fip/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/fip/integration.yaml new file mode 100644 index 00000000000..751255c59fd --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/fip/integration.yaml @@ -0,0 +1,526 @@ +- name: Single parent with one child - no income - maximum benefit + period: 2025-01 + input: + people: + parent: + age: 30 + employment_income_before_lsr: 0 + child: + age: 5 + tax_units: + tax_unit: + members: [parent, child] + spm_units: + spm_unit: + members: [parent, child] + is_tanf_enrolled: true + households: + household: + members: [parent, child] + state_code: MI + output: + # Family of 2: Payment standard = $478 + # Income = $0 + # Countable income = $0 + # Benefit = $478 - $0 = $478 + mi_fip_payment_standard: 478 + mi_fip_countable_income_for_benefit: 0 + mi_fip_income_eligible: true + mi_fip_eligible: true + mi_fip: 478 + +- name: Single parent with two children - no income - maximum benefit + period: 2025-01 + input: + people: + parent: + age: 35 + employment_income_before_lsr: 0 + child1: + age: 8 + child2: + age: 4 + tax_units: + tax_unit: + members: [parent, child1, child2] + spm_units: + spm_unit: + members: [parent, child1, child2] + is_tanf_enrolled: true + households: + household: + members: [parent, child1, child2] + state_code: MI + output: + # Family of 3: Payment standard = $583 + # Income = $0 + # Countable income = $0 + # Benefit = $583 - $0 = $583 + mi_fip_payment_standard: 583 + mi_fip_countable_income_for_benefit: 0 + mi_fip_income_eligible: true + mi_fip_eligible: true + mi_fip: 583 + +- name: Single parent with two children - low income - partial benefit + period: 2025-01 + input: + people: + parent: + age: 35 + employment_income_before_lsr: 12_000 # $1,000/month + child1: + age: 8 + child2: + age: 4 + tax_units: + tax_unit: + members: [parent, child1, child2] + spm_units: + spm_unit: + members: [parent, child1, child2] + is_tanf_enrolled: true + households: + household: + members: [parent, child1, child2] + state_code: MI + output: + # Family of 3: Payment standard = $583 + # Monthly income = $1,000 + # Ongoing earned income disregard = $200 + 50% of remainder + # Disregard = $200 + (50% × $800) = $200 + $400 = $600 + # Countable income = $1,000 - $600 = $400 + # Benefit = $583 - $400 = $183 + mi_fip_payment_standard: 583 + mi_fip_countable_income_for_benefit: 400 + mi_fip_income_eligible: true + mi_fip_eligible: true + mi_fip: 183 + +- name: Single parent with two children - moderate income - smaller benefit + period: 2025-01 + input: + people: + parent: + age: 35 + employment_income_before_lsr: 15_600 # $1,300/month + child1: + age: 8 + child2: + age: 4 + tax_units: + tax_unit: + members: [parent, child1, child2] + spm_units: + spm_unit: + members: [parent, child1, child2] + is_tanf_enrolled: true + households: + household: + members: [parent, child1, child2] + state_code: MI + output: + # Family of 3: Payment standard = $583 + # Monthly income = $1,300 + # Ongoing earned income disregard = $200 + 50% of remainder + # Disregard = $200 + (50% × $1,100) = $200 + $550 = $750 + # Countable income = $1,300 - $750 = $550 + # Benefit = $583 - $550 = $33 + mi_fip_payment_standard: 583 + mi_fip_countable_income_for_benefit: 550 + mi_fip_income_eligible: true + mi_fip_eligible: true + mi_fip: 33 + +- name: Single parent with two children - at ongoing eligibility threshold + period: 2025-01 + input: + people: + parent: + age: 35 + employment_income_before_lsr: 14_208 # $1,184/month - at threshold + child1: + age: 8 + child2: + age: 4 + tax_units: + tax_unit: + members: [parent, child1, child2] + spm_units: + spm_unit: + members: [parent, child1, child2] + is_tanf_enrolled: true + households: + household: + members: [parent, child1, child2] + state_code: MI + output: + # Family of 3: Payment standard = $583 + # Monthly income = $1,184 (maximum for ongoing eligibility) + # Ongoing earned income disregard = $200 + 50% of remainder + # Disregard = $200 + (50% × $984) = $200 + $492 = $692 + # Countable income = $1,184 - $692 = $492 + # Benefit = $583 - $492 = $91 + mi_fip_payment_standard: 583 + mi_fip_countable_income_for_benefit: 492 + mi_fip_income_eligible: true + mi_fip_eligible: true + mi_fip: 91 + +- name: Single parent with two children - above ongoing eligibility threshold + period: 2025-01 + input: + people: + parent: + age: 35 + employment_income_before_lsr: 14_400 # $1,200/month - above threshold + child1: + age: 8 + child2: + age: 4 + tax_units: + tax_unit: + members: [parent, child1, child2] + spm_units: + spm_unit: + members: [parent, child1, child2] + is_tanf_enrolled: true + households: + household: + members: [parent, child1, child2] + state_code: MI + output: + # Family of 3: Payment standard = $583 + # Monthly income = $1,200 + # Ongoing earned income disregard = $200 + 50% of remainder + # Disregard = $200 + (50% × $1,000) = $200 + $500 = $700 + # Countable income = $1,200 - $700 = $500 + # Benefit = $583 - $500 = $83 + mi_fip_payment_standard: 583 + mi_fip_countable_income_for_benefit: 500 + mi_fip_income_eligible: true + mi_fip_eligible: true + mi_fip: 83 + +- name: Single parent with two children - at initial eligibility threshold + period: 2025-01 + input: + people: + parent: + age: 35 + employment_income_before_lsr: 9_768 # $814/month - at initial eligibility threshold + child1: + age: 8 + child2: + age: 4 + tax_units: + tax_unit: + members: [parent, child1, child2] + spm_units: + spm_unit: + members: [parent, child1, child2] + is_tanf_enrolled: true + households: + household: + members: [parent, child1, child2] + state_code: MI + output: + # Family of 3: Payment standard = $583 + # Monthly income = $814 (maximum for initial eligibility) + # For ongoing benefit calculation, use 50% disregard + # Ongoing earned income disregard = $200 + 50% of remainder + # Disregard = $200 + (50% × $614) = $200 + $307 = $507 + # Countable income = $814 - $507 = $307 + # Benefit = $583 - $307 = $276 + mi_fip_payment_standard: 583 + mi_fip_countable_income_for_benefit: 307 + mi_fip_income_eligible: true + mi_fip_eligible: true + mi_fip: 276 + +- name: Large family - five members - no income + period: 2025-01 + input: + people: + parent: + age: 40 + employment_income_before_lsr: 0 + child1: + age: 12 + child2: + age: 10 + child3: + age: 7 + child4: + age: 3 + tax_units: + tax_unit: + members: [parent, child1, child2, child3, child4] + spm_units: + spm_unit: + members: [parent, child1, child2, child3, child4] + is_tanf_enrolled: true + households: + household: + members: [parent, child1, child2, child3, child4] + state_code: MI + output: + # Family of 5: Payment standard = $822 + # Income = $0 + # Countable income = $0 + # Benefit = $822 - $0 = $822 + mi_fip_payment_standard: 822 + mi_fip_countable_income_for_benefit: 0 + mi_fip_income_eligible: true + mi_fip_eligible: true + mi_fip: 822 + +- name: Large family - five members - moderate income + period: 2025-01 + input: + people: + parent: + age: 40 + employment_income_before_lsr: 18_000 # $1,500/month + child1: + age: 12 + child2: + age: 10 + child3: + age: 7 + child4: + age: 3 + tax_units: + tax_unit: + members: [parent, child1, child2, child3, child4] + spm_units: + spm_unit: + members: [parent, child1, child2, child3, child4] + is_tanf_enrolled: true + households: + household: + members: [parent, child1, child2, child3, child4] + state_code: MI + output: + # Family of 5: Payment standard = $822 + # Monthly income = $1,500 + # Ongoing earned income disregard = $200 + 50% of remainder + # Disregard = $200 + (50% × $1,300) = $200 + $650 = $850 + # Countable income = $1,500 - $850 = $650 + # Benefit = $822 - $650 = $172 + mi_fip_payment_standard: 822 + mi_fip_countable_income_for_benefit: 650 + mi_fip_income_eligible: true + mi_fip_eligible: true + mi_fip: 172 + +- name: Two-parent household with children - one parent working + period: 2025-01 + input: + people: + parent1: + age: 35 + employment_income_before_lsr: 12_000 # $1,000/month + parent2: + age: 33 + employment_income_before_lsr: 0 + child1: + age: 6 + child2: + age: 3 + tax_units: + tax_unit: + members: [parent1, parent2, child1, child2] + spm_units: + spm_unit: + members: [parent1, parent2, child1, child2] + is_tanf_enrolled: true + households: + household: + members: [parent1, parent2, child1, child2] + state_code: MI + output: + # Family of 4: Payment standard = $707 + # Monthly income = $1,000 + # Ongoing earned income disregard = $200 + 50% of remainder + # Disregard = $200 + (50% × $800) = $200 + $400 = $600 + # Countable income = $1,000 - $600 = $400 + # Benefit = $707 - $400 = $307 + mi_fip_payment_standard: 707 + mi_fip_countable_income_for_benefit: 400 + mi_fip_income_eligible: true + mi_fip_eligible: true + mi_fip: 307 + +- name: Two-parent household with children - both parents working + period: 2025-01 + input: + people: + parent1: + age: 35 + employment_income_before_lsr: 9_600 # $800/month + parent2: + age: 33 + employment_income_before_lsr: 4_800 # $400/month + child1: + age: 6 + child2: + age: 3 + tax_units: + tax_unit: + members: [parent1, parent2, child1, child2] + spm_units: + spm_unit: + members: [parent1, parent2, child1, child2] + is_tanf_enrolled: true + households: + household: + members: [parent1, parent2, child1, child2] + state_code: MI + output: + # Family of 4: Payment standard = $707 + # BEM 518: Per-person deductions (enrolled recipients, 50% rate) + # Parent 1: $800 - [$200 + 50%×$600] = $800 - $500 = $300 + # Parent 2: $400 - [$200 + 50%×$200] = $400 - $300 = $100 + # Countable income = $300 + $100 = $400 + # Benefit = $707 - $400 = $307 + mi_fip_payment_standard: 707 + mi_fip_countable_income_for_benefit: 400 + mi_fip_income_eligible: true + mi_fip_eligible: true + mi_fip: 307 + +- name: Family with unearned income only + period: 2025-01 + input: + people: + parent: + age: 35 + employment_income_before_lsr: 0 + social_security_disability: 6_000 # $500/month + child1: + age: 8 + child2: + age: 4 + tax_units: + tax_unit: + members: [parent, child1, child2] + spm_units: + spm_unit: + members: [parent, child1, child2] + is_tanf_enrolled: true + households: + household: + members: [parent, child1, child2] + state_code: MI + output: + # Family of 3: Payment standard = $583 + # Unearned income (social security) = $500/month + # No disregard for unearned income + # Countable income = $500 + # Benefit = $583 - $500 = $83 + mi_fip_payment_standard: 583 + mi_fip_countable_income_for_benefit: 500 + mi_fip_income_eligible: true + mi_fip_eligible: true + mi_fip: 83 + +- name: Family with mixed earned and unearned income + period: 2025-01 + input: + people: + parent: + age: 35 + employment_income_before_lsr: 6_000 # $500/month + social_security_retirement: 3_600 # $300/month + child1: + age: 8 + child2: + age: 4 + tax_units: + tax_unit: + members: [parent, child1, child2] + spm_units: + spm_unit: + members: [parent, child1, child2] + is_tanf_enrolled: true + households: + household: + members: [parent, child1, child2] + state_code: MI + output: + # Family of 3: Payment standard = $583 + # Earned income = $500/month + # Ongoing earned income disregard = $200 + 50% of remainder + # Earned disregard = $200 + (50% × $300) = $200 + $150 = $350 + # Countable earned income = $500 - $350 = $150 + # Unearned income (no disregard) = $300 + # Total countable income = $150 + $300 = $450 + # Benefit = $583 - $450 = $133 + mi_fip_payment_standard: 583 + mi_fip_countable_income_for_benefit: 450 + mi_fip_income_eligible: true + mi_fip_eligible: true + mi_fip: 133 + +- name: High income family - no benefit + period: 2025-01 + input: + people: + parent: + age: 35 + employment_income_before_lsr: 30_000 # $2,500/month - well above threshold + child1: + age: 8 + child2: + age: 4 + tax_units: + tax_unit: + members: [parent, child1, child2] + spm_units: + spm_unit: + members: [parent, child1, child2] + is_tanf_enrolled: true + households: + household: + members: [parent, child1, child2] + state_code: MI + output: + # Family of 3: Payment standard = $583 + # Monthly income = $2,500 + # Ongoing earned income disregard = $200 + 50% of remainder + # Disregard = $200 + (50% × $2,300) = $200 + $1,150 = $1,350 + # Countable income = $2,500 - $1,350 = $1,150 + # Benefit = $583 - $1,150 = -$567 (negative, so $0) + mi_fip_payment_standard: 583 + mi_fip_countable_income_for_benefit: 1_150 + mi_fip_income_eligible: false + mi_fip_eligible: false + mi_fip: 0 + +- name: Family outside Michigan - not eligible + period: 2025-01 + input: + people: + parent: + age: 35 + employment_income_before_lsr: 12_000 + child1: + age: 8 + child2: + age: 4 + tax_units: + tax_unit: + members: [parent, child1, child2] + spm_units: + spm_unit: + members: [parent, child1, child2] + is_tanf_enrolled: true + households: + household: + members: [parent, child1, child2] + state_code: CA + output: + # Not in Michigan - not eligible for MI TANF + mi_fip_eligible: false + mi_fip: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/fip/mi_fip.yaml b/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/fip/mi_fip.yaml new file mode 100644 index 00000000000..f1cb98bf17e --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/fip/mi_fip.yaml @@ -0,0 +1,62 @@ +- name: Eligible household with positive benefit + period: 2025-01 + input: + mi_fip_eligible: true + mi_fip_payment_standard: 500 + mi_fip_countable_income_for_benefit: 200 + output: + mi_fip: 300 + +- name: Eligible household at boundary - countable income equals payment standard + period: 2025-01 + input: + mi_fip_eligible: true + mi_fip_payment_standard: 500 + mi_fip_countable_income_for_benefit: 500 + output: + mi_fip: 0 + +- name: Eligible household with zero income + period: 2025-01 + input: + mi_fip_eligible: true + mi_fip_payment_standard: 500 + mi_fip_countable_income_for_benefit: 0 + output: + mi_fip: 500 + +- name: Eligible household with countable income exceeding payment standard + period: 2025-01 + input: + mi_fip_eligible: true + mi_fip_payment_standard: 500 + mi_fip_countable_income_for_benefit: 600 + output: + mi_fip: 0 + +- name: Ineligible household returns zero + period: 2025-01 + input: + mi_fip_eligible: false + mi_fip_payment_standard: 500 + mi_fip_countable_income_for_benefit: 200 + output: + mi_fip: 0 + +- name: Eligible household with small benefit amount + period: 2025-01 + input: + mi_fip_eligible: true + mi_fip_payment_standard: 100 + mi_fip_countable_income_for_benefit: 99 + output: + mi_fip: 1 + +- name: Eligible household with countable income one dollar below payment standard + period: 2025-01 + input: + mi_fip_eligible: true + mi_fip_payment_standard: 583 + mi_fip_countable_income_for_benefit: 582 + output: + mi_fip: 1 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/fip/mi_fip_payment_standard.yaml b/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/fip/mi_fip_payment_standard.yaml new file mode 100644 index 00000000000..e1163ce41b4 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/fip/mi_fip_payment_standard.yaml @@ -0,0 +1,239 @@ +- name: Single person household + period: 2025-01 + input: + people: + person1: + age: 30 + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: MI + output: + mi_fip_payment_standard: 363 + +- name: Two person household + period: 2025-01 + input: + people: + person1: + age: 30 + person2: + age: 5 + spm_units: + spm_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: MI + output: + mi_fip_payment_standard: 478 + +- name: Three person household + period: 2025-01 + input: + people: + person1: + age: 30 + person2: + age: 8 + person3: + age: 5 + spm_units: + spm_unit: + members: [person1, person2, person3] + households: + household: + members: [person1, person2, person3] + state_code: MI + output: + mi_fip_payment_standard: 583 + +- name: Four person household + period: 2025-01 + input: + people: + person1: + age: 30 + person2: + age: 8 + person3: + age: 5 + person4: + age: 3 + spm_units: + spm_unit: + members: [person1, person2, person3, person4] + households: + household: + members: [person1, person2, person3, person4] + state_code: MI + output: + mi_fip_payment_standard: 707 + +- name: Five person household + period: 2025-01 + input: + people: + person1: + age: 30 + person2: + age: 12 + person3: + age: 10 + person4: + age: 7 + person5: + age: 3 + spm_units: + spm_unit: + members: [person1, person2, person3, person4, person5] + households: + household: + members: [person1, person2, person3, person4, person5] + state_code: MI + output: + mi_fip_payment_standard: 822 + +- name: Six person household + period: 2025-01 + input: + people: + person1: + age: 30 + person2: + age: 12 + person3: + age: 10 + person4: + age: 7 + person5: + age: 5 + person6: + age: 3 + spm_units: + spm_unit: + members: [person1, person2, person3, person4, person5, person6] + households: + household: + members: [person1, person2, person3, person4, person5, person6] + state_code: MI + output: + mi_fip_payment_standard: 981 + +- name: Seven person household - maximum in bracket + period: 2025-01 + input: + people: + person1: + age: 30 + person2: + age: 14 + person3: + age: 12 + person4: + age: 10 + person5: + age: 7 + person6: + age: 5 + person7: + age: 3 + spm_units: + spm_unit: + members: [person1, person2, person3, person4, person5, person6, person7] + households: + household: + members: [person1, person2, person3, person4, person5, person6, person7] + state_code: MI + output: + mi_fip_payment_standard: 1_072 + +- name: Eight person household - beyond bracket + period: 2025-01 + input: + people: + person1: + age: 40 + person2: + age: 16 + person3: + age: 14 + person4: + age: 12 + person5: + age: 10 + person6: + age: 7 + person7: + age: 5 + person8: + age: 3 + spm_units: + spm_unit: + members: [person1, person2, person3, person4, person5, person6, person7, person8] + households: + household: + members: [person1, person2, person3, person4, person5, person6, person7, person8] + state_code: MI + output: + # Size 7 payment = $1,072 + # Additional person (8th) = $95 + # Total = $1,072 + $95 = $1,167 + mi_fip_payment_standard: 1_167 + +- name: Nine person household + period: 2025-01 + input: + people: + person1: + age: 40 + person2: + age: 18 + person3: + age: 16 + person4: + age: 14 + person5: + age: 12 + person6: + age: 10 + person7: + age: 7 + person8: + age: 5 + person9: + age: 3 + spm_units: + spm_unit: + members: [person1, person2, person3, person4, person5, person6, person7, person8, person9] + households: + household: + members: [person1, person2, person3, person4, person5, person6, person7, person8, person9] + state_code: MI + output: + # Size 7 payment = $1,072 + # Additional persons (8th and 9th) = 2 × $95 = $190 + # Total = $1,072 + $190 = $1,262 + mi_fip_payment_standard: 1_262 + +- name: Household outside Michigan returns zero + period: 2025-01 + input: + people: + person1: + age: 30 + person2: + age: 5 + spm_units: + spm_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: CA + output: + mi_fip_payment_standard: 0 diff --git a/policyengine_us/variables/gov/states/mi/mdhhs/fip/eligibility/mi_fip_eligible.py b/policyengine_us/variables/gov/states/mi/mdhhs/fip/eligibility/mi_fip_eligible.py new file mode 100644 index 00000000000..9e3eaa93715 --- /dev/null +++ b/policyengine_us/variables/gov/states/mi/mdhhs/fip/eligibility/mi_fip_eligible.py @@ -0,0 +1,44 @@ +from policyengine_us.model_api import * + + +class mi_fip_eligible(Variable): + value_type = bool + entity = SPMUnit + label = "Eligible for Michigan Family Independence Program" + definition_period = MONTH + reference = ( + "https://www.michigan.gov/mdhhs/-/media/Project/Websites/mdhhs/Inside-MDHHS/Reports-and-Statistics---Human-Services/State-Plans-and-Federal-Regulations/TANF_State_Plan_2023.pdf", + "https://mdhhs-pres-prod.michigan.gov/olmweb/ex/bp/public/bem/100.pdf", + ) + defined_for = StateCode.MI + + def formula(spm_unit, period, parameters): + person = spm_unit.members + + # Must meet demographic requirements (age, deprivation) + # Use federal demographic eligibility + demographic_eligible = spm_unit("is_demographic_tanf_eligible", period) + + # Must have at least one citizen or legal immigrant + # Per MI FIP requirements: "You must have at least one citizen or + # qualified lawful immigrant in your family" + has_citizen = spm_unit.any( + person("is_citizen_or_legal_immigrant", period) + ) + + # Must meet income eligibility + income_eligible = spm_unit("mi_fip_income_eligible", period) + + # Must meet resource eligibility + resources_eligible = spm_unit("mi_fip_resources_eligible", period) + + # Note: Simplified implementation does not model: + # - Time limits (60 months as of April 2025) + # - Work requirements (behavioral) + + return ( + demographic_eligible + & has_citizen + & income_eligible + & resources_eligible + ) diff --git a/policyengine_us/variables/gov/states/mi/mdhhs/fip/eligibility/mi_fip_income_eligible.py b/policyengine_us/variables/gov/states/mi/mdhhs/fip/eligibility/mi_fip_income_eligible.py new file mode 100644 index 00000000000..1fbdbefa45c --- /dev/null +++ b/policyengine_us/variables/gov/states/mi/mdhhs/fip/eligibility/mi_fip_income_eligible.py @@ -0,0 +1,39 @@ +from policyengine_us.model_api import * + + +class mi_fip_income_eligible(Variable): + value_type = bool + entity = SPMUnit + label = "Eligible for Michigan FIP based on income" + definition_period = MONTH + reference = ( + "https://mdhhs-pres-prod.michigan.gov/olmweb/ex/BP/Public/BEM/518.pdf", + "https://mdhhs-pres-prod.michigan.gov/olmweb/EX/BP/Public/BEM/520.pdf", + ) + defined_for = StateCode.MI + + def formula(spm_unit, period, parameters): + # BEM 518 Page 3: Financial need exists when the group passes + # the Qualifying Deficit Test (initial) or Issuance Deficit Test (ongoing) + + enrolled = spm_unit("is_tanf_enrolled", period) + payment_standard = spm_unit("mi_fip_payment_standard", period) + + # New applicants: Use Qualifying Deficit Test (BEM 520 Section C) + # - Uses 20% deduction rate + countable_income_for_eligibility = spm_unit( + "mi_fip_countable_income_for_eligibility", period + ) + passes_qualifying_test = ( + countable_income_for_eligibility < payment_standard + ) + + # Enrolled recipients: Use Issuance Deficit Test (BEM 520 Section D) + # - Uses 50% deduction rate (more generous) + countable_income_for_benefit = spm_unit( + "mi_fip_countable_income_for_benefit", period + ) + passes_issuance_test = countable_income_for_benefit < payment_standard + + # Return appropriate test based on enrollment status + return where(enrolled, passes_issuance_test, passes_qualifying_test) diff --git a/policyengine_us/variables/gov/states/mi/mdhhs/fip/eligibility/mi_fip_resources_eligible.py b/policyengine_us/variables/gov/states/mi/mdhhs/fip/eligibility/mi_fip_resources_eligible.py new file mode 100644 index 00000000000..4b3878875bd --- /dev/null +++ b/policyengine_us/variables/gov/states/mi/mdhhs/fip/eligibility/mi_fip_resources_eligible.py @@ -0,0 +1,24 @@ +from policyengine_us.model_api import * + + +class mi_fip_resources_eligible(Variable): + value_type = bool + entity = SPMUnit + label = "Eligible for Michigan FIP based on resources" + definition_period = MONTH + reference = ( + "https://mdhhs-pres-prod.michigan.gov/olmweb/EX/BP/Public/BEM/400.pdf", + "https://www.michigan.gov/mdhhs/-/media/Project/Websites/mdhhs/Inside-MDHHS/Reports-and-Statistics---Human-Services/State-Plans-and-Federal-Regulations/TANF_State_Plan_2023.pdf", + ) + defined_for = StateCode.MI + + def formula(spm_unit, period, parameters): + p = parameters(period).gov.states.mi.mdhhs.fip + + # Get total household assets + # Using spm_unit_assets as a proxy for countable assets + # Full implementation would apply specific asset exclusions per BEM 400 + # Note: spm_unit_assets is a YEAR variable, so we access it with this_year + assets = spm_unit("spm_unit_assets", period.this_year) + + return assets <= p.resources.limit diff --git a/policyengine_us/variables/gov/states/mi/mdhhs/fip/income/benefit/mi_fip_countable_earned_income_for_benefit.py b/policyengine_us/variables/gov/states/mi/mdhhs/fip/income/benefit/mi_fip_countable_earned_income_for_benefit.py new file mode 100644 index 00000000000..4c34758759d --- /dev/null +++ b/policyengine_us/variables/gov/states/mi/mdhhs/fip/income/benefit/mi_fip_countable_earned_income_for_benefit.py @@ -0,0 +1,19 @@ +from policyengine_us.model_api import * + + +class mi_fip_countable_earned_income_for_benefit(Variable): + value_type = float + entity = SPMUnit + label = "Michigan FIP countable earned income for benefit" + unit = USD + definition_period = MONTH + reference = ( + "https://mdhhs-pres-prod.michigan.gov/olmweb/ex/BP/Public/BEM/518.pdf", + "https://mdhhs-pres-prod.michigan.gov/olmweb/EX/BP/Public/BEM/520.pdf", + ) + defined_for = StateCode.MI + + # BEM 518: "Apply this disregard separately to each program group member's + # earned income" + # Sum the per-person countable earned income across all household members + adds = ["mi_fip_earned_income_after_deductions_for_benefit_person"] diff --git a/policyengine_us/variables/gov/states/mi/mdhhs/fip/income/benefit/mi_fip_countable_income_for_benefit.py b/policyengine_us/variables/gov/states/mi/mdhhs/fip/income/benefit/mi_fip_countable_income_for_benefit.py new file mode 100644 index 00000000000..25d8a695698 --- /dev/null +++ b/policyengine_us/variables/gov/states/mi/mdhhs/fip/income/benefit/mi_fip_countable_income_for_benefit.py @@ -0,0 +1,22 @@ +from policyengine_us.model_api import * + + +class mi_fip_countable_income_for_benefit(Variable): + value_type = float + entity = SPMUnit + label = "Michigan FIP countable income for benefit" + unit = USD + definition_period = MONTH + reference = ( + "https://mdhhs-pres-prod.michigan.gov/olmweb/ex/BP/Public/BEM/520.pdf", + "https://mdhhs-pres-prod.michigan.gov/olmweb/ex/BP/Public/BEM/503.pdf", + ) + defined_for = StateCode.MI + + # BEM 520 Section D Line 9: "Enter the sum of line 7 and line 8" + # Line 7: Countable earned income (after per-person deductions) + # Line 8: All countable unearned income (gross, no deductions per BEM 503) + adds = [ + "mi_fip_countable_earned_income_for_benefit", + "tanf_gross_unearned_income", + ] diff --git a/policyengine_us/variables/gov/states/mi/mdhhs/fip/income/benefit/mi_fip_earned_income_after_deductions_for_benefit_person.py b/policyengine_us/variables/gov/states/mi/mdhhs/fip/income/benefit/mi_fip_earned_income_after_deductions_for_benefit_person.py new file mode 100644 index 00000000000..a86541d504d --- /dev/null +++ b/policyengine_us/variables/gov/states/mi/mdhhs/fip/income/benefit/mi_fip_earned_income_after_deductions_for_benefit_person.py @@ -0,0 +1,41 @@ +from policyengine_us.model_api import * + + +class mi_fip_earned_income_after_deductions_for_benefit_person(Variable): + value_type = float + entity = Person + label = ( + "Michigan FIP earned income after deductions for benefit per person" + ) + unit = USD + definition_period = MONTH + defined_for = StateCode.MI + reference = ( + "https://mdhhs-pres-prod.michigan.gov/olmweb/ex/BP/Public/BEM/518.pdf", + "https://mdhhs-pres-prod.michigan.gov/olmweb/EX/BP/Public/BEM/520.pdf", + ) + + def formula(person, period, parameters): + p = parameters(period).gov.states.mi.mdhhs.fip.income.disregard + + # Get gross earned income for this person + gross_earned = person("tanf_gross_earned_income", period) + + # BEM 518 Page 5 - "Issuance Earned Income Disregard" + # BEM 520 Section D - Issuance Test (for benefit calculation) + # "Deduct $200 from each person's countable earnings. Then deduct an + # additional 50 percent of each person's remaining earnings." + + # This is used for BENEFIT CALCULATION for ALL recipients + # (both new applicants and enrolled recipients use 50% for benefits) + + # Step 1: Deduct $200 from this person's earnings + flat_deduction = p.flat + remainder = max_(gross_earned - flat_deduction, 0) + + # Step 2: Deduct 50% of remainder (for benefit calculation) + percent_deduction = remainder * p.ongoing_rate + + # Step 3: Countable income (cannot be negative) + # "The total disregard cannot exceed countable earnings" - BEM 518 + return max_(gross_earned - flat_deduction - percent_deduction, 0) diff --git a/policyengine_us/variables/gov/states/mi/mdhhs/fip/income/eligibility/mi_fip_countable_earned_income_for_eligibility.py b/policyengine_us/variables/gov/states/mi/mdhhs/fip/income/eligibility/mi_fip_countable_earned_income_for_eligibility.py new file mode 100644 index 00000000000..63f7ec36679 --- /dev/null +++ b/policyengine_us/variables/gov/states/mi/mdhhs/fip/income/eligibility/mi_fip_countable_earned_income_for_eligibility.py @@ -0,0 +1,20 @@ +from policyengine_us.model_api import * + + +class mi_fip_countable_earned_income_for_eligibility(Variable): + value_type = float + entity = SPMUnit + label = "Michigan FIP countable earned income for eligibility" + unit = USD + definition_period = MONTH + reference = ( + "https://mdhhs-pres-prod.michigan.gov/olmweb/ex/BP/Public/BEM/518.pdf", + "https://mdhhs-pres-prod.michigan.gov/olmweb/EX/BP/Public/BEM/520.pdf", + ) + defined_for = StateCode.MI + + # BEM 518: "Apply this disregard separately to each program group member's + # earned income" + # BEM 520 Section C: Qualifying Income Test (uses 20% rate for new applicants) + # Sum the per-person initial deductions across all household members + adds = ["mi_fip_earned_income_after_deductions_for_eligibility_person"] diff --git a/policyengine_us/variables/gov/states/mi/mdhhs/fip/income/eligibility/mi_fip_countable_income_for_eligibility.py b/policyengine_us/variables/gov/states/mi/mdhhs/fip/income/eligibility/mi_fip_countable_income_for_eligibility.py new file mode 100644 index 00000000000..5d12df8fb00 --- /dev/null +++ b/policyengine_us/variables/gov/states/mi/mdhhs/fip/income/eligibility/mi_fip_countable_income_for_eligibility.py @@ -0,0 +1,23 @@ +from policyengine_us.model_api import * + + +class mi_fip_countable_income_for_eligibility(Variable): + value_type = float + entity = SPMUnit + label = "Michigan FIP countable income for eligibility" + unit = USD + definition_period = MONTH + reference = ( + "https://mdhhs-pres-prod.michigan.gov/olmweb/ex/BP/Public/BEM/520.pdf", + "https://mdhhs-pres-prod.michigan.gov/olmweb/ex/BP/Public/BEM/503.pdf", + ) + defined_for = StateCode.MI + + # BEM 520 Section C: Qualifying Income Test + # Line C9: "Enter the sum of line 7 and line 8" + # Line 7: Countable earned income (20% rate for initial eligibility) + # Line 8: All countable unearned income (gross, no deductions per BEM 503) + adds = [ + "mi_fip_countable_earned_income_for_eligibility", + "tanf_gross_unearned_income", + ] diff --git a/policyengine_us/variables/gov/states/mi/mdhhs/fip/income/eligibility/mi_fip_earned_income_after_deductions_for_eligibility_person.py b/policyengine_us/variables/gov/states/mi/mdhhs/fip/income/eligibility/mi_fip_earned_income_after_deductions_for_eligibility_person.py new file mode 100644 index 00000000000..f5582c88ab4 --- /dev/null +++ b/policyengine_us/variables/gov/states/mi/mdhhs/fip/income/eligibility/mi_fip_earned_income_after_deductions_for_eligibility_person.py @@ -0,0 +1,38 @@ +from policyengine_us.model_api import * + + +class mi_fip_earned_income_after_deductions_for_eligibility_person(Variable): + value_type = float + entity = Person + label = "Michigan FIP earned income after deductions for eligibility per person" + unit = USD + definition_period = MONTH + defined_for = StateCode.MI + reference = ( + "https://mdhhs-pres-prod.michigan.gov/olmweb/ex/BP/Public/BEM/518.pdf", + "https://mdhhs-pres-prod.michigan.gov/olmweb/EX/BP/Public/BEM/520.pdf", + ) + + def formula(person, period, parameters): + p = parameters(period).gov.states.mi.mdhhs.fip.income.disregard + + # Get gross earned income for this person + gross_earned = person("tanf_gross_earned_income", period) + + # BEM 518 Page 5 - "Qualifying Earned Income Disregard" + # BEM 520 Section C - Qualifying Income Test (for initial eligibility) + # "At application, deduct $200 from each person's countable earnings. + # Then deduct an additional 20 percent of each person's remaining earnings." + + # Step 1: Deduct $200 from this person's earnings + flat_deduction = p.flat + remainder = max_(gross_earned - flat_deduction, 0) + + # Step 2: Deduct 20% of remainder (for initial eligibility test) + percent_deduction = remainder * p.initial_rate + + # Step 3: Total deduction + total_deduction = flat_deduction + percent_deduction + + # Step 4: Countable income (cannot be negative) + return max_(gross_earned - total_deduction, 0) diff --git a/policyengine_us/variables/gov/states/mi/mdhhs/fip/mi_fip.py b/policyengine_us/variables/gov/states/mi/mdhhs/fip/mi_fip.py new file mode 100644 index 00000000000..0f14136e4b6 --- /dev/null +++ b/policyengine_us/variables/gov/states/mi/mdhhs/fip/mi_fip.py @@ -0,0 +1,30 @@ +from policyengine_us.model_api import * + + +class mi_fip(Variable): + value_type = float + entity = SPMUnit + label = "Michigan Family Independence Program" + unit = USD + definition_period = MONTH + reference = ( + "https://mdhhs-pres-prod.michigan.gov/olmweb/EX/BP/Public/BEM/520.pdf", + "https://mdhhs-pres-prod.michigan.gov/olmweb/ex/BP/Public/BEM/518.pdf", + ) + defined_for = "mi_fip_eligible" + + def formula(spm_unit, period, parameters): + # BEM 520 Section F: Issuance Amount + # "Subtract line 2 and line 3 from the amount on line 1" + # Line 1: Payment Standard (from Section B) + # Line 2: Countable Income (from Section D - uses 50% deduction) + # Line 3: Recoupment (simplified: excluded) + + payment_standard = spm_unit("mi_fip_payment_standard", period) + countable_income = spm_unit( + "mi_fip_countable_income_for_benefit", period + ) + + # BEM 518: Minimum benefit requirement is $10 deficit + # Negative benefits are set to 0 + return max_(payment_standard - countable_income, 0) diff --git a/policyengine_us/variables/gov/states/mi/mdhhs/fip/mi_fip_payment_standard.py b/policyengine_us/variables/gov/states/mi/mdhhs/fip/mi_fip_payment_standard.py new file mode 100644 index 00000000000..c2bc8251c8c --- /dev/null +++ b/policyengine_us/variables/gov/states/mi/mdhhs/fip/mi_fip_payment_standard.py @@ -0,0 +1,32 @@ +from policyengine_us.model_api import * + + +class mi_fip_payment_standard(Variable): + value_type = float + entity = SPMUnit + label = "Michigan FIP payment standard" + unit = USD + definition_period = MONTH + reference = ( + "https://mdhhs-pres-prod.michigan.gov/olmweb/ex/RF/Public/RFT/210.pdf", + "https://www.michigan.gov/mdhhs/-/media/Project/Websites/mdhhs/Inside-MDHHS/Reports-and-Statistics---Human-Services/State-Plans-and-Federal-Regulations/TANF_State_Plan_2023.pdf", + ) + defined_for = StateCode.MI + + def formula(spm_unit, period, parameters): + p = parameters(period).gov.states.mi.mdhhs.fip.payment_standard + + # Determine household size + size = spm_unit("spm_unit_size", period) + + # For sizes 1-7, use the bracket schedule + # For size 8+, use the size 7 amount plus additional per person + max_household_size = p.max_household_size + capped_size = min_(size, max_household_size) + base_standard = p.base.calc(capped_size) + + # Add additional amount for household size 8+ + additional_persons = max_(size - max_household_size, 0) + additional_amount = additional_persons * p.additional + + return base_standard + additional_amount