From d2be9c201c96d3ed260545ea3f4f69cff747d3f5 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 19 Oct 2026 14:33:04 -0400 Subject: [PATCH 01/10] Let employers deduct less than the permitted state payroll premium Add employer_state_payroll_premium_deduction_share (default 1) for the programs whose statute puts the premium on the employer and lets it deduct up to a share from wages: CO FAMLI, DE Paid Leave, ME and MA PFML, MN Paid Leave, NY PFL and DBL, the VT Child Care Contribution and WA PFML. The employee amount is the permitted share times the deduction share; the employer-side variables carry the part it does not deduct. Encode Acts 2026 c. 101 ss. 25-26 for Massachusetts from 2027-01-01, with DFML's 2027 family and medical rates, and cite CGS 31-49g(b)(1) for the Connecticut paid leave rate. Co-Authored-By: Claude Opus 5.5 --- .../payroll-employer-deduction-share.added.md | 1 + .../tax/payroll/paid_leave/employee_rate.yaml | 4 +- .../paid_leave/family_employee_share.yaml | 22 ++ .../tax/payroll/paid_leave/family_rate.yaml | 6 +- .../paid_leave/medical_employee_share.yaml | 8 +- .../tax/payroll/paid_leave/medical_rate.yaml | 6 +- ...roll_premium_deduction_share_invariants.py | 193 ++++++++++++++ .../ma_employee_paid_leave_contribution.yaml | 45 ++++ ...state_payroll_premium_deduction_share.yaml | 239 ++++++++++++++++++ .../famli/co_employee_famli_contribution.py | 13 +- .../famli/co_employer_famli_contribution.py | 15 +- .../de_employee_paid_leave_contribution.py | 10 +- .../de_employer_paid_leave_contribution.py | 12 +- .../ma_employee_paid_leave_contribution.py | 19 +- .../ma_employer_paid_leave_contribution.py | 25 +- .../me_employee_paid_leave_contribution.py | 10 +- .../me_employer_paid_leave_contribution.py | 12 +- .../mn_employee_paid_leave_contribution.py | 13 +- ...ployee_disability_benefits_contribution.py | 17 +- ...employee_paid_family_leave_contribution.py | 13 +- .../tax/payroll/employee_state_payroll_tax.py | 18 +- .../employer_additional_state_payroll_tax.py | 6 +- ...oyer_total_additional_state_payroll_tax.py | 76 ++++-- .../state_payroll_premium_deduction_share.py | 21 ++ .../vt_employee_child_care_contribution.py | 10 +- .../vt_employer_child_care_contribution.py | 13 +- .../wa_employee_paid_leave_contribution.py | 13 +- .../wa_employer_paid_leave_contribution.py | 19 +- .../variables/input/employer_payroll.py | 21 ++ 29 files changed, 800 insertions(+), 80 deletions(-) create mode 100644 changelog.d/payroll-employer-deduction-share.added.md create mode 100644 policyengine_us/parameters/gov/states/ma/tax/payroll/paid_leave/family_employee_share.yaml create mode 100644 policyengine_us/tests/core/test_state_payroll_premium_deduction_share_invariants.py create mode 100644 policyengine_us/tests/policy/baseline/gov/states/tax/payroll/state_payroll_premium_deduction_share.yaml create mode 100644 policyengine_us/variables/gov/states/tax/payroll/state_payroll_premium_deduction_share.py diff --git a/changelog.d/payroll-employer-deduction-share.added.md b/changelog.d/payroll-employer-deduction-share.added.md new file mode 100644 index 00000000000..a556609c27a --- /dev/null +++ b/changelog.d/payroll-employer-deduction-share.added.md @@ -0,0 +1 @@ +Add `employer_state_payroll_premium_deduction_share`, the share of the permitted employee premium an employer deducts for Colorado FAMLI, Delaware Paid Leave, Maine and Massachusetts PFML, Minnesota Paid Leave, New York PFL and DBL, the Vermont Child Care Contribution and Washington PFML (default 1, the current full-share assumption), with the employer paying any part it does not deduct; apply Massachusetts' 2027 swap of the family and medical deduction caps and rates (Acts 2026 c. 101); and cite Conn. Gen. Stat. 31-49g(b)(1) for Connecticut's paid leave rate. diff --git a/policyengine_us/parameters/gov/states/ct/tax/payroll/paid_leave/employee_rate.yaml b/policyengine_us/parameters/gov/states/ct/tax/payroll/paid_leave/employee_rate.yaml index 6685da4dc7b..c28f33f86ba 100644 --- a/policyengine_us/parameters/gov/states/ct/tax/payroll/paid_leave/employee_rate.yaml +++ b/policyengine_us/parameters/gov/states/ct/tax/payroll/paid_leave/employee_rate.yaml @@ -7,7 +7,7 @@ metadata: unit: /1 label: Connecticut employee paid leave rate reference: - - title: Connecticut General Statutes Section 31-49e - href: https://www.cga.ct.gov/current/pub/chap_557.htm + - title: Connecticut General Statutes Section 31-49g(b)(1) + href: https://www.cga.ct.gov/current/pub/chap_557.htm#sec_31-49g - title: Connecticut Paid Leave - Remit Contributions href: https://www.ctpaidleave.org/remit-contributions diff --git a/policyengine_us/parameters/gov/states/ma/tax/payroll/paid_leave/family_employee_share.yaml b/policyengine_us/parameters/gov/states/ma/tax/payroll/paid_leave/family_employee_share.yaml new file mode 100644 index 00000000000..8db687296dd --- /dev/null +++ b/policyengine_us/parameters/gov/states/ma/tax/payroll/paid_leave/family_employee_share.yaml @@ -0,0 +1,22 @@ +description: Largest share of the Massachusetts PFML family leave contribution an employer may deduct from wages. +values: + 0000-01-01: 0 + 2024-01-01: 1 + # Acts 2026 c. 101 s. 26 strikes "100" in s. 6(c)(2), (d) and (e)(2) and inserts "40". + 2027-01-01: 0.4 + +metadata: + period: year + unit: /1 + label: Massachusetts PFML family employee share + reference: + - title: Massachusetts General Laws Chapter 175M Section 6 + href: https://malegislature.gov/Laws/GeneralLaws/PartI/TitleXXII/Chapter175M/Section6 + - title: Massachusetts Paid Family and Medical Leave employer contribution rates and calculator (2026 and 2027 sections, updated October 1, 2026) + href: https://www.mass.gov/info-details/paid-family-and-medical-leave-employer-contribution-rates-and-calculator + - title: Massachusetts Paid Family and Medical Leave employer contribution rates, 2024 section (archived May 26, 2024) + href: https://web.archive.org/web/20240526092717/https://www.mass.gov/info-details/paid-family-and-medical-leave-employer-contribution-rates-and-calculator + - title: Massachusetts Paid Family and Medical Leave employer contribution rates, 2025 & 2026 section (archived December 8, 2025) + href: https://web.archive.org/web/20251208080415/https://www.mass.gov/info-details/paid-family-and-medical-leave-employer-contribution-rates-and-calculator + - title: Acts of 2026, Chapter 101, Sections 25, 26, 43 and 45 + href: https://malegislature.gov/Laws/SessionLaws/Acts/2026/Chapter101 diff --git a/policyengine_us/parameters/gov/states/ma/tax/payroll/paid_leave/family_rate.yaml b/policyengine_us/parameters/gov/states/ma/tax/payroll/paid_leave/family_rate.yaml index 7c5733a58fb..be8f9a9dc3d 100644 --- a/policyengine_us/parameters/gov/states/ma/tax/payroll/paid_leave/family_rate.yaml +++ b/policyengine_us/parameters/gov/states/ma/tax/payroll/paid_leave/family_rate.yaml @@ -2,6 +2,8 @@ description: Massachusetts PFML family leave contribution rate. values: 0000-01-01: 0 2024-01-01: 0.0018 + # DFML's 2027 rates swap with the deduction caps (Acts 2026 c. 101 s. 43(a)), leaving the employee and employer totals unchanged. + 2027-01-01: 0.007 metadata: period: year @@ -10,9 +12,11 @@ metadata: reference: - title: Massachusetts General Laws Chapter 175M Section 6 href: https://malegislature.gov/Laws/GeneralLaws/PartI/TitleXXII/Chapter175M/Section6 - - title: Massachusetts Paid Family and Medical Leave employer contribution rates and calculator + - title: Massachusetts Paid Family and Medical Leave employer contribution rates and calculator (2026 and 2027 sections, updated October 1, 2026) href: https://www.mass.gov/info-details/paid-family-and-medical-leave-employer-contribution-rates-and-calculator - title: Massachusetts Paid Family and Medical Leave employer contribution rates, 2024 section (archived May 26, 2024) href: https://web.archive.org/web/20240526092717/https://www.mass.gov/info-details/paid-family-and-medical-leave-employer-contribution-rates-and-calculator - title: Massachusetts Paid Family and Medical Leave employer contribution rates, 2025 & 2026 section (archived December 8, 2025) href: https://web.archive.org/web/20251208080415/https://www.mass.gov/info-details/paid-family-and-medical-leave-employer-contribution-rates-and-calculator + - title: Acts of 2026, Chapter 101, Sections 25, 26, 43 and 45 + href: https://malegislature.gov/Laws/SessionLaws/Acts/2026/Chapter101 diff --git a/policyengine_us/parameters/gov/states/ma/tax/payroll/paid_leave/medical_employee_share.yaml b/policyengine_us/parameters/gov/states/ma/tax/payroll/paid_leave/medical_employee_share.yaml index 8025a24fd77..fed95485b78 100644 --- a/policyengine_us/parameters/gov/states/ma/tax/payroll/paid_leave/medical_employee_share.yaml +++ b/policyengine_us/parameters/gov/states/ma/tax/payroll/paid_leave/medical_employee_share.yaml @@ -1,7 +1,9 @@ -description: Employee share of the Massachusetts PFML medical leave contribution. +description: Largest share of the Massachusetts PFML medical leave contribution an employer may deduct from wages. values: 0000-01-01: 0 2024-01-01: 0.4 + # Acts 2026 c. 101 s. 25 strikes "40" in s. 6(c)(1), (d) and (e)(1) and inserts "100". + 2027-01-01: 1 metadata: period: year @@ -10,9 +12,11 @@ metadata: reference: - title: Massachusetts General Laws Chapter 175M Section 6 href: https://malegislature.gov/Laws/GeneralLaws/PartI/TitleXXII/Chapter175M/Section6 - - title: Massachusetts Paid Family and Medical Leave employer contribution rates and calculator + - title: Massachusetts Paid Family and Medical Leave employer contribution rates and calculator (2026 and 2027 sections, updated October 1, 2026) href: https://www.mass.gov/info-details/paid-family-and-medical-leave-employer-contribution-rates-and-calculator - title: Massachusetts Paid Family and Medical Leave employer contribution rates, 2024 section (archived May 26, 2024) href: https://web.archive.org/web/20240526092717/https://www.mass.gov/info-details/paid-family-and-medical-leave-employer-contribution-rates-and-calculator - title: Massachusetts Paid Family and Medical Leave employer contribution rates, 2025 & 2026 section (archived December 8, 2025) href: https://web.archive.org/web/20251208080415/https://www.mass.gov/info-details/paid-family-and-medical-leave-employer-contribution-rates-and-calculator + - title: Acts of 2026, Chapter 101, Sections 25, 26, 43 and 45 + href: https://malegislature.gov/Laws/SessionLaws/Acts/2026/Chapter101 diff --git a/policyengine_us/parameters/gov/states/ma/tax/payroll/paid_leave/medical_rate.yaml b/policyengine_us/parameters/gov/states/ma/tax/payroll/paid_leave/medical_rate.yaml index 9d8fef45424..daf955cbd03 100644 --- a/policyengine_us/parameters/gov/states/ma/tax/payroll/paid_leave/medical_rate.yaml +++ b/policyengine_us/parameters/gov/states/ma/tax/payroll/paid_leave/medical_rate.yaml @@ -2,6 +2,8 @@ description: Massachusetts PFML medical leave contribution rate. values: 0000-01-01: 0 2024-01-01: 0.007 + # DFML's 2027 rates swap with the deduction caps (Acts 2026 c. 101 s. 43(a)), leaving the employee and employer totals unchanged. + 2027-01-01: 0.0018 metadata: period: year @@ -10,9 +12,11 @@ metadata: reference: - title: Massachusetts General Laws Chapter 175M Section 6 href: https://malegislature.gov/Laws/GeneralLaws/PartI/TitleXXII/Chapter175M/Section6 - - title: Massachusetts Paid Family and Medical Leave employer contribution rates and calculator + - title: Massachusetts Paid Family and Medical Leave employer contribution rates and calculator (2026 and 2027 sections, updated October 1, 2026) href: https://www.mass.gov/info-details/paid-family-and-medical-leave-employer-contribution-rates-and-calculator - title: Massachusetts Paid Family and Medical Leave employer contribution rates, 2024 section (archived May 26, 2024) href: https://web.archive.org/web/20240526092717/https://www.mass.gov/info-details/paid-family-and-medical-leave-employer-contribution-rates-and-calculator - title: Massachusetts Paid Family and Medical Leave employer contribution rates, 2025 & 2026 section (archived December 8, 2025) href: https://web.archive.org/web/20251208080415/https://www.mass.gov/info-details/paid-family-and-medical-leave-employer-contribution-rates-and-calculator + - title: Acts of 2026, Chapter 101, Sections 25, 26, 43 and 45 + href: https://malegislature.gov/Laws/SessionLaws/Acts/2026/Chapter101 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 new file mode 100644 index 00000000000..156c530ac80 --- /dev/null +++ b/policyengine_us/tests/core/test_state_payroll_premium_deduction_share_invariants.py @@ -0,0 +1,193 @@ +"""Invariants for the employer's deduction of state payroll premiums. + +Some state programs put the premium on the employer and let it deduct up to a +share from wages without requiring it: Colorado FAMLI, Delaware Paid Leave, +Maine and Massachusetts PFML, Minnesota Paid Leave, New York PFL and DBL, the +Vermont Child Care Contribution and Washington PFML. +`employer_state_payroll_premium_deduction_share` says how much of the +permitted employee share the employer deducts (default 1); the employer pays +the rest. Hypothesis draws workers in those states, in states whose +contributions are mandatory withholding, and in a state with neither, with any +wages, employer size and share, and runs each worker three times in one +simulation: with two drawn shares and with the input left at its default. For +every worker, in 2026 and 2027: + +1. The employee amount of the optional programs is the clipped share times the + amount at the default, so the default is the full permitted share and a + share outside 0 to 1 acts as the nearer bound. +2. The mandatory components of `employee_state_payroll_tax` do not depend on + 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. +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 (a + one-worker employer), `employer_total_additional_state_payroll_tax` equals + `employer_additional_state_payroll_tax`: the employer-level path and the + per-worker path agree at any share. +6. Massachusetts' 2027 swap of the family and medical deduction caps, paired + with DFML's swap of the two rates (Acts 2026 c. 101 ss. 25, 26, 43(a)), + leaves each worker's employee and employer amounts where they were in + 2026, for wages under both years' wage bases. + +Amounts are compared to float32 precision, within a cent. + +Why Python: the YAML cases pin one share and employer size each; this checks +every program at once against drawn shares, sizes and wages, including +boundary employer sizes and shares outside 0 to 1, in one vectorized +simulation per example. +""" + +import numpy as np +from hypothesis import HealthCheck, given, settings +from hypothesis import strategies as st + +from policyengine_us import Simulation + +YEARS = (2026, 2027) +OPTIONAL_STATES = ("CO", "DE", "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", + "ma_employee_paid_leave_contribution", + "me_employee_paid_leave_contribution", + "mn_employee_paid_leave_contribution", + "ny_employee_paid_family_leave_contribution", + "ny_employee_disability_benefits_contribution", + "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. +UNCAPPED_WAGES = 184_500 +COPIES = 3 # drawn share a, drawn share b, input left at its default +# The model computes in float32: compare to that precision, never past a cent. +CENT = 0.01 +TOLERANCE = dict(rtol=1e-6, atol=CENT) + +share = st.one_of( + st.sampled_from([0.0, 0.25, 0.5, 1.0, -0.5, 1.5]), + st.floats(-0.5, 1.5, allow_nan=False), +) +workers = st.tuples( + st.sampled_from(OPTIONAL_STATES + MANDATORY_STATES + ("TX",)), + st.integers(0, 300_000), # employment_income + st.sampled_from([1, 9, 10, 14, 15, 20, 24, 25, 49, 50, 100]), + share, + share, +) + + +def simulate(rows): + people, households = {}, {} + groups = {"tax_units": {}, "spm_units": {}, "families": {}, "marital_units": {}} + index = 0 + for state, wages, headcount, share_a, share_b in rows: + for share_value in (share_a, share_b, None): + person = f"person_{index}" + people[person] = { + "age": {year: 30 for year in YEARS}, + "employment_income": {year: wages for year in YEARS}, + "employer_headcount": {year: headcount for year in YEARS}, + "employer_total_taxable_earnings_for_social_security": { + year: wages for year in YEARS + }, + "employer_total_payroll_tax_gross_wages": { + year: wages for year in YEARS + }, + } + if share_value is not None: + people[person]["employer_state_payroll_premium_deduction_share"] = { + year: share_value for year in YEARS + } + households[f"household_{index}"] = { + "members": [person], + "state_code": {year: state for year in YEARS}, + } + for name in groups: + groups[name][f"{name}_{index}"] = {"members": [person]} + index += 1 + return Simulation(situation={"people": people, "households": households, **groups}) + + +def by_copy(simulation, variable, year): + return np.asarray(simulation.calculate(variable, year)).reshape(-1, COPIES) + + +@settings( + max_examples=8, + deadline=None, + derandomize=True, + suppress_health_check=[HealthCheck.too_slow, HealthCheck.data_too_large], +) +@given( + st.lists(workers, min_size=4, max_size=30).map( + # One worker in each optional state at a share strictly between 0 and + # 1, so every program's properties compare positive amounts. + lambda rows: ( + [(state, 60_000, 100, 0.5, 0.0) for state in OPTIONAL_STATES] + rows + ) + ) +) +def test_state_payroll_premium_deduction_share_invariants(rows): + simulation = simulate(rows) + states = np.array([row[0] for row in 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")) + uncapped = wages <= UNCAPPED_WAGES + optional_by_year = {} + employer_by_year = {} + for year in YEARS: + optional = sum( + by_copy(simulation, variable, year) + for variable in OPTIONAL_EMPLOYEE_VARIABLES + ) + total = by_copy(simulation, "employee_state_payroll_tax", year) + employer = by_copy(simulation, "employer_additional_state_payroll_tax", year) + employer_total = by_copy( + simulation, "employer_total_additional_state_payroll_tax", year + ) + default = optional[:, [2]] + assert (default > 0).any(), "no worker with a positive optional premium" + # Property 1. + np.testing.assert_allclose(optional, shares * default, **TOLERANCE) + # Property 2. + mandatory = total - optional + np.testing.assert_allclose( + mandatory, mandatory[:, [2]] * np.ones((1, COPIES)), **TOLERANCE + ) + # Property 3. + remitted = optional + employer + np.testing.assert_allclose( + remitted[modeled], + remitted[modeled][:, [2]] * np.ones((1, COPIES)), + **TOLERANCE, + ) + assert (employer[unmodeled] == 0).all() + # Property 4. + for low, high in ((0, 1), (1, 0)): + lower = shares[:, low] <= shares[:, high] + assert (optional[lower, low] <= optional[lower, high] + CENT).all() + assert (employer[lower, low] >= employer[lower, high] - CENT).all() + # Property 5. + same_path = modeled & uncapped + np.testing.assert_allclose( + employer_total[same_path], employer[same_path], **TOLERANCE + ) + optional_by_year[year] = optional + employer_by_year[year] = employer + # Property 6. + ma = (states == "MA") & uncapped + assert ma.any() + np.testing.assert_allclose( + optional_by_year[2027][ma], optional_by_year[2026][ma], **TOLERANCE + ) + np.testing.assert_allclose( + employer_by_year[2027][ma], employer_by_year[2026][ma], **TOLERANCE + ) diff --git a/policyengine_us/tests/policy/baseline/gov/states/ma/tax/payroll/paid_leave/ma_employee_paid_leave_contribution.yaml b/policyengine_us/tests/policy/baseline/gov/states/ma/tax/payroll/paid_leave/ma_employee_paid_leave_contribution.yaml index 87456ff12a8..2580f52d02d 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ma/tax/payroll/paid_leave/ma_employee_paid_leave_contribution.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ma/tax/payroll/paid_leave/ma_employee_paid_leave_contribution.yaml @@ -61,3 +61,48 @@ ma_employer_paid_leave_contribution: 0 # The employee share does not depend on employer size. ma_employee_paid_leave_contribution: 276.00 + +- name: Massachusetts PFML 2026 keeps the 100% family and 40% medical deduction caps. + period: 2026 + input: + employment_income: 100_000 + state_code: MA + output: + # Acts 2026 c. 101 swaps the caps from 2027 (s. 43(a); DFML). Applying the + # swap to 2026 rates would give 40% * 0.18% + 0.70% = 0.772%, or 772. + # 0.18% + 40% * 0.70% = 0.46%; 0.46% * 100,000 = 460. + ma_employee_paid_leave_contribution: 460 + # 60% * 0.70% * 100,000 = 420. + ma_employer_paid_leave_contribution: 420 + +- name: Massachusetts PFML 2027 swaps the caps and the rates, leaving the employee share at 0.46%. + period: 2027 + input: + employment_income: 100_000 + state_code: MA + output: + # 40% * 0.70% family + 100% * 0.18% medical = 0.28% + 0.18% = 0.46%. + ma_employee_paid_leave_contribution: 460 + # 60% * 0.70% family = 0.42%; nothing of medical. + ma_employer_paid_leave_contribution: 420 + +- name: Massachusetts PFML 2027 small employer remits only the 0.46% it may deduct. + period: 2027 + input: + employment_income: 100_000 + employer_headcount: 24 + state_code: MA + output: + ma_employee_paid_leave_contribution: 460 + ma_employer_paid_leave_contribution: 0 + +- name: Massachusetts PFML 2027 employer pays the part of the employee share it does not deduct. + period: 2027 + input: + employment_income: 100_000 + employer_state_payroll_premium_deduction_share: 0.5 + state_code: MA + output: + ma_employee_paid_leave_contribution: 230 + # 420 + 230 = 650. + ma_employer_paid_leave_contribution: 650 diff --git a/policyengine_us/tests/policy/baseline/gov/states/tax/payroll/state_payroll_premium_deduction_share.yaml b/policyengine_us/tests/policy/baseline/gov/states/tax/payroll/state_payroll_premium_deduction_share.yaml new file mode 100644 index 00000000000..9bebda21773 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/tax/payroll/state_payroll_premium_deduction_share.yaml @@ -0,0 +1,239 @@ +# The employer deducts a share of the largest employee premium the law lets it +# deduct; it pays the rest. Wages are 100,000, below every 2026 wage base. + +- name: The deduction share defaults to the full permitted share. + period: 2026 + input: + employment_income: 100_000 + state_code: CO + output: + employer_state_payroll_premium_deduction_share: 1 + state_payroll_premium_deduction_share: 1 + # 0.44% * 100,000 = 440. + co_employee_famli_contribution: 440 + co_employer_famli_contribution: 440 + +- name: The deduction share is held between 0 and 1. + period: 2026 + input: + people: + over: + employment_income: 100_000 + employer_state_payroll_premium_deduction_share: 1.5 + under: + employment_income: 100_000 + employer_state_payroll_premium_deduction_share: -0.5 + tax_units: + over_unit: + members: [over] + under_unit: + members: [under] + households: + over_household: + members: [over] + state_code: CO + under_household: + members: [under] + state_code: CO + output: + state_payroll_premium_deduction_share: [1, 0] + co_employee_famli_contribution: [440, 0] + # 0.44% + 0.44% = 0.88% when the employer deducts nothing. + co_employer_famli_contribution: [440, 880] + +- name: Colorado FAMLI employer pays the half of the employee share it does not deduct. + period: 2026 + input: + employment_income: 100_000 + employer_state_payroll_premium_deduction_share: 0.5 + state_code: CO + output: + # 0.5 * 0.44% * 100,000 = 220. + co_employee_famli_contribution: 220 + co_employee_state_payroll_tax: 220 + employee_state_payroll_tax: 220 + # 0.44% * 100,000 + 220 = 660. + co_employer_famli_contribution: 660 + co_employer_additional_state_payroll_tax: 660 + +- name: Colorado FAMLI employer with fewer than 10 employees still pays the employee share it does not deduct. + period: 2026 + input: + employment_income: 100_000 + employer_headcount: 9 + employer_state_payroll_premium_deduction_share: 0.25 + state_code: CO + output: + # 0.25 * 0.44% * 100,000 = 110. + co_employee_famli_contribution: 110 + # No employer half; 0.75 * 440 = 330 of the 0.44% it must remit. + co_employer_famli_contribution: 330 + +- name: Massachusetts PFML employer pays the part of the 0.46% employee share it does not deduct. + period: 2026 + input: + employment_income: 100_000 + employer_state_payroll_premium_deduction_share: 0.5 + state_code: MA + output: + # 0.5 * (0.18% family + 40% * 0.70% medical) * 100,000 = 0.5 * 460 = 230. + ma_employee_paid_leave_contribution: 230 + employee_state_payroll_tax: 230 + # 60% * 0.70% * 100,000 = 420, plus the 230 not deducted. + ma_employer_paid_leave_contribution: 650 + +- name: Massachusetts PFML small employer that deducts nothing remits the 0.46% itself. + period: 2026 + input: + employment_income: 100_000 + employer_headcount: 24 + employer_state_payroll_premium_deduction_share: 0 + state_code: MA + output: + ma_employee_paid_leave_contribution: 0 + ma_employer_paid_leave_contribution: 460 + +- name: Maine PFML employer pays the part of the employee half it does not deduct. + period: 2026 + input: + employment_income: 100_000 + employer_state_payroll_premium_deduction_share: 0.5 + state_code: ME + output: + # 0.5 * 0.5% * 100,000 = 250. + me_employee_paid_leave_contribution: 250 + # 0.5% * 100,000 + 250 = 750. + me_employer_paid_leave_contribution: 750 + +- name: Maine PFML employer with fewer than 15 employees remits the half it does not deduct. + period: 2026 + input: + employment_income: 100_000 + employer_headcount: 14 + employer_state_payroll_premium_deduction_share: 0.5 + state_code: ME + output: + me_employee_paid_leave_contribution: 250 + # No employer half; it must still remit 0.5%, of which it deducted 250. + me_employer_paid_leave_contribution: 250 + +- name: Delaware Paid Leave employer pays the part of the employee half it does not deduct. + period: 2026 + input: + employment_income: 100_000 + employer_state_payroll_premium_deduction_share: 0.5 + state_code: DE + output: + # 0.5 * 50% * (0.40% + 0.08% + 0.32%) * 100,000 = 0.25 * 800 = 200. + de_employee_paid_leave_contribution: 200 + # 800 - 200 = 600. + de_employer_paid_leave_contribution: 600 + +- name: Delaware Paid Leave employer with 10 to 24 employees that deducts nothing pays the parental rate itself. + period: 2026 + input: + employment_income: 100_000 + employer_headcount: 20 + employer_state_payroll_premium_deduction_share: 0 + state_code: DE + output: + de_employee_paid_leave_contribution: 0 + # 0.32% * 100,000 = 320. + de_employer_paid_leave_contribution: 320 + +- name: Vermont employer pays the part of the permitted quarter it does not withhold. + period: 2026 + input: + employment_income: 100_000 + employer_state_payroll_premium_deduction_share: 0.5 + state_code: VT + output: + # 0.5 * 0.11% * 100,000 = 55. + vt_employee_child_care_contribution: 55 + # 0.33% * 100,000 + 55 = 385. + vt_employer_child_care_contribution: 385 + +- name: Washington PFML employer pays the part of the employee share it does not deduct; WA Cares does not change. + period: 2026 + input: + employment_income: 100_000 + employer_state_payroll_premium_deduction_share: 0.5 + state_code: WA + output: + # 0.5 * 1.13% * (1 - 0.2857) * 100,000 = 0.5 * 807.159 = 403.5795. + wa_employee_paid_leave_contribution: 403.5795 + # WA Cares is mandatory withholding: 0.58% * 100,000 = 580. + wa_employee_long_term_care_contribution: 580 + wa_employee_state_payroll_tax: 983.5795 + # 1.13% * 0.2857 * 100,000 = 322.841, plus 403.5795 not deducted. + wa_employer_paid_leave_contribution: 726.4205 + +- name: Washington PFML employer with fewer than 50 employees that deducts nothing pays the employee share. + period: 2026 + input: + employment_income: 100_000 + employer_headcount: 49 + employer_state_payroll_premium_deduction_share: 0 + state_code: WA + output: + wa_employee_paid_leave_contribution: 0 + # 1.13% * (1 - 0.2857) * 100,000 = 807.159. + wa_employer_paid_leave_contribution: 807.159 + +- name: Minnesota Paid Leave employee share scales with the deduction share. + period: 2026 + input: + employment_income: 100_000 + employer_state_payroll_premium_deduction_share: 0.5 + state_code: MN + output: + # 0.5 * 0.44% * 100,000 = 220. + mn_employee_paid_leave_contribution: 220 + employee_state_payroll_tax: 220 + +- name: New York PFL and DBL scale the capped permitted contribution. + period: 2026 + input: + employment_income: 100_000 + employer_state_payroll_premium_deduction_share: 0.5 + state_code: NY + output: + # 0.432% * 100,000 = 432 exceeds the 411.91 maximum; 0.5 * 411.91 = 205.955. + ny_employee_paid_family_leave_contribution: 205.955 + # 0.5% * 100,000 = 500 exceeds 52 weeks * 0.60 = 31.20; 0.5 * 31.20 = 15.60. + ny_employee_disability_benefits_contribution: 15.6 + ny_employee_state_payroll_tax: 221.555 + +- name: Mandatory employee withholding does not depend on the deduction share. + period: 2026 + input: + people: + ca_worker: + employment_income: 100_000 + employer_state_payroll_premium_deduction_share: 0 + ct_worker: + employment_income: 100_000 + employer_state_payroll_premium_deduction_share: 0 + pa_worker: + employment_income: 100_000 + employer_state_payroll_premium_deduction_share: 0 + tax_units: + ca_unit: + members: [ca_worker] + ct_unit: + members: [ct_worker] + pa_unit: + members: [pa_worker] + households: + ca_household: + members: [ca_worker] + state_code: CA + ct_household: + members: [ct_worker] + state_code: CT + pa_household: + members: [pa_worker] + state_code: PA + output: + # CA SDI 1.3%, CT Paid Leave 0.5%, PA UC 0.07%, each of 100,000. + employee_state_payroll_tax: [1_300, 500, 70] diff --git a/policyengine_us/variables/gov/states/co/tax/payroll/famli/co_employee_famli_contribution.py b/policyengine_us/variables/gov/states/co/tax/payroll/famli/co_employee_famli_contribution.py index c29f79b3e52..768f238266e 100644 --- a/policyengine_us/variables/gov/states/co/tax/payroll/famli/co_employee_famli_contribution.py +++ b/policyengine_us/variables/gov/states/co/tax/payroll/famli/co_employee_famli_contribution.py @@ -5,11 +5,20 @@ class co_employee_famli_contribution(Variable): value_type = float entity = Person label = "Colorado employee FAMLI contribution" - documentation = "Employee-side Colorado FAMLI payroll contribution." + documentation = ( + "Employee-side Colorado FAMLI premium. The employer remits the premium " + "and may deduct up to half of it from wages (C.R.S. 8-13.3-507(5)); " + "this is that permitted share times state_payroll_premium_deduction_share." + ) definition_period = YEAR unit = USD defined_for = StateCode.CO + reference = ( + "https://olls.info/crs/crs2025-title-08.pdf", + "https://www.sos.state.co.us/CCR/GenerateRulePdf.do?ruleVersionId=12341&fileName=7%20CCR%201107-1", + ) def formula(person, period, parameters): rate = parameters(period).gov.states.co.tax.payroll.famli.employee_rate - return rate * person("co_famli_taxable_wages", period) + permitted = rate * person("co_famli_taxable_wages", period) + return permitted * person("state_payroll_premium_deduction_share", period) diff --git a/policyengine_us/variables/gov/states/co/tax/payroll/famli/co_employer_famli_contribution.py b/policyengine_us/variables/gov/states/co/tax/payroll/famli/co_employer_famli_contribution.py index c29f2f349a5..09d2cee4b0c 100644 --- a/policyengine_us/variables/gov/states/co/tax/payroll/famli/co_employer_famli_contribution.py +++ b/policyengine_us/variables/gov/states/co/tax/payroll/famli/co_employer_famli_contribution.py @@ -5,13 +5,24 @@ class co_employer_famli_contribution(Variable): value_type = float entity = Person label = "Colorado employer FAMLI contribution" - documentation = "Employer-side Colorado FAMLI payroll contribution." + documentation = ( + "Employer-side Colorado FAMLI premium: the employer half for employers " + "with 10 or more employees, plus the part of the permitted employee " + "half the employer does not deduct, which it is treated as electing " + "to pay (7 CCR 1107-1 s. 1.4(6)(A))." + ) definition_period = YEAR unit = USD defined_for = StateCode.CO + reference = ( + "https://olls.info/crs/crs2025-title-08.pdf", + "https://www.sos.state.co.us/CCR/GenerateRulePdf.do?ruleVersionId=12341&fileName=7%20CCR%201107-1", + ) def formula(person, period, parameters): p = parameters(period).gov.states.co.tax.payroll.famli liable = person("employer_headcount", period) >= p.employer_headcount_threshold taxable_wages = person("co_famli_taxable_wages", period) - return where(liable, p.employer_rate * taxable_wages, 0) + employer_share = where(liable, p.employer_rate * taxable_wages, 0) + undeducted = 1 - person("state_payroll_premium_deduction_share", period) + return employer_share + undeducted * p.employee_rate * taxable_wages diff --git a/policyengine_us/variables/gov/states/de/tax/payroll/paid_leave/de_employee_paid_leave_contribution.py b/policyengine_us/variables/gov/states/de/tax/payroll/paid_leave/de_employee_paid_leave_contribution.py index aec23942ac6..e567136436e 100644 --- a/policyengine_us/variables/gov/states/de/tax/payroll/paid_leave/de_employee_paid_leave_contribution.py +++ b/policyengine_us/variables/gov/states/de/tax/payroll/paid_leave/de_employee_paid_leave_contribution.py @@ -6,18 +6,22 @@ class de_employee_paid_leave_contribution(Variable): entity = Person label = "Delaware employee paid leave contribution" documentation = ( - "Employee-side Delaware paid leave contribution, assuming the employer " - "withholds the maximum permitted employee share." + "Employee-side Delaware Paid Leave contribution. The employer remits " + "the contribution and may deduct up to half of it from wages " + "(19 Del. C. s. 3705(d)-(g)); this is that permitted share times " + "state_payroll_premium_deduction_share." ) definition_period = YEAR unit = USD defined_for = StateCode.DE + reference = "https://delcode.delaware.gov/title19/c037/index.html" def formula(person, period, parameters): p = parameters(period).gov.states.de.tax.payroll.paid_leave taxable_wages = person("taxable_earnings_for_social_security", period) - return ( + permitted = ( person("de_paid_leave_contribution_rate", period) * p.employee_share * taxable_wages ) + return permitted * person("state_payroll_premium_deduction_share", period) diff --git a/policyengine_us/variables/gov/states/de/tax/payroll/paid_leave/de_employer_paid_leave_contribution.py b/policyengine_us/variables/gov/states/de/tax/payroll/paid_leave/de_employer_paid_leave_contribution.py index 5ac4a6fce66..7036b6741d0 100644 --- a/policyengine_us/variables/gov/states/de/tax/payroll/paid_leave/de_employer_paid_leave_contribution.py +++ b/policyengine_us/variables/gov/states/de/tax/payroll/paid_leave/de_employer_paid_leave_contribution.py @@ -6,18 +6,24 @@ class de_employer_paid_leave_contribution(Variable): entity = Person label = "Delaware employer paid leave contribution" documentation = ( - "Employer-side Delaware paid leave contribution, assuming the employer " - "contributes only the minimum required share." + "Employer-side Delaware Paid Leave contribution: the half the " + "employer may not deduct, plus the part of the permitted employee " + "half it does not deduct, for which it remains liable " + "(19 Del. C. s. 3705(g))." ) definition_period = YEAR unit = USD defined_for = StateCode.DE + reference = "https://delcode.delaware.gov/title19/c037/index.html" def formula(person, period, parameters): p = parameters(period).gov.states.de.tax.payroll.paid_leave taxable_wages = person("taxable_earnings_for_social_security", period) + deducted = p.employee_share * person( + "state_payroll_premium_deduction_share", period + ) return ( person("de_paid_leave_contribution_rate", period) - * (1 - p.employee_share) + * (1 - deducted) * taxable_wages ) diff --git a/policyengine_us/variables/gov/states/ma/tax/payroll/paid_leave/ma_employee_paid_leave_contribution.py b/policyengine_us/variables/gov/states/ma/tax/payroll/paid_leave/ma_employee_paid_leave_contribution.py index 4b8a8f0fa76..d1d5c2f78ec 100644 --- a/policyengine_us/variables/gov/states/ma/tax/payroll/paid_leave/ma_employee_paid_leave_contribution.py +++ b/policyengine_us/variables/gov/states/ma/tax/payroll/paid_leave/ma_employee_paid_leave_contribution.py @@ -6,14 +6,25 @@ class ma_employee_paid_leave_contribution(Variable): entity = Person label = "Massachusetts employee paid leave contribution" documentation = ( - "Employee-side Massachusetts Paid Family and Medical Leave contribution, " - "assuming the employer withholds the maximum permitted employee share." + "Employee-side Massachusetts Paid Family and Medical Leave " + "contribution. The employer remits the contribution and may deduct up " + "to a capped share of the family and the medical contribution from " + "wages (M.G.L. c. 175M s. 6(c)-(d)); this is that permitted share " + "times state_payroll_premium_deduction_share." ) definition_period = YEAR unit = USD defined_for = StateCode.MA + reference = ( + "https://malegislature.gov/Laws/GeneralLaws/PartI/TitleXXII/Chapter175M/Section6", + "https://malegislature.gov/Laws/SessionLaws/Acts/2026/Chapter101", + ) def formula(person, period, parameters): p = parameters(period).gov.states.ma.tax.payroll.paid_leave - rate = p.family_rate + p.medical_rate * p.medical_employee_share - return rate * person("ma_paid_leave_taxable_wages", period) + rate = ( + p.family_rate * p.family_employee_share + + p.medical_rate * p.medical_employee_share + ) + permitted = rate * person("ma_paid_leave_taxable_wages", period) + return permitted * person("state_payroll_premium_deduction_share", period) diff --git a/policyengine_us/variables/gov/states/ma/tax/payroll/paid_leave/ma_employer_paid_leave_contribution.py b/policyengine_us/variables/gov/states/ma/tax/payroll/paid_leave/ma_employer_paid_leave_contribution.py index e2cc2236580..140cd0567e1 100644 --- a/policyengine_us/variables/gov/states/ma/tax/payroll/paid_leave/ma_employer_paid_leave_contribution.py +++ b/policyengine_us/variables/gov/states/ma/tax/payroll/paid_leave/ma_employer_paid_leave_contribution.py @@ -6,16 +6,33 @@ class ma_employer_paid_leave_contribution(Variable): entity = Person label = "Massachusetts employer paid leave contribution" documentation = ( - "Employer-side Massachusetts Paid Family and Medical Leave contribution, " - "using employer headcount as a proxy for covered individuals." + "Employer-side Massachusetts Paid Family and Medical Leave " + "contribution: the part of each contribution the employer may not " + "deduct, for employers with 25 or more covered individuals (employer " + "headcount is the proxy), plus the part of the permitted employee " + "share the employer does not deduct, which it must still remit " + "(458 CMR 2.05(5))." ) definition_period = YEAR unit = USD defined_for = StateCode.MA + reference = ( + "https://malegislature.gov/Laws/GeneralLaws/PartI/TitleXXII/Chapter175M/Section6", + "https://malegislature.gov/Laws/SessionLaws/Acts/2026/Chapter101", + "https://www.mass.gov/doc/458-cmr-2-family-and-medical-leave/download", + ) def formula(person, period, parameters): p = parameters(period).gov.states.ma.tax.payroll.paid_leave liable = person("employer_headcount", period) >= p.employer_headcount_threshold - rate = p.medical_rate * (1 - p.medical_employee_share) + employer_rate = p.family_rate * ( + 1 - p.family_employee_share + ) + p.medical_rate * (1 - p.medical_employee_share) + employee_rate = ( + p.family_rate * p.family_employee_share + + p.medical_rate * p.medical_employee_share + ) taxable_wages = person("ma_paid_leave_taxable_wages", period) - return where(liable, rate * taxable_wages, 0) + employer_share = where(liable, employer_rate * taxable_wages, 0) + undeducted = 1 - person("state_payroll_premium_deduction_share", period) + return employer_share + undeducted * employee_rate * taxable_wages diff --git a/policyengine_us/variables/gov/states/me/tax/payroll/paid_leave/me_employee_paid_leave_contribution.py b/policyengine_us/variables/gov/states/me/tax/payroll/paid_leave/me_employee_paid_leave_contribution.py index 874ba8d8f9c..736206e9457 100644 --- a/policyengine_us/variables/gov/states/me/tax/payroll/paid_leave/me_employee_paid_leave_contribution.py +++ b/policyengine_us/variables/gov/states/me/tax/payroll/paid_leave/me_employee_paid_leave_contribution.py @@ -6,13 +6,17 @@ class me_employee_paid_leave_contribution(Variable): entity = Person label = "Maine employee paid leave contribution" documentation = ( - "Employee-side Maine paid leave contribution, assuming the employer " - "withholds the maximum permitted employee share." + "Employee-side Maine Paid Family and Medical Leave premium. The " + "employer remits the premium and may deduct up to half of it from " + "wages (26 M.R.S. s. 850-F(5)); this is that permitted share times " + "state_payroll_premium_deduction_share." ) definition_period = YEAR unit = USD defined_for = StateCode.ME + reference = "https://legislature.maine.gov/legis/statutes/26/title26sec850-F.html" def formula(person, period, parameters): rate = parameters(period).gov.states.me.tax.payroll.paid_leave.employee_rate - return rate * person("me_paid_leave_taxable_wages", period) + permitted = rate * person("me_paid_leave_taxable_wages", period) + return permitted * person("state_payroll_premium_deduction_share", period) diff --git a/policyengine_us/variables/gov/states/me/tax/payroll/paid_leave/me_employer_paid_leave_contribution.py b/policyengine_us/variables/gov/states/me/tax/payroll/paid_leave/me_employer_paid_leave_contribution.py index 98e57db2fd2..c1ac657a992 100644 --- a/policyengine_us/variables/gov/states/me/tax/payroll/paid_leave/me_employer_paid_leave_contribution.py +++ b/policyengine_us/variables/gov/states/me/tax/payroll/paid_leave/me_employer_paid_leave_contribution.py @@ -6,15 +6,21 @@ class me_employer_paid_leave_contribution(Variable): entity = Person label = "Maine employer paid leave contribution" documentation = ( - "Employer-side Maine paid leave contribution, using employer headcount " - "as a proxy for covered employee count." + "Employer-side Maine Paid Family and Medical Leave premium: the " + "employer half for employers with 15 or more employees (employer " + "headcount is the proxy), plus the part of the permitted employee " + "half the employer does not deduct, which every employer must still " + "remit (26 M.R.S. s. 850-F(5))." ) definition_period = YEAR unit = USD defined_for = StateCode.ME + reference = "https://legislature.maine.gov/legis/statutes/26/title26sec850-F.html" def formula(person, period, parameters): p = parameters(period).gov.states.me.tax.payroll.paid_leave liable = person("employer_headcount", period) >= p.employer_headcount_threshold taxable_wages = person("me_paid_leave_taxable_wages", period) - return where(liable, p.employer_rate * taxable_wages, 0) + employer_share = where(liable, p.employer_rate * taxable_wages, 0) + undeducted = 1 - person("state_payroll_premium_deduction_share", period) + return employer_share + undeducted * p.employee_rate * taxable_wages diff --git a/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_employee_paid_leave_contribution.py b/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_employee_paid_leave_contribution.py index 74f346285ee..c2b427153e2 100644 --- a/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_employee_paid_leave_contribution.py +++ b/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_employee_paid_leave_contribution.py @@ -6,13 +6,20 @@ class mn_employee_paid_leave_contribution(Variable): entity = Person label = "Minnesota employee paid leave contribution" documentation = ( - "Employee-side Minnesota Paid Leave contribution, assuming the employer " - "withholds the maximum permitted employee share." + "Employee-side Minnesota Paid Leave premium. The premium is payable " + "by the employer, which must pay at least half; employees pay by wage " + "deduction only the part the employer does not pay (Minn. Stat. " + "268B.14, subds. 1, 3 and 5a). This is the largest such part, half " + "the premium at the full rate, times " + "state_payroll_premium_deduction_share. The employer premium is not " + "modeled." ) definition_period = YEAR unit = USD defined_for = StateCode.MN + reference = "https://www.revisor.mn.gov/statutes/cite/268B.14" def formula(person, period, parameters): rate = parameters(period).gov.states.mn.tax.payroll.paid_leave.employee_rate - return rate * person("mn_paid_leave_taxable_wages", period) + permitted = rate * person("mn_paid_leave_taxable_wages", period) + return permitted * person("state_payroll_premium_deduction_share", period) diff --git a/policyengine_us/variables/gov/states/ny/tax/payroll/disability_benefits/ny_employee_disability_benefits_contribution.py b/policyengine_us/variables/gov/states/ny/tax/payroll/disability_benefits/ny_employee_disability_benefits_contribution.py index c4154ab4e3c..608d52a4e74 100644 --- a/policyengine_us/variables/gov/states/ny/tax/payroll/disability_benefits/ny_employee_disability_benefits_contribution.py +++ b/policyengine_us/variables/gov/states/ny/tax/payroll/disability_benefits/ny_employee_disability_benefits_contribution.py @@ -6,16 +6,27 @@ class ny_employee_disability_benefits_contribution(Variable): entity = Person label = "New York employee disability benefits contribution" documentation = ( - "Employee-side New York Disability Benefits withholding, annualized " - "from the statutory weekly cap." + "Employee-side New York Disability Benefits contribution. The " + "employer is authorized, not required, to collect it by payroll " + "deduction, up to 0.5 percent of wages and 60 cents a week, and pays " + "the cost beyond what it collects (Workers' Compensation Law " + "ss. 209(3)(a), (4) and 210(1)); this is that permitted amount, " + "annualized from the weekly cap, times " + "state_payroll_premium_deduction_share. The employer's cost of " + "coverage is not modeled." ) definition_period = YEAR unit = USD defined_for = StateCode.NY + reference = ( + "https://www.nysenate.gov/legislation/laws/WKC/209", + "https://www.nysenate.gov/legislation/laws/WKC/210", + ) def formula(person, period, parameters): p = parameters(period).gov.states.ny.tax.payroll.disability_benefits uncapped = p.employee_rate * person( "ny_disability_benefits_taxable_wages", period ) - return min_(uncapped, p.annual_max_contribution) + permitted = min_(uncapped, p.annual_max_contribution) + return permitted * person("state_payroll_premium_deduction_share", period) diff --git a/policyengine_us/variables/gov/states/ny/tax/payroll/paid_family_leave/ny_employee_paid_family_leave_contribution.py b/policyengine_us/variables/gov/states/ny/tax/payroll/paid_family_leave/ny_employee_paid_family_leave_contribution.py index 6691dff1f8d..9b7fb67e980 100644 --- a/policyengine_us/variables/gov/states/ny/tax/payroll/paid_family_leave/ny_employee_paid_family_leave_contribution.py +++ b/policyengine_us/variables/gov/states/ny/tax/payroll/paid_family_leave/ny_employee_paid_family_leave_contribution.py @@ -5,14 +5,23 @@ class ny_employee_paid_family_leave_contribution(Variable): value_type = float entity = Person label = "New York employee paid family leave contribution" - documentation = "Employee-side New York Paid Family Leave contribution." + documentation = ( + "Employee-side New York Paid Family Leave contribution. The employer " + "is authorized, not required, to collect it by payroll deduction up " + "to the rate and annual maximum the Department of Financial Services " + "sets (Workers' Compensation Law s. 209(3)(b), (4)); this is that " + "permitted amount times state_payroll_premium_deduction_share. The " + "employer's cost of coverage is not modeled." + ) definition_period = YEAR unit = USD defined_for = StateCode.NY + reference = "https://www.nysenate.gov/legislation/laws/WKC/209" def formula(person, period, parameters): p = parameters(period).gov.states.ny.tax.payroll.paid_family_leave uncapped = p.employee_rate * person( "ny_paid_family_leave_taxable_wages", period ) - return min_(uncapped, p.annual_max_contribution) + permitted = min_(uncapped, p.annual_max_contribution) + return permitted * person("state_payroll_premium_deduction_share", period) 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..f2c84a26dae 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 @@ -23,8 +23,22 @@ class employee_state_payroll_tax(Variable): entity = TaxUnit label = "Employee state payroll taxes and contributions" documentation = ( - "Employee-side mandatory state payroll taxes and payroll-funded " - "contributions, aggregated from jurisdiction-specific rules." + "Employee-side state payroll taxes and payroll-funded contributions, " + "aggregated from jurisdiction-specific rules. Most components are " + "contributions the statute puts on the worker and requires the " + "employer to withhold: California SDI, Connecticut Paid Leave, New " + "Jersey TDI, FLI, UI and workforce contributions, Paid Leave Oregon " + "and the Oregon statewide transit tax, Pennsylvania UC, Rhode Island " + "TDI and WA Cares. The rest are premiums the statute puts on the " + "employer, which may deduct up to a share from wages without being " + "required to: 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. For those, the employee amount is the permitted share times " + "employer_state_payroll_premium_deduction_share, which defaults to 1, " + "the full permitted share; the employer-side variables carry any part " + "the employer does not deduct." ) definition_period = YEAR unit = USD 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 0f81b0058e2..34e864a0918 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 @@ -19,8 +19,10 @@ class employer_additional_state_payroll_tax(Variable): entity = Person label = "Employer additional state payroll taxes and contributions" documentation = ( - "Employer-side mandatory state payroll taxes and payroll-funded " - "contributions other than state unemployment insurance." + "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)." ) definition_period = YEAR unit = USD diff --git a/policyengine_us/variables/gov/states/tax/payroll/employer_total_additional_state_payroll_tax.py b/policyengine_us/variables/gov/states/tax/payroll/employer_total_additional_state_payroll_tax.py index 13102382f5a..b5ccf737615 100644 --- a/policyengine_us/variables/gov/states/tax/payroll/employer_total_additional_state_payroll_tax.py +++ b/policyengine_us/variables/gov/states/tax/payroll/employer_total_additional_state_payroll_tax.py @@ -7,7 +7,10 @@ class employer_total_additional_state_payroll_tax(Variable): label = "Employer total additional state payroll taxes and contributions" documentation = ( "Employer-level state payroll taxes and contributions other than " - "state unemployment insurance, from aggregate employer inputs." + "state unemployment insurance, from aggregate employer inputs. For " + "programs whose premium the statute puts on the employer, it applies " + "the person's state_payroll_premium_deduction_share to the whole " + "payroll, as if the employer deducts the same share for everyone." ) definition_period = YEAR unit = USD @@ -18,6 +21,9 @@ def formula(person, period, parameters): ss_taxable = person( "employer_total_taxable_earnings_for_social_security", period ) + # Programs whose premium the statute puts on the employer charge it + # the part of the permitted employee share it does not deduct. + deducted = person("state_payroll_premium_deduction_share", period) state_ui_taxable = person( "employer_total_taxable_earnings_for_state_unemployment_tax", period ) @@ -30,11 +36,14 @@ def formula(person, period, parameters): ) co_p = parameters(period).gov.states.co.tax.payroll.famli - co = where( - headcount >= co_p.employer_headcount_threshold, - co_p.employer_rate - * person("employer_total_co_famli_taxable_wages", period), - 0, + co_wages = person("employer_total_co_famli_taxable_wages", period) + co = ( + where( + headcount >= co_p.employer_headcount_threshold, + co_p.employer_rate * co_wages, + 0, + ) + + (1 - deducted) * co_p.employee_rate * co_wages ) dc = parameters( @@ -55,23 +64,35 @@ def formula(person, period, parameters): [0, de_p.parental_rate], default=de_full_coverage_rate, ) - de = de_contribution_rate * (1 - de_p.employee_share) * ss_taxable + de = de_contribution_rate * (1 - de_p.employee_share * deducted) * ss_taxable ma_p = parameters(period).gov.states.ma.tax.payroll.paid_leave - ma = where( - headcount >= ma_p.employer_headcount_threshold, - ma_p.medical_rate - * (1 - ma_p.medical_employee_share) - * person("employer_total_ma_paid_leave_taxable_wages", period), - 0, + ma_wages = person("employer_total_ma_paid_leave_taxable_wages", period) + ma_employer_rate = ma_p.family_rate * ( + 1 - ma_p.family_employee_share + ) + ma_p.medical_rate * (1 - ma_p.medical_employee_share) + ma_employee_rate = ( + ma_p.family_rate * ma_p.family_employee_share + + ma_p.medical_rate * ma_p.medical_employee_share + ) + ma = ( + where( + headcount >= ma_p.employer_headcount_threshold, + ma_employer_rate * ma_wages, + 0, + ) + + (1 - deducted) * ma_employee_rate * ma_wages ) me_p = parameters(period).gov.states.me.tax.payroll.paid_leave - me = where( - headcount >= me_p.employer_headcount_threshold, - me_p.employer_rate - * person("employer_total_me_paid_leave_taxable_wages", period), - 0, + me_wages = person("employer_total_me_paid_leave_taxable_wages", period) + me = ( + where( + headcount >= me_p.employer_headcount_threshold, + me_p.employer_rate * me_wages, + 0, + ) + + (1 - deducted) * me_p.employee_rate * me_wages ) or_p = parameters(period).gov.states["or"].tax.payroll.paid_leave @@ -89,19 +110,20 @@ def formula(person, period, parameters): * state_ui_taxable ) - vt = parameters( - period - ).gov.states.vt.tax.payroll.child_care.employer_rate * person( + vt_p = parameters(period).gov.states.vt.tax.payroll.child_care + vt = (vt_p.employer_rate + (1 - deducted) * vt_p.employee_rate) * person( "employer_total_vt_child_care_contribution_wages", period ) wa_p = parameters(period).gov.states.wa.tax.payroll.paid_leave - wa = where( - headcount >= wa_p.employer_headcount_threshold, - wa_p.total_rate - * wa_p.employer_share - * person("employer_total_wa_paid_leave_taxable_wages", period), - 0, + wa_wages = person("employer_total_wa_paid_leave_taxable_wages", period) + wa = ( + where( + headcount >= wa_p.employer_headcount_threshold, + wa_p.total_rate * wa_p.employer_share * wa_wages, + 0, + ) + + (1 - deducted) * wa_p.total_rate * (1 - wa_p.employer_share) * wa_wages ) return select( diff --git a/policyengine_us/variables/gov/states/tax/payroll/state_payroll_premium_deduction_share.py b/policyengine_us/variables/gov/states/tax/payroll/state_payroll_premium_deduction_share.py new file mode 100644 index 00000000000..6b0ca269a79 --- /dev/null +++ b/policyengine_us/variables/gov/states/tax/payroll/state_payroll_premium_deduction_share.py @@ -0,0 +1,21 @@ +from policyengine_us.model_api import * + + +class state_payroll_premium_deduction_share(Variable): + value_type = float + entity = Person + label = "State payroll premium deduction share" + documentation = ( + "Share of the permitted employee premium deducted from wages, for " + "state programs whose premium the statute puts on the employer and " + "lets it deduct up to a share from wages. It is " + "employer_state_payroll_premium_deduction_share held between 0 and 1: " + "an employer may not deduct more than the permitted share, and " + "deducting less than nothing has no meaning." + ) + unit = "/1" + definition_period = YEAR + + def formula(person, period, parameters): + share = person("employer_state_payroll_premium_deduction_share", period) + return clip(share, 0, 1) diff --git a/policyengine_us/variables/gov/states/vt/tax/payroll/child_care/vt_employee_child_care_contribution.py b/policyengine_us/variables/gov/states/vt/tax/payroll/child_care/vt_employee_child_care_contribution.py index 1014e8ca0f3..dd92530afe7 100644 --- a/policyengine_us/variables/gov/states/vt/tax/payroll/child_care/vt_employee_child_care_contribution.py +++ b/policyengine_us/variables/gov/states/vt/tax/payroll/child_care/vt_employee_child_care_contribution.py @@ -6,13 +6,17 @@ class vt_employee_child_care_contribution(Variable): entity = Person label = "Vermont employee child care contribution" documentation = ( - "Employee-side Vermont Child Care Contribution, assuming the employer " - "withholds the maximum permitted employee share." + "Employee-side Vermont Child Care Contribution. The employer pays the " + "contribution and may withhold up to a quarter of it from wages " + "(32 V.S.A. s. 10553(a)(1)); this is that permitted share times " + "state_payroll_premium_deduction_share." ) definition_period = YEAR unit = USD defined_for = StateCode.VT + reference = "https://legislature.vermont.gov/statutes/section/32/246/10553" def formula(person, period, parameters): rate = parameters(period).gov.states.vt.tax.payroll.child_care.employee_rate - return rate * person("vt_child_care_contribution_wages", period) + permitted = rate * person("vt_child_care_contribution_wages", period) + return permitted * person("state_payroll_premium_deduction_share", period) diff --git a/policyengine_us/variables/gov/states/vt/tax/payroll/child_care/vt_employer_child_care_contribution.py b/policyengine_us/variables/gov/states/vt/tax/payroll/child_care/vt_employer_child_care_contribution.py index f5aca0696d1..026d417832b 100644 --- a/policyengine_us/variables/gov/states/vt/tax/payroll/child_care/vt_employer_child_care_contribution.py +++ b/policyengine_us/variables/gov/states/vt/tax/payroll/child_care/vt_employer_child_care_contribution.py @@ -6,13 +6,18 @@ class vt_employer_child_care_contribution(Variable): entity = Person label = "Vermont employer child care contribution" documentation = ( - "Employer-side Vermont Child Care Contribution, assuming the employer " - "contributes only the minimum required employer share." + "Employer-side Vermont Child Care Contribution: the three quarters the " + "employer may not withhold, plus the part of the permitted employee " + "quarter it does not withhold, for which it is responsible " + "(32 V.S.A. s. 10553(c)(2))." ) definition_period = YEAR unit = USD defined_for = StateCode.VT + reference = "https://legislature.vermont.gov/statutes/section/32/246/10553" def formula(person, period, parameters): - rate = parameters(period).gov.states.vt.tax.payroll.child_care.employer_rate - return rate * person("vt_child_care_contribution_wages", period) + p = parameters(period).gov.states.vt.tax.payroll.child_care + wages = person("vt_child_care_contribution_wages", period) + undeducted = 1 - person("state_payroll_premium_deduction_share", period) + return (p.employer_rate + undeducted * p.employee_rate) * wages diff --git a/policyengine_us/variables/gov/states/wa/tax/payroll/paid_leave/wa_employee_paid_leave_contribution.py b/policyengine_us/variables/gov/states/wa/tax/payroll/paid_leave/wa_employee_paid_leave_contribution.py index b0b2c6b1c67..3854302f4d7 100644 --- a/policyengine_us/variables/gov/states/wa/tax/payroll/paid_leave/wa_employee_paid_leave_contribution.py +++ b/policyengine_us/variables/gov/states/wa/tax/payroll/paid_leave/wa_employee_paid_leave_contribution.py @@ -6,13 +6,22 @@ class wa_employee_paid_leave_contribution(Variable): entity = Person label = "Washington employee paid leave contribution" documentation = ( - "Employee-side Washington Paid Family and Medical Leave contribution." + "Employee-side Washington Paid Family and Medical Leave premium. The " + "employer collects and remits the premium, may deduct up to the " + "employee share from wages, and may elect to pay all or any portion " + "of it (RCW 50A.10.030(3), (7)); this is that permitted share times " + "state_payroll_premium_deduction_share." ) definition_period = YEAR unit = USD defined_for = StateCode.WA + reference = ( + "https://app.leg.wa.gov/RCW/default.aspx?cite=50A.10.030", + "https://app.leg.wa.gov/WAC/default.aspx?cite=192-510-065", + ) def formula(person, period, parameters): p = parameters(period).gov.states.wa.tax.payroll.paid_leave taxable_wages = person("wa_paid_leave_taxable_wages", period) - return p.total_rate * (1 - p.employer_share) * taxable_wages + permitted = p.total_rate * (1 - p.employer_share) * taxable_wages + return permitted * person("state_payroll_premium_deduction_share", period) diff --git a/policyengine_us/variables/gov/states/wa/tax/payroll/paid_leave/wa_employer_paid_leave_contribution.py b/policyengine_us/variables/gov/states/wa/tax/payroll/paid_leave/wa_employer_paid_leave_contribution.py index f4e26d381fb..d57d3c2873d 100644 --- a/policyengine_us/variables/gov/states/wa/tax/payroll/paid_leave/wa_employer_paid_leave_contribution.py +++ b/policyengine_us/variables/gov/states/wa/tax/payroll/paid_leave/wa_employer_paid_leave_contribution.py @@ -6,14 +6,29 @@ class wa_employer_paid_leave_contribution(Variable): entity = Person label = "Washington employer paid leave contribution" documentation = ( - "Employer-side Washington Paid Family and Medical Leave contribution." + "Employer-side Washington Paid Family and Medical Leave premium: the " + "employer portion for employers with 50 or more employees (employer " + "headcount is the proxy), plus the part of the permitted employee " + "share the employer does not deduct, which it is treated as electing " + "to pay (WAC 192-510-065(2))." ) definition_period = YEAR unit = USD defined_for = StateCode.WA + reference = ( + "https://app.leg.wa.gov/RCW/default.aspx?cite=50A.10.030", + "https://app.leg.wa.gov/WAC/default.aspx?cite=192-510-065", + ) def formula(person, period, parameters): p = parameters(period).gov.states.wa.tax.payroll.paid_leave liable = person("employer_headcount", period) >= p.employer_headcount_threshold taxable_wages = person("wa_paid_leave_taxable_wages", period) - return where(liable, p.total_rate * p.employer_share * taxable_wages, 0) + employer_share = where( + liable, p.total_rate * p.employer_share * taxable_wages, 0 + ) + undeducted = 1 - person("state_payroll_premium_deduction_share", period) + return ( + employer_share + + undeducted * p.total_rate * (1 - p.employer_share) * taxable_wages + ) diff --git a/policyengine_us/variables/input/employer_payroll.py b/policyengine_us/variables/input/employer_payroll.py index 4aff703de14..539b8355460 100644 --- a/policyengine_us/variables/input/employer_payroll.py +++ b/policyengine_us/variables/input/employer_payroll.py @@ -27,6 +27,27 @@ class employer_headcount(Variable): default_value = 100 +class employer_state_payroll_premium_deduction_share(Variable): + value_type = float + entity = Person + label = "Employer state payroll premium deduction share" + documentation = ( + "Share of the largest employee premium the law lets the employer " + "deduct from wages that the employer actually deducts, for state " + "programs whose premium the statute puts on 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 means the employer pays the whole " + "premium. The employer pays any part it does not deduct. Values " + "outside 0 to 1 are treated as the nearer bound." + ) + unit = "/1" + definition_period = YEAR + default_value = 1 + + class employer_quarterly_payroll_expense_override(Variable): value_type = float entity = Person From f16096861af36a0b3e306d44833e94a04aea2c6e Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 19 Oct 2026 15:05:43 -0400 Subject: [PATCH 02/10] Seed small-employer and capped-wage workers in the deduction share invariants Co-Authored-By: Claude Opus 5.5 --- .../de/tax/payroll/paid_leave/employee_share.yaml | 2 +- ...e_payroll_premium_deduction_share_invariants.py | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/policyengine_us/parameters/gov/states/de/tax/payroll/paid_leave/employee_share.yaml b/policyengine_us/parameters/gov/states/de/tax/payroll/paid_leave/employee_share.yaml index d30df3634d2..9961fdb19e2 100644 --- a/policyengine_us/parameters/gov/states/de/tax/payroll/paid_leave/employee_share.yaml +++ b/policyengine_us/parameters/gov/states/de/tax/payroll/paid_leave/employee_share.yaml @@ -1,4 +1,4 @@ -description: Default employee share of Delaware paid leave contributions. +description: Largest share of each Delaware Paid Leave contribution an employer may deduct from wages. values: 0000-01-01: 0 2025-01-01: 0.5 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 156c530ac80..9f1583c9dbb 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 @@ -126,10 +126,18 @@ def by_copy(simulation, variable, year): ) @given( st.lists(workers, min_size=4, max_size=30).map( - # One worker in each optional state at a share strictly between 0 and - # 1, so every program's properties compare positive amounts. + # Every optional state gets a worker at a large employer below every + # wage base, and one at a 9-employee employer (below the CO, MA, ME + # and WA size thresholds; DE does not cover it) above every wage base + # and New York's PFL maximum; Delaware also gets a 12-employee + # employer (parental leave only). Shares lie strictly between 0 and 1 + # or above 1, so each program's properties compare positive amounts, + # small-employer rules and capped amounts. lambda rows: ( - [(state, 60_000, 100, 0.5, 0.0) for state in OPTIONAL_STATES] + rows + [(state, 60_000, 100, 0.5, 0.0) for state in OPTIONAL_STATES] + + [(state, 250_000, 9, 0.3, 1.2) for state in OPTIONAL_STATES] + + [("DE", 250_000, 12, 0.3, 1.2)] + + rows ) ) ) From 8ec5126d383e113651014316717209d84ddb778f Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 19 Oct 2026 16:10:12 -0400 Subject: [PATCH 03/10] Address review r1: docstrings, Oregon line, employer-level coverage Describe the deduction-share programs by the employer's choice, not by who owes the premium (NY and WA put the contribution on the worker), note that MN and NY employer costs are not modeled, state why WA PFML is in and Paid Leave Oregon is out, and note that Rev. Rul. 2025-4's treatment of an employer-paid employee share as wages is not modeled. Cap the one-worker employer's taxable total in the invariants so the employer-level path is checked for every modeled state and size, pin the 2027 Massachusetts parameter values, and add file-page anchors to the PDF references. Co-Authored-By: Claude Opus 5.5 --- ...roll_premium_deduction_share_invariants.py | 35 +++++++++++++++---- .../famli/co_employee_famli_contribution.py | 5 +-- .../famli/co_employer_famli_contribution.py | 5 +-- .../ma_employer_paid_leave_contribution.py | 2 +- .../mn_employee_paid_leave_contribution.py | 9 ++--- .../tax/payroll/employee_state_payroll_tax.py | 27 +++++++++----- .../employer_additional_state_payroll_tax.py | 3 +- ...oyer_total_additional_state_payroll_tax.py | 11 +++--- .../state_payroll_premium_deduction_share.py | 4 +-- .../wa_employee_paid_leave_contribution.py | 5 ++- .../variables/input/employer_payroll.py | 19 +++++----- 11 files changed, 84 insertions(+), 41 deletions(-) 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 9f1583c9dbb..a6a02493a68 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 @@ -23,10 +23,12 @@ 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 (a - one-worker employer), `employer_total_additional_state_payroll_tax` equals - `employer_additional_state_payroll_tax`: the employer-level path and the - per-worker path agree at any share. +5. With employer-level wage totals equal to the worker's own wages, capped at + the Social Security base where the worker's are (a one-worker employer), + `employer_total_additional_state_payroll_tax` equals + `employer_additional_state_payroll_tax` in every modeled state: the + employer-level path and the per-worker path agree at any share and + employer size. 6. Massachusetts' 2027 swap of the family and medical deduction caps, paired with DFML's swap of the two rates (Acts 2026 c. 101 ss. 25, 26, 43(a)), leaves each worker's employee and employer amounts where they were in @@ -45,6 +47,7 @@ from hypothesis import strategies as st from policyengine_us import Simulation +from policyengine_us.system import system YEARS = (2026, 2027) OPTIONAL_STATES = ("CO", "DE", "MA", "ME", "MN", "NY", "VT", "WA") @@ -64,6 +67,10 @@ # Below the 2026 Social Security wage base, so every program's wage base is # the worker's wages in both years. UNCAPPED_WAGES = 184_500 +SOCIAL_SECURITY_CAP = { + year: system.parameters(f"{year}-01-01").gov.irs.payroll.social_security.cap + for year in YEARS +} COPIES = 3 # drawn share a, drawn share b, input left at its default # The model computes in float32: compare to that precision, never past a cent. CENT = 0.01 @@ -94,7 +101,7 @@ def simulate(rows): "employment_income": {year: wages for year in YEARS}, "employer_headcount": {year: headcount for year in YEARS}, "employer_total_taxable_earnings_for_social_security": { - year: wages for year in YEARS + year: min(wages, SOCIAL_SECURITY_CAP[year]) for year in YEARS }, "employer_total_payroll_tax_gross_wages": { year: wages for year in YEARS @@ -184,7 +191,7 @@ def test_state_payroll_premium_deduction_share_invariants(rows): assert (optional[lower, low] <= optional[lower, high] + CENT).all() assert (employer[lower, low] >= employer[lower, high] - CENT).all() # Property 5. - same_path = modeled & uncapped + same_path = modeled np.testing.assert_allclose( employer_total[same_path], employer[same_path], **TOLERANCE ) @@ -199,3 +206,19 @@ def test_state_payroll_premium_deduction_share_invariants(rows): np.testing.assert_allclose( employer_by_year[2027][ma], employer_by_year[2026][ma], **TOLERANCE ) + + +def test_massachusetts_2027_swaps_deduction_caps_and_rates(): + """Pins the four 2027 values: properties 1-6 hold whether they swap or not.""" + p = system.parameters.gov.states.ma.tax.payroll.paid_leave + # 2026: 100% of the 0.18% family rate and 40% of the 0.70% medical rate. + assert p.family_rate("2026-01-01") == 0.0018 + assert p.family_employee_share("2026-01-01") == 1 + assert p.medical_rate("2026-01-01") == 0.007 + assert p.medical_employee_share("2026-01-01") == 0.4 + # 2027 (Acts 2026 c. 101 ss. 25-26; DFML 2027 rates): 40% of the 0.70% + # family rate and 100% of the 0.18% medical rate. + assert p.family_rate("2027-01-01") == 0.007 + assert p.family_employee_share("2027-01-01") == 0.4 + assert p.medical_rate("2027-01-01") == 0.0018 + assert p.medical_employee_share("2027-01-01") == 1 diff --git a/policyengine_us/variables/gov/states/co/tax/payroll/famli/co_employee_famli_contribution.py b/policyengine_us/variables/gov/states/co/tax/payroll/famli/co_employee_famli_contribution.py index 768f238266e..23fb43677bb 100644 --- a/policyengine_us/variables/gov/states/co/tax/payroll/famli/co_employee_famli_contribution.py +++ b/policyengine_us/variables/gov/states/co/tax/payroll/famli/co_employee_famli_contribution.py @@ -14,8 +14,9 @@ class co_employee_famli_contribution(Variable): unit = USD defined_for = StateCode.CO reference = ( - "https://olls.info/crs/crs2025-title-08.pdf", - "https://www.sos.state.co.us/CCR/GenerateRulePdf.do?ruleVersionId=12341&fileName=7%20CCR%201107-1", + "https://olls.info/crs/crs2025-title-08.pdf#page=222", + # PDF pages 7-8 + "https://www.sos.state.co.us/CCR/GenerateRulePdf.do?ruleVersionId=12341&fileName=7%20CCR%201107-1#page=7", ) def formula(person, period, parameters): diff --git a/policyengine_us/variables/gov/states/co/tax/payroll/famli/co_employer_famli_contribution.py b/policyengine_us/variables/gov/states/co/tax/payroll/famli/co_employer_famli_contribution.py index 09d2cee4b0c..08ad3b5628f 100644 --- a/policyengine_us/variables/gov/states/co/tax/payroll/famli/co_employer_famli_contribution.py +++ b/policyengine_us/variables/gov/states/co/tax/payroll/famli/co_employer_famli_contribution.py @@ -15,8 +15,9 @@ class co_employer_famli_contribution(Variable): unit = USD defined_for = StateCode.CO reference = ( - "https://olls.info/crs/crs2025-title-08.pdf", - "https://www.sos.state.co.us/CCR/GenerateRulePdf.do?ruleVersionId=12341&fileName=7%20CCR%201107-1", + "https://olls.info/crs/crs2025-title-08.pdf#page=222", + # PDF pages 7-8 + "https://www.sos.state.co.us/CCR/GenerateRulePdf.do?ruleVersionId=12341&fileName=7%20CCR%201107-1#page=7", ) def formula(person, period, parameters): diff --git a/policyengine_us/variables/gov/states/ma/tax/payroll/paid_leave/ma_employer_paid_leave_contribution.py b/policyengine_us/variables/gov/states/ma/tax/payroll/paid_leave/ma_employer_paid_leave_contribution.py index 140cd0567e1..b88c5450ecc 100644 --- a/policyengine_us/variables/gov/states/ma/tax/payroll/paid_leave/ma_employer_paid_leave_contribution.py +++ b/policyengine_us/variables/gov/states/ma/tax/payroll/paid_leave/ma_employer_paid_leave_contribution.py @@ -19,7 +19,7 @@ class ma_employer_paid_leave_contribution(Variable): reference = ( "https://malegislature.gov/Laws/GeneralLaws/PartI/TitleXXII/Chapter175M/Section6", "https://malegislature.gov/Laws/SessionLaws/Acts/2026/Chapter101", - "https://www.mass.gov/doc/458-cmr-2-family-and-medical-leave/download", + "https://www.mass.gov/doc/458-cmr-2-family-and-medical-leave/download#page=12", ) def formula(person, period, parameters): diff --git a/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_employee_paid_leave_contribution.py b/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_employee_paid_leave_contribution.py index c2b427153e2..0fdf8023416 100644 --- a/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_employee_paid_leave_contribution.py +++ b/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_employee_paid_leave_contribution.py @@ -7,10 +7,11 @@ class mn_employee_paid_leave_contribution(Variable): label = "Minnesota employee paid leave contribution" documentation = ( "Employee-side Minnesota Paid Leave premium. The premium is payable " - "by the employer, which must pay at least half; employees pay by wage " - "deduction only the part the employer does not pay (Minn. Stat. " - "268B.14, subds. 1, 3 and 5a). This is the largest such part, half " - "the premium at the full rate, times " + "by the employer, which must pay at least half (a small employer " + "under subd. 5a, at least a quarter of the full rate); employees pay " + "by wage deduction only the part the employer does not pay (Minn. " + "Stat. 268B.14, subds. 1, 3 and 5a). This is the largest such part, " + "half the premium at the full rate, times " "state_payroll_premium_deduction_share. The employer premium is not " "modeled." ) 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 f2c84a26dae..c448aae2b51 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 @@ -29,16 +29,25 @@ class employee_state_payroll_tax(Variable): "employer to withhold: California SDI, Connecticut Paid Leave, New " "Jersey TDI, FLI, UI and workforce contributions, Paid Leave Oregon " "and the Oregon statewide transit tax, Pennsylvania UC, Rhode Island " - "TDI and WA Cares. The rest are premiums the statute puts on the " - "employer, which may deduct up to a share from wages without being " - "required to: 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. For those, the employee amount is the permitted share times " + "TDI and WA Cares. In the rest, 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 " + "Paid Family and Medical Leave, Minnesota Paid Leave and the Vermont " + "Child Care Contribution, whose premium the statute puts on the " + "employer; New York Paid Family Leave and Disability Benefits, which " + "the employer is authorized but not required to collect; and " + "Washington Paid Family and Medical Leave, whose employee share the " + "employer may deduct up to and may pay in any part. For those, the " + "employee amount is the permitted share times " "employer_state_payroll_premium_deduction_share, which defaults to 1, " - "the full permitted share; the employer-side variables carry any part " - "the employer does not deduct." + "the full permitted share, and the employer-side variables carry any " + "part the employer does not deduct, except in Minnesota and New York, " + "whose employer cost is not modeled. Not modeled: an employer's " + "election to pay a required employee contribution as a benefit (Paid " + "Leave Oregon, ORS 657B.150(5)) or voluntary payment of other " + "mandatory contributions, and the federal treatment of an employer's " + "payment of an employee share as the employee's wages (Rev. Rul. " + "2025-4)." ) definition_period = YEAR unit = USD 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 34e864a0918..0bbb5e52be0 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,7 +22,8 @@ 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)." + "(employer_state_payroll_premium_deduction_share). 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/gov/states/tax/payroll/employer_total_additional_state_payroll_tax.py b/policyengine_us/variables/gov/states/tax/payroll/employer_total_additional_state_payroll_tax.py index b5ccf737615..2c41614b842 100644 --- a/policyengine_us/variables/gov/states/tax/payroll/employer_total_additional_state_payroll_tax.py +++ b/policyengine_us/variables/gov/states/tax/payroll/employer_total_additional_state_payroll_tax.py @@ -8,9 +8,10 @@ class employer_total_additional_state_payroll_tax(Variable): documentation = ( "Employer-level state payroll taxes and contributions other than " "state unemployment insurance, from aggregate employer inputs. For " - "programs whose premium the statute puts on the employer, it applies " - "the person's state_payroll_premium_deduction_share to the whole " - "payroll, as if the employer deducts the same share for everyone." + "programs where the law caps the employer's deduction from wages and " + "the employer pays what it does not deduct, it applies the person's " + "state_payroll_premium_deduction_share to the whole payroll, as if " + "the employer deducts the same share for everyone." ) definition_period = YEAR unit = USD @@ -21,8 +22,8 @@ def formula(person, period, parameters): ss_taxable = person( "employer_total_taxable_earnings_for_social_security", period ) - # Programs whose premium the statute puts on the employer charge it - # the part of the permitted employee share it does not deduct. + # Where the law caps the employer's deduction from wages, the employer + # pays the part of the permitted employee share it does not deduct. deducted = person("state_payroll_premium_deduction_share", period) state_ui_taxable = person( "employer_total_taxable_earnings_for_state_unemployment_tax", period diff --git a/policyengine_us/variables/gov/states/tax/payroll/state_payroll_premium_deduction_share.py b/policyengine_us/variables/gov/states/tax/payroll/state_payroll_premium_deduction_share.py index 6b0ca269a79..9abd4cbe95f 100644 --- a/policyengine_us/variables/gov/states/tax/payroll/state_payroll_premium_deduction_share.py +++ b/policyengine_us/variables/gov/states/tax/payroll/state_payroll_premium_deduction_share.py @@ -7,8 +7,8 @@ class state_payroll_premium_deduction_share(Variable): label = "State payroll premium deduction share" documentation = ( "Share of the permitted employee premium deducted from wages, for " - "state programs whose premium the statute puts on the employer and " - "lets it deduct up to a share from wages. It is " + "state programs where the law caps the employer's deduction and " + "leaves the amount to the employer. It is " "employer_state_payroll_premium_deduction_share held between 0 and 1: " "an employer may not deduct more than the permitted share, and " "deducting less than nothing has no meaning." diff --git a/policyengine_us/variables/gov/states/wa/tax/payroll/paid_leave/wa_employee_paid_leave_contribution.py b/policyengine_us/variables/gov/states/wa/tax/payroll/paid_leave/wa_employee_paid_leave_contribution.py index 3854302f4d7..a77513c9c91 100644 --- a/policyengine_us/variables/gov/states/wa/tax/payroll/paid_leave/wa_employee_paid_leave_contribution.py +++ b/policyengine_us/variables/gov/states/wa/tax/payroll/paid_leave/wa_employee_paid_leave_contribution.py @@ -10,7 +10,10 @@ class wa_employee_paid_leave_contribution(Variable): "employer collects and remits the premium, may deduct up to the " "employee share from wages, and may elect to pay all or any portion " "of it (RCW 50A.10.030(3), (7)); this is that permitted share times " - "state_payroll_premium_deduction_share." + "state_payroll_premium_deduction_share. Unlike Paid Leave Oregon, " + "whose employer must deduct a fixed 60 percent (ORS 657B.150(2)(b)), " + "the deduction itself is permissive and capped, and a deduction not " + "taken is the employer's (WAC 192-510-065(2))." ) definition_period = YEAR unit = USD diff --git a/policyengine_us/variables/input/employer_payroll.py b/policyengine_us/variables/input/employer_payroll.py index 539b8355460..7b69a15ec1f 100644 --- a/policyengine_us/variables/input/employer_payroll.py +++ b/policyengine_us/variables/input/employer_payroll.py @@ -34,14 +34,17 @@ class employer_state_payroll_premium_deduction_share(Variable): documentation = ( "Share of the largest employee premium the law lets the employer " "deduct from wages that the employer actually deducts, for state " - "programs whose premium the statute puts on 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 means the employer pays the whole " - "premium. The employer pays any part it does not deduct. Values " - "outside 0 to 1 are treated as the nearer bound." + "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. The employer pays any part it does not deduct, which the " + "employer-side variables carry except in Minnesota and New York, " + "whose employer cost is not modeled. The federal treatment of that " + "payment as the employee's wages (Rev. Rul. 2025-4) is not modeled. " + "Values outside 0 to 1 are treated as the nearer bound." ) unit = "/1" definition_period = YEAR From 727ebcb881094b80dbf111670a055b50e7e43875 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 19 Oct 2026 16:33:38 -0400 Subject: [PATCH 04/10] Scope the Rev. Rul. 2025-4 note to the state paid leave funds Co-Authored-By: Claude Opus 5.5 --- .../gov/states/tax/payroll/employee_state_payroll_tax.py | 8 +++++--- policyengine_us/variables/input/employer_payroll.py | 5 +++-- 2 files changed, 8 insertions(+), 5 deletions(-) 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 c448aae2b51..91e48d04977 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 @@ -45,9 +45,11 @@ class employee_state_payroll_tax(Variable): "whose employer cost is not modeled. Not modeled: an employer's " "election to pay a required employee contribution as a benefit (Paid " "Leave Oregon, ORS 657B.150(5)) or voluntary payment of other " - "mandatory contributions, and the federal treatment of an employer's " - "payment of an employee share as the employee's wages (Rev. Rul. " - "2025-4)." + "mandatory contributions, and, for the state paid family and medical " + "leave funds (Colorado, Delaware, Maine, Massachusetts, Minnesota and " + "Washington), the federal treatment of an employer's payment of the " + "employee share as the employee's wages (Rev. Rul. 2025-4; Notice " + "2026-6)." ) definition_period = YEAR unit = USD diff --git a/policyengine_us/variables/input/employer_payroll.py b/policyengine_us/variables/input/employer_payroll.py index 7b69a15ec1f..c6c53cd6a67 100644 --- a/policyengine_us/variables/input/employer_payroll.py +++ b/policyengine_us/variables/input/employer_payroll.py @@ -42,8 +42,9 @@ class employer_state_payroll_premium_deduction_share(Variable): "Leave. 1, the default, deducts the full permitted share; 0 deducts " "nothing. The employer pays any part it does not deduct, which the " "employer-side variables carry except in Minnesota and New York, " - "whose employer cost is not modeled. The federal treatment of that " - "payment as the employee's wages (Rev. Rul. 2025-4) is not modeled. " + "whose employer cost is not modeled. For the state paid family and " + "medical leave funds, the federal treatment of that payment 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." ) unit = "/1" From 156453fd59909e8b925b79bfede6da5f04c7e0e6 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 19 Oct 2026 17:25:08 -0400 Subject: [PATCH 05/10] Model the Minnesota Paid Leave employer premium Minn. Stat. 268B.14 makes the premium payable by the employer, which pays at least half (subd. 3); a small employer under subd. 5a pays 75% of the rate and at least a quarter of the full rate. The employer pays the premium less the employee deduction, so the part of the permitted employee share an employer does not deduct (PR #10285) now lands on the employer side. - New parameters: rate (0.88% for 2026 and 2027), employer_minimum_share, and small_employer/{rate_share, employer_minimum_share, max_headcount, max_average_quarterly_wage}. They replace employee_rate (0.44%), which is now derived. - New variables: mn_paid_leave_small_employer (employer_headcount and a new average quarterly wage input stand in for subds. 5b and 5c), mn_paid_leave_premium_rate, mn_paid_leave_permitted_employee_rate, mn_paid_leave_premium, mn_employer_paid_leave_contribution, mn_employer_additional_state_payroll_tax and employer_total_mn_paid_leave_taxable_wages. - employer_additional_state_payroll_tax and the employer-level employer_total_additional_state_payroll_tax include Minnesota. - YAML cases computed from the statute and DEED rates, MN rows in the payroll contribution tests, and MN in the deduction-share invariants, with a new statute-literal property for Minnesota. Co-Authored-By: Claude Opus 5.5 --- .../mn-employer-paid-leave-premium.added.md | 1 + .../tax/payroll/paid_leave/employee_rate.yaml | 14 - .../paid_leave/employer_minimum_share.yaml | 14 + .../mn/tax/payroll/paid_leave/rate.yaml | 18 ++ .../employer_minimum_share.yaml | 12 + .../max_average_quarterly_wage.yaml | 14 + .../small_employer/max_headcount.yaml | 12 + .../paid_leave/small_employer/rate_share.yaml | 14 + .../tests/core/test_payroll_contributions.py | 19 ++ ...roll_premium_deduction_share_invariants.py | 111 ++++++-- .../mn_employer_paid_leave_contribution.yaml | 256 ++++++++++++++++++ ...state_payroll_premium_deduction_share.yaml | 4 +- ...n_employer_additional_state_payroll_tax.py | 12 + .../mn_employee_paid_leave_contribution.py | 8 +- .../mn_employer_paid_leave_contribution.py | 24 ++ ...d_leave_employer_average_quarterly_wage.py | 22 ++ .../mn_paid_leave_permitted_employee_rate.py | 29 ++ .../paid_leave/mn_paid_leave_premium.py | 21 ++ .../paid_leave/mn_paid_leave_premium_rate.py | 24 ++ .../mn_paid_leave_small_employer.py | 28 ++ .../paid_leave/mn_paid_leave_taxable_wages.py | 23 ++ .../tax/payroll/employee_state_payroll_tax.py | 4 +- .../employer_additional_state_payroll_tax.py | 5 +- ...oyer_total_additional_state_payroll_tax.py | 11 +- .../variables/input/employer_payroll.py | 9 +- 25 files changed, 662 insertions(+), 47 deletions(-) create mode 100644 changelog.d/mn-employer-paid-leave-premium.added.md delete mode 100644 policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/employee_rate.yaml create mode 100644 policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/employer_minimum_share.yaml create mode 100644 policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/rate.yaml create mode 100644 policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/employer_minimum_share.yaml create mode 100644 policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_average_quarterly_wage.yaml create mode 100644 policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_headcount.yaml create mode 100644 policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/rate_share.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/mn/tax/payroll/paid_leave/mn_employer_paid_leave_contribution.yaml create mode 100644 policyengine_us/variables/gov/states/mn/tax/payroll/mn_employer_additional_state_payroll_tax.py create mode 100644 policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_employer_paid_leave_contribution.py create mode 100644 policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_paid_leave_employer_average_quarterly_wage.py create mode 100644 policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_paid_leave_permitted_employee_rate.py create mode 100644 policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_paid_leave_premium.py create mode 100644 policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_paid_leave_premium_rate.py create mode 100644 policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_paid_leave_small_employer.py diff --git a/changelog.d/mn-employer-paid-leave-premium.added.md b/changelog.d/mn-employer-paid-leave-premium.added.md new file mode 100644 index 00000000000..fa092c00e41 --- /dev/null +++ b/changelog.d/mn-employer-paid-leave-premium.added.md @@ -0,0 +1 @@ +Model the Minnesota Paid Leave employer premium, including the small employer rate (Minn. Stat. 268B.14), and include it in employer payroll taxes. The Minnesota `employee_rate` parameter is replaced by `rate` and `employer_minimum_share`. diff --git a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/employee_rate.yaml b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/employee_rate.yaml deleted file mode 100644 index a9d643c601a..00000000000 --- a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/employee_rate.yaml +++ /dev/null @@ -1,14 +0,0 @@ -description: Employee-side Minnesota Paid Leave contribution rate. -values: - 0000-01-01: 0 - 2026-01-01: 0.0044 - -metadata: - period: year - unit: /1 - label: Minnesota Paid Leave employee rate - reference: - - title: Minnesota DEED confirms Paid Leave premium rate - href: https://mn.gov/deed/newscenter/press-releases/?id=1045-670064 - - title: Minnesota Statutes Section 268B.14 - href: https://www.revisor.mn.gov/statutes/cite/268B.14 diff --git a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/employer_minimum_share.yaml b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/employer_minimum_share.yaml new file mode 100644 index 00000000000..8957d9a9ac8 --- /dev/null +++ b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/employer_minimum_share.yaml @@ -0,0 +1,14 @@ +description: Minnesota requires employers to pay at least this share of the Paid Leave premium; employees pay the remaining portion, if any, through a wage deduction. +values: + 0000-01-01: 0 + 2026-01-01: 0.5 + +metadata: + period: year + unit: /1 + label: Minnesota Paid Leave employer minimum premium share + reference: + - title: Minnesota Statutes Section 268B.14, subdivision 3 + href: https://www.revisor.mn.gov/statutes/cite/268B.14 + - title: Minnesota Paid Leave, Premium rate and contributions ("employers can collect up to 0.44% from employees") + href: https://pl.mn.gov/resources/calculators/premium-rate-and-contributions diff --git a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/rate.yaml b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/rate.yaml new file mode 100644 index 00000000000..50d0d86dec9 --- /dev/null +++ b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/rate.yaml @@ -0,0 +1,18 @@ +description: Minnesota charges this Paid Leave premium rate on taxable wages for an employer participating in both the family and medical benefit programs. +values: + 0000-01-01: 0 + # Statutory 0.7% (subd. 6), adjusted by the commissioner under subd. 7. + 2026-01-01: 0.0088 + 2027-01-01: 0.0088 + +metadata: + period: year + unit: /1 + label: Minnesota Paid Leave premium rate + reference: + - title: Minnesota Statutes Section 268B.14, subdivisions 1, 6 and 7 + href: https://www.revisor.mn.gov/statutes/cite/268B.14 + - title: Minnesota DEED confirms Paid Leave premium rate + href: https://mn.gov/deed/newscenter/press-releases/?id=1045-670064 + - title: Minnesota Paid Leave, Premium rate and contributions ("For 2026 and 2027, the Paid Leave premium rate is 0.88%") + href: https://pl.mn.gov/resources/calculators/premium-rate-and-contributions diff --git a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/employer_minimum_share.yaml b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/employer_minimum_share.yaml new file mode 100644 index 00000000000..24f7cfa35f1 --- /dev/null +++ b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/employer_minimum_share.yaml @@ -0,0 +1,12 @@ +description: Minnesota requires small employers to pay at least this share of the full Paid Leave premium rate; employees pay the remaining portion of the small employer premium, if any, through a wage deduction. +values: + 0000-01-01: 0 + 2026-01-01: 0.25 + +metadata: + period: year + unit: /1 + label: Minnesota Paid Leave small employer minimum premium share + reference: + - title: Minnesota Statutes Section 268B.14, subdivision 5a(b)(1) + href: https://www.revisor.mn.gov/statutes/cite/268B.14 diff --git a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_average_quarterly_wage.yaml b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_average_quarterly_wage.yaml new file mode 100644 index 00000000000..a401fcb39ce --- /dev/null +++ b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_average_quarterly_wage.yaml @@ -0,0 +1,14 @@ +description: Minnesota limits the Paid Leave small employer premium rate to employers whose average quarterly wage is at most this amount, 150 percent of the state's average wage in covered employment for the basis period. +values: + 0000-01-01: 0 + # Basis period October 2024 to September 2025, for 2026 premiums. + 2026-01-01: 27_745.88 + +metadata: + unit: currency-USD + label: Minnesota Paid Leave small employer maximum average quarterly wage + reference: + - title: Minnesota Statutes Section 268B.14, subdivisions 5a(a)(2), 5b(a) and 5c + href: https://www.revisor.mn.gov/statutes/cite/268B.14 + - title: Minnesota Paid Leave, Premium rate and contributions ("150% of the statewide average wage, currently $27,745.88 per quarter") + href: https://pl.mn.gov/resources/calculators/premium-rate-and-contributions diff --git a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_headcount.yaml b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_headcount.yaml new file mode 100644 index 00000000000..fcb71239560 --- /dev/null +++ b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_headcount.yaml @@ -0,0 +1,12 @@ +description: Minnesota limits the Paid Leave small employer premium rate to employers with at most this many employees. +values: + 0000-01-01: 0 + 2026-01-01: 30 + +metadata: + period: year + unit: person + label: Minnesota Paid Leave small employer maximum employees + reference: + - title: Minnesota Statutes Section 268B.14, subdivisions 5a(a)(1) and 5b + href: https://www.revisor.mn.gov/statutes/cite/268B.14 diff --git a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/rate_share.yaml b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/rate_share.yaml new file mode 100644 index 00000000000..3c2b27b90ba --- /dev/null +++ b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/rate_share.yaml @@ -0,0 +1,14 @@ +description: Minnesota charges small employers this share of the full Paid Leave premium rate. +values: + 0000-01-01: 0 + 2026-01-01: 0.75 + +metadata: + period: year + unit: /1 + label: Minnesota Paid Leave small employer premium rate share + reference: + - title: Minnesota Statutes Section 268B.14, subdivision 5a(b) + href: https://www.revisor.mn.gov/statutes/cite/268B.14 + - title: Minnesota Paid Leave, Premium rate and contributions ("The Paid Leave small employer rate is 0.66%") + href: https://pl.mn.gov/resources/calculators/premium-rate-and-contributions diff --git a/policyengine_us/tests/core/test_payroll_contributions.py b/policyengine_us/tests/core/test_payroll_contributions.py index 6939b040785..c8111354484 100644 --- a/policyengine_us/tests/core/test_payroll_contributions.py +++ b/policyengine_us/tests/core/test_payroll_contributions.py @@ -78,6 +78,9 @@ def calculate(sim: Simulation, variable: str) -> float: pytest.param( "ME", "employer_total_me_paid_leave_taxable_wages", 4_000, id="ME" ), + pytest.param( + "MN", "employer_total_mn_paid_leave_taxable_wages", 4_000, id="MN" + ), pytest.param( "OR", "employer_total_or_paid_leave_taxable_wages", 4_000, id="OR" ), @@ -131,6 +134,7 @@ def make_employer_total_simulation( employer_total_dc_paid_leave_taxable_wages: float = WAGES, employer_total_ma_paid_leave_taxable_wages: float = WAGES, employer_total_me_paid_leave_taxable_wages: float = WAGES, + employer_total_mn_paid_leave_taxable_wages: float = WAGES, employer_total_or_paid_leave_taxable_wages: float = WAGES, employer_total_vt_child_care_contribution_wages: float = WAGES, employer_total_wa_paid_leave_taxable_wages: float = WAGES, @@ -182,6 +186,9 @@ def make_employer_total_simulation( "employer_total_me_paid_leave_taxable_wages": { PERIOD: employer_total_me_paid_leave_taxable_wages }, + "employer_total_mn_paid_leave_taxable_wages": { + PERIOD: employer_total_mn_paid_leave_taxable_wages + }, "employer_total_or_paid_leave_taxable_wages": { PERIOD: employer_total_or_paid_leave_taxable_wages }, @@ -508,6 +515,14 @@ def test_employee_payroll_tax_zero_when_payroll_tax_abolished(): }, id="ME", ), + pytest.param( + "MN", + { + "mn_employer_paid_leave_contribution": 440, + "mn_employer_additional_state_payroll_tax": 440, + }, + id="MN", + ), pytest.param( "OR", { @@ -637,6 +652,7 @@ def test_small_employer_thresholds_zero_employer_paid_leave_share( { "mn_paid_leave_taxable_wages": 7_000, "mn_employee_paid_leave_contribution": 30.8, + "mn_employer_paid_leave_contribution": 30.8, }, id="MN", ), @@ -962,6 +978,7 @@ def test_new_york_mctmt_employer_tax( pytest.param("DE", 100, WAGES, 9_000, 400, id="DE"), pytest.param("MA", 100, WAGES, 9_000, 420, id="MA"), pytest.param("ME", 100, WAGES, 9_000, 500, id="ME"), + pytest.param("MN", 100, WAGES, 9_000, 440, id="MN"), pytest.param("OR", 100, WAGES, 9_000, 400, id="OR"), pytest.param("RI", 100, WAGES, 29_200, 61.32, id="RI"), pytest.param("VT", 100, WAGES, 9_000, 330, id="VT"), @@ -996,6 +1013,7 @@ def test_employer_total_additional_state_payroll_tax( pytest.param("DE", 20, id="DE"), pytest.param("MA", 29.4, id="MA"), pytest.param("ME", 35, id="ME"), + pytest.param("MN", 30.8, id="MN"), pytest.param("OR", 28, id="OR"), pytest.param("RI", 8.4, id="RI"), pytest.param("VT", 13.2, id="VT"), @@ -1014,6 +1032,7 @@ def test_employer_total_additional_state_payroll_tax_uses_state_specific_wage_ba employer_total_dc_paid_leave_taxable_wages=7_000, employer_total_ma_paid_leave_taxable_wages=7_000, employer_total_me_paid_leave_taxable_wages=7_000, + employer_total_mn_paid_leave_taxable_wages=7_000, employer_total_or_paid_leave_taxable_wages=7_000, employer_total_vt_child_care_contribution_wages=4_000, employer_total_wa_paid_leave_taxable_wages=7_000, 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 a6a02493a68..f5cce7b0ac2 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 @@ -8,23 +8,25 @@ permitted employee share the employer deducts (default 1); the employer pays the rest. Hypothesis draws workers in those states, in states whose contributions are mandatory withholding, and in a state with neither, with any -wages, employer size and share, and runs each worker three times in one -simulation: with two drawn shares and with the input left at its default. For -every worker, in 2026 and 2027: +wages, employer size, share and (for Minnesota's small employer test) employer +average wage, and runs each worker three times in one simulation: with two +drawn shares and with the input left at its default. For every worker, in 2026 +and 2027: 1. The employee amount of the optional programs is the clipped share times the amount at the default, so the default is the full permitted share and a share outside 0 to 1 acts as the nearer bound. 2. The mandatory components of `employee_state_payroll_tax` do not depend on 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. +3. Where the employer side is modeled (CO, DE, MA, ME, MN, VT, WA), employee + plus employer premium does not depend on the share: the employer pays + exactly what it does not deduct. The New York employer cost is not + modeled, so its 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 - the Social Security base where the worker's are (a one-worker employer), + the Social Security base where the worker's are and at Minnesota's rounded + base for Minnesota (a one-worker employer), `employer_total_additional_state_payroll_tax` equals `employer_additional_state_payroll_tax` in every modeled state: the employer-level path and the per-worker path agree at any share and @@ -33,6 +35,13 @@ with DFML's swap of the two rates (Acts 2026 c. 101 ss. 25, 26, 43(a)), leaves each worker's employee and employer amounts where they were in 2026, for wages under both years' wage bases. +7. Minnesota matches the statute computed independently here: the premium is + 0.88% of taxable wages, or 0.66% for a small employer (30 or fewer + employees and an average quarterly wage of at most $27,745.88); employee + plus employer amounts equal it at any share; and the employer pays its + minimum (half the premium, or 0.22% of wages for a small employer) plus + the part of the rest it does not deduct (Minn. Stat. 268B.14, subds. 1, + 3 and 5a). Amounts are compared to float32 precision, within a cent. @@ -52,7 +61,7 @@ YEARS = (2026, 2027) OPTIONAL_STATES = ("CO", "DE", "MA", "ME", "MN", "NY", "VT", "WA") MANDATORY_STATES = ("CA", "CT", "NJ", "OR", "PA", "RI") -EMPLOYER_MODELED_STATES = ("CO", "DE", "MA", "ME", "VT", "WA") +EMPLOYER_MODELED_STATES = ("CO", "DE", "MA", "ME", "MN", "VT", "WA") OPTIONAL_EMPLOYEE_VARIABLES = ( "co_employee_famli_contribution", "de_employee_paid_leave_contribution", @@ -71,6 +80,19 @@ year: system.parameters(f"{year}-01-01").gov.irs.payroll.social_security.cap for year in YEARS } +# Minn. Stat. 268B.01, subd. 43: the Social Security base rounded to the +# nearest $1,000. +MN_TAXABLE_WAGE_BASE = { + year: 1_000 * np.floor(SOCIAL_SECURITY_CAP[year] / 1_000 + 0.5) for year in YEARS +} +# Minnesota Paid Leave, from the statute and DEED, independent of the +# parameter files: 0.88% in 2026 and 2027 (subd. 7); employer minimum 50% +# (subd. 3); small employers 75% of the rate with an employer minimum of 25% +# of the full rate (subd. 5a(b)), if 30 or fewer employees and an average +# quarterly wage of at most $27,745.88 (subd. 5a(a)). +MN_RATE = 0.0088 +MN_SMALL_MAX_HEADCOUNT = 30 +MN_SMALL_MAX_AVERAGE_QUARTERLY_WAGE = 27_745.88 COPIES = 3 # drawn share a, drawn share b, input left at its default # The model computes in float32: compare to that precision, never past a cent. CENT = 0.01 @@ -80,12 +102,17 @@ st.sampled_from([0.0, 0.25, 0.5, 1.0, -0.5, 1.5]), st.floats(-0.5, 1.5, allow_nan=False), ) +average_quarterly_wage = st.one_of( + st.sampled_from([0.0, 27_745.88, 27_745.89, 40_000.0]), + st.floats(0, 60_000, allow_nan=False), +) workers = st.tuples( st.sampled_from(OPTIONAL_STATES + MANDATORY_STATES + ("TX",)), st.integers(0, 300_000), # employment_income - st.sampled_from([1, 9, 10, 14, 15, 20, 24, 25, 49, 50, 100]), + st.sampled_from([1, 9, 10, 14, 15, 20, 24, 25, 30, 31, 49, 50, 100]), share, share, + average_quarterly_wage, # Minnesota small employer test only ) @@ -93,7 +120,7 @@ def simulate(rows): people, households = {}, {} groups = {"tax_units": {}, "spm_units": {}, "families": {}, "marital_units": {}} index = 0 - for state, wages, headcount, share_a, share_b in rows: + for state, wages, headcount, share_a, share_b, average_wage in rows: for share_value in (share_a, share_b, None): person = f"person_{index}" people[person] = { @@ -103,6 +130,12 @@ def simulate(rows): "employer_total_taxable_earnings_for_social_security": { year: min(wages, SOCIAL_SECURITY_CAP[year]) for year in YEARS }, + "employer_total_mn_paid_leave_taxable_wages": { + year: min(wages, MN_TAXABLE_WAGE_BASE[year]) for year in YEARS + }, + "mn_paid_leave_employer_average_quarterly_wage": { + year: average_wage for year in YEARS + }, "employer_total_payroll_tax_gross_wages": { year: wages for year in YEARS }, @@ -135,15 +168,23 @@ def by_copy(simulation, variable, year): st.lists(workers, min_size=4, max_size=30).map( # Every optional state gets a worker at a large employer below every # wage base, and one at a 9-employee employer (below the CO, MA, ME - # and WA size thresholds; DE does not cover it) above every wage base - # and New York's PFL maximum; Delaware also gets a 12-employee - # employer (parental leave only). Shares lie strictly between 0 and 1 - # or above 1, so each program's properties compare positive amounts, - # small-employer rules and capped amounts. + # and WA size thresholds; DE does not cover it; a Minnesota small + # employer) above every wage base and New York's PFL maximum; + # Delaware also gets a 12-employee employer (parental leave only). + # Minnesota also gets employers at 30 and 31 employees and one of 10 + # employees whose average wage fails the small employer test. Shares + # lie strictly between 0 and 1 or above 1, so each program's + # properties compare positive amounts, small-employer rules and + # capped amounts. lambda rows: ( - [(state, 60_000, 100, 0.5, 0.0) for state in OPTIONAL_STATES] - + [(state, 250_000, 9, 0.3, 1.2) for state in OPTIONAL_STATES] - + [("DE", 250_000, 12, 0.3, 1.2)] + [(state, 60_000, 100, 0.5, 0.0, 0.0) for state in OPTIONAL_STATES] + + [(state, 250_000, 9, 0.3, 1.2, 0.0) for state in OPTIONAL_STATES] + + [("DE", 250_000, 12, 0.3, 1.2, 0.0)] + + [ + ("MN", 60_000, 30, 0.5, 0.0, 27_745.88), + ("MN", 60_000, 31, 0.5, 0.0, 0.0), + ("MN", 60_000, 10, 0.5, 0.0, 27_745.89), + ] + rows ) ) @@ -152,9 +193,16 @@ def test_state_payroll_premium_deduction_share_invariants(rows): simulation = simulate(rows) states = np.array([row[0] for row in rows]) wages = np.array([row[1] for row in rows]) + headcounts = np.array([row[2] for row in rows]) shares = np.clip(np.array([[row[3], row[4], 1.0] for row in rows]), 0, 1) + average_wages = np.array([row[5] for row in rows]) modeled = np.isin(states, EMPLOYER_MODELED_STATES) - unmodeled = np.isin(states, ("MN", "NY")) + unmodeled = states == "NY" + mn = states == "MN" + mn_small = (headcounts <= MN_SMALL_MAX_HEADCOUNT) & ( + average_wages <= MN_SMALL_MAX_AVERAGE_QUARTERLY_WAGE + ) + assert mn_small[mn].any() and (~mn_small[mn]).any() uncapped = wages <= UNCAPPED_WAGES optional_by_year = {} employer_by_year = {} @@ -195,6 +243,29 @@ def test_state_payroll_premium_deduction_share_invariants(rows): np.testing.assert_allclose( employer_total[same_path], employer[same_path], **TOLERANCE ) + # Property 7. + mn_wages = np.minimum(np.maximum(wages, 0), MN_TAXABLE_WAGE_BASE[year]) + mn_premium = MN_RATE * np.where(mn_small, 0.75, 1) * mn_wages + mn_minimum = np.where(mn_small, 0.25 * MN_RATE * mn_wages, 0.5 * mn_premium) + mn_employee = by_copy(simulation, "mn_employee_paid_leave_contribution", year) + mn_employer = by_copy(simulation, "mn_employer_paid_leave_contribution", year) + np.testing.assert_allclose( + by_copy(simulation, "mn_paid_leave_premium", year)[mn], + mn_premium[mn][:, None] * np.ones((1, COPIES)), + **TOLERANCE, + ) + np.testing.assert_allclose( + (mn_employee + mn_employer)[mn], + mn_premium[mn][:, None] * np.ones((1, COPIES)), + **TOLERANCE, + ) + np.testing.assert_allclose( + mn_employer[mn], + (mn_minimum[:, None] + (1 - shares) * (mn_premium - mn_minimum)[:, None])[ + mn + ], + **TOLERANCE, + ) optional_by_year[year] = optional employer_by_year[year] = employer # Property 6. diff --git a/policyengine_us/tests/policy/baseline/gov/states/mn/tax/payroll/paid_leave/mn_employer_paid_leave_contribution.yaml b/policyengine_us/tests/policy/baseline/gov/states/mn/tax/payroll/paid_leave/mn_employer_paid_leave_contribution.yaml new file mode 100644 index 00000000000..7fc1a0fb0b3 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/mn/tax/payroll/paid_leave/mn_employer_paid_leave_contribution.yaml @@ -0,0 +1,256 @@ +# Minnesota Paid Leave premium, employer and employee sides (Minn. Stat. +# 268B.14). 2026 and 2027 premium rate 0.88% (DEED, subd. 7); the employer +# pays at least 50% (subd. 3). A small employer under subd. 5a (30 or fewer +# employees, average quarterly wage at most $27,745.88) pays 75% of the rate, +# 0.66%, of which the employer pays at least 25% of the full rate, 0.22%. +# Employees pay at most 0.88% - 0.44% = 0.66% - 0.22% = 0.44% of taxable +# wages. Taxable wages are capped at the Social Security base rounded to the +# nearest $1,000 (268B.01, subd. 43): $184,500 rounds to $185,000 in 2026. + +- name: A large employer that deducts the full permitted share pays half the premium. + period: 2026 + input: + employment_income: 100_000 + state_code: MN + output: + mn_paid_leave_small_employer: false + mn_paid_leave_premium_rate: 0.0088 + mn_paid_leave_permitted_employee_rate: 0.0044 + # 0.88% * 100,000 = 880. + mn_paid_leave_premium: 880 + # 0.44% * 100,000 = 440. + mn_employee_paid_leave_contribution: 440 + # 880 - 440 = 440. + mn_employer_paid_leave_contribution: 440 + mn_employer_additional_state_payroll_tax: 440 + employer_additional_state_payroll_tax: 440 + +- name: A large employer that deducts half the permitted share pays the rest. + period: 2026 + input: + employment_income: 100_000 + employer_state_payroll_premium_deduction_share: 0.5 + state_code: MN + output: + mn_paid_leave_premium: 880 + # 0.5 * 440 = 220. + mn_employee_paid_leave_contribution: 220 + # 880 - 220 = 660. + mn_employer_paid_leave_contribution: 660 + +- name: A large employer that deducts nothing pays the whole premium. + period: 2026 + input: + employment_income: 100_000 + employer_state_payroll_premium_deduction_share: 0 + state_code: MN + output: + mn_employee_paid_leave_contribution: 0 + mn_employer_paid_leave_contribution: 880 + +- name: A small employer pays 75% of the rate, of which employees may pay the same 0.44%. + period: 2026 + input: + employment_income: 100_000 + employer_headcount: 30 + state_code: MN + output: + mn_paid_leave_small_employer: true + # 75% * 0.88% = 0.66%. + mn_paid_leave_premium_rate: 0.0066 + # (75% - 25%) * 0.88% = 0.44%. + mn_paid_leave_permitted_employee_rate: 0.0044 + # 0.66% * 100,000 = 660. + mn_paid_leave_premium: 660 + mn_employee_paid_leave_contribution: 440 + # 660 - 440 = 220, the 25% * 0.88% = 0.22% minimum. + mn_employer_paid_leave_contribution: 220 + employer_additional_state_payroll_tax: 220 + +- name: A small employer that deducts half the permitted share pays the rest. + period: 2026 + input: + employment_income: 100_000 + employer_headcount: 30 + employer_state_payroll_premium_deduction_share: 0.5 + state_code: MN + output: + mn_employee_paid_leave_contribution: 220 + # 660 - 220 = 440. + mn_employer_paid_leave_contribution: 440 + +- name: A small employer that deducts nothing pays the whole small employer premium. + period: 2026 + input: + employment_income: 100_000 + employer_headcount: 30 + employer_state_payroll_premium_deduction_share: 0 + state_code: MN + output: + mn_employee_paid_leave_contribution: 0 + mn_employer_paid_leave_contribution: 660 + +- name: An employer with 31 employees is not a small employer. + period: 2026 + input: + employment_income: 100_000 + employer_headcount: 31 + state_code: MN + output: + mn_paid_leave_small_employer: false + mn_paid_leave_premium: 880 + mn_employer_paid_leave_contribution: 440 + +- name: An average quarterly wage equal to the limit meets the small employer wage test. + period: 2026 + input: + employment_income: 100_000 + employer_headcount: 10 + mn_paid_leave_employer_average_quarterly_wage: 27_745.88 + state_code: MN + output: + mn_paid_leave_small_employer: true + mn_paid_leave_premium: 660 + mn_employer_paid_leave_contribution: 220 + +- name: An average quarterly wage above the limit fails the small employer wage test. + period: 2026 + input: + employment_income: 100_000 + employer_headcount: 10 + mn_paid_leave_employer_average_quarterly_wage: 27_745.89 + state_code: MN + output: + mn_paid_leave_small_employer: false + mn_paid_leave_premium: 880 + mn_employer_paid_leave_contribution: 440 + +- name: Wages above the rounded Social Security base are capped at 185,000. + period: 2026 + input: + employment_income: 250_000 + state_code: MN + output: + mn_paid_leave_taxable_wages: 185_000 + # 0.88% * 185,000 = 1,628. + mn_paid_leave_premium: 1_628 + # 0.44% * 185,000 = 814. + mn_employee_paid_leave_contribution: 814 + mn_employer_paid_leave_contribution: 814 + +- name: A small employer's capped premium leaves the employer a quarter of the full rate. + period: 2026 + input: + employment_income: 250_000 + employer_headcount: 9 + state_code: MN + output: + # 0.66% * 185,000 = 1,221. + mn_paid_leave_premium: 1_221 + mn_employee_paid_leave_contribution: 814 + # 1,221 - 814 = 407 = 0.22% * 185,000. + mn_employer_paid_leave_contribution: 407 + +- name: Small wages keep fractional cents. + period: 2026 + input: + employment_income: 8_000 + state_code: MN + output: + # 0.88% * 8,000 = 70.40; 0.44% * 8,000 = 35.20. + mn_paid_leave_premium: 70.4 + mn_employee_paid_leave_contribution: 35.2 + mn_employer_paid_leave_contribution: 35.2 + +- name: Negative employment income owes no premium. + period: 2026 + input: + employment_income: -5_000 + state_code: MN + output: + mn_paid_leave_premium: 0 + mn_employee_paid_leave_contribution: 0 + mn_employer_paid_leave_contribution: 0 + +- name: The employer premium flows into employer state and total payroll taxes. + period: 2026 + input: + employment_income: 100_000 + state_code: MN + output: + mn_employer_paid_leave_contribution: 440 + # Unemployment insurance: 0.9% default rate * the 42,000 wage base = 378. + employer_state_unemployment_tax: 378 + # 378 + 440 = 818. + employer_state_payroll_tax: 818 + # Social Security 6.2% * 100,000 = 6,200; Medicare 1.45% * 100,000 = + # 1,450; FUTA 0.6% * 7,000 = 42; 6,200 + 1,450 + 42 + 818 = 8,510. + employer_payroll_tax: 8_510 + +- name: The 2027 premium rate is unchanged at 0.88%. + period: 2027 + input: + employment_income: 100_000 + state_code: MN + output: + mn_paid_leave_premium: 880 + mn_employee_paid_leave_contribution: 440 + mn_employer_paid_leave_contribution: 440 + +- name: No premium is due before 2026. + period: 2025 + input: + employment_income: 100_000 + employer_headcount: 9 + state_code: MN + output: + mn_paid_leave_premium: 0 + mn_employee_paid_leave_contribution: 0 + mn_employer_paid_leave_contribution: 0 + mn_employer_additional_state_payroll_tax: 0 + +- name: Workers outside Minnesota owe no Minnesota premium. + period: 2026 + input: + employment_income: 100_000 + employer_headcount: 9 + state_code: WI + output: + mn_paid_leave_premium: 0 + mn_employer_paid_leave_contribution: 0 + employer_additional_state_payroll_tax: 0 + +- name: Employer-level premium for a large employer that deducts the full permitted share. + period: 2026 + input: + employer_headcount: 100 + employer_total_taxable_earnings_for_social_security: 1_000_000 + state_code: MN + output: + employer_total_mn_paid_leave_taxable_wages: 1_000_000 + # (0.88% - 0.44%) * 1,000,000 = 4,400. + employer_total_additional_state_payroll_tax: 4_400 + +- name: Employer-level premium for a small employer that deducts a quarter of the permitted share. + period: 2026 + input: + employer_headcount: 20 + employer_state_payroll_premium_deduction_share: 0.25 + employer_total_taxable_earnings_for_social_security: 1_000_000 + state_code: MN + output: + # (0.66% - 0.25 * 0.44%) * 1,000,000 = 6,600 - 1,100 = 5,500. + employer_total_additional_state_payroll_tax: 5_500 + +- name: Employer-level wages can be set at Minnesota's rounded base. + period: 2026 + input: + employer_headcount: 100 + employer_total_taxable_earnings_for_social_security: 369_000 + # Two workers at Minnesota's 185,000 base, not the 184,500 Social + # Security base. + employer_total_mn_paid_leave_taxable_wages: 370_000 + state_code: MN + output: + # 0.44% * 370,000 = 1,628. + employer_total_additional_state_payroll_tax: 1_628 diff --git a/policyengine_us/tests/policy/baseline/gov/states/tax/payroll/state_payroll_premium_deduction_share.yaml b/policyengine_us/tests/policy/baseline/gov/states/tax/payroll/state_payroll_premium_deduction_share.yaml index 9bebda21773..528c90d7b06 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/tax/payroll/state_payroll_premium_deduction_share.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/tax/payroll/state_payroll_premium_deduction_share.yaml @@ -180,7 +180,7 @@ # 1.13% * (1 - 0.2857) * 100,000 = 807.159. wa_employer_paid_leave_contribution: 807.159 -- name: Minnesota Paid Leave employee share scales with the deduction share. +- name: Minnesota Paid Leave employer pays the premium the employee share leaves. period: 2026 input: employment_income: 100_000 @@ -190,6 +190,8 @@ # 0.5 * 0.44% * 100,000 = 220. mn_employee_paid_leave_contribution: 220 employee_state_payroll_tax: 220 + # 0.88% * 100,000 - 220 = 660. + mn_employer_paid_leave_contribution: 660 - name: New York PFL and DBL scale the capped permitted contribution. period: 2026 diff --git a/policyengine_us/variables/gov/states/mn/tax/payroll/mn_employer_additional_state_payroll_tax.py b/policyengine_us/variables/gov/states/mn/tax/payroll/mn_employer_additional_state_payroll_tax.py new file mode 100644 index 00000000000..f073e239871 --- /dev/null +++ b/policyengine_us/variables/gov/states/mn/tax/payroll/mn_employer_additional_state_payroll_tax.py @@ -0,0 +1,12 @@ +from policyengine_us.model_api import * + + +class mn_employer_additional_state_payroll_tax(Variable): + value_type = float + entity = Person + label = "Minnesota employer additional state payroll tax" + documentation = "Employer-side Minnesota Paid Leave premium." + definition_period = YEAR + unit = USD + defined_for = StateCode.MN + adds = ["mn_employer_paid_leave_contribution"] diff --git a/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_employee_paid_leave_contribution.py b/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_employee_paid_leave_contribution.py index 0fdf8023416..fc3f4b36cf7 100644 --- a/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_employee_paid_leave_contribution.py +++ b/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_employee_paid_leave_contribution.py @@ -11,9 +11,9 @@ class mn_employee_paid_leave_contribution(Variable): "under subd. 5a, at least a quarter of the full rate); employees pay " "by wage deduction only the part the employer does not pay (Minn. " "Stat. 268B.14, subds. 1, 3 and 5a). This is the largest such part, " - "half the premium at the full rate, times " - "state_payroll_premium_deduction_share. The employer premium is not " - "modeled." + "mn_paid_leave_permitted_employee_rate times taxable wages, times " + "state_payroll_premium_deduction_share. The employer pays the rest " + "of the premium (mn_employer_paid_leave_contribution)." ) definition_period = YEAR unit = USD @@ -21,6 +21,6 @@ class mn_employee_paid_leave_contribution(Variable): reference = "https://www.revisor.mn.gov/statutes/cite/268B.14" def formula(person, period, parameters): - rate = parameters(period).gov.states.mn.tax.payroll.paid_leave.employee_rate + rate = person("mn_paid_leave_permitted_employee_rate", period) permitted = rate * person("mn_paid_leave_taxable_wages", period) return permitted * person("state_payroll_premium_deduction_share", period) diff --git a/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_employer_paid_leave_contribution.py b/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_employer_paid_leave_contribution.py new file mode 100644 index 00000000000..9dc1402c42a --- /dev/null +++ b/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_employer_paid_leave_contribution.py @@ -0,0 +1,24 @@ +from policyengine_us.model_api import * + + +class mn_employer_paid_leave_contribution(Variable): + value_type = float + entity = Person + label = "Minnesota employer paid leave contribution" + documentation = ( + "Employer-side Minnesota Paid Leave premium: the premium the employer " + "owes on the person's taxable wages (Minn. Stat. 268B.14, subd. 1) " + "less the employee wage deduction (subds. 3 and 5a). That is at " + "least the employer's minimum, half the premium or for a small " + "employer a quarter of the full rate, plus any part of the permitted " + "employee portion the employer does not deduct." + ) + definition_period = YEAR + unit = USD + defined_for = StateCode.MN + reference = "https://www.revisor.mn.gov/statutes/cite/268B.14" + + def formula(person, period, parameters): + return person("mn_paid_leave_premium", period) - person( + "mn_employee_paid_leave_contribution", period + ) diff --git a/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_paid_leave_employer_average_quarterly_wage.py b/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_paid_leave_employer_average_quarterly_wage.py new file mode 100644 index 00000000000..f3ef0014bd6 --- /dev/null +++ b/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_paid_leave_employer_average_quarterly_wage.py @@ -0,0 +1,22 @@ +from policyengine_us.model_api import * + + +class mn_paid_leave_employer_average_quarterly_wage(Variable): + value_type = float + entity = Person + label = "Minnesota Paid Leave employer average quarterly wage" + documentation = ( + "Average wage of the person's employer as Minnesota Paid Leave " + "measures it for the small employer premium rate: the employer's " + "covered wages in its highest-paid quarter of the basis period (the " + "four quarters ending September 30 of the prior year) divided by its " + "highest quarterly employee count in that period; the two may come " + "from different quarters (Minn. Stat. 268B.14, subds. 5b(a) and " + "5c(a)). An employer not covered for the whole basis period uses its " + "own estimates instead (subd. 5c(b)). The default of 0 treats the " + "employer as meeting the wage test, so employer_headcount alone " + "decides whether the small employer rate applies." + ) + unit = USD + definition_period = YEAR + reference = "https://www.revisor.mn.gov/statutes/cite/268B.14" diff --git a/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_paid_leave_permitted_employee_rate.py b/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_paid_leave_permitted_employee_rate.py new file mode 100644 index 00000000000..ea238bfce84 --- /dev/null +++ b/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_paid_leave_permitted_employee_rate.py @@ -0,0 +1,29 @@ +from policyengine_us.model_api import * + + +class mn_paid_leave_permitted_employee_rate(Variable): + value_type = float + entity = Person + label = "Minnesota Paid Leave permitted employee premium rate" + documentation = ( + "Largest share of the person's taxable wages the employer may deduct " + "for Minnesota Paid Leave: the premium rate less the employer's " + "minimum. The employer pays at least half the premium (Minn. Stat. " + "268B.14, subd. 3), or for a small employer eligible under subd. 5a, " + "at least 25 percent of the full rate out of a premium of 75 percent " + "of it (subd. 5a(b)); employees pay the remaining portion, if any." + ) + definition_period = YEAR + unit = "/1" + defined_for = StateCode.MN + reference = "https://www.revisor.mn.gov/statutes/cite/268B.14" + + def formula(person, period, parameters): + p = parameters(period).gov.states.mn.tax.payroll.paid_leave + small = person("mn_paid_leave_small_employer", period) + employee_share_of_full_rate = where( + small, + p.small_employer.rate_share - p.small_employer.employer_minimum_share, + 1 - p.employer_minimum_share, + ) + return p.rate * max_(employee_share_of_full_rate, 0) diff --git a/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_paid_leave_premium.py b/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_paid_leave_premium.py new file mode 100644 index 00000000000..583930617b9 --- /dev/null +++ b/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_paid_leave_premium.py @@ -0,0 +1,21 @@ +from policyengine_us.model_api import * + + +class mn_paid_leave_premium(Variable): + value_type = float + entity = Person + label = "Minnesota Paid Leave premium" + documentation = ( + "Minnesota Paid Leave premium on the person's taxable wages, which " + "accrues to and is payable by the employer (Minn. Stat. 268B.14, " + "subd. 1). The employer pays it less the employee wage deduction." + ) + definition_period = YEAR + unit = USD + defined_for = StateCode.MN + reference = "https://www.revisor.mn.gov/statutes/cite/268B.14" + + def formula(person, period, parameters): + return person("mn_paid_leave_premium_rate", period) * person( + "mn_paid_leave_taxable_wages", period + ) diff --git a/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_paid_leave_premium_rate.py b/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_paid_leave_premium_rate.py new file mode 100644 index 00000000000..0c0a4ede280 --- /dev/null +++ b/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_paid_leave_premium_rate.py @@ -0,0 +1,24 @@ +from policyengine_us.model_api import * + + +class mn_paid_leave_premium_rate(Variable): + value_type = float + entity = Person + label = "Minnesota Paid Leave premium rate" + documentation = ( + "Minnesota Paid Leave premium rate on the person's taxable wages: the " + "annual premium rate, or 75 percent of it for a small employer " + "eligible under subd. 5a (Minn. Stat. 268B.14, subds. 5a(b), 6 and " + "7). Assumes the employer participates in both the family and " + "medical benefit programs; approved private plans (subds. 1 and 6, " + "section 268B.10) are not modeled." + ) + definition_period = YEAR + unit = "/1" + defined_for = StateCode.MN + reference = "https://www.revisor.mn.gov/statutes/cite/268B.14" + + def formula(person, period, parameters): + p = parameters(period).gov.states.mn.tax.payroll.paid_leave + small = person("mn_paid_leave_small_employer", period) + return p.rate * where(small, p.small_employer.rate_share, 1) diff --git a/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_paid_leave_small_employer.py b/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_paid_leave_small_employer.py new file mode 100644 index 00000000000..a8ee9a042ae --- /dev/null +++ b/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_paid_leave_small_employer.py @@ -0,0 +1,28 @@ +from policyengine_us.model_api import * + + +class mn_paid_leave_small_employer(Variable): + value_type = bool + entity = Person + label = "Minnesota Paid Leave small employer" + documentation = ( + "Whether the person's employer qualifies for the Minnesota Paid Leave " + "small employer premium rate: 30 or fewer employees, and an average " + "wage at most 150 percent of the state's average wage in covered " + "employment for the basis period (Minn. Stat. 268B.14, subd. 5a(a)). " + "employer_headcount stands in for the employer's highest quarterly " + "employee count in the basis period (subd. 5b), and " + "mn_paid_leave_employer_average_quarterly_wage for its average wage " + "(subd. 5c)." + ) + definition_period = YEAR + defined_for = StateCode.MN + reference = "https://www.revisor.mn.gov/statutes/cite/268B.14" + + def formula(person, period, parameters): + p = parameters(period).gov.states.mn.tax.payroll.paid_leave.small_employer + headcount = person("employer_headcount", period) + average_wage = person("mn_paid_leave_employer_average_quarterly_wage", period) + return (headcount <= p.max_headcount) & ( + average_wage <= p.max_average_quarterly_wage + ) diff --git a/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_paid_leave_taxable_wages.py b/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_paid_leave_taxable_wages.py index 03112c1cd04..8fc38110961 100644 --- a/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_paid_leave_taxable_wages.py +++ b/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_paid_leave_taxable_wages.py @@ -18,3 +18,26 @@ def formula(person, period, parameters): social_security_cap = parameters(period).gov.irs.payroll.social_security.cap taxable_wage_base = 1_000 * np.floor(social_security_cap / 1_000 + 0.5) return min_(max_(0, person("employment_income", period)), taxable_wage_base) + + +class employer_total_mn_paid_leave_taxable_wages(Variable): + value_type = float + entity = Person + label = "Employer total Minnesota Paid Leave taxable wages" + documentation = ( + "Aggregate employer wages subject to Minnesota Paid Leave premiums " + "for employer-only payroll tax calculations. Defaults to " + "employer_total_taxable_earnings_for_social_security, which caps each " + "worker's wages at the Social Security wage base. Minnesota caps them " + "at that base rounded to the nearest $1,000 (Minn. Stat. 268B.01, " + "subd. 43): $185,000 against $184,500 in 2026. The two bases differ " + "by at most $500, so the default is off by at most $500 per worker " + "paid above the lower one; set this input to use Minnesota's base." + ) + definition_period = YEAR + unit = USD + defined_for = StateCode.MN + reference = "https://www.revisor.mn.gov/statutes/cite/268B.01" + + def formula(person, period, parameters): + return person("employer_total_taxable_earnings_for_social_security", period) 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 c448aae2b51..85ee7a4b31e 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 @@ -41,8 +41,8 @@ class employee_state_payroll_tax(Variable): "employee amount is the permitted share times " "employer_state_payroll_premium_deduction_share, which defaults to 1, " "the full permitted share, and the employer-side variables carry any " - "part the employer does not deduct, except in Minnesota and New York, " - "whose employer cost is not modeled. Not modeled: an employer's " + "part the employer does not deduct, except in New York, whose " + "employer cost is not modeled. Not modeled: an employer's " "election to pay a required employee contribution as a benefit (Paid " "Leave Oregon, ORS 657B.150(5)) or voluntary payment of other " "mandatory contributions, and the federal treatment of an employer's " 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..c4fd5cc40cf 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 @@ -7,6 +7,7 @@ "de_employer_additional_state_payroll_tax", "ma_employer_additional_state_payroll_tax", "me_employer_additional_state_payroll_tax", + "mn_employer_additional_state_payroll_tax", "or_employer_additional_state_payroll_tax", "ri_employer_additional_state_payroll_tax", "vt_employer_additional_state_payroll_tax", @@ -22,8 +23,8 @@ 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). The New York PFL " + "and DBL employer costs are not modeled." ) definition_period = YEAR unit = USD diff --git a/policyengine_us/variables/gov/states/tax/payroll/employer_total_additional_state_payroll_tax.py b/policyengine_us/variables/gov/states/tax/payroll/employer_total_additional_state_payroll_tax.py index 2c41614b842..0966b78fc32 100644 --- a/policyengine_us/variables/gov/states/tax/payroll/employer_total_additional_state_payroll_tax.py +++ b/policyengine_us/variables/gov/states/tax/payroll/employer_total_additional_state_payroll_tax.py @@ -96,6 +96,14 @@ def formula(person, period, parameters): + (1 - deducted) * me_p.employee_rate * me_wages ) + # The premium and permitted employee rates depend on the small + # employer test (Minn. Stat. 268B.14, subd. 5a), which reads the same + # employer inputs as the per-worker path. + mn = ( + person("mn_paid_leave_premium_rate", period) + - deducted * person("mn_paid_leave_permitted_employee_rate", period) + ) * person("employer_total_mn_paid_leave_taxable_wages", period) + or_p = parameters(period).gov.states["or"].tax.payroll.paid_leave or_tax = where( headcount >= or_p.employer_headcount_threshold, @@ -135,11 +143,12 @@ def formula(person, period, parameters): state_code == StateCode.DE, state_code == StateCode.MA, state_code == StateCode.ME, + state_code == StateCode.MN, state_code == StateCode.OR, state_code == StateCode.RI, state_code == StateCode.VT, state_code == StateCode.WA, ], - [ca, co, dc, de, ma, me, or_tax, ri, vt, wa], + [ca, co, dc, de, ma, me, mn, or_tax, ri, vt, wa], default=0, ) diff --git a/policyengine_us/variables/input/employer_payroll.py b/policyengine_us/variables/input/employer_payroll.py index 7b69a15ec1f..bd64d714072 100644 --- a/policyengine_us/variables/input/employer_payroll.py +++ b/policyengine_us/variables/input/employer_payroll.py @@ -21,7 +21,10 @@ class employer_headcount(Variable): label = "Employer headcount" documentation = ( "Employer employee count used as a proxy for size-based payroll " - "contribution rules, including paid leave and similar programs." + "contribution rules, including paid leave and similar programs. For " + "Minnesota Paid Leave's small employer rate it stands in for the " + "employer's highest quarterly employee count in the basis period " + "(Minn. Stat. 268B.14, subd. 5b)." ) definition_period = YEAR default_value = 100 @@ -41,8 +44,8 @@ class employer_state_payroll_premium_deduction_share(Variable): "Child Care Contribution and Washington Paid Family and Medical " "Leave. 1, the default, deducts the full permitted share; 0 deducts " "nothing. The employer pays any part it does not deduct, which the " - "employer-side variables carry except in Minnesota and New York, " - "whose employer cost is not modeled. The federal treatment of that " + "employer-side variables carry except in New York, whose employer " + "cost is not modeled. The federal treatment of that " "payment as the employee's wages (Rev. Rul. 2025-4) is not modeled. " "Values outside 0 to 1 are treated as the nearer bound." ) From 5e55446497fbfa0d52fcfded30a6f9db0a564b9b Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 19 Oct 2026 17:28:11 -0400 Subject: [PATCH 06/10] Test that Minnesota's derived employee portion follows the parameters A reform of the rate or the employer minimums reaches both sides, and a small employer minimum above the small employer premium leaves employees nothing (subd. 5a(b)(2), "the remaining portion, if any"). Co-Authored-By: Claude Opus 5.5 --- .../tests/core/test_payroll_contributions.py | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/policyengine_us/tests/core/test_payroll_contributions.py b/policyengine_us/tests/core/test_payroll_contributions.py index c8111354484..5488144b57c 100644 --- a/policyengine_us/tests/core/test_payroll_contributions.py +++ b/policyengine_us/tests/core/test_payroll_contributions.py @@ -607,6 +607,57 @@ def test_small_employer_thresholds_zero_employer_paid_leave_share( assert calculate(sim, variable) == 0 +def test_minnesota_paid_leave_shares_follow_the_parameters(): + # The employee portion is derived from the rate and the employer minimums, + # so a reform of either reaches both sides, and a small employer minimum + # above the small employer premium leaves employees nothing ("the + # remaining portion, if any", Minn. Stat. 268B.14, subd. 5a(b)(2)). + paid_leave = "gov.states.mn.tax.payroll.paid_leave" + reform = Reform.from_dict( + { + f"{paid_leave}.rate": {"2026-01-01.2026-12-31": 0.01}, + f"{paid_leave}.employer_minimum_share": {"2026-01-01.2026-12-31": 0.6}, + f"{paid_leave}.small_employer.employer_minimum_share": { + "2026-01-01.2026-12-31": 0.8 + }, + }, + country_id="us", + ) + people = { + name: { + "age": {PERIOD: 30}, + "employment_income": {PERIOD: WAGES}, + "employer_headcount": {PERIOD: headcount}, + } + for name, headcount in (("large", 100), ("small", 30)) + } + sim = Simulation( + tax_benefit_system=CountryTaxBenefitSystem(reform=(reform,)), + situation={ + "people": people, + "households": { + "household": {"members": list(people), "state_code": {PERIOD: "MN"}} + }, + "tax_units": {"tax_unit": {"members": list(people)}}, + "spm_units": {"spm_unit": {"members": list(people)}}, + "families": {"family": {"members": list(people)}}, + "marital_units": { + f"{name}_marital_unit": {"members": [name]} for name in people + }, + }, + ) + + # Large: 1% premium, 40% of it deductible. Small: 75% * 1% premium, of + # which the employer must pay 80% * 1%, more than the whole premium. + assert sim.calculate("mn_paid_leave_premium", PERIOD) == pytest.approx([1_000, 750]) + assert sim.calculate( + "mn_employee_paid_leave_contribution", PERIOD + ) == pytest.approx([400, 0]) + assert sim.calculate( + "mn_employer_paid_leave_contribution", PERIOD + ) == pytest.approx([600, 750]) + + @pytest.mark.parametrize( ("state_code", "expected"), [ From f2f43141710d966c7e2ff63bf356fbd9b1858ed1 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 19 Oct 2026 18:55:26 -0400 Subject: [PATCH 07/10] Address review r1: wage limit source, float32 boundary, employee cap - The small employer wage limit is now the statutory 150% (subd. 5a(a)(2)) times DEED's statewide average annual wage ($73,989 for 2026 premiums, from DEED's small employer designation notice) per quarter, compared in whole cents as DEED states it ($27,745.88). This replaces the max_average_quarterly_wage parameter, which had no period and cited a page that gives no year. - The permitted employee rate is capped at the premium as well as floored at 0 ("the remaining portion, if any"); the reform test covers both. - The structural shares and limits no longer carry zero placeholders before 2026; the rate alone gates the program. - The property test draws the average wage in whole cents, so its float64 reference can't disagree with the model's float32 input below a cent. - Cite DEED's July 31, 2026 bulletin for the 2027 rate, and move the employee_rate removal into a removed changelog fragment. Co-Authored-By: Claude Opus 5.5 --- .../mn-employer-paid-leave-premium.added.md | 2 +- .../mn-employer-paid-leave-premium.removed.md | 1 + .../paid_leave/employer_minimum_share.yaml | 1 - .../mn/tax/payroll/paid_leave/rate.yaml | 2 ++ .../employer_minimum_share.yaml | 3 +- .../max_average_quarterly_wage.yaml | 14 --------- .../max_average_wage_ratio.yaml | 13 ++++++++ .../small_employer/max_headcount.yaml | 3 +- .../paid_leave/small_employer/rate_share.yaml | 3 +- .../state_average_annual_wage.yaml | 15 ++++++++++ .../tests/core/test_payroll_contributions.py | 30 ++++++++++++------- ...roll_premium_deduction_share_invariants.py | 7 +++-- .../mn_employer_paid_leave_contribution.yaml | 5 ++-- .../mn_paid_leave_permitted_employee_rate.py | 16 +++++++--- .../mn_paid_leave_small_employer.py | 19 ++++++++++-- 15 files changed, 94 insertions(+), 40 deletions(-) create mode 100644 changelog.d/mn-employer-paid-leave-premium.removed.md delete mode 100644 policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_average_quarterly_wage.yaml create mode 100644 policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_average_wage_ratio.yaml create mode 100644 policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/state_average_annual_wage.yaml diff --git a/changelog.d/mn-employer-paid-leave-premium.added.md b/changelog.d/mn-employer-paid-leave-premium.added.md index fa092c00e41..5a1c2f0017c 100644 --- a/changelog.d/mn-employer-paid-leave-premium.added.md +++ b/changelog.d/mn-employer-paid-leave-premium.added.md @@ -1 +1 @@ -Model the Minnesota Paid Leave employer premium, including the small employer rate (Minn. Stat. 268B.14), and include it in employer payroll taxes. The Minnesota `employee_rate` parameter is replaced by `rate` and `employer_minimum_share`. +Model the Minnesota Paid Leave employer premium, including the small employer rate and test (Minn. Stat. 268B.14, subds. 5a-5c), and include it in employer payroll taxes. diff --git a/changelog.d/mn-employer-paid-leave-premium.removed.md b/changelog.d/mn-employer-paid-leave-premium.removed.md new file mode 100644 index 00000000000..c4788dcf90f --- /dev/null +++ b/changelog.d/mn-employer-paid-leave-premium.removed.md @@ -0,0 +1 @@ +Remove the Minnesota Paid Leave `employee_rate` parameter, which is now derived from `rate` and `employer_minimum_share`; reforms that set it must set those instead. diff --git a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/employer_minimum_share.yaml b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/employer_minimum_share.yaml index 8957d9a9ac8..02e0b473108 100644 --- a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/employer_minimum_share.yaml +++ b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/employer_minimum_share.yaml @@ -1,6 +1,5 @@ description: Minnesota requires employers to pay at least this share of the Paid Leave premium; employees pay the remaining portion, if any, through a wage deduction. values: - 0000-01-01: 0 2026-01-01: 0.5 metadata: diff --git a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/rate.yaml b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/rate.yaml index 50d0d86dec9..bdcf95010de 100644 --- a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/rate.yaml +++ b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/rate.yaml @@ -16,3 +16,5 @@ metadata: href: https://mn.gov/deed/newscenter/press-releases/?id=1045-670064 - title: Minnesota Paid Leave, Premium rate and contributions ("For 2026 and 2027, the Paid Leave premium rate is 0.88%") href: https://pl.mn.gov/resources/calculators/premium-rate-and-contributions + - title: Minnesota DEED, Paid Leave premium rate unchanged for 2027 (July 31, 2026) + href: https://content.govdelivery.com/accounts/MNDEED/bulletins/422fdf7 diff --git a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/employer_minimum_share.yaml b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/employer_minimum_share.yaml index 24f7cfa35f1..45489211642 100644 --- a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/employer_minimum_share.yaml +++ b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/employer_minimum_share.yaml @@ -1,6 +1,5 @@ description: Minnesota requires small employers to pay at least this share of the full Paid Leave premium rate; employees pay the remaining portion of the small employer premium, if any, through a wage deduction. values: - 0000-01-01: 0 2026-01-01: 0.25 metadata: @@ -10,3 +9,5 @@ metadata: reference: - title: Minnesota Statutes Section 268B.14, subdivision 5a(b)(1) href: https://www.revisor.mn.gov/statutes/cite/268B.14 + - title: Minnesota DEED, Paid Leave small employer premium rate designation ("Like large employers, small employers can deduct up to 0.44% of wages") + href: https://mn.gov/deed/assets/paid-leave-small-employer-premium-rate-designation-acc_tcm1045-716947.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_average_quarterly_wage.yaml b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_average_quarterly_wage.yaml deleted file mode 100644 index a401fcb39ce..00000000000 --- a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_average_quarterly_wage.yaml +++ /dev/null @@ -1,14 +0,0 @@ -description: Minnesota limits the Paid Leave small employer premium rate to employers whose average quarterly wage is at most this amount, 150 percent of the state's average wage in covered employment for the basis period. -values: - 0000-01-01: 0 - # Basis period October 2024 to September 2025, for 2026 premiums. - 2026-01-01: 27_745.88 - -metadata: - unit: currency-USD - label: Minnesota Paid Leave small employer maximum average quarterly wage - reference: - - title: Minnesota Statutes Section 268B.14, subdivisions 5a(a)(2), 5b(a) and 5c - href: https://www.revisor.mn.gov/statutes/cite/268B.14 - - title: Minnesota Paid Leave, Premium rate and contributions ("150% of the statewide average wage, currently $27,745.88 per quarter") - href: https://pl.mn.gov/resources/calculators/premium-rate-and-contributions diff --git a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_average_wage_ratio.yaml b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_average_wage_ratio.yaml new file mode 100644 index 00000000000..e1a6d34da3b --- /dev/null +++ b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_average_wage_ratio.yaml @@ -0,0 +1,13 @@ +description: Minnesota limits the Paid Leave small employer premium rate to employers whose average wage is at most this multiple of the state's average wage in covered employment for the basis period. +values: + 2026-01-01: 1.5 + +metadata: + period: year + unit: /1 + label: Minnesota Paid Leave small employer average wage limit ratio + reference: + - title: Minnesota Statutes Section 268B.14, subdivision 5a(a)(2) + href: https://www.revisor.mn.gov/statutes/cite/268B.14 + - title: Minnesota DEED, Paid Leave small employer premium rate designation + href: https://mn.gov/deed/assets/paid-leave-small-employer-premium-rate-designation-acc_tcm1045-716947.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_headcount.yaml b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_headcount.yaml index fcb71239560..702a5968358 100644 --- a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_headcount.yaml +++ b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_headcount.yaml @@ -1,6 +1,5 @@ description: Minnesota limits the Paid Leave small employer premium rate to employers with at most this many employees. values: - 0000-01-01: 0 2026-01-01: 30 metadata: @@ -10,3 +9,5 @@ metadata: reference: - title: Minnesota Statutes Section 268B.14, subdivisions 5a(a)(1) and 5b href: https://www.revisor.mn.gov/statutes/cite/268B.14 + - title: Minnesota DEED, Paid Leave small employer premium rate designation + href: https://mn.gov/deed/assets/paid-leave-small-employer-premium-rate-designation-acc_tcm1045-716947.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/rate_share.yaml b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/rate_share.yaml index 3c2b27b90ba..00d20b2156f 100644 --- a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/rate_share.yaml +++ b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/rate_share.yaml @@ -1,6 +1,5 @@ description: Minnesota charges small employers this share of the full Paid Leave premium rate. values: - 0000-01-01: 0 2026-01-01: 0.75 metadata: @@ -12,3 +11,5 @@ metadata: href: https://www.revisor.mn.gov/statutes/cite/268B.14 - title: Minnesota Paid Leave, Premium rate and contributions ("The Paid Leave small employer rate is 0.66%") href: https://pl.mn.gov/resources/calculators/premium-rate-and-contributions + - title: Minnesota DEED, Paid Leave small employer premium rate designation + href: https://mn.gov/deed/assets/paid-leave-small-employer-premium-rate-designation-acc_tcm1045-716947.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/state_average_annual_wage.yaml b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/state_average_annual_wage.yaml new file mode 100644 index 00000000000..7a8dea8cdbe --- /dev/null +++ b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/state_average_annual_wage.yaml @@ -0,0 +1,15 @@ +description: Minnesota compares a small employer's average wage with this state average annual wage in covered employment, which the Department of Employment and Economic Development calculates for the Paid Leave basis period. +values: + # Statewide average annual wage for 2025 ("$73,989 per year"), applied to + # 2026 premiums, whose basis period is October 2024 to September 2025. + 2026-01-01: 73_989 + +metadata: + period: year + unit: currency-USD + label: Minnesota Paid Leave state average annual wage + reference: + - title: Minnesota Statutes Section 268B.14, subdivisions 5a(a)(2) and 5b(a) + href: https://www.revisor.mn.gov/statutes/cite/268B.14 + - title: Minnesota DEED, Paid Leave small employer premium rate designation, Key Terms ("The Statewide Average Annual Wage for 2025 was $73,989 per year") + href: https://mn.gov/deed/assets/paid-leave-small-employer-premium-rate-designation-acc_tcm1045-716947.pdf#page=2 diff --git a/policyengine_us/tests/core/test_payroll_contributions.py b/policyengine_us/tests/core/test_payroll_contributions.py index 5488144b57c..81cc8fa5815 100644 --- a/policyengine_us/tests/core/test_payroll_contributions.py +++ b/policyengine_us/tests/core/test_payroll_contributions.py @@ -607,18 +607,29 @@ def test_small_employer_thresholds_zero_employer_paid_leave_share( assert calculate(sim, variable) == 0 -def test_minnesota_paid_leave_shares_follow_the_parameters(): - # The employee portion is derived from the rate and the employer minimums, - # so a reform of either reaches both sides, and a small employer minimum - # above the small employer premium leaves employees nothing ("the - # remaining portion, if any", Minn. Stat. 268B.14, subd. 5a(b)(2)). +@pytest.mark.parametrize( + ("small_employer_minimum", "small_employee", "small_employer"), + [ + # A small employer minimum above the 75% small employer premium + # leaves employees nothing ("the remaining portion, if any"). + pytest.param(0.8, 0, 750, id="employee-floor"), + # A negative minimum can't make employees pay more than the premium. + pytest.param(-0.1, 750, 0, id="employee-cap"), + ], +) +def test_minnesota_paid_leave_shares_follow_the_parameters( + small_employer_minimum: float, small_employee: float, small_employer: float +): + # The employee portion is derived from the rate and the employer minimums + # (Minn. Stat. 268B.14, subds. 3 and 5a(b)), so a reform of either reaches + # both sides. paid_leave = "gov.states.mn.tax.payroll.paid_leave" reform = Reform.from_dict( { f"{paid_leave}.rate": {"2026-01-01.2026-12-31": 0.01}, f"{paid_leave}.employer_minimum_share": {"2026-01-01.2026-12-31": 0.6}, f"{paid_leave}.small_employer.employer_minimum_share": { - "2026-01-01.2026-12-31": 0.8 + "2026-01-01.2026-12-31": small_employer_minimum }, }, country_id="us", @@ -647,15 +658,14 @@ def test_minnesota_paid_leave_shares_follow_the_parameters(): }, ) - # Large: 1% premium, 40% of it deductible. Small: 75% * 1% premium, of - # which the employer must pay 80% * 1%, more than the whole premium. + # Large: 1% premium, 40% of it deductible. Small: 75% * 1% premium. assert sim.calculate("mn_paid_leave_premium", PERIOD) == pytest.approx([1_000, 750]) assert sim.calculate( "mn_employee_paid_leave_contribution", PERIOD - ) == pytest.approx([400, 0]) + ) == pytest.approx([400, small_employee]) assert sim.calculate( "mn_employer_paid_leave_contribution", PERIOD - ) == pytest.approx([600, 750]) + ) == pytest.approx([600, small_employer]) @pytest.mark.parametrize( 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 f5cce7b0ac2..c0de1ec3b42 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 @@ -89,7 +89,8 @@ # parameter files: 0.88% in 2026 and 2027 (subd. 7); employer minimum 50% # (subd. 3); small employers 75% of the rate with an employer minimum of 25% # of the full rate (subd. 5a(b)), if 30 or fewer employees and an average -# quarterly wage of at most $27,745.88 (subd. 5a(a)). +# quarterly wage of at most $27,745.88 (subd. 5a(a); DEED's 2026 designation: +# 150% of the $73,989 statewide average annual wage, per quarter). MN_RATE = 0.0088 MN_SMALL_MAX_HEADCOUNT = 30 MN_SMALL_MAX_AVERAGE_QUARTERLY_WAGE = 27_745.88 @@ -102,9 +103,11 @@ st.sampled_from([0.0, 0.25, 0.5, 1.0, -0.5, 1.5]), st.floats(-0.5, 1.5, allow_nan=False), ) +# Whole cents, as DEED states wages: the model holds the input in float32, +# which can't tell sub-cent amounts around the limit apart. average_quarterly_wage = st.one_of( st.sampled_from([0.0, 27_745.88, 27_745.89, 40_000.0]), - st.floats(0, 60_000, allow_nan=False), + st.integers(0, 6_000_000).map(lambda cents: cents / 100), ) workers = st.tuples( st.sampled_from(OPTIONAL_STATES + MANDATORY_STATES + ("TX",)), diff --git a/policyengine_us/tests/policy/baseline/gov/states/mn/tax/payroll/paid_leave/mn_employer_paid_leave_contribution.yaml b/policyengine_us/tests/policy/baseline/gov/states/mn/tax/payroll/paid_leave/mn_employer_paid_leave_contribution.yaml index 7fc1a0fb0b3..f2042e88622 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mn/tax/payroll/paid_leave/mn_employer_paid_leave_contribution.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mn/tax/payroll/paid_leave/mn_employer_paid_leave_contribution.yaml @@ -1,8 +1,9 @@ # Minnesota Paid Leave premium, employer and employee sides (Minn. Stat. # 268B.14). 2026 and 2027 premium rate 0.88% (DEED, subd. 7); the employer # pays at least 50% (subd. 3). A small employer under subd. 5a (30 or fewer -# employees, average quarterly wage at most $27,745.88) pays 75% of the rate, -# 0.66%, of which the employer pays at least 25% of the full rate, 0.22%. +# employees, average quarterly wage at most 150% * $73,989 / 4 = $27,745.88, +# per DEED's 2026 designation) pays 75% of the rate, 0.66%, of which the +# employer pays at least 25% of the full rate, 0.22%. # Employees pay at most 0.88% - 0.44% = 0.66% - 0.22% = 0.44% of taxable # wages. Taxable wages are capped at the Social Security base rounded to the # nearest $1,000 (268B.01, subd. 43): $184,500 rounds to $185,000 in 2026. diff --git a/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_paid_leave_permitted_employee_rate.py b/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_paid_leave_permitted_employee_rate.py index ea238bfce84..48d472c265e 100644 --- a/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_paid_leave_permitted_employee_rate.py +++ b/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_paid_leave_permitted_employee_rate.py @@ -21,9 +21,17 @@ class mn_paid_leave_permitted_employee_rate(Variable): def formula(person, period, parameters): p = parameters(period).gov.states.mn.tax.payroll.paid_leave small = person("mn_paid_leave_small_employer", period) - employee_share_of_full_rate = where( + premium_share_of_full_rate = where(small, p.small_employer.rate_share, 1) + employer_minimum_share_of_full_rate = where( small, - p.small_employer.rate_share - p.small_employer.employer_minimum_share, - 1 - p.employer_minimum_share, + p.small_employer.employer_minimum_share, + p.employer_minimum_share, ) - return p.rate * max_(employee_share_of_full_rate, 0) + # Employees pay the remaining portion, if any, which is never more + # than the premium itself. + employee_share_of_full_rate = clip( + premium_share_of_full_rate - employer_minimum_share_of_full_rate, + 0, + premium_share_of_full_rate, + ) + return p.rate * employee_share_of_full_rate diff --git a/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_paid_leave_small_employer.py b/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_paid_leave_small_employer.py index a8ee9a042ae..c0d8be92648 100644 --- a/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_paid_leave_small_employer.py +++ b/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_paid_leave_small_employer.py @@ -13,16 +13,29 @@ class mn_paid_leave_small_employer(Variable): "employer_headcount stands in for the employer's highest quarterly " "employee count in the basis period (subd. 5b), and " "mn_paid_leave_employer_average_quarterly_wage for its average wage " - "(subd. 5c)." + "(subd. 5c). The limit is a quarterly amount, which DEED states to " + "the cent: 150 percent of $73,989 / 4 is $27,745.88 for 2026. DEED's " + "further condition that the employer filed every wage detail report " + "in the basis period is not modeled." ) definition_period = YEAR defined_for = StateCode.MN - reference = "https://www.revisor.mn.gov/statutes/cite/268B.14" + reference = ( + "https://www.revisor.mn.gov/statutes/cite/268B.14", + "https://mn.gov/deed/assets/paid-leave-small-employer-premium-rate-designation-acc_tcm1045-716947.pdf#page=1", + ) def formula(person, period, parameters): p = parameters(period).gov.states.mn.tax.payroll.paid_leave.small_employer headcount = person("employer_headcount", period) average_wage = person("mn_paid_leave_employer_average_quarterly_wage", period) + quarterly_state_average = p.state_average_annual_wage / 4 + # DEED states the limit and compares wages in whole cents. Comparing + # cents also keeps a float32 input of exactly the limit within it. + max_average_wage_cents = np.round( + 100 * p.max_average_wage_ratio * quarterly_state_average + ) + average_wage_cents = np.round(100 * average_wage) return (headcount <= p.max_headcount) & ( - average_wage <= p.max_average_quarterly_wage + average_wage_cents <= max_average_wage_cents ) From f5b8ec27a22eb288e92baa14b43f20ef7d1d60c3 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 19 Oct 2026 22:26:26 -0400 Subject: [PATCH 08/10] Keep pre-2015 Minnesota calculations working; compare the wage at float32 - Restore the 0000-01-01 sentinels on the Minnesota Paid Leave shares and limits. pe-us backdates parameters only to 2015, so without them every Minnesota employer-side calculation before 2015 raised ParameterNotFoundError (caught by the delta review; a 2014 YAML case now guards it). - Compare the unrounded average wage with DEED's cent-rounded limit at the input's float32 precision, instead of rounding the input to cents: an average a fraction of a cent above $27,745.88 now fails the test, as it does against DEED's published limit, while an input of exactly $27,745.88 still meets it. A YAML case covers $27,745.885. Co-Authored-By: Claude Opus 5.5 --- .../paid_leave/employer_minimum_share.yaml | 1 + .../employer_minimum_share.yaml | 1 + .../max_average_wage_ratio.yaml | 1 + .../small_employer/max_headcount.yaml | 1 + .../paid_leave/small_employer/rate_share.yaml | 1 + .../state_average_annual_wage.yaml | 1 + .../mn_employer_paid_leave_contribution.yaml | 22 +++++++++++++++++++ .../mn_paid_leave_small_employer.py | 12 +++++----- 8 files changed, 34 insertions(+), 6 deletions(-) diff --git a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/employer_minimum_share.yaml b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/employer_minimum_share.yaml index 02e0b473108..8957d9a9ac8 100644 --- a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/employer_minimum_share.yaml +++ b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/employer_minimum_share.yaml @@ -1,5 +1,6 @@ description: Minnesota requires employers to pay at least this share of the Paid Leave premium; employees pay the remaining portion, if any, through a wage deduction. values: + 0000-01-01: 0 2026-01-01: 0.5 metadata: diff --git a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/employer_minimum_share.yaml b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/employer_minimum_share.yaml index 45489211642..b183989203a 100644 --- a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/employer_minimum_share.yaml +++ b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/employer_minimum_share.yaml @@ -1,5 +1,6 @@ description: Minnesota requires small employers to pay at least this share of the full Paid Leave premium rate; employees pay the remaining portion of the small employer premium, if any, through a wage deduction. values: + 0000-01-01: 0 2026-01-01: 0.25 metadata: diff --git a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_average_wage_ratio.yaml b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_average_wage_ratio.yaml index e1a6d34da3b..3ca7d6b43ff 100644 --- a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_average_wage_ratio.yaml +++ b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_average_wage_ratio.yaml @@ -1,5 +1,6 @@ description: Minnesota limits the Paid Leave small employer premium rate to employers whose average wage is at most this multiple of the state's average wage in covered employment for the basis period. values: + 0000-01-01: 0 2026-01-01: 1.5 metadata: diff --git a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_headcount.yaml b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_headcount.yaml index 702a5968358..1b3998715dd 100644 --- a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_headcount.yaml +++ b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_headcount.yaml @@ -1,5 +1,6 @@ description: Minnesota limits the Paid Leave small employer premium rate to employers with at most this many employees. values: + 0000-01-01: 0 2026-01-01: 30 metadata: diff --git a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/rate_share.yaml b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/rate_share.yaml index 00d20b2156f..3936af20ace 100644 --- a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/rate_share.yaml +++ b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/rate_share.yaml @@ -1,5 +1,6 @@ description: Minnesota charges small employers this share of the full Paid Leave premium rate. values: + 0000-01-01: 0 2026-01-01: 0.75 metadata: diff --git a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/state_average_annual_wage.yaml b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/state_average_annual_wage.yaml index 7a8dea8cdbe..9d16f3c4d0e 100644 --- a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/state_average_annual_wage.yaml +++ b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/state_average_annual_wage.yaml @@ -1,5 +1,6 @@ description: Minnesota compares a small employer's average wage with this state average annual wage in covered employment, which the Department of Employment and Economic Development calculates for the Paid Leave basis period. values: + 0000-01-01: 0 # Statewide average annual wage for 2025 ("$73,989 per year"), applied to # 2026 premiums, whose basis period is October 2024 to September 2025. 2026-01-01: 73_989 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mn/tax/payroll/paid_leave/mn_employer_paid_leave_contribution.yaml b/policyengine_us/tests/policy/baseline/gov/states/mn/tax/payroll/paid_leave/mn_employer_paid_leave_contribution.yaml index f2042e88622..4aeb14c8611 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mn/tax/payroll/paid_leave/mn_employer_paid_leave_contribution.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mn/tax/payroll/paid_leave/mn_employer_paid_leave_contribution.yaml @@ -126,6 +126,17 @@ mn_paid_leave_premium: 880 mn_employer_paid_leave_contribution: 440 +- name: An average quarterly wage a fraction of a cent above the limit fails the wage test. + period: 2026 + input: + employment_income: 100_000 + employer_headcount: 10 + mn_paid_leave_employer_average_quarterly_wage: 27_745.885 + state_code: MN + output: + mn_paid_leave_small_employer: false + mn_paid_leave_premium: 880 + - name: Wages above the rounded Social Security base are capped at 185,000. period: 2026 input: @@ -210,6 +221,17 @@ mn_employer_paid_leave_contribution: 0 mn_employer_additional_state_payroll_tax: 0 +- name: Years before the 2015 parameter backdating still compute. + period: 2014 + input: + employment_income: 100_000 + employer_headcount: 9 + state_code: MN + output: + mn_paid_leave_premium: 0 + mn_employer_paid_leave_contribution: 0 + employer_additional_state_payroll_tax: 0 + - name: Workers outside Minnesota owe no Minnesota premium. period: 2026 input: diff --git a/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_paid_leave_small_employer.py b/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_paid_leave_small_employer.py index c0d8be92648..c78cfc79ad9 100644 --- a/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_paid_leave_small_employer.py +++ b/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_paid_leave_small_employer.py @@ -30,12 +30,12 @@ def formula(person, period, parameters): headcount = person("employer_headcount", period) average_wage = person("mn_paid_leave_employer_average_quarterly_wage", period) quarterly_state_average = p.state_average_annual_wage / 4 - # DEED states the limit and compares wages in whole cents. Comparing - # cents also keeps a float32 input of exactly the limit within it. - max_average_wage_cents = np.round( - 100 * p.max_average_wage_ratio * quarterly_state_average + # DEED states the limit to the cent ($27,745.88 for 2026). + max_average_wage = np.round( + p.max_average_wage_ratio * quarterly_state_average, 2 ) - average_wage_cents = np.round(100 * average_wage) + # Inputs are held in float32: compare at that precision, so an input + # of exactly the published limit meets it. return (headcount <= p.max_headcount) & ( - average_wage_cents <= max_average_wage_cents + average_wage <= np.float32(max_average_wage) ) From b0596abccfc188e69c264b85e98138ba3606bd18 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 19 Oct 2026 22:46:52 -0400 Subject: [PATCH 09/10] Address review r2: statutory sentinels, half-up limit rounding, wording - The Minnesota Paid Leave shares and limits now carry their 2026 values from 0000-01-01, so a reform that sets an earlier rate gets the statutory split instead of a zero employer minimum; the zero rate still keeps the program off before 2026, and pre-2015 years still compute. - Round the small employer wage limit to the cent half up, as the repo does for Minnesota's taxable wage base, instead of numpy's half-to-even. - Say that DEED calculates the statewide average annual wage each year, and that 150% of $73,989 / 4 is $27,745.875, which DEED gives as $27,745.88. Co-Authored-By: Claude Opus 5.5 --- .../payroll/paid_leave/employer_minimum_share.yaml | 4 +++- .../small_employer/employer_minimum_share.yaml | 4 +++- .../small_employer/max_average_wage_ratio.yaml | 4 +++- .../paid_leave/small_employer/max_headcount.yaml | 4 +++- .../payroll/paid_leave/small_employer/rate_share.yaml | 4 +++- .../small_employer/state_average_annual_wage.yaml | 6 ++++-- .../mn_employer_paid_leave_contribution.yaml | 6 +++--- .../paid_leave/mn_paid_leave_small_employer.py | 11 +++++++---- 8 files changed, 29 insertions(+), 14 deletions(-) diff --git a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/employer_minimum_share.yaml b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/employer_minimum_share.yaml index 8957d9a9ac8..7c81e136e57 100644 --- a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/employer_minimum_share.yaml +++ b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/employer_minimum_share.yaml @@ -1,6 +1,8 @@ description: Minnesota requires employers to pay at least this share of the Paid Leave premium; employees pay the remaining portion, if any, through a wage deduction. values: - 0000-01-01: 0 + # The zero rate keeps the program off before 2026; earlier years read this + # value only under a reform that sets an earlier rate. + 0000-01-01: 0.5 2026-01-01: 0.5 metadata: diff --git a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/employer_minimum_share.yaml b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/employer_minimum_share.yaml index b183989203a..63f8148d1ae 100644 --- a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/employer_minimum_share.yaml +++ b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/employer_minimum_share.yaml @@ -1,6 +1,8 @@ description: Minnesota requires small employers to pay at least this share of the full Paid Leave premium rate; employees pay the remaining portion of the small employer premium, if any, through a wage deduction. values: - 0000-01-01: 0 + # The zero rate keeps the program off before 2026; earlier years read this + # value only under a reform that sets an earlier rate. + 0000-01-01: 0.25 2026-01-01: 0.25 metadata: diff --git a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_average_wage_ratio.yaml b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_average_wage_ratio.yaml index 3ca7d6b43ff..386b1e96108 100644 --- a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_average_wage_ratio.yaml +++ b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_average_wage_ratio.yaml @@ -1,6 +1,8 @@ description: Minnesota limits the Paid Leave small employer premium rate to employers whose average wage is at most this multiple of the state's average wage in covered employment for the basis period. values: - 0000-01-01: 0 + # The zero rate keeps the program off before 2026; earlier years read this + # value only under a reform that sets an earlier rate. + 0000-01-01: 1.5 2026-01-01: 1.5 metadata: diff --git a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_headcount.yaml b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_headcount.yaml index 1b3998715dd..508ccee689c 100644 --- a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_headcount.yaml +++ b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_headcount.yaml @@ -1,6 +1,8 @@ description: Minnesota limits the Paid Leave small employer premium rate to employers with at most this many employees. values: - 0000-01-01: 0 + # The zero rate keeps the program off before 2026; earlier years read this + # value only under a reform that sets an earlier rate. + 0000-01-01: 30 2026-01-01: 30 metadata: diff --git a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/rate_share.yaml b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/rate_share.yaml index 3936af20ace..7a8ac37da2f 100644 --- a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/rate_share.yaml +++ b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/rate_share.yaml @@ -1,6 +1,8 @@ description: Minnesota charges small employers this share of the full Paid Leave premium rate. values: - 0000-01-01: 0 + # The zero rate keeps the program off before 2026; earlier years read this + # value only under a reform that sets an earlier rate. + 0000-01-01: 0.75 2026-01-01: 0.75 metadata: diff --git a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/state_average_annual_wage.yaml b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/state_average_annual_wage.yaml index 9d16f3c4d0e..61899de5276 100644 --- a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/state_average_annual_wage.yaml +++ b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/state_average_annual_wage.yaml @@ -1,6 +1,8 @@ -description: Minnesota compares a small employer's average wage with this state average annual wage in covered employment, which the Department of Employment and Economic Development calculates for the Paid Leave basis period. +description: Minnesota compares a small employer's average wage with this statewide average annual wage in covered employment, which the Department of Employment and Economic Development calculates each year and applies to the Paid Leave basis period. values: - 0000-01-01: 0 + # The zero rate keeps the program off before 2026; earlier years read this + # value only under a reform that sets an earlier rate. + 0000-01-01: 73_989 # Statewide average annual wage for 2025 ("$73,989 per year"), applied to # 2026 premiums, whose basis period is October 2024 to September 2025. 2026-01-01: 73_989 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mn/tax/payroll/paid_leave/mn_employer_paid_leave_contribution.yaml b/policyengine_us/tests/policy/baseline/gov/states/mn/tax/payroll/paid_leave/mn_employer_paid_leave_contribution.yaml index 4aeb14c8611..d8ea540e219 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mn/tax/payroll/paid_leave/mn_employer_paid_leave_contribution.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mn/tax/payroll/paid_leave/mn_employer_paid_leave_contribution.yaml @@ -1,9 +1,9 @@ # Minnesota Paid Leave premium, employer and employee sides (Minn. Stat. # 268B.14). 2026 and 2027 premium rate 0.88% (DEED, subd. 7); the employer # pays at least 50% (subd. 3). A small employer under subd. 5a (30 or fewer -# employees, average quarterly wage at most 150% * $73,989 / 4 = $27,745.88, -# per DEED's 2026 designation) pays 75% of the rate, 0.66%, of which the -# employer pays at least 25% of the full rate, 0.22%. +# employees, average quarterly wage at most 150% * $73,989 / 4 = $27,745.875, +# which DEED's 2026 designation gives as $27,745.88) pays 75% of the rate, +# 0.66%, of which the employer pays at least 25% of the full rate, 0.22%. # Employees pay at most 0.88% - 0.44% = 0.66% - 0.22% = 0.44% of taxable # wages. Taxable wages are capped at the Social Security base rounded to the # nearest $1,000 (268B.01, subd. 43): $184,500 rounds to $185,000 in 2026. diff --git a/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_paid_leave_small_employer.py b/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_paid_leave_small_employer.py index c78cfc79ad9..8b776d183a1 100644 --- a/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_paid_leave_small_employer.py +++ b/policyengine_us/variables/gov/states/mn/tax/payroll/paid_leave/mn_paid_leave_small_employer.py @@ -14,7 +14,8 @@ class mn_paid_leave_small_employer(Variable): "employee count in the basis period (subd. 5b), and " "mn_paid_leave_employer_average_quarterly_wage for its average wage " "(subd. 5c). The limit is a quarterly amount, which DEED states to " - "the cent: 150 percent of $73,989 / 4 is $27,745.88 for 2026. DEED's " + "the cent: 150 percent of $73,989 / 4 is $27,745.875 for 2026, which " + "DEED gives as $27,745.88. DEED's " "further condition that the employer filed every wage detail report " "in the basis period is not modeled." ) @@ -30,9 +31,11 @@ def formula(person, period, parameters): headcount = person("employer_headcount", period) average_wage = person("mn_paid_leave_employer_average_quarterly_wage", period) quarterly_state_average = p.state_average_annual_wage / 4 - # DEED states the limit to the cent ($27,745.88 for 2026). - max_average_wage = np.round( - p.max_average_wage_ratio * quarterly_state_average, 2 + # DEED states the limit to the cent: $27,745.875 for 2026 becomes + # $27,745.88. Round half up, as for the taxable wage base. + max_average_wage = ( + np.floor(100 * p.max_average_wage_ratio * quarterly_state_average + 0.5) + / 100 ) # Inputs are held in float32: compare at that precision, so an input # of exactly the published limit meets it. From a19c04c83c6e932cfcd7bf278a81fa6e15040da5 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 19 Oct 2026 22:51:35 -0400 Subject: [PATCH 10/10] Address review r3: sentinel comment, pin half-up limit rounding - The sentinel comment now says what happens: earlier years read the 2026 value every year, and the zero rate keeps every amount at 0 before 2026. - A reform setting the statewide average to $73,991 puts the limit at $27,746.625, which half-up rounding makes $27,746.63; the new test fails under numpy's half-to-even rounding. Co-Authored-By: Claude Opus 5.5 --- .../paid_leave/employer_minimum_share.yaml | 4 +- .../employer_minimum_share.yaml | 4 +- .../max_average_wage_ratio.yaml | 4 +- .../small_employer/max_headcount.yaml | 4 +- .../paid_leave/small_employer/rate_share.yaml | 4 +- .../state_average_annual_wage.yaml | 4 +- .../tests/core/test_payroll_contributions.py | 42 +++++++++++++++++++ 7 files changed, 54 insertions(+), 12 deletions(-) diff --git a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/employer_minimum_share.yaml b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/employer_minimum_share.yaml index 7c81e136e57..c776058b3eb 100644 --- a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/employer_minimum_share.yaml +++ b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/employer_minimum_share.yaml @@ -1,7 +1,7 @@ description: Minnesota requires employers to pay at least this share of the Paid Leave premium; employees pay the remaining portion, if any, through a wage deduction. values: - # The zero rate keeps the program off before 2026; earlier years read this - # value only under a reform that sets an earlier rate. + # Earlier years read the 2026 value; the zero rate keeps every amount at 0 + # before 2026 unless a reform sets an earlier rate. 0000-01-01: 0.5 2026-01-01: 0.5 diff --git a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/employer_minimum_share.yaml b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/employer_minimum_share.yaml index 63f8148d1ae..49d11a9a8d9 100644 --- a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/employer_minimum_share.yaml +++ b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/employer_minimum_share.yaml @@ -1,7 +1,7 @@ description: Minnesota requires small employers to pay at least this share of the full Paid Leave premium rate; employees pay the remaining portion of the small employer premium, if any, through a wage deduction. values: - # The zero rate keeps the program off before 2026; earlier years read this - # value only under a reform that sets an earlier rate. + # Earlier years read the 2026 value; the zero rate keeps every amount at 0 + # before 2026 unless a reform sets an earlier rate. 0000-01-01: 0.25 2026-01-01: 0.25 diff --git a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_average_wage_ratio.yaml b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_average_wage_ratio.yaml index 386b1e96108..60e12ccb753 100644 --- a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_average_wage_ratio.yaml +++ b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_average_wage_ratio.yaml @@ -1,7 +1,7 @@ description: Minnesota limits the Paid Leave small employer premium rate to employers whose average wage is at most this multiple of the state's average wage in covered employment for the basis period. values: - # The zero rate keeps the program off before 2026; earlier years read this - # value only under a reform that sets an earlier rate. + # Earlier years read the 2026 value; the zero rate keeps every amount at 0 + # before 2026 unless a reform sets an earlier rate. 0000-01-01: 1.5 2026-01-01: 1.5 diff --git a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_headcount.yaml b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_headcount.yaml index 508ccee689c..ef96776fbd4 100644 --- a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_headcount.yaml +++ b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/max_headcount.yaml @@ -1,7 +1,7 @@ description: Minnesota limits the Paid Leave small employer premium rate to employers with at most this many employees. values: - # The zero rate keeps the program off before 2026; earlier years read this - # value only under a reform that sets an earlier rate. + # Earlier years read the 2026 value; the zero rate keeps every amount at 0 + # before 2026 unless a reform sets an earlier rate. 0000-01-01: 30 2026-01-01: 30 diff --git a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/rate_share.yaml b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/rate_share.yaml index 7a8ac37da2f..139fd33a798 100644 --- a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/rate_share.yaml +++ b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/rate_share.yaml @@ -1,7 +1,7 @@ description: Minnesota charges small employers this share of the full Paid Leave premium rate. values: - # The zero rate keeps the program off before 2026; earlier years read this - # value only under a reform that sets an earlier rate. + # Earlier years read the 2026 value; the zero rate keeps every amount at 0 + # before 2026 unless a reform sets an earlier rate. 0000-01-01: 0.75 2026-01-01: 0.75 diff --git a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/state_average_annual_wage.yaml b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/state_average_annual_wage.yaml index 61899de5276..8e8610ee00c 100644 --- a/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/state_average_annual_wage.yaml +++ b/policyengine_us/parameters/gov/states/mn/tax/payroll/paid_leave/small_employer/state_average_annual_wage.yaml @@ -1,7 +1,7 @@ description: Minnesota compares a small employer's average wage with this statewide average annual wage in covered employment, which the Department of Employment and Economic Development calculates each year and applies to the Paid Leave basis period. values: - # The zero rate keeps the program off before 2026; earlier years read this - # value only under a reform that sets an earlier rate. + # Earlier years read the 2026 value; the zero rate keeps every amount at 0 + # before 2026 unless a reform sets an earlier rate. 0000-01-01: 73_989 # Statewide average annual wage for 2025 ("$73,989 per year"), applied to # 2026 premiums, whose basis period is October 2024 to September 2025. diff --git a/policyengine_us/tests/core/test_payroll_contributions.py b/policyengine_us/tests/core/test_payroll_contributions.py index 81cc8fa5815..55e773ae5b2 100644 --- a/policyengine_us/tests/core/test_payroll_contributions.py +++ b/policyengine_us/tests/core/test_payroll_contributions.py @@ -668,6 +668,48 @@ def test_minnesota_paid_leave_shares_follow_the_parameters( ) == pytest.approx([600, small_employer]) +def test_minnesota_small_employer_wage_limit_rounds_half_up(): + # 150% * $73,991 / 4 = $27,746.625, which rounds half up to $27,746.63 + # (half to even would give $27,746.62). + reform = Reform.from_dict( + { + "gov.states.mn.tax.payroll.paid_leave.small_employer.state_average_annual_wage": { + "2026-01-01.2026-12-31": 73_991 + }, + }, + country_id="us", + ) + people = { + name: { + "age": {PERIOD: 30}, + "employment_income": {PERIOD: WAGES}, + "employer_headcount": {PERIOD: 10}, + "mn_paid_leave_employer_average_quarterly_wage": {PERIOD: average_wage}, + } + for name, average_wage in (("at_limit", 27_746.63), ("above", 27_746.64)) + } + sim = Simulation( + tax_benefit_system=CountryTaxBenefitSystem(reform=(reform,)), + situation={ + "people": people, + "households": { + "household": {"members": list(people), "state_code": {PERIOD: "MN"}} + }, + "tax_units": {"tax_unit": {"members": list(people)}}, + "spm_units": {"spm_unit": {"members": list(people)}}, + "families": {"family": {"members": list(people)}}, + "marital_units": { + f"{name}_marital_unit": {"members": [name]} for name in people + }, + }, + ) + + assert sim.calculate("mn_paid_leave_small_employer", PERIOD).tolist() == [ + True, + False, + ] + + @pytest.mark.parametrize( ("state_code", "expected"), [