diff --git a/changelog.d/history-ny-heap-income-tables.fixed.md b/changelog.d/history-ny-heap-income-tables.fixed.md new file mode 100644 index 00000000000..fc8c891e127 --- /dev/null +++ b/changelog.d/history-ny-heap-income-tables.fixed.md @@ -0,0 +1 @@ +Restore published New York HEAP income eligibility and Tier I thresholds at listed household sizes for FY2017–FY2023. diff --git a/changelog.d/history-ny-heap-regular-schedules.fixed.md b/changelog.d/history-ny-heap-regular-schedules.fixed.md new file mode 100644 index 00000000000..59bb2f6ab4e --- /dev/null +++ b/changelog.d/history-ny-heap-regular-schedules.fixed.md @@ -0,0 +1 @@ +Restore New York HEAP regular heating benefit schedules for FY2017–FY2023, including the December 2022 base-benefit increase, and preserve the inclusive Tier I income boundary when floating-point multiplication reaches an exact monthly dollar. diff --git a/policyengine_us/parameters/gov/states/ny/otda/heap/eligibility/historical_income_limits/last_published_household_size.yaml b/policyengine_us/parameters/gov/states/ny/otda/heap/eligibility/historical_income_limits/last_published_household_size.yaml new file mode 100644 index 00000000000..26a9b06e3ad --- /dev/null +++ b/policyengine_us/parameters/gov/states/ny/otda/heap/eligibility/historical_income_limits/last_published_household_size.yaml @@ -0,0 +1,17 @@ +description: New York publishes historical HEAP income-table rows through this household size. This + marks the verified table coverage, not an eligibility limit. +values: + 2016-10-01: 11 + 2020-10-01: 12 + 2021-10-01: 13 +metadata: + unit: /1 + period: year + label: New York HEAP last published historical income-table household size + reference: + - title: FY2017 HEAP Desk Guide + href: https://otda.ny.gov/policy/directives/2016/LCM/16-LCM-16-Attachment-1.pdf#page=1 + - title: FY2021 HEAP Desk Guide + href: https://otda.ny.gov/policy/directives/2020/LCM/20-LCM-11-Attachment-4.pdf#page=1 + - title: FY2022 HEAP Desk Guide + href: https://liheapch.acf.gov/docs/NY_BenefitMatrix_2022.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/ny/otda/heap/eligibility/historical_income_limits/maximum.yaml b/policyengine_us/parameters/gov/states/ny/otda/heap/eligibility/historical_income_limits/maximum.yaml new file mode 100644 index 00000000000..db07684177e --- /dev/null +++ b/policyengine_us/parameters/gov/states/ny/otda/heap/eligibility/historical_income_limits/maximum.yaml @@ -0,0 +1,148 @@ +# October dates identify FFY schedules under the annual model convention. +# Use only through last_published_household_size; larger households retain +# the existing generic calculation. These rows do not impose a size cap. +description: New York uses these published historical overall monthly income limits by HEAP household + size. +metadata: + type: single_amount + threshold_unit: /1 + amount_unit: currency-USD + period: month + label: New York HEAP historical overall monthly income limit + reference: + - title: FY2017 HEAP Desk Guide + href: https://otda.ny.gov/policy/directives/2016/LCM/16-LCM-16-Attachment-1.pdf#page=1 + - title: FY2018 HEAP Desk Guide + href: https://otda.ny.gov/policy/directives/2017/LCM/17-LCM-14-Attachment-2.pdf#page=1 + - title: FY2019 HEAP Desk Guide + href: https://otda.ny.gov/policy/directives/2018/LCM/18-LCM-20-Attachment-4.pdf#page=1 + - title: FY2020 HEAP Desk Guide + href: https://otda.ny.gov/policy/directives/2019/LCM/19-LCM-10-Attachment-4.pdf#page=1 + - title: FY2021 HEAP Desk Guide + href: https://otda.ny.gov/policy/directives/2020/LCM/20-LCM-11-Attachment-4.pdf#page=1 + - title: FY2022 HEAP Desk Guide + href: https://liheapch.acf.gov/docs/NY_BenefitMatrix_2022.pdf#page=1 + - title: FY2023 HEAP Desk Guide + href: https://otda.ny.gov/policy/directives/2022/LCM/22-LCM-12-Attachment-4.pdf#page=1 +brackets: +- threshold: + 2016-10-01: 1 + amount: + 2016-10-01: 2300 + 2017-10-01: 2318 + 2018-10-01: 2391 + 2019-10-01: 2494 + 2020-10-01: 2610 + 2021-10-01: 2729 + 2022-10-01: 2852 +- threshold: + 2016-10-01: 2 + amount: + 2016-10-01: 3007 + 2017-10-01: 3031 + 2018-10-01: 3127 + 2019-10-01: 3262 + 2020-10-01: 3413 + 2021-10-01: 3569 + 2022-10-01: 3730 +- threshold: + 2016-10-01: 3 + amount: + 2016-10-01: 3715 + 2017-10-01: 3744 + 2018-10-01: 3863 + 2019-10-01: 4030 + 2020-10-01: 4216 + 2021-10-01: 4409 + 2022-10-01: 4608 +- threshold: + 2016-10-01: 4 + amount: + 2016-10-01: 4423 + 2017-10-01: 4457 + 2018-10-01: 4598 + 2019-10-01: 4797 + 2020-10-01: 5019 + 2021-10-01: 5249 + 2022-10-01: 5485 +- threshold: + 2016-10-01: 5 + amount: + 2016-10-01: 5130 + 2017-10-01: 5170 + 2018-10-01: 5334 + 2019-10-01: 5565 + 2020-10-01: 5822 + 2021-10-01: 6088 + 2022-10-01: 6363 +- threshold: + 2016-10-01: 6 + amount: + 2016-10-01: 5838 + 2017-10-01: 5883 + 2018-10-01: 6070 + 2019-10-01: 6332 + 2020-10-01: 6625 + 2021-10-01: 6928 + 2022-10-01: 7241 +- threshold: + 2016-10-01: 7 + amount: + 2016-10-01: 5971 + 2017-10-01: 6017 + 2018-10-01: 6208 + 2019-10-01: 6476 + 2020-10-01: 6775 + 2021-10-01: 7086 + 2022-10-01: 7405 +- threshold: + 2016-10-01: 8 + amount: + 2016-10-01: 6103 + 2017-10-01: 6150 + 2018-10-01: 6346 + 2019-10-01: 6620 + 2020-10-01: 6926 + 2021-10-01: 7243 + 2022-10-01: 7570 +- threshold: + 2016-10-01: 9 + amount: + 2016-10-01: 6236 + 2017-10-01: 6284 + 2018-10-01: 6483 + 2019-10-01: 6764 + 2020-10-01: 7077 + 2021-10-01: 7401 + 2022-10-01: 7734 +- threshold: + 2016-10-01: 10 + amount: + 2016-10-01: 6369 + 2017-10-01: 6418 + 2018-10-01: 6621 + 2019-10-01: 6908 + 2020-10-01: 7227 + 2021-10-01: 7558 + 2022-10-01: 7899 +- threshold: + 2016-10-01: 11 + amount: + 2016-10-01: 6671 + 2017-10-01: 6733 + 2018-10-01: 6918 + 2019-10-01: 7086 + 2020-10-01: 7378 + 2021-10-01: 7715 + 2022-10-01: 8064 +- threshold: + 2016-10-01: 12 + amount: + 2020-10-01: 7755 + 2021-10-01: 7873 + 2022-10-01: 8228 +- threshold: + 2016-10-01: 13 + amount: + 2021-10-01: 8420 + 2022-10-01: 8778 diff --git a/policyengine_us/parameters/gov/states/ny/otda/heap/eligibility/historical_income_limits/tier_one.yaml b/policyengine_us/parameters/gov/states/ny/otda/heap/eligibility/historical_income_limits/tier_one.yaml new file mode 100644 index 00000000000..38fae38583f --- /dev/null +++ b/policyengine_us/parameters/gov/states/ny/otda/heap/eligibility/historical_income_limits/tier_one.yaml @@ -0,0 +1,148 @@ +# October dates identify FFY schedules under the annual model convention. +# Use only through last_published_household_size; larger households retain +# the existing generic calculation. These rows do not impose a size cap. +description: New York uses these published historical Tier I monthly income limits by HEAP household + size. +metadata: + type: single_amount + threshold_unit: /1 + amount_unit: currency-USD + period: month + label: New York HEAP historical Tier I monthly income limit + reference: + - title: FY2017 HEAP Desk Guide + href: https://otda.ny.gov/policy/directives/2016/LCM/16-LCM-16-Attachment-1.pdf#page=1 + - title: FY2018 HEAP Desk Guide + href: https://otda.ny.gov/policy/directives/2017/LCM/17-LCM-14-Attachment-2.pdf#page=1 + - title: FY2019 HEAP Desk Guide + href: https://otda.ny.gov/policy/directives/2018/LCM/18-LCM-20-Attachment-4.pdf#page=1 + - title: FY2020 HEAP Desk Guide + href: https://otda.ny.gov/policy/directives/2019/LCM/19-LCM-10-Attachment-4.pdf#page=1 + - title: FY2021 HEAP Desk Guide + href: https://otda.ny.gov/policy/directives/2020/LCM/20-LCM-11-Attachment-4.pdf#page=1 + - title: FY2022 HEAP Desk Guide + href: https://liheapch.acf.gov/docs/NY_BenefitMatrix_2022.pdf#page=1 + - title: FY2023 HEAP Desk Guide + href: https://otda.ny.gov/policy/directives/2022/LCM/22-LCM-12-Attachment-4.pdf#page=1 +brackets: +- threshold: + 2016-10-01: 1 + amount: + 2016-10-01: 1287 + 2017-10-01: 1307 + 2018-10-01: 1315 + 2019-10-01: 1353 + 2020-10-01: 1382 + 2021-10-01: 1395 + 2022-10-01: 1472 +- threshold: + 2016-10-01: 2 + amount: + 2016-10-01: 1736 + 2017-10-01: 1759 + 2018-10-01: 1783 + 2019-10-01: 1832 + 2020-10-01: 1868 + 2021-10-01: 1887 + 2022-10-01: 1983 +- threshold: + 2016-10-01: 3 + amount: + 2016-10-01: 2184 + 2017-10-01: 2212 + 2018-10-01: 2251 + 2019-10-01: 2311 + 2020-10-01: 2353 + 2021-10-01: 2379 + 2022-10-01: 2494 +- threshold: + 2016-10-01: 4 + amount: + 2016-10-01: 2633 + 2017-10-01: 2665 + 2018-10-01: 2719 + 2019-10-01: 2790 + 2020-10-01: 2838 + 2021-10-01: 2871 + 2022-10-01: 3006 +- threshold: + 2016-10-01: 5 + amount: + 2016-10-01: 3081 + 2017-10-01: 3118 + 2018-10-01: 3187 + 2019-10-01: 3268 + 2020-10-01: 3324 + 2021-10-01: 3363 + 2022-10-01: 3517 +- threshold: + 2016-10-01: 6 + amount: + 2016-10-01: 3530 + 2017-10-01: 3571 + 2018-10-01: 3655 + 2019-10-01: 3747 + 2020-10-01: 3809 + 2021-10-01: 3855 + 2022-10-01: 4028 +- threshold: + 2016-10-01: 7 + amount: + 2016-10-01: 3979 + 2017-10-01: 4024 + 2018-10-01: 4123 + 2019-10-01: 4226 + 2020-10-01: 4294 + 2021-10-01: 4346 + 2022-10-01: 4540 +- threshold: + 2016-10-01: 8 + amount: + 2016-10-01: 4430 + 2017-10-01: 4476 + 2018-10-01: 4591 + 2019-10-01: 4705 + 2020-10-01: 4780 + 2021-10-01: 4838 + 2022-10-01: 5051 +- threshold: + 2016-10-01: 9 + amount: + 2016-10-01: 4880 + 2017-10-01: 4929 + 2018-10-01: 5059 + 2019-10-01: 5184 + 2020-10-01: 5265 + 2021-10-01: 5330 + 2022-10-01: 5562 +- threshold: + 2016-10-01: 10 + amount: + 2016-10-01: 5331 + 2017-10-01: 5382 + 2018-10-01: 5527 + 2019-10-01: 5663 + 2020-10-01: 5750 + 2021-10-01: 5822 + 2022-10-01: 6074 +- threshold: + 2016-10-01: 11 + amount: + 2016-10-01: 5782 + 2017-10-01: 5835 + 2018-10-01: 5995 + 2019-10-01: 6141 + 2020-10-01: 6236 + 2021-10-01: 6314 + 2022-10-01: 6585 +- threshold: + 2016-10-01: 12 + amount: + 2020-10-01: 6721 + 2021-10-01: 6806 + 2022-10-01: 7096 +- threshold: + 2016-10-01: 13 + amount: + 2021-10-01: 7297 + 2022-10-01: 7608 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ny/otda/heap/eligibility/ny_heap_income_limit.yaml b/policyengine_us/tests/policy/baseline/gov/states/ny/otda/heap/eligibility/ny_heap_income_limit.yaml index d874a870cc5..067c3bc556a 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ny/otda/heap/eligibility/ny_heap_income_limit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ny/otda/heap/eligibility/ny_heap_income_limit.yaml @@ -58,3 +58,68 @@ output: # 2023-2024 Tier II maximums 3,035; 5,838; 9,532, each x 12. ny_heap_income_limit: [36_420, 70_056, 114_384] + +# First N values in each case are the original desk-guide monthly limits x12. +# The final N+1 value is a compatibility control of the preexisting generic +# calculation, NOT verified historical legal coverage. No size is denied solely +# because the published table ends. The FY2016 case is likewise compatibility. +# Exact source rows and temporal bounds are documented in the parameter files. +- name: Case 5, FY2017 all published income-limit rows and the first generic fallback size. + period: 2017 + input: + state_code: [NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY] + ny_heap_household_size: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] + output: + # The size-12 compatibility fallback uses exact 2016 HHS FPG: + # 40,890 + 4 * 4,160 = 57,530; floor(1.5 * 57,530 / 12) * 12 = 86,292. + # This is the generic fallback, not an additional published NY table row. + ny_heap_income_limit: [27600, 36084, 44580, 53076, 61560, 70056, 71652, 73236, 74832, 76428, 80052, 86292] +- name: Case 6, FY2018 all published income-limit rows and the first generic fallback size. + period: 2018 + input: + state_code: [NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY] + ny_heap_household_size: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] + output: + ny_heap_income_limit: [27816, 36372, 44928, 53484, 62040, 70596, 72204, 73800, 75408, 77016, 80796, 87060] +- name: Case 7, FY2019 all published income-limit rows and the first generic fallback size. + period: 2019 + input: + state_code: [NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY] + ny_heap_household_size: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] + output: + ny_heap_income_limit: [28692, 37524, 46356, 55176, 64008, 72840, 74496, 76152, 77796, 79452, 83016, 89484] +- name: Case 8, FY2020 all published income-limit rows and the first generic fallback size. + period: 2020 + input: + state_code: [NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY] + ny_heap_household_size: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] + output: + ny_heap_income_limit: [29928, 39144, 48360, 57564, 66780, 75984, 77712, 79440, 81168, 82896, 85032, 91656] +- name: Case 9, FY2021 all published income-limit rows and the first generic fallback size. + period: 2021 + input: + state_code: [NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY] + ny_heap_household_size: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] + output: + ny_heap_income_limit: [31320, 40956, 50592, 60228, 69864, 79500, 81300, 83112, 84924, 86724, 88536, 93060, 99780] +- name: Case 10, FY2022 all published income-limit rows and the first generic fallback size. + period: 2022 + input: + state_code: [NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY] + ny_heap_household_size: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] + output: + ny_heap_income_limit: [32748, 42828, 52908, 62988, 73056, 83136, 85032, 86916, 88812, 90696, 92580, 94476, 101040, 107844] +- name: Case 11, FY2023 all published income-limit rows and the first generic fallback size. + period: 2023 + input: + state_code: [NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY] + ny_heap_household_size: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] + output: + ny_heap_income_limit: [34224, 44760, 55296, 65820, 76356, 86892, 88860, 90840, 92808, 94788, 96768, 98736, 105336, 112416] +- name: Case 12, pre-FY2017 generic income-limit behavior is preserved without certifying its law. + period: 2016 + input: + state_code: NY + ny_heap_household_size: 1 + output: + ny_heap_income_limit: 26928 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ny/otda/heap/ny_heap.yaml b/policyengine_us/tests/policy/baseline/gov/states/ny/otda/heap/ny_heap.yaml index 6853033e64e..67dcde29ed6 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ny/otda/heap/ny_heap.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ny/otda/heap/ny_heap.yaml @@ -1012,3 +1012,157 @@ ny_heap_eligible: [true, true] # $1,287 monthly receives 350 + 26; $1,288 receives the $350 base. ny_heap: [376, 350] + +# FY2017 desk guide: one-person monthly Tier II maximum is $2,300. +# https://otda.ny.gov/policy/directives/2016/LCM/16-LCM-16-Attachment-1.pdf#page=1 +- name: Case 45, FY2017 published maximum income includes the final monthly dollar. + period: 2017 + input: + state_code: [NY, NY] + age: [40, 40] + employment_income: [27_600, 27_612] + ssi: [0, 0] + snap: [0, 0] + tanf: [0, 0] + heating_type: [NATURAL_GAS, NATURAL_GAS] + gas_expense: [1_500, 1_500] + output: + # $2,300 monthly is included; $2,301 exceeds the published maximum. + # Both are above Tier I and age 40 adds no vulnerable supplement. + ny_heap: [350, 0] + ny_heap_income_limit: [27_600, 27_600] + ny_heap_countable_income: [27_600, 27_612] + ny_heap_categorically_eligible: [false, false] + ny_heap_tier_one: [false, false] + ny_heap_eligible: [true, false] + +# Original desk-guide income tables: historical_income_limits parameter references. +# Four one-person citizen households at/above Tier I and at/above the overall +# published monthly maximum. Annual wages are twelve constant monthly wages; +# reported receipts are zero. Eligibility and countable income are calculated. +- name: Case 46, FY2017 published Tier I and overall income boundaries from actual wages. + period: 2017 + input: + state_code: [NY, NY, NY, NY] + age: [40, 40, 40, 40] + employment_income: &id001 [15444, 15456, 27600, 27612] + ssi: [0, 0, 0, 0] + snap: [0, 0, 0, 0] + tanf: [0, 0, 0, 0] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + gas_expense: [1500, 1500, 1500, 1500] + output: + ny_heap: [376, 350, 350, 0] + ny_heap_countable_income: *id001 + ny_heap_income_limit: [27600, 27600, 27600, 27600] + ny_heap_tier_one: [true, false, false, false] + ny_heap_eligible: [true, true, true, false] + ny_heap_categorically_eligible: [false, false, false, false] +- name: Case 47, FY2018 published Tier I and overall income boundaries from actual wages. + period: 2018 + input: + state_code: [NY, NY, NY, NY] + age: [40, 40, 40, 40] + employment_income: &id002 [15684, 15696, 27816, 27828] + ssi: [0, 0, 0, 0] + snap: [0, 0, 0, 0] + tanf: [0, 0, 0, 0] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + gas_expense: [1500, 1500, 1500, 1500] + output: + ny_heap: [376, 350, 350, 0] + ny_heap_countable_income: *id002 + ny_heap_income_limit: [27816, 27816, 27816, 27816] + ny_heap_tier_one: [true, false, false, false] + ny_heap_eligible: [true, true, true, false] + ny_heap_categorically_eligible: [false, false, false, false] +- name: Case 48, FY2019 published Tier I and overall income boundaries from actual wages. + period: 2019 + input: + state_code: [NY, NY, NY, NY] + age: [40, 40, 40, 40] + employment_income: &id003 [15780, 15792, 28692, 28704] + ssi: [0, 0, 0, 0] + snap: [0, 0, 0, 0] + tanf: [0, 0, 0, 0] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + gas_expense: [1500, 1500, 1500, 1500] + output: + ny_heap: [376, 350, 350, 0] + ny_heap_countable_income: *id003 + ny_heap_income_limit: [28692, 28692, 28692, 28692] + ny_heap_tier_one: [true, false, false, false] + ny_heap_eligible: [true, true, true, false] + ny_heap_categorically_eligible: [false, false, false, false] +- name: Case 49, FY2020 published Tier I and overall income boundaries from actual wages. + period: 2020 + input: + state_code: [NY, NY, NY, NY] + age: [40, 40, 40, 40] + employment_income: &id004 [16236, 16248, 29928, 29940] + ssi: [0, 0, 0, 0] + snap: [0, 0, 0, 0] + tanf: [0, 0, 0, 0] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + gas_expense: [1500, 1500, 1500, 1500] + output: + ny_heap: [376, 350, 350, 0] + ny_heap_countable_income: *id004 + ny_heap_income_limit: [29928, 29928, 29928, 29928] + ny_heap_tier_one: [true, false, false, false] + ny_heap_eligible: [true, true, true, false] + ny_heap_categorically_eligible: [false, false, false, false] +- name: Case 50, FY2021 published Tier I and overall income boundaries from actual wages. + period: 2021 + input: + state_code: [NY, NY, NY, NY] + age: [40, 40, 40, 40] + employment_income: &id005 [16584, 16596, 31320, 31332] + ssi: [0, 0, 0, 0] + snap: [0, 0, 0, 0] + tanf: [0, 0, 0, 0] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + gas_expense: [1500, 1500, 1500, 1500] + output: + ny_heap: [381, 350, 350, 0] + ny_heap_countable_income: *id005 + ny_heap_income_limit: [31320, 31320, 31320, 31320] + ny_heap_tier_one: [true, false, false, false] + ny_heap_eligible: [true, true, true, false] + ny_heap_categorically_eligible: [false, false, false, false] +- name: Case 51, FY2022 published Tier I and overall income boundaries from actual wages. + period: 2022 + input: + state_code: [NY, NY, NY, NY] + age: [40, 40, 40, 40] + employment_income: &id006 [16740, 16752, 32748, 32760] + ssi: [0, 0, 0, 0] + snap: [0, 0, 0, 0] + tanf: [0, 0, 0, 0] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + gas_expense: [1500, 1500, 1500, 1500] + output: + ny_heap: [391, 350, 350, 0] + ny_heap_countable_income: *id006 + ny_heap_income_limit: [32748, 32748, 32748, 32748] + ny_heap_tier_one: [true, false, false, false] + ny_heap_eligible: [true, true, true, false] + ny_heap_categorically_eligible: [false, false, false, false] +- name: Case 52, FY2023 published Tier I and overall income boundaries from actual wages. + period: 2023 + input: + state_code: [NY, NY, NY, NY] + age: [40, 40, 40, 40] + employment_income: &id007 [17664, 17676, 34224, 34236] + ssi: [0, 0, 0, 0] + snap: [0, 0, 0, 0] + tanf: [0, 0, 0, 0] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + gas_expense: [1500, 1500, 1500, 1500] + output: + ny_heap: [541, 500, 500, 0] + ny_heap_countable_income: *id007 + ny_heap_income_limit: [34224, 34224, 34224, 34224] + ny_heap_tier_one: [true, false, false, false] + ny_heap_eligible: [true, true, true, false] + ny_heap_categorically_eligible: [false, false, false, false] diff --git a/policyengine_us/tests/policy/baseline/gov/states/ny/otda/heap/payment/ny_heap_tier_one.yaml b/policyengine_us/tests/policy/baseline/gov/states/ny/otda/heap/payment/ny_heap_tier_one.yaml index 715601aff23..f778069a423 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ny/otda/heap/payment/ny_heap_tier_one.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ny/otda/heap/payment/ny_heap_tier_one.yaml @@ -78,3 +78,119 @@ ny_heap_categorically_eligible: [false, false] output: ny_heap_tier_one: [true, false] + +# Supplied HEAP sizes isolate this threshold component; wages compute countable +# income and no categorical-eligibility result is supplied. For each size the +# original table maximum qualifies and one more monthly dollar does not. The +# final N+1 household keeps the generic calculation beyond published rows; its +# wages are just above the last printed Tier I row, below the larger-size line. +# The FY2016 case protects existing behavior only, not verified historical law. +- name: Case 6, FY2017 every published Tier I maximum is inclusive with generic fallback above the table. + period: 2017 + input: + state_code: [NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY] + age: [40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40] + ny_heap_household_size: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] + employment_income: [15444, 20832, 26208, 31596, 36972, 42360, 47748, 53160, 58560, 63972, 69384, 15456, 20844, 26220, + 31608, 36984, 42372, 47760, 53172, 58572, 63984, 69396, 69396] + ssi: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + snap: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + tanf: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + output: + ny_heap_tier_one: [true, true, true, true, true, true, true, true, true, true, true, false, false, false, false, false, + false, false, false, false, false, false, true] +- name: Case 7, FY2018 every published Tier I maximum is inclusive with generic fallback above the table. + period: 2018 + input: + state_code: [NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY] + age: [40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40] + ny_heap_household_size: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] + employment_income: [15684, 21108, 26544, 31980, 37416, 42852, 48288, 53712, 59148, 64584, 70020, 15696, 21120, 26556, + 31992, 37428, 42864, 48300, 53724, 59160, 64596, 70032, 70032] + ssi: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + snap: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + tanf: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + output: + ny_heap_tier_one: [true, true, true, true, true, true, true, true, true, true, true, false, false, false, false, false, + false, false, false, false, false, false, true] +- name: Case 8, FY2019 every published Tier I maximum is inclusive with generic fallback above the table. + period: 2019 + input: + state_code: [NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY] + age: [40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40] + ny_heap_household_size: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] + employment_income: [15780, 21396, 27012, 32628, 38244, 43860, 49476, 55092, 60708, 66324, 71940, 15792, 21408, 27024, + 32640, 38256, 43872, 49488, 55104, 60720, 66336, 71952, 71952] + ssi: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + snap: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + tanf: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + output: + ny_heap_tier_one: [true, true, true, true, true, true, true, true, true, true, true, false, false, false, false, false, + false, false, false, false, false, false, true] +- name: Case 9, FY2020 every published Tier I maximum is inclusive with generic fallback above the table. + period: 2020 + input: + state_code: [NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY] + age: [40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40] + ny_heap_household_size: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] + employment_income: [16236, 21984, 27732, 33480, 39216, 44964, 50712, 56460, 62208, 67956, 73692, 16248, 21996, 27744, + 33492, 39228, 44976, 50724, 56472, 62220, 67968, 73704, 73704] + ssi: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + snap: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + tanf: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + output: + ny_heap_tier_one: [true, true, true, true, true, true, true, true, true, true, true, false, false, false, false, false, + false, false, false, false, false, false, true] +- name: Case 10, FY2021 every published Tier I maximum is inclusive with generic fallback above the table. + period: 2021 + input: + state_code: [NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY] + age: [40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40] + ny_heap_household_size: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] + employment_income: [16584, 22416, 28236, 34056, 39888, 45708, 51528, 57360, 63180, 69000, 74832, 80652, 16596, 22428, + 28248, 34068, 39900, 45720, 51540, 57372, 63192, 69012, 74844, 80664, 80664] + ssi: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + snap: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + tanf: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + output: + ny_heap_tier_one: [true, true, true, true, true, true, true, true, true, true, true, true, false, false, false, false, + false, false, false, false, false, false, false, false, true] +- name: Case 11, FY2022 every published Tier I maximum is inclusive with generic fallback above the table. + period: 2022 + input: + state_code: [NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY] + age: [40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40] + ny_heap_household_size: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] + employment_income: [16740, 22644, 28548, 34452, 40356, 46260, 52152, 58056, 63960, 69864, 75768, 81672, 87564, 16752, + 22656, 28560, 34464, 40368, 46272, 52164, 58068, 63972, 69876, 75780, 81684, 87576, 87576] + ssi: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + snap: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + tanf: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + output: + ny_heap_tier_one: [true, true, true, true, true, true, true, true, true, true, true, true, true, false, false, false, + false, false, false, false, false, false, false, false, false, false, true] +- name: Case 12, FY2023 every published Tier I maximum is inclusive with generic fallback above the table. + period: 2023 + input: + state_code: [NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, NY] + age: [40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40] + ny_heap_household_size: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] + employment_income: [17664, 23796, 29928, 36072, 42204, 48336, 54480, 60612, 66744, 72888, 79020, 85152, 91296, 17676, + 23808, 29940, 36084, 42216, 48348, 54492, 60624, 66756, 72900, 79032, 85164, 91308, 91308] + ssi: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + snap: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + tanf: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + output: + ny_heap_tier_one: [true, true, true, true, true, true, true, true, true, true, true, true, true, false, false, false, + false, false, false, false, false, false, false, false, false, false, true] +- name: Case 13, pre-FY2017 generic Tier I behavior is preserved without certifying its law. + period: 2016 + input: + state_code: [NY, NY] + age: [40, 40] + employment_income: [15300, 15312] + ssi: [0, 0] + snap: [0, 0] + tanf: [0, 0] + output: + ny_heap_tier_one: [true, false] diff --git a/policyengine_us/variables/gov/states/ny/otda/heap/eligibility/ny_heap_income_limit.py b/policyengine_us/variables/gov/states/ny/otda/heap/eligibility/ny_heap_income_limit.py index edd5d385e6b..bf056d800ce 100644 --- a/policyengine_us/variables/gov/states/ny/otda/heap/eligibility/ny_heap_income_limit.py +++ b/policyengine_us/variables/gov/states/ny/otda/heap/eligibility/ny_heap_income_limit.py @@ -10,12 +10,20 @@ class ny_heap_income_limit(Variable): unit = USD defined_for = StateCode.NY reference = ( + "https://otda.ny.gov/policy/directives/2016/LCM/16-LCM-16-Attachment-1.pdf#page=1", + "https://liheapch.acf.gov/docs/NY_BenefitMatrix_2022.pdf#page=1", "https://otda.ny.gov/programs/heap/HEAP-manual.pdf#page=37", "https://liheapch.acf.gov/docs/2026/state-plans/NY_Plan_2026.pdf#page=9", "https://liheapch.acf.gov/docs/2026/benefits-matricies/NY_BenefitMatrix_2026.docx", "https://acf.gov/sites/default/files/documents/ocs/COMM_LIHEAP_IM2025-02_SMIStateTable_Att4.pdf#page=2", ) + documentation = ( + "Published monthly table rows are used for FY2017 to FY2023, annualized once. " + "Larger households retain the generic SMI/FPG calculation, which is not " + "verified historical coverage. The table boundary is not an eligibility cap." + ) + def formula(spm_unit, period, parameters): p = parameters(period).gov.states.ny.otda.heap.eligibility size = max_(spm_unit("ny_heap_household_size", period), 1) @@ -34,3 +42,16 @@ def formula(spm_unit, period, parameters): # Chapter 8 D.9(c) and the FY2026 plan control sizes 14+, where the # matrix's additional-person shortcut differs from the stated FPG rate. return max_(monthly_smi, monthly_fpg) * MONTHS_IN_YEAR + + def formula_2017_01_01(spm_unit, period, parameters): + p = parameters( + period + ).gov.states.ny.otda.heap.eligibility.historical_income_limits + size = max_(spm_unit("ny_heap_household_size", period), 1) + last_size = p.last_published_household_size + table_limit = p.maximum.calc(min_(size, last_size)) * MONTHS_IN_YEAR + generic_limit = ny_heap_income_limit.formula(spm_unit, period, parameters) + return where(size <= last_size, table_limit, generic_limit) + + def formula_2024_01_01(spm_unit, period, parameters): + return ny_heap_income_limit.formula(spm_unit, period, parameters) diff --git a/policyengine_us/variables/gov/states/ny/otda/heap/payment/ny_heap_tier_one.py b/policyengine_us/variables/gov/states/ny/otda/heap/payment/ny_heap_tier_one.py index 8d08455538c..f7acf83615d 100644 --- a/policyengine_us/variables/gov/states/ny/otda/heap/payment/ny_heap_tier_one.py +++ b/policyengine_us/variables/gov/states/ny/otda/heap/payment/ny_heap_tier_one.py @@ -8,6 +8,8 @@ class ny_heap_tier_one(Variable): label = "New York HEAP Tier I status" defined_for = StateCode.NY reference = ( + "https://otda.ny.gov/policy/directives/2016/LCM/16-LCM-16-Attachment-1.pdf#page=1", + "https://liheapch.acf.gov/docs/NY_BenefitMatrix_2022.pdf#page=1", "https://liheapch.acf.gov/docs/NY_Plan_2017.pdf#page=9", "https://liheapch.acf.gov/docs/2026/state-plans/NY_Plan_2026.pdf#page=10", "https://liheapch.acf.gov/docs/2026/benefits-matricies/NY_BenefitMatrix_2026.docx", @@ -28,3 +30,19 @@ def formula(spm_unit, period, parameters): return spm_unit("ny_heap_categorically_eligible", period) | ( spm_unit("ny_heap_countable_income", period) <= threshold ) + + def formula_2017_01_01(spm_unit, period, parameters): + p = parameters( + period + ).gov.states.ny.otda.heap.eligibility.historical_income_limits + size = max_(spm_unit("ny_heap_household_size", period), 1) + last_size = p.last_published_household_size + threshold = p.tier_one.calc(min_(size, last_size)) * MONTHS_IN_YEAR + table_tier_one = spm_unit("ny_heap_categorically_eligible", period) | ( + spm_unit("ny_heap_countable_income", period) <= threshold + ) + generic_tier_one = ny_heap_tier_one.formula(spm_unit, period, parameters) + return where(size <= last_size, table_tier_one, generic_tier_one) + + def formula_2024_01_01(spm_unit, period, parameters): + return ny_heap_tier_one.formula(spm_unit, period, parameters)