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/or-federal-tax-subtraction-worksheet.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Reduce Oregon's federal tax liability subtraction by the premium tax credit and only the other refundable credits on the Publication OR-17 federal tax worksheet, and leave out the net investment income tax.
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
description: Oregon reduces the federal income tax liability it subtracts by these federal refundable credits.
values:
# Federal tax worksheet, Part A, of each year's Publication OR-17. The earned
# income credit is never listed (ORS 316.685(1)(b)), and outside 2021 neither
# is the additional child tax credit.
2017-01-01:
- refundable_american_opportunity_credit # Line 6
- assigned_aca_ptc # Line 7 (Form 8962, line 24)
2020-01-01:
- refundable_american_opportunity_credit # Line 6
- assigned_aca_ptc # Line 7 (Form 8962, line 24)
- recovery_rebate_credit # Line 8, with the stimulus payments received
2021-01-01:
# Line 8 is the child tax credit on Schedule 8812, line 14e. Line 2 adds
# back the nonrefundable part that line 1 already nets out.
- refundable_ctc
- refundable_american_opportunity_credit # Line 9
- recovery_rebate_credit # Line 10, with the stimulus payments received
- assigned_aca_ptc # Line 11 (Form 8962, line 24)
- cdcc # Line 12 (Schedule 3, line 13g)
2022-01-01:
- refundable_american_opportunity_credit # Line 6
- assigned_aca_ptc # Line 7 (Form 8962, line 24)
# From 2025, line 7 is the refundable adoption credit (Form 1040, line 30),
# which is not modeled, and the premium tax credit moves to line 8.
metadata:
unit: list
period: year
label: Oregon federal tax liability subtraction refundable credits
reference:
- title: ORS 316.680(1)(b) and 316.685
href: https://www.oregonlegislature.gov/bills_laws/ors/ors316.html
- title: "2017 Publication OR-17, federal tax worksheet, Part A, lines 6 to 8 #page 63"
href: https://www.oregon.gov/dor/forms/FormsPubs/publication-or-17_101-431_2017.pdf#page=63
- title: "2018 Publication OR-17, federal tax worksheet, Part A, lines 6 to 8 #page 68"
href: https://www.oregon.gov/dor/forms/FormsPubs/publication-or-17_101-431_2018.pdf#page=68
- title: "2019 Publication OR-17, federal tax worksheet, Part A, lines 6 to 8 #page 63"
href: https://www.oregon.gov/dor/forms/FormsPubs/publication-or-17_101-431_2019.pdf#page=63
- title: "2020 Publication OR-17, federal tax worksheet, Part A, lines 6 to 9 #page 65-66"
href: https://www.oregon.gov/dor/forms/FormsPubs/publication-or-17_101-431_2020.pdf#page=66
- title: "2021 Publication OR-17, federal tax worksheet, Part A, lines 2 and 8 to 13 #page 72-73"
href: https://www.oregon.gov/dor/forms/FormsPubs/publication-or-17_101-431_2021.pdf#page=72
- title: "2022 Publication OR-17, federal tax worksheet, Part A, lines 6 to 8 #page 73"
href: https://www.oregon.gov/dor/forms/FormsPubs/publication-or-17_101-431_2022.pdf#page=73
- title: "2023 Publication OR-17, federal tax worksheet, Part A, lines 6 to 8 #page 73"
href: https://www.oregon.gov/dor/forms/FormsPubs/publication-or-17_101-431_2023.pdf#page=73
- title: "2024 Publication OR-17, federal tax worksheet, Part A, lines 6 to 8 #page 71"
href: https://www.oregon.gov/dor/forms/FormsPubs/publication-or-17_101-431_2024.pdf#page=71
- title: "2025 Publication OR-17, federal tax worksheet, Part A, lines 6 to 9 #page 71-72"
href: https://www.oregon.gov/dor/forms/FormsPubs/publication-or-17_101-431_2025.pdf#page=71
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
"""Oregon's federal tax liability subtraction follows the OR-17 worksheet.

ORS 316.680(1)(b) and 316.685; Publication OR-17, federal tax worksheet,
Part A. The subtraction is federal tax after nonrefundable credits plus other
income taxes, less only the refundable credits the worksheet lists, floored at
zero and capped by federal AGI. These properties hold for every input:

1. 0 <= subtraction <= min(federal tax before refundable credits, cap).
2. The earned income credit never changes it (ORS 316.685(1)(b)), and outside
2021 neither does the additional child tax credit.
3. Raising a listed credit (the premium tax credit) by d lowers it by at most d.
4. Raising federal tax before refundable credits by d raises it by at most d.

Each Hypothesis example evaluates a batch of tax units and their variants in
one vectorized simulation.
"""

import numpy as np
import pytest
from hypothesis import HealthCheck, given, settings
from hypothesis import strategies as st

from policyengine_us import Simulation

TOLERANCE = 0.01
FILING_STATUSES = ("SINGLE", "JOINT", "HEAD_OF_HOUSEHOLD", "SEPARATE")
CREDITS = (
"eitc",
"refundable_ctc",
"refundable_american_opportunity_credit",
"assigned_aca_ptc",
"recovery_rebate_credit",
"cdcc",
)
VARIANTS = ("base", "eitc", "refundable_ctc", "assigned_aca_ptc", "tax")


@st.composite
def tax_unit(draw):
return {
"filing_status": draw(st.sampled_from(FILING_STATUSES)),
"adjusted_gross_income": draw(st.integers(0, 300_000)),
"income_tax_before_refundable_credits": draw(st.integers(0, 20_000)),
"net_investment_income_tax": draw(st.integers(0, 2_000)),
"unreported_payroll_tax": draw(st.integers(0, 1_000)),
**{credit: draw(st.integers(0, 6_000)) for credit in CREDITS},
"delta": draw(st.integers(1, 5_000)),
}


def _run(units, year):
situation = {"people": {}, "tax_units": {}, "households": {}}
rows = []
for i, unit in enumerate(units):
for variant in VARIANTS:
inputs = {k: v for k, v in unit.items() if k != "delta"}
if variant == "tax":
inputs["income_tax_before_refundable_credits"] += unit["delta"]
elif variant != "base":
inputs[variant] += unit["delta"]
name = f"{i}_{variant}"
situation["people"][name] = {"age": {year: 40}}
situation["tax_units"][name] = {
"members": [name],
**{k: {year: v} for k, v in inputs.items()},
}
situation["households"][name] = {
"members": [name],
"state_code": {year: "OR"},
}
rows.append((i, variant))
simulation = Simulation(situation=situation)
values = simulation.calculate("or_federal_tax_liability_subtraction", year)
result = {}
for (i, variant), value in zip(rows, values):
result.setdefault(i, {})[variant] = float(value)
# The largest cap, at zero AGI, from the simulation's own parameters.
p = (
simulation.tax_benefit_system.parameters(f"{year}-01-01")
.gov.states["or"]
.tax.income.subtractions.federal_tax_liability.cap
)
zero = np.array([0.0])
cap = {
"SINGLE": p.single.calc(zero)[0],
"JOINT": p.joint.calc(zero)[0],
"HEAD_OF_HOUSEHOLD": p.head_of_household.calc(zero)[0],
"SEPARATE": p.separate.calc(zero)[0],
}
return result, cap


@pytest.mark.parametrize("year", [2020, 2021, 2025])
@settings(
max_examples=4,
deadline=None,
derandomize=True,
suppress_health_check=[HealthCheck.too_slow, HealthCheck.data_too_large],
)
@given(units=st.lists(tax_unit(), min_size=6, max_size=6))
def test_or_federal_tax_liability_subtraction_invariants(year, units):
result, cap = _run(units, year)
for i, unit in enumerate(units):
r = result[i]
base, delta = r["base"], unit["delta"]
# 1. Bounds.
assert -TOLERANCE <= base
assert base <= unit["income_tax_before_refundable_credits"] + TOLERANCE
assert base <= cap[unit["filing_status"]] + TOLERANCE
# 2. Credits the worksheet never lists.
assert r["eitc"] == pytest.approx(base, abs=TOLERANCE)
if year == 2021:
assert base - delta - TOLERANCE <= r["refundable_ctc"] <= base + TOLERANCE
else:
assert r["refundable_ctc"] == pytest.approx(base, abs=TOLERANCE)
# 3. A listed credit lowers the subtraction by at most its increase.
assert base - delta - TOLERANCE <= r["assigned_aca_ptc"] <= base + TOLERANCE
# 4. More federal tax raises it by at most the increase.
assert base - TOLERANCE <= r["tax"] <= base + delta + TOLERANCE
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
state_code: OR
filing_status: SINGLE
adjusted_gross_income: 80_000
income_tax: 10_000
income_tax_before_refundable_credits: 10_000
eitc: 0
output:
or_federal_tax_liability_subtraction: 7_050
Expand All @@ -15,7 +15,8 @@
state_code: OR
filing_status: SINGLE
adjusted_gross_income: 80_000
income_tax: 6_000
# 6,000 of federal income tax plus the 1,000 EITC it nets out.
income_tax_before_refundable_credits: 7_000
eitc: 1_000
output:
or_federal_tax_liability_subtraction: 7_000
Expand All @@ -26,7 +27,7 @@
state_code: OR
filing_status: SINGLE
adjusted_gross_income: 145_000
income_tax: 10_000
income_tax_before_refundable_credits: 10_000
eitc: 0
output:
or_federal_tax_liability_subtraction: 0
Expand All @@ -37,7 +38,7 @@
state_code: OR
filing_status: SEPARATE
adjusted_gross_income: 80_000
income_tax: 10_000
income_tax_before_refundable_credits: 10_000
eitc: 0
output:
or_federal_tax_liability_subtraction: 3_525
Expand All @@ -48,7 +49,7 @@
state_code: OR
filing_status: SEPARATE
adjusted_gross_income: 145_000
income_tax: 10_000
income_tax_before_refundable_credits: 10_000
eitc: 0
output:
or_federal_tax_liability_subtraction: 0
Expand All @@ -59,8 +60,8 @@
state_code: OR
filing_status: JOINT
adjusted_gross_income: 80_000
income_tax: 10_000
eitc: 0
income_tax_before_refundable_credits: 10_000
eitc: 0
output:
or_federal_tax_liability_subtraction: 7_050

Expand All @@ -70,7 +71,7 @@
state_code: OR
filing_status: JOINT
adjusted_gross_income: 290_000
income_tax: 10_000
income_tax_before_refundable_credits: 10_000
eitc: 0
output:
or_federal_tax_liability_subtraction: 0
Expand All @@ -85,7 +86,7 @@
state_code: OR
filing_status: SINGLE
adjusted_gross_income: 80_000
income_tax: 10_000
income_tax_before_refundable_credits: 10_000
eitc: 0
output:
or_federal_tax_liability_subtraction: 8_750
Expand All @@ -96,7 +97,7 @@
state_code: OR
filing_status: JOINT
adjusted_gross_income: 255_000
income_tax: 30_000
income_tax_before_refundable_credits: 30_000
eitc: 0
output:
or_federal_tax_liability_subtraction: 7_000
Expand All @@ -107,8 +108,123 @@
state_code: OR
filing_status: SEPARATE
adjusted_gross_income: 80_000
income_tax: 10_000
income_tax_before_refundable_credits: 10_000
eitc: 0
output:
# 50% x 8,750
or_federal_tax_liability_subtraction: 4_375

# Federal tax worksheet, Part A, 2025 Publication OR-17, PDF pages 71-72: line 5
# (federal tax after nonrefundable credits plus other income taxes) less the
# American Opportunity, refundable adoption and premium tax credits (lines 6 to
# 9), floored at zero (line 10), then capped (line 12).
- name: Case 11, 2025 premium tax credit reduces the subtraction.
period: 2025
input:
state_code: OR
filing_status: SINGLE
adjusted_gross_income: 45_000
income_tax_before_refundable_credits: 4_000
assigned_aca_ptc: 1_500
eitc: 0
output:
# 4,000 - 1,500
or_federal_tax_liability_subtraction: 2_500

- name: Case 12, 2025 additional child tax credit does not reduce the subtraction.
period: 2025
input:
state_code: OR
filing_status: JOINT
adjusted_gross_income: 60_000
# Form 1040 line 22 is 0; a 1,000 additional tax on an early retirement
# plan distribution is Schedule 2, line 8 (worksheet line 4).
income_tax_before_refundable_credits: 1_000
refundable_ctc: 3_000
assigned_aca_ptc: 0
eitc: 500
output:
or_federal_tax_liability_subtraction: 1_000

- name: Case 13, 2025 listed credits above the liability floor the subtraction at zero.
period: 2025
input:
state_code: OR
filing_status: SINGLE
adjusted_gross_income: 40_000
income_tax_before_refundable_credits: 2_000
refundable_american_opportunity_credit: 1_000
assigned_aca_ptc: 1_500
eitc: 0
output:
# max(0, 2,000 - 1,000 - 1,500)
or_federal_tax_liability_subtraction: 0

- name: Case 14, 2025 net investment income tax and unreported payroll tax are not subtracted.
period: 2025
input:
state_code: OR
filing_status: SINGLE
adjusted_gross_income: 90_000
income_tax_before_refundable_credits: 5_000
# Schedule 2, lines 12 and 5, which worksheet line 4 leaves out.
net_investment_income_tax: 600
unreported_payroll_tax: 400
assigned_aca_ptc: 0
eitc: 0
output:
# 5,000 - 600 - 400
or_federal_tax_liability_subtraction: 4_000

# 2021 Publication OR-17, PDF page 72: lines 8 to 12 subtract the refundable
# child tax credit, the American Opportunity credit, the recovery rebate credit
# with stimulus payments received, the premium tax credit and the refundable
# child and dependent care credit.
- name: Case 15, 2021 refundable child tax, recovery rebate and child care credits reduce the subtraction.
period: 2021
input:
state_code: OR
filing_status: JOINT
adjusted_gross_income: 80_000
income_tax_before_refundable_credits: 9_000
refundable_ctc: 3_000
recovery_rebate_credit: 2_800
cdcc: 1_000
assigned_aca_ptc: 0
eitc: 0
output:
# 9,000 - 3,000 - 2,800 - 1,000
or_federal_tax_liability_subtraction: 2_200

# 2020 Publication OR-17, PDF page 66: line 8 subtracts the stimulus payments
# received plus the recovery rebate credit; the additional child tax credit is
# not listed.
- name: Case 16, 2020 recovery rebate credit reduces the subtraction but the additional child tax credit does not.
period: 2020
input:
state_code: OR
filing_status: JOINT
adjusted_gross_income: 50_000
income_tax_before_refundable_credits: 3_000
recovery_rebate_credit: 2_400
refundable_ctc: 1_000
assigned_aca_ptc: 0
eitc: 0
output:
# 3,000 - 2,400
or_federal_tax_liability_subtraction: 600

- name: Case 17, 2026 premium tax credit and American Opportunity credit reduce the subtraction.
period: 2026
input:
state_code: OR
filing_status: HEAD_OF_HOUSEHOLD
adjusted_gross_income: 70_000
income_tax_before_refundable_credits: 6_000
refundable_american_opportunity_credit: 1_000
assigned_aca_ptc: 2_000
refundable_ctc: 1_700
eitc: 0
output:
# 6,000 - 1,000 - 2,000
or_federal_tax_liability_subtraction: 3_000
Loading