diff --git a/changelog.d/or-ssp-2026.added.md b/changelog.d/or-ssp-2026.added.md new file mode 100644 index 00000000000..2b4c2c7bb0f --- /dev/null +++ b/changelog.d/or-ssp-2026.added.md @@ -0,0 +1 @@ +Add Oregon State Supplemental Payment for 2026. diff --git a/policyengine_us/parameters/gov/household/household_state_benefits.yaml b/policyengine_us/parameters/gov/household/household_state_benefits.yaml index 235a7a5d63a..658748a0580 100644 --- a/policyengine_us/parameters/gov/household/household_state_benefits.yaml +++ b/policyengine_us/parameters/gov/household/household_state_benefits.yaml @@ -66,6 +66,8 @@ values: # Oklahoma benefits - ok_child_care_subsidies - ok_ssp + # Oregon benefits + - or_ssp # South Carolina benefits - sc_child_care_subsidies # New Jersey benefits @@ -177,6 +179,8 @@ values: # Oklahoma benefits - ok_child_care_subsidies - ok_ssp + # Oregon benefits + - or_ssp # New Jersey benefits - nj_ssp # New Mexico benefits @@ -295,6 +299,8 @@ values: # Oklahoma benefits - ok_child_care_subsidies - ok_ssp + # Oregon benefits + - or_ssp # New Jersey benefits - nj_ssp # New Mexico benefits @@ -414,6 +420,8 @@ values: # Oklahoma benefits - ok_child_care_subsidies - ok_ssp + # Oregon benefits + - or_ssp # New Jersey benefits - nj_ssp # New Mexico benefits diff --git a/policyengine_us/parameters/gov/hud/annual_income/sources/unearned.yaml b/policyengine_us/parameters/gov/hud/annual_income/sources/unearned.yaml index 257416a9692..317d2e14917 100644 --- a/policyengine_us/parameters/gov/hud/annual_income/sources/unearned.yaml +++ b/policyengine_us/parameters/gov/hud/annual_income/sources/unearned.yaml @@ -22,6 +22,7 @@ values: - nj_ssp - pa_ssp - wy_ssp + - or_ssp_countable_for_hud - nc_sa_countable_for_hud - nv_oss - mt_ssp diff --git a/policyengine_us/parameters/gov/states/or/odhs/ssp/excluded_income_sources.yaml b/policyengine_us/parameters/gov/states/or/odhs/ssp/excluded_income_sources.yaml new file mode 100644 index 00000000000..31ec0e3b287 --- /dev/null +++ b/policyengine_us/parameters/gov/states/or/odhs/ssp/excluded_income_sources.yaml @@ -0,0 +1,15 @@ +description: Oregon excludes these income sources from countable income under the State Supplemental Payment program. +values: + 2018-04-01: + - interest_income + - dividend_income + +metadata: + unit: list + period: year + label: Oregon SSP excluded income sources + reference: + - title: OAR 461-145-0030(4) + href: https://ch461rules.odhs.oregon.gov/rules/461-145-0030.pdf#page=2 + - title: OAR 461-145-0108(1)(a) + href: https://ch461rules.odhs.oregon.gov/rules/461-145-0108.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/or/odhs/ssp/in_home_amount.yaml b/policyengine_us/parameters/gov/states/or/odhs/ssp/in_home_amount.yaml new file mode 100644 index 00000000000..894ded26f79 --- /dev/null +++ b/policyengine_us/parameters/gov/states/or/odhs/ssp/in_home_amount.yaml @@ -0,0 +1,11 @@ +description: Oregon provides this amount as the monthly in-home supplement under the State Supplemental Payment program. +values: + 2016-01-01: 22 + +metadata: + unit: currency-USD + period: month + label: Oregon SSP in-home reimbursement + reference: + - title: OAR 461-155-0575(2) + href: https://ch461rules.odhs.oregon.gov/rules/461-155-0575.pdf diff --git a/policyengine_us/parameters/gov/states/or/odhs/ssp/nursing_facility_payment_standard.yaml b/policyengine_us/parameters/gov/states/or/odhs/ssp/nursing_facility_payment_standard.yaml new file mode 100644 index 00000000000..ae1dcd0d5e9 --- /dev/null +++ b/policyengine_us/parameters/gov/states/or/odhs/ssp/nursing_facility_payment_standard.yaml @@ -0,0 +1,18 @@ +description: Oregon pays a nursing facility resident the difference between countable income and this amount under the State Supplemental Payment program. +values: + # NOTE: 461-155-0250(4)(c) prints $79.07, while (4)(b) (effective 1-01-26) + # and transmittal OEP-PT-25-045 (effective 1/1/2026) raise the personal + # incidentals allowance to $81.28. Every version with subsection (c), from + # 7-01-14 to 1-01-25, used one figure in (b) and (c). The model follows the + # text of (4)(c). + 2026-01-01: 79.07 + +metadata: + unit: currency-USD + period: month + label: Oregon SSP nursing facility payment standard + reference: + - title: "OAR 461-155-0250(4)(c) #page 1-2" + href: https://ch461rules.odhs.oregon.gov/rules/461-155-0250.pdf#page=1 + - title: ODHS transmittal OEP-PT-25-045 + href: https://www.oregon.gov/odhs/transmittals/oeptransmittals/pt25045.pdf#page=8 diff --git a/policyengine_us/parameters/gov/states/or/odhs/ssp/reduced_va_pension.yaml b/policyengine_us/parameters/gov/states/or/odhs/ssp/reduced_va_pension.yaml new file mode 100644 index 00000000000..1c0cbc05ec2 --- /dev/null +++ b/policyengine_us/parameters/gov/states/or/odhs/ssp/reduced_va_pension.yaml @@ -0,0 +1,13 @@ +description: Oregon limits the nursing-facility restoration payment to recipients whose reduced Veterans pension is at most this amount under the State Supplemental Payment program. +values: + 2020-01-01: 90 + +metadata: + unit: currency-USD + period: month + label: Oregon SSP reduced VA pension limit + reference: + - title: OAR 461-155-0700(1)(b) + href: https://ch461rules.odhs.oregon.gov/rules/461-155-0700.pdf#page=1 + - title: OAR 461-155-0700 history, version effective 1-01-20 + href: https://ch461rules.odhs.oregon.gov/historical/461-155-0700_history.pdf#page=7 diff --git a/policyengine_us/parameters/gov/states/or/odhs/ssp/room_and_board.yaml b/policyengine_us/parameters/gov/states/or/odhs/ssp/room_and_board.yaml new file mode 100644 index 00000000000..b4a84ab0e54 --- /dev/null +++ b/policyengine_us/parameters/gov/states/or/odhs/ssp/room_and_board.yaml @@ -0,0 +1,13 @@ +description: Oregon sets this amount as the monthly community-based care room and board standard under the State Supplemental Payment program. +values: + 2026-01-01: 773 + +metadata: + unit: currency-USD + period: month + label: Oregon OSIPM room and board standard + reference: + - title: OAR 461-155-0270 + href: https://ch461rules.odhs.oregon.gov/rules/461-155-0270.pdf + - title: OEP-PT-25-045, January 2026 standards + href: https://www.oregon.gov/odhs/transmittals/oeptransmittals/pt25045.pdf#page=7 diff --git a/policyengine_us/parameters/gov/usda/snap/income/sources/unearned.yaml b/policyengine_us/parameters/gov/usda/snap/income/sources/unearned.yaml index e8d950512b9..45cd902c557 100644 --- a/policyengine_us/parameters/gov/usda/snap/income/sources/unearned.yaml +++ b/policyengine_us/parameters/gov/usda/snap/income/sources/unearned.yaml @@ -61,6 +61,10 @@ values: - va_ag - wa_ssp - wy_ssp + # Not listed: OAR 461-145-0410(7)(c)(D) excludes Oregon's modeled OSIP + # special need payments (or_ssp) from SNAP income as reimbursements. Its + # nursing facility payment goes to institution residents, who are not SNAP + # households under 7 CFR 273.1(b)(7)(vi). # Listed elsewhere: ca_state_supplement is an SPM unit variable with no # person-level counterpart. policyengine-core projects a group value onto # every member, so listing it here would count it once per person in the diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index 43fdd714b80..c94a159f027 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -1174,6 +1174,17 @@ programs: homes, plus the institutional Supplement for Personal Needs. Mandatory grandfathered and essential-person supplements and ancillary facility subsidies are not modeled. + - state: OR + status: partial + name: Oregon SSP + full_name: Oregon State Supplemental Payment + variable: or_ssp + parameter_prefix: gov.states.or.odhs.ssp + verified_years: "2026" + notes: Models 2026 OSIPM in-home, SSI couple community care, transitional personal + incidentals/room and board, spousal diversion, special room-and-board and nursing + facility personal incidentals payments. Other special needs and historical mandatory + supplements are not modeled. - state: WY status: partial name: Wyoming SSP diff --git a/policyengine_us/tests/policy/baseline/gov/states/or/odhs/ssp/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/or/odhs/ssp/integration.yaml new file mode 100644 index 00000000000..344bb1f05ec --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/or/odhs/ssp/integration.yaml @@ -0,0 +1,160 @@ +- name: Case 1, couple SSI recipients in care. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 65 + or_ssp_category: COMMUNITY_CARE + person2: + age: 65 + or_ssp_category: COMMUNITY_CARE + marital_units: + couple: + members: [person1, person2] + households: + hh: + members: [person1, person2] + state_code: OR + output: + or_ssp: [248.5, 248.5] + +- name: Case 2, excess income of spouse offsets other spouse. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 65 + or_ssp_category: COMMUNITY_CARE + # $1,200 a month. + social_security_retirement: 14_400 + person2: + age: 65 + or_ssp_category: COMMUNITY_CARE + marital_units: + couple: + members: [person1, person2] + households: + hh: + members: [person1, person2] + state_code: OR + output: + # Couple computation: 1,491 / 2 = 745.50 each. + ssi_amount_if_eligible: [745.5, 745.5] + # Couple SSI: 1,491 - (1,200 - 20) = 311, or 155.50 each. + ssi: [155.5, 155.5] + # person1: 1,200 + 155.50 = 1,355.50; person2: 155.50. + or_ssp_countable_income: [1_355.5, 155.5] + # person1: 994 - 1,355.50 < 0, so 0; excess 1,355.50 - 994 = 361.50. + # person2: 994 - 155.50 - 361.50 = 477. + or_ssp: [0, 477] + +- name: Case 3, both spouses must receive SSI. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 65 + or_ssp_category: COMMUNITY_CARE + person2: + age: 65 + or_ssp_category: COMMUNITY_CARE + takes_up_ssi_if_eligible: false + marital_units: + couple: + members: [person1, person2] + households: + hh: + members: [person1, person2] + state_code: OR + output: + # person2 does not receive SSI, so OAR 461-155-0630(3) does not apply. + or_ssp: [0, 0] + +- name: Case 4, both spouses must have community care. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 65 + or_ssp_category: COMMUNITY_CARE + person2: + age: 65 + or_ssp_category: NONE + marital_units: + couple: + members: [person1, person2] + households: + hh: + members: [person1, person2] + state_code: OR + output: + or_ssp: [0, 0] + +- name: Case 5, in-home reimbursement excluded from SNAP and HUD income. + period: 2026 + absolute_error_margin: 0.01 + input: + age: 65 + state_code: OR + or_ssp_category: IN_HOME + output: + or_ssp: 264 + household_state_benefits: 264 + snap_unearned_income: 11_928 + hud_unearned_income: 11_928 + +- name: Case 6, community care payment counts as HUD but not SNAP unearned income. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 65 + or_ssp_category: COMMUNITY_CARE + # $1,200 a month. + social_security_retirement: 14_400 + person2: + age: 65 + or_ssp_category: COMMUNITY_CARE + marital_units: + couple: + members: [person1, person2] + households: + hh: + members: [person1, person2] + state_code: OR + output: + # Case 2 monthly amounts for 12 months: SSI 155.50 * 12 = 1,866 each. + ssi: [1_866, 1_866] + # person2: 477 * 12 = 5,724. + or_ssp: [0, 5_724] + # Community care is not the in-home reimbursement, so HUD counts it. + or_ssp_countable_for_hud: [0, 5_724] + # OAR 461-145-0410(7)(c)(D) excludes the special need from SNAP: + # 12 * (1,200 Social Security + 311 SSI) = 18,132. + snap_unearned_income: 18_132 + # person1: 14,400 Social Security + 1,866 SSI = 16,266. + # person2: 1,866 SSI + 5,724 SSP = 7,590. + hud_unearned_income: [16_266, 7_590] + +- name: Case 7, temporary absence without a care liability counts all countable income. + period: 2026-01 + absolute_error_margin: 0.01 + input: + age: 65 + state_code: OR + or_ssp_category: ROOM_BOARD_ABSENCE + # $500 a month. + social_security_retirement: 6_000 + output: + # SSI: 994 - (500 - 20) = 514. + ssi: 514 + # 514 SSI + 500 Social Security = 1,014. + or_ssp_countable_income: 1_014 + # No care liability, so all 1,014 is available for room and board. + # 773 room and board standard - 1,014 < 0, so no payment. + or_ssp: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/or/odhs/ssp/or_ssp.yaml b/policyengine_us/tests/policy/baseline/gov/states/or/odhs/ssp/or_ssp.yaml new file mode 100644 index 00000000000..66d110843ce --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/or/odhs/ssp/or_ssp.yaml @@ -0,0 +1,602 @@ +- name: Case 1, no certified services. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: + 2026: OR + age: + 2026: 65 + output: + or_ssp: 0 + +- name: Case 2, approved in-home SSI-only recipient. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: + 2026: OR + age: + 2026: 65 + or_ssp_category: IN_HOME + output: + or_ssp: 22 + +- name: Case 3, Social Security income disqualifies the in-home supplement. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: + 2026: OR + age: + 2026: 65 + or_ssp_category: IN_HOME + # $500 a month. + social_security_retirement: 6_000 + output: + # SSI: 994 - (500 - 20) = 514; countable income 514 + 500 = 1,014. + # 1,014 is not SSI alone, so no in-home supplement. + or_ssp: 0 + +- name: Case 4, part-time earnings disqualify the in-home supplement. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: + 2026: OR + age: + 2026: 65 + or_ssp_category: IN_HOME + # 10 hours a week at Oregon's $15.05 standard minimum wage (July 2025): + # 15.05 * 10 * 52 = 7,826 a year, or 652.17 a month. + employment_income: 7_826 + output: + # SSI: 994 - (652.17 - 20 - 65) * 0.5 = 994 - 283.58 = 710.42. + # Countable income 710.42 + 652.17 = 1,362.58 is not SSI alone, so 0. + or_ssp: 0 + +- name: Case 5, excluded bank interest does not disqualify. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: + 2026: OR + age: + 2026: 65 + or_ssp_category: IN_HOME + interest_income: 120 + output: + or_ssp: 22 + +- name: Case 6, in-home supplement requires SSI. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: + 2026: OR + age: + 2026: 65 + or_ssp_category: IN_HOME + ssi: 0 + output: + or_ssp: 0 + +- name: Case 7, disability interim assistance. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: + 2026: OR + age: + 2026: 40 + or_ssp_category: DISABILITY_INTERIM + ssi: 0 + output: + or_ssp: 994 + +- name: Case 8, interim amount uses countable not adjusted income. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: + 2026: OR + age: + 2026: 40 + or_ssp_category: DISABILITY_INTERIM + ssi: 0 + employment_income: 1_200 + output: + or_ssp: 894 + +- name: Case 9, SSI restored after institutional payment. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: + 2026: OR + age: + 2026: 65 + or_ssp_category: SSI_RESTORATION + ssi: 30 + output: + or_ssp: 964 + +- name: Case 10, full SSI ends restoration gap. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: + 2026: OR + age: + 2026: 65 + or_ssp_category: SSI_RESTORATION + output: + or_ssp: 0 + +- name: Case 11, reduced VA pension restoration. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: + 2026: OR + age: + 2026: 65 + or_ssp_category: VA_RESTORATION + ssi: 0 + veterans_benefits: 1_080 + output: + or_ssp: 904 + +- name: Case 12, pursuing naturalization after SSI time limit. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: + 2026: OR + age: + 2026: 65 + or_ssp_category: NATURALIZATION + ssi: 0 + output: + or_ssp: 994 + +- name: Case 13, suspended SSI gets room and board only. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: + 2026: OR + age: + 2026: 65 + or_ssp_category: SUSPENDED_SSI + ssi: 0 + output: + or_ssp: 773 + +- name: Case 14, suspension allowance ends when SSI restarts. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: + 2026: OR + age: + 2026: 65 + or_ssp_category: SUSPENDED_SSI + output: + or_ssp: 0 + +- name: Case 15, January sixteen day restoration grant. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: + 2026: OR + age: + 2026: 65 + or_ssp_category: SSI_RESTORATION + ssi: 30 + or_ssp_partial_month_days: 16 + output: + or_ssp: 497.5483870967742 + +- name: Case 16, February fourteen day interim grant. + period: 2026-02 + absolute_error_margin: 0.01 + input: + state_code: + 2026: OR + age: + 2026: 40 + or_ssp_category: DISABILITY_INTERIM + ssi: 0 + or_ssp_partial_month_days: 14 + output: + or_ssp: 497 + +- name: Case 17, eligible days covering the whole month pay the full amount. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: + 2026: OR + age: + 2026: 65 + or_ssp_category: IN_HOME + or_ssp_partial_month_days: 31 + output: + or_ssp: 22 + +- name: Case 18, spousal diversion includes income shortfall. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: + 2026: OR + age: + 2026: 65 + or_ssp_category: SPOUSAL_DIVERSION + ssi: 0 + social_security_retirement: 9_600 + or_ssp_spousal_diversion_difference: 100 + output: + or_ssp: 294 + +- name: Case 19, spousal diversion above standard. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: + 2026: OR + age: + 2026: 65 + or_ssp_category: SPOUSAL_DIVERSION + ssi: 0 + social_security_retirement: 14_400 + or_ssp_spousal_diversion_difference: 100 + output: + or_ssp: 100 + +- name: Case 20, temporary absence uses income available after care liability. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: + 2026: OR + age: + 2026: 65 + or_ssp_category: ROOM_BOARD_ABSENCE + # $1,200 a month, so no SSI. + social_security_retirement: 14_400 + # 1,200 less the $81.28 nursing facility personal needs allowance + # (OEP-PT-25-045; OAR 461-155-0250(4)(b)). + or_ssp_care_liability: 1_118.72 + output: + # 773 - (1,200 - 1,118.72) = 691.72. + or_ssp: 691.72 + +- name: Case 21, incapacity payment uses accessible income. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: + 2026: OR + age: + 2026: 65 + or_ssp_category: ROOM_BOARD_INCAPACITY + # $1,200 a month of Social Security that cannot be accessed. + social_security_retirement: 14_400 + or_ssp_inaccessible_income: 1_200 + output: + # SSI is 0, and the inaccessible income is not countable. + or_ssp_countable_income: 0 + or_ssp: 773 + +- name: Case 22, approved initial placement room and board. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: + 2026: OR + age: + 2026: 65 + or_ssp_category: INITIAL_ROOM_BOARD + or_ssp_approved_initial_room_board: 500 + output: + or_ssp: 500 + +- name: Case 23, initial placement payment capped at published amount. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: + 2026: OR + age: + 2026: 65 + or_ssp_category: INITIAL_ROOM_BOARD + or_ssp_approved_initial_room_board: 900 + output: + or_ssp: 773 + +- name: Case 24, nonresident. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: + 2026: WA + age: + 2026: 65 + or_ssp_category: IN_HOME + output: + or_ssp: 0 + +- name: Case 25, reported SSI receipt qualifies for the in-home supplement when modeled SSI is zero. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: + 2026: OR + age: + 2026: 65 + or_ssp_category: IN_HOME + ssi: 0 + receives_ssi: true + output: + # Reported receipt meets the SSI condition; countable income 0 equals SSI 0. + or_ssp: 22 + +- name: Case 26, reported SSI receipt is not a suspended SSI payment. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: + 2026: OR + age: + 2026: 65 + or_ssp_category: SUSPENDED_SSI + ssi: 0 + receives_ssi: true + output: + # SSI is reported as received, so it is not suspended and the 773 + # room and board allowance (Case 13) does not apply. + or_ssp: 0 + +- name: Case 27, unmarried community care recipient gets no couple payment. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: + 2026: OR + age: + 2026: 65 + or_ssp_category: COMMUNITY_CARE + output: + # OAR 461-155-0630(3) covers only spouses who each receive SSI. + ssi: 994 + or_ssp: 0 + +- name: Case 28, spouses paid SSI as individuals get no couple payment. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 65 + or_ssp_category: COMMUNITY_CARE + person2: + age: 65 + or_ssp_category: COMMUNITY_CARE + marital_units: + unit1: + members: [person1] + unit2: + members: [person2] + tax_units: + unit1: + members: [person1] + unit2: + members: [person2] + households: + household1: + members: [person1] + state_code: OR + household2: + members: [person2] + state_code: OR + output: + # Living apart, each receives the individual rate, so countable income + # equals the 994 standard. + ssi: [994, 994] + or_ssp: [0, 0] + +- name: Case 29, nursing facility resident on SSI receives the personal incidentals payment. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: + 2026: OR + age: + 2026: 80 + ssi_lives_in_medical_treatment_facility: true + ssi_medicaid_pays_majority_of_care: true + output: + ssi: 30 + # OAR 461-155-0250(4)(c): 79.07 - 30 = 49.07. + or_ssp: 49.07 + +- name: Case 30, nursing facility resident with Social Security above the payment standard. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: + 2026: OR + age: + 2026: 80 + ssi_lives_in_medical_treatment_facility: true + ssi_medicaid_pays_majority_of_care: true + # $1,200 a month. + social_security_retirement: 14_400 + output: + ssi: 0 + or_ssp: 0 + +- name: Case 31, nursing facility resident with a reduced $90 VA pension. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: + 2026: OR + age: + 2026: 80 + ssi_lives_in_medical_treatment_facility: true + ssi_medicaid_pays_majority_of_care: true + # OAR 461-155-0250(4)(a): $90 a month. + veterans_benefits: 1_080 + # POMS SI 00830.308 B: the reduced $90 pension is not SSI income. + ssi: 30 + output: + # 461-155-0250(4)(c) covers only (4)(b) residents, and countable income + # 30 + 90 = 120 exceeds 79.07, so 0. + or_ssp_countable_income: 120 + or_ssp: 0 + +- name: Case 32, partial-month days for one resident leave another resident's full month. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 65 + or_ssp_category: SSI_RESTORATION + ssi: 30 + or_ssp_partial_month_days: 16 + person2: + age: 65 + or_ssp_category: SSI_RESTORATION + ssi: 30 + marital_units: + unit1: + members: [person1] + unit2: + members: [person2] + tax_units: + unit1: + members: [person1] + unit2: + members: [person2] + households: + household: + members: [person1, person2] + state_code: OR + output: + # 964 * 16 / 31 = 497.55 and the full 964. + or_ssp: [497.5483870967742, 964] + +- name: Case 33, care liability for one absent resident leaves another resident's income. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 65 + or_ssp_category: ROOM_BOARD_ABSENCE + social_security_retirement: 14_400 + or_ssp_care_liability: 1_118.72 + person2: + age: 65 + or_ssp_category: ROOM_BOARD_ABSENCE + social_security_retirement: 14_400 + marital_units: + unit1: + members: [person1] + unit2: + members: [person2] + tax_units: + unit1: + members: [person1] + unit2: + members: [person2] + households: + household: + members: [person1, person2] + state_code: OR + output: + # person1: 773 - (1,200 - 1,118.72) = 691.72. person2 has no liability, + # and 773 - 1,200 < 0. + or_ssp: [691.72, 0] + +- name: Case 34, partial-month days entered for one month leave the next month full. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: OR + age: 65 + or_ssp_category: IN_HOME + or_ssp_partial_month_days: + 2026-01: 16 + output: + or_ssp_partial_month_days: + 2026-01: 16 + 2026-02: 0 + # January: 22 * 16 / 31 = 11.35; February is a full month: 22. + or_ssp: + 2026-01: 11.354838709677419 + 2026-02: 22 + +- name: Case 35, salary-reduction health premiums are not earned income. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: + 2026: OR + age: + 2026: 40 + or_ssp_category: DISABILITY_INTERIM + # Reviewer's example: $1,000 a month of wages, $100 of it paid as + # health premiums through a cafeteria plan by salary reduction. + employment_income: 12_000 + pre_tax_health_insurance_premiums: 1_200 + output: + ssi: 0 + # OAR 461-145-0120(5)(b) counts only cafeteria plan benefits taken as + # cash: 1,000 - 100 = 900, and 994 - 900 = 94. + or_ssp_countable_income: 900 + or_ssp: 94 + +- name: Case 36, a self-employment loss offsets wages. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: + 2026: OR + age: + 2026: 40 + or_ssp_category: DISABILITY_INTERIM + # OPEN (DHS 2818) Example 1: $650 a month of wages and a documented + # $1,000 net loss last year from a side business. + employment_income: 7_800 + self_employment_income: -1_000 + output: + ssi: 0 + # OAR 461-150-0095(2): 650 - 1,000 / 12 = 566.67, and 994 - 566.67 = 427.33. + or_ssp_countable_income: 566.67 + or_ssp: 427.33 + +- name: Case 37, a self-employment loss does not offset unearned income. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: + 2026: OR + age: + 2026: 40 + or_ssp_category: DISABILITY_INTERIM + # A documented $1,000 net loss last year and no wages. + self_employment_income: -1_000 + # VA compensation for a 10% rating, $180.42 a month from December 2025. + veterans_benefits: 2_165.04 + output: + ssi: 0 + # OAR 461-150-0095(2) excludes the loss only from other earned income, + # so earned income is 0 and countable income is 180.42: + # 994 - 180.42 = 813.58. + or_ssp_countable_income: 180.42 + or_ssp: 813.58 diff --git a/policyengine_us/variables/gov/states/or/odhs/ssp/or_ssp.py b/policyengine_us/variables/gov/states/or/odhs/ssp/or_ssp.py new file mode 100644 index 00000000000..80da0a570b1 --- /dev/null +++ b/policyengine_us/variables/gov/states/or/odhs/ssp/or_ssp.py @@ -0,0 +1,107 @@ +from policyengine_us.model_api import * + + +class or_ssp(Variable): + value_type = float + entity = Person + label = "Oregon State Supplemental Payment" + definition_period = MONTH + unit = USD + reference = ( + # ORS 411.704(1)(b), 411.706(1) + "https://www.oregonlegislature.gov/bills_laws/ors/ors411.html", + # PDF pages 1-2 + "https://ch461rules.odhs.oregon.gov/rules/461-155-0250.pdf#page=1", + "https://ch461rules.odhs.oregon.gov/rules/461-155-0575.pdf", + # PDF pages 1-3 + "https://ch461rules.odhs.oregon.gov/rules/461-155-0630.pdf#page=1", + # PDF pages 1-3 + "https://ch461rules.odhs.oregon.gov/rules/461-155-0700.pdf#page=1", + "https://ch461rules.odhs.oregon.gov/rules/461-160-0070.pdf", + # 461-101-0010(17) defines OSIP as these special need payments. + "https://ch461rules.odhs.oregon.gov/rules/461-101-0010.pdf#page=3", + # DHS 2818 (10/2026) + "https://sharedsystems.dhsoha.state.or.us/DHSForms/Served/de2818.pdf#page=583", + ) + defined_for = StateCode.OR + + def formula(person, period, parameters): + p = parameters(period).gov.states["or"].odhs.ssp + federal = parameters(period).gov.ssa.ssi.amount + # The OSIPM one-person adjusted income standard in OAR 461-155-0250(3) + # currently equals the SSI federal benefit rate ($994 in 2026). + standard = federal.individual + category = person("or_ssp_category", period) + c = category.possible_values + income = person("or_ssp_countable_income", period) + ssi = person("ssi", period) + receives_ssi = (ssi > 0) | person("receives_ssi", period) + in_home = (category == c.IN_HOME) & receives_ssi & (income == ssi) + # Community care and SSI restoration amounts depend on the SSI payment, + # so those paths require modeled SSI rather than reported receipt. + care = (category == c.COMMUNITY_CARE) & (ssi > 0) + couple = person.marital_unit.sum(care) == 2 + own_excess = max_(income - standard, 0) + spouse_excess = person.marital_unit.sum(own_excess) - own_excess + couple_payment = max_(standard - income - spouse_excess, 0) + interim = (category == c.DISABILITY_INTERIM) | (category == c.NATURALIZATION) + restoring_ssi = ( + (category == c.SSI_RESTORATION) + & (ssi > 0) + & (ssi <= federal.medical_facility) + ) + veterans = person("veterans_benefits", period) + restoring_va = ( + (category == c.VA_RESTORATION) + & (veterans > 0) + & (veterans <= p.reduced_va_pension) + ) + suspended = (category == c.SUSPENDED_SSI) & ~receives_ssi + diversion = max_(person("or_ssp_spousal_diversion_difference", period), 0) + absence = category == c.ROOM_BOARD_ABSENCE + incapacity = category == c.ROOM_BOARD_INCAPACITY + # 461-155-0630(2)(b)(A)(iv): a temporary absence uses the income left + # after contributing to the care liability. + liability = max_(person("or_ssp_care_liability", period), 0) + room_board_income = where(absence, max_(income - liability, 0), income) + # 461-155-0250(4)(c): an OSIPM nursing facility or ICF-ID resident + # receives the payment standard minus countable income (including + # SSI). A $90 VA pension already exceeds it. + # NOTE: SSI receipt stands in for OSIPM eligibility, so residents + # without SSI whose countable income is $50 to $79.07 are not modeled. + # The SSI medical treatment facility arrangement also includes + # hospitals, and OSIPM-EPD residents are not excluded. A temporary + # absence from community-based care pays only the room and board + # payment. + arrangement = person("ssi_federal_living_arrangement", period) + nursing_facility = ( + arrangement == arrangement.possible_values.MEDICAL_TREATMENT_FACILITY + ) & receives_ssi + amount = select( + [ + in_home, + care & couple, + interim | restoring_ssi | restoring_va, + suspended | absence | incapacity, + category == c.SPOUSAL_DIVERSION, + nursing_facility, + ], + [ + p.in_home_amount, + couple_payment, + max_(standard - income, 0), + max_(p.room_and_board - room_board_income, 0), + diversion + max_(standard - income, 0), + max_(p.nursing_facility_payment_standard - income, 0), + ], + default=0, + ) + days = person("or_ssp_partial_month_days", period) + partial_month = (days > 0) & (days < period.days) + # 461-160-0070 uses actual calendar days and does not round the result. + prorated = where(partial_month, amount * days / period.days, amount) + initial = min_( + max_(person("or_ssp_approved_initial_room_board", period), 0), + p.room_and_board, + ) + return where(category == c.INITIAL_ROOM_BOARD, initial, prorated) diff --git a/policyengine_us/variables/gov/states/or/odhs/ssp/or_ssp_approved_initial_room_board.py b/policyengine_us/variables/gov/states/or/odhs/ssp/or_ssp_approved_initial_room_board.py new file mode 100644 index 00000000000..ce881a5ad7d --- /dev/null +++ b/policyengine_us/variables/gov/states/or/odhs/ssp/or_ssp_approved_initial_room_board.py @@ -0,0 +1,11 @@ +from policyengine_us.model_api import * + + +class or_ssp_approved_initial_room_board(Variable): + value_type = float + entity = Person + label = "Oregon approved initial placement room and board payment" + definition_period = MONTH + unit = USD + reference = ("https://ch461rules.odhs.oregon.gov/rules/461-155-0630.pdf#page=1",) + documentation = "Approved payment for the covered initial placement period after the state assesses the unmet need; zero unless provided." diff --git a/policyengine_us/variables/gov/states/or/odhs/ssp/or_ssp_care_liability.py b/policyengine_us/variables/gov/states/or/odhs/ssp/or_ssp_care_liability.py new file mode 100644 index 00000000000..4251b0f0f9f --- /dev/null +++ b/policyengine_us/variables/gov/states/or/odhs/ssp/or_ssp_care_liability.py @@ -0,0 +1,16 @@ +from policyengine_us.model_api import * + + +class or_ssp_care_liability(Variable): + value_type = float + entity = Person + label = "Oregon SSP care liability during a temporary absence" + definition_period = MONTH + unit = USD + defined_for = StateCode.OR + reference = ( + "https://ch461rules.odhs.oregon.gov/rules/461-155-0630.pdf#page=2", + # PDF pages 1, 3-4 + "https://ch461rules.odhs.oregon.gov/rules/461-160-0620.pdf#page=1", + ) + documentation = "Liability owed under OAR 461-160-0620 during an approved temporary absence from a community-based care facility. Leave at 0 when none is owed." diff --git a/policyengine_us/variables/gov/states/or/odhs/ssp/or_ssp_category.py b/policyengine_us/variables/gov/states/or/odhs/ssp/or_ssp_category.py new file mode 100644 index 00000000000..609ce24a42a --- /dev/null +++ b/policyengine_us/variables/gov/states/or/odhs/ssp/or_ssp_category.py @@ -0,0 +1,46 @@ +from policyengine_us.model_api import * + + +class ORSSPCategory(Enum): + IN_HOME = "Approved SSI-only in-home services" + COMMUNITY_CARE = "Approved licensed community care with an SSI spouse" + DISABILITY_INTERIM = ( + "State disability determination while pursuing SSI through final appeal" + ) + SSI_RESTORATION = "Leaving nursing care and pursuing full SSI restoration" + VA_RESTORATION = "Leaving nursing care and pursuing full VA pension restoration" + NATURALIZATION = "Qualified noncitizen pursuing naturalization after SSI time limit" + SUSPENDED_SSI = ( + "Leaving a public institution and pursuing suspended SSI reinstatement" + ) + SPOUSAL_DIVERSION = "Approved additional community-spouse diversion" + ROOM_BOARD_ABSENCE = "Approved temporary absence with intent to return" + ROOM_BOARD_INCAPACITY = ( + "Approved inaccessible-income case pending representative appointment" + ) + INITIAL_ROOM_BOARD = "Approved initial placement room and board" + NONE = "No approved OSIPM special need" + + +class or_ssp_category(Variable): + value_type = Enum + entity = Person + label = "Oregon SSP approved special need category" + definition_period = MONTH + possible_values = ORSSPCategory + default_value = ORSSPCategory.NONE + reference = ( + "https://ch461rules.odhs.oregon.gov/rules/461-155-0575.pdf", + # PDF pages 1-3 + "https://ch461rules.odhs.oregon.gov/rules/461-155-0630.pdf#page=1", + # PDF pages 1-3 + "https://ch461rules.odhs.oregon.gov/rules/461-155-0700.pdf#page=1", + ) + documentation = """ + Select only after OSIPM financial eligibility, placement/service authorization, + lack of other resources to meet the need, and the category's procedural + requirements are established. Includes approval of temporary-absence and + incapacity extensions. Change to NONE when authorization or required + pursuit of benefits ends. Specialized care eligibility is not inferred + from ordinary Medicaid enrollment. + """ diff --git a/policyengine_us/variables/gov/states/or/odhs/ssp/or_ssp_countable_for_hud.py b/policyengine_us/variables/gov/states/or/odhs/ssp/or_ssp_countable_for_hud.py new file mode 100644 index 00000000000..0315fd154c6 --- /dev/null +++ b/policyengine_us/variables/gov/states/or/odhs/ssp/or_ssp_countable_for_hud.py @@ -0,0 +1,22 @@ +from policyengine_us.model_api import * + + +class or_ssp_countable_for_hud(Variable): + value_type = float + entity = Person + label = "Oregon SSP countable for HUD" + definition_period = MONTH + unit = USD + defined_for = StateCode.OR + reference = ( + # 24 CFR 5.609(b)(19) + "https://www.law.cornell.edu/cfr/text/24/5.609", + # DHS 2818 (10/2026) + "https://sharedsystems.dhsoha.state.or.us/DHSForms/Served/de2818.pdf#page=583", + ) + documentation = "The Oregon SSP payment without the in-home supplement, which reimburses uncovered assistance needs of a person receiving in-home services." + + def formula(person, period, parameters): + category = person("or_ssp_category", period) + in_home = category == category.possible_values.IN_HOME + return where(in_home, 0, person("or_ssp", period)) diff --git a/policyengine_us/variables/gov/states/or/odhs/ssp/or_ssp_countable_income.py b/policyengine_us/variables/gov/states/or/odhs/ssp/or_ssp_countable_income.py new file mode 100644 index 00000000000..6464ec88edd --- /dev/null +++ b/policyengine_us/variables/gov/states/or/odhs/ssp/or_ssp_countable_income.py @@ -0,0 +1,47 @@ +from policyengine_us.model_api import * + + +class or_ssp_countable_income(Variable): + value_type = float + entity = Person + label = "Oregon SSP countable income before deductions" + definition_period = MONTH + unit = USD + reference = ( + # PDF pages 1-2, 5 + "https://ch461rules.odhs.oregon.gov/rules/461-001-0000.pdf#page=1", + "https://ch461rules.odhs.oregon.gov/rules/461-145-0030.pdf#page=2", + "https://ch461rules.odhs.oregon.gov/rules/461-145-0108.pdf#page=1", + # PDF pages 2, 4 + "https://ch461rules.odhs.oregon.gov/rules/461-140-0040.pdf#page=2", + # Effective 10-01-26; the 7-01-18 version in the history applies before. + "https://ch461rules.odhs.oregon.gov/rules/461-145-0120.pdf#page=1", + "https://ch461rules.odhs.oregon.gov/historical/461-145-0120_history.pdf#page=1", + "https://ch461rules.odhs.oregon.gov/rules/461-145-0130.pdf#page=2", + "https://ch461rules.odhs.oregon.gov/rules/461-150-0095.pdf#page=1", + ) + documentation = "Includes SSI, earned income as SSI counts it and countable unearned cash income, before the general or earnings deductions. Earned income excludes salary-reduction health premiums, and a reported self-employment loss offsets only the person's other earned income. Investment interest and dividends and income inaccessible due to incapacity are excluded. Other state exclusions and countable loan interest are not modeled." + + def formula(person, period, parameters): + p = parameters(period).gov + excluded = p.states["or"].odhs.ssp.excluded_income_sources + # OAR 461-145-0120(5)(b) counts cafeteria plan benefits only when taken + # as cash, and 461-150-0095(2) excludes a documented self-employment + # loss "only from other earned income". ssi_earned_income applies both, + # as SSI does, and the floor keeps a loss off unearned income. + # NOTE: 461-140-0040(4) counts income "before deductions, such as + # garnishments, taxes, or other payroll deductions", but only "Unless + # exclusions under another rule in chapter 461 apply"; salary-reduction + # health premiums follow 0120(5)(b). + # NOTE: A loss offsets only the person's own earned income, while + # 0095(2) also applies it to other earned income of the financial + # group, such as a spouse's wages. + earned = max_(person("ssi_earned_income", period), 0) + income = person("ssi", period) + earned + for name in p.ssa.ssi.income.sources.unearned: + if name not in excluded: + income = income + max_(person(name, period), 0) + # OAR 461-140-0040(5)(e)(D): income inaccessible due to incapacity is + # unavailable, so it is not countable. + inaccessible = max_(person("or_ssp_inaccessible_income", period), 0) + return max_(income - inaccessible, 0) diff --git a/policyengine_us/variables/gov/states/or/odhs/ssp/or_ssp_inaccessible_income.py b/policyengine_us/variables/gov/states/or/odhs/ssp/or_ssp_inaccessible_income.py new file mode 100644 index 00000000000..c17e05cd661 --- /dev/null +++ b/policyengine_us/variables/gov/states/or/odhs/ssp/or_ssp_inaccessible_income.py @@ -0,0 +1,16 @@ +from policyengine_us.model_api import * + + +class or_ssp_inaccessible_income(Variable): + value_type = float + entity = Person + label = "Oregon SSP income inaccessible due to incapacity" + definition_period = MONTH + unit = USD + defined_for = StateCode.OR + reference = ( + "https://ch461rules.odhs.oregon.gov/rules/461-140-0040.pdf#page=4", + # PDF pages 2-3 + "https://ch461rules.odhs.oregon.gov/rules/461-155-0630.pdf#page=2", + ) + documentation = "Income that an individual determined eligible for community-based care or nursing facility services cannot access due to incapacity while a legal or financial representative is being established, approved as unavailable for up to three months under OAR 461-140-0040(5)(e)(D). Leave at 0 otherwise, including for in-home services." diff --git a/policyengine_us/variables/gov/states/or/odhs/ssp/or_ssp_partial_month_days.py b/policyengine_us/variables/gov/states/or/odhs/ssp/or_ssp_partial_month_days.py new file mode 100644 index 00000000000..9c7c3ddfb31 --- /dev/null +++ b/policyengine_us/variables/gov/states/or/odhs/ssp/or_ssp_partial_month_days.py @@ -0,0 +1,18 @@ +from policyengine_us.model_api import * + + +class or_ssp_partial_month_days(Variable): + value_type = int + entity = Person + label = "Oregon SSP eligible days in a partial month" + definition_period = MONTH + defined_for = StateCode.OR + reference = ( + "https://ch461rules.odhs.oregon.gov/rules/461-160-0070.pdf", + "https://ch461rules.odhs.oregon.gov/rules/461-155-0700.pdf#page=3", + ) + documentation = "Days of eligibility in a month of less than full eligibility. Leave at 0 for a full month. Enter it for the month, since a yearly value applies to every month. An approved initial room-and-board payment is already the amount for the covered period." + + def formula(person, period, parameters): + # A formula stops a one-month input from carrying into later months. + return 0 diff --git a/policyengine_us/variables/gov/states/or/odhs/ssp/or_ssp_spousal_diversion_difference.py b/policyengine_us/variables/gov/states/or/odhs/ssp/or_ssp_spousal_diversion_difference.py new file mode 100644 index 00000000000..17be97ab0df --- /dev/null +++ b/policyengine_us/variables/gov/states/or/odhs/ssp/or_ssp_spousal_diversion_difference.py @@ -0,0 +1,11 @@ +from policyengine_us.model_api import * + + +class or_ssp_spousal_diversion_difference(Variable): + value_type = float + entity = Person + label = "Oregon additional diversion available under nursing facility placement" + definition_period = MONTH + unit = USD + reference = ("https://ch461rules.odhs.oregon.gov/rules/461-155-0700.pdf#page=2",) + documentation = "State-calculated excess of permitted nursing-facility diversion over community-care diversion, using 461-160-0620 liability rules. Requires an approved request under 461-155-0700(1)(d)." diff --git a/policyengine_us/variables/household/income/spm_unit/spm_unit_benefits.py b/policyengine_us/variables/household/income/spm_unit/spm_unit_benefits.py index 4613c4da26a..70fc3c460e1 100644 --- a/policyengine_us/variables/household/income/spm_unit/spm_unit_benefits.py +++ b/policyengine_us/variables/household/income/spm_unit/spm_unit_benefits.py @@ -25,6 +25,7 @@ def formula(spm_unit, period, parameters): "nj_ssp", # New Jersey benefits "pa_ssp", # Pennsylvania benefits "wy_ssp", # Wyoming benefits + "or_ssp", # Oregon benefits "nc_sa", # North Carolina benefits "mt_ssp", # Montana benefits "ok_ssp", # Oklahoma benefits