diff --git a/changelog.d/hi-act-35-2026-conformity.fixed.md b/changelog.d/hi-act-35-2026-conformity.fixed.md new file mode 100644 index 00000000000..fe06de0019b --- /dev/null +++ b/changelog.d/hi-act-35-2026-conformity.fixed.md @@ -0,0 +1 @@ +Update Hawaii's income tax for Act 35, SLH 2026 (2025 Internal Revenue Code conformity): apply the overall itemized deduction limitation at Hawaii's $166,800 ($83,400 separate) threshold for every filing status and keep the full deduction when only excluded deductions are claimed; add the 2026-2028 qualified tips deduction and the non-itemizer charitable deduction; end the 2% miscellaneous itemized deductions after 2025; and measure the charitable deduction's limits and 2026 floor against Hawaii AGI. diff --git a/policyengine_us/parameters/gov/states/hi/tax/income/deductions/itemized/limitation/agi_rate.yaml b/policyengine_us/parameters/gov/states/hi/tax/income/deductions/itemized/limitation/agi_rate.yaml new file mode 100644 index 00000000000..36855e20ef1 --- /dev/null +++ b/policyengine_us/parameters/gov/states/hi/tax/income/deductions/itemized/limitation/agi_rate.yaml @@ -0,0 +1,19 @@ +description: Hawaii reduces itemized deductions by this share of Hawaii adjusted gross income above the itemized deductions reduction threshold, under the overall limitation on itemized deductions. +values: + 2021-01-01: 0.03 +metadata: + unit: /1 + period: year + label: Hawaii itemized deductions limitation adjusted gross income rate + reference: + # Section 68 stays operative for Hawaii "in the form that it existed as of + # December 31, 2024" (Act 35, SLH 2026, for taxable years beginning after + # December 31, 2025); section 68(a)(1) sets the 3 percent rate. + - title: Act 35, SLH 2026 (H.B. 2329, C.D. 1), Section 3, amending HRS 235-2.4(b) + href: https://data.capitol.hawaii.gov/sessions/session2026/bills/HB2329_CD1_.pdf#page=18 + - title: 26 U.S. Code 68(a)(1) (2024 edition) + href: https://www.govinfo.gov/content/pkg/USCODE-2024-title26/html/USCODE-2024-title26-subtitleA-chap1-subchapB-partI-sec68.htm + - title: 2021 Hawaii N-11 Instructions, Total Itemized Deductions Worksheet, line 9 + href: https://files.hawaii.gov/tax/forms/2021/n11ins.pdf#page=32 + - title: 2025 Hawaii N-11 Instructions, Total Itemized Deductions Worksheet, line 9 + href: https://files.hawaii.gov/tax/forms/2025/n11ins.pdf#page=34 diff --git a/policyengine_us/parameters/gov/states/hi/tax/income/deductions/itemized/limitation/excluded_deductions.yaml b/policyengine_us/parameters/gov/states/hi/tax/income/deductions/itemized/limitation/excluded_deductions.yaml new file mode 100644 index 00000000000..1f8d53c4c3a --- /dev/null +++ b/policyengine_us/parameters/gov/states/hi/tax/income/deductions/itemized/limitation/excluded_deductions.yaml @@ -0,0 +1,20 @@ +description: Hawaii excludes these deductions from the itemized deductions that the 80 percent ceiling of the overall limitation on itemized deductions applies to. +values: + # Section 68(c) excludes medical expenses, investment interest, and + # casualty, theft and wagering losses. PolicyEngine's Hawaii itemized + # deductions do not include wagering losses. + 2021-01-01: + - hi_medical_expense_deduction + - investment_interest_expense + - hi_casualty_loss_deduction +metadata: + unit: list + period: year + label: Hawaii itemized deductions excluded from the limitation ceiling + reference: + - title: 26 U.S. Code 68(c) (2024 edition) + href: https://www.govinfo.gov/content/pkg/USCODE-2024-title26/html/USCODE-2024-title26-subtitleA-chap1-subchapB-partI-sec68.htm + - title: 2021 Hawaii N-11 Instructions, Total Itemized Deductions Worksheet, line 2 + href: https://files.hawaii.gov/tax/forms/2021/n11ins.pdf#page=32 + - title: 2025 Hawaii N-11 Instructions, Total Itemized Deductions Worksheet, line 2 + href: https://files.hawaii.gov/tax/forms/2025/n11ins.pdf#page=34 diff --git a/policyengine_us/parameters/gov/states/hi/tax/income/deductions/itemized/limitation/itemized_deduction_rate.yaml b/policyengine_us/parameters/gov/states/hi/tax/income/deductions/itemized/limitation/itemized_deduction_rate.yaml new file mode 100644 index 00000000000..f0b32eeed0c --- /dev/null +++ b/policyengine_us/parameters/gov/states/hi/tax/income/deductions/itemized/limitation/itemized_deduction_rate.yaml @@ -0,0 +1,19 @@ +description: Hawaii limits the reduction of itemized deductions to this share of the itemized deductions other than the excluded deductions, under the overall limitation on itemized deductions. +values: + 2021-01-01: 0.8 +metadata: + unit: /1 + period: year + label: Hawaii itemized deductions limitation deduction rate + reference: + # Section 68 stays operative for Hawaii "in the form that it existed as of + # December 31, 2024" (Act 35, SLH 2026, for taxable years beginning after + # December 31, 2025); section 68(a)(2) sets the 80 percent rate. + - title: Act 35, SLH 2026 (H.B. 2329, C.D. 1), Section 3, amending HRS 235-2.4(b) + href: https://data.capitol.hawaii.gov/sessions/session2026/bills/HB2329_CD1_.pdf#page=18 + - title: 26 U.S. Code 68(a)(2) (2024 edition) + href: https://www.govinfo.gov/content/pkg/USCODE-2024-title26/html/USCODE-2024-title26-subtitleA-chap1-subchapB-partI-sec68.htm + - title: 2021 Hawaii N-11 Instructions, Total Itemized Deductions Worksheet, line 5 + href: https://files.hawaii.gov/tax/forms/2021/n11ins.pdf#page=32 + - title: 2025 Hawaii N-11 Instructions, Total Itemized Deductions Worksheet, line 5 + href: https://files.hawaii.gov/tax/forms/2025/n11ins.pdf#page=34 diff --git a/policyengine_us/parameters/gov/states/hi/tax/income/deductions/itemized/misc/in_effect.yaml b/policyengine_us/parameters/gov/states/hi/tax/income/deductions/itemized/misc/in_effect.yaml new file mode 100644 index 00000000000..dcfc1ece0cd --- /dev/null +++ b/policyengine_us/parameters/gov/states/hi/tax/income/deductions/itemized/misc/in_effect.yaml @@ -0,0 +1,22 @@ +description: Hawaii allows miscellaneous itemized deductions above the adjusted gross income floor if this is true. +values: + # HRS 235-2.4 made section 67 operative except for the section 67(g) + # suspension for 2018 through 2025. Act 35, SLH 2026 strikes that exception + # for taxable years beginning after December 31, 2025. + 2021-01-01: true + 2026-01-01: false +metadata: + unit: bool + period: year + label: Hawaii miscellaneous itemized deductions in effect + reference: + - title: 2021 Hawaii N-11 Instructions, Worksheet A-6, Miscellaneous Deductions + href: https://files.hawaii.gov/tax/forms/2021/n11ins.pdf#page=32 + - title: 2025 Hawaii N-11 Instructions, Line 21f, Miscellaneous Deductions + href: https://files.hawaii.gov/tax/forms/2025/n11ins.pdf#page=19 + - title: 2025 Hawaii N-11 Instructions, Worksheet A-6, Miscellaneous Deductions + href: https://files.hawaii.gov/tax/forms/2025/n11ins.pdf#page=34 + - title: Act 35, SLH 2026 (H.B. 2329, C.D. 1), Section 3, amending HRS 235-2.4 + href: https://data.capitol.hawaii.gov/sessions/session2026/bills/HB2329_CD1_.pdf#page=17 + - title: Hawaii Department of Taxation Announcement No. 2026-06, Act 35 + href: https://files.hawaii.gov/tax/news/announce/ann26-06.pdf#page=2 diff --git a/policyengine_us/parameters/gov/states/hi/tax/income/deductions/itemized/sources.yaml b/policyengine_us/parameters/gov/states/hi/tax/income/deductions/itemized/sources.yaml index 20406023cc9..176083a7938 100644 --- a/policyengine_us/parameters/gov/states/hi/tax/income/deductions/itemized/sources.yaml +++ b/policyengine_us/parameters/gov/states/hi/tax/income/deductions/itemized/sources.yaml @@ -1,11 +1,12 @@ description: Hawaii deducts the following itemized deductions. values: 2018-01-01: - - charitable_deduction + - hi_charitable_deduction - hi_medical_expense_deduction - hi_interest_deduction - hi_casualty_loss_deduction - hi_salt_deduction + - hi_misc_deduction metadata: unit: list period: year @@ -24,4 +25,6 @@ metadata: - title: Hawaii Income Tax Law, Chapter 235, Section 235-2.4 href: https://files.hawaii.gov/tax/legal/hrs/hrs_235.pdf#page=10 - title: 2025 Hawaii N-11 Instructions Itemized Deductions - href: https://files.hawaii.gov/tax/forms/current/n11ins.pdf#page=17 + href: https://files.hawaii.gov/tax/forms/2025/n11ins.pdf#page=17 + - title: 2025 Hawaii N-11 Instructions, Itemized Deductions Worksheet, line 32 + href: https://files.hawaii.gov/tax/forms/2025/n11ins.pdf#page=34 diff --git a/policyengine_us/parameters/gov/states/hi/tax/income/deductions/itemized/threshold/reduction.yaml b/policyengine_us/parameters/gov/states/hi/tax/income/deductions/itemized/threshold/reduction.yaml index d92dbf65907..8f0582eb8f5 100644 --- a/policyengine_us/parameters/gov/states/hi/tax/income/deductions/itemized/threshold/reduction.yaml +++ b/policyengine_us/parameters/gov/states/hi/tax/income/deductions/itemized/threshold/reduction.yaml @@ -1,4 +1,4 @@ -description: Hawaii reduces the total itemized deductions amount for filers with federal adjusted gross income below this amount, based on filing status. +description: Hawaii reduces itemized deductions for filers with Hawaii adjusted gross income above this amount, based on filing status, under the overall limitation on itemized deductions. SINGLE: @@ -28,9 +28,15 @@ metadata: href: https://files.hawaii.gov/tax/forms/2022/n11ins.pdf#page=32 - title: 2021 Hawaii Resident Income Tax Instructions href: https://files.hawaii.gov/tax/forms/2021/n11ins.pdf#page=32 - # The legal code specifies a reduction threshold aligning with the IRS threshold in 2009 - # However, the tax forms and instructions only reflect the 2009 threshold for single filers + # HRS 235-2.4 (subsection (c) before 2026, (b) from 2026 under Act 35, + # SLH 2026) uses the section 68 thresholds "operative for federal tax year + # 2009": $166,800 for every filing status except married filing + # separately ($83,400). - title: Hawaii Income Tax Law, Chapter 235, Section 235-2.4, (c)(1) href: https://files.hawaii.gov/tax/legal/hrs/hrs_235.pdf#page=11 - - title: 2025 Hawaii N-11 Instructions Itemized Deductions - href: https://files.hawaii.gov/tax/forms/current/n11ins.pdf#page=32 + - title: Act 35, SLH 2026 (H.B. 2329, C.D. 1), Section 3, amending HRS 235-2.4(b) + href: https://data.capitol.hawaii.gov/sessions/session2026/bills/HB2329_CD1_.pdf#page=18 + - title: 2009 Instructions for Schedule A (Form 1040), Itemized Deductions Worksheet, line 6 + href: https://www.irs.gov/pub/irs-prior/i1040sca--2009.pdf#page=11 + - title: 2025 Hawaii N-11 Instructions, Total Itemized Deductions Worksheet, line 7 + href: https://files.hawaii.gov/tax/forms/2025/n11ins.pdf#page=34 diff --git a/policyengine_us/parameters/gov/states/hi/tax/income/deductions/non_itemizer_charitable/in_effect.yaml b/policyengine_us/parameters/gov/states/hi/tax/income/deductions/non_itemizer_charitable/in_effect.yaml new file mode 100644 index 00000000000..60a89aacf8f --- /dev/null +++ b/policyengine_us/parameters/gov/states/hi/tax/income/deductions/non_itemizer_charitable/in_effect.yaml @@ -0,0 +1,22 @@ +description: Hawaii allows the federal charitable deduction for taxpayers who do not itemize if this is true. +values: + # Act 13, SLH 2020 adopted the 2020 deduction, which reduced federal + # adjusted gross income for taxpayers who did not itemize on their federal + # return; PolicyEngine applies it to Hawaii non-itemizers below the line. + # Act 89, SLH 2021 adopted section 170(p) for 2021. The federal amount is + # zero for 2022 to 2025. Act 35, SLH 2026 conforms to section 170(p) as + # amended by P.L. 119-21 for taxable years beginning after December 31, 2025. + 2020-01-01: true +metadata: + unit: bool + period: year + label: Hawaii non-itemizer charitable deduction in effect + reference: + - title: 2020 Hawaii N-11 Instructions, Changes to Note + href: https://files.hawaii.gov/tax/forms/2020/n11ins.pdf#page=2 + - title: 2021 Hawaii N-11 Instructions, Line 23 + href: https://files.hawaii.gov/tax/forms/2021/n11ins.pdf#page=20 + - title: Hawaii Department of Taxation Announcement No. 2026-06, Act 35 + href: https://files.hawaii.gov/tax/news/announce/ann26-06.pdf#page=2 + - title: 26 U.S. Code 170(p) + href: https://www.law.cornell.edu/uscode/text/26/170#p diff --git a/policyengine_us/parameters/gov/states/hi/tax/income/deductions/tip_income/in_effect.yaml b/policyengine_us/parameters/gov/states/hi/tax/income/deductions/tip_income/in_effect.yaml new file mode 100644 index 00000000000..ac7fb267fb4 --- /dev/null +++ b/policyengine_us/parameters/gov/states/hi/tax/income/deductions/tip_income/in_effect.yaml @@ -0,0 +1,22 @@ +description: Hawaii allows the federal deduction for qualified tips if this is true. +values: + # For 2025, HRS 235-2.3(a) conforms to the Internal Revenue Code as amended + # as of December 31, 2024, before P.L. 119-21 added section 224. + 2025-01-01: false + # Act 35, SLH 2026 conforms to the Internal Revenue Code as amended as of + # December 31, 2025 and does not make section 224 inoperative. + 2026-01-01: true + # Section 224(h) allows no deduction for taxable years beginning after + # December 31, 2028. + 2029-01-01: false +metadata: + unit: bool + period: year + label: Hawaii qualified tips deduction in effect + reference: + - title: Act 35, SLH 2026 (H.B. 2329, C.D. 1), Section 2, amending HRS 235-2.3 + href: https://data.capitol.hawaii.gov/sessions/session2026/bills/HB2329_CD1_.pdf#page=1 + - title: Hawaii Department of Taxation Announcement No. 2026-06, Act 35 + href: https://files.hawaii.gov/tax/news/announce/ann26-06.pdf#page=3 + - title: 26 U.S. Code 224(h) + href: https://www.law.cornell.edu/uscode/text/26/224#h diff --git a/policyengine_us/tests/core/test_hi_itemized_deduction_limitation_properties.py b/policyengine_us/tests/core/test_hi_itemized_deduction_limitation_properties.py new file mode 100644 index 00000000000..a3e662572af --- /dev/null +++ b/policyengine_us/tests/core/test_hi_itemized_deduction_limitation_properties.py @@ -0,0 +1,294 @@ +"""Invariants of Hawaii's overall limitation on itemized deductions and tips deduction. + +HRS 235-2.4 keeps section 68 of the Internal Revenue Code operative with the +thresholds "operative for federal tax year 2009" ($166,800, or $83,400 for a +married individual filing separately). Act 35, SLH 2026 keeps section 68 "in +the form that it existed as of December 31, 2024" for taxable years beginning +after December 31, 2025, so the 3 percent and 80 percent rates and the section +68(c) exclusions (medical expenses, investment interest, casualty losses) do +not change in 2026. The Total Itemized Deductions Worksheet in the Form N-11 +instructions implements this. + +Act 35 also conforms Hawaii to the section 224 deduction for qualified tips +from 2026 (section 224(h) ends it after 2028). + +Batches of tax units share one simulation per example. The tests check: + +1. hi_itemized_deductions_reduction matches an independent restatement of + worksheet lines 1-10, and hi_itemized_deductions equals line 11. +2. The reduction lies between 0 and both the 3 percent amount and 80 percent + of the deductions that section 68(c) does not exclude, so the excluded + deductions are never reduced. +3. The 2025 and 2026 results are identical for the same inputs. +4. Raising an excluded deduction leaves the reduction unchanged; raising any + deduction never lowers the allowed amount; raising Hawaii AGI never lowers + the reduction. +5. The tips deduction lies between 0 and the smaller of $25,000 and the + qualified tips, never rises with Hawaii AGI, is zero for married filing + separately, and is zero in 2025 and 2029. +""" + +import numpy as np +from hypothesis import HealthCheck, given, settings +from hypothesis import strategies as st + +from policyengine_us import Simulation + +STATUSES = ("SINGLE", "JOINT", "SEPARATE", "HEAD_OF_HOUSEHOLD", "SURVIVING_SPOUSE") +# Worksheet line 7: "Enter $166,800 ($83,400 if married filing separately)". +THRESHOLD = 166_800 +SEPARATE_THRESHOLD = 83_400 + +# Components of hi_total_itemized_deductions, split by whether section 68(c) +# excludes them. Investment interest and mortgage interest are person inputs +# that hi_interest_deduction adds up. +EXCLUDED = ("hi_medical_expense_deduction", "hi_casualty_loss_deduction") +EXCLUDED_PERSON = ("investment_interest_expense",) +OTHER = ("hi_charitable_deduction", "hi_salt_deduction", "hi_misc_deduction") +OTHER_PERSON = ("mortgage_interest",) +TAX_UNIT_COMPONENTS = EXCLUDED + OTHER +PERSON_COMPONENTS = EXCLUDED_PERSON + OTHER_PERSON +ALL_COMPONENTS = TAX_UNIT_COMPONENTS + PERSON_COMPONENTS +# PolicyEngine computes in float32, whose spacing near $1 million is 6 cents. +RTOL = 1e-6 +ATOL = 0.01 + + +def _slack(scale): + return ATOL + RTOL * np.abs(scale) + + +def _close(actual, desired, scale, message=""): + gap = np.abs(np.asarray(actual, dtype=float) - desired) + worst = int(np.argmax(gap - _slack(scale))) + assert np.all(gap <= _slack(scale)), ( + f"{message} unit {worst}: {actual[worst]} vs {desired[worst]}" + ) + + +money = st.one_of(st.just(0.0), st.integers(1, 120_000).map(float)) + + +@st.composite +def tax_units(draw): + unit = { + "filing_status": draw(st.sampled_from(STATUSES)), + # Hawaii AGI near the thresholds, far above them, and negative. + "hi_agi": float( + draw( + st.one_of( + st.integers(-50_000, 1_500_000), + st.integers(SEPARATE_THRESHOLD - 2, SEPARATE_THRESHOLD + 2), + st.integers(THRESHOLD - 2, THRESHOLD + 2), + ) + ) + ), + } + only_excluded = draw(st.booleans()) + for name in ALL_COMPONENTS: + excluded = name in EXCLUDED + EXCLUDED_PERSON + unit[name] = draw(money) if excluded or not only_excluded else 0.0 + return unit + + +def _arrays(units): + return {key: np.array([unit[key] for unit in units]) for key in units[0]} + + +def _simulate(year, units, outputs): + period = str(year) + people, groups, households = {}, {}, {} + for i, unit in enumerate(units): + person = f"p{i}" + people[person] = {"age": {period: 45}} + for name in PERSON_COMPONENTS: + people[person][name] = {period: unit[name]} + groups[f"t{i}"] = { + "members": [person], + "filing_status": {period: unit["filing_status"]}, + "hi_agi": {period: unit["hi_agi"]}, + **{name: {period: unit[name]} for name in TAX_UNIT_COMPONENTS}, + } + households[f"h{i}"] = {"members": [person], "state_code": {period: "HI"}} + simulation = Simulation( + situation={"people": people, "tax_units": groups, "households": households} + ) + return {name: simulation.calculate(name, period) for name in outputs} + + +def _thresholds(statuses): + return np.where(statuses == "SEPARATE", SEPARATE_THRESHOLD, THRESHOLD) + + +def _split(units): + excluded = sum(units[name] for name in EXCLUDED + EXCLUDED_PERSON) + other = sum(units[name] for name in OTHER + OTHER_PERSON) + return excluded, other + + +def _worksheet_line_10(units): + """Total Itemized Deductions Worksheet, lines 1-10, restated.""" + excluded, other = _split(units) + line_1 = excluded + other + line_3 = excluded + line_4 = np.where(line_3 < line_1, line_1 - line_3, 0) + line_5 = line_4 * 0.8 + line_6 = units["hi_agi"] + line_7 = _thresholds(units["filing_status"]) + line_8 = np.where(line_7 < line_6, line_6 - line_7, 0) + line_9 = line_8 * 0.03 + return np.minimum(line_5, line_9) + + +LIMITATION_OUTPUTS = ( + "hi_total_itemized_deductions", + "hi_itemized_deductions_reduction", + "hi_itemized_deductions", +) + +# A batch's cost is mostly per-variable overhead, so each example is a large +# batch and there are few examples. +SETTINGS = dict( + max_examples=5, + deadline=None, + derandomize=True, + suppress_health_check=[HealthCheck.too_slow, HealthCheck.data_too_large], +) + + +@settings(**SETTINGS) +@given(st.lists(tax_units(), min_size=20, max_size=60)) +def test_limitation_matches_the_worksheet_in_2025_and_2026(units): + arrays = _arrays(units) + excluded, other = _split(arrays) + scale = excluded + other + np.abs(arrays["hi_agi"]) + line_10 = _worksheet_line_10(arrays) + results = {} + for year in (2025, 2026): + run = _simulate(year, units, LIMITATION_OUTPUTS) + results[year] = run + total = run["hi_total_itemized_deductions"] + reduction = run["hi_itemized_deductions_reduction"] + allowed = run["hi_itemized_deductions"] + _close(total, excluded + other, scale, f"{year} total") + # 1. Worksheet lines 10 and 11. + _close(reduction, line_10, scale, f"{year} line 10") + _close(allowed, excluded + other - line_10, scale, f"{year} line 11") + # 2. Bounds; excluded deductions are never reduced. + agi_excess = np.maximum( + 0, arrays["hi_agi"] - _thresholds(arrays["filing_status"]) + ) + assert np.all(reduction >= 0) + assert np.all(reduction <= 0.03 * agi_excess + _slack(scale)) + assert np.all(reduction <= 0.8 * other + _slack(scale)) + assert np.all(allowed >= excluded + 0.2 * other - _slack(scale)) + # 3. Act 35 leaves the limitation unchanged in 2026. + for name in LIMITATION_OUTPUTS: + np.testing.assert_array_equal(results[2025][name], results[2026][name]) + + +def test_limitation_responds_to_components_and_income(): + rng = np.random.default_rng(2026) + n = 200 + units = [] + for i in range(n): + unit = { + "filing_status": str(STATUSES[i % len(STATUSES)]), + "hi_agi": float(np.round(rng.uniform(-20_000, 800_000), 2)), + } + for name in ALL_COMPONENTS: + amount = float(np.round(rng.uniform(0, 60_000), 2)) + unit[name] = amount if rng.random() < 0.6 else 0.0 + units.append(unit) + outputs = ("hi_itemized_deductions_reduction", "hi_itemized_deductions") + base = _simulate(2026, units, outputs) + arrays = _arrays(units) + excluded, other = _split(arrays) + scale = excluded + other + np.abs(arrays["hi_agi"]) + 10_000 + assert np.any(base["hi_itemized_deductions_reduction"] > 0) + for name in ALL_COMPONENTS: + raised = [dict(unit, **{name: unit[name] + 1_000}) for unit in units] + run = _simulate(2026, raised, outputs) + # 4. More of any deduction never lowers the allowed amount. + assert np.all( + run["hi_itemized_deductions"] + >= base["hi_itemized_deductions"] - _slack(scale) + ), name + if name in EXCLUDED + EXCLUDED_PERSON: + # Excluded deductions pass through without reduction. + _close( + run["hi_itemized_deductions_reduction"], + base["hi_itemized_deductions_reduction"], + scale, + name, + ) + _close( + run["hi_itemized_deductions"], + base["hi_itemized_deductions"] + 1_000, + scale, + name, + ) + richer = [dict(unit, hi_agi=unit["hi_agi"] + 10_000) for unit in units] + richer_reduction = _simulate(2026, richer, outputs)[ + "hi_itemized_deductions_reduction" + ] + assert np.all( + richer_reduction >= base["hi_itemized_deductions_reduction"] - _slack(scale) + ) + + +def _tips_situation(units, period): + people, groups, households = {}, {}, {} + for i, unit in enumerate(units): + person = f"p{i}" + people[person] = { + "age": {period: 30}, + "tip_income": {period: unit["tip_income"]}, + "treasury_tipped_occupation_code": {period: unit["occupation_code"]}, + } + groups[f"t{i}"] = { + "members": [person], + "filing_status": {period: unit["filing_status"]}, + "hi_agi": {period: unit["hi_agi"]}, + } + households[f"h{i}"] = {"members": [person], "state_code": {period: "HI"}} + return {"people": people, "tax_units": groups, "households": households} + + +def test_tips_deduction_bounds_and_years(): + rng = np.random.default_rng(224) + n = 250 + units = [ + { + "filing_status": str(STATUSES[i % len(STATUSES)]), + "hi_agi": float(np.round(rng.uniform(-10_000, 600_000), 2)), + "tip_income": float(np.round(rng.uniform(0, 40_000), 2)), + "occupation_code": int(rng.choice([0, 102])), + } + for i in range(n) + ] + arrays = _arrays(units) + qualified = arrays["tip_income"] * (arrays["occupation_code"] > 0) + separate = arrays["filing_status"] == "SEPARATE" + for year in (2025, 2029): + period = str(year) + deduction = Simulation(situation=_tips_situation(units, period)).calculate( + "hi_tip_income_deduction", period + ) + # 5. No deduction before Act 35 or after section 224(h) ends it. + assert np.all(deduction == 0), year + for year in (2026, 2028): + period = str(year) + base = Simulation(situation=_tips_situation(units, period)).calculate( + "hi_tip_income_deduction", period + ) + richer_units = [dict(unit, hi_agi=unit["hi_agi"] + 5_000) for unit in units] + richer = Simulation(situation=_tips_situation(richer_units, period)).calculate( + "hi_tip_income_deduction", period + ) + scale = qualified + np.abs(arrays["hi_agi"]) + assert np.all(base >= 0) + assert np.all(base <= np.minimum(25_000, qualified) + _slack(scale)) + assert np.all(base[separate] == 0) + assert np.all(richer <= base + _slack(scale)) + assert np.any(base > 0) diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/hi_deductions.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/hi_deductions.yaml index fee63c3b063..80adfded752 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/hi_deductions.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/hi_deductions.yaml @@ -6,7 +6,7 @@ tax_unit_earned_income: 1_000 hi_agi: 1_000 hi_total_itemized_deductions: 5_000 - hi_reduced_itemized_deductions: 1_000 + hi_itemized_deductions_reduction: 0 head_is_dependent_elsewhere: false output: hi_deductions: 5_000 @@ -19,7 +19,7 @@ tax_unit_earned_income: 1_000_000 hi_agi: 1_000_000 hi_total_itemized_deductions: 5_000 - hi_reduced_itemized_deductions: 1_000 + hi_itemized_deductions_reduction: 4_000 head_is_dependent_elsewhere: false output: hi_deductions: 4_400 @@ -32,7 +32,7 @@ tax_unit_earned_income: 1_000 hi_agi: 1_000 hi_total_itemized_deductions: 1_000 - hi_reduced_itemized_deductions: 1_000 + hi_itemized_deductions_reduction: 0 head_is_dependent_elsewhere: false output: hi_deductions: 4_400 @@ -45,7 +45,7 @@ tax_unit_earned_income: 1_000_000 hi_agi: 1_000_000 hi_total_itemized_deductions: 5_000 - hi_reduced_itemized_deductions: 1_000 + hi_itemized_deductions_reduction: 4_000 head_is_dependent_elsewhere: false output: hi_deductions: 2_200 @@ -58,7 +58,7 @@ tax_unit_earned_income: 1_000_000 hi_agi: 1_000_000 hi_total_itemized_deductions: 5_000 - hi_reduced_itemized_deductions: 4_500 + hi_itemized_deductions_reduction: 500 head_is_dependent_elsewhere: false output: hi_deductions: 4_500 @@ -71,3 +71,103 @@ employment_income: 62_000 output: hi_deductions: 8_800 + +- name: Case 7, non-itemizer adds the charitable deduction to the standard deduction in 2026. + period: 2026 + input: + state_code: HI + filing_status: SINGLE + hi_agi: 60_000 + hi_total_itemized_deductions: 0 + charitable_cash_donations: 1_500 + head_is_dependent_elsewhere: false + output: + # 8,000 standard deduction + min(1,000, 1,500) = 9,000. + hi_deductions: 9_000 + +- name: Case 8, joint non-itemizers get up to $2,000 in 2026. + period: 2026 + input: + state_code: HI + filing_status: JOINT + hi_agi: 90_000 + hi_total_itemized_deductions: 0 + charitable_cash_donations: 3_000 + head_is_dependent_elsewhere: false + output: + # 16,000 + 2,000 = 18,000. + hi_deductions: 18_000 + +- name: Case 9, no non-itemizer charitable deduction in 2025. + period: 2025 + input: + state_code: HI + filing_status: SINGLE + hi_agi: 60_000 + hi_total_itemized_deductions: 0 + charitable_cash_donations: 1_500 + head_is_dependent_elsewhere: false + output: + hi_deductions: 4_400 + +- name: Case 10, Act 89 allowed up to $300 for non-itemizers in 2021. + period: 2021 + input: + state_code: HI + filing_status: SINGLE + hi_agi: 60_000 + hi_total_itemized_deductions: 0 + charitable_cash_donations: 500 + head_is_dependent_elsewhere: false + output: + # 2,200 + 300 = 2,500. + hi_deductions: 2_500 + +- name: Case 11, itemizers do not get the non-itemizer charitable deduction. + period: 2026 + input: + state_code: HI + filing_status: SINGLE + hi_agi: 60_000 + hi_itemized_deductions: 20_000 + charitable_cash_donations: 1_500 + head_is_dependent_elsewhere: false + output: + hi_deductions: 20_000 + +- name: Case 12, itemizer is better off taking the standard and charitable deductions. + period: 2026 + input: + state_code: HI + filing_status: SINGLE + hi_agi: 60_000 + hi_itemized_deductions: 8_500 + charitable_cash_donations: 1_500 + head_is_dependent_elsewhere: false + output: + # max(8,500, 8,000 + 1,000) = 9,000. + hi_deductions: 9_000 + +- name: Case 13, the tips deduction is added for itemizers. + period: 2026 + input: + state_code: HI + filing_status: SINGLE + hi_agi: 60_000 + hi_itemized_deductions: 20_000 + hi_tip_income_deduction: 5_000 + head_is_dependent_elsewhere: false + output: + hi_deductions: 25_000 + +- name: Case 14, the tips deduction is added for non-itemizers. + period: 2026 + input: + state_code: HI + filing_status: SINGLE + hi_agi: 60_000 + hi_total_itemized_deductions: 0 + hi_tip_income_deduction: 5_000 + head_is_dependent_elsewhere: false + output: + hi_deductions: 13_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/hi_tip_income_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/hi_tip_income_deduction.yaml new file mode 100644 index 00000000000..845419ff689 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/hi_tip_income_deduction.yaml @@ -0,0 +1,125 @@ +- name: Case 1, tipped worker deducts qualified tips in 2026. + period: 2026 + input: + state_code: HI + filing_status: SINGLE + tip_income_deduction_ssn_requirement_met: true + treasury_tipped_occupation_code: 102 + tip_income: 10_000 + hi_agi: 50_000 + output: + hi_tip_income_deduction: 10_000 + +- name: Case 2, no Hawaii tips deduction in 2025. + period: 2025 + input: + state_code: HI + filing_status: SINGLE + tip_income_deduction_ssn_requirement_met: true + treasury_tipped_occupation_code: 102 + tip_income: 10_000 + hi_agi: 50_000 + output: + # HRS 235-2.3(a) uses the Internal Revenue Code as of December 31, 2024. + hi_tip_income_deduction: 0 + +- name: Case 3, the deduction ends after 2028. + period: 2029 + input: + state_code: HI + filing_status: SINGLE + tip_income_deduction_ssn_requirement_met: true + treasury_tipped_occupation_code: 102 + tip_income: 10_000 + hi_agi: 50_000 + output: + hi_tip_income_deduction: 0 + +- name: Case 4, the deduction is capped at $25,000. + period: 2026 + input: + state_code: HI + filing_status: SINGLE + tip_income_deduction_ssn_requirement_met: true + treasury_tipped_occupation_code: 102 + tip_income: 30_000 + hi_agi: 50_000 + output: + hi_tip_income_deduction: 25_000 + +- name: Case 5, the phase-out uses Hawaii AGI, not federal AGI. + period: 2026 + input: + state_code: HI + filing_status: SINGLE + tip_income_deduction_ssn_requirement_met: true + treasury_tipped_occupation_code: 102 + tip_income: 10_000 + adjusted_gross_income: 250_000 + hi_agi: 200_000 + output: + # (200,000 - 150,000) * 10% = 5,000. 10,000 - 5,000 = 5,000. + hi_tip_income_deduction: 5_000 + +- name: Case 6, joint filers phase out above $300,000. + period: 2026 + input: + state_code: HI + filing_status: JOINT + tip_income_deduction_ssn_requirement_met: true + treasury_tipped_occupation_code: 102 + tip_income: 10_000 + hi_agi: 320_000 + output: + # (320,000 - 300,000) * 10% = 2,000. 10,000 - 2,000 = 8,000. + hi_tip_income_deduction: 8_000 + +- name: Case 7, married filing separately gets no deduction. + period: 2026 + input: + state_code: HI + filing_status: SEPARATE + tip_income_deduction_ssn_requirement_met: true + treasury_tipped_occupation_code: 102 + tip_income: 10_000 + hi_agi: 50_000 + output: + # Section 224(f) requires married individuals to file a joint return. + hi_tip_income_deduction: 0 + +- name: Case 8, tips outside a listed tipped occupation do not qualify. + period: 2026 + input: + state_code: HI + filing_status: SINGLE + tip_income_deduction_ssn_requirement_met: true + treasury_tipped_occupation_code: 0 + tip_income: 10_000 + hi_agi: 50_000 + output: + hi_tip_income_deduction: 0 + +- name: Case 9, no deduction without a valid social security number. + period: 2026 + input: + state_code: HI + filing_status: SINGLE + tip_income_deduction_ssn_requirement_met: false + treasury_tipped_occupation_code: 102 + tip_income: 10_000 + hi_agi: 50_000 + output: + hi_tip_income_deduction: 0 + +- name: Case 10, fully phased out. + period: 2026 + input: + state_code: HI + filing_status: HEAD_OF_HOUSEHOLD + tip_income_deduction_ssn_requirement_met: true + treasury_tipped_occupation_code: 102 + tip_income: 10_000 + hi_agi: 260_000 + output: + # (260,000 - 150,000) * 10% = 11,000, more than the 10,000 of tips. + hi_tip_income_deduction: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_charitable_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_charitable_deduction.yaml new file mode 100644 index 00000000000..62e339f2de4 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_charitable_deduction.yaml @@ -0,0 +1,73 @@ +- name: Case 1, the 2026 floor is 0.5% of Hawaii AGI, not federal AGI. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: HI + filing_status: SINGLE + adjusted_gross_income: 110_600 + hi_agi: 50_000 + charitable_cash_donations: 20_000 + output: + # 20,000 - 0.5% * 50,000 = 19,750, below 60% of Hawaii AGI (30,000). + hi_charitable_deduction: 19_750 + +- name: Case 2, the 60% ceiling is a share of Hawaii AGI. + period: 2025 + absolute_error_margin: 0.01 + input: + state_code: HI + filing_status: SINGLE + adjusted_gross_income: 110_600 + hi_agi: 50_000 + charitable_cash_donations: 40_000 + output: + # 60% * 50,000 = 30,000. + hi_charitable_deduction: 30_000 + +- name: Case 3, no floor in 2025. + period: 2025 + absolute_error_margin: 0.01 + input: + state_code: HI + filing_status: SINGLE + hi_agi: 100_000 + charitable_cash_donations: 5_000 + output: + hi_charitable_deduction: 5_000 + +- name: Case 4, the floor applies to non-cash gifts first in 2026. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: HI + filing_status: JOINT + hi_agi: 100_000 + charitable_cash_donations: 1_000 + charitable_non_cash_donations: 300 + output: + # Floor 500: non-cash 300 is used up, then 200 of the cash. + # 0 + (1,000 - 200) = 800. + hi_charitable_deduction: 800 + +- name: Case 5, the 50% ceiling on non-cash gifts uses Hawaii AGI. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: HI + filing_status: JOINT + hi_agi: 100_000 + charitable_non_cash_donations: 60_000 + output: + # (60,000 - 500) capped at 50% * 100,000 = 50,000. + hi_charitable_deduction: 50_000 + +- name: Case 6, negative Hawaii AGI allows no deduction. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: HI + filing_status: SINGLE + hi_agi: -10_000 + charitable_cash_donations: 1_000 + output: + hi_charitable_deduction: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_itemized_deductions.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_itemized_deductions.yaml index 5a44ba7f696..b03767807d5 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_itemized_deductions.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_itemized_deductions.yaml @@ -1,33 +1,142 @@ -- name: Total deduction amount, single +- name: Case 1, single filer below the reduction threshold keeps the total. period: 2022 input: state_code: HI filing_status: SINGLE hi_agi: 166_779 hi_total_itemized_deductions: 5_000 - hi_reduced_itemized_deductions: 1_000 output: hi_itemized_deductions: 5_000 - -- name: Total deduction amount joint +- name: Case 2, joint filer below the reduction threshold keeps the total. period: 2022 input: state_code: HI filing_status: JOINT hi_agi: 166_779 hi_total_itemized_deductions: 5_000 - hi_reduced_itemized_deductions: 4_500 output: hi_itemized_deductions: 5_000 -- name: Separate, have reduced deductions +- name: Case 3, total less the reduction. period: 2022 input: state_code: HI filing_status: SEPARATE hi_agi: 166_880 hi_total_itemized_deductions: 5_000 - hi_reduced_itemized_deductions: 4_500 + hi_itemized_deductions_reduction: 500 output: hi_itemized_deductions: 4_500 + +- name: Case 4, joint filer above $166,800 is limited in 2026. + period: 2026 + input: + state_code: HI + filing_status: JOINT + hi_agi: 200_000 + hi_total_itemized_deductions: 30_000 + output: + # Worksheet line 9: (200,000 - 166,800) * 3% = 996, below line 5 (24,000). + # 30,000 - 996 = 29,004. + hi_itemized_deductions: 29_004 + +- name: Case 5, head of household above $166,800 is limited in 2026. + period: 2026 + input: + state_code: HI + filing_status: HEAD_OF_HOUSEHOLD + hi_agi: 200_000 + hi_total_itemized_deductions: 30_000 + output: + hi_itemized_deductions: 29_004 + +- name: Case 6, surviving spouse above $166,800 is limited in 2026. + period: 2026 + input: + state_code: HI + filing_status: SURVIVING_SPOUSE + hi_agi: 200_000 + hi_total_itemized_deductions: 30_000 + output: + hi_itemized_deductions: 29_004 + +- name: Case 7, separate filer above $83,400 is limited in 2026. + period: 2026 + input: + state_code: HI + filing_status: SEPARATE + hi_agi: 100_000 + hi_total_itemized_deductions: 30_000 + output: + # (100,000 - 83,400) * 3% = 498. 30,000 - 498 = 29,502. + hi_itemized_deductions: 29_502 + +- name: Case 8, single filer above $166,800 is limited in 2026. + period: 2026 + input: + state_code: HI + filing_status: SINGLE + hi_agi: 200_000 + hi_total_itemized_deductions: 30_000 + output: + hi_itemized_deductions: 29_004 + +- name: Case 9, only medical expenses, deduction is not limited. + period: 2026 + input: + state_code: HI + filing_status: SINGLE + hi_agi: 300_000 + hi_medical_expense_deduction: 20_000 + hi_total_itemized_deductions: 20_000 + output: + # Worksheet line 4: the excluded deductions are not less than the total, + # so the deduction is not limited. + hi_itemized_deductions: 20_000 + +- name: Case 10, the 80% ceiling applies to deductions other than medical expenses. + period: 2026 + input: + state_code: HI + filing_status: SINGLE + hi_agi: 1_000_000 + hi_medical_expense_deduction: 45_000 + hi_total_itemized_deductions: 50_000 + output: + # Line 5: (50,000 - 45,000) * 80% = 4,000. + # Line 9: (1,000,000 - 166,800) * 3% = 24,996. + # 50,000 - 4,000 = 46,000. + hi_itemized_deductions: 46_000 + +- name: Case 11, joint filer exactly at the threshold keeps the total. + period: 2026 + input: + state_code: HI + filing_status: JOINT + hi_agi: 166_800 + hi_total_itemized_deductions: 30_000 + output: + hi_itemized_deductions: 30_000 + +- name: Case 12, the same limit applies in 2025. + period: 2025 + input: + state_code: HI + filing_status: JOINT + hi_agi: 200_000 + hi_total_itemized_deductions: 30_000 + output: + hi_itemized_deductions: 29_004 + +- name: Case 13, high-income joint filer in 2026 loses 80% of limited deductions. + period: 2026 + input: + state_code: HI + filing_status: JOINT + hi_agi: 1_000_000 + hi_total_itemized_deductions: 30_000 + output: + # The federal 2/37 limit under P.L. 119-21 does not apply in Hawaii. + # 30,000 - min(24,000, 24,996) = 6,000. + hi_itemized_deductions: 6_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_itemized_deductions_reduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_itemized_deductions_reduction.yaml new file mode 100644 index 00000000000..7fd0864660f --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_itemized_deductions_reduction.yaml @@ -0,0 +1,114 @@ +- name: Case 1, separate filer above the threshold, reduction is 3% of the excess AGI. + period: 2022 + input: + people: + person1: + investment_interest_expense: 100 + person2: + investment_interest_expense: 100 + tax_units: + tax_unit: + filing_status: SEPARATE + hi_medical_expense_deduction: 300 + hi_casualty_loss_deduction: 400 + hi_agi: 200_000 + members: [person1, person2] + hi_total_itemized_deductions: 10_000 + households: + household: + members: [person1, person2] + state_code: HI + output: + # Line 5: (10,000 - 900) * 80% = 7,280. + # Line 9: (200,000 - 83,400) * 3% = 3,498. + hi_itemized_deductions_reduction: 3_498 + +- name: Case 2, Hawaii AGI below the threshold, no reduction. + period: 2022 + input: + people: + person1: + investment_interest_expense: 100 + person2: + investment_interest_expense: 100 + tax_units: + tax_unit: + filing_status: JOINT + hi_medical_expense_deduction: 300 + hi_casualty_loss_deduction: 400 + hi_agi: 166_779 + members: [person1, person2] + hi_total_itemized_deductions: 10_000 + households: + household: + members: [person1, person2] + state_code: HI + output: + hi_itemized_deductions_reduction: 0 + +- name: Case 3, excluded deductions equal the total, no reduction. + period: 2022 + input: + people: + person1: + investment_interest_expense: 100 + person2: + investment_interest_expense: 100 + tax_units: + tax_unit: + filing_status: JOINT + hi_medical_expense_deduction: 300 + hi_casualty_loss_deduction: 400 + hi_agi: 166_880 + members: [person1, person2] + hi_total_itemized_deductions: 900 + households: + household: + members: [person1, person2] + state_code: HI + output: + hi_itemized_deductions_reduction: 0 + +- name: Case 4, joint filer in 2026 uses the $166,800 threshold, not a joint federal amount. + period: 2026 + input: + state_code: HI + filing_status: JOINT + hi_agi: 200_000 + hi_total_itemized_deductions: 30_000 + output: + # Line 5: 30,000 * 80% = 24,000. + # Line 9: (200,000 - 166,800) * 3% = 996. + hi_itemized_deductions_reduction: 996 + +- name: Case 5, Hawaii AGI exactly at the threshold, no reduction. + period: 2026 + input: + state_code: HI + filing_status: SINGLE + hi_agi: 166_800 + hi_total_itemized_deductions: 30_000 + output: + hi_itemized_deductions_reduction: 0 + +- name: Case 6, the 80% ceiling binds at high income in 2026, not the federal 2/37 rule. + period: 2026 + input: + state_code: HI + filing_status: JOINT + hi_agi: 1_000_000 + hi_total_itemized_deductions: 30_000 + output: + # Line 5: 30,000 * 80% = 24,000. + # Line 9: (1,000,000 - 166,800) * 3% = 24,996. + hi_itemized_deductions_reduction: 24_000 + +- name: Case 7, negative Hawaii AGI, no reduction. + period: 2026 + input: + state_code: HI + filing_status: SINGLE + hi_agi: -50_000 + hi_total_itemized_deductions: 30_000 + output: + hi_itemized_deductions_reduction: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_misc_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_misc_deduction.yaml new file mode 100644 index 00000000000..7a67ab6af1c --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_misc_deduction.yaml @@ -0,0 +1,77 @@ +- name: Case 1, expenses above 2% of Hawaii AGI are deductible in 2025. + period: 2025 + absolute_error_margin: 0.01 + input: + state_code: HI + unreimbursed_business_employee_expenses: 5_000 + tax_preparation_fees: 500 + hi_agi: 50_000 + output: + # 5,500 - 2% * 50,000 = 4,500. + hi_misc_deduction: 4_500 + +- name: Case 2, Act 35 ends the deduction from 2026. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: HI + unreimbursed_business_employee_expenses: 5_000 + tax_preparation_fees: 500 + hi_agi: 50_000 + output: + hi_misc_deduction: 0 + +- name: Case 3, expenses below the floor give no deduction. + period: 2025 + absolute_error_margin: 0.01 + input: + state_code: HI + tax_preparation_fees: 800 + hi_agi: 50_000 + output: + hi_misc_deduction: 0 + +- name: Case 4, the floor is zero when Hawaii AGI is negative. + period: 2025 + absolute_error_margin: 0.01 + input: + state_code: HI + tax_preparation_fees: 800 + hi_agi: -20_000 + output: + hi_misc_deduction: 800 + +- name: Case 5, both spouses' expenses count on a joint return. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + unreimbursed_business_employee_expenses: 3_000 + person2: + age: 40 + tax_preparation_fees: 1_000 + tax_units: + tax_unit: + members: [person1, person2] + hi_agi: 100_000 + households: + household: + members: [person1, person2] + state_code: HI + output: + # 4,000 - 2% * 100,000 = 2,000. + hi_misc_deduction: 2_000 + +- name: Case 6, the deduction is part of Hawaii total itemized deductions. + period: 2025 + absolute_error_margin: 0.01 + input: + state_code: HI + unreimbursed_business_employee_expenses: 5_000 + tax_preparation_fees: 500 + hi_agi: 50_000 + hi_salt_deduction: 0 + output: + hi_total_itemized_deductions: 4_500 diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_reduced_itemized_deductions.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_reduced_itemized_deductions.yaml deleted file mode 100644 index 912c803624b..00000000000 --- a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_reduced_itemized_deductions.yaml +++ /dev/null @@ -1,69 +0,0 @@ -- name: base test, separate filers - period: 2022 - input: - people: - person1: - investment_interest_expense: 100 - person2: - investment_interest_expense: 100 - tax_units: - tax_unit: - filing_status: SEPARATE - hi_medical_expense_deduction: 300 - hi_casualty_loss_deduction: 400 - hi_agi: 200_000 - members: [person1, person2] - hi_total_itemized_deductions: 10_000 - households: - household: - members: [person1, person2] - state_code: HI - output: - hi_reduced_itemized_deductions: 6_502 - - -- name: AGI limit not met - period: 2022 - input: - people: - person1: - investment_interest_expense: 100 - person2: - investment_interest_expense: 100 - tax_units: - tax_unit: - filing_status: JOINT - hi_medical_expense_deduction: 300 - hi_casualty_loss_deduction: 400 - hi_agi: 166_779 - members: [person1, person2] - hi_total_itemized_deductions: 10_000 - households: - household: - members: [person1, person2] - state_code: HI - output: - hi_reduced_itemized_deductions: 0 - -- name: Reduced itemized deductions same as total - period: 2022 - input: - people: - person1: - investment_interest_expense: 100 - person2: - investment_interest_expense: 100 - tax_units: - tax_unit: - filing_status: JOINT - hi_medical_expense_deduction: 300 - hi_casualty_loss_deduction: 400 - hi_agi: 166_880 - members: [person1, person2] - hi_total_itemized_deductions: 900 - households: - household: - members: [person1, person2] - state_code: HI - output: - hi_reduced_itemized_deductions: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/hi_taxable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/hi_taxable_income.yaml index 057dce05319..18b820eba7c 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/hi_taxable_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/hi_taxable_income.yaml @@ -17,3 +17,79 @@ state_code: HI output: hi_taxable_income: 0 + +- name: Case 3, tipped worker deducts qualified tips from Hawaii taxable income in 2026. + period: 2026 + input: + people: + person1: + age: 30 + employment_income: 40_000 + tip_income: 8_000 + treasury_tipped_occupation_code: 102 + tax_units: + tax_unit: + members: [person1] + households: + household: + members: [person1] + state_code: HI + output: + hi_agi: 40_000 + hi_tip_income_deduction: 8_000 + # 8,000 standard deduction + 8,000 tips deduction. + hi_deductions: 16_000 + hi_exemptions: 1_144 + # 40,000 - 16,000 - 1,144 = 22,856. + hi_taxable_income: 22_856 + +- name: Case 4, joint itemizers above $166,800 have limited itemized deductions in 2026. + period: 2026 + input: + people: + person1: + age: 40 + person2: + age: 40 + tax_units: + tax_unit: + members: [person1, person2] + hi_agi: 200_000 + hi_total_itemized_deductions: 30_000 + households: + household: + members: [person1, person2] + state_code: HI + output: + # 30,000 - (200,000 - 166,800) * 3% = 29,004. + hi_itemized_deductions: 29_004 + hi_deductions: 29_004 + hi_exemptions: 2_288 + # 200,000 - 29,004 - 2,288 = 168,708. + hi_taxable_income: 168_708 + +- name: Case 5, Hawaii does not adopt the federal senior or overtime deductions in 2026. + period: 2026 + input: + people: + person1: + age: 70 + employment_income: 50_000 + fsla_overtime_premium: 5_000 + tax_units: + tax_unit: + members: [person1] + households: + household: + members: [person1] + state_code: HI + output: + # The federal return gets both deductions. + additional_senior_deduction: 6_000 + overtime_income_deduction: 5_000 + # Act 35 keeps sections 151 and 225 inoperative for Hawaii. + hi_deductions: 8_000 + # Regular plus aged exemption: 2 * 1,144 = 2,288. + hi_exemptions: 2_288 + # 50,000 - 8,000 - 2,288 = 39,712. + hi_taxable_income: 39_712 diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/deductions/itemizing/_charitable_deduction_helpers.py b/policyengine_us/variables/gov/irs/income/taxable_income/deductions/itemizing/_charitable_deduction_helpers.py new file mode 100644 index 00000000000..640bd4ac891 --- /dev/null +++ b/policyengine_us/variables/gov/irs/income/taxable_income/deductions/itemizing/_charitable_deduction_helpers.py @@ -0,0 +1,77 @@ +from policyengine_us.model_api import * + + +def charitable_deduction_given_contribution_base( + tax_unit, period, parameters, contribution_base +): + """Itemized charitable deduction under 26 USC 170(b) for a given contribution base. + + The federal deduction uses positive federal adjusted gross income as the + contribution base. States that measure the section 170(b) percentage + limits and floor against their own adjusted gross income pass that + instead. + """ + # TODO: 26 USC 170(b)(1) defines 4 contribution categories with + # separate AGI caps: + # (G) Cash to 50% limit orgs: 60% AGI (post-TCJA) + # (B) Cash to non-50% limit orgs: 30% AGI + # (C) Capital gain property to 50% limit orgs: 30% AGI + # (or 50% with basis-reduction election) + # (D) Capital gain property to non-50% limit orgs: 20% AGI + # Currently we model two non-cash categories (50% vs 30% cap) + # and treat all cash as subject to the overall cap only. + # The cash split and the 20% category are not yet modeled. + cash_donations = add(tax_unit, period, ["charitable_cash_donations"]) + non_cash_donations = add(tax_unit, period, ["charitable_non_cash_donations"]) + non_cash_to_non_50_pct = add( + tax_unit, + period, + ["charitable_non_cash_donations_non_50_pct_orgs"], + ) + non_cash_to_50_pct = non_cash_donations - non_cash_to_non_50_pct + p = parameters(period).gov.irs.deductions.itemized.charity + + # Non-cash to 50% limit orgs capped at 50% of AGI. + # Note: this models the basis-reduction election under + # 170(b)(1)(C)(iii); the default cap for capital gain + # property is 30% per 170(b)(1)(C)(i). + capped_non_cash_50 = min_( + non_cash_to_50_pct, p.ceiling.non_cash * contribution_base + ) + # Non-cash to non-50% limit orgs capped at 30% of AGI + # per 170(b)(1)(B). Note: the stricter 20% cap for + # capital gain property under 170(b)(1)(D) is not yet + # modeled separately. + capped_non_cash_non_50 = min_( + non_cash_to_non_50_pct, + p.ceiling.non_cash_to_non_50_pct_org * contribution_base, + ) + capped_non_cash = capped_non_cash_50 + capped_non_cash_non_50 + + total_cap = p.ceiling.all * contribution_base + if p.floor.applies: + deduction_floor = p.floor.amount * contribution_base + reduced_non_cash_50 = max_(non_cash_to_50_pct - deduction_floor, 0) + capped_reduced_non_cash_50 = min_( + reduced_non_cash_50, + p.ceiling.non_cash * contribution_base, + ) + + remaining_floor_after_50 = max_(deduction_floor - non_cash_to_50_pct, 0) + reduced_non_cash_non_50 = max_( + non_cash_to_non_50_pct - remaining_floor_after_50, 0 + ) + capped_reduced_non_cash_non_50 = min_( + reduced_non_cash_non_50, + p.ceiling.non_cash_to_non_50_pct_org * contribution_base, + ) + + remaining_floor = max_(remaining_floor_after_50 - non_cash_to_non_50_pct, 0) + reduced_cash_donations = max_(cash_donations - remaining_floor, 0) + return min_( + capped_reduced_non_cash_50 + + capped_reduced_non_cash_non_50 + + reduced_cash_donations, + total_cap, + ) + return min_(capped_non_cash + cash_donations, total_cap) diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/deductions/itemizing/charitable_deduction.py b/policyengine_us/variables/gov/irs/income/taxable_income/deductions/itemizing/charitable_deduction.py index 96b9285f230..5ea445bb17c 100644 --- a/policyengine_us/variables/gov/irs/income/taxable_income/deductions/itemizing/charitable_deduction.py +++ b/policyengine_us/variables/gov/irs/income/taxable_income/deductions/itemizing/charitable_deduction.py @@ -1,4 +1,7 @@ from policyengine_us.model_api import * +from policyengine_us.variables.gov.irs.income.taxable_income.deductions.itemizing._charitable_deduction_helpers import ( + charitable_deduction_given_contribution_base, +) class charitable_deduction(Variable): @@ -11,66 +14,7 @@ class charitable_deduction(Variable): reference = "https://www.law.cornell.edu/uscode/text/26/170" def formula(tax_unit, period, parameters): - # TODO: 26 USC 170(b)(1) defines 4 contribution categories with - # separate AGI caps: - # (G) Cash to 50% limit orgs: 60% AGI (post-TCJA) - # (B) Cash to non-50% limit orgs: 30% AGI - # (C) Capital gain property to 50% limit orgs: 30% AGI - # (or 50% with basis-reduction election) - # (D) Capital gain property to non-50% limit orgs: 20% AGI - # Currently we model two non-cash categories (50% vs 30% cap) - # and treat all cash as subject to the overall cap only. - # The cash split and the 20% category are not yet modeled. - cash_donations = add(tax_unit, period, ["charitable_cash_donations"]) - non_cash_donations = add(tax_unit, period, ["charitable_non_cash_donations"]) - non_cash_to_non_50_pct = add( - tax_unit, - period, - ["charitable_non_cash_donations_non_50_pct_orgs"], - ) - non_cash_to_50_pct = non_cash_donations - non_cash_to_non_50_pct positive_agi = tax_unit("positive_agi", period) - p = parameters(period).gov.irs.deductions.itemized.charity - - # Non-cash to 50% limit orgs capped at 50% of AGI. - # Note: this models the basis-reduction election under - # 170(b)(1)(C)(iii); the default cap for capital gain - # property is 30% per 170(b)(1)(C)(i). - capped_non_cash_50 = min_(non_cash_to_50_pct, p.ceiling.non_cash * positive_agi) - # Non-cash to non-50% limit orgs capped at 30% of AGI - # per 170(b)(1)(B). Note: the stricter 20% cap for - # capital gain property under 170(b)(1)(D) is not yet - # modeled separately. - capped_non_cash_non_50 = min_( - non_cash_to_non_50_pct, - p.ceiling.non_cash_to_non_50_pct_org * positive_agi, + return charitable_deduction_given_contribution_base( + tax_unit, period, parameters, positive_agi ) - capped_non_cash = capped_non_cash_50 + capped_non_cash_non_50 - - total_cap = p.ceiling.all * positive_agi - if p.floor.applies: - deduction_floor = p.floor.amount * positive_agi - reduced_non_cash_50 = max_(non_cash_to_50_pct - deduction_floor, 0) - capped_reduced_non_cash_50 = min_( - reduced_non_cash_50, - p.ceiling.non_cash * positive_agi, - ) - - remaining_floor_after_50 = max_(deduction_floor - non_cash_to_50_pct, 0) - reduced_non_cash_non_50 = max_( - non_cash_to_non_50_pct - remaining_floor_after_50, 0 - ) - capped_reduced_non_cash_non_50 = min_( - reduced_non_cash_non_50, - p.ceiling.non_cash_to_non_50_pct_org * positive_agi, - ) - - remaining_floor = max_(remaining_floor_after_50 - non_cash_to_non_50_pct, 0) - reduced_cash_donations = max_(cash_donations - remaining_floor, 0) - return min_( - capped_reduced_non_cash_50 - + capped_reduced_non_cash_non_50 - + reduced_cash_donations, - total_cap, - ) - return min_(capped_non_cash + cash_donations, total_cap) diff --git a/policyengine_us/variables/gov/states/hi/tax/income/deductions/hi_deductions.py b/policyengine_us/variables/gov/states/hi/tax/income/deductions/hi_deductions.py index 5e84412ac6c..52bedf8d748 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/deductions/hi_deductions.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/deductions/hi_deductions.py @@ -10,11 +10,17 @@ class hi_deductions(Variable): "https://files.hawaii.gov/tax/forms/2022/n11ins.pdf#page=15\n" # Itemized Deduction "https://files.hawaii.gov/tax/forms/2022/n11ins.pdf#page=20" # Standard Deduction ) + reference = ( + # Act 35, SLH 2026 conforms to sections 170(p) and 224 from 2026. + "https://data.capitol.hawaii.gov/sessions/session2026/bills/HB2329_CD1_.pdf#page=1", + "https://files.hawaii.gov/tax/news/announce/ann26-06.pdf#page=2", + ) definition_period = YEAR defined_for = StateCode.HI def formula(tax_unit, period, parameters): - p = parameters(period).gov.states.hi.tax.income.deductions.itemized + p_deductions = parameters(period).gov.states.hi.tax.income.deductions + p = p_deductions.itemized standard_deduction = tax_unit("hi_standard_deduction", period) total_itemized_deduction = tax_unit("hi_itemized_deductions", period) @@ -35,4 +41,20 @@ def formula(tax_unit, period, parameters): itemized_deduction = where( itemized_deductions_eligible, total_itemized_deduction, 0 ) - return max_(itemized_deduction, standard_deduction) + # Section 170(p) lets taxpayers who do not itemize deduct some cash + # charitable contributions on top of the standard deduction. + non_itemizer_charitable_deduction = ( + tax_unit("charitable_deduction_for_non_itemizers", period) + if p_deductions.non_itemizer_charitable.in_effect + else 0 + ) + # Section 224 allows the qualified tips deduction whether or not the + # taxpayer itemizes. + tip_income_deduction = tax_unit("hi_tip_income_deduction", period) + return ( + max_( + itemized_deduction, + standard_deduction + non_itemizer_charitable_deduction, + ) + + tip_income_deduction + ) diff --git a/policyengine_us/variables/gov/states/hi/tax/income/deductions/hi_tip_income_deduction.py b/policyengine_us/variables/gov/states/hi/tax/income/deductions/hi_tip_income_deduction.py new file mode 100644 index 00000000000..902af35be78 --- /dev/null +++ b/policyengine_us/variables/gov/states/hi/tax/income/deductions/hi_tip_income_deduction.py @@ -0,0 +1,45 @@ +from policyengine_us.model_api import * + + +class hi_tip_income_deduction(Variable): + value_type = float + entity = TaxUnit + label = "Hawaii qualified tips deduction" + unit = USD + definition_period = YEAR + defined_for = StateCode.HI + reference = ( + "https://data.capitol.hawaii.gov/sessions/session2026/bills/HB2329_CD1_.pdf#page=1", + "https://files.hawaii.gov/tax/news/announce/ann26-06.pdf#page=3", + "https://www.law.cornell.edu/uscode/text/26/224", + ) + + def formula(tax_unit, period, parameters): + if not parameters( + period + ).gov.states.hi.tax.income.deductions.tip_income.in_effect: + return 0 + p = parameters(period).gov.irs.deductions.tip_income + person = tax_unit.members + tip_income = person("tip_income", period) + occupation_requirement_met = person( + "tip_income_deduction_occupation_requirement_met", period + ) + qualified_tips = tax_unit.sum(tip_income * occupation_requirement_met) + capped_tips = min_(p.cap, qualified_tips) + # Section 224(b)(2) phases the deduction out with modified adjusted + # gross income. HRS 235-1 defines adjusted gross income as determined + # under the Internal Revenue Code as modified by chapter 235, so the + # phase-out uses Hawaii adjusted gross income. + hi_agi = tax_unit("hi_agi", period) + filing_status = tax_unit("filing_status", period) + agi_excess = max_(0, hi_agi - p.phase_out.start[filing_status]) + phase_out_amount = agi_excess * p.phase_out.rate + deduction = max_(0, capped_tips - phase_out_amount) + # Section 224(e) requires a social security number, and section 224(f) + # requires married individuals to file a joint return. + ssn_requirement_met = tax_unit( + "tip_income_deduction_ssn_requirement_met", period + ) + separate = filing_status == filing_status.possible_values.SEPARATE + return where(ssn_requirement_met & ~separate, deduction, 0) diff --git a/policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_charitable_deduction.py b/policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_charitable_deduction.py new file mode 100644 index 00000000000..6df5025408f --- /dev/null +++ b/policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_charitable_deduction.py @@ -0,0 +1,29 @@ +from policyengine_us.model_api import * +from policyengine_us.variables.gov.irs.income.taxable_income.deductions.itemizing._charitable_deduction_helpers import ( + charitable_deduction_given_contribution_base, +) + + +class hi_charitable_deduction(Variable): + value_type = float + entity = TaxUnit + label = "Hawaii charitable deduction" + unit = USD + definition_period = YEAR + defined_for = StateCode.HI + reference = ( + # Line 21d and the limits stated as a share of Hawaii AGI. + "https://files.hawaii.gov/tax/forms/2025/n11ins.pdf#page=17", + "https://files.hawaii.gov/tax/forms/2025/n11ins.pdf#page=18", + # Act 35, SLH 2026 conforms to the section 170(b)(1)(I) floor. + "https://files.hawaii.gov/tax/news/announce/ann26-06.pdf#page=2", + ) + + def formula(tax_unit, period, parameters): + # The N-11 instructions measure the section 170(b) limits against + # Hawaii adjusted gross income, so Hawaii AGI is the contribution + # base for the ceilings and for the 0.5% floor from 2026. + hi_agi = tax_unit("hi_agi", period) + return charitable_deduction_given_contribution_base( + tax_unit, period, parameters, max_(hi_agi, 0) + ) diff --git a/policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_itemized_deductions.py b/policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_itemized_deductions.py index 2358892da1a..566bd073d05 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_itemized_deductions.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_itemized_deductions.py @@ -6,27 +6,15 @@ class hi_itemized_deductions(Variable): entity = TaxUnit label = "Hawaii itemized deduction" unit = USD - documentation = ( - "https://files.hawaii.gov/tax/forms/2022/n11ins.pdf#page=15\n" - "https://files.hawaii.gov/tax/forms/2022/n11ins.pdf#page=32" # total itemized deduction worksheet - ) definition_period = YEAR defined_for = StateCode.HI + reference = ( + "https://files.hawaii.gov/tax/forms/2025/n11ins.pdf#page=20", + # Total Itemized Deductions Worksheet, line 11. + "https://files.hawaii.gov/tax/forms/2025/n11ins.pdf#page=34", + ) def formula(tax_unit, period, parameters): - # Hawaii applies an federal AGI limit which has been introduced in 2009 - p = parameters(f"2009-01-01").gov.irs.deductions.itemized.limitation - - # Hawaii did not suspend the overall limitation on itemized deductions - # You may not be able to deduct all of your itemized deductions if agi reach the cap - # need to calculate the reduced itemized deductions - hi_agi = tax_unit("hi_agi", period) - filing_status = tax_unit("filing_status", period) - total_itemized_deduction_eligibility = ( - hi_agi < p.applicable_amount[filing_status] - ) - return where( - total_itemized_deduction_eligibility, - tax_unit("hi_total_itemized_deductions", period), - tax_unit("hi_reduced_itemized_deductions", period), - ) + total_deductions = tax_unit("hi_total_itemized_deductions", period) + reduction = tax_unit("hi_itemized_deductions_reduction", period) + return max_(0, total_deductions - reduction) diff --git a/policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_itemized_deductions_reduction.py b/policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_itemized_deductions_reduction.py new file mode 100644 index 00000000000..6c503d3bace --- /dev/null +++ b/policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_itemized_deductions_reduction.py @@ -0,0 +1,39 @@ +from policyengine_us.model_api import * + + +class hi_itemized_deductions_reduction(Variable): + value_type = float + entity = TaxUnit + label = "Hawaii itemized deductions reduction" + unit = USD + definition_period = YEAR + defined_for = StateCode.HI + reference = ( + # HRS 235-2.4 keeps IRC section 68 operative with the 2009 thresholds; + # Act 35, SLH 2026 keeps it "in the form that it existed as of + # December 31, 2024" for taxable years beginning after 2025. + "https://data.capitol.hawaii.gov/sessions/session2026/bills/HB2329_CD1_.pdf#page=18", + "https://www.govinfo.gov/content/pkg/USCODE-2024-title26/html/USCODE-2024-title26-subtitleA-chap1-subchapB-partI-sec68.htm", + # Total Itemized Deductions Worksheet, lines 2 to 10. + "https://files.hawaii.gov/tax/forms/2025/n11ins.pdf#page=34", + ) + + def formula(tax_unit, period, parameters): + p = parameters(period).gov.states.hi.tax.income.deductions.itemized + total_deductions = tax_unit("hi_total_itemized_deductions", period) + # Lines 2 to 5: 80% of the deductions other than medical expenses, + # investment interest and casualty losses. If those deductions are not + # less than the total, the deduction is not limited. + excluded_deductions = add(tax_unit, period, p.limitation.excluded_deductions) + limited_deductions = max_(0, total_deductions - excluded_deductions) + deduction_based_reduction = ( + limited_deductions * p.limitation.itemized_deduction_rate + ) + # Lines 6 to 9: 3% of Hawaii AGI above the threshold. If Hawaii AGI is + # not above the threshold, the deduction is not limited. + hi_agi = tax_unit("hi_agi", period) + filing_status = tax_unit("filing_status", period) + agi_excess = max_(0, hi_agi - p.threshold.reduction[filing_status]) + agi_based_reduction = agi_excess * p.limitation.agi_rate + # Line 10: the smaller of the two. + return min_(deduction_based_reduction, agi_based_reduction) diff --git a/policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_misc_deduction.py b/policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_misc_deduction.py new file mode 100644 index 00000000000..c7c67665d7a --- /dev/null +++ b/policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_misc_deduction.py @@ -0,0 +1,26 @@ +from policyengine_us.model_api import * + + +class hi_misc_deduction(Variable): + value_type = float + entity = TaxUnit + label = "Hawaii miscellaneous itemized deduction" + unit = USD + definition_period = YEAR + defined_for = StateCode.HI + reference = ( + # Worksheet A-6, lines 23 to 29. + "https://files.hawaii.gov/tax/forms/2025/n11ins.pdf#page=34", + "https://data.capitol.hawaii.gov/sessions/session2026/bills/HB2329_CD1_.pdf#page=17", + ) + + def formula(tax_unit, period, parameters): + p = parameters(period).gov.states.hi.tax.income.deductions.itemized.misc + if not p.in_effect: + return 0 + # Hawaii applies the section 67(a) floor to Hawaii AGI. + floor_rate = parameters(period).gov.irs.deductions.itemized.misc.floor + expenses = tax_unit("total_misc_deductions", period) + hi_agi = tax_unit("hi_agi", period) + floor = max_(0, hi_agi * floor_rate) + return max_(0, expenses - floor) diff --git a/policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_reduced_itemized_deductions.py b/policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_reduced_itemized_deductions.py deleted file mode 100644 index c68fabcfe61..00000000000 --- a/policyengine_us/variables/gov/states/hi/tax/income/deductions/itemized/hi_reduced_itemized_deductions.py +++ /dev/null @@ -1,56 +0,0 @@ -from policyengine_us.model_api import * - - -class hi_reduced_itemized_deductions(Variable): - value_type = float - entity = TaxUnit - label = "Hawaii reduced itemized deduction" - unit = USD - documentation = ( - "https://files.hawaii.gov/tax/forms/2022/n11ins.pdf#page=15\n" - "https://files.hawaii.gov/tax/forms/2022/n11ins.pdf#page=32" # total itemized deduction worksheet - ) - definition_period = YEAR - defined_for = StateCode.HI - - # If the state AGI of the filer exceeds a certain amount, only partial itemized deductions - # can be deducted. - def formula(tax_unit, period, parameters): - total_deductions = tax_unit("hi_total_itemized_deductions", period) - partial_deductions = add( - tax_unit, - period, - [ - "hi_medical_expense_deduction", - "investment_interest_expense", - "hi_casualty_loss_deduction", - ], - ) - - # eligible check 1: deduction_difference need to be greater than 0 to have reduced deduction - partial_deductions_less_than_total = partial_deductions < total_deductions - total_less_partial_ded_amount = total_deductions - partial_deductions - # Take a percentage of the difference between the total and partial deductions - # Hawaii applies federal limits which have been revoked in 2018 - p_2017 = parameters(f"2017-01-01").gov.irs.deductions.itemized.limitation - total_less_partial_ded_percentage = ( - total_less_partial_ded_amount * p_2017.itemized_deduction_rate - ) - # eligible check 2: actual AGI need to be smaller than AGI cap - hi_agi = tax_unit("hi_agi", period) - filing_status = tax_unit("filing_status", period) - # Hawaii applies an federal AGI limit as of 2009. - p_hi = parameters(period).gov.states.hi.tax.income.deductions.itemized.threshold - agi_threshold = p_hi.reduction[filing_status] - agi_over_threshold = agi_threshold < hi_agi - # If the AGI is over a threshold, the AGI amount is reduced by the threshold and multiplied - # by a rate - reduced_agi = hi_agi - agi_threshold - reduced_agi_percentage = reduced_agi * p_2017.agi_rate - - smaller_reduced_ded = min_( - total_less_partial_ded_percentage, reduced_agi_percentage - ) - reduced_deductions = max_(0, total_deductions - smaller_reduced_ded) - eligible = partial_deductions_less_than_total & agi_over_threshold - return eligible * reduced_deductions