From 05898e0bff3c374e4e7da25d543d473acd395841 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Fri, 6 Nov 2026 06:30:28 -0500 Subject: [PATCH 1/6] Add Hawaii's employee temporary disability insurance contribution HRS 392-43 lets an employer withhold half the cost of coverage, up to 0.5% of weekly wages, and caps those wages at a maximum weekly wage base the Director publishes each year. Model the contribution at that limit from the published 2004-2026 wage bases, and add it to employee_state_payroll_tax so it reaches employee_payroll_tax and household net income. HRS 392-3 takes its wage exclusions from HRS 383-11, the same list Hawaii unemployment tax uses, so wages start from payroll_tax_gross_wages. That variable raised a TypeError before 2010 because the FICA pre-tax list started in 2010, so the list now applies to every year. Co-Authored-By: Claude Opus 5.5 --- .../hi-tdi-employee-contribution.added.md | 1 + .../fica_pre_tax_contributions.yaml | 2 +- .../employee_rate.yaml | 20 ++ .../maximum_weekly_wage_base.yaml | 83 +++++ .../qualification/minimum_wages.yaml | 16 + .../qualification/minimum_weekly_hours.yaml | 16 + .../qualification/minimum_weeks.yaml | 16 + .../test_hi_temporary_disability_insurance.py | 244 +++++++++++++++ .../tests/core/test_payroll_contributions.py | 9 + ...ary_disability_insurance_contribution.yaml | 288 ++++++++++++++++++ .../payroll/hi_employee_state_payroll_tax.py | 16 + ...orary_disability_insurance_contribution.py | 28 ++ ...emporary_disability_insurance_qualified.py | 33 ++ ...rary_disability_insurance_taxable_wages.py | 34 +++ .../tax/payroll/employee_state_payroll_tax.py | 6 +- 15 files changed, 810 insertions(+), 2 deletions(-) create mode 100644 changelog.d/hi-tdi-employee-contribution.added.md create mode 100644 policyengine_us/parameters/gov/states/hi/tax/payroll/temporary_disability_insurance/employee_rate.yaml create mode 100644 policyengine_us/parameters/gov/states/hi/tax/payroll/temporary_disability_insurance/maximum_weekly_wage_base.yaml create mode 100644 policyengine_us/parameters/gov/states/hi/tax/payroll/temporary_disability_insurance/qualification/minimum_wages.yaml create mode 100644 policyengine_us/parameters/gov/states/hi/tax/payroll/temporary_disability_insurance/qualification/minimum_weekly_hours.yaml create mode 100644 policyengine_us/parameters/gov/states/hi/tax/payroll/temporary_disability_insurance/qualification/minimum_weeks.yaml create mode 100644 policyengine_us/tests/core/test_hi_temporary_disability_insurance.py create mode 100644 policyengine_us/tests/policy/baseline/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_employee_temporary_disability_insurance_contribution.yaml create mode 100644 policyengine_us/variables/gov/states/hi/tax/payroll/hi_employee_state_payroll_tax.py create mode 100644 policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_employee_temporary_disability_insurance_contribution.py create mode 100644 policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_qualified.py create mode 100644 policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_taxable_wages.py diff --git a/changelog.d/hi-tdi-employee-contribution.added.md b/changelog.d/hi-tdi-employee-contribution.added.md new file mode 100644 index 00000000000..8a3851578da --- /dev/null +++ b/changelog.d/hi-tdi-employee-contribution.added.md @@ -0,0 +1 @@ +Add Hawaii's employee temporary disability insurance contribution under HRS 392-43, with the published maximum weekly wage base for 2004 through 2026, and count it in employee payroll tax and household net income. diff --git a/policyengine_us/parameters/gov/irs/gross_income/fica_pre_tax_contributions.yaml b/policyengine_us/parameters/gov/irs/gross_income/fica_pre_tax_contributions.yaml index 6834c47c44f..21f01c7a19b 100644 --- a/policyengine_us/parameters/gov/irs/gross_income/fica_pre_tax_contributions.yaml +++ b/policyengine_us/parameters/gov/irs/gross_income/fica_pre_tax_contributions.yaml @@ -1,6 +1,6 @@ description: Payroll deductions that reduce FICA (Social Security and Medicare) wages without a filing-status-specific cap. values: - 2010-01-01: + 0000-01-01: - pre_tax_health_insurance_premiums - health_savings_account_payroll_contributions # Dependent care assistance the employer reasonably believes excludable diff --git a/policyengine_us/parameters/gov/states/hi/tax/payroll/temporary_disability_insurance/employee_rate.yaml b/policyengine_us/parameters/gov/states/hi/tax/payroll/temporary_disability_insurance/employee_rate.yaml new file mode 100644 index 00000000000..ed337771574 --- /dev/null +++ b/policyengine_us/parameters/gov/states/hi/tax/payroll/temporary_disability_insurance/employee_rate.yaml @@ -0,0 +1,20 @@ +description: Hawaii limits the employee temporary disability insurance contribution to this share of weekly wages. +values: + # Zero marks years before the first notice read (2004), not a law without a + # contribution. See maximum_weekly_wage_base.yaml. + 0000-01-01: 0 + 2004-01-01: 0.005 + +metadata: + period: year + unit: /1 + label: Hawaii employee TDI maximum contribution rate + reference: + - title: Hawaii Revised Statutes Section 392-43(a) + href: https://data.capitol.hawaii.gov/hrscurrent/Vol07_Ch0346-0398/HRS0392/HRS_0392-0043.htm + - title: Hawaii DLIR, 2004 maximum weekly wage base notice + href: https://web.archive.org/web/20050226051453/http://hawaii.gov:80/labor/dcd/pdf/TDI/2004%20TDI%20Wage%20Base.pdf#page=1 + - title: Hawaii DLIR, 2026 maximum weekly wage base notice + href: https://labor.hawaii.gov/dcd/files/2025/12/2026-Maximum-Weekly-Wage-Base.pdf#page=1 + - title: Hawaii DLIR, Form TDI-15 (rev. June 2026) + href: https://labor.hawaii.gov/dcd/files/2023/05/TDI15-rev-2026.06-1.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/hi/tax/payroll/temporary_disability_insurance/maximum_weekly_wage_base.yaml b/policyengine_us/parameters/gov/states/hi/tax/payroll/temporary_disability_insurance/maximum_weekly_wage_base.yaml new file mode 100644 index 00000000000..6eb9ecb9f9e --- /dev/null +++ b/policyengine_us/parameters/gov/states/hi/tax/payroll/temporary_disability_insurance/maximum_weekly_wage_base.yaml @@ -0,0 +1,83 @@ +description: Hawaii counts weekly wages up to this amount toward the employee temporary disability insurance contribution. +values: + # Zero marks years before the first notice read (2004), not a law without a + # wage base. The Director publishes one amount a year under HRS 392-43(b)(2): + # the State's average annual wage / 52 * 1.21. + 0000-01-01: 0 + 2004-01-01: 720.70 + 2005-01-01: 752.44 + 2006-01-01: 791.70 + 2007-01-01: 820.26 + 2008-01-01: 842.56 + 2009-01-01: 877.69 + 2010-01-01: 901.70 + 2011-01-01: 884.29 + 2012-01-01: 903.28 + 2013-01-01: 921.78 + 2014-01-01: 940.05 + 2015-01-01: 951.23 + 2016-01-01: 982.36 + 2017-01-01: 1_023.31 + 2018-01-01: 1_068.62 + 2019-01-01: 1_088.08 + 2020-01-01: 1_119.44 + 2021-01-01: 1_102.90 + 2022-01-01: 1_200.30 + 2023-01-01: 1_318.48 + 2024-01-01: 1_374.78 + 2025-01-01: 1_441.72 + 2026-01-01: 1_500.21 + +metadata: + period: week + unit: currency-USD + label: Hawaii TDI maximum weekly wage base + reference: + - title: Hawaii Revised Statutes Section 392-43(b)(2) + href: https://data.capitol.hawaii.gov/hrscurrent/Vol07_Ch0346-0398/HRS0392/HRS_0392-0043.htm + - title: Hawaii DLIR, 2004 maximum weekly wage base notice + href: https://web.archive.org/web/20050226051453/http://hawaii.gov:80/labor/dcd/pdf/TDI/2004%20TDI%20Wage%20Base.pdf#page=1 + - title: Hawaii DLIR, 2005 maximum weekly wage base notice + href: https://web.archive.org/web/20050226052914/http://hawaii.gov:80/labor/dcd/pdf/general/2005_WAGE_BASE.pdf#page=1 + - title: Hawaii DLIR, 2006 maximum weekly wage base notice + href: https://web.archive.org/web/20060227223955/http://hawaii.gov:80/labor/dcd/pdf/general/2006_WAGE_BASE.pdf#page=1 + - title: Hawaii DLIR, 2007 maximum weekly wage base notice + href: https://web.archive.org/web/20080719002041/http://hawaii.gov/labor/dcd/PDF/general/2007_Wage_Base_12-1-06.pdf#page=1 + - title: Hawaii DLIR, 2008 maximum weekly wage base notice + href: https://web.archive.org/web/20080724052848/http://hawaii.gov/labor/dcd/PDF/general/WAGE%20BASE-2008%20%28PDF%29.pdf#page=1 + - title: Hawaii DLIR, 2009 maximum weekly wage base notice + href: https://web.archive.org/web/20121025001236/http://hawaii.gov/labor/dcd/PDF/general/2009%20Wage%20Base.pdf#page=1 + - title: Hawaii DLIR, 2010 maximum weekly wage base notice + href: https://web.archive.org/web/20101215215838/http://hawaii.gov/labor/dcd/PDF/general/2010%20Wage%20Base.pdf#page=1 + - title: Hawaii DLIR, 2011 maximum weekly wage base notice + href: https://web.archive.org/web/20101215215837/http://hawaii.gov/labor/dcd/PDF/general/2011%20Wage%20Base.pdf#page=1 + - title: Hawaii DLIR, 2012 maximum weekly wage base notice + href: https://web.archive.org/web/20121024102028/http://hawaii.gov/labor/dcd/PDF/general/2012%20Wage%20Base.pdf#page=1 + - title: Hawaii DLIR, 2013 maximum weekly wage base notice + href: https://web.archive.org/web/20141030021517/http://labor.hawaii.gov/dcd/files/2013/01/Old-Wage-Base.pdf#page=1 + - title: Hawaii DLIR, 2014 maximum weekly wage base notice + href: https://web.archive.org/web/20141030011206/http://labor.hawaii.gov/dcd/files/2013/01/New-Wage-Base.pdf#page=1 + - title: Hawaii DLIR, 2015 maximum weekly wage base notice + href: https://web.archive.org/web/20160824005208/http://labor.hawaii.gov:80/dcd/files/2013/01/2015-Wage-Base.pdf#page=1 + - title: Hawaii DLIR, 2016 maximum weekly wage base notice + href: https://web.archive.org/web/20160706000230/http://labor.hawaii.gov:80/dcd/files/2014/11/2016.pdf#page=1 + - title: Hawaii DLIR, 2017 maximum weekly wage base notice + href: https://web.archive.org/web/20171101222709/https://labor.hawaii.gov/dcd/files/2013/01/17Wbase.pdf#page=1 + - title: Hawaii DLIR, 2018 maximum weekly wage base notice + href: https://labor.hawaii.gov/dcd/files/2017/11/2018.pdf#page=1 + - title: Hawaii DLIR, 2019 maximum weekly wage base notice + href: https://labor.hawaii.gov/dcd/files/2018/11/2019wagebase.pdf#page=1 + - title: Hawaii DLIR, 2020 maximum weekly wage base notice + href: https://web.archive.org/web/20201017202041/https://labor.hawaii.gov/dcd/files/2019/11/newWBA.pdf#page=1 + - title: Hawaii DLIR, 2021 maximum weekly wage base notice + href: https://web.archive.org/web/20201204141806/https://labor.hawaii.gov/dcd/files/2019/11/newWBA.pdf#page=1 + - title: Hawaii DLIR, 2022 maximum weekly wage base notice + href: https://web.archive.org/web/20220819164238/https://labor.hawaii.gov/dcd/files/2019/11/newWBA.pdf#page=1 + - title: Hawaii DLIR, 2023 maximum weekly wage base notice + href: https://web.archive.org/web/20230306111219/https://labor.hawaii.gov/dcd/files/2019/11/newWBA.pdf#page=1 + - title: Hawaii DLIR, 2024 maximum weekly wage base notice + href: https://labor.hawaii.gov/dcd/files/2023/11/2024-Max-Weekly-Wage-Base-Weekly-Benefit-Amount.pdf#page=1 + - title: Hawaii DLIR, 2025 maximum weekly wage base notice + href: https://labor.hawaii.gov/dcd/files/2023/05/2025-Max-Weekly-Wage-Base-Rev-2025.03.pdf#page=1 + - title: Hawaii DLIR, 2026 maximum weekly wage base notice + href: https://labor.hawaii.gov/dcd/files/2025/12/2026-Maximum-Weekly-Wage-Base.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/hi/tax/payroll/temporary_disability_insurance/qualification/minimum_wages.yaml b/policyengine_us/parameters/gov/states/hi/tax/payroll/temporary_disability_insurance/qualification/minimum_wages.yaml new file mode 100644 index 00000000000..7256c49dfb0 --- /dev/null +++ b/policyengine_us/parameters/gov/states/hi/tax/payroll/temporary_disability_insurance/qualification/minimum_wages.yaml @@ -0,0 +1,16 @@ +description: Hawaii counts an employee's weekly wages toward the temporary disability insurance contribution only if the employee earned at least this amount in the prior 52 weeks. +values: + # Act 30 of 1999 applies from 2000 and left this amount in place. Later acts + # did not change it. Zero marks earlier years, which this program does not model. + 0000-01-01: 0 + 2000-01-01: 400 + +metadata: + period: year + unit: currency-USD + label: Hawaii TDI minimum wages to count weekly wages + reference: + - title: Hawaii Revised Statutes Section 392-43(b)(1) + href: https://data.capitol.hawaii.gov/hrscurrent/Vol07_Ch0346-0398/HRS0392/HRS_0392-0043.htm + - title: Session Laws of Hawaii 1999, Act 30 + href: https://data.capitol.hawaii.gov/slh/Years/SLH1999/SLH1999_Act30.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/hi/tax/payroll/temporary_disability_insurance/qualification/minimum_weekly_hours.yaml b/policyengine_us/parameters/gov/states/hi/tax/payroll/temporary_disability_insurance/qualification/minimum_weekly_hours.yaml new file mode 100644 index 00000000000..e97d742c76c --- /dev/null +++ b/policyengine_us/parameters/gov/states/hi/tax/payroll/temporary_disability_insurance/qualification/minimum_weekly_hours.yaml @@ -0,0 +1,16 @@ +description: Hawaii treats a week as qualifying for the temporary disability insurance contribution only if the employee was paid for at least this many hours in that week. +values: + # Act 30 of 1999 applies from 2000 and left this count in place. Later acts + # did not change it. Zero marks earlier years, which this program does not model. + 0000-01-01: 0 + 2000-01-01: 20 + +metadata: + period: week + unit: hour + label: Hawaii TDI minimum paid hours in a qualifying week + reference: + - title: Hawaii Revised Statutes Section 392-43(b)(1) + href: https://data.capitol.hawaii.gov/hrscurrent/Vol07_Ch0346-0398/HRS0392/HRS_0392-0043.htm + - title: Session Laws of Hawaii 1999, Act 30 + href: https://data.capitol.hawaii.gov/slh/Years/SLH1999/SLH1999_Act30.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/hi/tax/payroll/temporary_disability_insurance/qualification/minimum_weeks.yaml b/policyengine_us/parameters/gov/states/hi/tax/payroll/temporary_disability_insurance/qualification/minimum_weeks.yaml new file mode 100644 index 00000000000..ebc425bf7c4 --- /dev/null +++ b/policyengine_us/parameters/gov/states/hi/tax/payroll/temporary_disability_insurance/qualification/minimum_weeks.yaml @@ -0,0 +1,16 @@ +description: Hawaii counts an employee's weekly wages toward the temporary disability insurance contribution only if the employee worked at least this many qualifying weeks in the prior 52 weeks. +values: + # Act 30 of 1999 applies from 2000 and left this count in place. Later acts + # did not change it. Zero marks earlier years, which this program does not model. + 0000-01-01: 0 + 2000-01-01: 14 + +metadata: + period: year + unit: week + label: Hawaii TDI minimum qualifying weeks to count weekly wages + reference: + - title: Hawaii Revised Statutes Section 392-43(b)(1) + href: https://data.capitol.hawaii.gov/hrscurrent/Vol07_Ch0346-0398/HRS0392/HRS_0392-0043.htm + - title: Session Laws of Hawaii 1999, Act 30 + href: https://data.capitol.hawaii.gov/slh/Years/SLH1999/SLH1999_Act30.pdf#page=1 diff --git a/policyengine_us/tests/core/test_hi_temporary_disability_insurance.py b/policyengine_us/tests/core/test_hi_temporary_disability_insurance.py new file mode 100644 index 00000000000..60f3a597a58 --- /dev/null +++ b/policyengine_us/tests/core/test_hi_temporary_disability_insurance.py @@ -0,0 +1,244 @@ +"""Invariants for Hawaii's employee temporary disability insurance contribution. + +YAML covers the policy examples. This module covers what one YAML case cannot +state: + +1. Code against published figures. The wage base parameter matches every DLIR + notice, each notice's base follows HRS 392-43(b)(2) from that notice's + average annual wage, and each notice's maximum weekly deduction is 0.5% of + its base. +2. The annual formula against the weekly statute. The law caps wages week by + week. For any wages spread over a worker's weeks, the annual amount is at + least the weekly sum, and the two match when wages are even. +3. Properties across drawn batches of workers: bounds, monotonicity in wages, + state masking, and aggregation to the tax unit. +4. Net income. Removing the contribution raises household net income by exactly + the contribution. + +Intended differences from the published figures: + +- The 2010 notice's 901.70 equals the unrounded product rounded once. Every + other notice matches rounding the weekly wage first, which gives 901.69 for + 2010. HRS 392-43(b)(2) sets no rounding rule, and the parameter holds the + published amount. +- A full-year worker over the cap pays 52 * 0.5% * base here. DLIR rounds each + week's maximum deduction to the cent, so 52 weeks of it differ by at most 26 + cents. +""" + +from decimal import ROUND_HALF_UP, Decimal + +import numpy as np +import pytest +from hypothesis import HealthCheck, given, settings +from hypothesis import strategies as st + +from policyengine_us import Simulation +from policyengine_us.system import system as SYSTEM + +CONTRIBUTION = "hi_employee_temporary_disability_insurance_contribution" +TOLERANCE = 0.01 # dollars +RATE = Decimal("0.005") # HRS 392-43(a) +FACTOR = Decimal("1.21") # HRS 392-43(b)(2) + +# Transcribed from each year's DLIR notice, independent of the parameter file: +# state average annual wage, maximum weekly wage base, maximum weekly deduction. +NOTICES = { + 2004: ("30972.13", "720.70", "3.60"), + 2005: ("32336.24", "752.44", "3.76"), + 2006: ("34023.65", "791.70", "3.96"), + 2007: ("35250.68", "820.26", "4.10"), + 2008: ("36209.17", "842.56", "4.21"), + 2009: ("37718.93", "877.69", "4.39"), + 2010: ("38750.59", "901.70", "4.51"), + 2011: ("38002.62", "884.29", "4.42"), + 2012: ("38818.32", "903.28", "4.52"), + 2013: ("39613.57", "921.78", "4.61"), + 2014: ("40398.56", "940.05", "4.70"), + 2015: ("40879.44", "951.23", "4.76"), + 2016: ("42217.41", "982.36", "4.91"), + 2017: ("43976.82", "1023.31", "5.12"), + 2018: ("45924.45", "1068.62", "5.34"), + 2019: ("46760.69", "1088.08", "5.44"), + 2020: ("48108.20", "1119.44", "5.60"), + 2021: ("47397.51", "1102.90", "5.51"), + 2022: ("51582.77", "1200.30", "6.00"), + 2023: ("56662.05", "1318.48", "6.59"), + 2024: ("59081.47", "1374.78", "6.87"), + 2025: ("61958.21", "1441.72", "7.21"), + 2026: ("64471.82", "1500.21", "7.50"), +} +YEARS = sorted(NOTICES) +GROUP_ENTITIES = ( + "tax_units", + "spm_units", + "families", + "marital_units", + "households", +) +TDI = SYSTEM.parameters.gov.states.hi.tax.payroll.temporary_disability_insurance + +# A batch's cost is mostly per-variable overhead, so each example is a batch of +# workers and there are few examples. +SETTINGS = dict( + max_examples=8, + deadline=None, + derandomize=True, + suppress_health_check=[HealthCheck.too_slow, HealthCheck.data_too_large], +) + + +def cents(amount): + return amount.quantize(Decimal("0.01"), rounding=ROUND_HALF_UP) + + +def base_for(year): + return float(NOTICES[year][1]) + + +def simulate(year, workers, **overrides): + period = str(year) + situation = {"people": {}, **{entity: {} for entity in GROUP_ENTITIES}} + for i, worker in enumerate(workers): + person = f"person_{i}" + situation["people"][person] = { + "age": {period: 40}, + "employment_income": {period: worker["wages"]}, + "pre_tax_health_insurance_premiums": {period: worker.get("premiums", 0)}, + "weeks_worked": {period: worker.get("weeks", 0)}, + "weekly_hours_worked_before_lsr": {period: worker.get("hours", 0)}, + **{name: {period: value} for name, value in overrides.items()}, + } + for entity in GROUP_ENTITIES: + situation[entity][f"{entity}_{i}"] = {"members": [person]} + situation["households"][f"households_{i}"]["state_code"] = { + period: worker.get("state", "HI") + } + return Simulation(tax_benefit_system=SYSTEM, situation=situation) + + +@pytest.mark.parametrize("year", YEARS) +def test_parameters_match_the_published_notice(year): + instant = f"{year}-01-01" + assert TDI.maximum_weekly_wage_base(instant) == pytest.approx( + base_for(year), abs=1e-9 + ) + assert TDI.employee_rate(instant) == float(RATE) + + +@pytest.mark.parametrize("year", YEARS) +def test_notice_follows_the_statutory_formula(year): + annual_wage, base, deduction = (Decimal(value) for value in NOTICES[year]) + rounded_once = cents(annual_wage / 52 * FACTOR) + weekly_wage_rounded_first = cents(cents(annual_wage / 52) * FACTOR) + assert base == (rounded_once if year == 2010 else weekly_wage_rounded_first) + assert deduction == cents(base * RATE) + assert abs(52 * base * RATE - 52 * deduction) <= Decimal("0.26") + + +workers = st.fixed_dictionaries( + { + "state": st.sampled_from(["HI", "HI", "HI", "TX"]), + "wages": st.one_of( + st.sampled_from([0, 399, 400, 1_000_000]), + st.integers(-5_000, 200_000), + ), + "premiums": st.one_of(st.just(0), st.integers(0, 10_000)), + "weeks": st.one_of(st.sampled_from([0, 13, 14, 52]), st.integers(1, 52)), + "hours": st.one_of(st.sampled_from([0, 19, 20, 40]), st.integers(1, 80)), + } +) + + +@settings(**SETTINGS) +@given( + year=st.sampled_from(YEARS), + batch=st.lists(workers, min_size=5, max_size=30), + bump=st.integers(1, 20_000), +) +def test_contribution_properties(year, batch, bump): + period = str(year) + sim = simulate(year, batch) + contribution = sim.calculate(CONTRIBUTION, period) + raised = simulate( + year, [{**worker, "wages": worker["wages"] + bump} for worker in batch] + ).calculate(CONTRIBUTION, period) + + in_hawaii = np.array([worker["state"] == "HI" for worker in batch]) + wages = np.array( + [max(0, worker["wages"] - worker["premiums"]) for worker in batch], + dtype=float, + ) + weeks = np.array([worker["weeks"] or 52 for worker in batch], dtype=float) + limit = float(RATE) * np.minimum(wages, base_for(year) * weeks) + + # Bounds, for every input. + assert (contribution >= 0).all() + assert (contribution <= limit + TOLERANCE).all() + # State masking. + assert (contribution[~in_hawaii] == 0).all() + # More wages never lower the contribution. + assert (raised >= contribution - TOLERANCE).all() + # Each worker files alone, so the tax unit total is that worker's amount. + assert sim.calculate("hi_employee_state_payroll_tax", period) == pytest.approx( + contribution, abs=TOLERANCE + ) + assert sim.calculate("employee_state_payroll_tax", period)[ + in_hawaii + ] == pytest.approx(contribution[in_hawaii], abs=TOLERANCE) + + +# At least 14 weeks of at least 30 dollars clears the $400 and 14-week test. +weekly_wages = st.lists(st.integers(30, 4_000), min_size=14, max_size=52) + + +@settings(**SETTINGS) +@given( + year=st.sampled_from(YEARS), + batch=st.lists(weekly_wages, min_size=3, max_size=15), +) +def test_annual_amount_is_the_limit_of_the_weekly_statute(year, batch): + base = base_for(year) + annual = simulate( + year, + [{"wages": sum(series), "weeks": len(series), "hours": 40} for series in batch], + ).calculate(CONTRIBUTION, str(year)) + + statute = np.array( + [sum(float(RATE) * min(wage, base) for wage in series) for series in batch] + ) + assert (annual >= statute - TOLERANCE).all() + + even = np.array( + [ + len(series) * float(RATE) * min(sum(series) / len(series), base) + for series in batch + ] + ) + assert annual == pytest.approx(even, abs=TOLERANCE) + + +def test_contribution_lowers_household_net_income_dollar_for_dollar(): + period = "2026" + batch = [{"wages": wages} for wages in (30_000, 60_000, 100_000)] + baseline = simulate(2026, batch) + without = simulate(2026, batch, **{CONTRIBUTION: 0}) + + contribution = baseline.calculate(CONTRIBUTION, period) + # 0.5% of 30,000 and 60,000, then 0.5% * 52 * 1,500.21. + assert contribution == pytest.approx([150, 300, 390.0546], abs=TOLERANCE) + + for variable in ( + "employee_state_payroll_tax", + "employee_payroll_tax", + "household_tax_before_refundable_credits", + ): + added = baseline.calculate(variable, period) - without.calculate( + variable, period + ) + assert added == pytest.approx(contribution, abs=TOLERANCE), variable + + lost = without.calculate("household_net_income", period) - baseline.calculate( + "household_net_income", period + ) + assert lost == pytest.approx(contribution, abs=TOLERANCE) diff --git a/policyengine_us/tests/core/test_payroll_contributions.py b/policyengine_us/tests/core/test_payroll_contributions.py index 6939b040785..016187c7aa6 100644 --- a/policyengine_us/tests/core/test_payroll_contributions.py +++ b/policyengine_us/tests/core/test_payroll_contributions.py @@ -303,6 +303,15 @@ def make_employer_total_simulation( }, id="DE", ), + pytest.param( + "HI", + { + "hi_employee_temporary_disability_insurance_contribution": 390.0546, + "hi_employee_state_payroll_tax": 390.0546, + "employee_state_payroll_tax": 390.0546, + }, + id="HI", + ), pytest.param( "MA", { diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_employee_temporary_disability_insurance_contribution.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_employee_temporary_disability_insurance_contribution.yaml new file mode 100644 index 00000000000..b93c535e742 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_employee_temporary_disability_insurance_contribution.yaml @@ -0,0 +1,288 @@ +# Each year's maximum weekly wage base comes from the Hawaii DLIR notice for +# that year, cited in maximum_weekly_wage_base.yaml. The 0.5% rate comes from +# HRS 392-43(a), and the $400, 14-week and 20-hour test from HRS 392-43(b)(1). + +- name: Hawaii 2024 contribution is 0.5% of wages under the cap. + period: 2024 + absolute_error_margin: 0.01 + input: + employment_income: 50_000 + state_code: HI + output: + hi_temporary_disability_insurance_qualified: true + hi_temporary_disability_insurance_taxable_wages: 50_000 + # 0.5% * 50,000 = 250.00. + hi_employee_temporary_disability_insurance_contribution: 250.00 + hi_employee_state_payroll_tax: 250.00 + employee_state_payroll_tax: 250.00 + +- name: Hawaii 2024 contribution stops at the 1,374.78 weekly wage base. + period: 2024 + absolute_error_margin: 0.01 + input: + employment_income: 100_000 + state_code: HI + output: + # 52 * 1,374.78 = 71,488.56. + hi_temporary_disability_insurance_taxable_wages: 71_488.56 + # 0.5% * 71,488.56 = 357.44. DLIR's 6.87 weekly maximum * 52 = 357.24, and + # the 0.20 gap comes from DLIR rounding each week's maximum to the cent. + hi_employee_temporary_disability_insurance_contribution: 357.44 + hi_employee_state_payroll_tax: 357.44 + employee_state_payroll_tax: 357.44 + +- name: Hawaii 2026 contribution is 0.5% of wages under the cap. + period: 2026 + absolute_error_margin: 0.01 + input: + employment_income: 60_000 + state_code: HI + output: + # 0.5% * 60,000 = 300.00. + hi_employee_temporary_disability_insurance_contribution: 300.00 + employee_state_payroll_tax: 300.00 + +- name: Hawaii 2026 contribution stops at the 1,500.21 weekly wage base. + period: 2026 + absolute_error_margin: 0.01 + input: + employment_income: 100_000 + state_code: HI + output: + # 52 * 1,500.21 = 78,010.92. + hi_temporary_disability_insurance_taxable_wages: 78_010.92 + # 0.5% * 78,010.92 = 390.05. DLIR's 7.50 weekly maximum * 52 = 390.00. + hi_employee_temporary_disability_insurance_contribution: 390.05 + # Social Security 6.2% * 100,000 = 6,200.00, Medicare 1.45% * 100,000 = + # 1,450.00, and 6,200.00 + 1,450.00 + 390.05 = 8,040.05. + employee_payroll_tax: 8_040.05 + spm_unit_payroll_tax: 8_040.05 + +- name: Hawaii 2004, the first published year, uses the 720.70 weekly wage base. + period: 2004 + absolute_error_margin: 0.01 + input: + employment_income: 50_000 + state_code: HI + output: + # 52 * 720.70 = 37,476.40. + hi_temporary_disability_insurance_taxable_wages: 37_476.40 + # 0.5% * 37,476.40 = 187.38. + hi_employee_temporary_disability_insurance_contribution: 187.38 + +- name: Hawaii 2009 uses the 877.69 weekly wage base and counts pre-tax premiums out. + period: 2009 + absolute_error_margin: 0.01 + input: + employment_income: 50_000 + pre_tax_health_insurance_premiums: 10_000 + state_code: HI + output: + # 50,000 - 10,000 = 40,000, under 52 * 877.69 = 45,639.88. + hi_temporary_disability_insurance_taxable_wages: 40_000 + # 0.5% * 40,000 = 200.00. + hi_employee_temporary_disability_insurance_contribution: 200.00 + employee_state_payroll_tax: 200.00 + +- name: Hawaii models no contribution before the first published year. + period: 2003 + absolute_error_margin: 0.01 + input: + employment_income: 50_000 + state_code: HI + output: + hi_temporary_disability_insurance_taxable_wages: 0 + hi_employee_temporary_disability_insurance_contribution: 0 + +- name: Hawaii 2020 contribution stops at the 1,119.44 weekly wage base. + period: 2020 + absolute_error_margin: 0.01 + input: + employment_income: 100_000 + state_code: HI + output: + # 52 * 1,119.44 = 58,210.88. + hi_temporary_disability_insurance_taxable_wages: 58_210.88 + # 0.5% * 58,210.88 = 291.05. + hi_employee_temporary_disability_insurance_contribution: 291.05 + +- name: Hawaii 2021 contribution falls with the lower 1,102.90 weekly wage base. + period: 2021 + absolute_error_margin: 0.01 + input: + employment_income: 100_000 + state_code: HI + output: + # 52 * 1,102.90 = 57,350.80. + hi_temporary_disability_insurance_taxable_wages: 57_350.80 + # 0.5% * 57,350.80 = 286.75. + hi_employee_temporary_disability_insurance_contribution: 286.75 + +- name: Hawaii applies the weekly cap only to weeks worked. + period: 2024 + absolute_error_margin: 0.01 + input: + employment_income: 50_000 + weeks_worked: 26 + weekly_hours_worked_before_lsr: 40 + state_code: HI + output: + # 26 * 1,374.78 = 35,744.28. + hi_temporary_disability_insurance_taxable_wages: 35_744.28 + # 0.5% * 35,744.28 = 178.72. + hi_employee_temporary_disability_insurance_contribution: 178.72 + +- name: Hawaii taxes part-year wages in full when they stay under the weekly cap. + period: 2024 + absolute_error_margin: 0.01 + input: + employment_income: 20_000 + weeks_worked: 26 + weekly_hours_worked_before_lsr: 40 + state_code: HI + output: + # 26 * 1,374.78 = 35,744.28 exceeds 20,000. + hi_temporary_disability_insurance_taxable_wages: 20_000 + # 0.5% * 20,000 = 100.00. + hi_employee_temporary_disability_insurance_contribution: 100.00 + +- name: Hawaii counts wages at exactly 14 weeks and 20 hours. + period: 2024 + absolute_error_margin: 0.01 + input: + employment_income: 15_000 + weeks_worked: 14 + weekly_hours_worked_before_lsr: 20 + state_code: HI + output: + hi_temporary_disability_insurance_qualified: true + # 14 * 1,374.78 = 19,246.92 exceeds 15,000. + hi_temporary_disability_insurance_taxable_wages: 15_000 + # 0.5% * 15,000 = 75.00. + hi_employee_temporary_disability_insurance_contribution: 75.00 + +- name: Hawaii does not count wages at 13 weeks. + period: 2024 + absolute_error_margin: 0.01 + input: + employment_income: 15_000 + weeks_worked: 13 + weekly_hours_worked_before_lsr: 40 + state_code: HI + output: + hi_temporary_disability_insurance_qualified: false + hi_temporary_disability_insurance_taxable_wages: 0 + hi_employee_temporary_disability_insurance_contribution: 0 + +- name: Hawaii does not count wages under 20 hours a week. + period: 2024 + absolute_error_margin: 0.01 + input: + employment_income: 20_000 + weeks_worked: 52 + weekly_hours_worked_before_lsr: 19 + state_code: HI + output: + hi_temporary_disability_insurance_qualified: false + hi_temporary_disability_insurance_taxable_wages: 0 + hi_employee_temporary_disability_insurance_contribution: 0 + +- name: Hawaii counts wages at exactly 400. + period: 2024 + absolute_error_margin: 0.01 + input: + employment_income: 400 + state_code: HI + output: + hi_temporary_disability_insurance_qualified: true + # 0.5% * 400 = 2.00. + hi_employee_temporary_disability_insurance_contribution: 2.00 + +- name: Hawaii does not count wages under 400. + period: 2024 + absolute_error_margin: 0.01 + input: + employment_income: 399 + state_code: HI + output: + hi_temporary_disability_insurance_qualified: false + hi_employee_temporary_disability_insurance_contribution: 0 + +- name: Hawaii tests weeks when hours went unreported. + period: 2024 + absolute_error_margin: 0.01 + input: + employment_income: 15_000 + weeks_worked: 10 + state_code: HI + output: + hi_temporary_disability_insurance_qualified: false + hi_employee_temporary_disability_insurance_contribution: 0 + +- name: Hawaii tests hours when weeks went unreported. + period: 2024 + absolute_error_margin: 0.01 + input: + employment_income: 15_000 + weekly_hours_worked_before_lsr: 10 + state_code: HI + output: + hi_temporary_disability_insurance_qualified: false + hi_employee_temporary_disability_insurance_contribution: 0 + +- name: Hawaii leaves pre-tax health premiums out of wages. + period: 2024 + absolute_error_margin: 0.01 + input: + employment_income: 50_000 + pre_tax_health_insurance_premiums: 5_000 + state_code: HI + output: + hi_temporary_disability_insurance_taxable_wages: 45_000 + # 0.5% * 45,000 = 225.00. + hi_employee_temporary_disability_insurance_contribution: 225.00 + +- name: Hawaii ignores negative employment income. + period: 2024 + absolute_error_margin: 0.01 + input: + employment_income: -5_000 + state_code: HI + output: + hi_temporary_disability_insurance_taxable_wages: 0 + hi_employee_temporary_disability_insurance_contribution: 0 + +- name: Hawaii contribution does not apply in other states. + period: 2024 + absolute_error_margin: 0.01 + input: + employment_income: 50_000 + state_code: CA + output: + hi_temporary_disability_insurance_taxable_wages: 0 + hi_employee_temporary_disability_insurance_contribution: 0 + hi_employee_state_payroll_tax: 0 + +- name: Hawaii caps each spouse's wages on their own. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + employment_income: 100_000 + person2: + employment_income: 40_000 + tax_units: + tax_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: HI + output: + # 52 * 1,500.21 = 78,010.92 for person1, and 40,000 is under the cap. + hi_temporary_disability_insurance_taxable_wages: [78_010.92, 40_000] + # 0.5% * 78,010.92 = 390.05 and 0.5% * 40,000 = 200.00. + hi_employee_temporary_disability_insurance_contribution: [390.05, 200.00] + # 390.05 + 200.00 = 590.05. + employee_state_payroll_tax: 590.05 diff --git a/policyengine_us/variables/gov/states/hi/tax/payroll/hi_employee_state_payroll_tax.py b/policyengine_us/variables/gov/states/hi/tax/payroll/hi_employee_state_payroll_tax.py new file mode 100644 index 00000000000..478d4e2b1ca --- /dev/null +++ b/policyengine_us/variables/gov/states/hi/tax/payroll/hi_employee_state_payroll_tax.py @@ -0,0 +1,16 @@ +from policyengine_us.model_api import * + + +class hi_employee_state_payroll_tax(Variable): + value_type = float + entity = Person + label = "Hawaii employee state payroll tax" + documentation = ( + "Employee-side Hawaii payroll-funded contributions. Hawaii's temporary " + "disability insurance contribution goes to the employer's plan or " + "insurer, not to the State." + ) + definition_period = YEAR + unit = USD + defined_for = StateCode.HI + adds = ["hi_employee_temporary_disability_insurance_contribution"] diff --git a/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_employee_temporary_disability_insurance_contribution.py b/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_employee_temporary_disability_insurance_contribution.py new file mode 100644 index 00000000000..2ef4f8075a0 --- /dev/null +++ b/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_employee_temporary_disability_insurance_contribution.py @@ -0,0 +1,28 @@ +from policyengine_us.model_api import * + + +class hi_employee_temporary_disability_insurance_contribution(Variable): + value_type = float + entity = Person + label = "Hawaii employee temporary disability insurance contribution" + documentation = ( + "Employee-side Hawaii temporary disability insurance contribution at " + "the statutory limit. HRS 392-43(a) lets an employer withhold half the " + "cost of coverage, up to the employee rate times weekly wages, and " + "makes the employer pay the balance. Coverage cost varies by employer " + "and the model holds no premium data, so this assumes the employer " + "withholds the full rate limit." + ) + definition_period = YEAR + unit = USD + defined_for = StateCode.HI + reference = ( + "https://data.capitol.hawaii.gov/hrscurrent/Vol07_Ch0346-0398/HRS0392/HRS_0392-0043.htm", + ) + + def formula(person, period, parameters): + p = parameters(period).gov.states.hi.tax.payroll.temporary_disability_insurance + taxable_wages = person( + "hi_temporary_disability_insurance_taxable_wages", period + ) + return p.employee_rate * taxable_wages diff --git a/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_qualified.py b/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_qualified.py new file mode 100644 index 00000000000..169675a4f6a --- /dev/null +++ b/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_qualified.py @@ -0,0 +1,33 @@ +from policyengine_us.model_api import * + + +class hi_temporary_disability_insurance_qualified(Variable): + value_type = bool + entity = Person + label = "Wages count toward the Hawaii temporary disability insurance contribution" + documentation = ( + "HRS 392-43(b)(1) counts wages from a payroll period only if the prior " + "52 weeks hold enough wages and enough weeks with enough paid hours. " + "The model holds no prior-year work history, so this annual proxy tests " + "the simulation year's wages, weeks worked and usual weekly hours. Zero " + "weeks or zero hours mean the value went unreported, so that part of " + "the test passes." + ) + definition_period = YEAR + defined_for = StateCode.HI + reference = ( + "https://data.capitol.hawaii.gov/hrscurrent/Vol07_Ch0346-0398/HRS0392/HRS_0392-0043.htm", + "https://data.capitol.hawaii.gov/hrscurrent/Vol07_Ch0346-0398/HRS0392/HRS_0392-0043_0005.htm", + ) + + def formula(person, period, parameters): + p = parameters( + period + ).gov.states.hi.tax.payroll.temporary_disability_insurance.qualification + wages = person("payroll_tax_gross_wages", period) + weeks = person("weeks_worked", period) + hours = person("weekly_hours_worked_before_lsr", period) + meets_wages = wages >= p.minimum_wages + meets_weeks = (weeks == 0) | (weeks >= p.minimum_weeks) + meets_hours = (hours == 0) | (hours >= p.minimum_weekly_hours) + return meets_wages & meets_weeks & meets_hours diff --git a/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_taxable_wages.py b/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_taxable_wages.py new file mode 100644 index 00000000000..56683a68f2f --- /dev/null +++ b/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_taxable_wages.py @@ -0,0 +1,34 @@ +from policyengine_us.model_api import * + + +class hi_temporary_disability_insurance_taxable_wages(Variable): + value_type = float + entity = Person + label = "Hawaii temporary disability insurance taxable wages" + documentation = ( + "Wages subject to the Hawaii temporary disability insurance employee " + "contribution. HRS 392-3 excludes the payments HRS 383-11 lists, such " + "as employer-plan payments for medical expenses, so this starts from " + "payroll_tax_gross_wages. HRS 392-43(b)(2) caps wages week by week, so " + "the annual cap multiplies the maximum weekly wage base by weeks " + "worked, or by 52 when weeks worked went unreported. An even weekly " + "wage reaches the largest amount the weekly cap allows, so this amount " + "is an upper limit for a worker with one employer." + ) + definition_period = YEAR + unit = USD + defined_for = StateCode.HI + reference = ( + "https://data.capitol.hawaii.gov/hrscurrent/Vol07_Ch0346-0398/HRS0392/HRS_0392-0003.htm", + "https://data.capitol.hawaii.gov/hrscurrent/Vol07_Ch0346-0398/HRS0383/HRS_0383-0011.htm", + "https://data.capitol.hawaii.gov/hrscurrent/Vol07_Ch0346-0398/HRS0392/HRS_0392-0043.htm", + ) + + def formula(person, period, parameters): + p = parameters(period).gov.states.hi.tax.payroll.temporary_disability_insurance + wages = person("payroll_tax_gross_wages", period) + weeks = person("weeks_worked", period) + counted_weeks = where(weeks > 0, min_(weeks, WEEKS_IN_YEAR), WEEKS_IN_YEAR) + capped_wages = min_(wages, p.maximum_weekly_wage_base * counted_weeks) + qualified = person("hi_temporary_disability_insurance_qualified", period) + return where(qualified, capped_wages, 0) diff --git a/policyengine_us/variables/gov/states/tax/payroll/employee_state_payroll_tax.py b/policyengine_us/variables/gov/states/tax/payroll/employee_state_payroll_tax.py index 7956a031799..b7de702db7c 100644 --- a/policyengine_us/variables/gov/states/tax/payroll/employee_state_payroll_tax.py +++ b/policyengine_us/variables/gov/states/tax/payroll/employee_state_payroll_tax.py @@ -5,6 +5,7 @@ "co_employee_state_payroll_tax", "ct_employee_state_payroll_tax", "de_employee_state_payroll_tax", + "hi_employee_state_payroll_tax", "ma_employee_state_payroll_tax", "me_employee_state_payroll_tax", "mn_employee_state_payroll_tax", @@ -24,7 +25,10 @@ class employee_state_payroll_tax(Variable): label = "Employee state payroll taxes and contributions" documentation = ( "Employee-side mandatory state payroll taxes and payroll-funded " - "contributions, aggregated from jurisdiction-specific rules." + "contributions, aggregated from jurisdiction-specific rules. Hawaii " + "lets employers withhold its temporary disability insurance " + "contribution without requiring it, and the model assumes they " + "withhold the full limit." ) definition_period = YEAR unit = USD From 683f457d0b8509ec63518157a19abb20714ad02d Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Fri, 6 Nov 2026 07:12:36 -0500 Subject: [PATCH 2/6] Define Hawaii TDI wages from HRS 392-3 and 383-11 The contribution read the federal payroll wage base, which rests on federal rules Hawaii did not enact. Hawaii's text leaves out what an employer pays to medical and retirement plans, including salary reductions, and lists no dependent care payment. Build wages from those two sections, record the reading as a source-conflict candidate with short excerpts, and restore the shared FICA list's start date, which only the federal base needed. Co-Authored-By: Claude Opus 5.5 --- .../fica_pre_tax_contributions.yaml | 2 +- .../excluded_payments.yaml | 31 ++++ .../test_hi_temporary_disability_insurance.py | 33 ++++- ...ary_disability_insurance_contribution.yaml | 48 +++++++ ..._temporary_disability_insurance_wages.yaml | 136 ++++++++++++++++++ ...emporary_disability_insurance_qualified.py | 2 +- ...rary_disability_insurance_taxable_wages.py | 16 +-- ...hi_temporary_disability_insurance_wages.py | 33 +++++ source_conflicts/candidates.yaml | 41 ++++++ .../dlir-ui-employer-handbook-excerpt.txt | 4 + .../sources/us-hi/hrs-383-11-excerpt.txt | 6 + .../sources/us-hi/hrs-392-3-excerpt.txt | 5 + 12 files changed, 344 insertions(+), 13 deletions(-) create mode 100644 policyengine_us/parameters/gov/states/hi/tax/payroll/temporary_disability_insurance/excluded_payments.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_wages.yaml create mode 100644 policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_wages.py create mode 100644 source_conflicts/sources/us-hi/dlir-ui-employer-handbook-excerpt.txt create mode 100644 source_conflicts/sources/us-hi/hrs-383-11-excerpt.txt create mode 100644 source_conflicts/sources/us-hi/hrs-392-3-excerpt.txt diff --git a/policyengine_us/parameters/gov/irs/gross_income/fica_pre_tax_contributions.yaml b/policyengine_us/parameters/gov/irs/gross_income/fica_pre_tax_contributions.yaml index 21f01c7a19b..6834c47c44f 100644 --- a/policyengine_us/parameters/gov/irs/gross_income/fica_pre_tax_contributions.yaml +++ b/policyengine_us/parameters/gov/irs/gross_income/fica_pre_tax_contributions.yaml @@ -1,6 +1,6 @@ description: Payroll deductions that reduce FICA (Social Security and Medicare) wages without a filing-status-specific cap. values: - 0000-01-01: + 2010-01-01: - pre_tax_health_insurance_premiums - health_savings_account_payroll_contributions # Dependent care assistance the employer reasonably believes excludable diff --git a/policyengine_us/parameters/gov/states/hi/tax/payroll/temporary_disability_insurance/excluded_payments.yaml b/policyengine_us/parameters/gov/states/hi/tax/payroll/temporary_disability_insurance/excluded_payments.yaml new file mode 100644 index 00000000000..eabeee3af7a --- /dev/null +++ b/policyengine_us/parameters/gov/states/hi/tax/payroll/temporary_disability_insurance/excluded_payments.yaml @@ -0,0 +1,31 @@ +description: Hawaii leaves these payments out of wages for the temporary disability insurance contribution. +values: + # HRS 383-11(1)(C) covers what an employer pays under a plan for medical or + # hospitalization expenses, including amounts paid for insurance or into a fund. + 0000-01-01: + - pre_tax_health_insurance_premiums + - health_savings_account_payroll_contributions + # The law did not change in 2015. The model's elective deferral limit starts + # then, and the contribution variables below read it. + 2015-01-01: + - pre_tax_health_insurance_premiums + - health_savings_account_payroll_contributions + # HRS 383-11(4)(A) covers what an employer pays on an individual's behalf to + # a trust described in IRC 401(a). It sets no condition on income tax + # treatment, so Roth deferrals come out with traditional ones. + - traditional_401k_contributions + - roth_401k_contributions + # HRS 383-11(1)(A) covers what an employer pays for annuities under a plan + # on account of retirement. + - traditional_403b_contributions + - roth_403b_contributions + +metadata: + period: year + unit: list + label: Hawaii TDI payments excluded from wages + reference: + - title: Hawaii Revised Statutes Section 392-3 + href: https://data.capitol.hawaii.gov/hrscurrent/Vol07_Ch0346-0398/HRS0392/HRS_0392-0003.htm + - title: Hawaii Revised Statutes Section 383-11 + href: https://data.capitol.hawaii.gov/hrscurrent/Vol07_Ch0346-0398/HRS0383/HRS_0383-0011.htm diff --git a/policyengine_us/tests/core/test_hi_temporary_disability_insurance.py b/policyengine_us/tests/core/test_hi_temporary_disability_insurance.py index 60f3a597a58..94419b1f595 100644 --- a/policyengine_us/tests/core/test_hi_temporary_disability_insurance.py +++ b/policyengine_us/tests/core/test_hi_temporary_disability_insurance.py @@ -69,6 +69,14 @@ 2026: ("64471.82", "1500.21", "7.50"), } YEARS = sorted(NOTICES) +# HRS 383-11(1)(A) and (4)(A). The model's elective deferral limit starts in 2015. +DEFERRALS = ( + "traditional_401k_contributions", + "roth_401k_contributions", + "traditional_403b_contributions", + "roth_403b_contributions", +) +FIRST_DEFERRAL_YEAR = 2015 GROUP_ENTITIES = ( "tax_units", "spm_units", @@ -105,6 +113,17 @@ def simulate(year, workers, **overrides): "age": {period: 40}, "employment_income": {period: worker["wages"]}, "pre_tax_health_insurance_premiums": {period: worker.get("premiums", 0)}, + "dependent_care_employer_benefits": { + period: worker.get("dependent_care", 0) + }, + **{ + kind: { + period: worker.get("deferral", 0) + if kind == worker.get("deferral_kind") + else 0 + } + for kind in DEFERRALS + }, "weeks_worked": {period: worker.get("weeks", 0)}, "weekly_hours_worked_before_lsr": {period: worker.get("hours", 0)}, **{name: {period: value} for name, value in overrides.items()}, @@ -144,6 +163,9 @@ def test_notice_follows_the_statutory_formula(year): st.integers(-5_000, 200_000), ), "premiums": st.one_of(st.just(0), st.integers(0, 10_000)), + "deferral": st.one_of(st.just(0), st.integers(0, 10_000)), + "deferral_kind": st.sampled_from(DEFERRALS), + "dependent_care": st.one_of(st.just(0), st.integers(0, 5_000)), "weeks": st.one_of(st.sampled_from([0, 13, 14, 52]), st.integers(1, 52)), "hours": st.one_of(st.sampled_from([0, 19, 20, 40]), st.integers(1, 80)), } @@ -165,8 +187,17 @@ def test_contribution_properties(year, batch, bump): ).calculate(CONTRIBUTION, period) in_hawaii = np.array([worker["state"] == "HI" for worker in batch]) + counts_deferral = year >= FIRST_DEFERRAL_YEAR wages = np.array( - [max(0, worker["wages"] - worker["premiums"]) for worker in batch], + [ + max( + 0, + worker["wages"] + - worker["premiums"] + - (worker["deferral"] if counts_deferral else 0), + ) + for worker in batch + ], dtype=float, ) weeks = np.array([worker["weeks"] or 52 for worker in batch], dtype=float) diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_employee_temporary_disability_insurance_contribution.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_employee_temporary_disability_insurance_contribution.yaml index b93c535e742..d4499fbfed0 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_employee_temporary_disability_insurance_contribution.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_employee_temporary_disability_insurance_contribution.yaml @@ -242,6 +242,54 @@ # 0.5% * 45,000 = 225.00. hi_employee_temporary_disability_insurance_contribution: 225.00 +- name: Hawaii leaves a 401(k) deferral out of the contribution. + period: 2026 + absolute_error_margin: 0.01 + input: + employment_income: 60_000 + traditional_401k_contributions: 4_000 + state_code: HI + output: + # 60,000 - 4,000 = 56,000, and 0.5% * 56,000 = 280.00. + hi_employee_temporary_disability_insurance_contribution: 280.00 + +- name: Hawaii counts dependent care benefits in the contribution. + period: 2026 + absolute_error_margin: 0.01 + input: + employment_income: 60_000 + dependent_care_employer_benefits: 5_000 + state_code: HI + output: + # 0.5% * 60,000 = 300.00. + hi_employee_temporary_disability_insurance_contribution: 300.00 + +- name: Hawaii still applies the cap when wages after a deferral exceed it. + period: 2026 + absolute_error_margin: 0.01 + input: + employment_income: 100_000 + traditional_401k_contributions: 10_000 + state_code: HI + output: + # 100,000 - 10,000 = 90,000 exceeds 52 * 1,500.21 = 78,010.92. + hi_temporary_disability_insurance_taxable_wages: 78_010.92 + # 0.5% * 78,010.92 = 390.05. + hi_employee_temporary_disability_insurance_contribution: 390.05 + +- name: Hawaii tests the 400 minimum against wages after excluded payments. + period: 2024 + absolute_error_margin: 0.01 + input: + employment_income: 1_000 + traditional_401k_contributions: 700 + state_code: HI + output: + # 1,000 - 700 = 300, under 400. + hi_temporary_disability_insurance_wages: 300 + hi_temporary_disability_insurance_qualified: false + hi_employee_temporary_disability_insurance_contribution: 0 + - name: Hawaii ignores negative employment income. period: 2024 absolute_error_margin: 0.01 diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_wages.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_wages.yaml new file mode 100644 index 00000000000..e5f20e41f8a --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_wages.yaml @@ -0,0 +1,136 @@ +# HRS 392-3 counts all remuneration and leaves out the payments HRS 383-11 lists. +# The expected values below subtract those payments by hand. + +- name: Hawaii wages leave out a traditional 401(k) deferral. + period: 2026 + absolute_error_margin: 0.01 + input: + employment_income: 60_000 + traditional_401k_contributions: 4_000 + state_code: HI + output: + # HRS 383-11(4)(A): 60,000 - 4,000 = 56,000. + hi_temporary_disability_insurance_wages: 56_000 + # The federal base keeps the deferral. + payroll_tax_gross_wages: 60_000 + +- name: Hawaii wages leave out a Roth 401(k) deferral. + period: 2026 + absolute_error_margin: 0.01 + input: + employment_income: 60_000 + roth_401k_contributions: 4_000 + state_code: HI + output: + # HRS 383-11(4)(A) sets no income tax condition: 60,000 - 4,000 = 56,000. + hi_temporary_disability_insurance_wages: 56_000 + +- name: Hawaii wages leave out traditional and Roth 403(b) deferrals. + period: 2026 + absolute_error_margin: 0.01 + input: + employment_income: 60_000 + traditional_403b_contributions: 3_000 + roth_403b_contributions: 1_000 + state_code: HI + output: + # HRS 383-11(1)(A): 60,000 - 3,000 - 1,000 = 56,000. + hi_temporary_disability_insurance_wages: 56_000 + +- name: Hawaii wages leave out pre-tax health premiums and payroll HSA contributions. + period: 2026 + absolute_error_margin: 0.01 + input: + employment_income: 60_000 + pre_tax_health_insurance_premiums: 3_000 + health_savings_account_payroll_contributions: 2_000 + state_code: HI + output: + # HRS 383-11(1)(C): 60,000 - 3,000 - 2,000 = 55,000. + hi_temporary_disability_insurance_wages: 55_000 + +- name: Hawaii wages keep dependent care benefits, which HRS 383-11 does not list. + period: 2026 + absolute_error_margin: 0.01 + input: + employment_income: 60_000 + dependent_care_employer_benefits: 5_000 + state_code: HI + output: + hi_temporary_disability_insurance_wages: 60_000 + # The federal base removes them: 60,000 - 5,000 = 55,000. + payroll_tax_gross_wages: 55_000 + +- name: Hawaii wages leave out only the deferral the 2026 limit allows. + period: 2026 + absolute_error_margin: 0.01 + input: + age: 40 + employment_income: 100_000 + traditional_401k_contributions_desired: 30_000 + state_code: HI + output: + # The 2026 limit under age 50 is 24,500: 100,000 - 24,500 = 75,500. + traditional_401k_contributions: 24_500 + hi_temporary_disability_insurance_wages: 75_500 + +- name: Hawaii wages leave out deferrals in 2015, the first year the model holds a limit. + period: 2015 + absolute_error_margin: 0.01 + input: + age: 55 + employment_income: 60_000 + traditional_401k_contributions: 4_000 + state_code: HI + output: + # 60,000 - 4,000 = 56,000. + hi_temporary_disability_insurance_wages: 56_000 + +- name: Hawaii wages keep deferrals in 2014, before the model holds a limit. + period: 2014 + absolute_error_margin: 0.01 + input: + employment_income: 60_000 + traditional_401k_contributions: 4_000 + state_code: HI + output: + hi_temporary_disability_insurance_wages: 60_000 + +- name: Hawaii wages leave out premiums in 2004. + period: 2004 + absolute_error_margin: 0.01 + input: + employment_income: 60_000 + pre_tax_health_insurance_premiums: 3_000 + state_code: HI + output: + # 60,000 - 3,000 = 57,000. + hi_temporary_disability_insurance_wages: 57_000 + +- name: Hawaii wages stop at zero when excluded payments exceed pay. + period: 2026 + absolute_error_margin: 0.01 + input: + employment_income: 3_000 + pre_tax_health_insurance_premiums: 5_000 + state_code: HI + output: + hi_temporary_disability_insurance_wages: 0 + +- name: Hawaii wages stop at zero for negative employment income. + period: 2026 + absolute_error_margin: 0.01 + input: + employment_income: -5_000 + state_code: HI + output: + hi_temporary_disability_insurance_wages: 0 + +- name: Hawaii wages do not apply in other states. + period: 2026 + absolute_error_margin: 0.01 + input: + employment_income: 60_000 + state_code: NV + output: + hi_temporary_disability_insurance_wages: 0 diff --git a/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_qualified.py b/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_qualified.py index 169675a4f6a..bdf1c1aa4b0 100644 --- a/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_qualified.py +++ b/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_qualified.py @@ -24,7 +24,7 @@ def formula(person, period, parameters): p = parameters( period ).gov.states.hi.tax.payroll.temporary_disability_insurance.qualification - wages = person("payroll_tax_gross_wages", period) + wages = person("hi_temporary_disability_insurance_wages", period) weeks = person("weeks_worked", period) hours = person("weekly_hours_worked_before_lsr", period) meets_wages = wages >= p.minimum_wages diff --git a/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_taxable_wages.py b/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_taxable_wages.py index 56683a68f2f..a654e9f9737 100644 --- a/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_taxable_wages.py +++ b/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_taxable_wages.py @@ -7,26 +7,22 @@ class hi_temporary_disability_insurance_taxable_wages(Variable): label = "Hawaii temporary disability insurance taxable wages" documentation = ( "Wages subject to the Hawaii temporary disability insurance employee " - "contribution. HRS 392-3 excludes the payments HRS 383-11 lists, such " - "as employer-plan payments for medical expenses, so this starts from " - "payroll_tax_gross_wages. HRS 392-43(b)(2) caps wages week by week, so " - "the annual cap multiplies the maximum weekly wage base by weeks " - "worked, or by 52 when weeks worked went unreported. An even weekly " - "wage reaches the largest amount the weekly cap allows, so this amount " - "is an upper limit for a worker with one employer." + "contribution. HRS 392-43(b)(2) caps wages week by week, so the annual " + "cap multiplies the maximum weekly wage base by weeks worked, or by 52 " + "when weeks worked went unreported. An even weekly wage reaches the " + "largest amount the weekly cap allows, so this amount is an upper limit " + "for a worker with one employer." ) definition_period = YEAR unit = USD defined_for = StateCode.HI reference = ( - "https://data.capitol.hawaii.gov/hrscurrent/Vol07_Ch0346-0398/HRS0392/HRS_0392-0003.htm", - "https://data.capitol.hawaii.gov/hrscurrent/Vol07_Ch0346-0398/HRS0383/HRS_0383-0011.htm", "https://data.capitol.hawaii.gov/hrscurrent/Vol07_Ch0346-0398/HRS0392/HRS_0392-0043.htm", ) def formula(person, period, parameters): p = parameters(period).gov.states.hi.tax.payroll.temporary_disability_insurance - wages = person("payroll_tax_gross_wages", period) + wages = person("hi_temporary_disability_insurance_wages", period) weeks = person("weeks_worked", period) counted_weeks = where(weeks > 0, min_(weeks, WEEKS_IN_YEAR), WEEKS_IN_YEAR) capped_wages = min_(wages, p.maximum_weekly_wage_base * counted_weeks) diff --git a/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_wages.py b/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_wages.py new file mode 100644 index 00000000000..38ca933b356 --- /dev/null +++ b/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_wages.py @@ -0,0 +1,33 @@ +from policyengine_us.model_api import * + + +class hi_temporary_disability_insurance_wages(Variable): + value_type = float + entity = Person + label = "Hawaii temporary disability insurance wages" + documentation = ( + "Wages as HRS 392-3 defines them: all remuneration, less the payments " + "HRS 383-11 lists. That list covers what an employer pays under a plan " + "for medical expenses or retirement and what it pays to a trust " + "described in IRC 401(a). HRS 383-11(5) stops at payments made without " + "a deduction from pay, and paragraphs (1) and (4) carry no such limit, " + "so salary-reduction amounts for these plans come out. The list names " + "no dependent care payment, so dependent care benefits stay in. The " + "federal payroll wage base differs because Congress added rules for " + "elective deferrals, cafeteria plans and dependent care that Hawaii did " + "not enact. Before 2015 retirement deferrals stay in, because the " + "model's elective deferral limit starts in 2015." + ) + definition_period = YEAR + unit = USD + defined_for = StateCode.HI + reference = ( + "https://data.capitol.hawaii.gov/hrscurrent/Vol07_Ch0346-0398/HRS0392/HRS_0392-0003.htm", + "https://data.capitol.hawaii.gov/hrscurrent/Vol07_Ch0346-0398/HRS0383/HRS_0383-0011.htm", + ) + + def formula(person, period, parameters): + p = parameters(period).gov.states.hi.tax.payroll.temporary_disability_insurance + remuneration = person("employment_income", period) + excluded = add(person, period, p.excluded_payments) + return max_(remuneration - excluded, 0) diff --git a/source_conflicts/candidates.yaml b/source_conflicts/candidates.yaml index 43d9aa13941..30d49067efe 100644 --- a/source_conflicts/candidates.yaml +++ b/source_conflicts/candidates.yaml @@ -2933,6 +2933,47 @@ candidates: prs: [] state: unreviewed note: PE-US follows state rules; verify their effective text throughout the historical periods. +- id: us-hi:tdi-wages-salary-reductions + program: hi-tdi + years: + from: 2004 + type: calendar + question: Do amounts an employer pays into a retirement or medical plan out of an employee's salary count as wages for + Hawaii's temporary disability insurance contribution, and do dependent care benefits? + sources: + - kind: statute + citation: HRS 392-3 (wages), adopting HRS 383-11(1)(A), (1)(C), (4)(A) and (5) + url: https://data.capitol.hawaii.gov/hrscurrent/Vol07_Ch0346-0398/HRS0383/HRS_0383-0011.htm + - kind: regulation + citation: HAR 12-11-1, which takes wages from HRS 392-3 and says nothing on salary reductions + url: https://labor.hawaii.gov/dcd/files/2012/11/TDI-Admin-Rules.pdf#page=5 + - kind: guidance + citation: Hawaii DLIR Unemployment Insurance Division, Handbook for Employers, on the wages that chapter 383 reaches + url: https://labor.hawaii.gov/ui/test-handbook-for-employers/ + readings: + statute: A salary-reduction amount the employer pays under a medical or retirement plan, or to a section 401(a) trust, + is a listed payment, so it falls outside wages. HRS 383-11(5) limits one paragraph to payments without a deduction + from pay, and paragraphs (1) and (4) carry no such limit. The list names no dependent care payment, so those benefits + stay in wages. + other: The federal payroll wage base keeps elective deferrals in and takes cafeteria-plan amounts and dependent care + out, through IRC 3306(r)(1)(A), (b)(5)(G) and (b)(13), which Hawaii did not enact. A second alternative treats every + salary reduction as the employee's own remuneration, so all of it stays in wages. The rule and the handbook address + neither alternative. + example: In 2026, $60,000 of salary with a $4,000 traditional 401(k) deferral gives a $280 contribution under the statute, + versus $300 on the federal base. $60,000 with $5,000 of dependent care benefits gives $300, versus $275 on the federal + base. + pe_us: + follows: statute + code: + - policyengine_us/parameters/gov/states/hi/tax/payroll/temporary_disability_insurance/excluded_payments.yaml + - policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_wages.py + prs: + - https://github.com/PolicyEngine/policyengine-us/pull/10677 + state: ruled + note: The code follows the statute's text because we found no agency reading of salary reductions in the temporary + disability rules, the unemployment insurance rules (HAR 12-5) or the employer handbook. A published agency reading + would reopen this under the agency-breaks-ties principle if the text leaves the question open. Short excerpts are + in source_conflicts/sources/us-hi/. - id: us-ia:alternate-tax-claimed-spouse program: income-tax years: diff --git a/source_conflicts/sources/us-hi/dlir-ui-employer-handbook-excerpt.txt b/source_conflicts/sources/us-hi/dlir-ui-employer-handbook-excerpt.txt new file mode 100644 index 00000000000..6e4177c7678 --- /dev/null +++ b/source_conflicts/sources/us-hi/dlir-ui-employer-handbook-excerpt.txt @@ -0,0 +1,4 @@ +Source: https://labor.hawaii.gov/ui/test-handbook-for-employers/ +Short excerpt of the employer handbook's wages section, copied from the official HTML page. + +a few exclusions, such as payments to welfare plans, pension payments to retired workers diff --git a/source_conflicts/sources/us-hi/hrs-383-11-excerpt.txt b/source_conflicts/sources/us-hi/hrs-383-11-excerpt.txt new file mode 100644 index 00000000000..360e1fc0bfa --- /dev/null +++ b/source_conflicts/sources/us-hi/hrs-383-11-excerpt.txt @@ -0,0 +1,6 @@ +Source: https://data.capitol.hawaii.gov/hrscurrent/Vol07_Ch0346-0398/HRS0383/HRS_0383-0011.htm +Short excerpts of HRS 383-11(4), (4)(A) and (5), copied from the official HTML page. + +any payment by an employing unit to, or on behalf of, an individual +to a trust described in section 401(a) +without deduction from the remuneration diff --git a/source_conflicts/sources/us-hi/hrs-392-3-excerpt.txt b/source_conflicts/sources/us-hi/hrs-392-3-excerpt.txt new file mode 100644 index 00000000000..c8ecd53b1d7 --- /dev/null +++ b/source_conflicts/sources/us-hi/hrs-392-3-excerpt.txt @@ -0,0 +1,5 @@ +Source: https://data.capitol.hawaii.gov/hrscurrent/Vol07_Ch0346-0398/HRS0392/HRS_0392-0003.htm +Short excerpts of the definition of wages in HRS 392-3, copied from the official HTML page. + +all remuneration for services +Wages do not include the amount of any payment specified in section 383-11 From 8c4c9ce8d04ee70dd9020b2968915f751f650fe2 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 9 Nov 2026 08:29:45 -0500 Subject: [PATCH 3/6] Apply first review's fixes to Hawaii TDI Leave military basic pay out of Hawaii wages under HRS 392-5(6), let the wages test pass on the previous year's weeks and hours, and take retirement deferrals out from 2004. Extend the federal elective deferral and catch-up limits back to 2004 from the IRS table, which also corrects 2015-2017 to $18,000. Add the program to programs.yaml, split the source-conflict regulation captures by page, and update the Hawaii marginal rate case for the contribution. Co-Authored-By: Claude Opus 5.5 --- .../hi-tdi-employee-contribution.fixed.md | 1 + .../catch_up/age_threshold.yaml | 2 +- .../catch_up/limit/k401.yaml | 30 ++- .../retirement_contributions/limit/401k.yaml | 10 +- .../excluded_payments.yaml | 12 +- policyengine_us/programs.yaml | 12 + .../test_hi_temporary_disability_insurance.py | 57 ++++- .../medicare/test_irmaa_first_modeled_year.py | 4 +- .../retirement/k401_catch_up_limit.yaml | 45 ++++ ...ary_disability_insurance_contribution.yaml | 224 ++++++++++++++++++ ..._temporary_disability_insurance_wages.yaml | 52 +++- .../retirement/elective_deferral_limit.yaml | 59 +++++ ...al_tax_rate_including_health_benefits.yaml | 4 +- ...emporary_disability_insurance_qualified.py | 27 ++- ...rary_disability_insurance_taxable_wages.py | 3 +- ...hi_temporary_disability_insurance_wages.py | 8 +- source_conflicts/candidates.yaml | 9 +- .../sources/us-hi/har-12-11-excerpt.txt | 5 + .../sources/us-hi/hrs-383-11-excerpt.txt | 10 +- 19 files changed, 531 insertions(+), 43 deletions(-) create mode 100644 changelog.d/hi-tdi-employee-contribution.fixed.md create mode 100644 source_conflicts/sources/us-hi/har-12-11-excerpt.txt diff --git a/changelog.d/hi-tdi-employee-contribution.fixed.md b/changelog.d/hi-tdi-employee-contribution.fixed.md new file mode 100644 index 00000000000..97b419cc5d9 --- /dev/null +++ b/changelog.d/hi-tdi-employee-contribution.fixed.md @@ -0,0 +1 @@ +Extend the federal 401(k) elective deferral and catch-up limits back to 2004 from the IRS cost-of-living table, which sets the 2015 through 2017 elective deferral limit at $18,000 rather than $18,500. diff --git a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/age_threshold.yaml b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/age_threshold.yaml index e9fce231f72..ed255c327a3 100644 --- a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/age_threshold.yaml +++ b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/age_threshold.yaml @@ -1,6 +1,6 @@ description: The US limits catch-up pension contributions to individuals this age or older. values: - 2021-01-01: 50 + 2004-01-01: 50 metadata: unit: year period: year diff --git a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/k401.yaml b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/k401.yaml index 6bb4db946b3..cd2977c7be7 100644 --- a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/k401.yaml +++ b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/k401.yaml @@ -3,16 +3,20 @@ description: The IRS limits 401(k) catch-up contributions to this amount based o brackets: - threshold: values: - 2018-01-01: 0 + 2004-01-01: 0 amount: values: - 2018-01-01: 0 + 2004-01-01: 0 - threshold: values: - 2018-01-01: 50 + 2004-01-01: 50 amount: values: - 2018-01-01: 6_000 + 2004-01-01: 3_000 + 2005-01-01: 4_000 + 2006-01-01: 5_000 + 2009-01-01: 5_500 + 2015-01-01: 6_000 2020-01-01: 6_500 2023-01-01: 7_500 2024-01-01: 7_500 @@ -26,10 +30,14 @@ brackets: interval: 500 - threshold: values: - 2018-01-01: 60 + 2004-01-01: 60 amount: values: - 2018-01-01: 6_000 + 2004-01-01: 3_000 + 2005-01-01: 4_000 + 2006-01-01: 5_000 + 2009-01-01: 5_500 + 2015-01-01: 6_000 2020-01-01: 6_500 2023-01-01: 7_500 2024-01-01: 7_500 @@ -45,10 +53,14 @@ brackets: interval: 500 - threshold: values: - 2018-01-01: 64 + 2004-01-01: 64 amount: values: - 2018-01-01: 6_000 + 2004-01-01: 3_000 + 2005-01-01: 4_000 + 2006-01-01: 5_000 + 2009-01-01: 5_500 + 2015-01-01: 6_000 2020-01-01: 6_500 2023-01-01: 7_500 2024-01-01: 7_500 @@ -75,5 +87,5 @@ metadata: href: https://www.irs.gov/newsroom/401k-limit-increases-to-23500-for-2025-ira-limit-remains-7000 - title: IRS announcement - 401(k) limit increases for 2026 href: https://www.irs.gov/newsroom/401k-limit-increases-to-24500-for-2026-ira-limit-increases-to-7500 - - title: Cost-of-Living Adjustments for Retirement Items + - title: "Cost-of-Living Adjustments for Retirement Items #page 1,3-6" href: https://www.irs.gov/pub/irs-tege/cola-table.pdf#page=1 diff --git a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/401k.yaml b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/401k.yaml index 77a423bfe68..925f7b6f8ca 100644 --- a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/401k.yaml +++ b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/401k.yaml @@ -1,5 +1,13 @@ description: The US limits annual 401(k) contributions to this amount. values: + 2004-01-01: 13_000 + 2005-01-01: 14_000 + 2006-01-01: 15_000 + 2007-01-01: 15_500 + 2009-01-01: 16_500 + 2012-01-01: 17_000 + 2013-01-01: 17_500 + 2015-01-01: 18_000 2018-01-01: 18_500 2019-01-01: 19_000 2020-01-01: 19_500 @@ -21,5 +29,5 @@ metadata: - title: 26 CFR § 1.402(g)-1 - Limitation on exclusion for elective deferrals. (d))(i) href: https://law.cornell.edu/cfr/text/26/1.402(g)-1 # Inflation adjusted amounts - - title: Cost-of-Living Adjustments for Retirement Items + - title: "Cost-of-Living Adjustments for Retirement Items #page 1,3-6" href: https://www.irs.gov/pub/irs-tege/cola-table.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/hi/tax/payroll/temporary_disability_insurance/excluded_payments.yaml b/policyengine_us/parameters/gov/states/hi/tax/payroll/temporary_disability_insurance/excluded_payments.yaml index eabeee3af7a..57d792c0c21 100644 --- a/policyengine_us/parameters/gov/states/hi/tax/payroll/temporary_disability_insurance/excluded_payments.yaml +++ b/policyengine_us/parameters/gov/states/hi/tax/payroll/temporary_disability_insurance/excluded_payments.yaml @@ -2,14 +2,18 @@ description: Hawaii leaves these payments out of wages for the temporary disabil values: # HRS 383-11(1)(C) covers what an employer pays under a plan for medical or # hospitalization expenses, including amounts paid for insurance or into a fund. + # HRS 392-5(6) leaves service in the employ of the United States government + # out of employment. 0000-01-01: - pre_tax_health_insurance_premiums - health_savings_account_payroll_contributions - # The law did not change in 2015. The model's elective deferral limit starts - # then, and the contribution variables below read it. - 2015-01-01: + - military_basic_pay + # The law did not change in 2004. The federal elective deferral limit that the + # contribution variables below read starts then, as does the contribution rate. + 2004-01-01: - pre_tax_health_insurance_premiums - health_savings_account_payroll_contributions + - military_basic_pay # HRS 383-11(4)(A) covers what an employer pays on an individual's behalf to # a trust described in IRC 401(a). It sets no condition on income tax # treatment, so Roth deferrals come out with traditional ones. @@ -27,5 +31,7 @@ metadata: reference: - title: Hawaii Revised Statutes Section 392-3 href: https://data.capitol.hawaii.gov/hrscurrent/Vol07_Ch0346-0398/HRS0392/HRS_0392-0003.htm + - title: Hawaii Revised Statutes Section 392-5 + href: https://data.capitol.hawaii.gov/hrscurrent/Vol07_Ch0346-0398/HRS0392/HRS_0392-0005.htm - title: Hawaii Revised Statutes Section 383-11 href: https://data.capitol.hawaii.gov/hrscurrent/Vol07_Ch0346-0398/HRS0383/HRS_0383-0011.htm diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index 95ca1cfdbfe..77530634aba 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -2189,6 +2189,18 @@ programs: variable: ca_fera parameter_prefix: gov.states.ca.cpuc.fera + - id: hi_tdi + name: Hawaii Temporary Disability Insurance + full_name: Hawaii Temporary Disability Insurance employee contribution + category: Taxes + agency: Hawaii Department of Labor and Industrial Relations + status: partial + coverage: HI + variable: hi_employee_temporary_disability_insurance_contribution + parameter_prefix: gov.states.hi.tax.payroll.temporary_disability_insurance + verified_start_year: 2004 + notes: Models the employee contribution at the HRS 392-43(a) limit; benefits and employer costs are out of scope. + - id: il_fpp name: Illinois Family Planning Program full_name: Illinois HFS Family Planning Program diff --git a/policyengine_us/tests/core/test_hi_temporary_disability_insurance.py b/policyengine_us/tests/core/test_hi_temporary_disability_insurance.py index 94419b1f595..072a4677fa1 100644 --- a/policyengine_us/tests/core/test_hi_temporary_disability_insurance.py +++ b/policyengine_us/tests/core/test_hi_temporary_disability_insurance.py @@ -12,7 +12,9 @@ least the weekly sum, and the two match when wages are even. 3. Properties across drawn batches of workers: bounds, monotonicity in wages, state masking, and aggregation to the tax unit. -4. Net income. Removing the contribution raises household net income by exactly +4. Qualification. Previous-year history can only add workers, and exactly the + workers whose history clears the weeks and hours minimums. +5. Net income. Removing the contribution raises household net income by exactly the contribution. Intended differences from the published figures: @@ -37,6 +39,7 @@ from policyengine_us.system import system as SYSTEM CONTRIBUTION = "hi_employee_temporary_disability_insurance_contribution" +QUALIFIED = "hi_temporary_disability_insurance_qualified" TOLERANCE = 0.01 # dollars RATE = Decimal("0.005") # HRS 392-43(a) FACTOR = Decimal("1.21") # HRS 392-43(b)(2) @@ -69,14 +72,13 @@ 2026: ("64471.82", "1500.21", "7.50"), } YEARS = sorted(NOTICES) -# HRS 383-11(1)(A) and (4)(A). The model's elective deferral limit starts in 2015. +# HRS 383-11(1)(A) and (4)(A). DEFERRALS = ( "traditional_401k_contributions", "roth_401k_contributions", "traditional_403b_contributions", "roth_403b_contributions", ) -FIRST_DEFERRAL_YEAR = 2015 GROUP_ENTITIES = ( "tax_units", "spm_units", @@ -106,6 +108,7 @@ def base_for(year): def simulate(year, workers, **overrides): period = str(year) + previous = str(year - 1) situation = {"people": {}, **{entity: {} for entity in GROUP_ENTITIES}} for i, worker in enumerate(workers): person = f"person_{i}" @@ -113,6 +116,7 @@ def simulate(year, workers, **overrides): "age": {period: 40}, "employment_income": {period: worker["wages"]}, "pre_tax_health_insurance_premiums": {period: worker.get("premiums", 0)}, + "military_basic_pay": {period: worker.get("military", 0)}, "dependent_care_employer_benefits": { period: worker.get("dependent_care", 0) }, @@ -124,8 +128,14 @@ def simulate(year, workers, **overrides): } for kind in DEFERRALS }, - "weeks_worked": {period: worker.get("weeks", 0)}, - "weekly_hours_worked_before_lsr": {period: worker.get("hours", 0)}, + "weeks_worked": { + previous: worker.get("previous_weeks", 0), + period: worker.get("weeks", 0), + }, + "weekly_hours_worked_before_lsr": { + previous: worker.get("previous_hours", 0), + period: worker.get("hours", 0), + }, **{name: {period: value} for name, value in overrides.items()}, } for entity in GROUP_ENTITIES: @@ -163,6 +173,7 @@ def test_notice_follows_the_statutory_formula(year): st.integers(-5_000, 200_000), ), "premiums": st.one_of(st.just(0), st.integers(0, 10_000)), + "military": st.one_of(st.just(0), st.integers(0, 10_000)), "deferral": st.one_of(st.just(0), st.integers(0, 10_000)), "deferral_kind": st.sampled_from(DEFERRALS), "dependent_care": st.one_of(st.just(0), st.integers(0, 5_000)), @@ -187,14 +198,14 @@ def test_contribution_properties(year, batch, bump): ).calculate(CONTRIBUTION, period) in_hawaii = np.array([worker["state"] == "HI" for worker in batch]) - counts_deferral = year >= FIRST_DEFERRAL_YEAR wages = np.array( [ max( 0, worker["wages"] - worker["premiums"] - - (worker["deferral"] if counts_deferral else 0), + - worker["military"] + - worker["deferral"], ) for worker in batch ], @@ -219,6 +230,38 @@ def test_contribution_properties(year, batch, bump): ] == pytest.approx(contribution[in_hawaii], abs=TOLERANCE) +@settings(**SETTINGS) +@given( + year=st.sampled_from(YEARS), + batch=st.lists( + st.tuples( + workers, + st.sampled_from([0, 13, 14, 52]), + st.sampled_from([0, 19, 20, 40]), + ), + min_size=5, + max_size=30, + ), +) +def test_previous_year_history_only_adds_workers_who_clear_it(year, batch): + period = str(year) + alone = simulate(year, [worker for worker, _, _ in batch]).calculate( + QUALIFIED, period + ) + with_history = simulate( + year, + [ + {**worker, "previous_weeks": weeks, "previous_hours": hours} + for worker, weeks, hours in batch + ], + ).calculate(QUALIFIED, period) + + in_hawaii = np.array([worker["state"] == "HI" for worker, _, _ in batch]) + clears = np.array([weeks >= 14 and hours >= 20 for _, weeks, hours in batch]) + assert (with_history[in_hawaii] == (alone | clears)[in_hawaii]).all() + assert not with_history[~in_hawaii].any() + + # At least 14 weeks of at least 30 dollars clears the $400 and 14-week test. weekly_wages = st.lists(st.integers(30, 4_000), min_size=14, max_size=52) diff --git a/policyengine_us/tests/policy/baseline/gov/hhs/medicare/test_irmaa_first_modeled_year.py b/policyengine_us/tests/policy/baseline/gov/hhs/medicare/test_irmaa_first_modeled_year.py index d2a94141895..adf1ee8df81 100644 --- a/policyengine_us/tests/policy/baseline/gov/hhs/medicare/test_irmaa_first_modeled_year.py +++ b/policyengine_us/tests/policy/baseline/gov/hhs/medicare/test_irmaa_first_modeled_year.py @@ -57,8 +57,8 @@ def test_first_modeled_year_is_the_backdating_floor(): # The constant must match how far CountryTaxBenefitSystem backdates: a # parameter first dated after the floor is defined from it, not before. limit = SYSTEM.parameters.gov.irs.gross_income.retirement_contributions.limit - assert limit.children["401k"](f"{FIRST_MODELED_YEAR}-01-01") > 0 - assert limit.children["401k"](f"{FIRST_MODELED_YEAR - 1}-12-31") is None + assert limit.children["ira"](f"{FIRST_MODELED_YEAR}-01-01") > 0 + assert limit.children["ira"](f"{FIRST_MODELED_YEAR - 1}-12-31") is None @pytest.mark.parametrize("year", UNMODELED_LAG_YEARS) diff --git a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/retirement/k401_catch_up_limit.yaml b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/retirement/k401_catch_up_limit.yaml index 81bb15a436e..0b75d6afefd 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/retirement/k401_catch_up_limit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/retirement/k401_catch_up_limit.yaml @@ -125,3 +125,48 @@ age: 64 output: k401_catch_up_limit: 8_000 + +# Earlier years come from the IRS cost-of-living table, 414(v)(2)(B)(i), +# https://www.irs.gov/pub/irs-tege/cola-table.pdf. + +- name: Case 18, age 49 in 2004 receives no catch-up. + period: 2004 + input: + age: 49 + output: + k401_catch_up_limit: 0 + +- name: Case 19, age 50 in 2004 receives 2004 standard limit. + period: 2004 + input: + age: 50 + output: + k401_catch_up_limit: 3_000 + +- name: Case 20, age 50 in 2008 receives 2008 standard limit. + period: 2008 + input: + age: 50 + output: + k401_catch_up_limit: 5_000 + +- name: Case 21, age 62 in 2009 receives 2009 standard limit. + period: 2009 + input: + age: 62 + output: + k401_catch_up_limit: 5_500 + +- name: Case 22, age 50 in 2014 receives 2014 standard limit. + period: 2014 + input: + age: 50 + output: + k401_catch_up_limit: 5_500 + +- name: Case 23, age 50 in 2017 receives 2017 standard limit. + period: 2017 + input: + age: 50 + output: + k401_catch_up_limit: 6_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_employee_temporary_disability_insurance_contribution.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_employee_temporary_disability_insurance_contribution.yaml index d4499fbfed0..c3b183457b5 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_employee_temporary_disability_insurance_contribution.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_employee_temporary_disability_insurance_contribution.yaml @@ -334,3 +334,227 @@ hi_employee_temporary_disability_insurance_contribution: [390.05, 200.00] # 390.05 + 200.00 = 590.05. employee_state_payroll_tax: 590.05 + +- name: Hawaii leaves a 2014 deferral out of the contribution. + period: 2014 + absolute_error_margin: 0.01 + input: + employment_income: 30_000 + traditional_401k_contributions: 4_000 + state_code: HI + output: + # 0.5% * (30,000 - 4,000) = 130, under 52 * 940.05 = 48,882.60. + hi_employee_temporary_disability_insurance_contribution: 130 + +- name: Hawaii leaves out only the deferral the 2009 limit allows. + period: 2009 + absolute_error_margin: 0.01 + input: + age: 40 + employment_income: 50_000 + traditional_401k_contributions_desired: 20_000 + state_code: HI + output: + # 50,000 - 16,500 = 33,500, under 52 * 877.69 = 45,639.88. + hi_temporary_disability_insurance_wages: 33_500 + # 0.5% * 33,500 = 167.50. + hi_employee_temporary_disability_insurance_contribution: 167.50 + +- name: Hawaii leaves out a 2009 catch-up deferral. + period: 2009 + absolute_error_margin: 0.01 + input: + age: 55 + employment_income: 50_000 + traditional_401k_contributions_desired: 25_000 + state_code: HI + output: + # 16,500 + 5,500 = 22,000 of deferrals, and 50,000 - 22,000 = 28,000. + hi_temporary_disability_insurance_wages: 28_000 + # 0.5% * 28,000 = 140. + hi_employee_temporary_disability_insurance_contribution: 140 + +- name: Hawaii charges no contribution on active-duty pay alone. + period: 2026 + absolute_error_margin: 0.01 + input: + employment_income: 60_000 + military_basic_pay: 60_000 + state_code: HI + output: + hi_temporary_disability_insurance_wages: 0 + hi_employee_temporary_disability_insurance_contribution: 0 + +- name: Hawaii charges a service member's civilian wages. + period: 2026 + absolute_error_margin: 0.01 + input: + employment_income: 70_000 + military_basic_pay: 60_000 + state_code: HI + output: + # 0.5% * (70,000 - 60,000) = 50. + hi_employee_temporary_disability_insurance_contribution: 50 + +- name: Hawaii counts a short final year when the previous year passes. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + employment_income: + 2025: 52_000 + 2026: 1_000 + weeks_worked: + 2025: 52 + 2026: 1 + weekly_hours_worked_before_lsr: + 2025: 40 + 2026: 40 + households: + household: + members: [person1] + state_code: HI + output: + hi_temporary_disability_insurance_qualified: true + # One week's 1,000 falls under the 1,500.21 cap: 0.5% * 1,000 = 5. + hi_employee_temporary_disability_insurance_contribution: 5 + +- name: Hawaii counts final-year wages under 400 when the previous year passes. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + employment_income: + 2025: 52_000 + 2026: 300 + weeks_worked: + 2025: 52 + 2026: 1 + weekly_hours_worked_before_lsr: + 2025: 40 + 2026: 40 + households: + household: + members: [person1] + state_code: HI + output: + hi_temporary_disability_insurance_qualified: true + # 0.5% * 300 = 1.50. + hi_employee_temporary_disability_insurance_contribution: 1.50 + +- name: Hawaii passes a previous year at exactly 14 weeks and 20 hours. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + employment_income: + 2025: 10_000 + 2026: 1_000 + weeks_worked: + 2025: 14 + 2026: 1 + weekly_hours_worked_before_lsr: + 2025: 20 + 2026: 40 + households: + household: + members: [person1] + state_code: HI + output: + hi_temporary_disability_insurance_qualified: true + hi_employee_temporary_disability_insurance_contribution: 5 + +- name: Hawaii does not pass a previous year of 13 weeks. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + employment_income: + 2025: 10_000 + 2026: 1_000 + weeks_worked: + 2025: 13 + 2026: 1 + weekly_hours_worked_before_lsr: + 2025: 40 + 2026: 40 + households: + household: + members: [person1] + state_code: HI + output: + hi_temporary_disability_insurance_qualified: false + hi_employee_temporary_disability_insurance_contribution: 0 + +- name: Hawaii does not pass a previous year under 20 hours a week. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + employment_income: + 2025: 10_000 + 2026: 1_000 + weeks_worked: + 2025: 52 + 2026: 1 + weekly_hours_worked_before_lsr: + 2025: 19 + 2026: 40 + households: + household: + members: [person1] + state_code: HI + output: + hi_temporary_disability_insurance_qualified: false + hi_employee_temporary_disability_insurance_contribution: 0 + +- name: Hawaii does not read unreported previous-year hours as a pass. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + employment_income: + 2025: 52_000 + 2026: 1_000 + weeks_worked: + 2025: 52 + 2026: 1 + weekly_hours_worked_before_lsr: + 2026: 40 + households: + household: + members: [person1] + state_code: HI + output: + hi_temporary_disability_insurance_qualified: false + hi_employee_temporary_disability_insurance_contribution: 0 + +- name: Hawaii passes on the simulation year when the previous year fails. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + employment_income: + 2025: 1_000 + 2026: 20_000 + weeks_worked: + 2025: 2 + 2026: 52 + weekly_hours_worked_before_lsr: + 2025: 40 + 2026: 40 + households: + household: + members: [person1] + state_code: HI + output: + hi_temporary_disability_insurance_qualified: true + # 0.5% * 20,000 = 100. + hi_employee_temporary_disability_insurance_contribution: 100 diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_wages.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_wages.yaml index e5f20e41f8a..cd0ca7d9c86 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_wages.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_wages.yaml @@ -74,8 +74,8 @@ traditional_401k_contributions: 24_500 hi_temporary_disability_insurance_wages: 75_500 -- name: Hawaii wages leave out deferrals in 2015, the first year the model holds a limit. - period: 2015 +- name: Hawaii wages leave out deferrals in 2004, the first year the model holds a limit. + period: 2004 absolute_error_margin: 0.01 input: age: 55 @@ -86,9 +86,33 @@ # 60,000 - 4,000 = 56,000. hi_temporary_disability_insurance_wages: 56_000 -- name: Hawaii wages keep deferrals in 2014, before the model holds a limit. +- name: Hawaii wages leave out only the deferral the 2004 limit allows. + period: 2004 + absolute_error_margin: 0.01 + input: + age: 40 + employment_income: 100_000 + traditional_401k_contributions_desired: 30_000 + state_code: HI + output: + # The 2004 limit under age 50 is 13,000: 100,000 - 13,000 = 87,000. + traditional_401k_contributions: 13_000 + hi_temporary_disability_insurance_wages: 87_000 + +- name: Hawaii wages leave out deferrals in 2014. period: 2014 absolute_error_margin: 0.01 + input: + employment_income: 60_000 + traditional_401k_contributions: 4_000 + state_code: HI + output: + # 60,000 - 4,000 = 56,000. + hi_temporary_disability_insurance_wages: 56_000 + +- name: Hawaii wages keep deferrals in 2003, before the model holds a limit. + period: 2003 + absolute_error_margin: 0.01 input: employment_income: 60_000 traditional_401k_contributions: 4_000 @@ -96,6 +120,28 @@ output: hi_temporary_disability_insurance_wages: 60_000 +- name: Hawaii wages leave out military basic pay. + period: 2026 + absolute_error_margin: 0.01 + input: + employment_income: 60_000 + military_basic_pay: 60_000 + state_code: HI + output: + # HRS 392-5(6): 60,000 - 60,000 = 0. + hi_temporary_disability_insurance_wages: 0 + +- name: Hawaii wages keep a service member's civilian pay. + period: 2026 + absolute_error_margin: 0.01 + input: + employment_income: 70_000 + military_basic_pay: 60_000 + state_code: HI + output: + # 70,000 - 60,000 = 10,000. + hi_temporary_disability_insurance_wages: 10_000 + - name: Hawaii wages leave out premiums in 2004. period: 2004 absolute_error_margin: 0.01 diff --git a/policyengine_us/tests/policy/baseline/household/expense/retirement/elective_deferral_limit.yaml b/policyengine_us/tests/policy/baseline/household/expense/retirement/elective_deferral_limit.yaml index 1b435e8471c..2f23757cd22 100644 --- a/policyengine_us/tests/policy/baseline/household/expense/retirement/elective_deferral_limit.yaml +++ b/policyengine_us/tests/policy/baseline/household/expense/retirement/elective_deferral_limit.yaml @@ -119,3 +119,62 @@ output: elective_deferral_limit: 32_500 traditional_401k_contributions: 32_500 + +# The expected values below come from the IRS cost-of-living table, 402(g)(1) +# and 414(v)(2)(B)(i), https://www.irs.gov/pub/irs-tege/cola-table.pdf. + +- name: Case 11, the 2004 limit applies. + period: 2004 + input: + age: 40 + traditional_401k_contributions_desired: 20_000 + output: + elective_deferral_limit: 13_000 + traditional_401k_contributions: 13_000 + +- name: Case 12, the 2004 limit adds the 2004 catch-up. + period: 2004 + input: + age: 55 + traditional_401k_contributions_desired: 20_000 + output: + # 13,000 + 3,000 = 16,000. + elective_deferral_limit: 16_000 + traditional_401k_contributions: 16_000 + +- name: Case 13, the 2009 limit applies. + period: 2009 + input: + age: 40 + output: + elective_deferral_limit: 16_500 + +- name: Case 14, the 2014 limit adds the 2014 catch-up. + period: 2014 + input: + age: 55 + output: + # 17,500 + 5,500 = 23,000. + elective_deferral_limit: 23_000 + +- name: Case 15, the 2017 limit applies. + period: 2017 + input: + age: 40 + output: + elective_deferral_limit: 18_000 + +- name: Case 16, the 2017 limit adds the 2017 catch-up. + period: 2017 + input: + age: 55 + output: + # 18,000 + 6,000 = 24,000. + elective_deferral_limit: 24_000 + +- name: Case 17, the limit rises to 18,500 in 2018. + period: 2018 + input: + age: 40 + output: + elective_deferral_limit: 18_500 diff --git a/policyengine_us/tests/policy/baseline/household/marginal_tax_rate_including_health_benefits.yaml b/policyengine_us/tests/policy/baseline/household/marginal_tax_rate_including_health_benefits.yaml index 9525270bad6..71e43a29510 100644 --- a/policyengine_us/tests/policy/baseline/household/marginal_tax_rate_including_health_benefits.yaml +++ b/policyengine_us/tests/policy/baseline/household/marginal_tax_rate_including_health_benefits.yaml @@ -43,8 +43,8 @@ state_fips: 15 # HI employment_income: 0 healthcare_benefit_value: 0 - output: - marginal_tax_rate_including_health_benefits: -0.0306 + output: + marginal_tax_rate_including_health_benefits: -0.0256 - name: Michigan No income and yes healthcare benefits absolute_error_margin: 0.01 diff --git a/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_qualified.py b/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_qualified.py index bdf1c1aa4b0..eb9890d41d7 100644 --- a/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_qualified.py +++ b/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_qualified.py @@ -8,10 +8,16 @@ class hi_temporary_disability_insurance_qualified(Variable): documentation = ( "HRS 392-43(b)(1) counts wages from a payroll period only if the prior " "52 weeks hold enough wages and enough weeks with enough paid hours. " - "The model holds no prior-year work history, so this annual proxy tests " - "the simulation year's wages, weeks worked and usual weekly hours. Zero " - "weeks or zero hours mean the value went unreported, so that part of " - "the test passes." + "This annual proxy passes a worker in either of two ways. The first " + "tests the simulation year's wages, weeks worked and usual weekly " + "hours, and there zero weeks or zero hours mean the value went " + "unreported, so that part of the test passes. The second tests the " + "previous year's weeks worked and usual weekly hours, and it needs " + "both on file. A single year of inputs leaves the previous year at " + "zero, so a worker with a short year and no history on file does not " + "pass. Usual weekly hours stand in for each week's hours, so a worker " + "whose usual hours fall under the minimum does not pass even with " + "enough qualifying weeks." ) definition_period = YEAR defined_for = StateCode.HI @@ -30,4 +36,15 @@ def formula(person, period, parameters): meets_wages = wages >= p.minimum_wages meets_weeks = (weeks == 0) | (weeks >= p.minimum_weeks) meets_hours = (hours == 0) | (hours >= p.minimum_weekly_hours) - return meets_wages & meets_weeks & meets_hours + passes_in_year = meets_wages & meets_weeks & meets_hours + # The previous year passes only on positive evidence, because zero + # there means no history on file. The minimum weeks and hours make + # 280 paid hours, which reach the minimum wages at about $1.43 an + # hour, so the wages part of the test needs no separate read. + last_year = period.last_year + weeks_last_year = person("weeks_worked", last_year) + hours_last_year = person("weekly_hours_worked_before_lsr", last_year) + passes_last_year = (weeks_last_year >= p.minimum_weeks) & ( + hours_last_year >= p.minimum_weekly_hours + ) + return passes_in_year | passes_last_year diff --git a/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_taxable_wages.py b/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_taxable_wages.py index a654e9f9737..db3ee500989 100644 --- a/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_taxable_wages.py +++ b/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_taxable_wages.py @@ -11,7 +11,8 @@ class hi_temporary_disability_insurance_taxable_wages(Variable): "cap multiplies the maximum weekly wage base by weeks worked, or by 52 " "when weeks worked went unreported. An even weekly wage reaches the " "largest amount the weekly cap allows, so this amount is an upper limit " - "for a worker with one employer." + "for a worker with one employer who passes the annual qualification " + "test." ) definition_period = YEAR unit = USD diff --git a/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_wages.py b/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_wages.py index 38ca933b356..32a75624efa 100644 --- a/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_wages.py +++ b/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_wages.py @@ -15,14 +15,18 @@ class hi_temporary_disability_insurance_wages(Variable): "no dependent care payment, so dependent care benefits stay in. The " "federal payroll wage base differs because Congress added rules for " "elective deferrals, cafeteria plans and dependent care that Hawaii did " - "not enact. Before 2015 retirement deferrals stay in, because the " - "model's elective deferral limit starts in 2015." + "not enact. HRS 392-5(6) leaves service for the United States " + "government out of employment, so military basic pay comes out too. " + "The model's convention puts basic pay inside employment income, and " + "a service member's civilian wages stay in. The model identifies no " + "other federal employees, so their pay stays in." ) definition_period = YEAR unit = USD defined_for = StateCode.HI reference = ( "https://data.capitol.hawaii.gov/hrscurrent/Vol07_Ch0346-0398/HRS0392/HRS_0392-0003.htm", + "https://data.capitol.hawaii.gov/hrscurrent/Vol07_Ch0346-0398/HRS0392/HRS_0392-0005.htm", "https://data.capitol.hawaii.gov/hrscurrent/Vol07_Ch0346-0398/HRS0383/HRS_0383-0011.htm", ) diff --git a/source_conflicts/candidates.yaml b/source_conflicts/candidates.yaml index 2b3ad08e551..470c015534f 100644 --- a/source_conflicts/candidates.yaml +++ b/source_conflicts/candidates.yaml @@ -3159,8 +3159,11 @@ candidates: citation: HRS 392-3 (wages), adopting HRS 383-11(1)(A), (1)(C), (4)(A) and (5) url: https://data.capitol.hawaii.gov/hrscurrent/Vol07_Ch0346-0398/HRS0383/HRS_0383-0011.htm - kind: regulation - citation: HAR 12-11-1, which takes wages from HRS 392-3 and says nothing on salary reductions + citation: HAR 12-11-1, which takes wages from HRS 392-3 url: https://labor.hawaii.gov/dcd/files/2012/11/TDI-Admin-Rules.pdf#page=5 + - kind: regulation + citation: HAR 12-11-47(a), on pay in kind given rather than deducted from money wages + url: https://labor.hawaii.gov/dcd/files/2012/11/TDI-Admin-Rules.pdf#page=17 - kind: guidance citation: Hawaii DLIR Unemployment Insurance Division, Handbook for Employers, on the wages that chapter 383 reaches url: https://labor.hawaii.gov/ui/test-handbook-for-employers/ @@ -3171,8 +3174,8 @@ candidates: stay in wages. other: The federal payroll wage base keeps elective deferrals in and takes cafeteria-plan amounts and dependent care out, through IRC 3306(r)(1)(A), (b)(5)(G) and (b)(13), which Hawaii did not enact. A second alternative treats every - salary reduction as the employee's own remuneration, so all of it stays in wages. The rule and the handbook address - neither alternative. + salary reduction as the employee's own remuneration, so all of it stays in wages. The rules address pay in kind but + name no plan contribution or salary reduction, and the handbook addresses neither alternative. example: In 2026, $60,000 of salary with a $4,000 traditional 401(k) deferral gives a $280 contribution under the statute, versus $300 on the federal base. $60,000 with $5,000 of dependent care benefits gives $300, versus $275 on the federal base. diff --git a/source_conflicts/sources/us-hi/har-12-11-excerpt.txt b/source_conflicts/sources/us-hi/har-12-11-excerpt.txt new file mode 100644 index 00000000000..36d1d6fc1b0 --- /dev/null +++ b/source_conflicts/sources/us-hi/har-12-11-excerpt.txt @@ -0,0 +1,5 @@ +Source: https://labor.hawaii.gov/dcd/files/2012/11/TDI-Admin-Rules.pdf +Short excerpts of HAR 12-11-1 (wages) and HAR 12-11-47(a) (remuneration in kind), copied from the official PDF. + +"Wages" shall be as defined in section 392-3, HRS. +in addition to or in lieu of (rather than as a deduction from) money wages diff --git a/source_conflicts/sources/us-hi/hrs-383-11-excerpt.txt b/source_conflicts/sources/us-hi/hrs-383-11-excerpt.txt index 360e1fc0bfa..6413e8a9dbc 100644 --- a/source_conflicts/sources/us-hi/hrs-383-11-excerpt.txt +++ b/source_conflicts/sources/us-hi/hrs-383-11-excerpt.txt @@ -1,6 +1,8 @@ Source: https://data.capitol.hawaii.gov/hrscurrent/Vol07_Ch0346-0398/HRS0383/HRS_0383-0011.htm -Short excerpts of HRS 383-11(4), (4)(A) and (5), copied from the official HTML page. +Short excerpts of HRS 383-11(1)(A), (1)(C), (4), (4)(A) and (5), copied from the official HTML page. -any payment by an employing unit to, or on behalf of, an individual -to a trust described in section 401(a) -without deduction from the remuneration +Retirement +Medical or hospitalization expenses +by an employing unit to, or on behalf of, an individual +trust described in section 401(a) +without deduction from From deca0e3638de3967bebb2ee5ece8575edf2136c8 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 9 Nov 2026 12:27:56 -0500 Subject: [PATCH 4/6] Scale Hawaii's TDI contribution by the employer's deduction share HRS 392-43(a) permits the employer to withhold the contribution without requiring it, like New York's PFL and DBL. Following main's #10285, the employee amount is the 0.5% limit times state_payroll_premium_deduction_share, which defaults to 1. Hawaii joins the programs whose employer cost the model doesn't hold, in the documentation of employee_state_payroll_tax, employer_additional_state_payroll_tax and the share input, and in the share invariants test. Three YAML cases cover a share of 0.5, 0 and 1.5. Co-Authored-By: Claude Opus 5.5 --- policyengine_us/programs.yaml | 2 +- ...roll_premium_deduction_share_invariants.py | 21 +++++----- ...ary_disability_insurance_contribution.yaml | 38 +++++++++++++++++++ ...orary_disability_insurance_contribution.py | 17 +++++---- .../employer_additional_state_payroll_tax.py | 5 ++- .../variables/input/employer_payroll.py | 16 ++++---- 6 files changed, 71 insertions(+), 28 deletions(-) diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index 77530634aba..d13eca8e4b3 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -2199,7 +2199,7 @@ programs: variable: hi_employee_temporary_disability_insurance_contribution parameter_prefix: gov.states.hi.tax.payroll.temporary_disability_insurance verified_start_year: 2004 - notes: Models the employee contribution at the HRS 392-43(a) limit; benefits and employer costs are out of scope. + notes: Models the employee contribution as the HRS 392-43(a) limit times the employer's deduction share, which defaults to 1; benefits and employer costs are out of scope. - id: il_fpp name: Illinois Family Planning Program diff --git a/policyengine_us/tests/core/test_state_payroll_premium_deduction_share_invariants.py b/policyengine_us/tests/core/test_state_payroll_premium_deduction_share_invariants.py index fa28532f644..63aaaa49f1d 100644 --- a/policyengine_us/tests/core/test_state_payroll_premium_deduction_share_invariants.py +++ b/policyengine_us/tests/core/test_state_payroll_premium_deduction_share_invariants.py @@ -3,10 +3,10 @@ In some state programs the law caps what the employer may deduct from wages and leaves it to decide how much of that to deduct: Colorado FAMLI, Delaware Paid Leave, Maine and Massachusetts PFML, Minnesota Paid Leave and the Vermont -Child Care Contribution, whose premium the statute puts on the employer; New -York PFL and DBL, which the employer is authorized but not required to -collect; and Washington PFML, whose employee share the employer may deduct up -to and may pay in any part. +Child Care Contribution, whose premium the statute puts on the employer; +Hawaii TDI and New York PFL and DBL, which the employer is authorized but not +required to collect; and Washington PFML, whose employee share the employer +may deduct up to and may pay in any part. `employer_state_payroll_premium_deduction_share` says how much of the permitted employee share the employer deducts (default 1). Hypothesis draws workers in those states, in states whose contributions are mandatory @@ -22,8 +22,8 @@ the share. 3. Where the employer side is modeled (CO, DE, MA, ME, VT, WA), employee plus employer premium does not depend on the share: the employer pays exactly - what it does not deduct. Minnesota and New York employer costs are not - modeled, so their employer amount is zero at any share. + what it does not deduct. Hawaii, Minnesota and New York employer costs are + not modeled, so their employer amount is zero at any share. 4. A larger share never lowers the employee amount or raises the employer amount. 5. With employer-level wage totals equal to the worker's own wages, capped at @@ -53,12 +53,13 @@ from policyengine_us.system import system YEARS = (2026, 2027) -OPTIONAL_STATES = ("CO", "DE", "MA", "ME", "MN", "NY", "VT", "WA") +OPTIONAL_STATES = ("CO", "DE", "HI", "MA", "ME", "MN", "NY", "VT", "WA") MANDATORY_STATES = ("CA", "CT", "NJ", "OR", "PA", "RI") EMPLOYER_MODELED_STATES = ("CO", "DE", "MA", "ME", "VT", "WA") OPTIONAL_EMPLOYEE_VARIABLES = ( "co_employee_famli_contribution", "de_employee_paid_leave_contribution", + "hi_employee_temporary_disability_insurance_contribution", "ma_employee_paid_leave_contribution", "me_employee_paid_leave_contribution", "mn_employee_paid_leave_contribution", @@ -67,8 +68,8 @@ "vt_employee_child_care_contribution", "wa_employee_paid_leave_contribution", ) -# Below the 2026 Social Security wage base, so every program's wage base is -# the worker's wages in both years. +# Below the 2026 Social Security wage base, so Massachusetts' wage base is the +# worker's wages in both years (property 6). UNCAPPED_WAGES = 184_500 SOCIAL_SECURITY_CAP = { year: system.parameters(f"{year}-01-01").gov.irs.payroll.social_security.cap @@ -174,7 +175,7 @@ def test_state_payroll_premium_deduction_share_invariants(rows): wages = np.array([row[1] for row in rows]) shares = np.clip(np.array([[row[3], row[4], 1.0] for row in rows]), 0, 1) modeled = np.isin(states, EMPLOYER_MODELED_STATES) - unmodeled = np.isin(states, ("MN", "NY")) + unmodeled = np.isin(states, ("HI", "MN", "NY")) uncapped = wages <= UNCAPPED_WAGES optional_by_year = {} employer_by_year = {} diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_employee_temporary_disability_insurance_contribution.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_employee_temporary_disability_insurance_contribution.yaml index c3b183457b5..9ab108bc940 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_employee_temporary_disability_insurance_contribution.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_employee_temporary_disability_insurance_contribution.yaml @@ -558,3 +558,41 @@ hi_temporary_disability_insurance_qualified: true # 0.5% * 20,000 = 100. hi_employee_temporary_disability_insurance_contribution: 100 + +- name: Hawaii scales the contribution by the employer's deduction share. + period: 2026 + absolute_error_margin: 0.01 + input: + employment_income: 60_000 + employer_state_payroll_premium_deduction_share: 0.5 + state_code: HI + output: + # HRS 392-43(a) lets the employer withhold up to 0.5% * 60,000 = 300 and + # leaves the amount to it. Withholding half gives 0.5 * 300 = 150. + hi_employee_temporary_disability_insurance_contribution: 150 + hi_employee_state_payroll_tax: 150 + employee_state_payroll_tax: 150 + +- name: Hawaii charges nothing when the employer withholds nothing. + period: 2026 + absolute_error_margin: 0.01 + input: + employment_income: 100_000 + employer_state_payroll_premium_deduction_share: 0 + state_code: HI + output: + # 52 * 1,500.21 = 78,010.92. + hi_temporary_disability_insurance_taxable_wages: 78_010.92 + hi_employee_temporary_disability_insurance_contribution: 0 + employee_state_payroll_tax: 0 + +- name: Hawaii holds a deduction share above 1 at the 0.5% limit. + period: 2026 + absolute_error_margin: 0.01 + input: + employment_income: 100_000 + employer_state_payroll_premium_deduction_share: 1.5 + state_code: HI + output: + # The employer may withhold at most 0.5% * 78,010.92 = 390.05. + hi_employee_temporary_disability_insurance_contribution: 390.05 diff --git a/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_employee_temporary_disability_insurance_contribution.py b/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_employee_temporary_disability_insurance_contribution.py index 2ef4f8075a0..4d5aaeaa358 100644 --- a/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_employee_temporary_disability_insurance_contribution.py +++ b/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_employee_temporary_disability_insurance_contribution.py @@ -6,12 +6,14 @@ class hi_employee_temporary_disability_insurance_contribution(Variable): entity = Person label = "Hawaii employee temporary disability insurance contribution" documentation = ( - "Employee-side Hawaii temporary disability insurance contribution at " - "the statutory limit. HRS 392-43(a) lets an employer withhold half the " - "cost of coverage, up to the employee rate times weekly wages, and " - "makes the employer pay the balance. Coverage cost varies by employer " - "and the model holds no premium data, so this assumes the employer " - "withholds the full rate limit." + "Employee-side Hawaii temporary disability insurance contribution. " + "HRS 392-43(a) authorizes the employer, without requiring it, to " + "withhold half the cost of coverage, up to the employee rate times " + "weekly wages, and makes the employer pay the balance. Coverage cost " + "varies by employer and the model holds no premium data, so it takes " + "the rate limit as the permitted amount; this is that amount times " + "state_payroll_premium_deduction_share. The employer's cost of " + "coverage is not modeled." ) definition_period = YEAR unit = USD @@ -25,4 +27,5 @@ def formula(person, period, parameters): taxable_wages = person( "hi_temporary_disability_insurance_taxable_wages", period ) - return p.employee_rate * taxable_wages + permitted = p.employee_rate * taxable_wages + return permitted * person("state_payroll_premium_deduction_share", period) diff --git a/policyengine_us/variables/gov/states/tax/payroll/employer_additional_state_payroll_tax.py b/policyengine_us/variables/gov/states/tax/payroll/employer_additional_state_payroll_tax.py index 0bbb5e52be0..6ccdbd3444a 100644 --- a/policyengine_us/variables/gov/states/tax/payroll/employer_additional_state_payroll_tax.py +++ b/policyengine_us/variables/gov/states/tax/payroll/employer_additional_state_payroll_tax.py @@ -22,8 +22,9 @@ class employer_additional_state_payroll_tax(Variable): "Employer-side state payroll taxes and payroll-funded contributions " "other than state unemployment insurance, including any part of a " "permitted employee premium the employer does not deduct " - "(employer_state_payroll_premium_deduction_share). Minnesota Paid " - "Leave and the New York PFL and DBL employer costs are not modeled." + "(employer_state_payroll_premium_deduction_share). Hawaii TDI, " + "Minnesota Paid Leave and the New York PFL and DBL employer costs are " + "not modeled." ) definition_period = YEAR unit = USD diff --git a/policyengine_us/variables/input/employer_payroll.py b/policyengine_us/variables/input/employer_payroll.py index 48b93d8f61e..c996f59e1c1 100644 --- a/policyengine_us/variables/input/employer_payroll.py +++ b/policyengine_us/variables/input/employer_payroll.py @@ -35,14 +35,14 @@ class employer_state_payroll_premium_deduction_share(Variable): "Share of the largest employee premium the law lets the employer " "deduct from wages that the employer actually deducts, for state " "programs where the law caps the deduction and leaves the amount to " - "the employer: Colorado FAMLI, Delaware Paid Leave, Maine and " - "Massachusetts Paid Family and Medical Leave, Minnesota Paid Leave, " - "New York Paid Family Leave and Disability Benefits, the Vermont " - "Child Care Contribution and Washington Paid Family and Medical " - "Leave. 1, the default, deducts the full permitted share; 0 deducts " - "nothing. Outside Minnesota and New York, whose employer cost is not " - "modeled, the employer-side variables carry any part the employer " - "does not deduct. For the state paid family and medical leave funds, " + "the employer: Colorado FAMLI, Delaware Paid Leave, Hawaii TDI, Maine " + "and Massachusetts Paid Family and Medical Leave, Minnesota Paid " + "Leave, New York Paid Family Leave and Disability Benefits, the " + "Vermont Child Care Contribution and Washington Paid Family and " + "Medical Leave. 1, the default, deducts the full permitted share; 0 " + "deducts nothing. Outside Hawaii, Minnesota and New York, whose " + "employer cost is not modeled, the employer-side variables carry any " + "part the employer does not deduct. For the state paid family and medical leave funds, " "the federal treatment of an employer's payment of the employee " "share as the employee's wages (Rev. Rul. 2025-4; Notice 2026-6) is " "not modeled. Values outside 0 to 1 are treated as the nearer bound." From 3d431812a53819145d8bd6cdf60d15c9f9d69399 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Tue, 10 Nov 2026 15:08:17 -0500 Subject: [PATCH 5/6] Require previous-year Hawaii wages in the TDI wages test The previous-year route of hi_temporary_disability_insurance_qualified now also needs that year's wages, less the payments the Hawaii wage definition leaves out, to reach the $400 minimum, so a prior year of active-duty pay or self-employment no longer passes. The route reads employment_income_before_lsr, so it calculates no labor supply response for the earlier year, and before 2015 it reads only values provided for that year. Also: - describe the model's two military_basic_pay conventions in the Hawaii wage; - describe Hawaii's lesser-of limit in the deduction share input; - add federal limit cases for each year from 2004 to 2018 in which the 402(g)(1) or 414(v) amount changes and no case existed; - make the backdating-floor probe scan the parameter tree; - cite IRC 3306 in the source-conflict candidate and narrow its handbook claim; - enter previous-year pay in YAML as employment_income_before_lsr, because the YAML runner builds its simulation without Simulation's input move. Co-Authored-By: Claude Opus 5.5 --- .../test_hi_temporary_disability_insurance.py | 65 +++++- .../medicare/test_irmaa_first_modeled_year.py | 17 +- .../retirement/k401_catch_up_limit.yaml | 21 ++ ...ary_disability_insurance_contribution.yaml | 204 +++++++++++++++++- .../retirement/elective_deferral_limit.yaml | 42 ++++ ...emporary_disability_insurance_qualified.py | 63 +++++- ...hi_temporary_disability_insurance_wages.py | 13 +- .../variables/input/employer_payroll.py | 9 +- source_conflicts/candidates.yaml | 12 +- 9 files changed, 408 insertions(+), 38 deletions(-) diff --git a/policyengine_us/tests/core/test_hi_temporary_disability_insurance.py b/policyengine_us/tests/core/test_hi_temporary_disability_insurance.py index 072a4677fa1..d484fc56173 100644 --- a/policyengine_us/tests/core/test_hi_temporary_disability_insurance.py +++ b/policyengine_us/tests/core/test_hi_temporary_disability_insurance.py @@ -13,7 +13,8 @@ 3. Properties across drawn batches of workers: bounds, monotonicity in wages, state masking, and aggregation to the tax unit. 4. Qualification. Previous-year history can only add workers, and exactly the - workers whose history clears the weeks and hours minimums. + workers whose history clears the wages, weeks and hours minimums, when the + simulation year's weeks and hours are on file. 5. Net income. Removing the contribution raises household net income by exactly the contribution. @@ -37,6 +38,7 @@ from policyengine_us import Simulation from policyengine_us.system import system as SYSTEM +from policyengine_us.tools.parameters import FIRST_MODELED_YEAR CONTRIBUTION = "hi_employee_temporary_disability_insurance_contribution" QUALIFIED = "hi_temporary_disability_insurance_qualified" @@ -114,7 +116,10 @@ def simulate(year, workers, **overrides): person = f"person_{i}" situation["people"][person] = { "age": {period: 40}, - "employment_income": {period: worker["wages"]}, + "employment_income": { + previous: worker.get("previous_wages", 0), + period: worker["wages"], + }, "pre_tax_health_insurance_premiums": {period: worker.get("premiums", 0)}, "military_basic_pay": {period: worker.get("military", 0)}, "dependent_care_employer_benefits": { @@ -236,6 +241,7 @@ def test_contribution_properties(year, batch, bump): batch=st.lists( st.tuples( workers, + st.sampled_from([0, 399, 400, 10_000]), st.sampled_from([0, 13, 14, 52]), st.sampled_from([0, 19, 20, 40]), ), @@ -244,24 +250,69 @@ def test_contribution_properties(year, batch, bump): ), ) def test_previous_year_history_only_adds_workers_who_clear_it(year, batch): + # simulate() enters the simulation year's weeks and hours, zero included, + # so no history carries into that year. period = str(year) - alone = simulate(year, [worker for worker, _, _ in batch]).calculate( + alone = simulate(year, [worker for worker, _, _, _ in batch]).calculate( QUALIFIED, period ) with_history = simulate( year, [ - {**worker, "previous_weeks": weeks, "previous_hours": hours} - for worker, weeks, hours in batch + { + **worker, + "previous_wages": wages, + "previous_weeks": weeks, + "previous_hours": hours, + } + for worker, wages, weeks, hours in batch ], ).calculate(QUALIFIED, period) - in_hawaii = np.array([worker["state"] == "HI" for worker, _, _ in batch]) - clears = np.array([weeks >= 14 and hours >= 20 for _, weeks, hours in batch]) + in_hawaii = np.array([worker["state"] == "HI" for worker, _, _, _ in batch]) + clears = np.array( + [ + wages >= 400 and weeks >= 14 and hours >= 20 + for _, wages, weeks, hours in batch + ] + ) assert (with_history[in_hawaii] == (alone | clears)[in_hawaii]).all() assert not with_history[~in_hawaii].any() +def test_history_before_the_first_modeled_year_is_read_not_calculated(): + # A short 2015 passes on 2014 history provided as inputs, and 2014 keeps + # only those inputs: the model calculates and caches nothing before 2015. + year = FIRST_MODELED_YEAR + sim = simulate( + year, + [ + { + "wages": 900, + "weeks": 1, + "hours": 40, + "previous_wages": 52_000, + "previous_weeks": 52, + "previous_hours": 40, + "state": "HI", + } + ], + ) + assert sim.calculate(QUALIFIED, str(year)).tolist() == [True] + early = { + name + for population in sim.populations.values() + for name, holder in population._holders.items() + for known in holder.get_known_periods() + if known.unit != "eternity" and known.start.year < FIRST_MODELED_YEAR + } + assert early == { + "employment_income_before_lsr", + "weeks_worked", + "weekly_hours_worked_before_lsr", + } + + # At least 14 weeks of at least 30 dollars clears the $400 and 14-week test. weekly_wages = st.lists(st.integers(30, 4_000), min_size=14, max_size=52) diff --git a/policyengine_us/tests/policy/baseline/gov/hhs/medicare/test_irmaa_first_modeled_year.py b/policyengine_us/tests/policy/baseline/gov/hhs/medicare/test_irmaa_first_modeled_year.py index adf1ee8df81..d925275369b 100644 --- a/policyengine_us/tests/policy/baseline/gov/hhs/medicare/test_irmaa_first_modeled_year.py +++ b/policyengine_us/tests/policy/baseline/gov/hhs/medicare/test_irmaa_first_modeled_year.py @@ -56,9 +56,20 @@ def cached_periods_before_first_modeled_year(simulation): def test_first_modeled_year_is_the_backdating_floor(): # The constant must match how far CountryTaxBenefitSystem backdates: a # parameter first dated after the floor is defined from it, not before. - limit = SYSTEM.parameters.gov.irs.gross_income.retirement_contributions.limit - assert limit.children["ira"](f"{FIRST_MODELED_YEAR}-01-01") > 0 - assert limit.children["ira"](f"{FIRST_MODELED_YEAR - 1}-12-31") is None + # So every parameter starts by the floor, the latest start is the floor + # itself, and a parameter starting there has no value the day before. + # Scanning the tree keeps the check from naming a parameter that a later + # backfill could move. + floor = f"{FIRST_MODELED_YEAR}-01-01" + leaves = [ + parameter + for parameter in SYSTEM.parameters.get_descendants() + if getattr(parameter, "values_list", None) + ] + starts = [leaf.values_list[-1].instant_str for leaf in leaves] + assert max(starts) == floor + starts_at_floor = leaves[starts.index(floor)] + assert starts_at_floor(f"{FIRST_MODELED_YEAR - 1}-12-31") is None @pytest.mark.parametrize("year", UNMODELED_LAG_YEARS) diff --git a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/retirement/k401_catch_up_limit.yaml b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/retirement/k401_catch_up_limit.yaml index 0b75d6afefd..b9ea1d56daf 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/retirement/k401_catch_up_limit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/retirement/k401_catch_up_limit.yaml @@ -170,3 +170,24 @@ age: 50 output: k401_catch_up_limit: 6_000 + +- name: Case 24, age 50 in 2005 receives the 2005 standard limit. + period: 2005 + input: + age: 50 + output: + k401_catch_up_limit: 4_000 + +- name: Case 25, age 50 in 2006 receives the 2006 standard limit. + period: 2006 + input: + age: 50 + output: + k401_catch_up_limit: 5_000 + +- name: Case 26, age 50 in 2015 receives the 2015 standard limit. + period: 2015 + input: + age: 50 + output: + k401_catch_up_limit: 6_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_employee_temporary_disability_insurance_contribution.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_employee_temporary_disability_insurance_contribution.yaml index 9ab108bc940..b9538e47fb9 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_employee_temporary_disability_insurance_contribution.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_employee_temporary_disability_insurance_contribution.yaml @@ -396,14 +396,18 @@ # 0.5% * (70,000 - 60,000) = 50. hi_employee_temporary_disability_insurance_contribution: 50 +# The cases below enter the previous year's pay as employment_income_before_lsr, +# which the previous-year route reads. Simulation moves employment_income inputs +# there, but the YAML runner builds its simulation without that move. - name: Hawaii counts a short final year when the previous year passes. period: 2026 absolute_error_margin: 0.01 input: people: person1: - employment_income: + employment_income_before_lsr: 2025: 52_000 + employment_income: 2026: 1_000 weeks_worked: 2025: 52 @@ -426,8 +430,9 @@ input: people: person1: - employment_income: + employment_income_before_lsr: 2025: 52_000 + employment_income: 2026: 300 weeks_worked: 2025: 52 @@ -450,8 +455,9 @@ input: people: person1: - employment_income: + employment_income_before_lsr: 2025: 10_000 + employment_income: 2026: 1_000 weeks_worked: 2025: 14 @@ -473,8 +479,9 @@ input: people: person1: - employment_income: + employment_income_before_lsr: 2025: 10_000 + employment_income: 2026: 1_000 weeks_worked: 2025: 13 @@ -496,8 +503,9 @@ input: people: person1: - employment_income: + employment_income_before_lsr: 2025: 10_000 + employment_income: 2026: 1_000 weeks_worked: 2025: 52 @@ -519,8 +527,9 @@ input: people: person1: - employment_income: + employment_income_before_lsr: 2025: 52_000 + employment_income: 2026: 1_000 weeks_worked: 2025: 52 @@ -541,8 +550,9 @@ input: people: person1: - employment_income: + employment_income_before_lsr: 2025: 1_000 + employment_income: 2026: 20_000 weeks_worked: 2025: 2 @@ -559,6 +569,186 @@ # 0.5% * 20,000 = 100. hi_employee_temporary_disability_insurance_contribution: 100 +- name: Hawaii does not pass a previous year with wages under 400. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + employment_income_before_lsr: + 2025: 399 + employment_income: + 2026: 1_000 + weeks_worked: + 2025: 52 + 2026: 1 + weekly_hours_worked_before_lsr: + 2025: 40 + 2026: 40 + households: + household: + members: [person1] + state_code: HI + output: + hi_temporary_disability_insurance_qualified: false + hi_employee_temporary_disability_insurance_contribution: 0 + +- name: Hawaii passes a previous year at exactly 400 of wages. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + employment_income_before_lsr: + 2025: 400 + employment_income: + 2026: 1_000 + weeks_worked: + 2025: 14 + 2026: 1 + weekly_hours_worked_before_lsr: + 2025: 20 + 2026: 40 + households: + household: + members: [person1] + state_code: HI + output: + hi_temporary_disability_insurance_qualified: true + # 0.5% * 1,000 = 5. + hi_employee_temporary_disability_insurance_contribution: 5 + +- name: Hawaii does not pass a previous year of active-duty pay. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + employment_income_before_lsr: + 2025: 60_000 + employment_income: + 2026: 1_000 + military_basic_pay: + 2025: 60_000 + 2026: 0 + weeks_worked: + 2025: 52 + 2026: 1 + weekly_hours_worked_before_lsr: + 2025: 40 + 2026: 40 + households: + household: + members: [person1] + state_code: HI + output: + # 2025's 52 weeks were federal service, so 2025 holds no Hawaii wages. + hi_temporary_disability_insurance_qualified: false + hi_employee_temporary_disability_insurance_contribution: 0 + +- name: Hawaii does not pass a previous year of self-employment. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + self_employment_income: + 2025: 50_000 + 2026: 0 + employment_income_before_lsr: + 2025: 0 + employment_income: + 2026: 10_000 + weeks_worked: + 2025: 52 + 2026: 10 + weekly_hours_worked_before_lsr: + 2025: 40 + 2026: 40 + households: + household: + members: [person1] + state_code: HI + output: + hi_temporary_disability_insurance_qualified: false + hi_employee_temporary_disability_insurance_contribution: 0 + +- name: Hawaii leaves a previous-year deferral out of the previous year's wages. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: + 2025: 40 + 2026: 41 + employment_income_before_lsr: + 2025: 10_000 + employment_income: + 2026: 1_000 + traditional_401k_contributions_desired: + 2025: 9_700 + 2026: 0 + weeks_worked: + 2025: 52 + 2026: 1 + weekly_hours_worked_before_lsr: + 2025: 40 + 2026: 40 + households: + household: + members: [person1] + state_code: HI + output: + # 10,000 - 9,700 = 300 of 2025 wages, under the 400 minimum. + hi_temporary_disability_insurance_qualified: false + hi_employee_temporary_disability_insurance_contribution: 0 + +- name: Hawaii carries earlier-year hours into a simulation year that enters none. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + employment_income: + 2026: 10_000 + weekly_hours_worked_before_lsr: + 2025: 19 + households: + household: + members: [person1] + state_code: HI + output: + # The 2025 hours carry into 2026, where 19 hours fail the 20-hour minimum, + # and 2025 holds no wages to pass on. + hi_temporary_disability_insurance_qualified: false + hi_employee_temporary_disability_insurance_contribution: 0 + +- name: Hawaii counts provided 2014 history for a short 2015. + period: 2015 + absolute_error_margin: 0.01 + input: + people: + person1: + employment_income_before_lsr: + 2014: 52_000 + employment_income: + 2015: 900 + weeks_worked: + 2014: 52 + 2015: 1 + weekly_hours_worked_before_lsr: + 2014: 40 + 2015: 40 + households: + household: + members: [person1] + state_code: HI + output: + hi_temporary_disability_insurance_qualified: true + # 900 falls under the 951.23 cap for one week: 0.5% * 900 = 4.50. + hi_employee_temporary_disability_insurance_contribution: 4.50 + - name: Hawaii scales the contribution by the employer's deduction share. period: 2026 absolute_error_margin: 0.01 diff --git a/policyengine_us/tests/policy/baseline/household/expense/retirement/elective_deferral_limit.yaml b/policyengine_us/tests/policy/baseline/household/expense/retirement/elective_deferral_limit.yaml index 2f23757cd22..a2d43d82c75 100644 --- a/policyengine_us/tests/policy/baseline/household/expense/retirement/elective_deferral_limit.yaml +++ b/policyengine_us/tests/policy/baseline/household/expense/retirement/elective_deferral_limit.yaml @@ -178,3 +178,45 @@ age: 40 output: elective_deferral_limit: 18_500 + +- name: Case 18, the limit rises to 14,000 in 2005. + period: 2005 + input: + age: 40 + output: + elective_deferral_limit: 14_000 + +- name: Case 19, the limit rises to 15,000 in 2006. + period: 2006 + input: + age: 40 + output: + elective_deferral_limit: 15_000 + +- name: Case 20, the limit rises to 15,500 in 2007. + period: 2007 + input: + age: 40 + output: + elective_deferral_limit: 15_500 + +- name: Case 21, the limit rises to 17,000 in 2012. + period: 2012 + input: + age: 40 + output: + elective_deferral_limit: 17_000 + +- name: Case 22, the limit rises to 17,500 in 2013. + period: 2013 + input: + age: 40 + output: + elective_deferral_limit: 17_500 + +- name: Case 23, the limit rises to 18,000 in 2015. + period: 2015 + input: + age: 40 + output: + elective_deferral_limit: 18_000 diff --git a/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_qualified.py b/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_qualified.py index eb9890d41d7..e4093b60ea5 100644 --- a/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_qualified.py +++ b/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_qualified.py @@ -1,4 +1,36 @@ from policyengine_us.model_api import * +from policyengine_us.tools.parameters import FIRST_MODELED_YEAR + + +def previous_year_wages(person, period, parameters): + """Hawaii TDI wages in the year before ``period``. + + These follow hi_temporary_disability_insurance_wages without its state + test, since a household's state is often entered for the simulation year + alone. Pay comes from employment_income_before_lsr, where situations and + datasets store employment income, so the earlier year calculates no labor + supply response. Before the first modeled year, only values provided for + that year count, so nothing is calculated or cached for an unmodeled year. + """ + last_year = period.last_year + excluded = parameters( + last_year + ).gov.states.hi.tax.payroll.temporary_disability_insurance.excluded_payments + if last_year.start.year >= FIRST_MODELED_YEAR: + pay = person("employment_income_before_lsr", last_year) + excluded_pay = add(person, last_year, excluded) + else: + simulation = person.simulation + + def provided(variable): + value = simulation.get_holder(variable).get_array( + last_year, simulation.branch_name + ) + return person.empty_array() if value is None else value + + pay = provided("employment_income_before_lsr") + excluded_pay = sum(provided(variable) for variable in excluded) + return max_(pay - excluded_pay, 0) class hi_temporary_disability_insurance_qualified(Variable): @@ -12,12 +44,18 @@ class hi_temporary_disability_insurance_qualified(Variable): "tests the simulation year's wages, weeks worked and usual weekly " "hours, and there zero weeks or zero hours mean the value went " "unreported, so that part of the test passes. The second tests the " - "previous year's weeks worked and usual weekly hours, and it needs " - "both on file. A single year of inputs leaves the previous year at " - "zero, so a worker with a short year and no history on file does not " - "pass. Usual weekly hours stand in for each week's hours, so a worker " - "whose usual hours fall under the minimum does not pass even with " - "enough qualifying weeks." + "previous year's wages, weeks worked and usual weekly hours, and it " + "needs all three on file. A single year of inputs leaves the previous " + "year at zero, so a worker with a short year and no history on file " + "does not pass. The previous year's wages leave out what the wage " + "definition leaves out, such as military basic pay, but skip its " + "state test, so a worker new to Hawaii can pass on wages earned " + "elsewhere. They come from employment_income_before_lsr, and before " + "2015, the first year the model covers, only values provided for that " + "year count. Weeks and hours count all work, self-employment " + "included. Usual weekly hours stand in for each week's hours, so a " + "worker whose usual hours fall under the minimum does not pass even " + "with enough qualifying weeks." ) definition_period = YEAR defined_for = StateCode.HI @@ -38,13 +76,16 @@ def formula(person, period, parameters): meets_hours = (hours == 0) | (hours >= p.minimum_weekly_hours) passes_in_year = meets_wages & meets_weeks & meets_hours # The previous year passes only on positive evidence, because zero - # there means no history on file. The minimum weeks and hours make - # 280 paid hours, which reach the minimum wages at about $1.43 an - # hour, so the wages part of the test needs no separate read. + # there means no history on file. Weeks and hours count all work, so + # that year's wages must also clear the minimum: a year of military + # basic pay or self-employment holds no Hawaii covered wages. last_year = period.last_year weeks_last_year = person("weeks_worked", last_year) hours_last_year = person("weekly_hours_worked_before_lsr", last_year) - passes_last_year = (weeks_last_year >= p.minimum_weeks) & ( - hours_last_year >= p.minimum_weekly_hours + wages_last_year = previous_year_wages(person, period, parameters) + passes_last_year = ( + (wages_last_year >= p.minimum_wages) + & (weeks_last_year >= p.minimum_weeks) + & (hours_last_year >= p.minimum_weekly_hours) ) return passes_in_year | passes_last_year diff --git a/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_wages.py b/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_wages.py index 32a75624efa..0d4146e6c31 100644 --- a/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_wages.py +++ b/policyengine_us/variables/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_temporary_disability_insurance_wages.py @@ -17,9 +17,16 @@ class hi_temporary_disability_insurance_wages(Variable): "elective deferrals, cafeteria plans and dependent care that Hawaii did " "not enact. HRS 392-5(6) leaves service for the United States " "government out of employment, so military basic pay comes out too. " - "The model's convention puts basic pay inside employment income, and " - "a service member's civilian wages stay in. The model identifies no " - "other federal employees, so their pay stays in." + "This variable reads military_basic_pay as the part of " + "employment_income that is basic pay, as federal income tax does, " + "since it taxes basic pay only through employment_income, and as the " + "Virginia and Indiana subtractions do. Hawaii LIHEAP instead reads " + "basic pay as an input apart from employment_income, so a household " + "that enters it that way has it subtracted from civilian wages here. " + "A dataset that leaves military_basic_pay empty charges active-duty " + "pay recorded as wages. Other military pay, such as special pays, " + "stays in. The model identifies no other federal employees, so their " + "pay stays in." ) definition_period = YEAR unit = USD diff --git a/policyengine_us/variables/input/employer_payroll.py b/policyengine_us/variables/input/employer_payroll.py index c996f59e1c1..43a41eb5a2f 100644 --- a/policyengine_us/variables/input/employer_payroll.py +++ b/policyengine_us/variables/input/employer_payroll.py @@ -40,9 +40,12 @@ class employer_state_payroll_premium_deduction_share(Variable): "Leave, New York Paid Family Leave and Disability Benefits, the " "Vermont Child Care Contribution and Washington Paid Family and " "Medical Leave. 1, the default, deducts the full permitted share; 0 " - "deducts nothing. Outside Hawaii, Minnesota and New York, whose " - "employer cost is not modeled, the employer-side variables carry any " - "part the employer does not deduct. For the state paid family and medical leave funds, " + "deducts nothing. Hawaii permits the lesser of half the cost of " + "coverage and 0.5% of wages. The model holds no cost and uses 0.5%, " + "so there a share of 1 gives an upper bound. Outside Hawaii, " + "Minnesota and New York, whose employer cost is not modeled, the " + "employer-side variables carry any part the employer does not " + "deduct. For the state paid family and medical leave funds, " "the federal treatment of an employer's payment of the employee " "share as the employee's wages (Rev. Rul. 2025-4; Notice 2026-6) is " "not modeled. Values outside 0 to 1 are treated as the nearer bound." diff --git a/source_conflicts/candidates.yaml b/source_conflicts/candidates.yaml index 208072f1538..9340f1a6149 100644 --- a/source_conflicts/candidates.yaml +++ b/source_conflicts/candidates.yaml @@ -3209,15 +3209,19 @@ candidates: - kind: guidance citation: Hawaii DLIR Unemployment Insurance Division, Handbook for Employers, on the wages that chapter 383 reaches url: https://labor.hawaii.gov/ui/test-handbook-for-employers/ + - kind: statute + citation: IRC 3306(b)(5)(D), (b)(5)(G), (b)(13) and (r)(1), the federal comparison + url: https://www.law.cornell.edu/uscode/text/26/3306 readings: statute: A salary-reduction amount the employer pays under a medical or retirement plan, or to a section 401(a) trust, is a listed payment, so it falls outside wages. HRS 383-11(5) limits one paragraph to payments without a deduction from pay, and paragraphs (1) and (4) carry no such limit. The list names no dependent care payment, so those benefits stay in wages. - other: The federal payroll wage base keeps elective deferrals in and takes cafeteria-plan amounts and dependent care - out, through IRC 3306(r)(1)(A), (b)(5)(G) and (b)(13), which Hawaii did not enact. A second alternative treats every - salary reduction as the employee's own remuneration, so all of it stays in wages. The rules address pay in kind but - name no plan contribution or salary reduction, and the handbook addresses neither alternative. + other: The federal payroll wage base keeps 401(k) deferrals in through IRC 3306(r)(1)(A) and 403(b) salary reductions + in through 3306(b)(5)(D), and takes cafeteria-plan amounts and dependent care out through 3306(b)(5)(G) and (b)(13). + Hawaii enacted none of these. A second alternative treats every salary reduction as the employee's own remuneration, + so all of it stays in wages. The rules address pay in kind but name no plan contribution or salary reduction. The + handbook lists payments to welfare plans among its exclusions but doesn't address salary reductions. example: In 2026, $60,000 of salary with a $4,000 traditional 401(k) deferral gives a $280 contribution under the statute, versus $300 on the federal base. $60,000 with $5,000 of dependent care benefits gives $300, versus $275 on the federal base. From c1e0406d83fde8bb6c6b75c34668ea33114c0ced Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Wed, 11 Nov 2026 01:10:28 -0500 Subject: [PATCH 6/6] Base Hawaii's TDI wage cap on the average that HRS 383-61(b) rounds HRS 392-43(b)(2) sets the maximum weekly wage base from the State's average annual wage as HRS 383-61(b) determines it, and 383-61(b) rounds that average to the nearest hundred dollars. DLIR's TDI notices apply 1.21 / 52 to the average before rounding, so each published base differs from the statutory one by up to $1.15. Follow the statute: each year's base is the notice's average rounded to $100, times 1.21 / 52, rounded once to the cent (2026: 64,500 * 1.21 / 52 = 1,500.87, where DLIR publishes 1,500.21). Record the choice as source conflict us-hi:tdi-wage-base-rounded-average in source_conflicts/candidates.yaml with excerpts of both sections and the 2026 notice, and update the YAML cases, the Python property tests and the changelog to the statutory bases. Co-Authored-By: Claude Opus 5.5 --- .../hi-tdi-employee-contribution.added.md | 2 +- .../maximum_weekly_wage_base.yaml | 58 ++++---- .../test_hi_temporary_disability_insurance.py | 42 ++++-- .../tests/core/test_payroll_contributions.py | 6 +- ...ary_disability_insurance_contribution.yaml | 126 +++++++++--------- source_conflicts/candidates.yaml | 46 +++++++ ...dlir-tdi-2026-wage-base-notice-excerpt.txt | 6 + .../sources/us-hi/hrs-383-61-excerpt.txt | 5 + .../sources/us-hi/hrs-392-43-excerpt.txt | 5 + 9 files changed, 193 insertions(+), 103 deletions(-) create mode 100644 source_conflicts/sources/us-hi/dlir-tdi-2026-wage-base-notice-excerpt.txt create mode 100644 source_conflicts/sources/us-hi/hrs-383-61-excerpt.txt create mode 100644 source_conflicts/sources/us-hi/hrs-392-43-excerpt.txt diff --git a/changelog.d/hi-tdi-employee-contribution.added.md b/changelog.d/hi-tdi-employee-contribution.added.md index 8a3851578da..3c4081244ec 100644 --- a/changelog.d/hi-tdi-employee-contribution.added.md +++ b/changelog.d/hi-tdi-employee-contribution.added.md @@ -1 +1 @@ -Add Hawaii's employee temporary disability insurance contribution under HRS 392-43, with the published maximum weekly wage base for 2004 through 2026, and count it in employee payroll tax and household net income. +Add Hawaii's employee temporary disability insurance contribution under HRS 392-43, with the maximum weekly wage base for 2004 through 2026 computed from the state average annual wage that HRS 383-61(b) rounds to $100, and count it in employee payroll tax and household net income. diff --git a/policyengine_us/parameters/gov/states/hi/tax/payroll/temporary_disability_insurance/maximum_weekly_wage_base.yaml b/policyengine_us/parameters/gov/states/hi/tax/payroll/temporary_disability_insurance/maximum_weekly_wage_base.yaml index 6eb9ecb9f9e..73b83c7a714 100644 --- a/policyengine_us/parameters/gov/states/hi/tax/payroll/temporary_disability_insurance/maximum_weekly_wage_base.yaml +++ b/policyengine_us/parameters/gov/states/hi/tax/payroll/temporary_disability_insurance/maximum_weekly_wage_base.yaml @@ -1,32 +1,38 @@ description: Hawaii counts weekly wages up to this amount toward the employee temporary disability insurance contribution. values: # Zero marks years before the first notice read (2004), not a law without a - # wage base. The Director publishes one amount a year under HRS 392-43(b)(2): - # the State's average annual wage / 52 * 1.21. + # wage base. HRS 392-43(b)(2) sets the base at one fifty-second of the State's + # average annual wage as HRS 383-61(b) determines it for the preceding year, + # times 1.21, and HRS 383-61(b) computes that average rounded to the nearest + # hundred dollars. Each value takes the average from that year's DLIR notice, + # rounds it to $100, multiplies by 1.21 / 52 and rounds once to the cent. For + # 2026, 64,471.82 rounds to 64,500, and 64,500 * 1.21 / 52 = 1,500.87. DLIR's + # notices use the average before rounding (1,500.21 for 2026), so the values + # here run from 0.84 below to 1.15 above the published bases. 0000-01-01: 0 - 2004-01-01: 720.70 - 2005-01-01: 752.44 - 2006-01-01: 791.70 - 2007-01-01: 820.26 - 2008-01-01: 842.56 - 2009-01-01: 877.69 - 2010-01-01: 901.70 - 2011-01-01: 884.29 - 2012-01-01: 903.28 - 2013-01-01: 921.78 - 2014-01-01: 940.05 - 2015-01-01: 951.23 - 2016-01-01: 982.36 - 2017-01-01: 1_023.31 - 2018-01-01: 1_068.62 - 2019-01-01: 1_088.08 - 2020-01-01: 1_119.44 - 2021-01-01: 1_102.90 - 2022-01-01: 1_200.30 - 2023-01-01: 1_318.48 - 2024-01-01: 1_374.78 - 2025-01-01: 1_441.72 - 2026-01-01: 1_500.21 + 2004-01-01: 721.35 + 2005-01-01: 751.60 + 2006-01-01: 791.15 + 2007-01-01: 821.40 + 2008-01-01: 842.35 + 2009-01-01: 877.25 + 2010-01-01: 902.85 + 2011-01-01: 884.23 + 2012-01-01: 902.85 + 2013-01-01: 921.46 + 2014-01-01: 940.08 + 2015-01-01: 951.71 + 2016-01-01: 981.96 + 2017-01-01: 1_023.85 + 2018-01-01: 1_068.06 + 2019-01-01: 1_089.00 + 2020-01-01: 1_119.25 + 2021-01-01: 1_102.96 + 2022-01-01: 1_200.69 + 2023-01-01: 1_319.37 + 2024-01-01: 1_375.21 + 2025-01-01: 1_442.69 + 2026-01-01: 1_500.87 metadata: period: week @@ -35,6 +41,8 @@ metadata: reference: - title: Hawaii Revised Statutes Section 392-43(b)(2) href: https://data.capitol.hawaii.gov/hrscurrent/Vol07_Ch0346-0398/HRS0392/HRS_0392-0043.htm + - title: Hawaii Revised Statutes Section 383-61(b) + href: https://data.capitol.hawaii.gov/hrscurrent/Vol07_Ch0346-0398/HRS0383/HRS_0383-0061.htm - title: Hawaii DLIR, 2004 maximum weekly wage base notice href: https://web.archive.org/web/20050226051453/http://hawaii.gov:80/labor/dcd/pdf/TDI/2004%20TDI%20Wage%20Base.pdf#page=1 - title: Hawaii DLIR, 2005 maximum weekly wage base notice diff --git a/policyengine_us/tests/core/test_hi_temporary_disability_insurance.py b/policyengine_us/tests/core/test_hi_temporary_disability_insurance.py index d484fc56173..d09b737cfc0 100644 --- a/policyengine_us/tests/core/test_hi_temporary_disability_insurance.py +++ b/policyengine_us/tests/core/test_hi_temporary_disability_insurance.py @@ -3,10 +3,11 @@ YAML covers the policy examples. This module covers what one YAML case cannot state: -1. Code against published figures. The wage base parameter matches every DLIR - notice, each notice's base follows HRS 392-43(b)(2) from that notice's - average annual wage, and each notice's maximum weekly deduction is 0.5% of - its base. +1. Code against published figures. The wage base parameter follows HRS + 392-43(b)(2) from each DLIR notice's average annual wage, rounded to $100 as + HRS 383-61(b) computes it; each notice's own base follows from the same + average before rounding; each notice's maximum weekly deduction is 0.5% of + its base; and the two bases stay within the gap that rounding can open. 2. The annual formula against the weekly statute. The law caps wages week by week. For any wages spread over a worker's weeks, the annual amount is at least the weekly sum, and the two match when wages are even. @@ -20,10 +21,14 @@ Intended differences from the published figures: -- The 2010 notice's 901.70 equals the unrounded product rounded once. Every - other notice matches rounding the weekly wage first, which gives 901.69 for - 2010. HRS 392-43(b)(2) sets no rounding rule, and the parameter holds the - published amount. +- HRS 392-43(b)(2) takes the average annual wage as HRS 383-61(b) determines + it, and 383-61(b) computes that average rounded to the nearest hundred + dollars. DLIR's notices use the average before rounding, so the parameter + runs from 0.84 below to 1.15 above the published base + (us-hi:tdi-wage-base-rounded-average in source_conflicts/candidates.yaml). +- The parameter rounds the base once, to the cent. The 2010 notice does the + same; every other notice rounds the weekly wage to the cent first, which + would move 14 of the 23 parameter values by a cent. - A full-year worker over the cap pays 52 * 0.5% * base here. DLIR rounds each week's maximum deduction to the cent, so 52 weeks of it differ by at most 26 cents. @@ -104,8 +109,16 @@ def cents(amount): return amount.quantize(Decimal("0.01"), rounding=ROUND_HALF_UP) +def statutory_base(year): + # HRS 383-61(b) rounds the average to the nearest hundred dollars, and HRS + # 392-43(b)(2) takes one fifty-second of it times 1.21. + average = Decimal(NOTICES[year][0]) + hundreds = (average / 100).quantize(Decimal("1"), rounding=ROUND_HALF_UP) * 100 + return cents(hundreds * FACTOR / 52) + + def base_for(year): - return float(NOTICES[year][1]) + return float(statutory_base(year)) def simulate(year, workers, **overrides): @@ -152,7 +165,7 @@ def simulate(year, workers, **overrides): @pytest.mark.parametrize("year", YEARS) -def test_parameters_match_the_published_notice(year): +def test_parameters_follow_the_statute_from_each_notice_average(year): instant = f"{year}-01-01" assert TDI.maximum_weekly_wage_base(instant) == pytest.approx( base_for(year), abs=1e-9 @@ -161,13 +174,16 @@ def test_parameters_match_the_published_notice(year): @pytest.mark.parametrize("year", YEARS) -def test_notice_follows_the_statutory_formula(year): +def test_notice_applies_the_formula_to_the_average_before_rounding(year): annual_wage, base, deduction = (Decimal(value) for value in NOTICES[year]) rounded_once = cents(annual_wage / 52 * FACTOR) weekly_wage_rounded_first = cents(cents(annual_wage / 52) * FACTOR) assert base == (rounded_once if year == 2010 else weekly_wage_rounded_first) assert deduction == cents(base * RATE) assert abs(52 * base * RATE - 52 * deduction) <= Decimal("0.26") + # Rounding moves the average by at most 50, so the bases differ by at most + # 50 * 1.21 / 52 = 1.1635, plus up to 0.016 of rounding to cents. + assert abs(statutory_base(year) - base) <= Decimal("1.18") workers = st.fixed_dictionaries( @@ -350,8 +366,8 @@ def test_contribution_lowers_household_net_income_dollar_for_dollar(): without = simulate(2026, batch, **{CONTRIBUTION: 0}) contribution = baseline.calculate(CONTRIBUTION, period) - # 0.5% of 30,000 and 60,000, then 0.5% * 52 * 1,500.21. - assert contribution == pytest.approx([150, 300, 390.0546], abs=TOLERANCE) + # 0.5% of 30,000 and 60,000, then 0.5% * 52 * 1,500.87. + assert contribution == pytest.approx([150, 300, 390.2262], abs=TOLERANCE) for variable in ( "employee_state_payroll_tax", diff --git a/policyengine_us/tests/core/test_payroll_contributions.py b/policyengine_us/tests/core/test_payroll_contributions.py index c309c0799a7..bd59d018f9b 100644 --- a/policyengine_us/tests/core/test_payroll_contributions.py +++ b/policyengine_us/tests/core/test_payroll_contributions.py @@ -313,9 +313,9 @@ def make_employer_total_simulation( pytest.param( "HI", { - "hi_employee_temporary_disability_insurance_contribution": 390.0546, - "hi_employee_state_payroll_tax": 390.0546, - "employee_state_payroll_tax": 390.0546, + "hi_employee_temporary_disability_insurance_contribution": 390.2262, + "hi_employee_state_payroll_tax": 390.2262, + "employee_state_payroll_tax": 390.2262, }, id="HI", ), diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_employee_temporary_disability_insurance_contribution.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_employee_temporary_disability_insurance_contribution.yaml index b9538e47fb9..b16118bbd4b 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_employee_temporary_disability_insurance_contribution.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/payroll/temporary_disability_insurance/hi_employee_temporary_disability_insurance_contribution.yaml @@ -1,6 +1,8 @@ -# Each year's maximum weekly wage base comes from the Hawaii DLIR notice for -# that year, cited in maximum_weekly_wage_base.yaml. The 0.5% rate comes from -# HRS 392-43(a), and the $400, 14-week and 20-hour test from HRS 392-43(b)(1). +# Each year's maximum weekly wage base is the State's average annual wage from +# that year's DLIR notice, rounded to $100 under HRS 383-61(b), times 1.21 / 52 +# (HRS 392-43(b)(2)), rounded to the cent; maximum_weekly_wage_base.yaml cites +# each notice. The 0.5% rate comes from HRS 392-43(a), and the $400, 14-week +# and 20-hour test from HRS 392-43(b)(1). - name: Hawaii 2024 contribution is 0.5% of wages under the cap. period: 2024 @@ -16,20 +18,20 @@ hi_employee_state_payroll_tax: 250.00 employee_state_payroll_tax: 250.00 -- name: Hawaii 2024 contribution stops at the 1,374.78 weekly wage base. +- name: Hawaii 2024 contribution stops at the 1,375.21 weekly wage base. period: 2024 absolute_error_margin: 0.01 input: employment_income: 100_000 state_code: HI output: - # 52 * 1,374.78 = 71,488.56. - hi_temporary_disability_insurance_taxable_wages: 71_488.56 - # 0.5% * 71,488.56 = 357.44. DLIR's 6.87 weekly maximum * 52 = 357.24, and - # the 0.20 gap comes from DLIR rounding each week's maximum to the cent. - hi_employee_temporary_disability_insurance_contribution: 357.44 - hi_employee_state_payroll_tax: 357.44 - employee_state_payroll_tax: 357.44 + # 59,081.47 rounds to 59,100, and 59,100 * 1.21 / 52 = 1,375.21. + # 52 * 1,375.21 = 71,510.92. + hi_temporary_disability_insurance_taxable_wages: 71_510.92 + # 0.5% * 71,510.92 = 357.55. + hi_employee_temporary_disability_insurance_contribution: 357.55 + hi_employee_state_payroll_tax: 357.55 + employee_state_payroll_tax: 357.55 - name: Hawaii 2026 contribution is 0.5% of wages under the cap. period: 2026 @@ -42,35 +44,37 @@ hi_employee_temporary_disability_insurance_contribution: 300.00 employee_state_payroll_tax: 300.00 -- name: Hawaii 2026 contribution stops at the 1,500.21 weekly wage base. +- name: Hawaii 2026 contribution stops at the 1,500.87 weekly wage base. period: 2026 absolute_error_margin: 0.01 input: employment_income: 100_000 state_code: HI output: - # 52 * 1,500.21 = 78,010.92. - hi_temporary_disability_insurance_taxable_wages: 78_010.92 - # 0.5% * 78,010.92 = 390.05. DLIR's 7.50 weekly maximum * 52 = 390.00. - hi_employee_temporary_disability_insurance_contribution: 390.05 + # 64,471.82 rounds to 64,500, and 64,500 * 1.21 / 52 = 1,500.87. + # 52 * 1,500.87 = 78,045.24. + hi_temporary_disability_insurance_taxable_wages: 78_045.24 + # 0.5% * 78,045.24 = 390.23. + hi_employee_temporary_disability_insurance_contribution: 390.23 # Social Security 6.2% * 100,000 = 6,200.00, Medicare 1.45% * 100,000 = - # 1,450.00, and 6,200.00 + 1,450.00 + 390.05 = 8,040.05. - employee_payroll_tax: 8_040.05 - spm_unit_payroll_tax: 8_040.05 + # 1,450.00, and 6,200.00 + 1,450.00 + 390.23 = 8,040.23. + employee_payroll_tax: 8_040.23 + spm_unit_payroll_tax: 8_040.23 -- name: Hawaii 2004, the first published year, uses the 720.70 weekly wage base. +- name: Hawaii 2004, the first modeled year, uses the 721.35 weekly wage base. period: 2004 absolute_error_margin: 0.01 input: employment_income: 50_000 state_code: HI output: - # 52 * 720.70 = 37,476.40. - hi_temporary_disability_insurance_taxable_wages: 37_476.40 - # 0.5% * 37,476.40 = 187.38. - hi_employee_temporary_disability_insurance_contribution: 187.38 + # 30,972.13 rounds to 31,000, and 31,000 * 1.21 / 52 = 721.35. + # 52 * 721.35 = 37,510.20. + hi_temporary_disability_insurance_taxable_wages: 37_510.20 + # 0.5% * 37,510.20 = 187.55. + hi_employee_temporary_disability_insurance_contribution: 187.55 -- name: Hawaii 2009 uses the 877.69 weekly wage base and counts pre-tax premiums out. +- name: Hawaii 2009 uses the 877.25 weekly wage base and counts pre-tax premiums out. period: 2009 absolute_error_margin: 0.01 input: @@ -78,13 +82,13 @@ pre_tax_health_insurance_premiums: 10_000 state_code: HI output: - # 50,000 - 10,000 = 40,000, under 52 * 877.69 = 45,639.88. + # 50,000 - 10,000 = 40,000, under 52 * 877.25 = 45,617.00. hi_temporary_disability_insurance_taxable_wages: 40_000 # 0.5% * 40,000 = 200.00. hi_employee_temporary_disability_insurance_contribution: 200.00 employee_state_payroll_tax: 200.00 -- name: Hawaii models no contribution before the first published year. +- name: Hawaii models no contribution before the first modeled year. period: 2003 absolute_error_margin: 0.01 input: @@ -94,29 +98,29 @@ hi_temporary_disability_insurance_taxable_wages: 0 hi_employee_temporary_disability_insurance_contribution: 0 -- name: Hawaii 2020 contribution stops at the 1,119.44 weekly wage base. +- name: Hawaii 2020 contribution stops at the 1,119.25 weekly wage base. period: 2020 absolute_error_margin: 0.01 input: employment_income: 100_000 state_code: HI output: - # 52 * 1,119.44 = 58,210.88. - hi_temporary_disability_insurance_taxable_wages: 58_210.88 - # 0.5% * 58,210.88 = 291.05. - hi_employee_temporary_disability_insurance_contribution: 291.05 + # 52 * 1,119.25 = 58,201.00. + hi_temporary_disability_insurance_taxable_wages: 58_201.00 + # 0.5% * 58,201.00 = 291.005. + hi_employee_temporary_disability_insurance_contribution: 291.005 -- name: Hawaii 2021 contribution falls with the lower 1,102.90 weekly wage base. +- name: Hawaii 2021 contribution falls with the lower 1,102.96 weekly wage base. period: 2021 absolute_error_margin: 0.01 input: employment_income: 100_000 state_code: HI output: - # 52 * 1,102.90 = 57,350.80. - hi_temporary_disability_insurance_taxable_wages: 57_350.80 - # 0.5% * 57,350.80 = 286.75. - hi_employee_temporary_disability_insurance_contribution: 286.75 + # 52 * 1,102.96 = 57,353.92. + hi_temporary_disability_insurance_taxable_wages: 57_353.92 + # 0.5% * 57,353.92 = 286.77. + hi_employee_temporary_disability_insurance_contribution: 286.77 - name: Hawaii applies the weekly cap only to weeks worked. period: 2024 @@ -127,10 +131,10 @@ weekly_hours_worked_before_lsr: 40 state_code: HI output: - # 26 * 1,374.78 = 35,744.28. - hi_temporary_disability_insurance_taxable_wages: 35_744.28 - # 0.5% * 35,744.28 = 178.72. - hi_employee_temporary_disability_insurance_contribution: 178.72 + # 26 * 1,375.21 = 35,755.46. + hi_temporary_disability_insurance_taxable_wages: 35_755.46 + # 0.5% * 35,755.46 = 178.78. + hi_employee_temporary_disability_insurance_contribution: 178.78 - name: Hawaii taxes part-year wages in full when they stay under the weekly cap. period: 2024 @@ -141,7 +145,7 @@ weekly_hours_worked_before_lsr: 40 state_code: HI output: - # 26 * 1,374.78 = 35,744.28 exceeds 20,000. + # 26 * 1,375.21 = 35,755.46 exceeds 20,000. hi_temporary_disability_insurance_taxable_wages: 20_000 # 0.5% * 20,000 = 100.00. hi_employee_temporary_disability_insurance_contribution: 100.00 @@ -156,7 +160,7 @@ state_code: HI output: hi_temporary_disability_insurance_qualified: true - # 14 * 1,374.78 = 19,246.92 exceeds 15,000. + # 14 * 1,375.21 = 19,252.94 exceeds 15,000. hi_temporary_disability_insurance_taxable_wages: 15_000 # 0.5% * 15,000 = 75.00. hi_employee_temporary_disability_insurance_contribution: 75.00 @@ -272,10 +276,10 @@ traditional_401k_contributions: 10_000 state_code: HI output: - # 100,000 - 10,000 = 90,000 exceeds 52 * 1,500.21 = 78,010.92. - hi_temporary_disability_insurance_taxable_wages: 78_010.92 - # 0.5% * 78,010.92 = 390.05. - hi_employee_temporary_disability_insurance_contribution: 390.05 + # 100,000 - 10,000 = 90,000 exceeds 52 * 1,500.87 = 78,045.24. + hi_temporary_disability_insurance_taxable_wages: 78_045.24 + # 0.5% * 78,045.24 = 390.23. + hi_employee_temporary_disability_insurance_contribution: 390.23 - name: Hawaii tests the 400 minimum against wages after excluded payments. period: 2024 @@ -328,12 +332,12 @@ members: [person1, person2] state_code: HI output: - # 52 * 1,500.21 = 78,010.92 for person1, and 40,000 is under the cap. - hi_temporary_disability_insurance_taxable_wages: [78_010.92, 40_000] - # 0.5% * 78,010.92 = 390.05 and 0.5% * 40,000 = 200.00. - hi_employee_temporary_disability_insurance_contribution: [390.05, 200.00] - # 390.05 + 200.00 = 590.05. - employee_state_payroll_tax: 590.05 + # 52 * 1,500.87 = 78,045.24 for person1, and 40,000 is under the cap. + hi_temporary_disability_insurance_taxable_wages: [78_045.24, 40_000] + # 0.5% * 78,045.24 = 390.23 and 0.5% * 40,000 = 200.00. + hi_employee_temporary_disability_insurance_contribution: [390.23, 200.00] + # 390.23 + 200.00 = 590.23. + employee_state_payroll_tax: 590.23 - name: Hawaii leaves a 2014 deferral out of the contribution. period: 2014 @@ -343,7 +347,7 @@ traditional_401k_contributions: 4_000 state_code: HI output: - # 0.5% * (30,000 - 4,000) = 130, under 52 * 940.05 = 48,882.60. + # 0.5% * (30,000 - 4,000) = 130, under 52 * 940.08 = 48,884.16. hi_employee_temporary_disability_insurance_contribution: 130 - name: Hawaii leaves out only the deferral the 2009 limit allows. @@ -355,7 +359,7 @@ traditional_401k_contributions_desired: 20_000 state_code: HI output: - # 50,000 - 16,500 = 33,500, under 52 * 877.69 = 45,639.88. + # 50,000 - 16,500 = 33,500, under 52 * 877.25 = 45,617.00. hi_temporary_disability_insurance_wages: 33_500 # 0.5% * 33,500 = 167.50. hi_employee_temporary_disability_insurance_contribution: 167.50 @@ -421,7 +425,7 @@ state_code: HI output: hi_temporary_disability_insurance_qualified: true - # One week's 1,000 falls under the 1,500.21 cap: 0.5% * 1,000 = 5. + # One week's 1,000 falls under the 1,500.87 cap: 0.5% * 1,000 = 5. hi_employee_temporary_disability_insurance_contribution: 5 - name: Hawaii counts final-year wages under 400 when the previous year passes. @@ -746,7 +750,7 @@ state_code: HI output: hi_temporary_disability_insurance_qualified: true - # 900 falls under the 951.23 cap for one week: 0.5% * 900 = 4.50. + # 900 falls under the 951.71 cap for one week: 0.5% * 900 = 4.50. hi_employee_temporary_disability_insurance_contribution: 4.50 - name: Hawaii scales the contribution by the employer's deduction share. @@ -771,8 +775,8 @@ employer_state_payroll_premium_deduction_share: 0 state_code: HI output: - # 52 * 1,500.21 = 78,010.92. - hi_temporary_disability_insurance_taxable_wages: 78_010.92 + # 52 * 1,500.87 = 78,045.24. + hi_temporary_disability_insurance_taxable_wages: 78_045.24 hi_employee_temporary_disability_insurance_contribution: 0 employee_state_payroll_tax: 0 @@ -784,5 +788,5 @@ employer_state_payroll_premium_deduction_share: 1.5 state_code: HI output: - # The employer may withhold at most 0.5% * 78,010.92 = 390.05. - hi_employee_temporary_disability_insurance_contribution: 390.05 + # The employer may withhold at most 0.5% * 78,045.24 = 390.23. + hi_employee_temporary_disability_insurance_contribution: 390.23 diff --git a/source_conflicts/candidates.yaml b/source_conflicts/candidates.yaml index 9d47a70e019..07dd336320b 100644 --- a/source_conflicts/candidates.yaml +++ b/source_conflicts/candidates.yaml @@ -3189,6 +3189,52 @@ candidates: prs: [] state: unreviewed note: PE-US follows state rules; verify their effective text throughout the historical periods. +- id: us-hi:tdi-wage-base-rounded-average + program: hi-tdi + years: + from: 2004 + type: calendar + question: Does Hawaii's temporary disability insurance maximum weekly wage base start from the State's average annual + wage rounded to the nearest hundred dollars, as HRS 383-61(b) computes it, or from the average before rounding, as + DLIR's yearly notices do? + sources: + - kind: statute + citation: HRS 392-43(b)(2), which takes one fifty-second of the average annual wage determined pursuant to section + 383-61(b), times 1.21 + url: https://data.capitol.hawaii.gov/hrscurrent/Vol07_Ch0346-0398/HRS0392/HRS_0392-0043.htm + - kind: statute + citation: HRS 383-61(b), which computes the average annual wage and rounds it to the nearest hundred dollars + url: https://data.capitol.hawaii.gov/hrscurrent/Vol07_Ch0346-0398/HRS0383/HRS_0383-0061.htm + - kind: agency-practice + citation: Hawaii DLIR Disability Compensation Division, 2026 maximum weekly wage base notice (and each notice from + 2003 to 2025), dividing the unrounded average by 52 + url: https://labor.hawaii.gov/dcd/files/2025/12/2026-Maximum-Weekly-Wage-Base.pdf + - kind: agency-practice + citation: Hawaii DLIR Unemployment Insurance Division, taxable wage bases for 2003 to 2026 (this page from 2020, the + December 2019 trust fund evaluation before), each the same average rounded to the nearest hundred dollars, or 90% + of it in 2010 and 2011, except the fixed $13,000 of 2008 and 2009 + url: https://labor.hawaii.gov/ui/tax-rate-schedule-and-weekly-benefit-amount/ + readings: + statute: HRS 383-61(b) says how the average annual wage is computed, and the computation ends by rounding to the + nearest hundred dollars. HRS 392-43(b)(2) takes the average as determined under that subsection, so the base is + the rounded average times 1.21 / 52. DLIR's own determination under 383-61(b), the unemployment insurance wage + base, is the rounded figure. + other: DLIR's temporary disability insurance notices divide the average before rounding by 52 and multiply by 1.21, + so each published base runs from 1.15 below to 0.84 above the statutory one. + example: For 2026, the notice's average of $64,471.82 gives a published base of $1,500.21; rounded to $64,500 it gives + $1,500.87. A worker paid $100,000 contributes 0.5% of 52 weeks at the base, $390.23 under the statute against $390.05 + under the notice. + pe_us: + follows: statute + code: + - policyengine_us/parameters/gov/states/hi/tax/payroll/temporary_disability_insurance/maximum_weekly_wage_base.yaml + prs: + - https://github.com/PolicyEngine/policyengine-us/pull/10677 + state: ruled + note: Definitions control, and the text settles the reading, so the agency-breaks-ties principle doesn't reach it. The + same reading of HRS 383-61(b) decides us-hi:ui-taxable-wage-base-2010-rounding. Within the formula the parameter + rounds once to the cent, because HRS 392-43(b)(2) sets no intermediate rounding. Short excerpts are in + source_conflicts/sources/us-hi/. - id: us-hi:tdi-wages-salary-reductions program: hi-tdi years: diff --git a/source_conflicts/sources/us-hi/dlir-tdi-2026-wage-base-notice-excerpt.txt b/source_conflicts/sources/us-hi/dlir-tdi-2026-wage-base-notice-excerpt.txt new file mode 100644 index 00000000000..c43c5b409a7 --- /dev/null +++ b/source_conflicts/sources/us-hi/dlir-tdi-2026-wage-base-notice-excerpt.txt @@ -0,0 +1,6 @@ +Source: https://labor.hawaii.gov/dcd/files/2025/12/2026-Maximum-Weekly-Wage-Base.pdf +Short excerpt of the temporary disability insurance computation in DLIR's 2026 maximum weekly wage base notice, copied from the official PDF. + +State Average Annual Wage 64,471.82 +Divided by 52 1,239.84 +Multiplied by 1.21 1,500.21 diff --git a/source_conflicts/sources/us-hi/hrs-383-61-excerpt.txt b/source_conflicts/sources/us-hi/hrs-383-61-excerpt.txt new file mode 100644 index 00000000000..977078dc8a9 --- /dev/null +++ b/source_conflicts/sources/us-hi/hrs-383-61-excerpt.txt @@ -0,0 +1,5 @@ +Source: https://data.capitol.hawaii.gov/hrscurrent/Vol07_Ch0346-0398/HRS0383/HRS_0383-0061.htm +Short excerpts of HRS 383-61(b), on computing the average annual wage, copied from the official HTML page. + +shall be computed as follows +rounded to the nearest hundred dollars diff --git a/source_conflicts/sources/us-hi/hrs-392-43-excerpt.txt b/source_conflicts/sources/us-hi/hrs-392-43-excerpt.txt new file mode 100644 index 00000000000..41c6a23862b --- /dev/null +++ b/source_conflicts/sources/us-hi/hrs-392-43-excerpt.txt @@ -0,0 +1,5 @@ +Source: https://data.capitol.hawaii.gov/hrscurrent/Vol07_Ch0346-0398/HRS0392/HRS_0392-0043.htm +Short excerpts of HRS 392-43(b)(2), on the maximum weekly wage base, copied from the official HTML page. + +pursuant to section 383-61(b) +multiplied by the factor 1.21