diff --git a/changelog_entry.yaml b/changelog_entry.yaml index e69de29bb2d..0d297ed8844 100644 --- a/changelog_entry.yaml +++ b/changelog_entry.yaml @@ -0,0 +1,4 @@ +- bump: minor + changes: + added: + - Ohio Works First (OWF) cash assistance program. diff --git a/policyengine_us/parameters/gov/states/oh/odjfs/owf/income/deductions/earned_income_disregard/flat_amount.yaml b/policyengine_us/parameters/gov/states/oh/odjfs/owf/income/deductions/earned_income_disregard/flat_amount.yaml new file mode 100644 index 00000000000..8025d13a7c1 --- /dev/null +++ b/policyengine_us/parameters/gov/states/oh/odjfs/owf/income/deductions/earned_income_disregard/flat_amount.yaml @@ -0,0 +1,12 @@ +description: Ohio deducts this flat amount from gross earned income under Ohio Works First program. + +values: + 2023-10-01: 250 + +metadata: + unit: currency-USD + period: month + label: Ohio Works First earned income flat disregard + reference: + - title: Ohio Revised Code § 5107.10(D)(3) - Two hundred-fifty dollar disregard + href: https://codes.ohio.gov/ohio-revised-code/section-5107.10 diff --git a/policyengine_us/parameters/gov/states/oh/odjfs/owf/income/deductions/earned_income_disregard/percentage_of_disregard.yaml b/policyengine_us/parameters/gov/states/oh/odjfs/owf/income/deductions/earned_income_disregard/percentage_of_disregard.yaml new file mode 100644 index 00000000000..b0515ebc5f6 --- /dev/null +++ b/policyengine_us/parameters/gov/states/oh/odjfs/owf/income/deductions/earned_income_disregard/percentage_of_disregard.yaml @@ -0,0 +1,12 @@ +description: Ohio disregards this share of remaining earned income after the flat amount deduction under Ohio Works First program. + +values: + 2023-10-01: 0.5 + +metadata: + unit: /1 + period: month + label: Ohio Works First earned income percentage disregard + reference: + - title: Ohio Revised Code § 5107.10(D)(3) - One-half of remainder disregard + href: https://codes.ohio.gov/ohio-revised-code/section-5107.10 diff --git a/policyengine_us/parameters/gov/states/oh/odjfs/owf/initial_eligibility/income_limit_rate.yaml b/policyengine_us/parameters/gov/states/oh/odjfs/owf/initial_eligibility/income_limit_rate.yaml new file mode 100644 index 00000000000..8f0f1fa7671 --- /dev/null +++ b/policyengine_us/parameters/gov/states/oh/odjfs/owf/initial_eligibility/income_limit_rate.yaml @@ -0,0 +1,14 @@ +description: Ohio applies this percentage of federal poverty level as initial eligibility income limit under Ohio Works First program. + +values: + 2023-10-01: 0.5 + +metadata: + unit: /1 + period: year + label: Ohio Works First initial eligibility income limit rate + reference: + - title: Ohio Revised Code § 5107.10(D) - Gross income cannot exceed 50% of federal poverty guidelines + href: https://codes.ohio.gov/ohio-revised-code/section-5107.10 + - title: ODJFS OWF Payment Standards Table - Initial Eligibility Test column + href: https://dam.assets.ohio.gov/image/upload/jfs.ohio.gov/ofam/OWFPaymentStandards.pdf diff --git a/policyengine_us/parameters/gov/states/oh/odjfs/owf/payment_standard/additional_person_increment.yaml b/policyengine_us/parameters/gov/states/oh/odjfs/owf/payment_standard/additional_person_increment.yaml new file mode 100644 index 00000000000..1e8aa4d6c7d --- /dev/null +++ b/policyengine_us/parameters/gov/states/oh/odjfs/owf/payment_standard/additional_person_increment.yaml @@ -0,0 +1,14 @@ +description: Ohio adds this amount to the payment standard for each additional person under Ohio Works First program. + +values: + 2025-01-01: 155 + +metadata: + unit: currency-USD + period: month + label: Ohio Works First additional person increment + reference: + - title: ODJFS Action Change Transmittal Letter No. 295 - January 1, 2025 COLA Increase + href: https://emanuals.jfs.ohio.gov/CashFoodAssist/CAM/ACT/ + - title: Ohio Administrative Code 5101:1-23-40 - Payments + href: http://codes.ohio.gov/oac/5101:1-23-40 diff --git a/policyengine_us/parameters/gov/states/oh/odjfs/owf/payment_standard/amounts.yaml b/policyengine_us/parameters/gov/states/oh/odjfs/owf/payment_standard/amounts.yaml new file mode 100644 index 00000000000..5f27833d1c3 --- /dev/null +++ b/policyengine_us/parameters/gov/states/oh/odjfs/owf/payment_standard/amounts.yaml @@ -0,0 +1,31 @@ +description: Ohio sets this monthly payment standard for Ohio Works First program. +metadata: + unit: currency-USD + period: month + label: Ohio Works First payment standard amounts + breakdown: + - range(1, 9) + reference: + - title: ODJFS Action Change Transmittal Letter No. 295 - January 1, 2025 COLA Increase + href: https://emanuals.jfs.ohio.gov/CashFoodAssist/CAM/ACT/ + - title: Ohio Revised Code § 5107.04 - Payment standard adjustments + href: https://codes.ohio.gov/ohio-revised-code/section-5107.04 + - title: Ohio Administrative Code 5101:1-23-40 - Payments + href: http://codes.ohio.gov/oac/5101:1-23-40 + +1: + 2025-01-01: 372 +2: + 2025-01-01: 507 +3: + 2025-01-01: 623 +4: + 2025-01-01: 768 +5: + 2025-01-01: 899 +6: + 2025-01-01: 1_000 +7: + 2025-01-01: 1_118 +8: + 2025-01-01: 1_240 diff --git a/policyengine_us/tests/policy/baseline/gov/states/oh/odjfs/owf/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/oh/odjfs/owf/integration.yaml new file mode 100644 index 00000000000..ef83480b71b --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/oh/odjfs/owf/integration.yaml @@ -0,0 +1,152 @@ +- name: Single parent with two children and moderate earned income receives benefit + period: 2025-01 + input: + people: + parent: + age: 32 + employment_income_before_lsr: 9_600 + child1: + age: 5 + is_tax_unit_dependent: true + child2: + age: 8 + is_tax_unit_dependent: true + spm_units: + spm_unit: + members: [parent, child1, child2] + households: + household: + members: [parent, child1, child2] + state_code: OH + output: + # Income calculation: + # Gross earned income: $9,600/year = $800/month + # Earned income disregard: $250 + 50% of remainder + # $800 - $250 = $550; $550 × 50% = $275 disregarded + # Countable income: $550 - $275 = $275/month + # + # Payment standard for 3: $623/month + # Benefit: $623 - $275 = $348/month + oh_owf_countable_income: 275 + oh_owf_payment_standard: 623 + oh_owf_income_eligible: true + oh_owf_eligible: true + oh_owf: 348 + +- name: Single parent with one child and no income receives maximum benefit + period: 2025-01 + input: + people: + parent: + age: 28 + child1: + age: 3 + is_tax_unit_dependent: true + spm_units: + spm_unit: + members: [parent, child1] + households: + household: + members: [parent, child1] + state_code: OH + output: + # No income means no disregard needed + # Countable income: $0 + # Payment standard for 2: $507/month (January 2025 COLA increase) + oh_owf_countable_income: 0 + oh_owf_payment_standard: 507 + oh_owf_income_eligible: true + oh_owf_eligible: true + oh_owf: 507 + +- name: Single parent with earned and unearned income receives reduced benefit + period: 2025-01 + input: + people: + parent: + age: 40 + employment_income_before_lsr: 6_000 + social_security: 4_800 + child1: + age: 14 + is_tax_unit_dependent: true + child2: + age: 16 + is_tax_unit_dependent: true + spm_units: + spm_unit: + members: [parent, child1, child2] + households: + household: + members: [parent, child1, child2] + state_code: OH + output: + # Earned: $6,000/year = $500/month + # Disregard: $500 - $250 = $250; $250 × 50% = $125 + # Countable earned: $250 - $125 = $125/month + # + # Unearned: $4,800/year = $400/month (no disregard) + # Total countable: $125 + $400 = $525/month + # + # Payment standard for 3: $623/month + # Benefit: $623 - $525 = $98/month + oh_owf_countable_income: 525 + oh_owf_payment_standard: 623 + oh_owf_income_eligible: true + oh_owf_eligible: true + oh_owf: 98 + +- name: Family with income above payment standard receives no benefit + period: 2025-01 + input: + people: + parent: + age: 35 + employment_income_before_lsr: 18_000 + child1: + age: 6 + is_tax_unit_dependent: true + child2: + age: 9 + is_tax_unit_dependent: true + spm_units: + spm_unit: + members: [parent, child1, child2] + households: + household: + members: [parent, child1, child2] + state_code: OH + output: + # Earned: $18,000/year = $1,500/month + # Disregard: $1,500 - $250 = $1,250; $1,250 × 50% = $625 + # Countable: $1,250 - $625 = $625/month + # + # Payment standard for 3: $623/month + # Countable income ($625) >= payment standard ($623) + # Not income eligible, no benefit + oh_owf_countable_income: 625 + oh_owf_payment_standard: 623 + oh_owf_income_eligible: false + oh_owf_eligible: false + oh_owf: 0 + +- name: Two adults with no children are not eligible + period: 2025-01 + input: + people: + adult1: + age: 28 + adult2: + age: 30 + spm_units: + spm_unit: + members: [adult1, adult2] + households: + household: + members: [adult1, adult2] + state_code: OH + output: + # No minor children means categorically ineligible + # Must have at least one minor child or be pregnant + oh_owf_eligible: false + oh_owf: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/oh/odjfs/owf/oh_owf_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/oh/odjfs/owf/oh_owf_countable_income.yaml new file mode 100644 index 00000000000..3c106cd98ea --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/oh/odjfs/owf/oh_owf_countable_income.yaml @@ -0,0 +1,113 @@ +- name: Case 1, no income. + period: 2025-01 + input: + employment_income_before_lsr: 0 + social_security: 0 + state_code: OH + output: + oh_owf_countable_income: 0 +- name: Case 2, earned income only, low amount. + period: 2025-01 + input: + employment_income_before_lsr: 6000 + social_security: 0 + state_code: OH + output: + oh_owf_countable_income: 125.0 +- name: Case 3, earned income only, moderate amount. + period: 2025-01 + input: + employment_income_before_lsr: 12000 + social_security: 0 + state_code: OH + output: + oh_owf_countable_income: 375.0 +- name: Case 4, unearned income only. + period: 2025-01 + input: + employment_income_before_lsr: 0 + social_security: 6000 + state_code: OH + output: + oh_owf_countable_income: 500.0 +- name: Case 5, both earned and unearned income. + period: 2025-01 + input: + employment_income_before_lsr: 9600 + social_security: 3600 + state_code: OH + output: + oh_owf_countable_income: 575.0 +- name: Case 6, earned income below disregard threshold. + period: 2025-01 + input: + employment_income_before_lsr: 2400 + social_security: 0 + state_code: OH + output: + oh_owf_countable_income: 0 +- name: Case 7, earned income exactly at disregard threshold. + period: 2025-01 + input: + employment_income_before_lsr: 3000 + social_security: 0 + state_code: OH + output: + oh_owf_countable_income: 0 +- name: Case 8, high earned income. + period: 2025-01 + input: + employment_income_before_lsr: 24000 + social_security: 0 + state_code: OH + output: + oh_owf_countable_income: 875.0 +- name: Case 9, earned income one dollar over disregard threshold. + period: 2025-01 + input: + employment_income_before_lsr: 3012 + social_security: 0 + state_code: OH + output: + oh_owf_countable_income: 0.5 +- name: Case 10, very high earned income. + period: 2025-01 + absolute_error_margin: 0.1 + input: + employment_income_before_lsr: 100000 + social_security: 0 + state_code: OH + output: + oh_owf_countable_income: 4041.67 +- name: Case 11, small unearned income only. + period: 2025-01 + input: + employment_income_before_lsr: 0 + social_security: 1200 + state_code: OH + output: + oh_owf_countable_income: 100.0 +- name: Case 12, small earned income with small unearned income. + period: 2025-01 + input: + employment_income_before_lsr: 3600 + social_security: 1200 + state_code: OH + output: + oh_owf_countable_income: 125.0 +- name: Case 13, earned income at boundary below disregard. + period: 2025-01 + input: + employment_income_before_lsr: 2999 + social_security: 0 + state_code: OH + output: + oh_owf_countable_income: 0 +- name: Case 14, mixed income with high unearned component. + period: 2025-01 + input: + employment_income_before_lsr: 6000 + social_security: 12000 + state_code: OH + output: + oh_owf_countable_income: 1125.0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/oh/odjfs/owf/oh_owf_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/oh/odjfs/owf/oh_owf_eligible.yaml new file mode 100644 index 00000000000..57d8b8591d1 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/oh/odjfs/owf/oh_owf_eligible.yaml @@ -0,0 +1,239 @@ +- name: Family with child - all requirements met + period: 2025-01 + input: + people: + parent: + age: 30 + employment_income_before_lsr: 5_000 + child: + age: 5 + spm_units: + spm_unit: + members: [parent, child] + households: + household: + members: [parent, child] + state_code: OH + output: + oh_owf_eligible: true + +- name: Pregnant woman - categorically eligible + period: 2025-01 + input: + people: + woman: + age: 28 + is_pregnant: true + employment_income_before_lsr: 3_000 + spm_units: + spm_unit: + members: [woman] + households: + household: + members: [woman] + state_code: OH + output: + oh_owf_eligible: true + +- name: Two adults no children - not demographic eligible + period: 2025-01 + input: + people: + adult1: + age: 30 + adult2: + age: 32 + spm_units: + spm_unit: + members: [adult1, adult2] + households: + household: + members: [adult1, adult2] + state_code: OH + output: + oh_owf_eligible: false + +- name: Family with child but income too high + period: 2025-01 + input: + people: + parent: + age: 30 + employment_income_before_lsr: 20_000 + child: + age: 5 + spm_units: + spm_unit: + members: [parent, child] + households: + household: + members: [parent, child] + state_code: OH + output: + # Income: $20,000/12 = $1,666.67/month + # After disregards: $1,666.67 - $250 = $1,416.67; 50% disregard = $708.33 + # Countable: $1,416.67 - $708.33 = $708.33/month + # Payment standard for 2: $495/month + # Not income eligible since $708.33 >= $495 + oh_owf_eligible: false + +- name: Family with child but no citizen or legal immigrant + period: 2025-01 + input: + people: + parent: + age: 30 + employment_income_before_lsr: 5_000 + immigration_status: UNDOCUMENTED + child: + age: 5 + immigration_status: UNDOCUMENTED + spm_units: + spm_unit: + members: [parent, child] + households: + household: + members: [parent, child] + state_code: OH + output: + oh_owf_eligible: false + +- name: Family with child, parent undocumented but child is citizen - eligible + period: 2025-01 + input: + people: + parent: + age: 30 + employment_income_before_lsr: 5_000 + immigration_status: UNDOCUMENTED + child: + age: 5 + immigration_status: CITIZEN + spm_units: + spm_unit: + members: [parent, child] + households: + household: + members: [parent, child] + state_code: OH + output: + oh_owf_eligible: true + +- name: Family at income threshold - just eligible + period: 2025-01 + input: + people: + parent: + age: 30 + employment_income_before_lsr: 11_879 + child: + age: 5 + spm_units: + spm_unit: + members: [parent, child] + is_tanf_enrolled: true + households: + household: + members: [parent, child] + state_code: OH + output: + # Income: $11,879/12 = $989.92/month + # After disregards: $989.92 - $250 = $739.92; 50% disregard = $369.96 + # Countable: $739.92 - $369.96 = $369.96/month + # Payment standard for 2: $495/month + # Eligible since $369.96 < $495 + oh_owf_eligible: true + +- name: Family just over income threshold - still eligible due to disregard + period: 2025-01 + input: + people: + parent: + age: 30 + employment_income_before_lsr: 11_881 + child: + age: 5 + spm_units: + spm_unit: + members: [parent, child] + is_tanf_enrolled: true + households: + household: + members: [parent, child] + state_code: OH + output: + # Income: $11,881/12 = $990.08/month + # After disregards: $990.08 - $250 = $740.08; 50% disregard = $370.04 + # Countable: $740.08 - $370.04 = $370.04/month + # Payment standard for 2: $495/month + # Eligible since $370.04 < $495 + oh_owf_eligible: true + +- name: Family with zero income - eligible + period: 2025-01 + input: + people: + parent: + age: 30 + employment_income_before_lsr: 0 + child: + age: 5 + spm_units: + spm_unit: + members: [parent, child] + households: + household: + members: [parent, child] + state_code: OH + output: + oh_owf_eligible: true + +- name: New applicant with low income - passes initial eligibility + period: 2025-01 + input: + people: + parent: + age: 30 + employment_income_before_lsr: 8_000 + child: + age: 5 + spm_units: + spm_unit: + members: [parent, child] + is_tanf_enrolled: false + households: + household: + members: [parent, child] + state_code: OH + output: + # Gross income: $8,000/year = $666.67/month + # 50% FPL for size 2 (2024): ~$852/month + # Passes initial test: $666.67 < $852 + # After disregards: $666.67 - $250 = $416.67; 50% = $208.33 + # Countable: $208.33/month + # Payment standard: $495/month + # Passes ongoing: $208.33 < $495 + oh_owf_eligible: true + +- name: New applicant with high income - fails initial eligibility + period: 2025-01 + input: + people: + parent: + age: 30 + employment_income_before_lsr: 25_000 + child: + age: 5 + spm_units: + spm_unit: + members: [parent, child] + is_tanf_enrolled: false + households: + household: + members: [parent, child] + state_code: OH + output: + # Gross income: $25,000/year = $2,083/month + # 50% FPL for size 2 (2024): ~$852/month + # Fails initial test: $2,083 > $852 + oh_owf_eligible: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/oh/odjfs/owf/oh_owf_income_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/oh/odjfs/owf/oh_owf_income_eligible.yaml new file mode 100644 index 00000000000..37fc36172e1 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/oh/odjfs/owf/oh_owf_income_eligible.yaml @@ -0,0 +1,80 @@ +- name: Case 1, no income, eligible. + period: 2025-01 + input: + oh_owf_countable_income: 0 + oh_owf_payment_standard: 7_476 + state_code: OH + output: + oh_owf_income_eligible: true + +- name: Case 2, income below payment standard, eligible. + period: 2025-01 + input: + oh_owf_countable_income: 3_000 + oh_owf_payment_standard: 7_476 + state_code: OH + output: + oh_owf_income_eligible: true + +- name: Case 3, income exactly at payment standard, not eligible. + period: 2025-01 + input: + oh_owf_countable_income: 7_476 + oh_owf_payment_standard: 7_476 + state_code: OH + output: + oh_owf_income_eligible: false + +- name: Case 4, income exceeds payment standard, not eligible. + period: 2025-01 + input: + oh_owf_countable_income: 8_000 + oh_owf_payment_standard: 7_476 + state_code: OH + output: + oh_owf_income_eligible: false + +- name: Case 5, income just below payment standard, eligible. + period: 2025-01 + input: + oh_owf_countable_income: 7_475 + oh_owf_payment_standard: 7_476 + state_code: OH + output: + oh_owf_income_eligible: true + +- name: Case 6, income just above payment standard, not eligible. + period: 2025-01 + input: + oh_owf_countable_income: 7_477 + oh_owf_payment_standard: 7_476 + state_code: OH + output: + oh_owf_income_eligible: false + +- name: Case 7, very high income, not eligible. + period: 2025-01 + input: + oh_owf_countable_income: 50_000 + oh_owf_payment_standard: 7_476 + state_code: OH + output: + oh_owf_income_eligible: false + +- name: Case 8, small payment standard with zero income, eligible. + period: 2025-01 + input: + oh_owf_countable_income: 0 + oh_owf_payment_standard: 4_356 + state_code: OH + output: + oh_owf_income_eligible: true + +- name: Case 9, large family payment standard with moderate income, eligible. + period: 2025-01 + input: + oh_owf_countable_income: 10_000 + oh_owf_payment_standard: 16_668 + state_code: OH + output: + oh_owf_income_eligible: true diff --git a/policyengine_us/tests/policy/baseline/gov/states/oh/odjfs/owf/oh_owf_payment_standard.yaml b/policyengine_us/tests/policy/baseline/gov/states/oh/odjfs/owf/oh_owf_payment_standard.yaml new file mode 100644 index 00000000000..5293de413fa --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/oh/odjfs/owf/oh_owf_payment_standard.yaml @@ -0,0 +1,77 @@ +- name: Family of 1 + period: 2025-01 + input: + spm_unit_size: 1 + state_code: OH + output: + oh_owf_payment_standard: 372 +- name: Family of 2 + period: 2025-01 + input: + spm_unit_size: 2 + state_code: OH + output: + oh_owf_payment_standard: 507 +- name: Family of 3 + period: 2025-01 + input: + spm_unit_size: 3 + state_code: OH + output: + oh_owf_payment_standard: 623 +- name: Family of 4 + period: 2025-01 + input: + spm_unit_size: 4 + state_code: OH + output: + oh_owf_payment_standard: 768 +- name: Family of 5 + period: 2025-01 + input: + spm_unit_size: 5 + state_code: OH + output: + oh_owf_payment_standard: 899 +- name: Family of 6 + period: 2025-01 + input: + spm_unit_size: 6 + state_code: OH + output: + oh_owf_payment_standard: 1000 +- name: Family of 7 + period: 2025-01 + input: + spm_unit_size: 7 + state_code: OH + output: + oh_owf_payment_standard: 1118 +- name: Family of 8 + period: 2025-01 + input: + spm_unit_size: 8 + state_code: OH + output: + oh_owf_payment_standard: 1240 +- name: Family of 9 (uses increment) + period: 2025-01 + input: + spm_unit_size: 9 + state_code: OH + output: + oh_owf_payment_standard: 1395 +- name: Family of 10 (uses increment) + period: 2025-01 + input: + spm_unit_size: 10 + state_code: OH + output: + oh_owf_payment_standard: 1550 +- name: Family of 12 (uses increment) + period: 2025-01 + input: + spm_unit_size: 12 + state_code: OH + output: + oh_owf_payment_standard: 1860 diff --git a/policyengine_us/variables/gov/states/oh/odjfs/owf/eligibility/oh_owf_eligible.py b/policyengine_us/variables/gov/states/oh/odjfs/owf/eligibility/oh_owf_eligible.py new file mode 100644 index 00000000000..1e1c7a96223 --- /dev/null +++ b/policyengine_us/variables/gov/states/oh/odjfs/owf/eligibility/oh_owf_eligible.py @@ -0,0 +1,38 @@ +from policyengine_us.model_api import * + + +class oh_owf_eligible(Variable): + value_type = bool + entity = SPMUnit + label = "Ohio OWF eligibility" + definition_period = MONTH + defined_for = StateCode.OH + reference = ( + "https://codes.ohio.gov/ohio-revised-code/section-5107.10", + "http://codes.ohio.gov/oac/5101:1-23-10", + ) + + def formula(spm_unit, period, parameters): + person = spm_unit.members + + # Must meet demographic requirements (minor child with parent/relative + # OR pregnant woman at least 6 months pregnant) + # Use federal demographic eligibility + demographic_eligible = spm_unit("is_demographic_tanf_eligible", period) + + # Must have at least one citizen or legal immigrant + # Use federal immigration eligibility + has_citizen = spm_unit.any( + person("is_citizen_or_legal_immigrant", period) + ) + + # Must meet income requirements + # (oh_owf_income_eligible handles both initial and ongoing tests) + income_eligible = spm_unit("oh_owf_income_eligible", period) + + # Ohio has NO resource limits per working_references.md: + # "No Resource Test: Resources such as a car or home ownership + # are NOT considered in determining eligibility" + + # All requirements must be met + return demographic_eligible & has_citizen & income_eligible diff --git a/policyengine_us/variables/gov/states/oh/odjfs/owf/eligibility/oh_owf_income_eligible.py b/policyengine_us/variables/gov/states/oh/odjfs/owf/eligibility/oh_owf_income_eligible.py new file mode 100644 index 00000000000..4daf3eeb9da --- /dev/null +++ b/policyengine_us/variables/gov/states/oh/odjfs/owf/eligibility/oh_owf_income_eligible.py @@ -0,0 +1,34 @@ +from policyengine_us.model_api import * + + +class oh_owf_income_eligible(Variable): + value_type = bool + entity = SPMUnit + label = "Ohio OWF income eligibility" + definition_period = MONTH + defined_for = StateCode.OH + reference = ( + "https://codes.ohio.gov/ohio-revised-code/section-5107.10", + "http://codes.ohio.gov/oac/5101:1-23-20", + ) + + def formula(spm_unit, period, parameters): + # Check enrollment status + enrolled = spm_unit("is_tanf_enrolled", period) + + # Initial eligibility test: gross income < 50% FPL (new applicants) + initial_eligible = spm_unit("oh_owf_initial_income_eligible", period) + + # Ongoing eligibility test: countable income < payment standard + countable_income = spm_unit("oh_owf_countable_income", period) + payment_standard = spm_unit("oh_owf_payment_standard", period) + ongoing_eligible = countable_income < payment_standard + + # Per ORC 5107.10(D): + # - New applicants: must pass both initial (50% FPL) and ongoing tests + # - Enrolled recipients: only need to pass ongoing test + return where( + enrolled, + ongoing_eligible, + initial_eligible & ongoing_eligible, + ) diff --git a/policyengine_us/variables/gov/states/oh/odjfs/owf/eligibility/oh_owf_initial_income_eligible.py b/policyengine_us/variables/gov/states/oh/odjfs/owf/eligibility/oh_owf_initial_income_eligible.py new file mode 100644 index 00000000000..b460cde4171 --- /dev/null +++ b/policyengine_us/variables/gov/states/oh/odjfs/owf/eligibility/oh_owf_initial_income_eligible.py @@ -0,0 +1,30 @@ +from policyengine_us.model_api import * + + +class oh_owf_initial_income_eligible(Variable): + value_type = bool + entity = SPMUnit + label = "Ohio OWF initial income eligibility" + definition_period = MONTH + defined_for = StateCode.OH + reference = ( + "https://codes.ohio.gov/ohio-revised-code/section-5107.10", + "http://codes.ohio.gov/oac/5101:1-23-20", + ) + + def formula(spm_unit, period, parameters): + # Get gross income (before any disregards) using federal TANF variables + gross_income = add( + spm_unit, + period, + ["tanf_gross_earned_income", "tanf_gross_unearned_income"], + ) + + # Get initial eligibility income limit (50% of current year FPL) + fpg = spm_unit("tanf_fpg", period) + p = parameters(period).gov.states.oh.odjfs.owf.initial_eligibility + income_limit = fpg * p.income_limit_rate + + # Initial eligibility test: gross income must be below limit + # Per ORC 5107.10(D)(1) and ODJFS Payment Standards Table + return gross_income < income_limit diff --git a/policyengine_us/variables/gov/states/oh/odjfs/owf/income/oh_owf_countable_income.py b/policyengine_us/variables/gov/states/oh/odjfs/owf/income/oh_owf_countable_income.py new file mode 100644 index 00000000000..72555769a60 --- /dev/null +++ b/policyengine_us/variables/gov/states/oh/odjfs/owf/income/oh_owf_countable_income.py @@ -0,0 +1,43 @@ +from policyengine_us.model_api import * + + +class oh_owf_countable_income(Variable): + value_type = float + entity = SPMUnit + label = "Ohio OWF countable income" + unit = USD + definition_period = MONTH + defined_for = StateCode.OH + reference = ( + "https://codes.ohio.gov/ohio-revised-code/section-5107.10", + "http://codes.ohio.gov/oac/5101:1-23-20", + ) + + def formula(spm_unit, period, parameters): + # Get gross earned income from federal TANF variable + gross_earned = add(spm_unit, period, ["tanf_gross_earned_income"]) + + # Apply earned income disregard: $250 + 50% of remainder + # ORC 5107.10(D)(3) specifies "the two hundred-fifty dollar and + # one-half of the remainder disregards" + p = parameters( + period + ).gov.states.oh.odjfs.owf.income.deductions.earned_income_disregard + + # Calculate remainder after flat disregard + remainder = max_(gross_earned - p.flat_amount, 0) + + # Apply percentage disregard to remainder + percent_disregarded = remainder * p.percentage_of_disregard + + # Countable earned income = gross - flat disregard - percent of remainder + countable_earned = max_( + remainder - percent_disregarded, 0 + ) + + # Get gross unearned income from federal variable + # Per ORC 5107.10(D)(3): "No disregards apply to gross unearned income" + gross_unearned = add(spm_unit, period, ["tanf_gross_unearned_income"]) + + # Total countable income + return countable_earned + gross_unearned diff --git a/policyengine_us/variables/gov/states/oh/odjfs/owf/oh_owf.py b/policyengine_us/variables/gov/states/oh/odjfs/owf/oh_owf.py new file mode 100644 index 00000000000..5aaa3ad0b57 --- /dev/null +++ b/policyengine_us/variables/gov/states/oh/odjfs/owf/oh_owf.py @@ -0,0 +1,30 @@ +from policyengine_us.model_api import * + + +class oh_owf(Variable): + value_type = float + entity = SPMUnit + label = "Ohio OWF" + unit = USD + definition_period = MONTH + defined_for = "oh_owf_eligible" + reference = ( + "https://codes.ohio.gov/ohio-revised-code/section-5107.10", + "http://codes.ohio.gov/oac/5101:1-23-40", + ) + + def formula(spm_unit, period, parameters): + # Get payment standard for assistance group size + payment_standard = spm_unit("oh_owf_payment_standard", period) + + # Get countable income (after all disregards) + countable_income = spm_unit("oh_owf_countable_income", period) + + # Calculate benefit: Payment Standard - Countable Income + # Per OAC 5101:1-23-40: "OWF payments are payments made to an + # assistance group which represent the difference between the + # countable income and the appropriate OWF payment standard." + benefit = payment_standard - countable_income + + # Benefit cannot be negative + return max_(benefit, 0) diff --git a/policyengine_us/variables/gov/states/oh/odjfs/owf/oh_owf_payment_standard.py b/policyengine_us/variables/gov/states/oh/odjfs/owf/oh_owf_payment_standard.py new file mode 100644 index 00000000000..921cf5993cf --- /dev/null +++ b/policyengine_us/variables/gov/states/oh/odjfs/owf/oh_owf_payment_standard.py @@ -0,0 +1,25 @@ +from policyengine_us.model_api import * + + +class oh_owf_payment_standard(Variable): + value_type = float + entity = SPMUnit + label = "Ohio OWF payment standard" + unit = USD + definition_period = MONTH + defined_for = StateCode.OH + reference = "https://codes.ohio.gov/ohio-revised-code/section-5107.04" + + def formula(spm_unit, period, parameters): + size = spm_unit("spm_unit_size", period) + p = parameters(period).gov.states.oh.odjfs.owf.payment_standard + + # For assistance groups up to 8, use table lookup + # For groups larger than 8, calculate using increment + table_size = min_(size, 8) + extra_members = max_(size - 8, 0) + + base_amount = p.amounts[table_size] + additional_amount = extra_members * p.additional_person_increment + + return base_amount + additional_amount