diff --git a/changelog.d/nm-ccap.added.md b/changelog.d/nm-ccap.added.md new file mode 100644 index 00000000000..2371d183265 --- /dev/null +++ b/changelog.d/nm-ccap.added.md @@ -0,0 +1 @@ +Implement New Mexico Child Care Assistance Program (CCAP). diff --git a/policyengine_us/parameters/gov/hhs/ccdf/child_care_subsidy_programs.yaml b/policyengine_us/parameters/gov/hhs/ccdf/child_care_subsidy_programs.yaml index ab4fddc1e7c..8b60ce652a4 100644 --- a/policyengine_us/parameters/gov/hhs/ccdf/child_care_subsidy_programs.yaml +++ b/policyengine_us/parameters/gov/hhs/ccdf/child_care_subsidy_programs.yaml @@ -24,6 +24,7 @@ values: - mn_child_care_subsidies # Minnesota Child Care Assistance Program - ms_child_care_subsidies # Mississippi Child Care Payment Program (CCPP) - ne_child_care_subsidies # Nebraska Child Care Subsidy + - nm_child_care_subsidies # New Mexico Child Care Assistance Program - vt_child_care_subsidies # Vermont Child Care Financial Assistance Program - nh_child_care_subsidies # New Hampshire Child Care Scholarship Program - pa_child_care_subsidies # Pennsylvania Child Care Works diff --git a/policyengine_us/parameters/gov/states/nm/ececd/ccap/copay/waived.yaml b/policyengine_us/parameters/gov/states/nm/ececd/ccap/copay/waived.yaml new file mode 100644 index 00000000000..1c8a72a3eff --- /dev/null +++ b/policyengine_us/parameters/gov/states/nm/ececd/ccap/copay/waived.yaml @@ -0,0 +1,19 @@ +description: New Mexico uses this indicator to determine whether child care assistance copayments are waived under the Child Care Assistance Program. +# Beginning May 1, 2022, ECECD waives copayments for any family that qualifies +# for child care assistance; the waiver remains in effect under Universal +# Child Care. At least three months' notice is required before reinstatement. +# The pre-2022 copayment formula (8.15.2.13.B) is not modeled at the moment, so +# the copayment is treated as zero for all periods. +values: + 2016-10-01: false + 2022-05-01: true + +metadata: + unit: bool + period: month + label: New Mexico CCAP copayment waived + reference: + - title: ECECD Child Care Co-payments Waived (Effective May 1, 2022) + href: https://www.nmececd.org/wp-content/uploads/2024/09/CCA-Co-payments-waived_rev1l.pdf#page=1 + - title: 8.15.2.13.B NMAC + href: https://www.srca.nm.gov/parts/title08/08.015.0002.html diff --git a/policyengine_us/parameters/gov/states/nm/ececd/ccap/eligibility/child_age_limit.yaml b/policyengine_us/parameters/gov/states/nm/ececd/ccap/eligibility/child_age_limit.yaml new file mode 100644 index 00000000000..d923c826543 --- /dev/null +++ b/policyengine_us/parameters/gov/states/nm/ececd/ccap/eligibility/child_age_limit.yaml @@ -0,0 +1,11 @@ +description: New Mexico limits child care assistance to children under this age under the Child Care Assistance Program. +values: + 2016-10-01: 13 + +metadata: + unit: year + period: year + label: New Mexico CCAP child age limit + reference: + - title: 8.15.2.11.F NMAC + href: https://www.srca.nm.gov/parts/title08/08.015.0002.html diff --git a/policyengine_us/parameters/gov/states/nm/ececd/ccap/eligibility/fpl_limit/continuation.yaml b/policyengine_us/parameters/gov/states/nm/ececd/ccap/eligibility/fpl_limit/continuation.yaml new file mode 100644 index 00000000000..28691eb4990 --- /dev/null +++ b/policyengine_us/parameters/gov/states/nm/ececd/ccap/eligibility/fpl_limit/continuation.yaml @@ -0,0 +1,11 @@ +description: New Mexico limits continuing income eligibility to this percentage of the federal poverty guideline under the Child Care Assistance Program. +values: + 2016-10-01: 2.5 + +metadata: + unit: /1 + period: year + label: New Mexico CCAP continuing income limit + reference: + - title: 8.15.2.12 NMAC + href: https://www.srca.nm.gov/parts/title08/08.015.0002.html diff --git a/policyengine_us/parameters/gov/states/nm/ececd/ccap/eligibility/fpl_limit/initial.yaml b/policyengine_us/parameters/gov/states/nm/ececd/ccap/eligibility/fpl_limit/initial.yaml new file mode 100644 index 00000000000..037a65c2d0c --- /dev/null +++ b/policyengine_us/parameters/gov/states/nm/ececd/ccap/eligibility/fpl_limit/initial.yaml @@ -0,0 +1,11 @@ +description: New Mexico limits initial income eligibility to this percentage of the federal poverty guideline under the Child Care Assistance Program. +values: + 2016-10-01: 2 + +metadata: + unit: /1 + period: year + label: New Mexico CCAP initial income limit + reference: + - title: 8.15.2.9 NMAC + href: https://www.srca.nm.gov/parts/title08/08.015.0002.html diff --git a/policyengine_us/parameters/gov/states/nm/ececd/ccap/eligibility/immigration_test_in_effect.yaml b/policyengine_us/parameters/gov/states/nm/ececd/ccap/eligibility/immigration_test_in_effect.yaml new file mode 100644 index 00000000000..e9fc67b3fc6 --- /dev/null +++ b/policyengine_us/parameters/gov/states/nm/ececd/ccap/eligibility/immigration_test_in_effect.yaml @@ -0,0 +1,19 @@ +description: New Mexico uses this indicator to determine whether a child citizenship or qualified-immigrant test applies under the Child Care Assistance Program. +# New Mexico's Universal Child Care initiative (a state ECECD program; +# effective November 1, 2025) removes the child-level citizenship/immigration +# test; a participating New Mexico child without a federally eligible status is +# served with state funds, so status only sets the funding source, not +# eligibility. +values: + 2016-10-01: true + 2025-11-01: false + +metadata: + unit: bool + period: month + label: New Mexico CCAP immigration test in effect + reference: + - title: 8.15.2.11.E NMAC + href: https://www.srca.nm.gov/parts/title08/08.015.0002.html + - title: ECECD Universal Child Care + href: https://www.nmececd.org/universal/ diff --git a/policyengine_us/parameters/gov/states/nm/ececd/ccap/eligibility/income_limit_in_effect.yaml b/policyengine_us/parameters/gov/states/nm/ececd/ccap/eligibility/income_limit_in_effect.yaml new file mode 100644 index 00000000000..436310514ac --- /dev/null +++ b/policyengine_us/parameters/gov/states/nm/ececd/ccap/eligibility/income_limit_in_effect.yaml @@ -0,0 +1,17 @@ +description: New Mexico uses this indicator to determine whether an income limit applies under the Child Care Assistance Program. +# New Mexico's Universal Child Care initiative (a state ECECD program, the +# first in the nation; effective November 1, 2025) removes all income limits; +# New Mexico families are eligible regardless of income from that date forward. +values: + 2016-10-01: true + 2025-11-01: false + +metadata: + unit: bool + period: month + label: New Mexico CCAP income limit in effect + reference: + - title: 8.15.2.9 NMAC + href: https://www.srca.nm.gov/parts/title08/08.015.0002.html + - title: ECECD Universal Child Care + href: https://www.nmececd.org/universal/ diff --git a/policyengine_us/parameters/gov/states/nm/ececd/ccap/eligibility/special_needs_child_age_limit.yaml b/policyengine_us/parameters/gov/states/nm/ececd/ccap/eligibility/special_needs_child_age_limit.yaml new file mode 100644 index 00000000000..3e779bbf5d9 --- /dev/null +++ b/policyengine_us/parameters/gov/states/nm/ececd/ccap/eligibility/special_needs_child_age_limit.yaml @@ -0,0 +1,11 @@ +description: New Mexico limits child care assistance for children with special supervision needs to those under this age under the Child Care Assistance Program. +values: + 2016-10-01: 18 + +metadata: + unit: year + period: year + label: New Mexico CCAP special needs child age limit + reference: + - title: 8.15.2.11.G NMAC + href: https://www.srca.nm.gov/parts/title08/08.015.0002.html diff --git a/policyengine_us/parameters/gov/states/nm/ececd/ccap/income/countable_income/sources.yaml b/policyengine_us/parameters/gov/states/nm/ececd/ccap/income/countable_income/sources.yaml new file mode 100644 index 00000000000..b5a6e6f034f --- /dev/null +++ b/policyengine_us/parameters/gov/states/nm/ececd/ccap/income/countable_income/sources.yaml @@ -0,0 +1,54 @@ +description: New Mexico counts these income sources under the Child Care Assistance Program. +values: + 2016-10-01: + - employment_income + - self_employment_income + # PolicyEngine records farm income separately from self-employment, so + # include it under the 8.15.2.11.C(5) self-employment category. + - farm_operations_income + - alimony_income + # VA payments are counted except those exempted under 8.15.2.11.C(6). + - veterans_benefits + - workers_compensation + - railroad_benefits + - pension_income + # PolicyEngine records military retirement separately from pension_income, + # so include it under the 8.15.2.11.C(5) pensions category. + - military_retirement_pay + - rental_income + # Social Security retirement, survivors, and dependents benefits are + # counted; SSDI is exempt (8.15.2.11.C(6)), so the aggregate social_security + # variable is split into its non-disability components here. (Social + # Security received by household children is also exempt, but that is + # handled by the caretaker filter below, not by omission.) + - social_security_retirement + - social_security_survivors + - social_security_dependents + # Overtime is counted only if CYFD finds it is paid regularly + # (8.15.2.11.C(5)); the regularity condition is captured within + # employment_income and not separately modeled. + # + # Whose income counts: 8.15.2.11.C(3) excludes the earned and unearned + # income of legal dependents and of grandparents who are not legal + # guardians. nm_ccap_countable_income applies this by counting only the + # income of caretakers (tax unit head/spouse), so household children's + # income (earnings and Social Security) is excluded. A non-guardian + # grandparent who files as their own tax unit head is not separately + # excluded at the moment. + # + # Provisions we don't model at the moment: + # - Royalties (8.15.2.11.C(5)) — no corresponding PolicyEngine variable. + # The other ~30 exempt categories in 8.15.2.11.C(6) (SNAP, TANF, SSI, + # unemployment, child support, energy assistance, foster care payments, + # etc.) are excluded by omission from this positive list. Omitting TANF + # also avoids a CCAP <-> TANF circular dependency; the is_tanf_enrolled + # bypass in nm_ccap_income_eligible already makes TANF-active families + # categorically income-eligible. + +metadata: + unit: list + period: year + label: New Mexico CCAP countable income sources + reference: + - title: 8.15.2.11.C(3) and (5) NMAC + href: https://www.srca.nm.gov/parts/title08/08.015.0002.html diff --git a/policyengine_us/parameters/gov/states/nm/ececd/ccap/rates/age_group/months.yaml b/policyengine_us/parameters/gov/states/nm/ececd/ccap/rates/age_group/months.yaml new file mode 100644 index 00000000000..89e4e619f0b --- /dev/null +++ b/policyengine_us/parameters/gov/states/nm/ececd/ccap/rates/age_group/months.yaml @@ -0,0 +1,32 @@ +description: New Mexico assigns children to these reimbursement age categories by age in months under the Child Care Assistance Program. +# Maps age in months to an nm_ccap_age_group enum index: +# 0 = INFANT (0-23 months), 1 = TODDLER (24-35 months), +# 2 = PRESCHOOL (three to five year olds, 36-71 months), +# 3 = SCHOOL_AGE (six year olds and older, 72+ months). +metadata: + type: single_amount + threshold_unit: month + amount_unit: /1 + period: year + label: New Mexico CCAP age group by months + reference: + - title: 8.15.2.7(I)(2)(a)-(d) NMAC + href: https://www.srca.nm.gov/parts/title08/08.015.0002.html + +brackets: + - threshold: + 2016-10-01: 0 + amount: + 2016-10-01: 0 + - threshold: + 2016-10-01: 24 + amount: + 2016-10-01: 1 + - threshold: + 2016-10-01: 36 + amount: + 2016-10-01: 2 + - threshold: + 2016-10-01: 72 + amount: + 2016-10-01: 3 diff --git a/policyengine_us/parameters/gov/states/nm/ececd/ccap/rates/center.yaml b/policyengine_us/parameters/gov/states/nm/ececd/ccap/rates/center.yaml new file mode 100644 index 00000000000..9867d491e94 --- /dev/null +++ b/policyengine_us/parameters/gov/states/nm/ececd/ccap/rates/center.yaml @@ -0,0 +1,61 @@ +description: New Mexico provides these full-time monthly reimbursement rates for licensed child care centers under the Child Care Assistance Program. +# Full-time base rates. Part-time rates are these multiplied by the +# service-unit fractions in rates/service_unit_fraction.yaml (PT1 0.75, +# PT2 0.50, PT3 0.25). The 2+Star FOCUS and 3-Star FOCUS rates are identical +# in the published flyer. +metadata: + period: month + unit: currency-USD + label: New Mexico CCAP licensed center monthly rates + breakdown: + - nm_ccap_focus_level + - nm_ccap_age_group + reference: + - title: ECECD Child Care Assistance Rates, Licensed Center Monthly Rates (Effective August 2023) + href: https://www.nmececd.org/wp-content/uploads/2024/05/Cost-Model-Reimbursement-Rate-Flyer-English-and-Spanish-Revised-May-2024.pdf#page=2 + +TWO_STAR: + INFANT: + 2023-08-01: 1_075 + TODDLER: + 2023-08-01: 775 + PRESCHOOL: + 2023-08-01: 700 + SCHOOL_AGE: + 2023-08-01: 500 +TWO_PLUS_STAR: + INFANT: + 2023-08-01: 1_150 + TODDLER: + 2023-08-01: 865 + PRESCHOOL: + 2023-08-01: 800 + SCHOOL_AGE: + 2023-08-01: 550 +THREE_STAR: + INFANT: + 2023-08-01: 1_150 + TODDLER: + 2023-08-01: 865 + PRESCHOOL: + 2023-08-01: 800 + SCHOOL_AGE: + 2023-08-01: 550 +FOUR_STAR: + INFANT: + 2023-08-01: 1_500 + TODDLER: + 2023-08-01: 1_100 + PRESCHOOL: + 2023-08-01: 1_000 + SCHOOL_AGE: + 2023-08-01: 650 +FIVE_STAR: + INFANT: + 2023-08-01: 1_925 + TODDLER: + 2023-08-01: 1_500 + PRESCHOOL: + 2023-08-01: 1_125 + SCHOOL_AGE: + 2023-08-01: 750 diff --git a/policyengine_us/parameters/gov/states/nm/ececd/ccap/rates/family_home.yaml b/policyengine_us/parameters/gov/states/nm/ececd/ccap/rates/family_home.yaml new file mode 100644 index 00000000000..de6c2a7c719 --- /dev/null +++ b/policyengine_us/parameters/gov/states/nm/ececd/ccap/rates/family_home.yaml @@ -0,0 +1,61 @@ +description: New Mexico provides these full-time monthly reimbursement rates for licensed family homes under the Child Care Assistance Program. +# Full-time base rates for licensed family homes (capacity 6 or fewer +# children). Part-time rates are these multiplied by the service-unit +# fractions in rates/service_unit_fraction.yaml. The 2+Star FOCUS and 3-Star +# FOCUS rates are identical in the published flyer. +metadata: + period: month + unit: currency-USD + label: New Mexico CCAP licensed family home monthly rates + breakdown: + - nm_ccap_focus_level + - nm_ccap_age_group + reference: + - title: ECECD Child Care Assistance Rates, Licensed Family Home Monthly Rates (Effective August 2023) + href: https://www.nmececd.org/wp-content/uploads/2024/05/Cost-Model-Reimbursement-Rate-Flyer-English-and-Spanish-Revised-May-2024.pdf#page=4 + +TWO_STAR: + INFANT: + 2023-08-01: 1_100 + TODDLER: + 2023-08-01: 1_075 + PRESCHOOL: + 2023-08-01: 870 + SCHOOL_AGE: + 2023-08-01: 530 +TWO_PLUS_STAR: + INFANT: + 2023-08-01: 1_175 + TODDLER: + 2023-08-01: 1_125 + PRESCHOOL: + 2023-08-01: 950 + SCHOOL_AGE: + 2023-08-01: 600 +THREE_STAR: + INFANT: + 2023-08-01: 1_175 + TODDLER: + 2023-08-01: 1_125 + PRESCHOOL: + 2023-08-01: 950 + SCHOOL_AGE: + 2023-08-01: 600 +FOUR_STAR: + INFANT: + 2023-08-01: 1_275 + TODDLER: + 2023-08-01: 1_250 + PRESCHOOL: + 2023-08-01: 1_075 + SCHOOL_AGE: + 2023-08-01: 680 +FIVE_STAR: + INFANT: + 2023-08-01: 1_375 + TODDLER: + 2023-08-01: 1_325 + PRESCHOOL: + 2023-08-01: 1_160 + SCHOOL_AGE: + 2023-08-01: 725 diff --git a/policyengine_us/parameters/gov/states/nm/ececd/ccap/rates/group_home.yaml b/policyengine_us/parameters/gov/states/nm/ececd/ccap/rates/group_home.yaml new file mode 100644 index 00000000000..3be2cc64ea1 --- /dev/null +++ b/policyengine_us/parameters/gov/states/nm/ececd/ccap/rates/group_home.yaml @@ -0,0 +1,61 @@ +description: New Mexico provides these full-time monthly reimbursement rates for licensed group homes under the Child Care Assistance Program. +# Full-time base rates for licensed group homes (capacity 7-12 children). +# Part-time rates are these multiplied by the service-unit fractions in +# rates/service_unit_fraction.yaml. The 2+Star FOCUS and 3-Star FOCUS rates +# are identical in the published flyer. +metadata: + period: month + unit: currency-USD + label: New Mexico CCAP licensed group home monthly rates + breakdown: + - nm_ccap_focus_level + - nm_ccap_age_group + reference: + - title: ECECD Child Care Assistance Rates, Licensed Group Home Monthly Rates (Effective August 2023) + href: https://www.nmececd.org/wp-content/uploads/2024/05/Cost-Model-Reimbursement-Rate-Flyer-English-and-Spanish-Revised-May-2024.pdf#page=3 + +TWO_STAR: + INFANT: + 2023-08-01: 1_040 + TODDLER: + 2023-08-01: 1_000 + PRESCHOOL: + 2023-08-01: 830 + SCHOOL_AGE: + 2023-08-01: 475 +TWO_PLUS_STAR: + INFANT: + 2023-08-01: 1_115 + TODDLER: + 2023-08-01: 1_050 + PRESCHOOL: + 2023-08-01: 910 + SCHOOL_AGE: + 2023-08-01: 545 +THREE_STAR: + INFANT: + 2023-08-01: 1_115 + TODDLER: + 2023-08-01: 1_050 + PRESCHOOL: + 2023-08-01: 910 + SCHOOL_AGE: + 2023-08-01: 545 +FOUR_STAR: + INFANT: + 2023-08-01: 1_215 + TODDLER: + 2023-08-01: 1_175 + PRESCHOOL: + 2023-08-01: 1_035 + SCHOOL_AGE: + 2023-08-01: 625 +FIVE_STAR: + INFANT: + 2023-08-01: 1_315 + TODDLER: + 2023-08-01: 1_250 + PRESCHOOL: + 2023-08-01: 1_120 + SCHOOL_AGE: + 2023-08-01: 670 diff --git a/policyengine_us/parameters/gov/states/nm/ececd/ccap/rates/registered_home.yaml b/policyengine_us/parameters/gov/states/nm/ececd/ccap/rates/registered_home.yaml new file mode 100644 index 00000000000..2841bf5802e --- /dev/null +++ b/policyengine_us/parameters/gov/states/nm/ececd/ccap/rates/registered_home.yaml @@ -0,0 +1,22 @@ +description: New Mexico provides these full-time monthly reimbursement rates for registered and in-home child care under the Child Care Assistance Program. +# Full-time base rates for registered homes / in-home care, which receive a +# single rate with no FOCUS star levels. Part-time rates are these multiplied +# by the service-unit fractions in rates/service_unit_fraction.yaml. +metadata: + period: month + unit: currency-USD + label: New Mexico CCAP registered home monthly rates + breakdown: + - nm_ccap_age_group + reference: + - title: ECECD Child Care Assistance Rates, Registered Home Monthly Rates (Effective August 2023) + href: https://www.nmececd.org/wp-content/uploads/2024/05/Cost-Model-Reimbursement-Rate-Flyer-English-and-Spanish-Revised-May-2024.pdf#page=4 + +INFANT: + 2023-08-01: 425 +TODDLER: + 2023-08-01: 425 +PRESCHOOL: + 2023-08-01: 375 +SCHOOL_AGE: + 2023-08-01: 350 diff --git a/policyengine_us/parameters/gov/states/nm/ececd/ccap/rates/service_unit/hours.yaml b/policyengine_us/parameters/gov/states/nm/ececd/ccap/rates/service_unit/hours.yaml new file mode 100644 index 00000000000..6b5dde8b0c6 --- /dev/null +++ b/policyengine_us/parameters/gov/states/nm/ececd/ccap/rates/service_unit/hours.yaml @@ -0,0 +1,29 @@ +description: New Mexico assigns children to these service units by average weekly hours of care under the Child Care Assistance Program. +# Maps average hours of care per week to an nm_ccap_service_unit enum index: +# 3 = PART_TIME_3 (7 or fewer hours), 1 = PART_TIME_1 (8-29 hours), +# 0 = FULL_TIME (30 or more hours). PART_TIME_2 (index 2) is a split-custody / +# two-provider arrangement (8-19 hours) that cannot be derived from hours +# alone, so it is reachable only via explicit input, not from this bracket. +metadata: + type: single_amount + threshold_unit: hour + amount_unit: /1 + period: year + label: New Mexico CCAP service unit by weekly hours + reference: + - title: ECECD Child Care Assistance Rates, Definitions (Effective August 2023) + href: https://www.nmececd.org/wp-content/uploads/2024/05/Cost-Model-Reimbursement-Rate-Flyer-English-and-Spanish-Revised-May-2024.pdf#page=1 + +brackets: + - threshold: + 2023-08-01: 0 + amount: + 2023-08-01: 3 + - threshold: + 2023-08-01: 8 + amount: + 2023-08-01: 1 + - threshold: + 2023-08-01: 30 + amount: + 2023-08-01: 0 diff --git a/policyengine_us/parameters/gov/states/nm/ececd/ccap/rates/service_unit_fraction.yaml b/policyengine_us/parameters/gov/states/nm/ececd/ccap/rates/service_unit_fraction.yaml new file mode 100644 index 00000000000..ca2770224ff --- /dev/null +++ b/policyengine_us/parameters/gov/states/nm/ececd/ccap/rates/service_unit_fraction.yaml @@ -0,0 +1,22 @@ +description: New Mexico multiplies the full-time provider rate by this fraction for each service unit under the Child Care Assistance Program. +# Per the rate flyer service-unit definitions: Full time (30+ hrs/wk) pays +# 100% of the full-time rate, Part time 1 (8-29 hrs/wk) 75%, Part time 2 +# (split custody, 8-19 hrs/wk) 50%, and Part time 3 (7 or fewer hrs/wk) 25%. +metadata: + unit: /1 + period: year + label: New Mexico CCAP service unit fraction + breakdown: + - nm_ccap_service_unit + reference: + - title: ECECD Child Care Assistance Rates, Definitions (Effective August 2023) + href: https://www.nmececd.org/wp-content/uploads/2024/05/Cost-Model-Reimbursement-Rate-Flyer-English-and-Spanish-Revised-May-2024.pdf#page=1 + +FULL_TIME: + 2023-08-01: 1 +PART_TIME_1: + 2023-08-01: 0.75 +PART_TIME_2: + 2023-08-01: 0.5 +PART_TIME_3: + 2023-08-01: 0.25 diff --git a/policyengine_us/parameters/gov/states/nm/ececd/ccap/supplements/non_traditional_hours.yaml b/policyengine_us/parameters/gov/states/nm/ececd/ccap/supplements/non_traditional_hours.yaml new file mode 100644 index 00000000000..237d65735e6 --- /dev/null +++ b/policyengine_us/parameters/gov/states/nm/ececd/ccap/supplements/non_traditional_hours.yaml @@ -0,0 +1,33 @@ +description: New Mexico adds this share of the applicable rate for non-traditional hours of care under the Child Care Assistance Program. +# ECECD pays a differential of 5%, 10%, or 15% of the applicable full-time or +# part-time rate for care provided during non-traditional hours (after-hours +# 7 p.m.-7 a.m. Monday-Friday and weekend hours): 5% for 1-10 hrs/wk, 10% for +# 11-20 hrs/wk, 15% for 21 or more hrs/wk. Zero non-traditional hours adds no +# supplement. +metadata: + type: single_amount + threshold_unit: hour + amount_unit: /1 + period: year + label: New Mexico CCAP non-traditional hours supplement rate + reference: + - title: ECECD Child Care Assistance Rates, Differential Rates for Non-Traditional Hours (Effective August 2023) + href: https://www.nmececd.org/wp-content/uploads/2024/05/Cost-Model-Reimbursement-Rate-Flyer-English-and-Spanish-Revised-May-2024.pdf#page=5 + +brackets: + - threshold: + 2023-08-01: 0 + amount: + 2023-08-01: 0 + - threshold: + 2023-08-01: 1 + amount: + 2023-08-01: 0.05 + - threshold: + 2023-08-01: 11 + amount: + 2023-08-01: 0.1 + - threshold: + 2023-08-01: 21 + amount: + 2023-08-01: 0.15 diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index 7d90c813240..c8158d16e06 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -460,7 +460,7 @@ programs: category: Benefits agency: HHS status: partial - coverage: AK, AR, AL, AZ, CA, CO, CT, DE, DC, FL, GA, HI, IA, ID, IL, IN, KS, KY, LA, MA, MD, ME, MN, MS, NC, NH, NJ, PA, RI, SC, TX, VA, VT, WA, WV + coverage: AK, AR, AL, AZ, CA, CO, CT, DE, DC, FL, GA, HI, IA, ID, IL, IN, KS, KY, LA, MA, MD, ME, MN, MS, NC, NH, NJ, NM, PA, RI, SC, TX, VA, VT, WA, WV state_implementations: - state: AK status: complete @@ -654,6 +654,12 @@ programs: full_name: Georgia Childcare and Parent Services variable: ga_caps parameter_prefix: gov.states.ga.decal.caps + - state: NM + status: complete + name: New Mexico CCAP + full_name: New Mexico Child Care Assistance Program + variable: nm_ccap + parameter_prefix: gov.states.nm.ececd.ccap - id: head_start name: Head Start diff --git a/policyengine_us/tests/policy/baseline/gov/states/nm/ececd/ccap/eligibility/nm_ccap_activity_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/nm/ececd/ccap/eligibility/nm_ccap_activity_eligible.yaml new file mode 100644 index 00000000000..2d484e25823 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/nm/ececd/ccap/eligibility/nm_ccap_activity_eligible.yaml @@ -0,0 +1,163 @@ +# NM CCAP activity eligibility (8.15.2.11.I, 8.15.2.7 NMAC): benefits are only +# for families working, attending school, or in job training. Every head/spouse +# caretaker must be working (any hours) or a full-time student. NM does not +# impose a minimum weekly-hours floor (unlike WV's 20-hour test) -- the +# regulation requires activity presence, not a threshold. + +- name: Case 1, single working parent meets the activity test. + period: 2025-01 + input: + people: + person1: + age: 30 + weekly_hours_worked: 35 + person2: + age: 3 + is_tax_unit_dependent: true + spm_units: + spm_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: NM + output: + nm_ccap_activity_eligible: true + +- name: Case 2, parent with no work hours and not a student fails the test. + period: 2025-01 + input: + people: + person1: + age: 30 + weekly_hours_worked: 0 + is_full_time_student: false + person2: + age: 3 + is_tax_unit_dependent: true + spm_units: + spm_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: NM + output: + # No work and not a student -> the caretaker is not engaged in an activity. + nm_ccap_activity_eligible: false + +- name: Case 3, full time student parent meets the activity test. + period: 2025-01 + input: + people: + person1: + age: 22 + weekly_hours_worked: 0 + is_full_time_student: true + person2: + age: 3 + is_tax_unit_dependent: true + spm_units: + spm_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: NM + output: + nm_ccap_activity_eligible: true + +- name: Case 4, two parents both in an activity meet the test. + period: 2025-01 + input: + people: + person1: + age: 30 + weekly_hours_worked: 20 + person2: + age: 28 + weekly_hours_worked: 0 + is_full_time_student: true + person3: + age: 3 + is_tax_unit_dependent: true + spm_units: + spm_unit: + members: [person1, person2, person3] + households: + household: + members: [person1, person2, person3] + state_code: NM + output: + # One parent works, the other is a full-time student -> both qualify. + nm_ccap_activity_eligible: true + +- name: Case 5, two parents one inactive fails the test. + period: 2025-01 + input: + people: + person1: + age: 30 + weekly_hours_worked: 20 + person2: + age: 28 + weekly_hours_worked: 0 + is_full_time_student: false + person3: + age: 3 + is_tax_unit_dependent: true + spm_units: + spm_unit: + members: [person1, person2, person3] + households: + household: + members: [person1, person2, person3] + state_code: NM + output: + # One caretaker is neither working nor a student -> whole unit fails. + nm_ccap_activity_eligible: false + +- name: Case 6, child only unit with no caretaker fails the test. + period: 2025-01 + input: + people: + person1: + age: 5 + is_tax_unit_dependent: true + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: NM + output: + # No head/spouse caretaker present -> activity test must fail, otherwise + # child-only units would pass on income and assets alone. + nm_ccap_activity_eligible: false + +- name: Case 7, CCDF activity fallback covers an activity not modeled here. + period: 2025-01 + input: + people: + person1: + age: 30 + weekly_hours_worked: 0 + is_full_time_student: false + person2: + age: 3 + is_tax_unit_dependent: true + spm_units: + spm_unit: + members: [person1, person2] + # Parent is in job training / on temporary leave -- not captured by + # work hours or student status, so flagged via the federal CCDF input. + meets_ccdf_activity_test: true + households: + household: + members: [person1, person2] + state_code: NM + output: + # Without the fallback this unit would fail (0 hours, not a student); the + # meets_ccdf_activity_test flag rescues it. + nm_ccap_activity_eligible: true diff --git a/policyengine_us/tests/policy/baseline/gov/states/nm/ececd/ccap/eligibility/nm_ccap_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/nm/ececd/ccap/eligibility/nm_ccap_eligible.yaml new file mode 100644 index 00000000000..684a8be61b7 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/nm/ececd/ccap/eligibility/nm_ccap_eligible.yaml @@ -0,0 +1,160 @@ +# NM CCAP overall eligibility: an eligible child AND income-eligible AND +# asset-eligible AND activity-eligible, all within New Mexico. +# 2025 FPG family of 2 = 21_150/yr; 200% = 42_300/yr. + +- name: Case 1, family meeting all requirements is eligible. + period: 2025-01 + input: + people: + person1: + age: 30 + employment_income: 24_000 + weekly_hours_worked: 35 + immigration_status: CITIZEN + person2: + age: 4 + is_tax_unit_dependent: true + immigration_status: CITIZEN + childcare_hours_per_week: 40 + spm_units: + spm_unit: + members: [person1, person2] + nm_ccap_enrolled: false + spm_unit_assets: 5_000 + households: + household: + members: [person1, person2] + state_code: NM + output: + # Child 4 < 13 and citizen; 24_000 < 200% FPL; assets < 1M; parent works. + nm_ccap_eligible: true + +- name: Case 2, no eligible child means not eligible. + period: 2025-01 + input: + people: + person1: + age: 30 + employment_income: 24_000 + weekly_hours_worked: 35 + immigration_status: CITIZEN + person2: + # Age 14, no developmental delay -> not an eligible child. + age: 14 + is_tax_unit_dependent: true + immigration_status: CITIZEN + spm_units: + spm_unit: + members: [person1, person2] + nm_ccap_enrolled: false + spm_unit_assets: 5_000 + households: + household: + members: [person1, person2] + state_code: NM + output: + nm_ccap_eligible: false + +- name: Case 3, income too high means not eligible. + period: 2025-01 + input: + people: + person1: + age: 30 + # 100_000/yr is far above the 200% FPL initial cap (42_300/yr). + employment_income: 100_000 + weekly_hours_worked: 35 + immigration_status: CITIZEN + person2: + age: 4 + is_tax_unit_dependent: true + immigration_status: CITIZEN + spm_units: + spm_unit: + members: [person1, person2] + nm_ccap_enrolled: false + spm_unit_assets: 5_000 + households: + household: + members: [person1, person2] + state_code: NM + output: + nm_ccap_eligible: false + +- name: Case 4, excess assets means not eligible. + period: 2025-01 + input: + people: + person1: + age: 30 + employment_income: 24_000 + weekly_hours_worked: 35 + immigration_status: CITIZEN + person2: + age: 4 + is_tax_unit_dependent: true + immigration_status: CITIZEN + spm_units: + spm_unit: + members: [person1, person2] + nm_ccap_enrolled: false + # Above the $1,000,000 asset limit. + spm_unit_assets: 2_000_000 + households: + household: + members: [person1, person2] + state_code: NM + output: + nm_ccap_eligible: false + +- name: Case 5, no activity means not eligible. + period: 2025-01 + input: + people: + person1: + age: 30 + employment_income: 24_000 + weekly_hours_worked: 0 + is_full_time_student: false + immigration_status: CITIZEN + person2: + age: 4 + is_tax_unit_dependent: true + immigration_status: CITIZEN + spm_units: + spm_unit: + members: [person1, person2] + nm_ccap_enrolled: false + spm_unit_assets: 5_000 + households: + household: + members: [person1, person2] + state_code: NM + output: + nm_ccap_eligible: false + +- name: Case 6, non New Mexico family is not eligible. + period: 2025-01 + input: + people: + person1: + age: 30 + employment_income: 24_000 + weekly_hours_worked: 35 + immigration_status: CITIZEN + person2: + age: 4 + is_tax_unit_dependent: true + immigration_status: CITIZEN + spm_units: + spm_unit: + members: [person1, person2] + nm_ccap_enrolled: false + spm_unit_assets: 5_000 + households: + household: + members: [person1, person2] + # Texas, not New Mexico -> defined_for zeroes the output. + state_code: TX + output: + nm_ccap_eligible: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/nm/ececd/ccap/eligibility/nm_ccap_eligible_child.yaml b/policyengine_us/tests/policy/baseline/gov/states/nm/ececd/ccap/eligibility/nm_ccap_eligible_child.yaml new file mode 100644 index 00000000000..7f038de61be --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/nm/ececd/ccap/eligibility/nm_ccap_eligible_child.yaml @@ -0,0 +1,243 @@ +# NM CCAP eligible child: age 6 weeks to under 13 (8.15.2.11.F NMAC), or +# under 18 with a developmental delay (special-supervision extension, +# 8.15.2.11.G), AND a US citizen or qualified immigrant (8.15.2.11.E, +# reusing is_ccdf_immigration_eligible_child). + +- name: Case 1, citizen child under thirteen is eligible. + period: 2025-01 + input: + people: + person1: + age: 35 + immigration_status: CITIZEN + person2: + age: 4 + immigration_status: CITIZEN + households: + household: + members: [person1, person2] + state_code: NM + output: + # Adult (35) not a child; child age 4 < 13 and citizen -> eligible. + nm_ccap_eligible_child: [false, true] + +- name: Case 2, child at exactly age thirteen is ineligible. + period: 2025-01 + input: + people: + person1: + age: 35 + immigration_status: CITIZEN + person2: + # Age 13 is NOT < 13 -> ineligible (no developmental delay) + age: 13 + immigration_status: CITIZEN + households: + household: + members: [person1, person2] + state_code: NM + output: + nm_ccap_eligible_child: [false, false] + +- name: Case 3, child at age twelve point nine nine is eligible. + period: 2025-01 + input: + people: + person1: + age: 35 + immigration_status: CITIZEN + person2: + # Age 12.99 < 13 -> eligible + age: 12.99 + immigration_status: CITIZEN + households: + household: + members: [person1, person2] + state_code: NM + output: + nm_ccap_eligible_child: [false, true] + +- name: Case 4, special needs child under eighteen is eligible. + period: 2025-01 + input: + people: + person1: + age: 45 + immigration_status: CITIZEN + person2: + # Developmental delay, age 15 < 18 -> eligible via special-supervision + age: 15 + has_developmental_delay: true + immigration_status: CITIZEN + households: + household: + members: [person1, person2] + state_code: NM + output: + nm_ccap_eligible_child: [false, true] + +- name: Case 5, special needs child at exactly eighteen is ineligible. + period: 2025-01 + input: + people: + person1: + age: 45 + immigration_status: CITIZEN + person2: + # Developmental delay, age 18 is NOT < 18 -> ineligible + age: 18 + has_developmental_delay: true + immigration_status: CITIZEN + households: + household: + members: [person1, person2] + state_code: NM + output: + nm_ccap_eligible_child: [false, false] + +- name: Case 6, child without qualified immigration status is ineligible. + period: 2025-01 + input: + people: + person1: + age: 35 + immigration_status: CITIZEN + person2: + # Under 13 but undocumented -> fails the citizenship/qualified-immigrant test + age: 4 + immigration_status: UNDOCUMENTED + households: + household: + members: [person1, person2] + state_code: NM + output: + nm_ccap_eligible_child: [false, false] + +# ============================================================ +# Edge cases: age-limit boundaries. +# Regular limit is < 13 (8.15.2.11.F); the special-supervision +# extension raises it to < 18 (8.15.2.11.G) for children with a +# developmental delay. +# ============================================================ + +- name: Case 7, developmental delay child at exactly thirteen is eligible. + period: 2025-01 + input: + people: + person1: + age: 35 + immigration_status: CITIZEN + person2: + # Age 13 fails the regular < 13 limit, but the developmental-delay + # extension to < 18 keeps the child eligible. + age: 13 + has_developmental_delay: true + immigration_status: CITIZEN + households: + household: + members: [person1, person2] + state_code: NM + output: + nm_ccap_eligible_child: [false, true] + +- name: Case 8, developmental delay child at seventeen point nine nine is eligible. + period: 2025-01 + input: + people: + person1: + age: 45 + immigration_status: CITIZEN + person2: + # Just below the < 18 special-supervision limit -> eligible. + age: 17.99 + has_developmental_delay: true + immigration_status: CITIZEN + households: + household: + members: [person1, person2] + state_code: NM + output: + nm_ccap_eligible_child: [false, true] + +- name: Case 9, infant at six weeks is eligible. + period: 2025-01 + input: + people: + person1: + age: 35 + immigration_status: CITIZEN + person2: + # 6 weeks ~= 0.115 years. The regulation's lower bound is 6 weeks, but + # the formula only enforces the upper age limit (no lower bound is + # modeled at the moment), so any citizen child below 13 is eligible. + age: 0.115 + immigration_status: CITIZEN + households: + household: + members: [person1, person2] + state_code: NM + output: + nm_ccap_eligible_child: [false, true] + +- name: Case 10, child at exactly age thirteen without delay is ineligible. + period: 2025-01 + input: + people: + person1: + age: 35 + immigration_status: CITIZEN + person2: + # Age 13 with no developmental delay -> fails the regular < 13 limit. + age: 13 + has_developmental_delay: false + immigration_status: CITIZEN + households: + household: + members: [person1, person2] + state_code: NM + output: + nm_ccap_eligible_child: [false, false] + +# ============================================================ +# Universal Child Care (from 2025-11-01) removes the child-level +# citizenship/qualified-immigrant test (8.15.2.11.E NMAC pre-UCC; +# ECECD Universal Child Care post-UCC). Before UCC the test bars +# non-qualified-immigrant children; after UCC they are eligible. +# ============================================================ + +- name: Case 11, pre-UCC undocumented child is ineligible. + period: 2024-01 + input: + people: + person1: + age: 35 + immigration_status: CITIZEN + person2: + # Under 13 but undocumented; immigration test in effect -> ineligible. + age: 4 + immigration_status: UNDOCUMENTED + households: + household: + members: [person1, person2] + state_code: NM + output: + nm_ccap_eligible_child: [false, false] + +- name: Case 12, post-UCC undocumented child is eligible. + period: 2026-01 + input: + people: + person1: + age: 35 + immigration_status: CITIZEN + person2: + # Same undocumented child; Universal Child Care removes the immigration + # test, so the child is eligible on age alone. + age: 4 + immigration_status: UNDOCUMENTED + households: + household: + members: [person1, person2] + state_code: NM + output: + nm_ccap_eligible_child: [false, true] diff --git a/policyengine_us/tests/policy/baseline/gov/states/nm/ececd/ccap/eligibility/nm_ccap_income_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/nm/ececd/ccap/eligibility/nm_ccap_income_eligible.yaml new file mode 100644 index 00000000000..74190954925 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/nm/ececd/ccap/eligibility/nm_ccap_income_eligible.yaml @@ -0,0 +1,289 @@ +# NM CCAP income eligibility (8.15.2.9, 8.15.2.12 NMAC). +# Dual threshold: initial applicants (nm_ccap_enrolled = false) capped at +# 200% FPL; enrolled recipients (nm_ccap_enrolled = true) capped at 250% FPL. +# TANF-enrolled families (is_tanf_enrolled) are categorically income-eligible +# (Priority 2 bypass). Universal Child Care removes the income limit entirely +# from 2025-11-01 (income_limit_in_effect turns off), so families are +# income-eligible regardless of income from that date forward. +# +# 2025 FPG (CONTIGUOUS_US): family of 2 = 15_650 + 5_500 = 21_150/yr. +# 200% = 42_300/yr; 250% = 52_875/yr. +# 2024 FPG: family of 2 = 15_060 + 5_380 = 20_440/yr. +# 200% = 40_880/yr; 250% = 51_100/yr. + +# ============================================================ +# Initial applicant: 200% FPL cap +# ============================================================ + +- name: Case 1, initial applicant below 200% FPL is eligible. + period: 2025-01 + input: + people: + person1: + age: 30 + # 40_000/yr < 200% FPL family of 2 (42_300/yr) + employment_income: 40_000 + person2: + age: 3 + is_tax_unit_dependent: true + spm_units: + spm_unit: + members: [person1, person2] + nm_ccap_enrolled: false + households: + household: + members: [person1, person2] + state_code: NM + output: + nm_ccap_income_eligible: true + +- name: Case 2, initial applicant above 200% FPL is ineligible. + period: 2025-01 + input: + people: + person1: + age: 30 + # 45_000/yr > 200% FPL family of 2 (42_300/yr), but below 250% (52_875) + employment_income: 45_000 + person2: + age: 3 + is_tax_unit_dependent: true + spm_units: + spm_unit: + members: [person1, person2] + nm_ccap_enrolled: false + households: + household: + members: [person1, person2] + state_code: NM + output: + # Above the initial 200% cap and not yet enrolled -> ineligible + nm_ccap_income_eligible: false + +# ============================================================ +# Enrolled recipient: 250% FPL cap +# ============================================================ + +- name: Case 3, enrolled recipient between 200 and 250 percent FPL is eligible. + period: 2025-01 + input: + people: + person1: + age: 30 + # 45_000/yr is above 200% (42_300) but below 250% (52_875) FPL + employment_income: 45_000 + person2: + age: 3 + is_tax_unit_dependent: true + spm_units: + spm_unit: + members: [person1, person2] + nm_ccap_enrolled: true + households: + household: + members: [person1, person2] + state_code: NM + output: + # Enrolled recipient uses the higher 250% continuing cap -> eligible + nm_ccap_income_eligible: true + +- name: Case 4, enrolled recipient above 250 percent FPL is ineligible. + period: 2025-01 + input: + people: + person1: + age: 30 + # 55_000/yr > 250% FPL family of 2 (52_875/yr) + employment_income: 55_000 + person2: + age: 3 + is_tax_unit_dependent: true + spm_units: + spm_unit: + members: [person1, person2] + nm_ccap_enrolled: true + households: + household: + members: [person1, person2] + state_code: NM + output: + nm_ccap_income_eligible: false + +# ============================================================ +# TANF Priority 2 categorical bypass +# ============================================================ + +- name: Case 5, TANF enrolled family is categorically income eligible. + period: 2025-01 + input: + people: + person1: + age: 30 + # Well above 250% FPL, but TANF enrollment bypasses the income test. + employment_income: 100_000 + person2: + age: 3 + is_tax_unit_dependent: true + spm_units: + spm_unit: + members: [person1, person2] + nm_ccap_enrolled: false + is_tanf_enrolled: true + households: + household: + members: [person1, person2] + state_code: NM + output: + # is_tanf_enrolled -> Priority 2 categorical income eligibility. + nm_ccap_income_eligible: true + +# ============================================================ +# Universal Child Care: no income limit from 2025-11-01 +# ============================================================ + +- name: Case 6, before UCC a high income initial applicant is ineligible. + period: 2025-01 + input: + people: + person1: + age: 30 + # Far above 200% FPL; pre-UCC the income limit still applies. + employment_income: 100_000 + person2: + age: 3 + is_tax_unit_dependent: true + spm_units: + spm_unit: + members: [person1, person2] + nm_ccap_enrolled: false + households: + household: + members: [person1, person2] + state_code: NM + output: + # income_limit_in_effect is true in 2025-01 -> 200% cap applies -> ineligible + nm_ccap_income_eligible: false + +- name: Case 7, after UCC a high income family is eligible with no income limit. + period: 2026-01 + input: + people: + person1: + age: 30 + # Far above any FPL threshold; UCC removes the income limit entirely. + employment_income: 250_000 + person2: + age: 3 + is_tax_unit_dependent: true + spm_units: + spm_unit: + members: [person1, person2] + nm_ccap_enrolled: false + households: + household: + members: [person1, person2] + state_code: NM + output: + # income_limit_in_effect turned off 2025-11-01 -> eligible regardless of income. + nm_ccap_income_eligible: true + +# ============================================================ +# Threshold boundaries (income test uses <=, so income AT the +# limit is eligible). 2025 FPG family of 2 = 21_150/yr. +# 200% = 42_300/yr -> 3_525/month (initial cap). +# 250% = 52_875/yr -> 4_406.25/month (continuing cap). +# nm_ccap_countable_income is monthly (annual employment_income / 12), +# and the FPG limit auto-divides to monthly, so the comparison is monthly. +# ============================================================ + +- name: Case 8, initial applicant exactly at 200 percent FPL is eligible. + period: 2025-01 + input: + people: + person1: + age: 30 + # 42_300/yr / 12 = 3_525/month = exactly 200% FPL (family of 2). + employment_income: 42_300 + person2: + age: 3 + is_tax_unit_dependent: true + spm_units: + spm_unit: + members: [person1, person2] + nm_ccap_enrolled: false + households: + household: + members: [person1, person2] + state_code: NM + output: + # Countable income (3_525) <= 200% limit (3_525) -> eligible. + nm_ccap_income_eligible: true + +- name: Case 9, initial applicant just above 200 percent FPL is ineligible. + period: 2025-01 + input: + people: + person1: + age: 30 + # 42_360/yr / 12 = 3_530/month, just above the 3_525 cap. + employment_income: 42_360 + person2: + age: 3 + is_tax_unit_dependent: true + spm_units: + spm_unit: + members: [person1, person2] + nm_ccap_enrolled: false + households: + household: + members: [person1, person2] + state_code: NM + output: + # Countable income (3_530) > 200% limit (3_525) -> ineligible. + nm_ccap_income_eligible: false + +- name: Case 10, enrolled recipient exactly at 250 percent FPL is eligible. + period: 2025-01 + input: + people: + person1: + age: 30 + # 52_875/yr / 12 = 4_406.25/month = exactly 250% FPL (family of 2). + employment_income: 52_875 + person2: + age: 3 + is_tax_unit_dependent: true + spm_units: + spm_unit: + members: [person1, person2] + nm_ccap_enrolled: true # continuing recipient -> 250% cap + households: + household: + members: [person1, person2] + state_code: NM + output: + # Countable income (4_406.25) <= 250% limit (4_406.25) -> eligible. + nm_ccap_income_eligible: true + +- name: Case 11, enrolled recipient just above 250 percent FPL is ineligible. + period: 2025-01 + input: + people: + person1: + age: 30 + # 52_920/yr / 12 = 4_410/month, just above the 4_406.25 cap. + employment_income: 52_920 + person2: + age: 3 + is_tax_unit_dependent: true + spm_units: + spm_unit: + members: [person1, person2] + nm_ccap_enrolled: true + households: + household: + members: [person1, person2] + state_code: NM + output: + # Countable income (4_410) > 250% limit (4_406.25) -> ineligible. + nm_ccap_income_eligible: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/nm/ececd/ccap/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/nm/ececd/ccap/integration.yaml new file mode 100644 index 00000000000..16cdb2231bc --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/nm/ececd/ccap/integration.yaml @@ -0,0 +1,529 @@ +# NM CCAP integration tests — full pipeline from eligibility through benefit. +# +# Subsidy per child = min(monthly provider rate, pre-subsidy monthly charge) +# minus copay. Copayments are waived ($0) from 2022-05-01, so for current-era +# cases the benefit is simply sum over children of min(rate, monthly charge). +# +# Rates effective 2023-08-01 (full-time monthly base): +# Center: 2-Star Infant 1_075, Toddler 775, School Age 500; 5-Star Infant 1_925 +# Family Home: 2-Star Preschool 870 +# Registered Home: Infant 425 +# Service-unit fraction: FT 1.0, PT1 0.75, PT2 0.50, PT3 0.25. +# Non-traditional-hours supplement: +5% (1-10 hrs), +10% (11-20), +15% (21+). +# +# 2025 FPG (CONTIGUOUS_US): family of 2 = 21_150/yr; 200% = 42_300/yr, +# 250% = 52_875/yr. Family of 3 = 26_650/yr; 200% = 53_300/yr. +# 2026 FPG: family of 2 = 21_640/yr (income limit removed under UCC). +# Childcare expenses are set at the SPM-unit level via the annual +# spm_unit_pre_subsidy_childcare_expenses input (the per-child variable +# apportions from this aggregate); monthly charge = annual / 12. + +# ============================================================ +# Case 1: basic eligible family, full-time center care, positive subsidy. +# ============================================================ +- name: Case 1, eligible family with full time center care receives a positive subsidy. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + employment_income: 24_000 + weekly_hours_worked: 40 + immigration_status: CITIZEN + person2: + age: 3 # 36 months -> PRESCHOOL + is_tax_unit_dependent: true + immigration_status: CITIZEN + childcare_hours_per_week: 40 # FULL_TIME (fraction 1.0) + nm_ccap_provider_type: CENTER + nm_ccap_focus_level: TWO_STAR + spm_units: + spm_unit: + members: [person1, person2] + nm_ccap_enrolled: false + spm_unit_assets: 5_000 + spm_unit_pre_subsidy_childcare_expenses: 12_000 + households: + household: + members: [person1, person2] + state_code: NM + output: + # Eligible: child 3 < 13 and citizen; 24_000 < 200% FPL (42_300); assets ok; parent works. + nm_ccap_eligible: true + # Age 3 = 36 months -> PRESCHOOL. Center 2-Star Preschool = 700 (full-time). + nm_ccap_age_group: [SCHOOL_AGE, PRESCHOOL] + nm_ccap_monthly_rate: [0, 700] + # Monthly charge = 12_000/12 = 1_000. Subsidy = min(700, 1_000) = 700; copay $0. + nm_ccap: 700 + # At a single-month test period the YEAR aggregator returns that month's + # nm_ccap value (700); over a full year it would sum to 700 x 12 = 8_400. + nm_child_care_subsidies: 700 + +# ============================================================ +# Case 2: subsidy capped at actual (low) childcare charge. +# ============================================================ +- name: Case 2, subsidy capped at the pre-subsidy childcare charge. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 28 + employment_income: 6_000 + weekly_hours_worked: 35 + immigration_status: CITIZEN + person2: + age: 1 # 12 months -> INFANT + is_tax_unit_dependent: true + immigration_status: CITIZEN + childcare_hours_per_week: 40 # FULL_TIME + nm_ccap_provider_type: CENTER + nm_ccap_focus_level: FIVE_STAR + # Low charge: 3_600/yr -> 300/month, below the 1_925 rate. + spm_units: + spm_unit: + members: [person1, person2] + nm_ccap_enrolled: false + spm_unit_assets: 2_000 + spm_unit_pre_subsidy_childcare_expenses: 3_600 + households: + household: + members: [person1, person2] + state_code: NM + output: + nm_ccap_eligible: true + # Center 5-Star Infant = 1_925 (full-time). + nm_ccap_monthly_rate: [0, 1_925] + # Monthly charge = 3_600/12 = 300. Subsidy = min(1_925, 300) = 300. + nm_ccap: 300 + +# ============================================================ +# Case 3: enrolled recipient between 200% and 250% FPL stays eligible. +# ============================================================ +- name: Case 3, enrolled recipient above 200 percent FPL with family home care. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + # 45_000/yr is above 200% (42_300) but below 250% (52_875) family of 2. + employment_income: 45_000 + weekly_hours_worked: 40 + immigration_status: CITIZEN + person2: + age: 4 # 48 months -> PRESCHOOL + is_tax_unit_dependent: true + immigration_status: CITIZEN + childcare_hours_per_week: 40 # FULL_TIME + nm_ccap_provider_type: FAMILY_HOME + nm_ccap_focus_level: TWO_STAR + spm_units: + spm_unit: + members: [person1, person2] + nm_ccap_enrolled: true # continuing recipient -> 250% cap + spm_unit_assets: 10_000 + spm_unit_pre_subsidy_childcare_expenses: 9_600 + households: + household: + members: [person1, person2] + state_code: NM + output: + # Enrolled, 45_000 < 250% FPL -> eligible. + nm_ccap_income_eligible: true + nm_ccap_eligible: true + # Family Home 2-Star Preschool = 870 (full-time). + nm_ccap_monthly_rate: [0, 870] + # Monthly charge = 9_600/12 = 800. Subsidy = min(870, 800) = 800. + nm_ccap: 800 + +# ============================================================ +# Case 4: TANF Priority 2 bypass — high income but TANF-enrolled. +# ============================================================ +- name: Case 4, TANF enrolled family bypasses the income test and gets a subsidy. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + # Above 250% FPL, but TANF enrollment bypasses the income test. + employment_income: 80_000 + weekly_hours_worked: 40 + immigration_status: CITIZEN + person2: + age: 2 # 24 months -> TODDLER + is_tax_unit_dependent: true + immigration_status: CITIZEN + childcare_hours_per_week: 40 # FULL_TIME + nm_ccap_provider_type: CENTER + nm_ccap_focus_level: TWO_STAR + spm_units: + spm_unit: + members: [person1, person2] + nm_ccap_enrolled: false + is_tanf_enrolled: true + spm_unit_assets: 1_000 + spm_unit_pre_subsidy_childcare_expenses: 18_000 + households: + household: + members: [person1, person2] + state_code: NM + output: + nm_ccap_income_eligible: true + nm_ccap_eligible: true + # Age 2 = 24 months -> TODDLER. Center 2-Star Toddler = 775 (full-time). + nm_ccap_age_group: [SCHOOL_AGE, TODDLER] + nm_ccap_monthly_rate: [0, 775] + # Monthly charge = 18_000/12 = 1_500. Subsidy = min(775, 1_500) = 775. + nm_ccap: 775 + +# ============================================================ +# Case 5: part-time care with non-traditional-hours supplement. +# ============================================================ +- name: Case 5, part time one care with a non traditional hours supplement. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + employment_income: 18_000 + weekly_hours_worked: 25 + immigration_status: CITIZEN + person2: + age: 7 # 84 months -> SCHOOL_AGE + is_tax_unit_dependent: true + immigration_status: CITIZEN + # 20 hours/week of care -> PART_TIME_1 (0.75 fraction). + childcare_hours_per_week: 20 + nm_ccap_provider_type: CENTER + nm_ccap_focus_level: TWO_STAR + # 15 non-traditional hours/week -> +10% supplement (11-20 hr tier). + nm_ccap_non_traditional_hours: 15 + spm_units: + spm_unit: + members: [person1, person2] + nm_ccap_enrolled: false + spm_unit_assets: 3_000 + spm_unit_pre_subsidy_childcare_expenses: 12_000 + households: + household: + members: [person1, person2] + state_code: NM + output: + nm_ccap_eligible: true + # person1 (adult) has no care hours -> 0 hrs -> PART_TIME_3 (not an + # eligible child, so its rate is 0 regardless). person2 has 20 care hours + # -> PART_TIME_1. + nm_ccap_service_unit: [PART_TIME_3, PART_TIME_1] + # Center 2-Star School Age base = 500. PART_TIME_1 fraction 0.75 -> 375. + # Non-traditional supplement +10% -> 375 x 1.10 = 412.50. + nm_ccap_monthly_rate: [0, 412.5] + # Monthly charge = 1_000 > 412.50. Subsidy = min(412.50, 1_000) = 412.50. + nm_ccap: 412.5 + +# ============================================================ +# Case 6: Universal Child Care — high income, no income limit (2026). +# ============================================================ +- name: Case 6, after UCC a high income family is eligible with no income limit. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + # Far above any FPL threshold; UCC (2025-11-01) removed the income limit. + employment_income: 250_000 + weekly_hours_worked: 40 + immigration_status: CITIZEN + person2: + age: 0.5 # 6 months -> INFANT + is_tax_unit_dependent: true + immigration_status: CITIZEN + childcare_hours_per_week: 40 # FULL_TIME + nm_ccap_provider_type: REGISTERED_HOME + spm_units: + spm_unit: + members: [person1, person2] + nm_ccap_enrolled: false + spm_unit_assets: 50_000 + spm_unit_pre_subsidy_childcare_expenses: 6_000 + households: + household: + members: [person1, person2] + state_code: NM + output: + # No income limit in 2026 -> income eligible despite 250k earnings. + nm_ccap_income_eligible: true + nm_ccap_eligible: true + # Registered Home Infant = 425 (no FOCUS levels, full-time). + nm_ccap_monthly_rate: [0, 425] + # Monthly charge = 6_000/12 = 500. Subsidy = min(425, 500) = 425. + nm_ccap: 425 + +# ============================================================ +# Case 7: ineligible — non-NM household gets zero. +# ============================================================ +- name: Case 7, non New Mexico family receives zero benefit. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + employment_income: 24_000 + weekly_hours_worked: 40 + immigration_status: CITIZEN + person2: + age: 3 + is_tax_unit_dependent: true + immigration_status: CITIZEN + childcare_hours_per_week: 40 + nm_ccap_provider_type: CENTER + nm_ccap_focus_level: TWO_STAR + spm_units: + spm_unit: + members: [person1, person2] + nm_ccap_enrolled: false + spm_unit_assets: 5_000 + spm_unit_pre_subsidy_childcare_expenses: 12_000 + households: + household: + members: [person1, person2] + # Colorado, not New Mexico -> defined_for zeroes the benefit. + state_code: CO + output: + nm_ccap: 0 + nm_child_care_subsidies: 0 + +# ============================================================ +# Case 8: degenerate — single adult, no eligible child. +# ============================================================ +- name: Case 8, single adult with no child receives zero benefit. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + employment_income: 12_000 + weekly_hours_worked: 40 + immigration_status: CITIZEN + spm_units: + spm_unit: + members: [person1] + nm_ccap_enrolled: false + spm_unit_assets: 1_000 + households: + household: + members: [person1] + state_code: NM + output: + nm_ccap_eligible: false + nm_ccap: 0 + +# ============================================================ +# Case 9: edge — zero childcare expenses yields zero subsidy. +# ============================================================ +- name: Case 9, eligible family with zero childcare expenses gets zero subsidy. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + employment_income: 24_000 + weekly_hours_worked: 40 + immigration_status: CITIZEN + person2: + age: 3 + is_tax_unit_dependent: true + immigration_status: CITIZEN + childcare_hours_per_week: 40 + nm_ccap_provider_type: CENTER + nm_ccap_focus_level: TWO_STAR + spm_units: + spm_unit: + members: [person1, person2] + nm_ccap_enrolled: false + spm_unit_assets: 5_000 + spm_unit_pre_subsidy_childcare_expenses: 0 + households: + household: + members: [person1, person2] + state_code: NM + output: + nm_ccap_eligible: true + # Subsidy = min(rate, 0) = 0 -> capped at actual expenses. + nm_ccap: 0 + +# ============================================================ +# Case 10: edge — negative income does not inflate the subsidy. +# ============================================================ +- name: Case 10, negative income family subsidy stays capped at the charge. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + # Large business loss -> negative countable income. + self_employment_income: -600_000 + weekly_hours_worked: 40 + immigration_status: CITIZEN + person2: + age: 3 + is_tax_unit_dependent: true + immigration_status: CITIZEN + childcare_hours_per_week: 40 # FULL_TIME + nm_ccap_provider_type: CENTER + nm_ccap_focus_level: TWO_STAR + spm_units: + spm_unit: + members: [person1, person2] + nm_ccap_enrolled: false + spm_unit_assets: 1_000 + spm_unit_pre_subsidy_childcare_expenses: 6_000 + households: + household: + members: [person1, person2] + state_code: NM + output: + # Negative income still passes the income test (below 200% FPL). + nm_ccap_income_eligible: true + nm_ccap_eligible: true + # Center 2-Star Preschool = 700. Monthly charge = 500. + # Subsidy = min(700, 500) = 500 — capped at the charge, NOT inflated by the loss. + nm_ccap: 500 + +# ============================================================ +# Case 11: whole-year period — annual aggregator sums 12 months. +# ============================================================ +- name: Case 11, annual child care subsidies sum twelve monthly benefits. + period: 2025 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + employment_income: 24_000 + weekly_hours_worked: 40 + immigration_status: CITIZEN + person2: + age: 3 # 36 months -> PRESCHOOL + is_tax_unit_dependent: true + immigration_status: CITIZEN + childcare_hours_per_week: 40 # FULL_TIME + nm_ccap_provider_type: CENTER + nm_ccap_focus_level: TWO_STAR + spm_units: + spm_unit: + members: [person1, person2] + nm_ccap_enrolled: false + spm_unit_assets: 5_000 + spm_unit_pre_subsidy_childcare_expenses: 12_000 + households: + household: + members: [person1, person2] + state_code: NM + output: + # Monthly nm_ccap = min(700, 1_000) = 700; the YEAR aggregator sums 12 + # months: 700 x 12 = 8_400. + nm_child_care_subsidies: 8_400 + +# ============================================================ +# Case 12: edge — initial applicant exactly at 200% FPL (family of 3) +# stays eligible and a positive subsidy flows through the pipeline. +# 2025 FPG family of 3 = 26_650/yr; 200% = 53_300/yr -> 4_441.67/month. +# ============================================================ +- name: Case 12, initial applicant exactly at 200 percent FPL receives a subsidy. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 32 + # 53_300/yr / 12 = 4_441.67/month = exactly 200% FPL (family of 3). + employment_income: 53_300 + weekly_hours_worked: 40 + immigration_status: CITIZEN + person2: + age: 30 + weekly_hours_worked: 40 + immigration_status: CITIZEN + person3: + age: 1 # 12 months -> INFANT + is_tax_unit_dependent: true + immigration_status: CITIZEN + childcare_hours_per_week: 40 # FULL_TIME + nm_ccap_provider_type: CENTER + nm_ccap_focus_level: TWO_STAR + pre_subsidy_childcare_expenses: 18_000 # 1_500/month + spm_units: + spm_unit: + members: [person1, person2, person3] + nm_ccap_enrolled: false # initial applicant -> 200% cap + spm_unit_assets: 8_000 + spm_unit_pre_subsidy_childcare_expenses: 18_000 + households: + household: + members: [person1, person2, person3] + state_code: NM + output: + # Countable income (4_441.67) <= 200% limit (4_441.67) -> eligible. + nm_ccap_income_eligible: true + nm_ccap_eligible: true + # Center 2-Star Infant = 1_075 (full-time). Monthly charge = 1_500. + nm_ccap_monthly_rate: [0, 0, 1_075] + # Subsidy = min(1_075, 1_500) = 1_075; copay $0. + nm_ccap: 1_075 + +# ============================================================ +# Case 13: edge — two eligible children sum their per-child subsidies. +# ============================================================ +- name: Case 13, two eligible children sum their per child subsidies. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + employment_income: 24_000 + weekly_hours_worked: 40 + immigration_status: CITIZEN + person2: + age: 1 # 12 months -> INFANT + is_tax_unit_dependent: true + immigration_status: CITIZEN + childcare_hours_per_week: 40 # FULL_TIME + nm_ccap_provider_type: CENTER + nm_ccap_focus_level: TWO_STAR + pre_subsidy_childcare_expenses: 18_000 # 1_500/month, above the rate + person3: + age: 7 # 84 months -> SCHOOL_AGE + is_tax_unit_dependent: true + immigration_status: CITIZEN + childcare_hours_per_week: 40 # FULL_TIME + nm_ccap_provider_type: CENTER + nm_ccap_focus_level: TWO_STAR + pre_subsidy_childcare_expenses: 12_000 # 1_000/month, above the rate + spm_units: + spm_unit: + members: [person1, person2, person3] + nm_ccap_enrolled: false + spm_unit_assets: 5_000 + # Family total = 18_000 + 12_000 = 30_000/yr -> 2_500/month. + spm_unit_pre_subsidy_childcare_expenses: 30_000 + households: + household: + members: [person1, person2, person3] + state_code: NM + output: + nm_ccap_eligible: true + # Center 2-Star Infant = 1_075; Center 2-Star School Age = 500. + nm_ccap_monthly_rate: [0, 1_075, 500] + # max_reimbursement summed = 1_075 + 500 = 1_575; family charge = 2_500. + # Subsidy = min(1_575, 2_500) - 0 = 1_575. + nm_ccap: 1_575 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nm/ececd/ccap/nm_ccap_age_group.yaml b/policyengine_us/tests/policy/baseline/gov/states/nm/ececd/ccap/nm_ccap_age_group.yaml new file mode 100644 index 00000000000..d1df7483a63 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/nm/ececd/ccap/nm_ccap_age_group.yaml @@ -0,0 +1,90 @@ +# NM CCAP age group classification by age in months (8.15.2.7 NMAC). +# Bracket boundaries (rates/age_group/months.yaml): +# 0-23 months -> INFANT +# 24-35 months -> TODDLER +# 36-71 months -> PRESCHOOL +# 72+ months -> SCHOOL_AGE + +- name: Case 1, six month old is an infant. + period: 2025-01 + input: + people: + person1: + # 0.5 years = 6 months -> INFANT + age: 0.5 + households: + household: + members: [person1] + state_code: NM + output: + nm_ccap_age_group: INFANT + +- name: Case 2, twenty three months is the top of the infant band. + period: 2025-01 + input: + people: + person1: + # 23/12 = 1.9167 years = 23 months -> INFANT (below 24-month threshold) + age: 1.9167 + households: + household: + members: [person1] + state_code: NM + output: + nm_ccap_age_group: INFANT + +- name: Case 3, exactly twenty four months is a toddler. + period: 2025-01 + input: + people: + person1: + # 2.0 years = 24 months -> TODDLER (at the 24-month threshold) + age: 2.0 + households: + household: + members: [person1] + state_code: NM + output: + nm_ccap_age_group: TODDLER + +- name: Case 4, exactly thirty six months is preschool. + period: 2025-01 + input: + people: + person1: + # 3.0 years = 36 months -> PRESCHOOL (at the 36-month threshold) + age: 3.0 + households: + household: + members: [person1] + state_code: NM + output: + nm_ccap_age_group: PRESCHOOL + +- name: Case 5, seventy one months is the top of preschool. + period: 2025-01 + input: + people: + person1: + # 71/12 = 5.9167 years = 71 months -> PRESCHOOL (below 72-month threshold) + age: 5.9167 + households: + household: + members: [person1] + state_code: NM + output: + nm_ccap_age_group: PRESCHOOL + +- name: Case 6, exactly seventy two months is school age. + period: 2025-01 + input: + people: + person1: + # 6.0 years = 72 months -> SCHOOL_AGE (at the 72-month threshold) + age: 6.0 + households: + household: + members: [person1] + state_code: NM + output: + nm_ccap_age_group: SCHOOL_AGE diff --git a/policyengine_us/tests/policy/baseline/gov/states/nm/ececd/ccap/nm_ccap_copay.yaml b/policyengine_us/tests/policy/baseline/gov/states/nm/ececd/ccap/nm_ccap_copay.yaml new file mode 100644 index 00000000000..ebf7fa576ea --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/nm/ececd/ccap/nm_ccap_copay.yaml @@ -0,0 +1,72 @@ +# NM CCAP copayment (8.15.2.13 NMAC; ECECD Co-payments Waived flyer). +# ECECD waives all copayments from 2022-05-01 forward, and the waiver remains +# in effect under Universal Child Care. The pre-2022 copayment formula +# (8.15.2.13.B) is not modeled at the moment, so the copay is treated as $0 for +# all modeled periods regardless of income. + +- name: Case 1, copay is zero in the current waiver era. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + employment_income: 30_000 + person2: + age: 3 + is_tax_unit_dependent: true + spm_units: + spm_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: NM + output: + # Copayments waived since 2022-05-01 -> $0. + nm_ccap_copay: 0 + +- name: Case 2, copay is zero even for a higher income family. + period: 2024-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + employment_income: 40_000 + person2: + age: 5 + is_tax_unit_dependent: true + spm_units: + spm_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: NM + output: + nm_ccap_copay: 0 + +- name: Case 3, copay is zero before the waiver because the formula is deferred. + period: 2021-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 32 + employment_income: 30_000 + person2: + age: 4 + is_tax_unit_dependent: true + spm_units: + spm_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: NM + output: + # Pre-2022 era: the waiver is not yet in effect (waived = false), but the + # pre-2022 copay formula (8.15.2.13.B) is not modeled, so the copay is + # still $0. + nm_ccap_copay: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nm/ececd/ccap/nm_ccap_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/nm/ececd/ccap/nm_ccap_countable_income.yaml new file mode 100644 index 00000000000..2940c769fd7 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/nm/ececd/ccap/nm_ccap_countable_income.yaml @@ -0,0 +1,115 @@ +# NM CCAP countable income (8.15.2.11.C NMAC). The counted sources are a +# positive list (sources.yaml); exempt sources (SNAP, TANF, SSI, SSDI, child +# support, etc.) are excluded by omission. 8.15.2.11.C(3) further excludes the +# earned and unearned income of legal dependents, so the formula counts only +# caretakers' (tax unit head/spouse) income. This is a MONTH variable that sums +# YEAR-defined income sources, so the monthly value is the annual sum / 12. + +- name: Case 1, counted earned and unearned income. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + employment_income: 24_000 # counted + social_security_retirement: 6_000 # counted (adult retirement) + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: NM + output: + # (24_000 + 6_000) / 12 = 2_500/month + nm_ccap_countable_income: 2_500 + +- name: Case 2, exempt sources are not counted. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + employment_income: 12_000 # counted + ssi: 9_000 # exempt (omitted) + child_support_received: 3_600 # exempt (omitted) + unemployment_compensation: 4_800 # exempt (omitted) + spm_units: + spm_unit: + members: [person1] + snap: 3_000 # exempt (omitted) + tanf: 2_400 # exempt (omitted) + households: + household: + members: [person1] + state_code: NM + output: + # Only employment_income counts: 12_000 / 12 = 1_000/month. + nm_ccap_countable_income: 1_000 + +- name: Case 3, zero income. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + employment_income: 0 + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: NM + output: + nm_ccap_countable_income: 0 + +- name: Case 4, SSDI is exempt for an adult. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + employment_income: 12_000 # counted + social_security_disability: 18_000 # exempt SSDI (8.15.2.11.C(6)) + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: NM + output: + # Only employment counts: 12_000 / 12 = 1_000/month. SSDI is excluded. + nm_ccap_countable_income: 1_000 + +- name: Case 5, a household dependent's earned and unearned income is excluded. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + social_security_retirement: 12_000 # counted (caretaker) + is_tax_unit_head_or_spouse: true + person2: + age: 10 + employment_income: 8_000 # excluded (dependent) + social_security_survivors: 6_000 # excluded (dependent) + is_tax_unit_head_or_spouse: false + spm_units: + spm_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: NM + output: + # Only the caretaker's retirement counts: 12_000 / 12 = 1_000/month. + # Under 8.15.2.11.C(3), the dependent child's earnings and survivors + # benefit are both excluded from household income. + nm_ccap_countable_income: 1_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nm/ececd/ccap/nm_ccap_monthly_rate.yaml b/policyengine_us/tests/policy/baseline/gov/states/nm/ececd/ccap/nm_ccap_monthly_rate.yaml new file mode 100644 index 00000000000..9dc27e8b4d8 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/nm/ececd/ccap/nm_ccap_monthly_rate.yaml @@ -0,0 +1,464 @@ +# NM CCAP monthly provider reimbursement rate lookup (rate flyer, effective +# August 2023; 8.15.2.13.C NMAC). Rate = provider type x FOCUS level x age +# group, scaled by the service-unit fraction (FT 100% / PT1 75% / PT2 50% / +# PT3 25%) and a non-traditional-hours supplement when applicable. +# +# Every case below uses FULL_TIME care (childcare_hours_per_week >= 30, so the +# service-unit fraction is 1.0) and zero non-traditional hours (no supplement), +# so the rate equals the full-time base table value. All cases run in 2025 +# (rates effective 2023-08-01). state_code: NM so defined_for does not zero the +# rate, and immigration/age make each child eligible. +# +# Base full-time monthly rates: +# Center: 2-Star Infant 1_075, Toddler 775, Preschool 700, School Age 500 +# 5-Star Infant 1_925; 4-Star Toddler 1_100; 2+/3-Star Preschool 800 +# Group: 2-Star Infant 1_040; 5-Star School Age 670 +# Family: 2-Star Infant 1_100; 4-Star Toddler 1_250 +# Registered (no stars): Infant 425, Toddler 425, Preschool 375, School Age 350 + +# ============================================================ +# Licensed Center, all FOCUS levels (Toddler unless noted) +# ============================================================ + +- name: Case 1, center two star infant. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 0.5 # 6 months -> INFANT + immigration_status: CITIZEN + childcare_hours_per_week: 40 + nm_ccap_provider_type: CENTER + nm_ccap_focus_level: TWO_STAR + households: + household: + members: [person1] + state_code: NM + output: + nm_ccap_monthly_rate: 1_075 + +- name: Case 2, center two plus star toddler. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 2.5 # 30 months -> TODDLER + immigration_status: CITIZEN + childcare_hours_per_week: 40 + nm_ccap_provider_type: CENTER + nm_ccap_focus_level: TWO_PLUS_STAR + households: + household: + members: [person1] + state_code: NM + output: + # Center 2+Star Toddler = 865 + nm_ccap_monthly_rate: 865 + +- name: Case 3, center three star preschool. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 3.5 # 42 months -> PRESCHOOL + immigration_status: CITIZEN + childcare_hours_per_week: 40 + nm_ccap_provider_type: CENTER + nm_ccap_focus_level: THREE_STAR + households: + household: + members: [person1] + state_code: NM + output: + # Center 3-Star Preschool = 800 (identical to 2+Star in the flyer) + nm_ccap_monthly_rate: 800 + +- name: Case 4, center four star toddler. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 2.5 # TODDLER + immigration_status: CITIZEN + childcare_hours_per_week: 40 + nm_ccap_provider_type: CENTER + nm_ccap_focus_level: FOUR_STAR + households: + household: + members: [person1] + state_code: NM + output: + # Center 4-Star Toddler = 1_100 + nm_ccap_monthly_rate: 1_100 + +- name: Case 5, center five star infant. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 0.5 # INFANT + immigration_status: CITIZEN + childcare_hours_per_week: 40 + nm_ccap_provider_type: CENTER + nm_ccap_focus_level: FIVE_STAR + households: + household: + members: [person1] + state_code: NM + output: + # Center 5-Star Infant = 1_925 + nm_ccap_monthly_rate: 1_925 + +# ============================================================ +# Licensed Group Home and Licensed Family Home +# ============================================================ + +- name: Case 6, group home two star infant. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 0.5 # INFANT + immigration_status: CITIZEN + childcare_hours_per_week: 40 + nm_ccap_provider_type: GROUP_HOME + nm_ccap_focus_level: TWO_STAR + households: + household: + members: [person1] + state_code: NM + output: + # Group Home 2-Star Infant = 1_040 + nm_ccap_monthly_rate: 1_040 + +- name: Case 7, group home five star school age. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 7 # 84 months -> SCHOOL_AGE + immigration_status: CITIZEN + childcare_hours_per_week: 40 + nm_ccap_provider_type: GROUP_HOME + nm_ccap_focus_level: FIVE_STAR + households: + household: + members: [person1] + state_code: NM + output: + # Group Home 5-Star School Age = 670 + nm_ccap_monthly_rate: 670 + +- name: Case 8, family home two star infant. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 0.5 # INFANT + immigration_status: CITIZEN + childcare_hours_per_week: 40 + nm_ccap_provider_type: FAMILY_HOME + nm_ccap_focus_level: TWO_STAR + households: + household: + members: [person1] + state_code: NM + output: + # Family Home 2-Star Infant = 1_100 + nm_ccap_monthly_rate: 1_100 + +- name: Case 9, family home four star toddler. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 2.5 # TODDLER + immigration_status: CITIZEN + childcare_hours_per_week: 40 + nm_ccap_provider_type: FAMILY_HOME + nm_ccap_focus_level: FOUR_STAR + households: + household: + members: [person1] + state_code: NM + output: + # Family Home 4-Star Toddler = 1_250 + nm_ccap_monthly_rate: 1_250 + +# ============================================================ +# Registered / In-Home: single rate, no FOCUS levels. +# All four age groups. +# ============================================================ + +- name: Case 10, registered home infant. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 0.5 # INFANT + immigration_status: CITIZEN + childcare_hours_per_week: 40 + nm_ccap_provider_type: REGISTERED_HOME + households: + household: + members: [person1] + state_code: NM + output: + # Registered Home Infant = 425 (no star levels) + nm_ccap_monthly_rate: 425 + +- name: Case 11, registered home toddler. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 2.5 # TODDLER + immigration_status: CITIZEN + childcare_hours_per_week: 40 + nm_ccap_provider_type: REGISTERED_HOME + households: + household: + members: [person1] + state_code: NM + output: + # Registered Home Toddler = 425 + nm_ccap_monthly_rate: 425 + +- name: Case 12, registered home preschool. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 3.5 # PRESCHOOL + immigration_status: CITIZEN + childcare_hours_per_week: 40 + nm_ccap_provider_type: REGISTERED_HOME + households: + household: + members: [person1] + state_code: NM + output: + # Registered Home Preschool = 375 + nm_ccap_monthly_rate: 375 + +- name: Case 13, registered home school age. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 7 # SCHOOL_AGE + immigration_status: CITIZEN + childcare_hours_per_week: 40 + nm_ccap_provider_type: REGISTERED_HOME + households: + household: + members: [person1] + state_code: NM + output: + # Registered Home School Age = 350 + nm_ccap_monthly_rate: 350 + +# ============================================================ +# Part-time service-unit fractions applied to the base rate +# ============================================================ + +- name: Case 14, part time one applies seventy five percent. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 0.5 # INFANT + immigration_status: CITIZEN + # 20 hours/week -> PART_TIME_1 (75%) + childcare_hours_per_week: 20 + nm_ccap_provider_type: CENTER + nm_ccap_focus_level: TWO_STAR + households: + household: + members: [person1] + state_code: NM + output: + # Center 2-Star Infant 1_075 x 0.75 = 806.25 + nm_ccap_monthly_rate: 806.25 + +- name: Case 15, part time three applies twenty five percent. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 0.5 # INFANT + immigration_status: CITIZEN + # 5 hours/week -> PART_TIME_3 (25%) + childcare_hours_per_week: 5 + nm_ccap_provider_type: CENTER + nm_ccap_focus_level: TWO_STAR + households: + household: + members: [person1] + state_code: NM + output: + # Center 2-Star Infant 1_075 x 0.25 = 268.75 + nm_ccap_monthly_rate: 268.75 + +- name: Case 16, part time two split custody applies fifty percent. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 0.5 # INFANT + immigration_status: CITIZEN + childcare_hours_per_week: 40 + nm_ccap_provider_type: CENTER + nm_ccap_focus_level: TWO_STAR + # PART_TIME_2 is only reachable via explicit input (split custody). + nm_ccap_service_unit: PART_TIME_2 + households: + household: + members: [person1] + state_code: NM + output: + # Center 2-Star Infant 1_075 x 0.50 = 537.50 + nm_ccap_monthly_rate: 537.5 + +# ============================================================ +# Non-traditional-hours supplement bracket boundaries +# (supplements/non_traditional_hours.yaml): 0 hrs -> +0%, +# 1-10 hrs -> +5%, 11-20 hrs -> +10%, 21+ hrs -> +15%. +# Base full-time Center 2-Star Infant = 1_075 in every case below. +# ============================================================ + +- name: Case 17, zero non traditional hours adds no supplement. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 0.5 # INFANT + immigration_status: CITIZEN + childcare_hours_per_week: 40 # FULL_TIME + nm_ccap_provider_type: CENTER + nm_ccap_focus_level: TWO_STAR + nm_ccap_non_traditional_hours: 0 # below the 1-hour threshold -> +0% + households: + household: + members: [person1] + state_code: NM + output: + # 1_075 x (1 + 0) = 1_075 + nm_ccap_monthly_rate: 1_075 + +- name: Case 18, one non traditional hour applies the five percent tier. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 0.5 # INFANT + immigration_status: CITIZEN + childcare_hours_per_week: 40 # FULL_TIME + nm_ccap_provider_type: CENTER + nm_ccap_focus_level: TWO_STAR + nm_ccap_non_traditional_hours: 1 # at the 1-hour threshold -> +5% + households: + household: + members: [person1] + state_code: NM + output: + # 1_075 x 1.05 = 1_128.75 + nm_ccap_monthly_rate: 1_128.75 + +- name: Case 19, ten non traditional hours is the top of the five percent tier. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 0.5 # INFANT + immigration_status: CITIZEN + childcare_hours_per_week: 40 # FULL_TIME + nm_ccap_provider_type: CENTER + nm_ccap_focus_level: TWO_STAR + nm_ccap_non_traditional_hours: 10 # 10 < 11 -> still +5% + households: + household: + members: [person1] + state_code: NM + output: + # 1_075 x 1.05 = 1_128.75 + nm_ccap_monthly_rate: 1_128.75 + +- name: Case 20, eleven non traditional hours applies the ten percent tier. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 0.5 # INFANT + immigration_status: CITIZEN + childcare_hours_per_week: 40 # FULL_TIME + nm_ccap_provider_type: CENTER + nm_ccap_focus_level: TWO_STAR + nm_ccap_non_traditional_hours: 11 # at the 11-hour threshold -> +10% + households: + household: + members: [person1] + state_code: NM + output: + # 1_075 x 1.10 = 1_182.50 + nm_ccap_monthly_rate: 1_182.5 + +- name: Case 21, twenty non traditional hours is the top of the ten percent tier. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 0.5 # INFANT + immigration_status: CITIZEN + childcare_hours_per_week: 40 # FULL_TIME + nm_ccap_provider_type: CENTER + nm_ccap_focus_level: TWO_STAR + nm_ccap_non_traditional_hours: 20 # 20 < 21 -> still +10% + households: + household: + members: [person1] + state_code: NM + output: + # 1_075 x 1.10 = 1_182.50 + nm_ccap_monthly_rate: 1_182.5 + +- name: Case 22, twenty one non traditional hours applies the fifteen percent tier. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 0.5 # INFANT + immigration_status: CITIZEN + childcare_hours_per_week: 40 # FULL_TIME + nm_ccap_provider_type: CENTER + nm_ccap_focus_level: TWO_STAR + nm_ccap_non_traditional_hours: 21 # at the 21-hour threshold -> +15% + households: + household: + members: [person1] + state_code: NM + output: + # 1_075 x 1.15 = 1_236.25 + nm_ccap_monthly_rate: 1_236.25 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nm/ececd/ccap/nm_ccap_service_unit.yaml b/policyengine_us/tests/policy/baseline/gov/states/nm/ececd/ccap/nm_ccap_service_unit.yaml new file mode 100644 index 00000000000..5247422dd75 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/nm/ececd/ccap/nm_ccap_service_unit.yaml @@ -0,0 +1,112 @@ +# NM CCAP service unit classification by average weekly hours of care +# (rate flyer definitions; rates/service_unit/hours.yaml). +# Bracket boundaries: +# 0-7 hours/week -> PART_TIME_3 (25%) +# 8-29 hours/week -> PART_TIME_1 (75%) +# 30+ hours/week -> FULL_TIME (100%) +# PART_TIME_2 (split custody, 50%) is not derivable from hours alone and is +# reachable only via explicit input, so it never appears from this bracket. + +- name: Case 1, seven or fewer hours is part time three. + period: 2025-01 + input: + people: + person1: + age: 3 + childcare_hours_per_week: 5 + households: + household: + members: [person1] + state_code: NM + output: + # 5 hours <= 7 -> PART_TIME_3 + nm_ccap_service_unit: PART_TIME_3 + +- name: Case 2, seven hours is the top of part time three. + period: 2025-01 + input: + people: + person1: + age: 3 + childcare_hours_per_week: 7 + households: + household: + members: [person1] + state_code: NM + output: + # 7 hours < 8 -> PART_TIME_3 + nm_ccap_service_unit: PART_TIME_3 + +- name: Case 3, eight hours is part time one. + period: 2025-01 + input: + people: + person1: + age: 3 + childcare_hours_per_week: 8 + households: + household: + members: [person1] + state_code: NM + output: + # 8 hours -> PART_TIME_1 + nm_ccap_service_unit: PART_TIME_1 + +- name: Case 4, twenty nine hours is the top of part time one. + period: 2025-01 + input: + people: + person1: + age: 3 + childcare_hours_per_week: 29 + households: + household: + members: [person1] + state_code: NM + output: + # 29 hours < 30 -> PART_TIME_1 + nm_ccap_service_unit: PART_TIME_1 + +- name: Case 5, thirty hours is full time. + period: 2025-01 + input: + people: + person1: + age: 3 + childcare_hours_per_week: 30 + households: + household: + members: [person1] + state_code: NM + output: + # 30 hours -> FULL_TIME + nm_ccap_service_unit: FULL_TIME + +- name: Case 6, forty five hours is full time. + period: 2025-01 + input: + people: + person1: + age: 3 + childcare_hours_per_week: 45 + households: + household: + members: [person1] + state_code: NM + output: + nm_ccap_service_unit: FULL_TIME + +- name: Case 7, zero hours is part time three. + period: 2025-01 + input: + people: + person1: + age: 3 + childcare_hours_per_week: 0 + households: + household: + members: [person1] + state_code: NM + output: + # 0 hours falls in the lowest bracket [0, 8) -> PART_TIME_3. + nm_ccap_service_unit: PART_TIME_3 diff --git a/policyengine_us/variables/gov/states/nm/ececd/ccap/eligibility/nm_ccap_activity_eligible.py b/policyengine_us/variables/gov/states/nm/ececd/ccap/eligibility/nm_ccap_activity_eligible.py new file mode 100644 index 00000000000..5a7aba39371 --- /dev/null +++ b/policyengine_us/variables/gov/states/nm/ececd/ccap/eligibility/nm_ccap_activity_eligible.py @@ -0,0 +1,33 @@ +from policyengine_us.model_api import * + + +class nm_ccap_activity_eligible(Variable): + value_type = bool + entity = SPMUnit + label = "Eligible for New Mexico CCAP based on activity requirements" + definition_period = MONTH + defined_for = StateCode.NM + reference = "https://www.srca.nm.gov/parts/title08/08.015.0002.html" + + def formula(spm_unit, period, parameters): + # 8.15.2.11.I / 8.15.2.7: benefits are for families working, attending + # school, or in a job-training/educational program. 8.15.2.11.I + # requires the activity to be present (it does not set a minimum-hours + # floor), so we test for any work hours or full-time student status. + # Each head/spouse caretaker must independently meet the activity + # requirement. + person = spm_unit.members + is_head_or_spouse = person("is_tax_unit_head_or_spouse", period.this_year) + hours_worked = person("weekly_hours_worked", period.this_year) + is_student = person("is_full_time_student", period.this_year) + individually_eligible = (hours_worked > 0) | is_student + has_caretaker = spm_unit.sum(is_head_or_spouse) > 0 + no_ineligible_caretaker = ( + spm_unit.sum(is_head_or_spouse & ~individually_eligible) == 0 + ) + # Fallback for approved activities not modeled individually here -- job + # training, job search, and temporary leave from work or school + # (8.15.2.7): set meets_ccdf_activity_test to flag the unit as meeting + # the activity requirement. + meets_ccdf = spm_unit("meets_ccdf_activity_test", period.this_year) + return (has_caretaker & no_ineligible_caretaker) | meets_ccdf diff --git a/policyengine_us/variables/gov/states/nm/ececd/ccap/eligibility/nm_ccap_eligible.py b/policyengine_us/variables/gov/states/nm/ececd/ccap/eligibility/nm_ccap_eligible.py new file mode 100644 index 00000000000..b7fefbfffdd --- /dev/null +++ b/policyengine_us/variables/gov/states/nm/ececd/ccap/eligibility/nm_ccap_eligible.py @@ -0,0 +1,17 @@ +from policyengine_us.model_api import * + + +class nm_ccap_eligible(Variable): + value_type = bool + entity = SPMUnit + label = "Eligible for New Mexico CCAP" + definition_period = MONTH + defined_for = StateCode.NM + reference = "https://www.srca.nm.gov/parts/title08/08.015.0002.html" + + def formula(spm_unit, period, parameters): + has_eligible_child = add(spm_unit, period, ["nm_ccap_eligible_child"]) > 0 + income_eligible = spm_unit("nm_ccap_income_eligible", period) + asset_eligible = spm_unit("is_ccdf_asset_eligible", period.this_year) + activity_eligible = spm_unit("nm_ccap_activity_eligible", period) + return has_eligible_child & income_eligible & asset_eligible & activity_eligible diff --git a/policyengine_us/variables/gov/states/nm/ececd/ccap/eligibility/nm_ccap_eligible_child.py b/policyengine_us/variables/gov/states/nm/ececd/ccap/eligibility/nm_ccap_eligible_child.py new file mode 100644 index 00000000000..89fda812759 --- /dev/null +++ b/policyengine_us/variables/gov/states/nm/ececd/ccap/eligibility/nm_ccap_eligible_child.py @@ -0,0 +1,42 @@ +from policyengine_us.model_api import * + + +class nm_ccap_eligible_child(Variable): + value_type = bool + entity = Person + label = "Eligible child for New Mexico CCAP" + definition_period = MONTH + defined_for = StateCode.NM + reference = ( + "https://www.srca.nm.gov/parts/title08/08.015.0002.html", + "https://www.law.cornell.edu/cfr/text/45/98.20", + ) + + def formula(person, period, parameters): + # 8.15.2.11.F: eligible from 6 weeks old through the day before the + # 13th birthday. The 6-week lower bound is not modeled because we don't + # track sub-year age granularity at the moment (all children under 1 + # read age == 0), so enforcing it would wrongly exclude every infant. + # 8.15.2.11.G: extended to under 18 for children needing special + # supervision. We model the medical/treatment-professional branch via + # has_developmental_delay; the court-supervision branch is not tracked + # at the moment. + p = parameters(period).gov.states.nm.ececd.ccap.eligibility + age = person("age", period.this_year) + has_developmental_delay = person("has_developmental_delay", period.this_year) + age_eligible = where( + has_developmental_delay, + age < p.special_needs_child_age_limit, + age < p.child_age_limit, + ) + # 8.15.2.11.E: before Universal Child Care, a child must be a citizen or + # qualified immigrant. Under Universal Child Care (from 2025-11-01) this + # bar is removed: a participating child without a federally eligible + # status is served with state funds, so status only sets the funding + # source, not eligibility (ECECD Universal Child Care). + if p.immigration_test_in_effect: + immigration_eligible = person( + "is_ccdf_immigration_eligible_child", period.this_year + ) + return age_eligible & immigration_eligible + return age_eligible diff --git a/policyengine_us/variables/gov/states/nm/ececd/ccap/eligibility/nm_ccap_income_eligible.py b/policyengine_us/variables/gov/states/nm/ececd/ccap/eligibility/nm_ccap_income_eligible.py new file mode 100644 index 00000000000..ed7e3f45917 --- /dev/null +++ b/policyengine_us/variables/gov/states/nm/ececd/ccap/eligibility/nm_ccap_income_eligible.py @@ -0,0 +1,34 @@ +from policyengine_us.model_api import * + + +class nm_ccap_income_eligible(Variable): + value_type = bool + entity = SPMUnit + label = "Eligible for New Mexico CCAP based on income" + definition_period = MONTH + defined_for = StateCode.NM + reference = ( + "https://www.srca.nm.gov/parts/title08/08.015.0002.html", + "https://www.nmececd.org/universal/", + ) + + def formula(spm_unit, period, parameters): + p = parameters(period).gov.states.nm.ececd.ccap.eligibility + # Universal Child Care removes the income limit from November 1, 2025: + # families are income-eligible regardless of income from that date. + if not p.income_limit_in_effect: + return True + # 8.15.2.9 / 8.15.2.12: initial certification caps income at 200% FPL; + # enrolled families remain eligible (recertification) up to 250% FPL. + countable_income = spm_unit("nm_ccap_countable_income", period) + fpg = spm_unit("spm_unit_fpg", period) + enrolled = spm_unit("nm_ccap_enrolled", period) + income_limit = fpg * where( + enrolled, p.fpl_limit.continuation, p.fpl_limit.initial + ) + fpl_eligible = countable_income <= income_limit + # 8.15.2.9 Priority 2: families transitioning off TANF need not meet + # the income test. is_tanf_enrolled is a bare input that breaks the + # CCAP <-> TANF circular dependency. + is_tanf = spm_unit("is_tanf_enrolled", period) + return is_tanf | fpl_eligible diff --git a/policyengine_us/variables/gov/states/nm/ececd/ccap/nm_ccap.py b/policyengine_us/variables/gov/states/nm/ececd/ccap/nm_ccap.py new file mode 100644 index 00000000000..c8b707d43aa --- /dev/null +++ b/policyengine_us/variables/gov/states/nm/ececd/ccap/nm_ccap.py @@ -0,0 +1,27 @@ +from policyengine_us.model_api import * + + +class nm_ccap(Variable): + value_type = float + entity = SPMUnit + unit = USD + label = "New Mexico CCAP benefit amount" + definition_period = MONTH + defined_for = "nm_ccap_eligible" + reference = ( + "https://www.srca.nm.gov/parts/title08/08.015.0002.html", + "https://www.nmececd.org/wp-content/uploads/2024/05/Cost-Model-Reimbursement-Rate-Flyer-English-and-Spanish-Revised-May-2024.pdf#page=2", + ) + + def formula(spm_unit, period, parameters): + # 8.15.2.13.C: ECECD pays providers the lesser of the reimbursement + # rate and the provider's actual charge, less the family copayment. + # Following the standard PolicyEngine childcare pattern (see RI CCAP), + # the reimbursement is summed across eligible children and capped at + # the family's actual pre-subsidy expenses at the SPM-unit level. + max_reimbursement = add(spm_unit, period, ["nm_ccap_monthly_rate"]) + # spm_unit_pre_subsidy_childcare_expenses is annual; reading it with + # the bare period auto-divides it to a monthly amount. + actual_expenses = spm_unit("spm_unit_pre_subsidy_childcare_expenses", period) + copay = spm_unit("nm_ccap_copay", period) + return max_(min_(max_reimbursement, actual_expenses) - copay, 0) diff --git a/policyengine_us/variables/gov/states/nm/ececd/ccap/nm_ccap_age_group.py b/policyengine_us/variables/gov/states/nm/ececd/ccap/nm_ccap_age_group.py new file mode 100644 index 00000000000..ef572cf20e7 --- /dev/null +++ b/policyengine_us/variables/gov/states/nm/ececd/ccap/nm_ccap_age_group.py @@ -0,0 +1,25 @@ +from policyengine_us.model_api import * + + +class NMCCAPAgeGroup(Enum): + INFANT = "Infant" + TODDLER = "Toddler" + PRESCHOOL = "Preschool" + SCHOOL_AGE = "School Age" + + +class nm_ccap_age_group(Variable): + value_type = Enum + entity = Person + possible_values = NMCCAPAgeGroup + default_value = NMCCAPAgeGroup.PRESCHOOL + definition_period = MONTH + label = "New Mexico CCAP child age group" + defined_for = StateCode.NM + reference = "https://www.srca.nm.gov/parts/title08/08.015.0002.html" + + def formula(person, period, parameters): + # age is a YEAR variable; convert to months to look up the age group. + age_in_months = person("age", period.this_year) * MONTHS_IN_YEAR + p = parameters(period).gov.states.nm.ececd.ccap.rates.age_group + return p.months.calc(age_in_months) diff --git a/policyengine_us/variables/gov/states/nm/ececd/ccap/nm_ccap_copay.py b/policyengine_us/variables/gov/states/nm/ececd/ccap/nm_ccap_copay.py new file mode 100644 index 00000000000..11719c2dbdc --- /dev/null +++ b/policyengine_us/variables/gov/states/nm/ececd/ccap/nm_ccap_copay.py @@ -0,0 +1,24 @@ +from policyengine_us.model_api import * + + +class nm_ccap_copay(Variable): + value_type = float + entity = SPMUnit + unit = USD + definition_period = MONTH + label = "New Mexico CCAP family copayment" + defined_for = StateCode.NM + reference = ( + "https://www.nmececd.org/wp-content/uploads/2024/09/CCA-Co-payments-waived_rev1l.pdf#page=1", + "https://www.srca.nm.gov/parts/title08/08.015.0002.html", + ) + + def formula(spm_unit, period, parameters): + p = parameters(period).gov.states.nm.ececd.ccap.copay + # ECECD has waived all copayments since May 1, 2022, so families pay $0 + # while the waiver is in effect. The pre-2022 copayment formula + # (8.15.2.13.B) is not modeled at the moment; in that era the + # copayment is also treated as $0. + if p.waived: + return 0 + return 0 diff --git a/policyengine_us/variables/gov/states/nm/ececd/ccap/nm_ccap_countable_income.py b/policyengine_us/variables/gov/states/nm/ececd/ccap/nm_ccap_countable_income.py new file mode 100644 index 00000000000..cef70eba159 --- /dev/null +++ b/policyengine_us/variables/gov/states/nm/ececd/ccap/nm_ccap_countable_income.py @@ -0,0 +1,23 @@ +from policyengine_us.model_api import * + + +class nm_ccap_countable_income(Variable): + value_type = float + entity = SPMUnit + label = "New Mexico CCAP countable income" + definition_period = MONTH + unit = USD + defined_for = StateCode.NM + reference = "https://www.srca.nm.gov/parts/title08/08.015.0002.html" + + def formula(spm_unit, period, parameters): + p = parameters(period).gov.states.nm.ececd.ccap.income.countable_income + # 8.15.2.11.C(3): household income excludes the earned and unearned + # income of legal dependents and of grandparents who are not legal + # guardians. Count only caretakers (tax unit head/spouse), so household + # children's income is excluded. SSDI is omitted from the sources list, + # so it is exempt for everyone (8.15.2.11.C(6)). + person = spm_unit.members + is_caretaker = person("is_tax_unit_head_or_spouse", period.this_year) + person_income = add(person, period, p.sources) + return spm_unit.sum(person_income * is_caretaker) diff --git a/policyengine_us/variables/gov/states/nm/ececd/ccap/nm_ccap_enrolled.py b/policyengine_us/variables/gov/states/nm/ececd/ccap/nm_ccap_enrolled.py new file mode 100644 index 00000000000..4697ad11fd9 --- /dev/null +++ b/policyengine_us/variables/gov/states/nm/ececd/ccap/nm_ccap_enrolled.py @@ -0,0 +1,9 @@ +from policyengine_us.model_api import * + + +class nm_ccap_enrolled(Variable): + value_type = bool + entity = SPMUnit + definition_period = MONTH + label = "Whether the family is currently enrolled in New Mexico CCAP" + defined_for = StateCode.NM diff --git a/policyengine_us/variables/gov/states/nm/ececd/ccap/nm_ccap_focus_level.py b/policyengine_us/variables/gov/states/nm/ececd/ccap/nm_ccap_focus_level.py new file mode 100644 index 00000000000..761be3cb061 --- /dev/null +++ b/policyengine_us/variables/gov/states/nm/ececd/ccap/nm_ccap_focus_level.py @@ -0,0 +1,20 @@ +from policyengine_us.model_api import * + + +class NMCCAPFocusLevel(Enum): + TWO_STAR = "2-Star" + TWO_PLUS_STAR = "2+Star FOCUS" + THREE_STAR = "3-Star FOCUS" + FOUR_STAR = "4-Star FOCUS" + FIVE_STAR = "5-Star FOCUS" + + +class nm_ccap_focus_level(Variable): + value_type = Enum + entity = Person + possible_values = NMCCAPFocusLevel + default_value = NMCCAPFocusLevel.TWO_STAR + definition_period = MONTH + label = "New Mexico CCAP provider FOCUS quality level" + defined_for = StateCode.NM + reference = "https://www.nmececd.org/wp-content/uploads/2024/05/Cost-Model-Reimbursement-Rate-Flyer-English-and-Spanish-Revised-May-2024.pdf#page=2" diff --git a/policyengine_us/variables/gov/states/nm/ececd/ccap/nm_ccap_monthly_rate.py b/policyengine_us/variables/gov/states/nm/ececd/ccap/nm_ccap_monthly_rate.py new file mode 100644 index 00000000000..01ec65ed0b0 --- /dev/null +++ b/policyengine_us/variables/gov/states/nm/ececd/ccap/nm_ccap_monthly_rate.py @@ -0,0 +1,54 @@ +from policyengine_us.model_api import * +from policyengine_us.variables.gov.states.nm.ececd.ccap.nm_ccap_provider_type import ( + NMCCAPProviderType, +) + + +class nm_ccap_monthly_rate(Variable): + value_type = float + entity = Person + unit = USD + definition_period = MONTH + label = "New Mexico CCAP monthly provider reimbursement rate" + defined_for = "nm_ccap_eligible_child" + reference = "https://www.nmececd.org/wp-content/uploads/2024/05/Cost-Model-Reimbursement-Rate-Flyer-English-and-Spanish-Revised-May-2024.pdf#page=2" + + def formula(person, period, parameters): + p = parameters(period).gov.states.nm.ececd.ccap + provider_type = person("nm_ccap_provider_type", period) + focus_level = person("nm_ccap_focus_level", period) + age_group = person("nm_ccap_age_group", period) + service_unit = person("nm_ccap_service_unit", period) + + rates = p.rates + # Licensed Center / Group Home / Family Home rates vary by FOCUS level + # and age group; Registered / In-Home receives a single rate by age + # group with no FOCUS dimension. + center_rate = rates.center[focus_level][age_group] + group_home_rate = rates.group_home[focus_level][age_group] + family_home_rate = rates.family_home[focus_level][age_group] + registered_rate = rates.registered_home[age_group] + types = NMCCAPProviderType + full_time_rate = select( + [ + provider_type == types.CENTER, + provider_type == types.GROUP_HOME, + provider_type == types.FAMILY_HOME, + provider_type == types.REGISTERED_HOME, + ], + [ + center_rate, + group_home_rate, + family_home_rate, + registered_rate, + ], + default=registered_rate, + ) + # Part-time service units pay a fraction of the full-time rate. + applicable_rate = full_time_rate * rates.service_unit_fraction[service_unit] + # Non-traditional-hours differential adds a share of the applicable rate. + non_traditional_hours = person("nm_ccap_non_traditional_hours", period) + supplement_rate = p.supplements.non_traditional_hours.calc( + non_traditional_hours + ) + return applicable_rate * (1 + supplement_rate) diff --git a/policyengine_us/variables/gov/states/nm/ececd/ccap/nm_ccap_non_traditional_hours.py b/policyengine_us/variables/gov/states/nm/ececd/ccap/nm_ccap_non_traditional_hours.py new file mode 100644 index 00000000000..2eee4a3b5c6 --- /dev/null +++ b/policyengine_us/variables/gov/states/nm/ececd/ccap/nm_ccap_non_traditional_hours.py @@ -0,0 +1,11 @@ +from policyengine_us.model_api import * + + +class nm_ccap_non_traditional_hours(Variable): + value_type = float + entity = Person + unit = "hour" + definition_period = MONTH + label = "New Mexico CCAP weekly non-traditional hours of care" + defined_for = StateCode.NM + reference = "https://www.nmececd.org/wp-content/uploads/2024/05/Cost-Model-Reimbursement-Rate-Flyer-English-and-Spanish-Revised-May-2024.pdf#page=5" diff --git a/policyengine_us/variables/gov/states/nm/ececd/ccap/nm_ccap_provider_type.py b/policyengine_us/variables/gov/states/nm/ececd/ccap/nm_ccap_provider_type.py new file mode 100644 index 00000000000..0eb5c677f81 --- /dev/null +++ b/policyengine_us/variables/gov/states/nm/ececd/ccap/nm_ccap_provider_type.py @@ -0,0 +1,19 @@ +from policyengine_us.model_api import * + + +class NMCCAPProviderType(Enum): + CENTER = "Licensed Center" + GROUP_HOME = "Licensed Group Home" + FAMILY_HOME = "Licensed Family Home" + REGISTERED_HOME = "Registered Home / In-Home" + + +class nm_ccap_provider_type(Variable): + value_type = Enum + entity = Person + possible_values = NMCCAPProviderType + default_value = NMCCAPProviderType.CENTER + definition_period = MONTH + label = "New Mexico CCAP child care provider type" + defined_for = StateCode.NM + reference = "https://www.nmececd.org/wp-content/uploads/2024/05/Cost-Model-Reimbursement-Rate-Flyer-English-and-Spanish-Revised-May-2024.pdf#page=2" diff --git a/policyengine_us/variables/gov/states/nm/ececd/ccap/nm_ccap_service_unit.py b/policyengine_us/variables/gov/states/nm/ececd/ccap/nm_ccap_service_unit.py new file mode 100644 index 00000000000..c6e753d726b --- /dev/null +++ b/policyengine_us/variables/gov/states/nm/ececd/ccap/nm_ccap_service_unit.py @@ -0,0 +1,31 @@ +from policyengine_us.model_api import * + + +class NMCCAPServiceUnit(Enum): + FULL_TIME = "Full time (30+ hrs/wk)" + PART_TIME_1 = "Part time 1 (8-29 hrs/wk)" + PART_TIME_2 = "Part time 2 (split custody, 8-19 hrs/wk)" + PART_TIME_3 = "Part time 3 (<=7 hrs/wk)" + + +class nm_ccap_service_unit(Variable): + value_type = Enum + entity = Person + possible_values = NMCCAPServiceUnit + default_value = NMCCAPServiceUnit.FULL_TIME + definition_period = MONTH + label = "New Mexico CCAP service unit" + defined_for = StateCode.NM + reference = "https://www.nmececd.org/wp-content/uploads/2024/05/Cost-Model-Reimbursement-Rate-Flyer-English-and-Spanish-Revised-May-2024.pdf#page=1" + + def formula(person, period, parameters): + # The hours bracket maps average weekly care hours to FULL_TIME, + # PART_TIME_1, or PART_TIME_3. PART_TIME_2 (split custody / two + # providers) is not derivable from hours alone, so it is never + # produced by this formula; we don't track custody arrangements at the + # moment. + # childcare_hours_per_week is a weekly rate stored annually; read it + # with period.this_year so it is not auto-divided to a monthly value. + hours = person("childcare_hours_per_week", period.this_year) + p = parameters(period).gov.states.nm.ececd.ccap.rates.service_unit + return p.hours.calc(hours) diff --git a/policyengine_us/variables/gov/states/nm/ececd/ccap/nm_child_care_subsidies.py b/policyengine_us/variables/gov/states/nm/ececd/ccap/nm_child_care_subsidies.py new file mode 100644 index 00000000000..40e6586f8ef --- /dev/null +++ b/policyengine_us/variables/gov/states/nm/ececd/ccap/nm_child_care_subsidies.py @@ -0,0 +1,11 @@ +from policyengine_us.model_api import * + + +class nm_child_care_subsidies(Variable): + value_type = float + entity = SPMUnit + label = "New Mexico child care subsidies" + unit = USD + definition_period = YEAR + defined_for = StateCode.NM + adds = ["nm_ccap"]