Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/me-affordability-payment.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Zero Maine's 2025 affordability payment (HP 1491 Part T direct payment, modeled in PolicyEngine as a refundable credit) so it stays out of siitax for ME comparisons.
4 changes: 4 additions & 0 deletions policyengine_taxsim/runners/policyengine_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ def _initialize_dataset_structure(self) -> dict:
"early_head_start", # Early Head Start should be 0 to match TAXSIM (which doesn't model Early Head Start)
"commodity_supplemental_food_program", # Commodity supplemental food program should be 0 to match TAXSIM (which doesn't model this program)
"medical_expense_health_insurance_premiums", # TAXSIM has no medical-expense input; zero PE's imputed Medicare Part B premiums so they don't flow into state medical exemptions/deductions via the federal itemized medical deduction
"me_affordability_payment", # Maine's 2025 affordability payment (HP 1491 Part T) is a direct payment mailed by the assessor in 2026-27, not a 1040ME line; PE models it as a refundable credit, which would land in siitax
}

# Combine all variables
Expand Down Expand Up @@ -904,6 +905,9 @@ def generate(self) -> None:
data["ok_use_tax"][year_int] = np.zeros(
n_year_records
) # Prevent Oklahoma use tax calculations
data["me_affordability_payment"][year_int] = np.zeros(
n_year_records
) # Maine's 2025 affordability payment (HP 1491 Part T, $300/adult) is a direct payment mailed in 2026-27 based on the 2025 return, not a 1040ME line item; TAXSIM/TaxAct never report it, so keep it out of siitax

# Set person-level variables that need to be set per person, not per tax unit
total_people_for_year = len(person_data.get("person_id", []))
Expand Down
48 changes: 48 additions & 0 deletions tests/test_me_affordability_payment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"""
Maine's 2025 affordability payment must stay out of siitax.

H.P. 1491 (132nd Leg.), Part T creates a one-time $300-per-adult payment
for TY2025 filers under FAGI limits, paid by the State Tax Assessor from a
Special Revenue Fund in 2026-27. It is not a 1040ME line item, so neither
TAXSIM nor commercial software reports it in state liability. PolicyEngine
models it as a refundable credit (me_affordability_payment) to surface it
in tax-unit outputs; the emulator zeroes it so ME 2025 comparisons are not
off by $300-600 per record. See taxsim #1056.
"""

import io

import pandas as pd
from click.testing import CliRunner

from policyengine_taxsim.cli import cli

ME = 20


def _row(year: int, mstat: int, page: int, sage: int, pwages: float):
record = (
f"taxsimid,year,state,mstat,page,sage,depx,pwages,idtl\n"
f"1,{year},{ME},{mstat},{page},{sage},0,{pwages},2\n"
)
result = CliRunner().invoke(cli, [], input=record)
assert result.exit_code == 0, f"CLI failed: {result.output}\n{result.exception}"
out = result.output
idx = out.find("taxsimid,")
assert idx != -1, f"No CSV output:\n{out}"
return pd.read_csv(io.StringIO(out[idx:])).iloc[0]


def test_me_2025_joint_excludes_affordability_payment():
"""A 2025 ME joint filer under the FAGI limit must not receive the
$600 ($300 x 2 adults) affordability payment through siitax.

From taxsim #1056: MFJ ages 23/24, ~$10.5K wages. With the payment
included, siitax carried an extra -$600; the couple's only remaining
refundable credits (PTFC/STFC) total well under $600, so siitax must
stay above (less negative than) -600."""
r = _row(year=2025, mstat=2, page=23, sage=24, pwages=10476.19)
assert r["siitax"] > -600, (
f"siitax {r['siitax']} suggests the $600 affordability payment "
f"is still included in ME state liability"
)
Loading