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/deductions/earned_income_disregard/flat_amount.yaml b/policyengine_us/parameters/gov/states/mi/mdhhs/fip/income/deductions/earned_income_disregard/flat_amount.yaml new file mode 100644 index 00000000000..cbe02e5030f --- /dev/null +++ b/policyengine_us/parameters/gov/states/mi/mdhhs/fip/income/deductions/earned_income_disregard/flat_amount.yaml @@ -0,0 +1,15 @@ +description: Michigan deducts this fixed dollar amount from earned income when determining Family Independence Program eligibility and calculating benefits. + +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 + - 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 + publication_date: 2023-01-01 diff --git a/policyengine_us/parameters/gov/states/mi/mdhhs/fip/income/deductions/earned_income_disregard/initial_percent.yaml b/policyengine_us/parameters/gov/states/mi/mdhhs/fip/income/deductions/earned_income_disregard/initial_percent.yaml new file mode 100644 index 00000000000..14b2759597e --- /dev/null +++ b/policyengine_us/parameters/gov/states/mi/mdhhs/fip/income/deductions/earned_income_disregard/initial_percent.yaml @@ -0,0 +1,14 @@ +description: Michigan deducts this share of remaining earned income (after the flat deduction) when determining initial Family Independence Program eligibility for new applicants. + +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 (Qualifying Earned Income Disregard) + href: https://mdhhs-pres-prod.michigan.gov/olmweb/ex/BP/Public/BEM/518.pdf + - title: Michigan DHHS BEM 520 - Computing the FIP/RCA/SDA Budget (Section C) + href: https://mdhhs-pres-prod.michigan.gov/olmweb/EX/BP/Public/BEM/520.pdf diff --git a/policyengine_us/parameters/gov/states/mi/mdhhs/fip/income/deductions/earned_income_disregard/ongoing_percent.yaml b/policyengine_us/parameters/gov/states/mi/mdhhs/fip/income/deductions/earned_income_disregard/ongoing_percent.yaml new file mode 100644 index 00000000000..ad2f82e8ae9 --- /dev/null +++ b/policyengine_us/parameters/gov/states/mi/mdhhs/fip/income/deductions/earned_income_disregard/ongoing_percent.yaml @@ -0,0 +1,15 @@ +description: Michigan deducts this share of remaining earned income (after the flat deduction) when calculating Family Independence Program benefits for currently enrolled recipients. + +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 (Issuance Earned Income Disregard) + href: https://mdhhs-pres-prod.michigan.gov/olmweb/ex/BP/Public/BEM/518.pdf + - title: Michigan DHHS BEM 520 - Computing the FIP/RCA/SDA Budget (Section D) + href: https://mdhhs-pres-prod.michigan.gov/olmweb/EX/BP/Public/BEM/520.pdf diff --git a/policyengine_us/parameters/gov/states/mi/mdhhs/fip/payment_standard/additional_person_increment.yaml b/policyengine_us/parameters/gov/states/mi/mdhhs/fip/payment_standard/additional_person_increment.yaml new file mode 100644 index 00000000000..8984bb12971 --- /dev/null +++ b/policyengine_us/parameters/gov/states/mi/mdhhs/fip/payment_standard/additional_person_increment.yaml @@ -0,0 +1,18 @@ +description: Michigan adds this amount to the Family Independence Program payment standard for each person beyond household size 7. + +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 + publication_date: 2025-01-01 diff --git a/policyengine_us/parameters/gov/states/mi/mdhhs/fip/payment_standard/amounts.yaml b/policyengine_us/parameters/gov/states/mi/mdhhs/fip/payment_standard/amounts.yaml new file mode 100644 index 00000000000..e31ddc1a957 --- /dev/null +++ b/policyengine_us/parameters/gov/states/mi/mdhhs/fip/payment_standard/amounts.yaml @@ -0,0 +1,53 @@ +description: Michigan provides up to this monthly payment amount under the Family Independence Program, based on household size. + +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 + publication_date: 2025-01-01 + +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/resource_limit.yaml b/policyengine_us/parameters/gov/states/mi/mdhhs/fip/resource_limit.yaml new file mode 100644 index 00000000000..98de56ed125 --- /dev/null +++ b/policyengine_us/parameters/gov/states/mi/mdhhs/fip/resource_limit.yaml @@ -0,0 +1,14 @@ +description: Michigan limits the Family Independence Program to households with countable assets up to this amount. + +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 + effective_date: 2025-10-01 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/mi_fip_countable_earned_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/fip/income/mi_fip_countable_earned_income.yaml new file mode 100644 index 00000000000..87df91270df --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/fip/income/mi_fip_countable_earned_income.yaml @@ -0,0 +1,108 @@ +- name: Household with zero earned income + period: 2025-01 + input: + employment_income_before_lsr: 0 + state_code: MI + output: + mi_fip_countable_earned_income: 0 + +- name: Household with earned income below fixed deduction + period: 2025-01 + input: + employment_income_before_lsr: 1_800 # $150/month + state_code: MI + output: + # Monthly: $1,800/12 = $150 + # Issuance deduction (50%): $200 + 50% of remainder + # Since $150 < $200, deduction is $200 + # Countable = max($150 - $200, 0) = $0 + mi_fip_countable_earned_income: 0 + +- name: Household with earned income equal to fixed deduction + period: 2025-01 + input: + employment_income_before_lsr: 2_400 # $200/month + state_code: MI + output: + # Monthly: $2,400/12 = $200 + # Issuance deduction (50%): $200 + 50% of remainder + # Remainder = max($200 - $200, 0) = $0 + # Total deduction = $200 + (50% × $0) = $200 + # Countable = $200 - $200 = $0 + mi_fip_countable_earned_income: 0 + +- name: Household with earned income just above fixed deduction + period: 2025-01 + input: + employment_income_before_lsr: 3_000 # $250/month + state_code: MI + output: + # Monthly: $3,000/12 = $250 + # Issuance deduction (50%): $200 + 50% of remainder + # Remainder = $250 - $200 = $50 + # Total deduction = $200 + (50% × $50) = $200 + $25 = $225 + # Countable = $250 - $225 = $25 + mi_fip_countable_earned_income: 25 + +- name: Household with moderate earned income + period: 2025-01 + input: + employment_income_before_lsr: 12_000 # $1,000/month + state_code: MI + output: + # Monthly: $12,000/12 = $1,000 + # Issuance deduction (50%): $200 + 50% of remainder + # Remainder = $1,000 - $200 = $800 + # Total deduction = $200 + (50% × $800) = $200 + $400 = $600 + # Countable = $1,000 - $600 = $400 + mi_fip_countable_earned_income: 400 + +- name: Household with high earned income + period: 2025-01 + input: + employment_income_before_lsr: 24_000 # $2,000/month + state_code: MI + output: + # Monthly: $24,000/12 = $2,000 + # Issuance deduction (50%): $200 + 50% of remainder + # Remainder = $2,000 - $200 = $1,800 + # Total deduction = $200 + (50% × $1,800) = $200 + $900 = $1,100 + # Countable = $2,000 - $1,100 = $900 + mi_fip_countable_earned_income: 900 + +- name: Household with multiple earners (per-person deductions) + period: 2025-01 + input: + people: + person1: + employment_income_before_lsr: 12_000 # $1,000/month + age: 35 + person2: + employment_income_before_lsr: 6_000 # $500/month + age: 30 + spm_units: + spm_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: MI + output: + # BEM 518: "Apply this disregard separately to each program group member" + # Issuance deduction (50% for benefit calculation) + # Person 1 monthly: $12,000/12 = $1,000 + # Deduction = $200 + (50% × $800) = $600 + # Countable = $1,000 - $600 = $400 + # Person 2 monthly: $6,000/12 = $500 + # Deduction = $200 + (50% × $300) = $350 + # Countable = $500 - $350 = $150 + # Total countable = $400 + $150 = $550 + mi_fip_countable_earned_income: 550 + +- name: Household outside Michigan returns zero + period: 2025-01 + input: + employment_income_before_lsr: 12_000 + state_code: CA + output: + mi_fip_countable_earned_income: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/fip/income/mi_fip_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/fip/income/mi_fip_countable_income.yaml new file mode 100644 index 00000000000..db44067c7bb --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/fip/income/mi_fip_countable_income.yaml @@ -0,0 +1,69 @@ +- name: Household with no income + period: 2025-01 + input: + employment_income_before_lsr: 0 + social_security: 0 + state_code: MI + output: + mi_fip_countable_income: 0 + +- name: Household with earned income only + period: 2025-01 + input: + employment_income_before_lsr: 12_000 # $1,000/month + social_security: 0 + state_code: MI + output: + # Earned: $1,000/month + # Deduction = $200 + (50% × $800) = $600 + # Countable earned = $1,000 - $600 = $400 + # Unearned = $0 + # Total countable = $400 + mi_fip_countable_income: 400 + +- name: Household with unearned income only + period: 2025-01 + input: + employment_income_before_lsr: 0 + social_security_disability: 6_000 # $500/month + state_code: MI + output: + # Earned = $0 + # Unearned = $500/month (no deduction per BEM 503) + # Total countable = $500 + mi_fip_countable_income: 500 + +- name: Household with mixed earned and unearned income + period: 2025-01 + input: + employment_income_before_lsr: 9_600 # $800/month + social_security_retirement: 3_600 # $300/month + state_code: MI + output: + # Earned: $800/month + # Deduction = $200 + (50% × $600) = $500 + # Countable earned = $800 - $500 = $300 + # Unearned = $300/month (no deduction) + # Total countable = $300 + $300 = $600 + mi_fip_countable_income: 600 + +- name: Household with zero earned income but has unearned + period: 2025-01 + input: + employment_income_before_lsr: 0 + unemployment_compensation: 12_000 # $1,000/month + state_code: MI + output: + # Earned = $0 + # Unearned = $1,000/month (no deduction) + # Total countable = $1,000 + mi_fip_countable_income: 1_000 + +- name: Household outside Michigan returns zero + period: 2025-01 + input: + employment_income_before_lsr: 12_000 + social_security: 6_000 + state_code: CA + output: + mi_fip_countable_income: 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..6c71786a06c --- /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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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..773c19565a7 --- /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: 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: 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: 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: 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: 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: 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: 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..981f2b5559d --- /dev/null +++ b/policyengine_us/variables/gov/states/mi/mdhhs/fip/eligibility/mi_fip_income_eligible.py @@ -0,0 +1,35 @@ +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_initial = spm_unit( + "mi_fip_countable_income_initial", period + ) + passes_qualifying_test = countable_income_initial < payment_standard + + # Enrolled recipients: Use Issuance Deficit Test (BEM 520 Section D) + # - Uses 50% deduction rate (more generous) + countable_income = spm_unit("mi_fip_countable_income", period) + passes_issuance_test = countable_income < 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..c926093cf43 --- /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.resource_limit diff --git a/policyengine_us/variables/gov/states/mi/mdhhs/fip/income/mi_fip_countable_earned_income.py b/policyengine_us/variables/gov/states/mi/mdhhs/fip/income/mi_fip_countable_earned_income.py new file mode 100644 index 00000000000..a779a40a596 --- /dev/null +++ b/policyengine_us/variables/gov/states/mi/mdhhs/fip/income/mi_fip_countable_earned_income.py @@ -0,0 +1,19 @@ +from policyengine_us.model_api import * + + +class mi_fip_countable_earned_income(Variable): + value_type = float + entity = SPMUnit + label = "Michigan FIP countable earned income" + 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_person"] diff --git a/policyengine_us/variables/gov/states/mi/mdhhs/fip/income/mi_fip_countable_earned_income_initial.py b/policyengine_us/variables/gov/states/mi/mdhhs/fip/income/mi_fip_countable_earned_income_initial.py new file mode 100644 index 00000000000..3408c86ec83 --- /dev/null +++ b/policyengine_us/variables/gov/states/mi/mdhhs/fip/income/mi_fip_countable_earned_income_initial.py @@ -0,0 +1,20 @@ +from policyengine_us.model_api import * + + +class mi_fip_countable_earned_income_initial(Variable): + value_type = float + entity = SPMUnit + label = "Michigan FIP countable earned income for initial 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_initial_person"] diff --git a/policyengine_us/variables/gov/states/mi/mdhhs/fip/income/mi_fip_countable_income.py b/policyengine_us/variables/gov/states/mi/mdhhs/fip/income/mi_fip_countable_income.py new file mode 100644 index 00000000000..ee294147b1b --- /dev/null +++ b/policyengine_us/variables/gov/states/mi/mdhhs/fip/income/mi_fip_countable_income.py @@ -0,0 +1,19 @@ +from policyengine_us.model_api import * + + +class mi_fip_countable_income(Variable): + value_type = float + entity = SPMUnit + label = "Michigan FIP countable income" + 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", "tanf_gross_unearned_income"] diff --git a/policyengine_us/variables/gov/states/mi/mdhhs/fip/income/mi_fip_countable_income_initial.py b/policyengine_us/variables/gov/states/mi/mdhhs/fip/income/mi_fip_countable_income_initial.py new file mode 100644 index 00000000000..445b09a122c --- /dev/null +++ b/policyengine_us/variables/gov/states/mi/mdhhs/fip/income/mi_fip_countable_income_initial.py @@ -0,0 +1,23 @@ +from policyengine_us.model_api import * + + +class mi_fip_countable_income_initial(Variable): + value_type = float + entity = SPMUnit + label = "Michigan FIP countable income for initial 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_initial", + "tanf_gross_unearned_income", + ] diff --git a/policyengine_us/variables/gov/states/mi/mdhhs/fip/income/mi_fip_earned_income_after_deductions_initial_person.py b/policyengine_us/variables/gov/states/mi/mdhhs/fip/income/mi_fip_earned_income_after_deductions_initial_person.py new file mode 100644 index 00000000000..2c1e63b9278 --- /dev/null +++ b/policyengine_us/variables/gov/states/mi/mdhhs/fip/income/mi_fip_earned_income_after_deductions_initial_person.py @@ -0,0 +1,40 @@ +from policyengine_us.model_api import * + + +class mi_fip_earned_income_after_deductions_initial_person(Variable): + value_type = float + entity = Person + label = "Michigan FIP earned income after initial deductions 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.deductions.earned_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_amount + remainder = max_(gross_earned - flat_deduction, 0) + + # Step 2: Deduct 20% of remainder (for initial eligibility test) + percent_deduction = remainder * p.initial_percent + + # 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/income/mi_fip_earned_income_after_deductions_person.py b/policyengine_us/variables/gov/states/mi/mdhhs/fip/income/mi_fip_earned_income_after_deductions_person.py new file mode 100644 index 00000000000..afea05dfa00 --- /dev/null +++ b/policyengine_us/variables/gov/states/mi/mdhhs/fip/income/mi_fip_earned_income_after_deductions_person.py @@ -0,0 +1,41 @@ +from policyengine_us.model_api import * + + +class mi_fip_earned_income_after_deductions_person(Variable): + value_type = float + entity = Person + label = "Michigan FIP earned income after deductions 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.deductions.earned_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_amount + remainder = max_(gross_earned - flat_deduction, 0) + + # Step 2: Deduct 50% of remainder (for benefit calculation) + percent_deduction = remainder * p.ongoing_percent + + # 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/mi_fip.py b/policyengine_us/variables/gov/states/mi/mdhhs/fip/mi_fip.py new file mode 100644 index 00000000000..07a84a3db4d --- /dev/null +++ b/policyengine_us/variables/gov/states/mi/mdhhs/fip/mi_fip.py @@ -0,0 +1,28 @@ +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", 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..4db3ee23681 --- /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_bracket_size = 7 # Maximum size in bracket schedule + size_capped = min_(size, max_bracket_size) + base_standard = p.amounts.calc(size_capped) + + # Add additional amount for household size 8+ + additional_persons = max_(size - max_bracket_size, 0) + additional_amount = additional_persons * p.additional_person_increment + + return base_standard + additional_amount