diff --git a/changelog.d/head-dependent-elsewhere-swap.fixed.md b/changelog.d/head-dependent-elsewhere-swap.fixed.md new file mode 100644 index 00000000000..5222c01b901 --- /dev/null +++ b/changelog.d/head-dependent-elsewhere-swap.fixed.md @@ -0,0 +1 @@ +The federal standard deduction and the New York, Michigan, Oregon, Virginia, Missouri, New Mexico, Maine and Hawaii rules for a filer claimed as a dependent elsewhere no longer depend on which spouse is labelled head; each follows its jurisdiction's rule for a joint return where one spouse can be claimed. New York's $3,100 dependent standard deduction now applies only to single filers; Hawaii now limits the standard deduction of a filer who can be claimed as a dependent; the federal EITC without a qualifying child now requires that no filer can be claimed as a dependent; and New Mexico's medical care credit requires a filer who is both 65 or older and not a dependent when a filer is a dependent elsewhere. Separate actual dependency claims from claimability and the nonrequired claimant filing exception, preserving dependent deduction limits while restoring EITC and lawful own-dependent counts. Exclude externally claimable children from Hawaii food/excise ordinary exemptions. diff --git a/policyengine_us/parameters/gov/states/hi/tax/income/deductions/itemized/threshold/dependent.yaml b/policyengine_us/parameters/gov/states/hi/tax/income/deductions/itemized/threshold/dependent.yaml index 0a894e4a148..36300fde6de 100644 --- a/policyengine_us/parameters/gov/states/hi/tax/income/deductions/itemized/threshold/dependent.yaml +++ b/policyengine_us/parameters/gov/states/hi/tax/income/deductions/itemized/threshold/dependent.yaml @@ -1,4 +1,4 @@ -description: Hawaii allows for itemized deductions for dependent filers with deductions above this amount. +description: Hawaii limits the standard deduction of a filer who can be claimed as a dependent to the greater of this amount or earned income, and lets such a filer itemize deductions above that limit. values: 2018-01-01: 500 metadata: @@ -16,6 +16,6 @@ metadata: - title: 2018 Hawaii Resident Income Tax Instructions Itemized Deductions Worksheet href: https://files.hawaii.gov/tax/forms/2018/n11ins.pdf#page=38 - title: Hawaii Income Tax Law, Chapter 235, Section 235-2.4, (a)(3) - href: https://files.hawaii.gov/tax/legal/hrs/hrs_235.pdf#page=10 + 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 diff --git a/policyengine_us/parameters/gov/states/mi/tax/income/exemptions/dependent_on_other_return.yaml b/policyengine_us/parameters/gov/states/mi/tax/income/exemptions/dependent_on_other_return.yaml index 44fbabc57c8..46ca0dabbff 100644 --- a/policyengine_us/parameters/gov/states/mi/tax/income/exemptions/dependent_on_other_return.yaml +++ b/policyengine_us/parameters/gov/states/mi/tax/income/exemptions/dependent_on_other_return.yaml @@ -1,21 +1,10 @@ -description: Michigan provides this exemption amount for a taxpayer who can be claimed as a dependent on someone else’s return. -SINGLE: - 2021-01-01: 1_500 -JOINT: - 2021-01-01: 3_000 -HEAD_OF_HOUSEHOLD: - 2021-01-01: 1_500 -SURVIVING_SPOUSE: - 2021-01-01: 1_500 -SEPARATE: +description: Michigan provides this exemption amount to each individual who can be claimed as a dependent on another taxpayer's return, in place of that individual's personal exemption. +values: 2021-01-01: 1_500 metadata: - propagate_metadata_to_children: true unit: currency-USD period: year - breakdown: - - filing_status - label: Michigan dependent on other return exemption amount + label: Michigan exemption amount per individual claimable as a dependent elsewhere reference: - title: Michigan Legal Code Section 206.30 - (4) href: https://www.legislature.mi.gov/Laws/MCL?objectName=mcl-206-30 diff --git a/policyengine_us/parameters/gov/states/ny/tax/income/deductions/standard/dependent_elsewhere.yaml b/policyengine_us/parameters/gov/states/ny/tax/income/deductions/standard/dependent_elsewhere.yaml index 4744cadf89b..efe6e74704b 100644 --- a/policyengine_us/parameters/gov/states/ny/tax/income/deductions/standard/dependent_elsewhere.yaml +++ b/policyengine_us/parameters/gov/states/ny/tax/income/deductions/standard/dependent_elsewhere.yaml @@ -1,10 +1,10 @@ -description: New York standard deduction for filers who can be claimed as a dependent elsewhere. +description: New York provides this standard deduction to single filers who can be claimed as a dependent on another taxpayer's federal return. values: 2020-01-01: 3_100 metadata: unit: currency-USD period: year - label: New York standard deduction for dependent filers + label: New York standard deduction for single dependent filers reference: - title: 2021 NY Form IT-201 Instructions Line 34 href: https://www.tax.ny.gov/pdf/2021/inc/it201i_2021.pdf#page=19 diff --git a/policyengine_us/reforms/congress/watca/working_americans_tax_cut_act.py b/policyengine_us/reforms/congress/watca/working_americans_tax_cut_act.py index 860462a6ed5..27becb3a73f 100644 --- a/policyengine_us/reforms/congress/watca/working_americans_tax_cut_act.py +++ b/policyengine_us/reforms/congress/watca/working_americans_tax_cut_act.py @@ -52,7 +52,11 @@ class watca_alternative_tax_eligible(Variable): label = "Eligible for WATCA alternative maximum tax" definition_period = YEAR reference = WATCA_REFERENCES - documentation = "Section 1A(b): MAGI < 175% of exemption, and not claimed as dependent on another return." + documentation = ( + "Section 1A(b): MAGI < 175% of exemption. Retains the reform's " + "modeled actual-dependent-claim restriction, which is not stated " + "in the cited bill's section 1A(b)(2)." + ) def formula(tax_unit, period, parameters): magi = tax_unit("watca_alternative_tax_magi", period) @@ -61,9 +65,10 @@ def formula(tax_unit, period, parameters): exemption_amount = p.amount[filing_status] income_limit = exemption_amount * p.income_limit_multiple income_eligible = magi < income_limit - head_dependent = tax_unit("head_is_dependent_elsewhere", period) - spouse_dependent = tax_unit("spouse_is_dependent_elsewhere", period) - not_dependent = ~head_dependent & ~spouse_dependent + person = tax_unit.members + claimed = person("claimed_as_dependent_on_another_return", period) + filer = person("is_tax_unit_head_or_spouse", period) + not_dependent = ~tax_unit.any(claimed & filer) return income_eligible & not_dependent class watca_alternative_max_tax(Variable): diff --git a/policyengine_us/reforms/state_dependent_exemptions/repeal_state_dependent_exemptions.py b/policyengine_us/reforms/state_dependent_exemptions/repeal_state_dependent_exemptions.py index 55765f89e4b..d05820bfb6b 100644 --- a/policyengine_us/reforms/state_dependent_exemptions/repeal_state_dependent_exemptions.py +++ b/policyengine_us/reforms/state_dependent_exemptions/repeal_state_dependent_exemptions.py @@ -93,7 +93,7 @@ def formula(person, period, parameters): # if they are not claimed as a dependent elsewhere head_or_spouse = person("is_tax_unit_head_or_spouse", period) dependent_on_another_return = person( - "claimed_as_dependent_on_another_return", period + "claimable_as_dependent_on_another_return", period ) # The personal exemption is also provided to dependents return ~dependent_on_another_return & head_or_spouse diff --git a/policyengine_us/reforms/states/ct/hb5114/ct_hb5114.py b/policyengine_us/reforms/states/ct/hb5114/ct_hb5114.py index 91d44eb6f65..c95f0f8a032 100644 --- a/policyengine_us/reforms/states/ct/hb5114/ct_hb5114.py +++ b/policyengine_us/reforms/states/ct/hb5114/ct_hb5114.py @@ -19,11 +19,12 @@ def formula(tax_unit, period, parameters): rent = add(tax_unit, period, ["rent"]) pays_rent = rent > 0 - # Must not be claimed as a dependent on another return - head_is_dependent_elsewhere = tax_unit( - "head_is_dependent_elsewhere", period - ) - not_dependent = ~head_is_dependent_elsewhere + # Preserve the modeled actual-claim gate; the cited bill does + # not state this restriction. + person = tax_unit.members + claimed = person("claimed_as_dependent_on_another_return", period) + head = person("is_tax_unit_head", period) + not_dependent = ~tax_unit.any(claimed & head) # Income eligibility filing_status = tax_unit("filing_status", period) diff --git a/policyengine_us/tests/core/test_dependent_elsewhere_head_spouse_swap_invariance.py b/policyengine_us/tests/core/test_dependent_elsewhere_head_spouse_swap_invariance.py new file mode 100644 index 00000000000..4d5f97ae469 --- /dev/null +++ b/policyengine_us/tests/core/test_dependent_elsewhere_head_spouse_swap_invariance.py @@ -0,0 +1,555 @@ +"""Rules for a filer claimed as a dependent elsewhere do not depend on which +spouse is labelled head. + +A married couple may list either spouse first, so exchanging the two adults' +`is_tax_unit_head` and `is_tax_unit_spouse` labels, with every other input +kept, must not change their taxes. On main, the federal standard deduction and +16 state formulas read only `head_is_dependent_elsewhere`, so a joint return +where one spouse can be claimed on another return (a joint return filed only +to recover withholding can meet the federal exception, IRS Publication 501) +was taxed differently depending on which spouse was labelled head. Each +formula now applies its jurisdiction's rule for that case: + +- Either spouse limits the return (the IRC 63(c)(5) worksheet): the federal + standard deduction, Oregon's and Hawaii's standard deductions, and + Virginia's credit for low-income individuals. The federal EITC without a + qualifying child also requires that neither spouse is claimable outside the + nonrequired-claimant filing exception. +- Only single filers: New York's dependent standard deduction. +- Each spouse separately: Michigan's exemptions and Hawaii's food/excise, + renters and Act 115 credits count only the filers who cannot be claimed. +- Only when every filer is a dependent: New Mexico's rebates, credits and + dependents deduction, and Maine's sales tax fairness credit. When a filer + is a dependent elsewhere, New Mexico's medical care credit also needs a + filer who is both 65 or older and not a dependent. +- Never on a joint return: Missouri's own stop in its working family credit, + since such a couple files as married filing combined. + +Hypothesis draws batches of married couples, with and without dependents, in +the eight states with such a rule and one without, for 2021-2026, with either, +both or neither spouse claimed or claimable elsewhere, independently of the +nonrequired-claimant filing exception, and checks 1 and 3. A seeded +population and crafted cases check 1, 2 and 3 in every year, and two seeded +couples plus the review case in every state check 1 for federal and state +income tax in 2026. Each batch is one vectorized simulation. For each couple: + +1. Swap invariance: federal income tax, state income tax before and after + refundable credits, and each formula in CONSUMERS are the same to the cent + (or the same boolean) under either labelling. +2. Monotonicity: marking another filer as claimed elsewhere never raises any + amount in CONSUMERS or turns an eligibility on. This holds for current + parameters and inputs the model derives; for example, a Hawaii standard + deduction input below the itemization threshold could break it. +3. The helper identities: `head_or_spouse_is_dependent_elsewhere` equals + `head_is_dependent_elsewhere | spouse_is_dependent_elsewhere`, + `every_filer_is_dependent_elsewhere` equals their conjunction for a + couple, and `head_spouse_count_not_dependent_elsewhere` plus the claimable + filers equals the number of filers. + +The 2025 seeded population also compares legacy input only with explicit +claimability equal to that input, protecting the new input's fallback. A +separate vectorized population holds actual claims and claimability fixed +while toggling the exception: general claimability rules and Medicaid stay +unchanged, childless EITC eligibility is restored, and Michigan/Hawaii may +count the filer's own dependents. Benefits derived from the restored federal +EITC may also change: Missouri's working family credit eligibility must track +positive federal EITC for this population's joint returns with no investment +income. Its separate claimability gate stays fixed. YAML examples establish +the corresponding legal amounts, including the limited $2,450 federal +standard deduction. +""" + +import numpy as np +import pytest +from hypothesis import HealthCheck, given, settings +from hypothesis import strategies as st + +from policyengine_us import Simulation +from policyengine_us.model_api import STATES + +TOLERANCE = 0.01 # dollars: the same to the cent +YEARS = [2021, 2022, 2023, 2024, 2025, 2026] +AFFECTED = ["HI", "ME", "MI", "MO", "NM", "NY", "OR", "VA", "CA"] +ALL_STATES = [s for s in STATES if s not in ("PR", "VI")] +TAX_UNIT_OUTPUTS = [ + "income_tax", + "state_income_tax", + "state_income_tax_before_refundable_credits", +] +# The formulas that read a filer's claimability or dependent-taxpayer status. +CONSUMERS = [ + "basic_standard_deduction", + "eitc", + "eitc_demographic_eligible", + "ny_standard_deduction", + "mi_exemptions", + "or_standard_deduction", + "va_low_income_tax_credit_eligible", + "mo_wftc_eligible", + "nm_2021_income_rebate", + "nm_additional_2021_income_rebate", + "nm_supplemental_2021_income_rebate", + "nm_deduction_for_certain_dependents_eligible", + "nm_medical_expense_credit", + "nm_cdcc_eligible", + "me_sales_tax_fairness_credit_eligible", + "hi_deductions", + "hi_act_115_rebate", + "hi_food_excise_exemption_amount", + "hi_food_excise_credit_minor_child_count", + "hi_food_excise_credit", + "hi_tax_credit_for_low_income_household_renters_eligible", + "hi_tax_credit_for_low_income_household_renters", +] +CLAIM_PATTERNS = [(True, False), (False, True), (True, True), (False, False)] +DEPENDENT_TAXPAYER_TEST_OUTPUTS = { + "mi_exemptions", + "hi_act_115_rebate", + "hi_food_excise_exemption_amount", + "hi_food_excise_credit", + "hi_tax_credit_for_low_income_household_renters", +} +# Federal EITC eligibility is restored directly. The federal amount and +# Missouri eligibility can change as a consequence, without changing their +# general claimability gates. +EITC_RELATED_OUTPUTS = { + "eitc_demographic_eligible", + "eitc", + "mo_wftc_eligible", +} +EXCEPTION_SENSITIVE = DEPENDENT_TAXPAYER_TEST_OUTPUTS | EITC_RELATED_OUTPUTS +ACTUAL_CLAIM_OUTPUTS = [ + "medicaid_is_tax_dependent", + "medicaid_uses_missing_claimant_fallback", +] + + +def money(high): + return st.one_of(st.just(0.0), st.integers(1, high).map(float)) + + +@st.composite +def adults(draw): + # Low incomes reach the credits' limits; ages reach the 65+ rules. + return { + "age": draw(st.integers(18, 90)), + "is_blind": draw(st.booleans()), + "is_disabled": draw(st.booleans()), + "employment_income": draw(money(60_000)), + "self_employment_income": draw(money(20_000)), + "taxable_interest_income": draw(money(3_000)), + "social_security_retirement": draw(money(25_000)), + "real_estate_taxes": draw(money(6_000)), + "rent": draw(money(18_000)), + "charitable_cash_donations": draw(money(6_000)), + "other_medical_expenses": draw(money(40_000)), + } + + +@st.composite +def dependents(draw): + age = draw(st.integers(0, 23)) + return { + "age": age, + "is_full_time_student": age >= 18, + "employment_income": draw(money(10_000)), + "pre_subsidy_childcare_expenses": draw(money(8_000)), + } + + +@st.composite +def couples(draw): + claimable = draw(st.sampled_from(CLAIM_PATTERNS)) + claimed = tuple(can_claim and draw(st.booleans()) for can_claim in claimable) + return { + "state": draw(st.sampled_from(AFFECTED)), + "adults": [draw(adults()), draw(adults())], + "dependents": draw(st.lists(dependents(), max_size=2)), + "claimed": claimed, + "claimable": claimable, + "exception": tuple( + not is_claimed and draw(st.booleans()) for is_claimed in claimed + ), + } + + +SEED = 20261008 + + +def _seeded_adult(rng): + def some(high, p): + return float(round(rng.uniform(1, high))) if rng.random() < p else 0.0 + + age = int(rng.integers(18, 91)) + return { + "age": age, + "is_blind": bool(rng.random() < 0.1), + "is_disabled": bool(rng.random() < 0.1), + "employment_income": some(60_000, 0.7), + "self_employment_income": some(20_000, 0.15), + "taxable_interest_income": some(3_000, 0.3), + "social_security_retirement": some(25_000, 0.5 if age >= 62 else 0.03), + "real_estate_taxes": some(6_000, 0.3), + "rent": some(18_000, 0.5), + "charitable_cash_donations": some(6_000, 0.3), + "other_medical_expenses": some(40_000, 0.15), + } + + +def _seeded_couples(states, per_state): + rng = np.random.default_rng(SEED) + return [ + { + "state": state, + "adults": [_seeded_adult(rng), _seeded_adult(rng)], + "dependents": [ + {"age": int(rng.integers(0, 18))} + for _ in range(int(rng.choice([0, 0, 1, 2]))) + ], + "claimed": CLAIM_PATTERNS[int(rng.integers(0, 3))], + } + for state in states + for _ in range(per_state) + ] + + +def _crafted_cases(states, review_case_only=False): + # The review case (married adults aged 20 earning $8,000 each, one + # claimed by a parent), with and without a child, and older couples who + # reach the 65+ and renter rules. + young = {"age": 20, "employment_income": 8_000.0, "rent": 6_000.0} + old = { + "age": 70, + "social_security_retirement": 12_000.0, + "rent": 9_000.0, + "other_medical_expenses": 30_000.0, + } + # One claimed adult covers both labellings of a claimed spouse, since each + # case runs with the labels exchanged. + cases = [] + for state in states: + for claimed in [(True, False), (True, True)]: + cases += [ + { + "state": state, + "adults": [young, young], + "dependents": [], + "claimed": claimed, + }, + ] + if review_case_only: + continue + cases += [ + { + "state": state, + "adults": [young, {"age": 22, "employment_income": 3_000.0}], + "dependents": [{"age": 2}], + "claimed": claimed, + }, + { + "state": state, + "adults": [old, {**old, "age": 66}], + "dependents": [], + "claimed": claimed, + }, + ] + return cases + + +def _situation(units, year, variants, explicit_claimability=False): + # Each unit appears once per variant: (head is the first adult, first + # adult claimed, second adult claimed). People keep their order and every + # other input in every copy. + people = {} + groups = { + "tax_units": {}, + "spm_units": {}, + "families": {}, + "marital_units": {}, + "households": {}, + } + for i, unit in enumerate(units): + for copy, variant in enumerate(variants(unit)): + first_is_head, claim_a, claim_b = variant[:3] + claimable = unit.get("claimable") + exception = unit.get("exception") + if len(variant) == 5: + claimable, exception = variant[3:] + if explicit_claimability: + claimable = (claim_a, claim_b) + a, b = f"a_{i}_{copy}", f"b_{i}_{copy}" + for adult_index, (name, amounts, is_head, claimed) in enumerate( + ( + (a, unit["adults"][0], first_is_head, claim_a), + (b, unit["adults"][1], not first_is_head, claim_b), + ) + ): + people[name] = { + **amounts, + "is_tax_unit_head": is_head, + "is_tax_unit_spouse": not is_head, + "is_tax_unit_dependent": False, + "claimed_as_dependent_on_another_return": claimed, + } + if claimable is not None: + people[name]["claimable_as_dependent_on_another_return"] = ( + claimable[adult_index] + ) + if exception is not None: + people[name]["dependent_claimant_filing_exception"] = exception[ + adult_index + ] + members = [a, b] + groups["marital_units"][f"couple_{i}_{copy}"] = {"members": [a, b]} + for j, amounts in enumerate(unit["dependents"]): + child = f"dependent_{i}_{copy}_{j}" + people[child] = { + **amounts, + "is_tax_unit_head": False, + "is_tax_unit_spouse": False, + "is_tax_unit_dependent": True, + } + if explicit_claimability: + people[child]["claimable_as_dependent_on_another_return"] = ( + amounts.get("claimed_as_dependent_on_another_return", False) + ) + members.append(child) + groups["marital_units"][f"single_{i}_{copy}_{j}"] = {"members": [child]} + for group, prefix in ( + ("tax_units", "tax_unit"), + ("spm_units", "spm_unit"), + ("families", "family"), + ): + groups[group][f"{prefix}_{i}_{copy}"] = {"members": members} + groups["households"][f"household_{i}_{copy}"] = { + "members": members, + "state_code": {year: unit["state"]}, + } + people = { + name: {k: {year: v} for k, v in values.items()} + for name, values in people.items() + } + return {"people": people, **groups} + + +def _swap_variants(unit): + claim_a, claim_b = unit["claimed"] + return [(True, claim_a, claim_b), (False, claim_a, claim_b)] + + +def _calc(sim, name, year): + return np.asarray(sim.calculate(name, year), dtype=float) + + +def _check_swap(units, year, outputs, capture_fallback=False): + sim = Simulation(situation=_situation(units, year, _swap_variants)) + states = np.array([u["state"] for u in units]) + for name in outputs: + values = _calc(sim, name, year) + assert np.isfinite(values).all(), f"{name} in {year} is not finite" + drawn, swapped = values[0::2], values[1::2] + differs = ~(np.abs(swapped - drawn) <= TOLERANCE) + assert not differs.any(), ( + f"{name} in {year} changes when the head and spouse labels are " + f"exchanged: " + + ", ".join( + f"{states[i]} claimed={units[i]['claimed']} " + f"{drawn[i]:.2f} -> {swapped[i]:.2f}" + for i in np.flatnonzero(differs)[:10] + ) + ) + # 3. The helper identities. + either = _calc(sim, "head_or_spouse_is_dependent_elsewhere", year) + head = _calc(sim, "head_is_dependent_elsewhere", year) + spouse = _calc(sim, "spouse_is_dependent_elsewhere", year) + np.testing.assert_array_equal(either, np.maximum(head, spouse)) + independent = _calc(sim, "head_spouse_count_not_dependent_elsewhere", year) + np.testing.assert_array_equal(independent + head + spouse, 2) + every = _calc(sim, "every_filer_is_dependent_elsewhere", year) + np.testing.assert_array_equal(every, np.minimum(head, spouse)) + blocked = _calc(sim, "is_dependent_elsewhere_without_filing_exception", year) + filers = _calc(sim, "is_tax_unit_head", year) + _calc( + sim, "is_tax_unit_spouse", year + ) + np.testing.assert_array_equal( + _calc( + sim, "head_or_spouse_is_dependent_elsewhere_without_filing_exception", year + ), + sim.populations["tax_unit"].any(blocked.astype(bool) & filers.astype(bool)), + ) + if capture_fallback: + # Keep only arrays so the compatibility comparison can release this + # simulation before constructing the next full model. + return { + name: _calc(sim, name, year) + for name in TAX_UNIT_OUTPUTS + CONSUMERS + ACTUAL_CLAIM_OUTPUTS + } + + +def _check_fallback(units, year, legacy_outputs): + # The absence of a new input is simulation-wide: compare separate + # simulations so Core does not fill omitted people with input defaults. + explicit_sim = Simulation( + situation=_situation(units, year, _swap_variants, explicit_claimability=True) + ) + for name in TAX_UNIT_OUTPUTS + CONSUMERS + ACTUAL_CLAIM_OUTPUTS: + np.testing.assert_allclose( + legacy_outputs[name], + _calc(explicit_sim, name, year), + atol=TOLERANCE, + rtol=0, + err_msg=f"{name} changes when fallback claimability is made explicit", + ) + + +def _exception_variants(unit): + claimable = unit["claimed"] + return [ + (True, False, False, claimable, (False, False)), + (False, False, False, claimable, (False, False)), + (True, False, False, claimable, (True, True)), + (False, False, False, claimable, (True, True)), + ] + + +def _check_exception_scope(): + year = 2025 + units = [ + { + "state": state, + "adults": [{"age": 26, "employment_income": 2_000}, {"age": 30}], + "dependents": children, + "claimed": claimed, + } + for state in AFFECTED + # Isolate ordinary dependents: publicly supported minors have a + # separate Hawaii qualification that does not use this exception. + for children in ( + [], + [{"age": 5, "hi_food_excise_credit_child_receiving_public_support": False}], + ) + for claimed in CLAIM_PATTERNS + ] + sim = Simulation(situation=_situation(units, year, _exception_variants)) + for name in CONSUMERS + ACTUAL_CLAIM_OUTPUTS: + values = _calc(sim, name, year) + if name in ACTUAL_CLAIM_OUTPUTS: + # Person outputs have two filers and optionally one child per unit. + start = 0 + for unit in units: + size = 2 + len(unit["dependents"]) + copies = values[start : start + 4 * size].reshape(4, size) + np.testing.assert_array_equal(copies[0:2], copies[2:4]) + start += 4 * size + continue + copies = values.reshape(-1, 4) + np.testing.assert_allclose( + copies[:, 0::2], copies[:, 1::2], atol=TOLERANCE, rtol=0 + ) + if name not in EXCEPTION_SENSITIVE: + np.testing.assert_allclose( + copies[:, :2], + copies[:, 2:], + atol=TOLERANCE, + rtol=0, + err_msg=f"The filing exception changes general claimability rule {name}", + ) + eligibility = _calc(sim, "eitc_demographic_eligible", year).reshape(-1, 4) + childless = np.array([not unit["dependents"] for unit in units]) + claimable = np.array([any(unit["claimed"]) for unit in units]) + assert not eligibility[childless & claimable, :2].any() + assert eligibility[childless, 2:].all() + # RSMo 143.177.2 requires federal EITC. These units file jointly, so the + # separate dependent-filer stop does not apply, and their zero investment + # income meets Missouri's limit. The exception's Missouri effect must + # therefore follow positive federal EITC, rather than alter claimability. + missouri = np.array([unit["state"] == "MO" for unit in units]) + federal_eitc = _calc(sim, "eitc", year).reshape(-1, 4) + missouri_eligible = _calc(sim, "mo_wftc_eligible", year).reshape(-1, 4) + np.testing.assert_array_equal( + missouri_eligible[missouri].astype(bool), federal_eitc[missouri] > 0 + ) + direct_claimability = _calc( + sim, "head_or_spouse_is_dependent_elsewhere", year + ).reshape(-1, 4) + np.testing.assert_array_equal( + direct_claimability, np.repeat(claimable[:, None], 4, axis=1) + ) + for name, state in (("mi_exemptions", "MI"), ("hi_food_excise_credit", "HI")): + values = _calc(sim, name, year).reshape(-1, 4) + restores_child = np.array( + [ + unit["state"] == state + and bool(unit["dependents"]) + and sum(unit["claimed"]) == 1 + for unit in units + ] + ) + assert (values[restores_child, 2:] > values[restores_child, :2]).all() + + +def _monotone_variants(unit): + # Nobody, either adult, then both adults claimed; labels fixed. + return [ + (True, False, False), + (True, True, False), + (True, False, True), + (True, True, True), + ] + + +def _check_monotone(units, year): + sim = Simulation(situation=_situation(units, year, _monotone_variants)) + states = np.array([u["state"] for u in units]) + for name in CONSUMERS: + values = _calc(sim, name, year).reshape(-1, 4) + nobody, first, second, both = values.T + for more, fewer, label in ( + (first, nobody, "first adult"), + (second, nobody, "second adult"), + (both, first, "both after first"), + (both, second, "both after second"), + ): + rises = more > fewer + TOLERANCE + assert not rises.any(), ( + f"{name} in {year} rises when the {label} is claimed " + f"elsewhere: " + + ", ".join( + f"{states[i]} {fewer[i]:.2f} -> {more[i]:.2f}" + for i in np.flatnonzero(rises)[:10] + ) + ) + + +# A batch's cost is mostly per-variable overhead, so each example is a large +# batch and there are few examples. +@settings( + max_examples=4, + deadline=None, + derandomize=True, + suppress_health_check=[HealthCheck.too_slow, HealthCheck.data_too_large], +) +@given(st.sampled_from(YEARS), st.lists(couples(), min_size=6, max_size=15)) +def test_claimed_spouse_does_not_depend_on_head_label(year, units): + _check_swap(units, year, TAX_UNIT_OUTPUTS + CONSUMERS) + + +@pytest.mark.parametrize("year", YEARS) +def test_seeded_population(year): + units = _seeded_couples(AFFECTED, per_state=2) + _crafted_cases(AFFECTED) + legacy_outputs = _check_swap( + units, year, TAX_UNIT_OUTPUTS + CONSUMERS, capture_fallback=year == 2025 + ) + if year == 2025: + _check_fallback(units, year, legacy_outputs) + _check_exception_scope() + _check_monotone(units, year) + + +def test_every_state_with_a_claimed_spouse(): + # Other states' rules for a claimed filer read both spouses already; + # this guards their state income tax in one year. + units = _seeded_couples(ALL_STATES, per_state=2) + _crafted_cases( + ALL_STATES, review_case_only=True + ) + _check_swap(units, 2026, TAX_UNIT_OUTPUTS) diff --git a/policyengine_us/tests/core/test_me_standard_deduction_federal_conformity.py b/policyengine_us/tests/core/test_me_standard_deduction_federal_conformity.py index 1fb402c0372..fc95d8fb04f 100644 --- a/policyengine_us/tests/core/test_me_standard_deduction_federal_conformity.py +++ b/policyengine_us/tests/core/test_me_standard_deduction_federal_conformity.py @@ -110,21 +110,23 @@ def test_me_standard_deduction_does_not_follow_federal_in_2026(): def test_me_standard_deduction_applies_the_federal_dependent_cap_in_2027(): # IRC Section 63(c)(5), as adjusted by Rev. Proc. 2025-32 section 4.14(2): - # an individual who is a dependent of another taxpayer gets at most the + # an individual who can be claimed by another taxpayer gets at most the # greater of the dependent floor and earned income plus a fixed addition, # capped at the ordinary basic standard deduction. Maine follows the federal - # standard deduction from 2027, so the cap has to reach Maine too. + # standard deduction from 2027, so the cap has to reach Maine too. Supply + # the filer's claimability even though no actual claim is made. simulation = _simulation( { "person": { "age": 17, "employment_income": 3_000, "is_tax_unit_head": True, + "claimable_as_dependent_on_another_return": True, + "claimed_as_dependent_on_another_return": False, } }, "SINGLE", 2027, - tax_unit_inputs={"head_is_dependent_elsewhere": True}, ) maine = simulation.calculate("me_standard_deduction", "2027")[0] federal = simulation.calculate("standard_deduction", "2027")[0] diff --git a/policyengine_us/tests/core/test_medical_expense_premium_conservation.py b/policyengine_us/tests/core/test_medical_expense_premium_conservation.py index 6cffa421527..e6db39bbff2 100644 --- a/policyengine_us/tests/core/test_medical_expense_premium_conservation.py +++ b/policyengine_us/tests/core/test_medical_expense_premium_conservation.py @@ -32,6 +32,7 @@ @pytest.fixture(scope="module") def reference_system(): + # "Main" below is a handwritten paid-cost oracle, not a second model. return system @@ -351,6 +352,19 @@ def _state_case(**values): } +DEPENDENT_ELSEWHERE_STATE_CASES = [ + _state_case(age=64, income=0, deduction=0, direct=(False, False, False)), + _state_case( + age=65, + income=0, + deduction=0, + direct=(False, False, False), + dependent_elsewhere=True, + other=(0, 0, 28_000), + ), +] + + def _state_simulation(cases, reference_system, with_se_deduction): people, tax_units, households, marital_units = {}, {}, {}, {} expectations = { @@ -404,6 +418,11 @@ def _state_simulation(cases, reference_system, with_se_deduction): "is_tax_unit_head": person_index == 0, "is_tax_unit_spouse": person_index == 1, "is_tax_unit_dependent": person_index == 2, + # Person facts let both the legacy head helper and #10024's + # spouse-independent claimability helpers derive consistently. + "claimed_as_dependent_on_another_return": ( + case["dependent_elsewhere"] if person_index == 0 else False + ), "employment_income": case["income"] if person_index == 0 else 0, "medicare_enrolled": False, premium_variable: premiums[position], @@ -421,7 +440,6 @@ def _state_simulation(cases, reference_system, with_se_deduction): "members": names, "self_employed_health_insurance_ald": {PERIOD: deduction}, "filing_status": {PERIOD: case["filing_status"]}, - "head_is_dependent_elsewhere": {PERIOD: case["dependent_elsewhere"]}, } if supplied: tax_unit["itemized_medical_expenses"] = {PERIOD: case["subtotal"]} @@ -501,6 +519,7 @@ def _assert_state_outputs(cases, reference_system, with_se_deduction): @settings(max_examples=12, deadline=None, derandomize=True) +@example(cases=DEPENDENT_ELSEWHERE_STATE_CASES) @example( cases=[ _state_case(subtotal_mode="subtotal_only", subtotal=6_500), @@ -521,6 +540,7 @@ def test_state_paid_cost_bases_match_main_without_se_deduction(reference_system, @settings(max_examples=12, deadline=None, derandomize=True) +@example(cases=DEPENDENT_ELSEWHERE_STATE_CASES) @example( cases=[ _state_case(premiums=(6_000, 0, 0), other=(500, 0, 0)), diff --git a/policyengine_us/tests/policy/baseline/gov/hhs/medicaid/income/medicaid_is_tax_dependent.yaml b/policyengine_us/tests/policy/baseline/gov/hhs/medicaid/income/medicaid_is_tax_dependent.yaml new file mode 100644 index 00000000000..c04ad8732f3 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/hhs/medicaid/income/medicaid_is_tax_dependent.yaml @@ -0,0 +1,49 @@ +- name: Claimability alone does not create an expected Medicaid tax-dependent claim + period: 2025 + input: + age: 18 + is_tax_unit_head: true + is_tax_unit_spouse: false + is_tax_unit_dependent: false + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + medicaid_has_known_claiming_tax_unit: false + tax_unit_is_filer: true + output: + # 42 CFR 435.603(f)(1)-(2) uses expected actual claims, not claimability. + medicaid_is_tax_dependent: false + medicaid_uses_missing_claimant_fallback: false + medicaid_uses_non_filer_rules: false + +- name: An expected actual claim invokes Medicaid's dependent and missing-claimant rules + period: 2025 + input: + age: 18 + is_tax_unit_head: true + is_tax_unit_spouse: false + is_tax_unit_dependent: false + claimed_as_dependent_on_another_return: true + claimable_as_dependent_on_another_return: true + medicaid_has_known_claiming_tax_unit: false + tax_unit_is_filer: true + output: + medicaid_is_tax_dependent: true + medicaid_uses_missing_claimant_fallback: true + medicaid_uses_non_filer_rules: true + +- name: The tax filing exception leaves Medicaid's expected actual claim unchanged + period: 2025 + input: + age: 18 + is_tax_unit_head: true + is_tax_unit_spouse: false + is_tax_unit_dependent: false + claimed_as_dependent_on_another_return: true + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + medicaid_has_known_claiming_tax_unit: false + tax_unit_is_filer: true + output: + medicaid_is_tax_dependent: true + medicaid_uses_missing_claimant_fallback: true + medicaid_uses_non_filer_rules: true diff --git a/policyengine_us/tests/policy/baseline/gov/irs/credits/earned_income/eitc_demographic_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/irs/credits/earned_income/eitc_demographic_eligible.yaml index ae065070ccc..584e066b887 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/credits/earned_income/eitc_demographic_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/credits/earned_income/eitc_demographic_eligible.yaml @@ -127,3 +127,224 @@ is_full_time_student: true output: eitc_demographic_eligible: false + +- name: 2025 childless single filer who can be claimed as a dependent is ineligible + period: 2025 + input: + people: + head: + age: 30 + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [head] + output: + # IRC 32(c)(1)(A)(ii)(III). + eitc_demographic_eligible: false + +- name: 2025 childless joint return where one spouse can be claimed is ineligible (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: CA + output: + # Publication 596 (2025, p.18): "neither you nor your spouse can be claimed as a + # dependent by another person if you claim the EIC on your joint return." + eitc_demographic_eligible: false + +- name: 2025 childless joint return where one spouse can be claimed is ineligible (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 30 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + households: + household: + members: [person_b, person_a] + state_code: CA + output: + # Publication 596 (2025, p.18): "neither you nor your spouse can be claimed as a + # dependent by another person if you claim the EIC on your joint return." + eitc_demographic_eligible: false + +- name: 2025 joint return with a child where one spouse can be claimed (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_a, person_b, child_1] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child_1] + state_code: CA + output: + # The dependent condition in IRC 32(c)(1)(A)(ii)(III) applies only without a + # qualifying child. + eitc_demographic_eligible: true + +- name: 2025 joint return with a child where one spouse can be claimed (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 30 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_b, person_a, child_1] + filing_status: JOINT + households: + household: + members: [person_b, person_a, child_1] + state_code: CA + output: + # The dependent condition in IRC 32(c)(1)(A)(ii)(III) applies only without a + # qualifying child. + eitc_demographic_eligible: true + +- name: 2025 claimable childless filer qualifies when nonrequired parent files only for a withholding refund + period: 2025 + input: + people: + head: + age: 26 + employment_income: 2_000 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: CA + output: + # Pub. 596 PDF p.18: nonrequired parent files only for a refund of income tax + # withheld. Pub. 501 PDF p.24: claimability still limits the standard deduction. + eitc_demographic_eligible: true + basic_standard_deduction: 2_450 + +- name: 2025 claimable childless filer qualifies when nonrequired parent files no return + period: 2025 + input: + people: + head: + age: 26 + employment_income: 2_000 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: CA + output: + # Pub. 596 PDF p.18 also covers a nonrequired claimant who files no return. + # The exception changes EITC, while Pub. 501 PDF p.24 still limits deductions. + eitc_demographic_eligible: true + basic_standard_deduction: 2_450 + +- name: 2025 unclaimed childless filer remains ineligible when claimable by a parent required to file + period: 2025 + input: + people: + head: + age: 26 + employment_income: 2_000 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: false + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: CA + output: + # Pub. 596 PDF p.18 Example 2: choosing not to claim the filer is insufficient. + eitc_demographic_eligible: false + basic_standard_deduction: 2_450 + +- name: 2025 unclaimed childless filer remains ineligible when parent files for refundable credits + period: 2025 + input: + people: + head: + age: 26 + employment_income: 2_000 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: false + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: CA + output: + # Pub. 596 PDF pp.18-19: filing for a credit is not filing solely for a refund + # of withholding or estimated payments, even if no return was required. + eitc_demographic_eligible: false + basic_standard_deduction: 2_450 diff --git a/policyengine_us/tests/policy/baseline/gov/irs/credits/retirement_saving/savers_credit_eligible_person.yaml b/policyengine_us/tests/policy/baseline/gov/irs/credits/retirement_saving/savers_credit_eligible_person.yaml index 7309a5c6dc6..c070b58f18f 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/credits/retirement_saving/savers_credit_eligible_person.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/credits/retirement_saving/savers_credit_eligible_person.yaml @@ -17,3 +17,25 @@ is_full_time_student: false output: savers_credit_eligible_person: false + +- name: Savers credit admits a claimable filer who is not actually claimed + period: 2025 + input: + age: 26 + is_full_time_student: false + is_tax_unit_head_or_spouse: true + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + output: + savers_credit_eligible_person: true + +- name: Savers credit excludes an actually claimed filer + period: 2025 + input: + age: 26 + is_full_time_student: false + is_tax_unit_head_or_spouse: true + claimed_as_dependent_on_another_return: true + claimable_as_dependent_on_another_return: true + output: + savers_credit_eligible_person: false diff --git a/policyengine_us/tests/policy/baseline/gov/irs/deductions/standard/basic_standard_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/irs/deductions/standard/basic_standard_deduction.yaml index 483f073599a..4099214070c 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/deductions/standard/basic_standard_deduction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/deductions/standard/basic_standard_deduction.yaml @@ -207,3 +207,81 @@ members: [head] output: basic_standard_deduction: 3_650 + +- name: 2025 joint return uses the dependent worksheet when either spouse can be claimed (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 20 + employment_income: 8_000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + employment_income: 8_000 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: CA + output: + # Joint earned income $16,000 + $450 = $16,450, below the $31,500 joint amount. + basic_standard_deduction: 16_450 + +- name: 2025 joint return uses the dependent worksheet when either spouse can be claimed (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 20 + employment_income: 8_000 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + employment_income: 8_000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + households: + household: + members: [person_b, person_a] + state_code: CA + output: + # Joint earned income $16,000 + $450 = $16,450, below the $31,500 joint amount. + basic_standard_deduction: 16_450 + +- name: 2025 dependent filer with self-employment income uses earned income net of the self-employment tax deduction + period: 2025 + absolute_error_margin: 0.01 + input: + people: + head: + age: 20 + self_employment_income: 3_000 + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [head] + filing_status: SINGLE + households: + household: + members: [head] + state_code: CA + output: + # Worksheet earned income subtracts Schedule 1 line 15: + # $3,000 - $3,000 x 0.9235 x 15.3% / 2 = $2,788.06; plus $450 = $3,238.06. + basic_standard_deduction: 3238.06 diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/tax/income/exemptions/az_aged_exemption_eligible_person.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/tax/income/exemptions/az_aged_exemption_eligible_person.yaml index a4d945380ab..1bb415b0fed 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/az/tax/income/exemptions/az_aged_exemption_eligible_person.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/az/tax/income/exemptions/az_aged_exemption_eligible_person.yaml @@ -4,13 +4,13 @@ people: person1: is_tax_unit_head: true + claimed_as_dependent_on_another_return: false person2: is_tax_unit_spouse: true + claimed_as_dependent_on_another_return: true tax_units: tax_unit: az_filing_status: JOINT - head_is_dependent_elsewhere: false - spouse_is_dependent_elsewhere: true members: [person1, person2] household: members: [person1, person2] @@ -24,13 +24,13 @@ people: person1: is_tax_unit_head: true + claimed_as_dependent_on_another_return: true person2: is_tax_unit_spouse: true + claimed_as_dependent_on_another_return: true tax_units: tax_unit: az_filing_status: JOINT - head_is_dependent_elsewhere: true - spouse_is_dependent_elsewhere: true members: [person1, person2] household: members: [person1, person2] @@ -44,13 +44,13 @@ people: person1: is_tax_unit_head: true + claimed_as_dependent_on_another_return: false person2: is_tax_unit_spouse: true + claimed_as_dependent_on_another_return: false tax_units: tax_unit: az_filing_status: SEPARATE - head_is_dependent_elsewhere: false - spouse_is_dependent_elsewhere: false members: [person1, person2] household: members: [person1, person2] @@ -64,19 +64,41 @@ people: person1: is_tax_unit_head: true + claimed_as_dependent_on_another_return: false person2: is_tax_unit_spouse: true + claimed_as_dependent_on_another_return: false person3: is_tax_unit_head: false is_tax_unit_spouse: false tax_units: tax_unit: az_filing_status: SEPARATE - head_is_dependent_elsewhere: false - spouse_is_dependent_elsewhere: false members: [person1, person2, person3] household: members: [person1, person2, person3] state_code: AZ output: az_aged_exemption_eligible_person: [true, false, false] + +- name: Arizona aged exemption retains an unclaimed claimable filer + period: 2025 + input: + state_code: AZ + age: 70 + is_tax_unit_head: true + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + output: + az_aged_exemption_eligible_person: true + +- name: Arizona aged exemption excludes an actually claimed filer + period: 2025 + input: + state_code: AZ + age: 70 + is_tax_unit_head: true + claimed_as_dependent_on_another_return: true + claimable_as_dependent_on_another_return: true + output: + az_aged_exemption_eligible_person: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/act_163_sunset.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/act_163_sunset.yaml index 4116a0a6dac..778252cac05 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/act_163_sunset.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/act_163_sunset.yaml @@ -34,7 +34,6 @@ adjusted_gross_income: 10_000 filing_status: JOINT exemptions_count: 2 - head_is_dependent_elsewhere: false households: household: members: [person1, person2] @@ -58,7 +57,6 @@ adjusted_gross_income: 4_000 filing_status: JOINT exemptions_count: 2 - head_is_dependent_elsewhere: false households: household: members: [person1, person2] @@ -80,7 +78,6 @@ adjusted_gross_income: 10_000 filing_status: SINGLE exemptions_count: 1 - head_is_dependent_elsewhere: false households: household: members: [person1] @@ -102,7 +99,6 @@ adjusted_gross_income: 4_000 filing_status: SINGLE exemptions_count: 1 - head_is_dependent_elsewhere: false households: household: members: [person1] diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit.yaml index 9ad568e7fee..c4daec08bc0 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit.yaml @@ -20,7 +20,6 @@ adjusted_gross_income: 4_000 filing_status: SINGLE exemptions_count: 3 - head_is_dependent_elsewhere: false households: household: members: [person1, person2, person3] @@ -50,7 +49,6 @@ adjusted_gross_income: 4_000 filing_status: SINGLE exemptions_count: 3 - head_is_dependent_elsewhere: false households: household: members: [person1, person2, person3] @@ -71,6 +69,7 @@ person2: age: 39 is_child: false + claimed_as_dependent_on_another_return: true spm_units: spm_unit: members: [person1, person2] @@ -81,7 +80,6 @@ adjusted_gross_income: 4_000 filing_status: SINGLE exemptions_count: 2 - head_is_dependent_elsewhere: true households: household: members: [person1, person2] @@ -111,7 +109,6 @@ adjusted_gross_income: 35_000 filing_status: SINGLE exemptions_count: 3 - head_is_dependent_elsewhere: false households: household: members: [person1, person2, person3] @@ -141,7 +138,6 @@ adjusted_gross_income: 35_000 filing_status: JOINT exemptions_count: 3 - head_is_dependent_elsewhere: false households: household: members: [person1, person2, person3] @@ -171,7 +167,6 @@ adjusted_gross_income: 55_000 filing_status: JOINT exemptions_count: 3 - head_is_dependent_elsewhere: false households: household: members: [person1, person2, person3] @@ -197,7 +192,6 @@ members: [person1, person2] adjusted_gross_income: 6_000 filing_status: SINGLE - head_is_dependent_elsewhere: false households: household: members: [person1, person2] @@ -223,7 +217,6 @@ members: [person1, person2] adjusted_gross_income: 6_000 filing_status: SINGLE - head_is_dependent_elsewhere: false households: household: members: [person1, person2] @@ -258,3 +251,686 @@ exemptions_count: 2 hi_food_excise_credit: 440 hi_income_tax: -440 + +- name: 2027 joint return where only one spouse can be claimed (the claimable spouse is the head) + period: 2027 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + adjusted_gross_income: 10_000 + households: + household: + members: [person_a, person_b] + state_code: HI + output: + # Form N-311 counts only the spouse who cannot be claimed: 1 x $220. + hi_food_excise_credit: 220 + +- name: 2027 joint return where only one spouse can be claimed (the claimable spouse is the spouse) + period: 2027 + input: + people: + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + adjusted_gross_income: 10_000 + households: + household: + members: [person_b, person_a] + state_code: HI + output: + # Form N-311 counts only the spouse who cannot be claimed: 1 x $220. + hi_food_excise_credit: 220 + +- name: 2027 joint return where both spouses can be claimed + period: 2027 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + adjusted_gross_income: 10_000 + households: + household: + members: [person_a, person_b] + state_code: HI + output: + hi_food_excise_credit: 0 + +- name: 2022 joint return with a publicly supported minor where only one spouse can be claimed (the claimable spouse is the head) + period: 2022 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + hi_food_excise_credit_child_receiving_public_support: true + tax_units: + tax_unit: + members: [person_a, person_b, child_1] + filing_status: JOINT + adjusted_gross_income: 4_000 + households: + household: + members: [person_a, person_b, child_1] + state_code: HI + output: + # The spouse who cannot be claimed: $110; HRS 235-55.85(c) lets the minor + # receiving public support count for this credit: $110. + hi_food_excise_credit: 220 + +- name: 2022 joint return with a publicly supported minor where only one spouse can be claimed (the claimable spouse is the spouse) + period: 2022 + input: + people: + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + hi_food_excise_credit_child_receiving_public_support: true + tax_units: + tax_unit: + members: [person_b, person_a, child_1] + filing_status: JOINT + adjusted_gross_income: 4_000 + households: + household: + members: [person_b, person_a, child_1] + state_code: HI + output: + # The spouse who cannot be claimed: $110; HRS 235-55.85(c) lets the minor + # receiving public support count for this credit: $110. + hi_food_excise_credit: 220 + +- name: 2025 joint return with a publicly supported minor where only one spouse can be claimed (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + hi_food_excise_credit_child_receiving_public_support: true + tax_units: + tax_unit: + members: [person_a, person_b, child_1] + filing_status: JOINT + adjusted_gross_income: 4_000 + households: + household: + members: [person_a, person_b, child_1] + state_code: HI + output: + # From 2023 the minor counts as a qualified exemption: 2 x $220. + hi_food_excise_credit: 440 + +- name: 2025 joint return with a publicly supported minor where only one spouse can be claimed (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + hi_food_excise_credit_child_receiving_public_support: true + tax_units: + tax_unit: + members: [person_b, person_a, child_1] + filing_status: JOINT + adjusted_gross_income: 4_000 + households: + household: + members: [person_b, person_a, child_1] + state_code: HI + output: + # From 2023 the minor counts as a qualified exemption: 2 x $220. + hi_food_excise_credit: 440 + +- name: 2022 joint return where one spouse and the publicly supported minor can be claimed elsewhere (the claimable spouse is the head) + period: 2022 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + hi_food_excise_credit_child_receiving_public_support: true + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [person_a, person_b, child_1] + filing_status: JOINT + adjusted_gross_income: 4_000 + households: + household: + members: [person_a, person_b, child_1] + state_code: HI + output: + # Form N-311 line 3 lists only minors who cannot be claimed by another taxpayer, + # so only the spouse who cannot be claimed counts: $110. + hi_food_excise_credit: 110 + +- name: 2022 joint return where one spouse and the publicly supported minor can be claimed elsewhere (the claimable spouse is the spouse) + period: 2022 + input: + people: + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + hi_food_excise_credit_child_receiving_public_support: true + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [person_b, person_a, child_1] + filing_status: JOINT + adjusted_gross_income: 4_000 + households: + household: + members: [person_b, person_a, child_1] + state_code: HI + output: + # Form N-311 line 3 lists only minors who cannot be claimed by another taxpayer, + # so only the spouse who cannot be claimed counts: $110. + hi_food_excise_credit: 110 + +- name: 2025 joint return where one spouse and the publicly supported minor can be claimed elsewhere (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + hi_food_excise_credit_child_receiving_public_support: true + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [person_a, person_b, child_1] + filing_status: JOINT + adjusted_gross_income: 4_000 + households: + household: + members: [person_a, person_b, child_1] + state_code: HI + output: + # Only the spouse who cannot be claimed counts: 1 x $220. + hi_food_excise_credit: 220 + +- name: 2025 joint return where one spouse and the publicly supported minor can be claimed elsewhere (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + hi_food_excise_credit_child_receiving_public_support: true + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [person_b, person_a, child_1] + filing_status: JOINT + adjusted_gross_income: 4_000 + households: + household: + members: [person_b, person_a, child_1] + state_code: HI + output: + # Only the spouse who cannot be claimed counts: 1 x $220. + hi_food_excise_credit: 220 + +# N-311 (2022 and 2025), PDF p.1 lines 2-3, excludes externally claimable +# persons from both ordinary qualified persons and publicly supported minors. +# At $4,000 AGI, 2022 lines 7-10 give 2 x $110; 2025 lines 7-9 give 2 x $220. +- name: 2022 independent joint filers exclude externally claimable publicly supported child when person_a is head + period: 2022 + input: + people: + person_a: + age: 40 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: false + dependent_claimant_filing_exception: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 45 + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + hi_food_excise_credit_child_receiving_public_support: true + claimable_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [person_a, person_b, child_1] + filing_status: JOINT + adjusted_gross_income: 4000 + households: + household: + members: [person_a, person_b, child_1] + state_code: HI + output: + hi_food_excise_credit: 220 + hi_food_excise_exemption_amount: 220 + hi_food_excise_credit_minor_child_count: 0 + +- name: 2022 independent joint filers exclude externally claimable publicly supported child when person_b is head + period: 2022 + input: + people: + person_b: + age: 45 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 40 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: false + dependent_claimant_filing_exception: false + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + hi_food_excise_credit_child_receiving_public_support: true + claimable_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [person_b, person_a, child_1] + filing_status: JOINT + adjusted_gross_income: 4000 + households: + household: + members: [person_b, person_a, child_1] + state_code: HI + output: + hi_food_excise_credit: 220 + hi_food_excise_exemption_amount: 220 + hi_food_excise_credit_minor_child_count: 0 + +- name: 2025 independent joint filers exclude externally claimable publicly supported child when person_a is head + period: 2025 + input: + people: + person_a: + age: 40 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: false + dependent_claimant_filing_exception: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 45 + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + hi_food_excise_credit_child_receiving_public_support: true + claimable_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [person_a, person_b, child_1] + filing_status: JOINT + adjusted_gross_income: 4000 + households: + household: + members: [person_a, person_b, child_1] + state_code: HI + output: + hi_food_excise_credit: 440 + hi_food_excise_exemption_amount: 440 + hi_food_excise_credit_minor_child_count: 0 + +- name: 2025 independent joint filers exclude externally claimable publicly supported child when person_b is head + period: 2025 + input: + people: + person_b: + age: 45 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 40 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: false + dependent_claimant_filing_exception: false + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + hi_food_excise_credit_child_receiving_public_support: true + claimable_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [person_b, person_a, child_1] + filing_status: JOINT + adjusted_gross_income: 4000 + households: + household: + members: [person_b, person_a, child_1] + state_code: HI + output: + hi_food_excise_credit: 440 + hi_food_excise_exemption_amount: 440 + hi_food_excise_credit_minor_child_count: 0 + +- name: 2022 joint return filing exception restores own ordinary dependent but excludes claimable head + period: 2022 + input: + people: + person_a: + age: 26 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + hi_food_excise_credit_child_receiving_public_support: false + tax_units: + tax_unit: + members: [person_a, person_b, child_1] + filing_status: JOINT + adjusted_gross_income: 4000 + households: + household: + members: [person_a, person_b, child_1] + state_code: HI + output: + hi_food_excise_credit_minor_child_count: 0 + hi_food_excise_credit: 220 + hi_food_excise_exemption_amount: 220 + +- name: 2022 joint return filing exception restores own ordinary dependent but excludes claimable spouse + period: 2022 + input: + people: + person_b: + age: 30 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 26 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + hi_food_excise_credit_child_receiving_public_support: false + tax_units: + tax_unit: + members: [person_b, person_a, child_1] + filing_status: JOINT + adjusted_gross_income: 4000 + households: + household: + members: [person_b, person_a, child_1] + state_code: HI + output: + hi_food_excise_credit_minor_child_count: 0 + hi_food_excise_credit: 220 + hi_food_excise_exemption_amount: 220 + +- name: 2025 joint return filing exception restores own ordinary dependent but excludes claimable head + period: 2025 + input: + people: + person_a: + age: 26 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + hi_food_excise_credit_child_receiving_public_support: false + tax_units: + tax_unit: + members: [person_a, person_b, child_1] + filing_status: JOINT + adjusted_gross_income: 4000 + households: + household: + members: [person_a, person_b, child_1] + state_code: HI + output: + hi_food_excise_credit_minor_child_count: 0 + hi_food_excise_credit: 440 + hi_food_excise_exemption_amount: 440 + +- name: 2025 joint return filing exception restores own ordinary dependent but excludes claimable spouse + period: 2025 + input: + people: + person_b: + age: 30 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 26 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + hi_food_excise_credit_child_receiving_public_support: false + tax_units: + tax_unit: + members: [person_b, person_a, child_1] + filing_status: JOINT + adjusted_gross_income: 4000 + households: + household: + members: [person_b, person_a, child_1] + state_code: HI + output: + hi_food_excise_credit_minor_child_count: 0 + hi_food_excise_credit: 440 + hi_food_excise_exemption_amount: 440 + +# A publicly supported minor filer belongs to N-311 line 2, not line 3. +# At $16,000 AGI in 2022, 2 ordinary persons x $70 = $140. +- name: 2022 publicly supported minor filer is only an ordinary qualified person when labelled head + period: 2022 + input: + people: + person_a: + age: 17 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: false + dependent_claimant_filing_exception: false + is_tax_unit_head: true + is_tax_unit_spouse: false + hi_food_excise_credit_child_receiving_public_support: true + person_b: + age: 20 + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + adjusted_gross_income: 16000 + households: + household: + members: [person_a, person_b] + state_code: HI + output: + hi_food_excise_credit: 140 + hi_food_excise_credit_minor_child_count: 0 + +- name: 2022 publicly supported minor filer is only an ordinary qualified person when labelled spouse + period: 2022 + input: + people: + person_b: + age: 20 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 17 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: false + dependent_claimant_filing_exception: false + is_tax_unit_head: false + is_tax_unit_spouse: true + hi_food_excise_credit_child_receiving_public_support: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + adjusted_gross_income: 16000 + households: + household: + members: [person_b, person_a] + state_code: HI + output: + hi_food_excise_credit: 140 + hi_food_excise_credit_minor_child_count: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/hi_act_115_rebate.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/hi_act_115_rebate.yaml index 13ebfd8d94a..124ffe32e48 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/hi_act_115_rebate.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/hi_act_115_rebate.yaml @@ -162,3 +162,156 @@ state_code: HI output: hi_act_115_rebate: 0 + +- name: Joint return with a child where only one spouse can be claimed (the claimable spouse is the head) + period: 2021 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_a, person_b, child_1] + filing_status: JOINT + adjusted_gross_income: 5_000 + households: + household: + members: [person_a, person_b, child_1] + state_code: HI + output: + # Only the spouse who cannot be claimed has a qualified exemption; a joint + # return on which a spouse can be claimed claims no dependents. 1 x $300. + hi_act_115_rebate: 300 + +- name: Joint return with a child where only one spouse can be claimed (the claimable spouse is the spouse) + period: 2021 + input: + people: + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_b, person_a, child_1] + filing_status: JOINT + adjusted_gross_income: 5_000 + households: + household: + members: [person_b, person_a, child_1] + state_code: HI + output: + # Only the spouse who cannot be claimed has a qualified exemption; a joint + # return on which a spouse can be claimed claims no dependents. 1 x $300. + hi_act_115_rebate: 300 + +- name: Joint return where both spouses can be claimed + period: 2021 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + adjusted_gross_income: 5_000 + households: + household: + members: [person_a, person_b] + state_code: HI + output: + hi_act_115_rebate: 0 + +# Act 115 sec.3(a)-(b), HRS 235-54(a), and Pub. 501 PDF p.11. +- name: Joint return filing exception restores own dependent but excludes claimable head from Act 115 + period: 2021 + input: + people: + person_a: + age: 26 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_a, person_b, child_1] + filing_status: JOINT + adjusted_gross_income: 4000 + households: + household: + members: [person_a, person_b, child_1] + state_code: HI + output: + hi_act_115_rebate: 600 + +- name: Joint return filing exception restores own dependent but excludes claimable spouse from Act 115 + period: 2021 + input: + people: + person_b: + age: 30 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 26 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_b, person_a, child_1] + filing_status: JOINT + adjusted_gross_income: 4000 + households: + household: + members: [person_b, person_a, child_1] + state_code: HI + output: + hi_act_115_rebate: 600 diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_low_income_household_renters_tax_credit.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_low_income_household_renters_tax_credit.yaml index cddb436375d..25fa5e14ce5 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_low_income_household_renters_tax_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_low_income_household_renters_tax_credit.yaml @@ -37,3 +37,132 @@ hi_tax_credit_for_low_income_household_renters_eligible: false output: hi_tax_credit_for_low_income_household_renters: 0 + +- name: Joint return with a child where only one spouse can be claimed (the claimable spouse is the head) + period: 2021 + input: + people: + person_a: + age: 66 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 66 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_a, person_b, child_1] + filing_status: JOINT + hi_tax_credit_for_low_income_household_renters_eligible: true + households: + household: + members: [person_a, person_b, child_1] + state_code: HI + output: + # Schedule X lines 8-10: the spouse who cannot be claimed, plus their age + # 65 exemption. 2 x $50. + hi_tax_credit_for_low_income_household_renters: 100 + +- name: Joint return with a child where only one spouse can be claimed (the claimable spouse is the spouse) + period: 2021 + input: + people: + person_b: + age: 66 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 66 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_b, person_a, child_1] + filing_status: JOINT + hi_tax_credit_for_low_income_household_renters_eligible: true + households: + household: + members: [person_b, person_a, child_1] + state_code: HI + output: + # Schedule X lines 8-10: the spouse who cannot be claimed, plus their age + # 65 exemption. 2 x $50. + hi_tax_credit_for_low_income_household_renters: 100 + +# Schedule X lines 8-10; Pub. 501 filing exception restores only dependents. +- name: Joint return filing exception restores own dependent without claimable head personal or aged exemption + period: 2021 + input: + people: + person_a: + age: 66 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 66 + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_a, person_b, child_1] + filing_status: JOINT + adjusted_gross_income: 4000 + hi_tax_credit_for_low_income_household_renters_eligible: true + households: + household: + members: [person_a, person_b, child_1] + state_code: HI + output: + hi_tax_credit_for_low_income_household_renters: 150 + +- name: Joint return filing exception restores own dependent without claimable spouse personal or aged exemption + period: 2021 + input: + people: + person_b: + age: 66 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 66 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_b, person_a, child_1] + filing_status: JOINT + adjusted_gross_income: 4000 + hi_tax_credit_for_low_income_household_renters_eligible: true + households: + household: + members: [person_b, person_a, child_1] + state_code: HI + output: + hi_tax_credit_for_low_income_household_renters: 150 diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_tax_credit_for_low_income_household_renters_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_tax_credit_for_low_income_household_renters_eligible.yaml index 4916c7667b8..629efc27e05 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_tax_credit_for_low_income_household_renters_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_tax_credit_for_low_income_household_renters_eligible.yaml @@ -2,7 +2,7 @@ period: 2021 input: state_code: HI - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false hi_agi: 29_000 rent: 1_100 output: @@ -12,7 +12,7 @@ period: 2021 input: state_code: HI - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false hi_agi: 31_000 rent: 1_100 output: @@ -22,7 +22,7 @@ period: 2021 input: state_code: HI - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false hi_agi: 29_000 rent: 900 output: @@ -32,8 +32,91 @@ period: 2021 input: state_code: HI - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false hi_agi: 32_000 rent: 900 output: hi_tax_credit_for_low_income_household_renters_eligible: false + +- name: Joint return where only one spouse can be claimed (the claimable spouse is the head) + period: 2021 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + rent: 1_100 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + hi_agi: 29_000 + households: + household: + members: [person_a, person_b] + state_code: HI + output: + # HRS 235-55.7(b): the spouse who cannot be claimed may claim the credit. + hi_tax_credit_for_low_income_household_renters_eligible: true + +- name: Joint return where only one spouse can be claimed (the claimable spouse is the spouse) + period: 2021 + input: + people: + person_b: + age: 20 + rent: 1_100 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + hi_agi: 29_000 + households: + household: + members: [person_b, person_a] + state_code: HI + output: + # HRS 235-55.7(b): the spouse who cannot be claimed may claim the credit. + hi_tax_credit_for_low_income_household_renters_eligible: true + +- name: Joint return where both spouses can be claimed + period: 2021 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + rent: 1_100 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + hi_agi: 29_000 + households: + household: + members: [person_a, person_b] + state_code: HI + output: + hi_tax_credit_for_low_income_household_renters_eligible: false 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..56eb95f2a84 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 @@ -7,7 +7,7 @@ hi_agi: 1_000 hi_total_itemized_deductions: 5_000 hi_reduced_itemized_deductions: 1_000 - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: hi_deductions: 5_000 @@ -20,7 +20,7 @@ hi_agi: 1_000_000 hi_total_itemized_deductions: 5_000 hi_reduced_itemized_deductions: 1_000 - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: hi_deductions: 4_400 @@ -33,7 +33,7 @@ hi_agi: 1_000 hi_total_itemized_deductions: 1_000 hi_reduced_itemized_deductions: 1_000 - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: hi_deductions: 4_400 @@ -46,7 +46,7 @@ hi_agi: 1_000_000 hi_total_itemized_deductions: 5_000 hi_reduced_itemized_deductions: 1_000 - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: hi_deductions: 2_200 @@ -59,7 +59,7 @@ hi_agi: 1_000_000 hi_total_itemized_deductions: 5_000 hi_reduced_itemized_deductions: 4_500 - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: hi_deductions: 4_500 @@ -71,3 +71,208 @@ employment_income: 62_000 output: hi_deductions: 8_800 + +- name: 2025 joint standard deduction limited when either spouse can be claimed (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + tax_unit_earned_income: 3_000 + hi_itemized_deductions: 0 + households: + household: + members: [person_a, person_b] + state_code: HI + output: + # HRS 235-2.4(a)(3): greater of $500 or earned income ($3,000), up to $8,800. + hi_deductions: 3_000 + +- name: 2025 joint standard deduction limited when either spouse can be claimed (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + tax_unit_earned_income: 3_000 + hi_itemized_deductions: 0 + households: + household: + members: [person_b, person_a] + state_code: HI + output: + # HRS 235-2.4(a)(3): greater of $500 or earned income ($3,000), up to $8,800. + hi_deductions: 3_000 + +- name: 2025 joint filers with a claimable spouse itemize above the limited standard deduction (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + tax_unit_earned_income: 3_000 + hi_itemized_deductions: 4_000 + households: + household: + members: [person_a, person_b] + state_code: HI + output: + # Itemized deductions of $4,000 exceed the limited $3,000 standard deduction. + hi_deductions: 4_000 + +- name: 2025 joint filers with a claimable spouse itemize above the limited standard deduction (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + tax_unit_earned_income: 3_000 + hi_itemized_deductions: 4_000 + households: + household: + members: [person_b, person_a] + state_code: HI + output: + # Itemized deductions of $4,000 exceed the limited $3,000 standard deduction. + hi_deductions: 4_000 + +- name: 2025 single dependent filer with little earned income gets the $500 minimum + period: 2025 + input: + state_code: HI + filing_status: SINGLE + claimed_as_dependent_on_another_return: true + tax_unit_earned_income: 300 + hi_itemized_deductions: 0 + output: + # HRS 235-2.4(a)(3): greater of $500 or earned income ($300). + hi_deductions: 500 + +- name: 2025 single dependent filer with self-employment income uses earned income net of the self-employment tax deduction + period: 2025 + absolute_error_margin: 0.01 + input: + people: + head: + age: 20 + self_employment_income: 3_000 + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [head] + filing_status: SINGLE + hi_itemized_deductions: 0 + households: + household: + members: [head] + state_code: HI + output: + # N-11 worksheet earned income subtracts Schedule 1 line 15: + # $3,000 x 0.9235 x 15.3% / 2 = $211.94, so $3,000 - $211.94 = $2,788.06. + hi_deductions: 2_788.06 + +- name: 2025 joint return with a claimable self-employed spouse uses earned income net of the self-employment tax deduction (the claimable spouse is the head) + period: 2025 + absolute_error_margin: 0.01 + input: + people: + person_a: + age: 20 + self_employment_income: 3_000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + hi_itemized_deductions: 0 + households: + household: + members: [person_a, person_b] + state_code: HI + output: + # $3,000 - $3,000 x 0.9235 x 15.3% / 2 = $2,788.06, below the $8,800 joint amount. + hi_deductions: 2788.06 + +- name: 2025 joint return with a claimable self-employed spouse uses earned income net of the self-employment tax deduction (the claimable spouse is the spouse) + period: 2025 + absolute_error_margin: 0.01 + input: + people: + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + self_employment_income: 3_000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + hi_itemized_deductions: 0 + households: + household: + members: [person_b, person_a] + state_code: HI + output: + # $3,000 - $3,000 x 0.9235 x 15.3% / 2 = $2,788.06, below the $8,800 joint amount. + hi_deductions: 2788.06 diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_salt_deduction_integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_salt_deduction_integration.yaml index 74db648797c..7efb22ba1f0 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_salt_deduction_integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_salt_deduction_integration.yaml @@ -4,7 +4,7 @@ state_code: HI filing_status: SINGLE age: 40 - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false adjusted_gross_income: 99_999 hi_agi: 99_999 state_withheld_income_tax: 6_000 @@ -32,7 +32,7 @@ state_code: HI filing_status: SINGLE age: 40 - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false adjusted_gross_income: 100_000 hi_agi: 100_000 state_withheld_income_tax: 6_000 @@ -60,7 +60,7 @@ state_code: HI filing_status: SINGLE age: 40 - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false adjusted_gross_income: 99_999 hi_agi: 99_999 state_withheld_income_tax: 2_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/me/tax/income/credits/sales_tax_fairness_credit/me_sales_tax_fairness_credit_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/me/tax/income/credits/sales_tax_fairness_credit/me_sales_tax_fairness_credit_eligible.yaml index b3afa273542..b81ac606775 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/me/tax/income/credits/sales_tax_fairness_credit/me_sales_tax_fairness_credit_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/me/tax/income/credits/sales_tax_fairness_credit/me_sales_tax_fairness_credit_eligible.yaml @@ -2,7 +2,7 @@ period: 2022 input: state_code: ME - head_is_dependent_elsewhere: true + claimed_as_dependent_on_another_return: true filing_status: JOINT output: me_sales_tax_fairness_credit_eligible: false @@ -11,7 +11,7 @@ period: 2022 input: state_code: ME - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false filing_status: SINGLE output: me_sales_tax_fairness_credit_eligible: true @@ -20,7 +20,86 @@ period: 2022 input: state_code: ME - head_is_dependent_elsewhere: true + claimed_as_dependent_on_another_return: true filing_status: SEPARATE output: me_sales_tax_fairness_credit_eligible: false + +- name: Joint return where only one spouse can be claimed as a dependent (the claimable spouse is the head) + period: 2022 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: ME + output: + # 36 MRSA 5213-A(6)(C) excludes the claimable individual; the other spouse + # still qualifies for the joint credit. + me_sales_tax_fairness_credit_eligible: true + +- name: Joint return where only one spouse can be claimed as a dependent (the claimable spouse is the spouse) + period: 2022 + input: + people: + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + households: + household: + members: [person_b, person_a] + state_code: ME + output: + # 36 MRSA 5213-A(6)(C) excludes the claimable individual; the other spouse + # still qualifies for the joint credit. + me_sales_tax_fairness_credit_eligible: true + +- name: Joint return where both spouses can be claimed as dependents + period: 2022 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: ME + output: + me_sales_tax_fairness_credit_eligible: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/exemptions/mi_exemptions.yaml b/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/exemptions/mi_exemptions.yaml index f916a47b51d..e67f331c532 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/exemptions/mi_exemptions.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/exemptions/mi_exemptions.yaml @@ -55,20 +55,36 @@ period: 2021 input: mi_personal_exemptions: 5_000 - head_is_dependent_elsewhere: true + claimed_as_dependent_on_another_return: true filing_status: SINGLE state_code: MI output: mi_exemptions: 1_500 -- name: not disabled, is a dependent, married +- name: not disabled, both spouses can be claimed as dependents, married period: 2021 input: - mi_personal_exemptions: 10_000 - head_is_dependent_elsewhere: true - filing_status: JOINT - state_code: MI + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: MI output: + # $1,500 for each spouse. mi_exemptions: 3_000 # 2025 test cases - personal exemption $5,800, disabled exemption $3,400 @@ -301,15 +317,220 @@ person1: mi_disabled_exemption_eligible_person: true is_fully_disabled_service_connected_veteran: false + claimed_as_dependent_on_another_return: true tax_units: tax_unit: mi_personal_exemptions: 5_800 - head_is_dependent_elsewhere: true filing_status: SINGLE members: [person1] household: members: [person1] state_code: MI output: - # Dependent exemption: $1,500 (for single) + $3,400 disabled = $4,900 + # Dependent exemption: $1,500 + $3,400 disabled = $4,900 mi_exemptions: 4_900 + +- name: 2025 joint return where only one spouse can be claimed (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: MI + output: + # MCL 206.30(4): $1,500 for the claimable spouse, plus the other + # spouse's own $5,800 personal exemption. + mi_exemptions: 7_300 + +- name: 2025 joint return where only one spouse can be claimed (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + households: + household: + members: [person_b, person_a] + state_code: MI + output: + # MCL 206.30(4): $1,500 for the claimable spouse, plus the other + # spouse's own $5,800 personal exemption. + mi_exemptions: 7_300 + +- name: 2025 joint return with a child where only one spouse can be claimed (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_a, person_b, child_1] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child_1] + state_code: MI + output: + # A joint return on which a spouse can be claimed claims no dependents + # (IRS Publication 501), so the child adds no exemption. + mi_exemptions: 7_300 + +- name: 2025 joint return with a child where only one spouse can be claimed (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_b, person_a, child_1] + filing_status: JOINT + households: + household: + members: [person_b, person_a, child_1] + state_code: MI + output: + # A joint return on which a spouse can be claimed claims no dependents + # (IRS Publication 501), so the child adds no exemption. + mi_exemptions: 7_300 + +# Pub. 501 (2025), Dependent Taxpayer Test, PDF p.11; MCL 206.30(4). +- name: 2025 joint return filing exception restores own dependent while claimable head keeps the dependent allowance + period: 2025 + input: + people: + person_a: + age: 26 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_a, person_b, child_1] + filing_status: JOINT + adjusted_gross_income: 4000 + households: + household: + members: [person_a, person_b, child_1] + state_code: MI + output: + mi_exemptions: 13100 + +- name: 2025 joint return filing exception restores own dependent while claimable spouse keeps the dependent allowance + period: 2025 + input: + people: + person_b: + age: 30 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 26 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_b, person_a, child_1] + filing_status: JOINT + adjusted_gross_income: 4000 + households: + household: + members: [person_b, person_a, child_1] + state_code: MI + output: + mi_exemptions: 13100 + +- name: 2025 single filer with a nonrequired claimant who files no return may claim their own dependent + period: 2025 + input: + people: + filer: + age: 26 + is_tax_unit_head: true + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + child: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [filer, child] + filing_status: SINGLE + households: + household: + members: [filer, child] + state_code: MI + output: + # $1,500 claimable-filer allowance + $5,800 for their child. + mi_exemptions: 7_300 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/credits/wftc/mo_wftc_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/credits/wftc/mo_wftc_eligible.yaml index dba14ed3fb0..0cea1d97f45 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/credits/wftc/mo_wftc_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/credits/wftc/mo_wftc_eligible.yaml @@ -133,3 +133,121 @@ # $4,401 is below the federal EITC investment income limit ($11,950 in # 2025) but above Missouri's $4,400 limit. mo_wftc_eligible: false + +- name: 2025 married couple filing combined is not stopped by a claimable spouse (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + eitc: 500 + households: + household: + members: [person_a, person_b] + state_code: MO + output: + # A joint federal filer whose spouse can be claimed still files Form MO-1040 + # as married filing combined, not as claimed as a dependent. + mo_wftc_eligible: true + +- name: 2025 married couple filing combined is not stopped by a claimable spouse (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 30 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + eitc: 500 + households: + household: + members: [person_b, person_a] + state_code: MO + output: + # A joint federal filer whose spouse can be claimed still files Form MO-1040 + # as married filing combined, not as claimed as a dependent. + mo_wftc_eligible: true + +- name: 2025 childless couple with a claimable student spouse gets no federal EITC and so no WFTC (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 20 + is_full_time_student: true + employment_income: 8_000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + employment_income: 8_000 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: MO + output: + # Without a qualifying child, neither spouse may be claimable for the federal + # EITC (Publication 596), which RSMo 143.177 requires. + eitc: 0 + mo_wftc_eligible: false + +- name: 2025 childless couple with a claimable student spouse gets no federal EITC and so no WFTC (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 30 + employment_income: 8_000 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + is_full_time_student: true + employment_income: 8_000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + households: + household: + members: [person_b, person_a] + state_code: MO + output: + # Without a qualifying child, neither spouse may be claimable for the federal + # EITC (Publication 596), which RSMo 143.177 requires. + eitc: 0 + mo_wftc_eligible: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/credits/cdcc/nm_cdcc_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/credits/cdcc/nm_cdcc_eligible.yaml index df774c30456..de3db5f2233 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/credits/cdcc/nm_cdcc_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/credits/cdcc/nm_cdcc_eligible.yaml @@ -1,7 +1,7 @@ - name: Is dependent on another return period: 2022 input: - head_is_dependent_elsewhere: true + claimed_as_dependent_on_another_return: true state_code: NM earned_income: 1 is_disabled: false @@ -15,7 +15,7 @@ - name: No earned income period: 2022 input: - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false state_code: NM earned_income: 0 is_disabled: false @@ -29,7 +29,7 @@ - name: No earned income but disabled - eligible period: 2022 input: - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false state_code: NM earned_income: 0 is_disabled: true @@ -43,7 +43,7 @@ - name: Over income threshold period: 2022 input: - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false state_code: NM earned_income: 0 is_disabled: false @@ -57,7 +57,7 @@ - name: Receives tanf period: 2022 input: - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false state_code: NM earned_income: 0 is_disabled: false @@ -71,7 +71,7 @@ - name: Eligible period: 2022 input: - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false state_code: NM earned_income: 1 is_disabled: false @@ -101,7 +101,6 @@ tax_units: tax_unit: members: [head, spouse] - head_is_dependent_elsewhere: false filing_status: JOINT nm_modified_gross_income: 30_159 households: @@ -130,7 +129,6 @@ tax_units: tax_unit: members: [head, spouse] - head_is_dependent_elsewhere: false filing_status: JOINT nm_modified_gross_income: 30_159 households: @@ -159,7 +157,6 @@ tax_units: tax_unit: members: [head, spouse] - head_is_dependent_elsewhere: false filing_status: JOINT nm_modified_gross_income: 30_159 households: @@ -188,7 +185,6 @@ tax_units: tax_unit: members: [head, spouse] - head_is_dependent_elsewhere: false filing_status: JOINT nm_modified_gross_income: 30_159 households: @@ -217,7 +213,6 @@ tax_units: tax_unit: members: [head, spouse] - head_is_dependent_elsewhere: false filing_status: JOINT nm_modified_gross_income: 30_159 households: @@ -230,7 +225,7 @@ - name: Reported TANF receipt disqualifies despite zero computed TANF period: 2022 input: - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false state_code: NM earned_income: 0 is_disabled: true @@ -241,3 +236,89 @@ nm_modified_gross_income: 30_159 output: nm_cdcc_eligible: false + +- name: Joint return where only one spouse is a dependent of another taxpayer (the claimable spouse is the head) + period: 2022 + input: + people: + person_a: + age: 30 + earned_income: 1 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + earned_income: 1 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + nm_modified_gross_income: 30_159 + households: + household: + members: [person_a, person_b] + state_code: NM + output: + # A spouse who is not a dependent may still qualify (PIT-RC instructions). + nm_cdcc_eligible: true + +- name: Joint return where only one spouse is a dependent of another taxpayer (the claimable spouse is the spouse) + period: 2022 + input: + people: + person_b: + age: 30 + earned_income: 1 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 30 + earned_income: 1 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + nm_modified_gross_income: 30_159 + households: + household: + members: [person_b, person_a] + state_code: NM + output: + # A spouse who is not a dependent may still qualify (PIT-RC instructions). + nm_cdcc_eligible: true + +- name: Joint return where both spouses are a dependent of another taxpayer + period: 2022 + input: + people: + person_a: + age: 30 + earned_income: 1 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + earned_income: 1 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + nm_modified_gross_income: 30_159 + households: + household: + members: [person_a, person_b] + state_code: NM + output: + nm_cdcc_eligible: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/credits/nm_medical_expense_credit.yaml b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/credits/nm_medical_expense_credit.yaml index 10a46317f43..e68538d0c80 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/credits/nm_medical_expense_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/credits/nm_medical_expense_credit.yaml @@ -4,7 +4,7 @@ state_code: NM age: 64 other_medical_expenses: 30_000 - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_medical_expense_credit: 0 @@ -17,8 +17,9 @@ health_insurance_premiums: 6_000 other_medical_expenses: 22_000 self_employed_health_insurance_ald: 1_000 - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: + # Federal: 6,000 + 22,000 - 1,000 = 27,000. NM: 27,000 + 1,000 = 28,000. itemized_medical_expenses: 27_000 nm_medical_expense_credit: 2_800 @@ -29,7 +30,7 @@ age: 65 medicare_enrolled: false itemized_medical_expenses: 28_000 - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_medical_expense_credit: 2_800 @@ -41,7 +42,7 @@ medicare_enrolled: false itemized_medical_expenses: 27_999 self_employed_health_insurance_ald: 1_000 - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_medical_expense_credit: 0 @@ -53,7 +54,7 @@ medicare_enrolled: false other_medical_expenses: 27_999 filing_status: JOINT - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_medical_expense_credit: 0 @@ -64,7 +65,7 @@ age: 65 other_medical_expenses: 28_000 filing_status: JOINT - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_medical_expense_credit: 2_800 @@ -75,7 +76,7 @@ age: 65 other_medical_expenses: 28_000 filing_status: SEPARATE - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_medical_expense_credit: 1_400 @@ -86,7 +87,7 @@ age: 65 other_medical_expenses: 40_000 filing_status: SINGLE - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_medical_expense_credit: 2_800 @@ -97,23 +98,327 @@ age: 65 other_medical_expenses: 40_000 filing_status: SINGLE - head_is_dependent_elsewhere: true + claimed_as_dependent_on_another_return: true output: nm_medical_expense_credit: 0 +- name: An independent senior household cannot qualify a senior claimed elsewhere in another household + period: 2025 + input: + people: + independent_senior: + age: 65 + is_tax_unit_head: true + is_tax_unit_spouse: false + medicare_enrolled: false + claimed_as_dependent_on_another_return: false + other_medical_expenses: 28_000 + claimed_senior: + age: 65 + is_tax_unit_head: true + is_tax_unit_spouse: false + medicare_enrolled: false + claimed_as_dependent_on_another_return: true + child: + age: 17 + is_tax_unit_head: false + is_tax_unit_spouse: false + is_tax_unit_dependent: true + medicare_enrolled: false + other_medical_expenses: 28_000 + tax_units: + independent_unit: + members: [independent_senior] + filing_status: SINGLE + self_employed_health_insurance_ald: 0 + claimed_unit: + members: [claimed_senior, child] + filing_status: SINGLE + self_employed_health_insurance_ald: 0 + households: + independent_household: + members: [independent_senior] + state_code: NM + claimed_household: + members: [claimed_senior, child] + state_code: NM + output: + itemized_medical_expenses: [28_000, 28_000] + itemized_medical_expenses_applied_exclusion: [0, 0] + head_is_dependent_elsewhere: [false, true] + head_or_spouse_is_dependent_elsewhere: [false, true] + # NMSA 7-2-18.13(A): age 65 and 28,000 of costs are insufficient + # when the SINGLE filer is a dependent of another taxpayer. + nm_medical_expense_credit: [2_800, 0] + +- name: Joint return where only one spouse is a dependent of another taxpayer (the claimable spouse is the head) + period: 2023 + input: + people: + person_a: + age: 66 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 66 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + itemized_medical_expenses: 28_000 + households: + household: + members: [person_a, person_b] + state_code: NM + output: + # A spouse who is not a dependent may still qualify (PIT-RC instructions). + nm_medical_expense_credit: 2_800 + +- name: Joint return where only one spouse is a dependent of another taxpayer (the claimable spouse is the spouse) + period: 2023 + input: + people: + person_b: + age: 66 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 66 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + itemized_medical_expenses: 28_000 + households: + household: + members: [person_b, person_a] + state_code: NM + output: + # A spouse who is not a dependent may still qualify (PIT-RC instructions). + nm_medical_expense_credit: 2_800 + +- name: Joint return where both spouses are a dependent of another taxpayer + period: 2023 + input: + people: + person_a: + age: 66 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 66 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + itemized_medical_expenses: 28_000 + households: + household: + members: [person_a, person_b] + state_code: NM + output: + nm_medical_expense_credit: 0 + +- name: Joint return where only the claimable spouse is 65 or older (the claimable spouse is the head) + period: 2023 + input: + people: + person_a: + age: 70 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 60 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + itemized_medical_expenses: 28_000 + households: + household: + members: [person_a, person_b] + state_code: NM + output: + # NMSA 7-2-18.13(A): the taxpayer who is 65 or older must also not be a dependent. + nm_medical_expense_credit: 0 + +- name: Joint return where only the claimable spouse is 65 or older (the claimable spouse is the spouse) + period: 2023 + input: + people: + person_b: + age: 60 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 70 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + itemized_medical_expenses: 28_000 + households: + household: + members: [person_b, person_a] + state_code: NM + output: + # NMSA 7-2-18.13(A): the taxpayer who is 65 or older must also not be a dependent. + nm_medical_expense_credit: 0 + +- name: Joint return where the spouse who is not claimable is 65 or older (the claimable spouse is the head) + period: 2023 + input: + people: + person_a: + age: 60 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 70 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + itemized_medical_expenses: 28_000 + households: + household: + members: [person_a, person_b] + state_code: NM + output: + nm_medical_expense_credit: 2_800 + +- name: Joint return where the spouse who is not claimable is 65 or older (the claimable spouse is the spouse) + period: 2023 + input: + people: + person_b: + age: 70 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 60 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + itemized_medical_expenses: 28_000 + households: + household: + members: [person_b, person_a] + state_code: NM + output: + nm_medical_expense_credit: 2_800 + - name: New Mexico supplied subtotal and SE premiums do not create senior credit eligibility period: 2025 input: state_code: NM age: 65 medicare_enrolled: false - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false itemized_medical_expenses: 27_999 health_insurance_premiums: 6_000 self_employed_health_insurance_ald: 1_000 output: + # Supplied subtotal: 27,999 + 0 applied exclusion < 28,000, so no credit. itemized_medical_expenses: 27_999 itemized_medical_expenses_excluded_premiums: 1_000 itemized_medical_expenses_applied_exclusion: 0 nm_medical_expense_credit: 0 nm_medical_expense_exemption: 0 + +- name: Independent aged spouse qualifies with computed medical expenses after the federal premium exclusion (claimable spouse is head) + period: 2025 + input: + people: + claimable_spouse: + age: 60 + medicare_enrolled: false + claimable_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + independent_spouse: + age: 70 + medicare_enrolled: false + claimable_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + health_insurance_premiums: 6_000 + other_medical_expenses: 22_000 + tax_units: + tax_unit: + members: [claimable_spouse, independent_spouse] + filing_status: JOINT + self_employed_health_insurance_ald: 1_000 + households: + household: + members: [claimable_spouse, independent_spouse] + state_code: NM + output: + # Federal: 6,000 + 22,000 - 1,000 = 27,000; NM restores 1,000 to reach 28,000. + # The independent spouse is 70 >= 65, so the joint credit is 2,800 / 1 = 2,800. + itemized_medical_expenses: 27_000 + itemized_medical_expenses_applied_exclusion: 1_000 + nm_medical_expense_credit: 2_800 + +- name: Independent aged spouse qualifies with computed medical expenses after the federal premium exclusion (claimable spouse is spouse) + period: 2025 + input: + people: + independent_spouse: + age: 70 + medicare_enrolled: false + claimable_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + health_insurance_premiums: 6_000 + other_medical_expenses: 22_000 + claimable_spouse: + age: 60 + medicare_enrolled: false + claimable_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [independent_spouse, claimable_spouse] + filing_status: JOINT + self_employed_health_insurance_ald: 1_000 + households: + household: + members: [independent_spouse, claimable_spouse] + state_code: NM + output: + # Federal: 6,000 + 22,000 - 1,000 = 27,000; NM restores 1,000 to reach 28,000. + # The independent spouse is 70 >= 65, so the joint credit is 2,800 / 1 = 2,800. + itemized_medical_expenses: 27_000 + itemized_medical_expenses_applied_exclusion: 1_000 + nm_medical_expense_credit: 2_800 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/deductions/certain_dependents/nm_deduction_for_certain_dependents_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/deductions/certain_dependents/nm_deduction_for_certain_dependents_eligible.yaml index 6f65f043918..bcb29cf3062 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/deductions/certain_dependents/nm_deduction_for_certain_dependents_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/deductions/certain_dependents/nm_deduction_for_certain_dependents_eligible.yaml @@ -3,7 +3,7 @@ input: exemptions: 0 state_code: NM - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_deduction_for_certain_dependents_eligible: true @@ -12,7 +12,7 @@ input: exemptions: 1 state_code: NM - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_deduction_for_certain_dependents_eligible: false @@ -22,6 +22,86 @@ input: exemptions: 0 state_code: NM - head_is_dependent_elsewhere: true + claimed_as_dependent_on_another_return: true + output: + nm_deduction_for_certain_dependents_eligible: false + +- name: Joint return where only one spouse is a dependent of another taxpayer (the claimable spouse is the head) + period: 2021 + input: + people: + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + exemptions: 0 + households: + household: + members: [person_a, person_b] + state_code: NM + output: + # A spouse who is not a dependent may still qualify (PIT-RC instructions). + nm_deduction_for_certain_dependents_eligible: true + +- name: Joint return where only one spouse is a dependent of another taxpayer (the claimable spouse is the spouse) + period: 2021 + input: + people: + person_b: + age: 30 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + exemptions: 0 + households: + household: + members: [person_b, person_a] + state_code: NM + output: + # A spouse who is not a dependent may still qualify (PIT-RC instructions). + nm_deduction_for_certain_dependents_eligible: true + +- name: Joint return where both spouses are a dependent of another taxpayer + period: 2021 + input: + people: + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + exemptions: 0 + households: + household: + members: [person_a, person_b] + state_code: NM output: nm_deduction_for_certain_dependents_eligible: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/rebates/2021_income/nm_2021_income_rebate.yaml b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/rebates/2021_income/nm_2021_income_rebate.yaml index 72c2bb91a67..3a1c22772bc 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/rebates/2021_income/nm_2021_income_rebate.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/rebates/2021_income/nm_2021_income_rebate.yaml @@ -4,7 +4,7 @@ state_code: NM adjusted_gross_income: 74_999 filing_status: SINGLE - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_2021_income_rebate: 250 @@ -14,7 +14,7 @@ state_code: NM adjusted_gross_income: 75_000 filing_status: SEPARATE - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_2021_income_rebate: 0 @@ -24,7 +24,7 @@ state_code: NM adjusted_gross_income: 149_999 filing_status: JOINT - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_2021_income_rebate: 500 @@ -34,7 +34,7 @@ state_code: NM adjusted_gross_income: 150_000 filing_status: SURVIVING_SPOUSE - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_2021_income_rebate: 0 @@ -44,6 +44,86 @@ state_code: NM adjusted_gross_income: 149_999 filing_status: JOINT - head_is_dependent_elsewhere: true + claimed_as_dependent_on_another_return: true + output: + nm_2021_income_rebate: 0 + +- name: Joint return where only one spouse is a dependent of another taxpayer (the claimable spouse is the head) + period: 2021 + input: + people: + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + adjusted_gross_income: 50_000 + households: + household: + members: [person_a, person_b] + state_code: NM + output: + # A spouse who is not a dependent may still qualify (PIT-RC instructions). + nm_2021_income_rebate: 500 + +- name: Joint return where only one spouse is a dependent of another taxpayer (the claimable spouse is the spouse) + period: 2021 + input: + people: + person_b: + age: 30 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + adjusted_gross_income: 50_000 + households: + household: + members: [person_b, person_a] + state_code: NM + output: + # A spouse who is not a dependent may still qualify (PIT-RC instructions). + nm_2021_income_rebate: 500 + +- name: Joint return where both spouses are a dependent of another taxpayer + period: 2021 + input: + people: + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + adjusted_gross_income: 50_000 + households: + household: + members: [person_a, person_b] + state_code: NM output: nm_2021_income_rebate: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/rebates/2021_income/nm_additional_2021_income_rebate.yaml b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/rebates/2021_income/nm_additional_2021_income_rebate.yaml index ddf2f6150d1..91a64599414 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/rebates/2021_income/nm_additional_2021_income_rebate.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/rebates/2021_income/nm_additional_2021_income_rebate.yaml @@ -3,7 +3,7 @@ input: state_code: NM filing_status: JOINT - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_additional_2021_income_rebate: 1_000 @@ -12,7 +12,7 @@ input: state_code: NM filing_status: SINGLE - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_additional_2021_income_rebate: 500 @@ -21,7 +21,7 @@ input: state_code: NM filing_status: SEPARATE - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_additional_2021_income_rebate: 500 @@ -30,7 +30,7 @@ input: state_code: NM filing_status: SURVIVING_SPOUSE - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_additional_2021_income_rebate: 1_000 @@ -39,7 +39,7 @@ input: state_code: NM filing_status: HEAD_OF_HOUSEHOLD - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_additional_2021_income_rebate: 1_000 @@ -48,6 +48,83 @@ input: state_code: NM filing_status: SEPARATE - head_is_dependent_elsewhere: true + claimed_as_dependent_on_another_return: true + output: + nm_additional_2021_income_rebate: 0 + +- name: Joint return where only one spouse is a dependent of another taxpayer (the claimable spouse is the head) + period: 2021 + input: + people: + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: NM + output: + # A spouse who is not a dependent may still qualify (PIT-RC instructions). + nm_additional_2021_income_rebate: 1_000 + +- name: Joint return where only one spouse is a dependent of another taxpayer (the claimable spouse is the spouse) + period: 2021 + input: + people: + person_b: + age: 30 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + households: + household: + members: [person_b, person_a] + state_code: NM + output: + # A spouse who is not a dependent may still qualify (PIT-RC instructions). + nm_additional_2021_income_rebate: 1_000 + +- name: Joint return where both spouses are a dependent of another taxpayer + period: 2021 + input: + people: + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: NM output: nm_additional_2021_income_rebate: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/rebates/2021_income/nm_supplemental_2021_income_rebate.yaml b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/rebates/2021_income/nm_supplemental_2021_income_rebate.yaml index ac64cc9f434..0b1b8565f7a 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/rebates/2021_income/nm_supplemental_2021_income_rebate.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/rebates/2021_income/nm_supplemental_2021_income_rebate.yaml @@ -3,7 +3,7 @@ input: state_code: NM filing_status: JOINT - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_supplemental_2021_income_rebate: 1_000 @@ -12,7 +12,7 @@ input: state_code: NM filing_status: SINGLE - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_supplemental_2021_income_rebate: 500 @@ -21,7 +21,7 @@ input: state_code: NM filing_status: SEPARATE - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_supplemental_2021_income_rebate: 500 @@ -30,7 +30,7 @@ input: state_code: NM filing_status: SURVIVING_SPOUSE - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_supplemental_2021_income_rebate: 1_000 @@ -39,7 +39,7 @@ input: state_code: NM filing_status: HEAD_OF_HOUSEHOLD - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_supplemental_2021_income_rebate: 1_000 @@ -48,6 +48,83 @@ input: state_code: NM filing_status: HEAD_OF_HOUSEHOLD - head_is_dependent_elsewhere: true + claimed_as_dependent_on_another_return: true + output: + nm_supplemental_2021_income_rebate: 0 + +- name: Joint return where only one spouse is a dependent of another taxpayer (the claimable spouse is the head) + period: 2021 + input: + people: + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: NM + output: + # A spouse who is not a dependent may still qualify (PIT-RC instructions). + nm_supplemental_2021_income_rebate: 1_000 + +- name: Joint return where only one spouse is a dependent of another taxpayer (the claimable spouse is the spouse) + period: 2021 + input: + people: + person_b: + age: 30 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + households: + household: + members: [person_b, person_a] + state_code: NM + output: + # A spouse who is not a dependent may still qualify (PIT-RC instructions). + nm_supplemental_2021_income_rebate: 1_000 + +- name: Joint return where both spouses are a dependent of another taxpayer + period: 2021 + input: + people: + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: NM output: nm_supplemental_2021_income_rebate: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/taxable_income/deductions/ny_standard_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/taxable_income/deductions/ny_standard_deduction.yaml new file mode 100644 index 00000000000..cd98af8b0dc --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/taxable_income/deductions/ny_standard_deduction.yaml @@ -0,0 +1,94 @@ +- name: 2025 single filer who can be claimed as a dependent + period: 2025 + input: + state_code: NY + filing_status: SINGLE + claimed_as_dependent_on_another_return: true + output: + ny_standard_deduction: 3_100 + +- name: 2025 single filer who cannot be claimed as a dependent + period: 2025 + input: + state_code: NY + filing_status: SINGLE + claimed_as_dependent_on_another_return: false + output: + ny_standard_deduction: 8_000 + +- name: 2025 head of household who can be claimed as a dependent keeps the regular amount + period: 2025 + input: + state_code: NY + filing_status: HEAD_OF_HOUSEHOLD + claimed_as_dependent_on_another_return: true + output: + # The IT-201 table gives the dependent amount only to filing status 1. + ny_standard_deduction: 11_200 + +- name: 2025 separate filer who can be claimed as a dependent keeps the regular amount + period: 2025 + input: + state_code: NY + filing_status: SEPARATE + claimed_as_dependent_on_another_return: true + output: + ny_standard_deduction: 8_000 + +- name: 2025 joint return where only one spouse can be claimed (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 20 + employment_income: 8_000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + employment_income: 8_000 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: NY + output: + # The IT-201 table gives married filing jointly $16,050 whether or not + # item C is marked. + ny_standard_deduction: 16_050 + +- name: 2025 joint return where only one spouse can be claimed (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 20 + employment_income: 8_000 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + employment_income: 8_000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + households: + household: + members: [person_b, person_a] + state_code: NY + output: + # The IT-201 table gives married filing jointly $16,050 whether or not + # item C is marked. + ny_standard_deduction: 16_050 diff --git a/policyengine_us/tests/policy/baseline/gov/states/oh/tax/income/exemptions/personal/oh_personal_exemptions_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/oh/tax/income/exemptions/personal/oh_personal_exemptions_eligible.yaml index 97f70fe2376..d9fe76117ae 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/oh/tax/income/exemptions/personal/oh_personal_exemptions_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/oh/tax/income/exemptions/personal/oh_personal_exemptions_eligible.yaml @@ -27,3 +27,14 @@ is_tax_unit_dependent: true output: oh_personal_exemptions_eligible_person: true + +- name: Ohio personal exemption excludes a claimable but unclaimed filer + period: 2025 + input: + state_code: OH + is_tax_unit_head_or_spouse: true + is_tax_unit_dependent: false + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + output: + oh_personal_exemptions_eligible_person: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/or/tax/income/deductions/standard/or_standard_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/or/tax/income/deductions/standard/or_standard_deduction.yaml index 6c2182a4783..c040dee6619 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/or/tax/income/deductions/standard/or_standard_deduction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/or/tax/income/deductions/standard/or_standard_deduction.yaml @@ -68,7 +68,7 @@ state_code: OR filing_status: JOINT age_head: 35 - head_is_dependent_elsewhere: true + claimed_as_dependent_on_another_return: true tax_unit_earned_income: 600 output: # $1100 base @@ -80,7 +80,7 @@ state_code: OR filing_status: SINGLE age_head: 35 - head_is_dependent_elsewhere: true + claimed_as_dependent_on_another_return: true tax_unit_earned_income: 1_000 output: # $1000 income + $350 addition, below the $2,350 normal standard deduction cap. @@ -92,7 +92,7 @@ state_code: OR filing_status: SINGLE age: 35 - head_is_dependent_elsewhere: true + claimed_as_dependent_on_another_return: true tax_unit_earned_income: 1_000 blind_head: true output: @@ -137,3 +137,79 @@ output: # $2,835 base + $1,200 aged or_standard_deduction: 4_035 + +- name: 2025 joint return limited to the dependent amount when either spouse can be claimed (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 35 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 35 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + tax_unit_earned_income: 3_000 + households: + household: + members: [person_a, person_b] + state_code: OR + output: + # max($1,350, $3,000 + $450) = $3,450, below the $5,670 joint amount. + or_standard_deduction: 3_450 + +- name: 2025 joint return limited to the dependent amount when either spouse can be claimed (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 35 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 35 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + tax_unit_earned_income: 3_000 + households: + household: + members: [person_b, person_a] + state_code: OR + output: + # max($1,350, $3,000 + $450) = $3,450, below the $5,670 joint amount. + or_standard_deduction: 3_450 + +- name: 2025 dependent filer with self-employment income uses earned income net of the self-employment tax deduction + period: 2025 + absolute_error_margin: 0.01 + input: + people: + head: + age: 20 + self_employment_income: 1_500 + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [head] + filing_status: SINGLE + households: + household: + members: [head] + state_code: OR + output: + # $1,500 - $1,500 x 0.9235 x 15.3% / 2 = $1,394.03; plus $450 = $1,844.03, + # above the $1,350 minimum and below the $2,835 single amount. + or_standard_deduction: 1844.03 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ut/tax/property/homeowner_renter_relief/ut_homeowner_renter_relief_pre_one_claimant_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/property/homeowner_renter_relief/ut_homeowner_renter_relief_pre_one_claimant_eligible.yaml new file mode 100644 index 00000000000..64ee21b5111 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/property/homeowner_renter_relief/ut_homeowner_renter_relief_pre_one_claimant_eligible.yaml @@ -0,0 +1,21 @@ +- name: Utah property relief retains an unclaimed claimable filer + period: 2025 + input: + state_code: UT + age: 66 + rent: 12_000 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + output: + ut_homeowner_renter_relief_pre_one_claimant_eligible: true + +- name: Utah property relief excludes an actually claimed filer + period: 2025 + input: + state_code: UT + age: 66 + rent: 12_000 + claimed_as_dependent_on_another_return: true + claimable_as_dependent_on_another_return: true + output: + ut_homeowner_renter_relief_pre_one_claimant_eligible: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/va/tax/income/credits/low_income_credit/va_low_income_tax_credit_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/va/tax/income/credits/low_income_credit/va_low_income_tax_credit_eligible.yaml index 5584f3c0288..b5cf4a21af1 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/va/tax/income/credits/low_income_credit/va_low_income_tax_credit_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/va/tax/income/credits/low_income_credit/va_low_income_tax_credit_eligible.yaml @@ -2,7 +2,7 @@ period: 2021 input: state_code: VA - head_is_dependent_elsewhere: true + claimed_as_dependent_on_another_return: true va_aged_blind_exemption: 0 va_age_deduction: 0 va_military_benefit_subtraction: 0 @@ -14,7 +14,7 @@ period: 2021 input: state_code: VA - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false va_aged_blind_exemption: 1_600 va_age_deduction: 12_000 va_military_benefit_subtraction: 0 @@ -26,10 +26,60 @@ period: 2021 input: state_code: VA - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false va_aged_blind_exemption: 0 va_age_deduction: 0 va_military_benefit_subtraction: 0 va_federal_state_employees_subtraction: 0 output: va_low_income_tax_credit_eligible: true + +- name: Joint return is ineligible when either spouse is claimed as a dependent (the claimable spouse is the head) + period: 2021 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: VA + output: + va_low_income_tax_credit_eligible: false + +- name: Joint return is ineligible when either spouse is claimed as a dependent (the claimable spouse is the spouse) + period: 2021 + input: + people: + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + households: + household: + members: [person_b, person_a] + state_code: VA + output: + va_low_income_tax_credit_eligible: false diff --git a/policyengine_us/tests/policy/baseline/household/demographic/person/claimable_as_dependent_on_another_return.yaml b/policyengine_us/tests/policy/baseline/household/demographic/person/claimable_as_dependent_on_another_return.yaml new file mode 100644 index 00000000000..2530e578832 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/household/demographic/person/claimable_as_dependent_on_another_return.yaml @@ -0,0 +1,34 @@ +- name: An existing actual claim supplies the default claimability + period: 2025 + input: + claimed_as_dependent_on_another_return: true + output: + claimable_as_dependent_on_another_return: true + +- name: An existing false claim input preserves default independence + period: 2025 + input: + claimed_as_dependent_on_another_return: false + output: + claimable_as_dependent_on_another_return: false + +- name: No claim inputs preserves default independence + period: 2025 + input: + age: 26 + output: + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: false + +- name: A parent can claim the filer without making or expecting a claim + period: 2025 + input: + age: 26 + employment_income: 2_000 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + output: + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + basic_standard_deduction: 2_450 + eitc_demographic_eligible: false diff --git a/policyengine_us/tests/policy/baseline/household/demographic/person/is_dependent_elsewhere_without_filing_exception.yaml b/policyengine_us/tests/policy/baseline/household/demographic/person/is_dependent_elsewhere_without_filing_exception.yaml new file mode 100644 index 00000000000..4164a45064a --- /dev/null +++ b/policyengine_us/tests/policy/baseline/household/demographic/person/is_dependent_elsewhere_without_filing_exception.yaml @@ -0,0 +1,36 @@ +- name: Unclaimed but claimable filer fails the dependent taxpayer test + period: 2025 + input: + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + output: + dependent_claimant_filing_exception: false + is_dependent_elsewhere_without_filing_exception: true + +- name: Nonrequired refund-only claimant restores dependent taxpayer eligibility + period: 2025 + input: + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + output: + # Publication 501 PDF p.11, Dependent Taxpayer Test exception. + is_dependent_elsewhere_without_filing_exception: false + claimable_as_dependent_on_another_return: true + +- name: Nonrequired claimant filing no return restores dependent taxpayer eligibility + period: 2025 + input: + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + output: + # Publication 596 PDF p.18 includes the nonrequired claimant who files no return. + is_dependent_elsewhere_without_filing_exception: false + claimable_as_dependent_on_another_return: true + +- name: The filing exception does not create claimability + period: 2025 + input: + claimable_as_dependent_on_another_return: false + dependent_claimant_filing_exception: true + output: + is_dependent_elsewhere_without_filing_exception: false diff --git a/policyengine_us/tests/policy/baseline/household/demographic/tax_unit/every_filer_is_dependent_elsewhere.yaml b/policyengine_us/tests/policy/baseline/household/demographic/tax_unit/every_filer_is_dependent_elsewhere.yaml new file mode 100644 index 00000000000..2b934fda74a --- /dev/null +++ b/policyengine_us/tests/policy/baseline/household/demographic/tax_unit/every_filer_is_dependent_elsewhere.yaml @@ -0,0 +1,89 @@ +- name: Only the head is claimed elsewhere + period: 2025 + input: + people: + head: + age: 20 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimed_as_dependent_on_another_return: true + spouse: + age: 20 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimed_as_dependent_on_another_return: false + tax_units: + tax_unit: + members: [head, spouse] + output: + every_filer_is_dependent_elsewhere: false + +- name: Only the spouse is claimed elsewhere + period: 2025 + input: + people: + head: + age: 20 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimed_as_dependent_on_another_return: false + spouse: + age: 20 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [head, spouse] + output: + every_filer_is_dependent_elsewhere: false + +- name: Both spouses are claimed elsewhere + period: 2025 + input: + people: + head: + age: 20 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimed_as_dependent_on_another_return: true + spouse: + age: 20 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [head, spouse] + output: + every_filer_is_dependent_elsewhere: true + +- name: Single filer claimed elsewhere + period: 2025 + input: + people: + head: + age: 19 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [head] + output: + every_filer_is_dependent_elsewhere: true + +- name: A tax unit with neither a head nor a spouse has no filer claimed elsewhere + period: 2025 + input: + people: + child: + age: 10 + is_tax_unit_head: false + is_tax_unit_spouse: false + claimed_as_dependent_on_another_return: false + tax_units: + tax_unit: + members: [child] + output: + every_filer_is_dependent_elsewhere: false diff --git a/policyengine_us/tests/policy/baseline/household/demographic/tax_unit/head_or_spouse_is_dependent_elsewhere.yaml b/policyengine_us/tests/policy/baseline/household/demographic/tax_unit/head_or_spouse_is_dependent_elsewhere.yaml new file mode 100644 index 00000000000..80029885451 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/household/demographic/tax_unit/head_or_spouse_is_dependent_elsewhere.yaml @@ -0,0 +1,114 @@ +- name: Head is claimed elsewhere + period: 2025 + input: + people: + head: + age: 20 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimed_as_dependent_on_another_return: true + spouse: + age: 20 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimed_as_dependent_on_another_return: false + tax_units: + tax_unit: + members: [head, spouse] + output: + head_or_spouse_is_dependent_elsewhere: true + +- name: Spouse is claimed elsewhere + period: 2025 + input: + people: + head: + age: 20 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimed_as_dependent_on_another_return: false + spouse: + age: 20 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [head, spouse] + output: + head_or_spouse_is_dependent_elsewhere: true + +- name: Both spouses are claimed elsewhere + period: 2025 + input: + people: + head: + age: 20 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimed_as_dependent_on_another_return: true + spouse: + age: 20 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [head, spouse] + output: + head_or_spouse_is_dependent_elsewhere: true + +- name: Neither spouse is claimed elsewhere + period: 2025 + input: + people: + head: + age: 20 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimed_as_dependent_on_another_return: false + spouse: + age: 20 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimed_as_dependent_on_another_return: false + tax_units: + tax_unit: + members: [head, spouse] + output: + head_or_spouse_is_dependent_elsewhere: false + +- name: A dependent claimed elsewhere is not a filer + period: 2025 + input: + people: + head: + age: 40 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimed_as_dependent_on_another_return: false + child: + age: 10 + is_tax_unit_head: false + is_tax_unit_spouse: false + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [head, child] + output: + head_or_spouse_is_dependent_elsewhere: false + +- name: Single filer claimed elsewhere + period: 2025 + input: + people: + head: + age: 19 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [head] + output: + head_or_spouse_is_dependent_elsewhere: true diff --git a/policyengine_us/tests/policy/baseline/household/demographic/tax_unit/head_or_spouse_is_dependent_elsewhere_without_filing_exception.yaml b/policyengine_us/tests/policy/baseline/household/demographic/tax_unit/head_or_spouse_is_dependent_elsewhere_without_filing_exception.yaml new file mode 100644 index 00000000000..8b9e439d173 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/household/demographic/tax_unit/head_or_spouse_is_dependent_elsewhere_without_filing_exception.yaml @@ -0,0 +1,119 @@ +- name: Claimable unclaimed head prevents claiming the return's own dependents + period: 2025 + input: + people: + person_a: + is_tax_unit_head: true + is_tax_unit_spouse: false + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + person_b: + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: false + tax_units: + tax_unit: + members: [person_a, person_b] + output: + head_or_spouse_is_dependent_elsewhere: true + head_or_spouse_is_dependent_elsewhere_without_filing_exception: true + +- name: Claimable unclaimed spouse prevents claiming the return's own dependents + period: 2025 + input: + people: + person_b: + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: false + person_a: + is_tax_unit_head: false + is_tax_unit_spouse: true + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [person_b, person_a] + output: + head_or_spouse_is_dependent_elsewhere: true + head_or_spouse_is_dependent_elsewhere_without_filing_exception: true + +- name: The head's nonrequired claimant filing exception restores own-dependent eligibility + period: 2025 + input: + people: + person_a: + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + person_b: + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: false + tax_units: + tax_unit: + members: [person_a, person_b] + output: + head_or_spouse_is_dependent_elsewhere: true + head_or_spouse_is_dependent_elsewhere_without_filing_exception: false + +- name: The spouse's nonrequired claimant filing exception restores own-dependent eligibility + period: 2025 + input: + people: + person_b: + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: false + person_a: + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + tax_units: + tax_unit: + members: [person_b, person_a] + output: + head_or_spouse_is_dependent_elsewhere: true + head_or_spouse_is_dependent_elsewhere_without_filing_exception: false + +- name: One exception is insufficient when the other spouse has an ordinary claimant + period: 2025 + input: + people: + head: + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + spouse: + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: false + tax_units: + tax_unit: + members: [head, spouse] + output: + head_or_spouse_is_dependent_elsewhere_without_filing_exception: true + +- name: A claimable child does not apply the dependent taxpayer test to independent filers + period: 2025 + input: + people: + head: + age: 40 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: false + child: + age: 10 + is_tax_unit_head: false + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [head, child] + output: + head_or_spouse_is_dependent_elsewhere_without_filing_exception: false diff --git a/policyengine_us/tests/policy/baseline/household/demographic/tax_unit/head_spouse_count_not_dependent_elsewhere.yaml b/policyengine_us/tests/policy/baseline/household/demographic/tax_unit/head_spouse_count_not_dependent_elsewhere.yaml new file mode 100644 index 00000000000..ac5381a1b4b --- /dev/null +++ b/policyengine_us/tests/policy/baseline/household/demographic/tax_unit/head_spouse_count_not_dependent_elsewhere.yaml @@ -0,0 +1,114 @@ +- name: Head is claimed elsewhere + period: 2025 + input: + people: + head: + age: 20 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimed_as_dependent_on_another_return: true + spouse: + age: 20 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimed_as_dependent_on_another_return: false + tax_units: + tax_unit: + members: [head, spouse] + output: + head_spouse_count_not_dependent_elsewhere: 1 + +- name: Spouse is claimed elsewhere + period: 2025 + input: + people: + head: + age: 20 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimed_as_dependent_on_another_return: false + spouse: + age: 20 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [head, spouse] + output: + head_spouse_count_not_dependent_elsewhere: 1 + +- name: Both spouses are claimed elsewhere + period: 2025 + input: + people: + head: + age: 20 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimed_as_dependent_on_another_return: true + spouse: + age: 20 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [head, spouse] + output: + head_spouse_count_not_dependent_elsewhere: 0 + +- name: Neither spouse is claimed elsewhere + period: 2025 + input: + people: + head: + age: 20 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimed_as_dependent_on_another_return: false + spouse: + age: 20 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimed_as_dependent_on_another_return: false + tax_units: + tax_unit: + members: [head, spouse] + output: + head_spouse_count_not_dependent_elsewhere: 2 + +- name: A dependent claimed elsewhere is not a filer + period: 2025 + input: + people: + head: + age: 40 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimed_as_dependent_on_another_return: false + child: + age: 10 + is_tax_unit_head: false + is_tax_unit_spouse: false + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [head, child] + output: + head_spouse_count_not_dependent_elsewhere: 1 + +- name: Single filer claimed elsewhere + period: 2025 + input: + people: + head: + age: 19 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [head] + output: + head_spouse_count_not_dependent_elsewhere: 0 diff --git a/policyengine_us/tests/policy/contrib/congress/watca.yaml b/policyengine_us/tests/policy/contrib/congress/watca.yaml index 500ed7e863b..2b40103ff7f 100644 --- a/policyengine_us/tests/policy/contrib/congress/watca.yaml +++ b/policyengine_us/tests/policy/contrib/congress/watca.yaml @@ -440,7 +440,7 @@ gov.contrib.congress.watca.in_effect: true adjusted_gross_income: 30_000 filing_status: SINGLE - head_is_dependent_elsewhere: true + claimed_as_dependent_on_another_return: true output: # Even though income is below limit, not eligible because claimed as dependent watca_alternative_tax_eligible: false @@ -450,9 +450,18 @@ reforms: policyengine_us.reforms.congress.watca.working_americans_tax_cut_act.watca_reform_object input: gov.contrib.congress.watca.in_effect: true - adjusted_gross_income: 30_000 - filing_status: JOINT - spouse_is_dependent_elsewhere: true + people: + head: + is_tax_unit_head: true + claimed_as_dependent_on_another_return: false + spouse: + is_tax_unit_spouse: true + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [head, spouse] + adjusted_gross_income: 30_000 + filing_status: JOINT output: watca_alternative_tax_eligible: false @@ -529,10 +538,18 @@ reforms: policyengine_us.reforms.congress.watca.working_americans_tax_cut_act.watca_reform_object input: gov.contrib.congress.watca.in_effect: true - adjusted_gross_income: 30_000 - filing_status: JOINT - head_is_dependent_elsewhere: true - spouse_is_dependent_elsewhere: true + people: + head: + is_tax_unit_head: true + claimed_as_dependent_on_another_return: true + spouse: + is_tax_unit_spouse: true + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [head, spouse] + adjusted_gross_income: 30_000 + filing_status: JOINT output: watca_alternative_tax_eligible: false diff --git a/policyengine_us/tests/policy/contrib/states/ct/hb5114/ct_hb5114.yaml b/policyengine_us/tests/policy/contrib/states/ct/hb5114/ct_hb5114.yaml index 9557ae1102e..bc07699ca08 100644 --- a/policyengine_us/tests/policy/contrib/states/ct/hb5114/ct_hb5114.yaml +++ b/policyengine_us/tests/policy/contrib/states/ct/hb5114/ct_hb5114.yaml @@ -427,11 +427,11 @@ age: 20 employment_income: 30_000 rent: 12_000 + claimed_as_dependent_on_another_return: true tax_units: tax_unit: members: [person1] filing_status: SINGLE - head_is_dependent_elsewhere: true households: household: members: [person1] diff --git a/policyengine_us/variables/gov/irs/credits/earned_income/eligibility/eitc_demographic_eligible.py b/policyengine_us/variables/gov/irs/credits/earned_income/eligibility/eitc_demographic_eligible.py index 2338356763e..9cb742b764f 100644 --- a/policyengine_us/variables/gov/irs/credits/earned_income/eligibility/eitc_demographic_eligible.py +++ b/policyengine_us/variables/gov/irs/credits/earned_income/eligibility/eitc_demographic_eligible.py @@ -6,7 +6,10 @@ class eitc_demographic_eligible(Variable): entity = TaxUnit label = "Meets demographic eligibility for EITC" definition_period = YEAR - reference = "https://www.law.cornell.edu/uscode/text/26/32#c_1_A_ii_II" + reference = ( + "https://www.law.cornell.edu/uscode/text/26/32#c_1_A_ii", + "https://www.irs.gov/pub/irs-prior/p596--2025.pdf#page=18", + ) def formula(tax_unit, period, parameters): person = tax_unit.members @@ -27,4 +30,17 @@ def formula(tax_unit, period, parameters): # IRC § 32(c)(1)(A)(ii)(II) applies the age test to the filer or, # on a joint return, either spouse — never to dependents. is_filer_or_spouse = ~person("is_tax_unit_dependent", period) - return has_child | tax_unit.any(meets_age_requirements & is_filer_or_spouse) + # IRC § 32(c)(1)(A)(ii)(III): without a qualifying child, the filer + # must not be a dependent of another taxpayer; on a joint return, + # "neither you nor your spouse can be claimed as a dependent by another + # person" (IRS Publication 596). A nonrequired claimant who files + # no return or only for a withholding/estimated-payment refund is + # excepted, without changing the standard-deduction limitation. + dependent_elsewhere = tax_unit( + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period + ) + childless_eligible = ( + tax_unit.any(meets_age_requirements & is_filer_or_spouse) + & ~dependent_elsewhere + ) + return has_child | childless_eligible diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/deductions/standard_deduction/basic_standard_deduction.py b/policyengine_us/variables/gov/irs/income/taxable_income/deductions/standard_deduction/basic_standard_deduction.py index e3d3745bdc5..952da61a05f 100644 --- a/policyengine_us/variables/gov/irs/income/taxable_income/deductions/standard_deduction/basic_standard_deduction.py +++ b/policyengine_us/variables/gov/irs/income/taxable_income/deductions/standard_deduction/basic_standard_deduction.py @@ -7,19 +7,35 @@ class basic_standard_deduction(Variable): label = "Basic standard deduction" definition_period = YEAR unit = USD - reference = "https://www.law.cornell.edu/uscode/text/26/63#c_2" + reference = ( + "https://www.law.cornell.edu/uscode/text/26/63#c_2", + "https://www.law.cornell.edu/uscode/text/26/63#c_5", + # Standard Deduction Worksheet for Dependents: "Use this worksheet + # only if someone can claim you, or your spouse if filing jointly, + # as a dependent." + "https://www.irs.gov/pub/irs-prior/i1040gi--2025.pdf#page=35", + ) def formula(tax_unit, period, parameters): p = parameters(period).gov.irs.deductions.standard filing_status = tax_unit("filing_status", period) separate_filer_itemizes = tax_unit("separate_filer_itemizes", period) - dependent_elsewhere = tax_unit("head_is_dependent_elsewhere", period) + # A joint return uses the dependent worksheet, with the couple's + # earned income and the joint amount as the cap, when either spouse + # can be claimed. + dependent_elsewhere = tax_unit("head_or_spouse_is_dependent_elsewhere", period) standard_deduction = p.amount[filing_status] + # Worksheet earned income: wages and self-employment income minus the + # deductible part of self-employment tax (Schedule 1, line 15). + worksheet_earned_income = max_( + tax_unit("tax_unit_earned_income", period) + - tax_unit("self_employment_tax_ald", period), + 0, + ) standard_deduction_if_dependent = min_( standard_deduction, max_( - p.dependent.additional_earned_income - + tax_unit("tax_unit_earned_income", period), + p.dependent.additional_earned_income + worksheet_earned_income, p.dependent.amount, ), ) diff --git a/policyengine_us/variables/gov/states/az/tax/income/exemptions/az_aged_exemption_eligible_person.py b/policyengine_us/variables/gov/states/az/tax/income/exemptions/az_aged_exemption_eligible_person.py index 6169e38bb5e..af27ae56bd4 100644 --- a/policyengine_us/variables/gov/states/az/tax/income/exemptions/az_aged_exemption_eligible_person.py +++ b/policyengine_us/variables/gov/states/az/tax/income/exemptions/az_aged_exemption_eligible_person.py @@ -8,6 +8,7 @@ class az_aged_exemption_eligible_person(Variable): unit = USD definition_period = YEAR defined_for = StateCode.AZ + reference = "https://azdor.gov/sites/default/files/document/FORMS_INDIVIDUAL_2025_140i.pdf#page=6" def formula(person, period, parameters): head = person("is_tax_unit_head", period) @@ -17,9 +18,6 @@ def formula(person, period, parameters): filing_status = tax_unit("az_filing_status", period) separate = filing_status == filing_status.possible_values.SEPARATE - dependent_head = tax_unit("head_is_dependent_elsewhere", period) - - dependent_spouse = tax_unit("spouse_is_dependent_elsewhere", period) - spouse_eligible_condition = ~dependent_spouse * ~separate - - return (head & ~dependent_head) | (spouse & spouse_eligible_condition) + # Form 140, Box 8, excludes each spouse only when actually claimed. + claimed_elsewhere = person("claimed_as_dependent_on_another_return", period) + return ~claimed_elsewhere & (head | (spouse & ~separate)) diff --git a/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit.py b/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit.py index c4f098d02f1..a1c5b5ff4aa 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit.py @@ -8,17 +8,22 @@ class hi_food_excise_credit(Variable): defined_for = StateCode.HI unit = USD definition_period = YEAR - reference = "https://files.hawaii.gov/tax/legal/hrs/hrs_235.pdf#page=44" + reference = ( + "https://files.hawaii.gov/tax/legal/hrs/hrs_235.pdf#page=50", + "https://files.hawaii.gov/tax/forms/2025/n311_i.pdf#page=1", + ) def formula(tax_unit, period, parameters): - # filer can not be a dependent on another return - dependent_elsewhere = tax_unit("head_is_dependent_elsewhere", period) - total_amount = add( - tax_unit, - period, - [ - "hi_food_excise_credit_minor_child_amount", - "hi_food_excise_exemption_amount", - ], + # HRS 235-55.85(a): only a taxpayer who cannot be claimed as a + # dependent may claim the credit, for each qualified exemption; the + # exemption amount excludes own ordinary dependents when either + # filer can be claimed without the Dependent Taxpayer Test exception. + # Minor children receiving public support count for this + # credit under 235-55.85(c), so they keep their amount unless every + # filer can be claimed. + every_filer_dependent = tax_unit("every_filer_is_dependent_elsewhere", period) + exemption_amount = tax_unit("hi_food_excise_exemption_amount", period) + minor_child_amount = tax_unit( + "hi_food_excise_credit_minor_child_amount", period ) - return ~dependent_elsewhere * total_amount + return exemption_amount + where(every_filer_dependent, 0, minor_child_amount) diff --git a/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit_minor_child_count.py b/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit_minor_child_count.py index ba06f9635a3..15cb4a0c8de 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit_minor_child_count.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit_minor_child_count.py @@ -7,6 +7,10 @@ class hi_food_excise_credit_minor_child_count(Variable): label = "Minor child's number for the Hawaii Food/Excise Tax Credit" definition_period = YEAR defined_for = StateCode.HI + reference = ( + "https://files.hawaii.gov/tax/forms/2022/n311_i.pdf#page=1", + "https://files.hawaii.gov/tax/forms/2025/n311_i.pdf#page=1", + ) def formula(tax_unit, period, parameters): p = parameters(period).gov.states.hi.tax.income.credits.food_excise_tax @@ -16,6 +20,13 @@ def formula(tax_unit, period, parameters): ) is_child = person("is_child", period) minor = person("age", period) < p.minor_child.age_threshold - eligible_minor_child = is_child & minor & public_support_over_half + # Form N-311 line 3 lists minor children who, among other tests, + # "Cannot be claimed as a dependent by another taxpayer"; the filers + # are listed on line 2. + claimable_elsewhere = person("claimable_as_dependent_on_another_return", period) + filer = person("is_tax_unit_head_or_spouse", period) + eligible_minor_child = ( + is_child & minor & public_support_over_half & ~claimable_elsewhere & ~filer + ) return tax_unit.sum(eligible_minor_child) diff --git a/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_exemption_amount.py b/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_exemption_amount.py index 1b4e22e61bc..09127fcd2c1 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_exemption_amount.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_exemption_amount.py @@ -7,13 +7,18 @@ class hi_food_excise_exemption_amount(Variable): label = "Exemption amount for Hawaii Food/Excise Tax Credit" definition_period = YEAR defined_for = StateCode.HI + reference = ( + "https://files.hawaii.gov/tax/legal/hrs/hrs_235.pdf#page=50", + "https://files.hawaii.gov/tax/forms/2022/n311_i.pdf#page=1", + "https://files.hawaii.gov/tax/forms/2025/n311_i.pdf#page=1", + "https://www.irs.gov/pub/irs-prior/p501--2025.pdf#page=11", + ) def formula(tax_unit, period, parameters): p = parameters(period).gov.states.hi.tax.income.credits.food_excise_tax income = tax_unit("adjusted_gross_income", period) - minor_child_include = p.minor_child.in_effect # Count exemptions as number of people # The legal code defines exemptions as Hawaii exemptions, # which normally include additional exemptions for aged and @@ -38,11 +43,43 @@ def formula(tax_unit, period, parameters): p.amount.surviving_spouse.calc(income), ], ) - exemptions = tax_unit("exemptions_count", period) + # Both 2022 and 2025 N-311 lines 2-3 exclude every person who can + # be claimed by another taxpayer, including children. In 2022, + # lines 8-10 price ordinary persons and publicly supported minors + # separately; 2025 lines 8-9 combine the two qualified counts. + externally_claimable = tax_unit.sum( + tax_unit.members("claimable_as_dependent_on_another_return", period) + ) + exemptions = max_( + tax_unit("exemptions_count", period) - externally_claimable, 0 + ) + minor_children = tax_unit("hi_food_excise_credit_minor_child_count", period) if p.minor_child.in_effect: - # Reduce number of exemptions by the number of minor children - minor_children = tax_unit("hi_food_excise_credit_minor_child_count", period) - claimable_exemptions = exemptions - minor_children + # Reduce number of exemptions by the number of minor children, + # who get a fixed amount instead. + claimable_exemptions = max_(exemptions - minor_children, 0) + minor_child_exemptions = 0 else: claimable_exemptions = exemptions - return claimable_exemptions * amount_per_exemption + minor_child_exemptions = minor_children + # Form N-311 counts only people who cannot be claimed as a dependent + # by another taxpayer. A return on which a filer can be claimed + # generally claims no dependents (IRS Publication 501, Dependent + # Taxpayer Test). Its filing exception restores ordinary dependent + # entries, but never a claimable person's own exemption. Publicly + # supported minors count independently under HRS 235-55.85(c). + dependent_taxpayer = tax_unit( + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period + ) + independent_filers = tax_unit( + "head_spouse_count_not_dependent_elsewhere", period + ) + mixed_return_exemptions = independent_filers + where( + independent_filers > 0, minor_child_exemptions, 0 + ) + qualified_exemptions = where( + dependent_taxpayer, mixed_return_exemptions, claimable_exemptions + ) + every_filer_dependent = tax_unit("every_filer_is_dependent_elsewhere", period) + qualified_exemptions = where(every_filer_dependent, 0, qualified_exemptions) + return qualified_exemptions * amount_per_exemption diff --git a/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_act_115_rebate.py b/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_act_115_rebate.py index 8fd79179391..adc6f987c58 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_act_115_rebate.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_act_115_rebate.py @@ -12,6 +12,8 @@ class hi_act_115_rebate(Variable): "https://tax.hawaii.gov/act-115-ref/", "https://data.capitol.hawaii.gov/sessions/session2022/bills/SB514_CD2_.HTM", "https://files.hawaii.gov/tax/news/announce/ann22-03.pdf#page=2", + "https://files.hawaii.gov/tax/legal/hrs/hrs_235.pdf#page=45", + "https://www.irs.gov/pub/irs-prior/p501--2025.pdf#page=11", ) def formula(tax_unit, period, parameters): @@ -39,6 +41,27 @@ def formula(tax_unit, period, parameters): # person in the tax unit is one exemption. exemptions = tax_unit("exemptions_count", period) # A person who can be claimed as a dependent by another taxpayer is - # not a qualifying resident taxpayer. - head_is_dependent_elsewhere = tax_unit("head_is_dependent_elsewhere", period) - return where(head_is_dependent_elsewhere, 0, amount_per_exemption * exemptions) + # not a qualifying resident taxpayer, and each qualifying resident + # taxpayer claims the refund for the exemptions they are entitled to. + # A filer who can be claimed has no exemption (HRS 235-54(a)), and a + # return on which a filer can be claimed generally claims no dependents + # (IRS Publication 501, Dependent Taxpayer Test). Its filing exception + # restores those dependents, but does not restore the claimable + # filer's personal exemption or qualification as a claimant. + dependent_filer = tax_unit("head_or_spouse_is_dependent_elsewhere", period) + independent_filers = tax_unit( + "head_spouse_count_not_dependent_elsewhere", period + ) + dependent_taxpayer = tax_unit( + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period + ) + filers = add(tax_unit, period, ["is_tax_unit_head_or_spouse"]) + dependent_filers = filers - independent_filers + ordinary_exemptions = max_(exemptions - dependent_filers, 0) + qualified_exemptions = where( + dependent_taxpayer, independent_filers, ordinary_exemptions + ) + qualified_exemptions = where( + dependent_filer & (independent_filers == 0), 0, qualified_exemptions + ) + return amount_per_exemption * qualified_exemptions diff --git a/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_low_income_household_renters_tax_credit.py b/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_low_income_household_renters_tax_credit.py index e0a69bef748..a744ce369bf 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_low_income_household_renters_tax_credit.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_low_income_household_renters_tax_credit.py @@ -8,18 +8,42 @@ class hi_tax_credit_for_low_income_household_renters(Variable): unit = USD definition_period = YEAR defined_for = "hi_tax_credit_for_low_income_household_renters_eligible" + reference = ( + "https://files.hawaii.gov/tax/legal/hrs/hrs_235.pdf#page=48", + "https://files.hawaii.gov/tax/forms/2025/schx_i.pdf#page=1", + "https://www.irs.gov/pub/irs-prior/p501--2025.pdf#page=11", + ) def formula(tax_unit, period, parameters): p = parameters(period).gov.states.hi.tax.income.credits.lihrtc - # Aged extra exemptions - aged_head = (tax_unit("age_head", period) >= p.aged_age_threshold).astype(int) - aged_spouse = (tax_unit("age_spouse", period) >= p.aged_age_threshold).astype( - int + # Schedule X lines 8-10 count only people who cannot be claimed as a + # dependent by another taxpayer, with an extra exemption for each + # such filer aged 65 or over. A return on which a filer can be + # claimed generally claims no dependents (IRS Publication 501, + # Dependent Taxpayer Test). Its filing exception restores the + # return's own dependents, while claimable filers and their age + # additions remain excluded. + aged_head = (tax_unit("age_head", period) >= p.aged_age_threshold) & ~tax_unit( + "head_is_dependent_elsewhere", period ) + aged_spouse = ( + tax_unit("age_spouse", period) >= p.aged_age_threshold + ) & ~tax_unit("spouse_is_dependent_elsewhere", period) + aged_exemptions = aged_head.astype(int) + aged_spouse.astype(int) - aged_exemptions = aged_head + aged_spouse - exemptions = tax_unit("exemptions_count", period) + dependent_taxpayer = tax_unit( + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period + ) + independent_filers = tax_unit( + "head_spouse_count_not_dependent_elsewhere", period + ) + filers = add(tax_unit, period, ["is_tax_unit_head_or_spouse"]) + dependent_filers = filers - independent_filers + ordinary_exemptions = max_( + tax_unit("exemptions_count", period) - dependent_filers, 0 + ) + exemptions = where(dependent_taxpayer, independent_filers, ordinary_exemptions) total_exemptions = exemptions + aged_exemptions return p.amount * total_exemptions diff --git a/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_tax_credit_for_low_income_household_renters_eligible.py b/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_tax_credit_for_low_income_household_renters_eligible.py index 3c99b935916..e25924271ee 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_tax_credit_for_low_income_household_renters_eligible.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_tax_credit_for_low_income_household_renters_eligible.py @@ -6,16 +6,26 @@ class hi_tax_credit_for_low_income_household_renters_eligible(Variable): entity = TaxUnit label = "Eligible for the Hawaii low income household renters tax credit" definition_period = YEAR - reference = "https://files.hawaii.gov/tax/legal/har/har_235.pdf#page=105" # §18-235-55.7 (b) + reference = ( + "https://files.hawaii.gov/tax/legal/har/har_235.pdf#page=105", # §18-235-55.7 (b) + "https://files.hawaii.gov/tax/legal/hrs/hrs_235.pdf#page=48", + "https://files.hawaii.gov/tax/forms/2025/schx_i.pdf#page=1", + ) defined_for = StateCode.HI def formula(tax_unit, period, parameters): - dependent_elsewhere = tax_unit("head_is_dependent_elsewhere", period) + # HRS 235-55.7(b) allows the credit to each resident taxpayer who + # cannot be claimed as a dependent, and Schedule X stops at line 3 if + # "you" can be claimed. Neither settles a joint return where only one + # spouse can be claimed; since the credit and its exemption count are + # per person, we read them as letting the other spouse claim it, so a + # joint return qualifies unless both spouses can be claimed. + every_filer_dependent = tax_unit("every_filer_is_dependent_elsewhere", period) p = parameters(period).gov.states.hi.tax.income.credits.lihrtc agi = tax_unit("hi_agi", period) rent = add(tax_unit, period, ["rent"]) return ( (rent > p.eligibility.rent_threshold) & (agi < p.eligibility.agi_limit) - & ~dependent_elsewhere + & ~every_filer_dependent ) 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 a92860b971e..fe50a345a3c 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 @@ -7,8 +7,11 @@ class hi_deductions(Variable): label = "Hawaii deductions" unit = USD reference = ( - "https://files.hawaii.gov/tax/forms/2022/n11ins.pdf#page=15", # Itemized Deduction - "https://files.hawaii.gov/tax/forms/2022/n11ins.pdf#page=20", # Standard Deduction + "https://files.hawaii.gov/tax/legal/hrs/hrs_235.pdf#page=11", + # PDF pages 15 (itemized deduction), 20 (standard deduction) + "https://files.hawaii.gov/tax/forms/2022/n11ins.pdf#page=15", + # PDF pages 15, 20 + "https://files.hawaii.gov/tax/forms/2025/n11ins.pdf#page=15", ) definition_period = YEAR defined_for = StateCode.HI @@ -25,14 +28,31 @@ def formula(tax_unit, period, parameters): filing_status_eligible = ( total_itemized_deduction > p.threshold.deductions[filing_status] ) - is_dependent_on_another_return = tax_unit("head_is_dependent_elsewhere", period) - standard_cap = min_(tax_unit_earned_income, standard_deduction) - dependent_floor = max_(p.threshold.dependent, standard_cap) + # HRS 235-2.4(a)(3) applies IRC 63(c)(5): the standard deduction of a + # filer who can be claimed as a dependent is the greater of $500 or + # earned income, up to the regular amount. As federally, a joint + # return is limited when either spouse can be claimed. + is_dependent_on_another_return = tax_unit( + "head_or_spouse_is_dependent_elsewhere", period + ) + # Worksheet earned income: wages and self-employment income minus the + # deductible part of self-employment tax (Schedule 1, line 15). + worksheet_earned_income = max_( + tax_unit_earned_income - tax_unit("self_employment_tax_ald", period), 0 + ) + dependent_standard_deduction = min_( + max_(p.threshold.dependent, worksheet_earned_income), standard_deduction + ) + applicable_standard_deduction = where( + is_dependent_on_another_return, + dependent_standard_deduction, + standard_deduction, + ) dependent_eligible = is_dependent_on_another_return & ( - total_itemized_deduction > dependent_floor + total_itemized_deduction > dependent_standard_deduction ) itemized_deductions_eligible = filing_status_eligible | dependent_eligible itemized_deduction = where( itemized_deductions_eligible, total_itemized_deduction, 0 ) - return max_(itemized_deduction, standard_deduction) + return max_(itemized_deduction, applicable_standard_deduction) diff --git a/policyengine_us/variables/gov/states/me/tax/income/credits/fairness/sales_tax_fairness_credit/me_sales_tax_fairness_credit_eligible.py b/policyengine_us/variables/gov/states/me/tax/income/credits/fairness/sales_tax_fairness_credit/me_sales_tax_fairness_credit_eligible.py index c0ea36df12a..cf13c6de8ea 100644 --- a/policyengine_us/variables/gov/states/me/tax/income/credits/fairness/sales_tax_fairness_credit/me_sales_tax_fairness_credit_eligible.py +++ b/policyengine_us/variables/gov/states/me/tax/income/credits/fairness/sales_tax_fairness_credit/me_sales_tax_fairness_credit_eligible.py @@ -6,11 +6,19 @@ class me_sales_tax_fairness_credit_eligible(Variable): entity = TaxUnit label = "Eligible for the Maine sales tax fairness credit" definition_period = YEAR - reference = "https://legislature.maine.gov/statutes/36/title36sec5213-A.html" + reference = ( + "https://legislature.maine.gov/statutes/36/title36sec5213-A.html", + "https://www.maine.gov/revenue/sites/maine.gov.revenue/files/inline-files/25_1040me_sch_ptfc_fillable.pdf#page=3", + ) defined_for = StateCode.ME def formula(tax_unit, period, parameters): - dependent_elsewhere = tax_unit("head_is_dependent_elsewhere", period) + # 36 MRSA 5213-A(6)(C) excludes individuals who may be claimed as a + # dependent on another return, and subsection 2 allows each resident + # individual the base credit for the return's filing status. Neither + # settles a joint return where only one spouse can be claimed; we read + # them as letting the other spouse qualify for the joint credit. + every_filer_dependent = tax_unit("every_filer_is_dependent_elsewhere", period) filing_status = tax_unit("filing_status", period) separate = filing_status == filing_status.possible_values.SEPARATE - return ~dependent_elsewhere & ~separate + return ~every_filer_dependent & ~separate diff --git a/policyengine_us/variables/gov/states/me/tax/income/credits/me_affordability_payment.py b/policyengine_us/variables/gov/states/me/tax/income/credits/me_affordability_payment.py index b488cf00424..7f1a547ff0e 100644 --- a/policyengine_us/variables/gov/states/me/tax/income/credits/me_affordability_payment.py +++ b/policyengine_us/variables/gov/states/me/tax/income/credits/me_affordability_payment.py @@ -14,7 +14,7 @@ class me_affordability_payment(Variable): "payment from a Special Revenue Fund rather than a refundable tax " "credit. PolicyEngine folds the payment into Maine's refundable " "credit aggregation to surface it in tax-unit-level outputs. " - "Sec. T-1, sub-§1(C)(3) excludes individuals claimed as a " + "Sec. T-1, sub-§1(C)(3) excludes individuals who may be claimed as a " "dependent on another taxpayer's return, evaluated per recipient." ) @@ -24,6 +24,6 @@ def formula(tax_unit, period, parameters): is_recipient = person("is_tax_unit_head", period) | person( "is_tax_unit_spouse", period ) - dependent_elsewhere = person("claimed_as_dependent_on_another_return", period) + dependent_elsewhere = person("claimable_as_dependent_on_another_return", period) eligible_recipients = tax_unit.sum(is_recipient & ~dependent_elsewhere) return eligible_recipients * p.amount diff --git a/policyengine_us/variables/gov/states/mi/tax/income/exemptions/mi_exemptions.py b/policyengine_us/variables/gov/states/mi/tax/income/exemptions/mi_exemptions.py index 5974fd46303..7c9874052ea 100644 --- a/policyengine_us/variables/gov/states/mi/tax/income/exemptions/mi_exemptions.py +++ b/policyengine_us/variables/gov/states/mi/tax/income/exemptions/mi_exemptions.py @@ -11,6 +11,8 @@ class mi_exemptions(Variable): reference = ( "https://www.legislature.mi.gov/Laws/MCL?objectName=mcl-206-30", "https://www.legislature.mi.gov/Publications/TaxpayerGuide.pdf", + "https://www.michigan.gov/taxes/-/media/Project/Websites/taxes/Forms/IIT/TY2025/MI-1040-Book.pdf#page=10", + "https://www.irs.gov/pub/irs-prior/p501--2025.pdf#page=11", ) def formula(tax_unit, period, parameters): @@ -31,16 +33,42 @@ def formula(tax_unit, period, parameters): ) disabled_veteran_exemption = disabled_veterans * p.disabled.amount.veteran - # Dependent-on-other-return exemptions - filing_status = tax_unit("filing_status", period) - dependent_elsewhere = tax_unit("head_is_dependent_elsewhere", period) - dependent_exemption = ( - dependent_elsewhere * p.dependent_on_other_return[filing_status] + # MCL 206.30(4): an individual who can be claimed as a dependent on + # another return gets no personal exemption but subtracts $1,500. + # Treasury's instructions do not address a joint return where only + # one spouse can be claimed; since subsection (4) speaks of an + # individual, we apply it to each spouse, so the other keeps their own + # personal exemption (and any stillbirth exemption). A joint return on + # which either spouse can be claimed generally claims no dependents + # (IRS Publication 501, "Dependent Taxpayer Test"). The filing + # exception restores the return's own dependents, while each + # claimable filer keeps the $1,500 allowance. + filers = add(tax_unit, period, ["is_tax_unit_head_or_spouse"]) + independent_filers = tax_unit( + "head_spouse_count_not_dependent_elsewhere", period ) + dependent_filers = filers - independent_filers + stillborn = tax_unit("tax_unit_stillborn_children", period) + independent_filer_exemptions = ( + independent_filers + where(independent_filers > 0, stillborn, 0) + ) * p.personal + dependent_filer_exemptions = dependent_filers * p.dependent_on_other_return + + dependent_taxpayer = tax_unit( + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period + ) + own_dependents = max_(tax_unit("tax_unit_size", period) - filers, 0) + dependent_exemptions = where(dependent_taxpayer, 0, own_dependents) * p.personal # Total exemptions return ( - where(dependent_elsewhere, dependent_exemption, personal_exemption) + where( + dependent_filers > 0, + independent_filer_exemptions + + dependent_filer_exemptions + + dependent_exemptions, + personal_exemption, + ) + disabled_exemption + disabled_veteran_exemption ) diff --git a/policyengine_us/variables/gov/states/mn/tax/property/homestead_credit_refund/mn_homestead_credit_refund_household_income.py b/policyengine_us/variables/gov/states/mn/tax/property/homestead_credit_refund/mn_homestead_credit_refund_household_income.py index aa74a39cca7..cdaa9b234fa 100644 --- a/policyengine_us/variables/gov/states/mn/tax/property/homestead_credit_refund/mn_homestead_credit_refund_household_income.py +++ b/policyengine_us/variables/gov/states/mn/tax/property/homestead_credit_refund/mn_homestead_credit_refund_household_income.py @@ -33,7 +33,7 @@ def formula(tax_unit, period, parameters): people = tax_unit.members is_dependent = people("is_tax_unit_dependent", period) | people( - "claimed_as_dependent_on_another_return", period + "claimable_as_dependent_on_another_return", period ) social_security = people("social_security", period) taxable_social_security = people("taxable_social_security", period) diff --git a/policyengine_us/variables/gov/states/mo/tax/income/credits/mo_wftc_eligible.py b/policyengine_us/variables/gov/states/mo/tax/income/credits/mo_wftc_eligible.py index d1b71e8d93e..582fdb3ed2a 100644 --- a/policyengine_us/variables/gov/states/mo/tax/income/credits/mo_wftc_eligible.py +++ b/policyengine_us/variables/gov/states/mo/tax/income/credits/mo_wftc_eligible.py @@ -8,7 +8,8 @@ class mo_wftc_eligible(Variable): definition_period = YEAR reference = ( "https://revisor.mo.gov/main/OneSection.aspx?section=143.177&bid=49978&hl=", - "https://dor.mo.gov/forms/MO-1040%20Instructions_2025.pdf#page=43", + # PDF pages 6, 43 + "https://dor.mo.gov/forms/MO-1040%20Instructions_2025.pdf#page=6", ) defined_for = StateCode.MO @@ -23,10 +24,17 @@ def formula(tax_unit, period, parameters): # married filing separately. filing_status = tax_unit("filing_status", period) separate = filing_status == filing_status.possible_values.SEPARATE - # The 2024 and 2025 Form MO-WFTC checklists also stop filers - # claimed as a dependent on another return; the 2023 form does not. - excluded_dependent = p.dependent_filers_excluded & tax_unit( - "head_is_dependent_elsewhere", period + # The 2024 and 2025 Form MO-WFTC checklists also stop filers with a + # filing status of "claimed as a dependent"; the 2023 form does not. + # A couple filing a joint federal return files Form MO-1040 as + # married filing combined even when a spouse can be claimed (2025 + # MO-1040 instructions, "Filing status"), so the stop never applies + # to a joint return. + joint = filing_status == filing_status.possible_values.JOINT + excluded_dependent = ( + p.dependent_filers_excluded + & ~joint + & tax_unit("head_or_spouse_is_dependent_elsewhere", period) ) # Form MO-WFTC applies Missouri's own investment income limit, # reflecting IRC Section 32(i) as of January 1, 2021 per RSMo diff --git a/policyengine_us/variables/gov/states/nm/tax/income/credits/cdcc/nm_cdcc_eligible.py b/policyengine_us/variables/gov/states/nm/tax/income/credits/cdcc/nm_cdcc_eligible.py index ea1c0e9fd46..3bdf5fa4fed 100644 --- a/policyengine_us/variables/gov/states/nm/tax/income/credits/cdcc/nm_cdcc_eligible.py +++ b/policyengine_us/variables/gov/states/nm/tax/income/credits/cdcc/nm_cdcc_eligible.py @@ -6,13 +6,20 @@ class nm_cdcc_eligible(Variable): entity = TaxUnit label = "Eligible household for the New Mexico dependent child day care credit" definition_period = YEAR - reference = "https://nmonesource.com/nmos/nmsa/en/item/4340/index.do#!fragment/zoupio-_Toc140503752/BQCwhgziBcwMYgK4DsDWszIQewE4BUBTADwBdoAvbRABwEtsBaAfX2zgEYAWABgFYeAZgDsfAEwBKADTJspQhACKiQrgCe0AOSapEQmFwJlqjdt37DIAMp5SAIQ0AlAKIAZZwDUAggDkAws5SpGAARtCk7BISQA" + reference = ( + "https://nmonesource.com/nmos/nmsa/en/item/4340/index.do#!fragment/zoupio-_Toc140503752/BQCwhgziBcwMYgK4DsDWszIQewE4BUBTADwBdoAvbRABwEtsBaAfX2zgEYAWABgFYeAZgDsfAEwBKADTJspQhACKiQrgCe0AOSapEQmFwJlqjdt37DIAMp5SAIQ0AlAKIAZZwDUAggDkAws5SpGAARtCk7BISQA", + "https://realfile.tax.newmexico.gov/2025pit-rc-ins.pdf#page=2", + ) defined_for = StateCode.NM def formula(tax_unit, period, parameters): person = tax_unit.members - # Filer can not be a dependent on another tax return - dependent_elsewhere = tax_unit("head_is_dependent_elsewhere", period) + # Filer can not be a dependent on another tax return, but "If you are + # a dependent with a spouse who was not a dependent of another + # taxpayer, your spouse may still qualify to claim rebates or + # credits" (PIT-RC instructions). So only a return on which every + # filer is a dependent elsewhere is barred. + every_filer_dependent = tax_unit("every_filer_is_dependent_elsewhere", period) p = parameters(period).gov.states.nm.tax.income.credits.cdcc # Filer has to be be gainfully employed to receive credit has_earnings = person("earned_income", period) > 0 @@ -43,7 +50,7 @@ def formula(tax_unit, period, parameters): ) income_eligible = nm_modified_gross_income <= income_limit return ( - ~dependent_elsewhere + ~every_filer_dependent & employment_eligible & ~receives_tanf & income_eligible diff --git a/policyengine_us/variables/gov/states/nm/tax/income/credits/nm_medical_expense_credit.py b/policyengine_us/variables/gov/states/nm/tax/income/credits/nm_medical_expense_credit.py index 7d584b8d7df..741ee9c0cfd 100644 --- a/policyengine_us/variables/gov/states/nm/tax/income/credits/nm_medical_expense_credit.py +++ b/policyengine_us/variables/gov/states/nm/tax/income/credits/nm_medical_expense_credit.py @@ -6,7 +6,10 @@ class nm_medical_expense_credit(Variable): entity = TaxUnit label = "New Mexico unreimbursed medical expense care credit" definition_period = YEAR - reference = "https://nmonesource.com/nmos/nmsa/en/item/4340/index.do#!fragment/zoupio-_Toc140503776/BQCwhgziBcwMYgK4DsDWszIQewE4BUBTADwBdoAvbRABwEtsBaAfX2zgEYAWABgFYeAZgDswgGwBKADTJspQhACKiQrgCe0AOSapEQmFwJlqjdt37DIAMp5SAIQ0AlAKIAZZwDUAggDkAws5SpGAARtCk7BISQA" + reference = ( + "https://nmonesource.com/nmos/nmsa/en/item/4340/index.do#!fragment/zoupio-_Toc140503776/BQCwhgziBcwMYgK4DsDWszIQewE4BUBTADwBdoAvbRABwEtsBaAfX2zgEYAWABgFYeAZgDswgGwBKADTJspQhACKiQrgCe0AOSapEQmFwJlqjdt37DIAMp5SAIQ0AlAKIAZZwDUAggDkAws5SpGAARtCk7BISQA", + "https://realfile.tax.newmexico.gov/2025pit-rc-ins.pdf#page=2", + ) defined_for = StateCode.NM documentation = ( @@ -33,10 +36,21 @@ def formula(tax_unit, period, parameters): "itemized_medical_expenses_applied_exclusion", ], ) - age_eligible = tax_unit.any(age >= p.age_eligibility) + aged = age >= p.age_eligibility expense_eligible = medical_expense >= p.min_expenses - dependent_elsewhere = tax_unit("head_is_dependent_elsewhere", period) - eligible = age_eligible & expense_eligible & ~dependent_elsewhere + # NMSA 7-2-18.13(A) allows the credit to a taxpayer "who is sixty-five + # years of age or older and who is not a dependent of another + # taxpayer", and the PIT-RC instructions let a spouse who is not a + # dependent claim it when the other spouse is. So when a filer is a + # dependent elsewhere, a filer who is not must be 65 or older. + filer = person("is_tax_unit_head_or_spouse", period) + claimed = person("claimable_as_dependent_on_another_return", period) + independent_aged_filer = tax_unit.any(filer & ~claimed & aged) + dependent_filer = tax_unit("head_or_spouse_is_dependent_elsewhere", period) + age_eligible = where( + dependent_filer, independent_aged_filer, tax_unit.any(aged) + ) + eligible = age_eligible & expense_eligible # exemption is halved for married filing separately filing_status = tax_unit("filing_status", period) separate = filing_status == filing_status.possible_values.SEPARATE diff --git a/policyengine_us/variables/gov/states/nm/tax/income/deductions/certain_dependents/nm_deduction_for_certain_dependents_eligible.py b/policyengine_us/variables/gov/states/nm/tax/income/deductions/certain_dependents/nm_deduction_for_certain_dependents_eligible.py index d0415b60595..c083077aebd 100644 --- a/policyengine_us/variables/gov/states/nm/tax/income/deductions/certain_dependents/nm_deduction_for_certain_dependents_eligible.py +++ b/policyengine_us/variables/gov/states/nm/tax/income/deductions/certain_dependents/nm_deduction_for_certain_dependents_eligible.py @@ -6,12 +6,21 @@ class nm_deduction_for_certain_dependents_eligible(Variable): entity = TaxUnit label = "Eligibility for New Mexico deduction for certain dependents" definition_period = YEAR - reference = "https://nmonesource.com/nmos/nmsa/en/item/4340/index.do#!fragment/zoupio-_Toc140503892/BQCwhgziBcwMYgK4DsDWszIQewE4BUBTADwBdoAvbRABwEtsBaAfX2zgEYAWABgFYeAZgAcATgBMASgA0ybKUIQAiokK4AntADkW6REJhcCFWs069BoyADKeUgCFNAJQCiAGRcA1AIIA5AMIu0qRgAEbQpOySkkA" + reference = ( + "https://nmonesource.com/nmos/nmsa/en/item/4340/index.do#!fragment/zoupio-_Toc140503892/BQCwhgziBcwMYgK4DsDWszIQewE4BUBTADwBdoAvbRABwEtsBaAfX2zgEYAWABgFYeAZgAcATgBMASgA0ybKUIQAiokK4AntADkW6REJhcCFWs069BoyADKeUgCFNAJQCiAGRcA1AIIA5AMIu0qRgAEbQpOySkkA", + "https://realfile.tax.newmexico.gov/2025pit-rc-ins.pdf#page=2", + ) defined_for = StateCode.NM def formula(tax_unit, period, parameters): - dependent_elsewhere = tax_unit("head_is_dependent_elsewhere", period) + # The deduction uses the same "not a dependent of another individual" + # test as the rebates and credits on Schedule PIT-RC, and the law does + # not settle a joint return where only one spouse is a dependent. We + # apply the PIT-RC instructions' reading, which lets a spouse who is + # not a dependent still qualify, so only a return on which every + # filer is a dependent elsewhere is barred. + every_filer_dependent = tax_unit("every_filer_is_dependent_elsewhere", period) # deduction does not apply if an exemption under IRS 151 is claimed; # IRC 151 refers to the federal personal exemption federal_exemption_amount = tax_unit("exemptions", period) - return ~dependent_elsewhere & (federal_exemption_amount == 0) + return ~every_filer_dependent & (federal_exemption_amount == 0) diff --git a/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_2021_income_rebate.py b/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_2021_income_rebate.py index 1f37e1c7c21..0dab3d96509 100644 --- a/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_2021_income_rebate.py +++ b/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_2021_income_rebate.py @@ -6,15 +6,27 @@ class nm_2021_income_rebate(Variable): entity = TaxUnit label = "New Mexico 2021 income tax rebate" definition_period = YEAR - reference = "https://nmonesource.com/nmos/nmsa/en/item/4340/index.do#!fragment/zoupio-_Toc140503708/BQCwhgziBcwMYgK4DsDWszIQewE4BUBTADwBdoAvbRABwEtsBaAfX2zgEYAWABgFYeAZgDsPABwBKADTJspQhACKiQrgCe0AOSapEQmFwJlqjdt37DIAMp5SAIQ0AlAKIAZZwDUAggDkAws5SpGAARtCk7BISQA" + reference = ( + "https://nmonesource.com/nmos/nmsa/en/item/4340/index.do#!fragment/zoupio-_Toc140503708/BQCwhgziBcwMYgK4DsDWszIQewE4BUBTADwBdoAvbRABwEtsBaAfX2zgEYAWABgFYeAZgDsPABwBKADTJspQhACKiQrgCe0AOSapEQmFwJlqjdt37DIAMp5SAIQ0AlAKIAZZwDUAggDkAws5SpGAARtCk7BISQA", + # PDF pages 2-3 + "https://realfile.tax.newmexico.gov/2021pit-rc-ins.pdf#page=2", + ) defined_for = StateCode.NM def formula(tax_unit, period, parameters): - dependent_elsewhere = tax_unit("head_is_dependent_elsewhere", period) + # The rebate goes to "a resident ... who is not a dependent of another + # individual", and the law does not settle a joint return where only + # one spouse is. For the same phrase on its rebate and credit schedule, + # TRD says "If you are a dependent with a spouse who was not a + # dependent of another taxpayer, your spouse may still qualify to + # claim rebates or credits" (PIT-RC instructions); we apply that + # reading, so only a return on which every filer is a dependent + # elsewhere is barred. + every_filer_dependent = tax_unit("every_filer_is_dependent_elsewhere", period) agi = tax_unit("adjusted_gross_income", period) filing_status = tax_unit("filing_status", period) p = parameters(period).gov.states.nm.tax.income.rebates["2021_income"] income_eligible = agi < p.main.income_limit[filing_status] - eligible = income_eligible & ~dependent_elsewhere + eligible = income_eligible & ~every_filer_dependent amount_if_eligible = p.main.amount[filing_status] return eligible * amount_if_eligible diff --git a/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_additional_2021_income_rebate.py b/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_additional_2021_income_rebate.py index 891ce07d54e..78493990ef0 100644 --- a/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_additional_2021_income_rebate.py +++ b/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_additional_2021_income_rebate.py @@ -6,11 +6,23 @@ class nm_additional_2021_income_rebate(Variable): entity = TaxUnit label = "New Mexico additional 2021 income tax rebate" definition_period = YEAR - reference = "https://nmonesource.com/nmos/nmsa/en/item/4340/index.do#!fragment/zoupio-_Toc140503710/BQCwhgziBcwMYgK4DsDWszIQewE4BUBTADwBdoAvbRABwEtsBaAfX2zgEYAWABgFYeAZgDsHHgEoANMmylCEAIqJCuAJ7QA5BskRCYXAiUr1WnXoMgAynlIAhdQCUAogBknANQCCAOQDCTyVIwACNoUnZxcSA" + reference = ( + "https://nmonesource.com/nmos/nmsa/en/item/4340/index.do#!fragment/zoupio-_Toc140503710/BQCwhgziBcwMYgK4DsDWszIQewE4BUBTADwBdoAvbRABwEtsBaAfX2zgEYAWABgFYeAZgDsHHgEoANMmylCEAIqJCuAJ7QA5BskRCYXAiUr1WnXoMgAynlIAhdQCUAogBknANQCCAOQDCTyVIwACNoUnZxcSA", + # PDF pages 2-3 + "https://realfile.tax.newmexico.gov/2021pit-rc-ins.pdf#page=2", + ) defined_for = StateCode.NM def formula(tax_unit, period, parameters): - dependent_elsewhere = tax_unit("head_is_dependent_elsewhere", period) + # The rebate goes to "a resident ... who is not a dependent of another + # individual", and the law does not settle a joint return where only + # one spouse is. For the same phrase on its rebate and credit schedule, + # TRD says "If you are a dependent with a spouse who was not a + # dependent of another taxpayer, your spouse may still qualify to + # claim rebates or credits" (PIT-RC instructions); we apply that + # reading, so only a return on which every filer is a dependent + # elsewhere is barred. + every_filer_dependent = tax_unit("every_filer_is_dependent_elsewhere", period) p = parameters(period).gov.states.nm.tax.income.rebates["2021_income"] filing_status = tax_unit("filing_status", period) - return ~dependent_elsewhere * p.additional.amount[filing_status] + return ~every_filer_dependent * p.additional.amount[filing_status] diff --git a/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_supplemental_2021_income_rebate.py b/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_supplemental_2021_income_rebate.py index 7839b75873d..cd82e30a49b 100644 --- a/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_supplemental_2021_income_rebate.py +++ b/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_supplemental_2021_income_rebate.py @@ -6,11 +6,23 @@ class nm_supplemental_2021_income_rebate(Variable): entity = TaxUnit label = "New Mexico supplemental 2021 income tax rebate" definition_period = YEAR - reference = "https://nmonesource.com/nmos/nmsa/en/item/4340/index.do#!fragment/zoupio-_Toc140503706/BQCwhgziBcwMYgK4DsDWszIQewE4BUBTADwBdoAvbRABwEtsBaAfX2zgEYAWABgFYeAZgDsPAGwBKADTJspQhACKiQrgCe0AOSapEQmFwJlqjdt37DIAMp5SAIQ0AlAKIAZZwDUAggDkAws5SpGAARtCk7BISQA" + reference = ( + "https://nmonesource.com/nmos/nmsa/en/item/4340/index.do#!fragment/zoupio-_Toc140503706/BQCwhgziBcwMYgK4DsDWszIQewE4BUBTADwBdoAvbRABwEtsBaAfX2zgEYAWABgFYeAZgDsPAGwBKADTJspQhACKiQrgCe0AOSapEQmFwJlqjdt37DIAMp5SAIQ0AlAKIAZZwDUAggDkAws5SpGAARtCk7BISQA", + # PDF pages 2-3 + "https://realfile.tax.newmexico.gov/2021pit-rc-ins.pdf#page=2", + ) defined_for = StateCode.NM def formula(tax_unit, period, parameters): - dependent_elsewhere = tax_unit("head_is_dependent_elsewhere", period) + # The rebate goes to "a resident ... who is not a dependent of another + # individual", and the law does not settle a joint return where only + # one spouse is. For the same phrase on its rebate and credit schedule, + # TRD says "If you are a dependent with a spouse who was not a + # dependent of another taxpayer, your spouse may still qualify to + # claim rebates or credits" (PIT-RC instructions); we apply that + # reading, so only a return on which every filer is a dependent + # elsewhere is barred. + every_filer_dependent = tax_unit("every_filer_is_dependent_elsewhere", period) p = parameters(period).gov.states.nm.tax.income.rebates["2021_income"] filing_status = tax_unit("filing_status", period) - return ~dependent_elsewhere * p.supplemental.amount[filing_status] + return ~every_filer_dependent * p.supplemental.amount[filing_status] diff --git a/policyengine_us/variables/gov/states/ny/tax/income/taxable_income/deductions/ny_standard_deduction.py b/policyengine_us/variables/gov/states/ny/tax/income/taxable_income/deductions/ny_standard_deduction.py index d98250ebddb..1720bb49cd2 100644 --- a/policyengine_us/variables/gov/states/ny/tax/income/taxable_income/deductions/ny_standard_deduction.py +++ b/policyengine_us/variables/gov/states/ny/tax/income/taxable_income/deductions/ny_standard_deduction.py @@ -7,17 +7,21 @@ class ny_standard_deduction(Variable): label = "NY standard deduction" unit = USD definition_period = YEAR - reference = "https://www.nysenate.gov/legislation/laws/TAX/613" + reference = ( + "https://www.nysenate.gov/legislation/laws/TAX/614", + # Standard deduction table: the dependent amount applies only to + # filing status 1, "Single and you marked item C Yes". + "https://www.tax.ny.gov/pdf/2025/inc/it201i_2025.pdf#page=11", + ) defined_for = StateCode.NY def formula(tax_unit, period, parameters): - dependent_elsewhere = tax_unit("head_is_dependent_elsewhere", period) - standard_deduction = parameters( - period - ).gov.states.ny.tax.income.deductions.standard + p = parameters(period).gov.states.ny.tax.income.deductions.standard filing_status = tax_unit("filing_status", period) + single = filing_status == filing_status.possible_values.SINGLE + dependent_elsewhere = tax_unit("head_or_spouse_is_dependent_elsewhere", period) return where( - dependent_elsewhere, - standard_deduction.dependent_elsewhere, - standard_deduction.amount[filing_status], + single & dependent_elsewhere, + p.dependent_elsewhere, + p.amount[filing_status], ) diff --git a/policyengine_us/variables/gov/states/oh/tax/income/exemptions/oh_personal_exemptions_eligible_person.py b/policyengine_us/variables/gov/states/oh/tax/income/exemptions/oh_personal_exemptions_eligible_person.py index e222b7bf362..9a45b4bda79 100644 --- a/policyengine_us/variables/gov/states/oh/tax/income/exemptions/oh_personal_exemptions_eligible_person.py +++ b/policyengine_us/variables/gov/states/oh/tax/income/exemptions/oh_personal_exemptions_eligible_person.py @@ -16,7 +16,7 @@ def formula(person, period, parameters): # if they are not claimed as a dependent elsewhere head_or_spouse = person("is_tax_unit_head_or_spouse", period) dependent_on_another_return = person( - "claimed_as_dependent_on_another_return", period + "claimable_as_dependent_on_another_return", period ) # The personal exemption is also provided to dependents dependent = person("is_tax_unit_dependent", period) diff --git a/policyengine_us/variables/gov/states/or/tax/income/deductions/or_standard_deduction.py b/policyengine_us/variables/gov/states/or/tax/income/deductions/or_standard_deduction.py index 6d8dde3554d..54d2dc18fbc 100644 --- a/policyengine_us/variables/gov/states/or/tax/income/deductions/or_standard_deduction.py +++ b/policyengine_us/variables/gov/states/or/tax/income/deductions/or_standard_deduction.py @@ -8,9 +8,10 @@ class or_standard_deduction(Variable): unit = USD definition_period = YEAR documentation = "Oregon standard deduction, including bonus for aged or blind and special rules for filers who are claimable as dependents." - reference = ( # TODO: update + reference = ( "https://www.oregon.gov/dor/forms/FormsPubs/form-or-40-inst_101-040-1_2021.pdf#page=18", - "https://www.oregonlegislature.gov/bills_laws/ors/ors316.html", # Subsection 316.695 (7) + "https://www.oregonlegislature.gov/bills_laws/ors/ors316.html", # ORS 316.695(7)-(8) + "https://www.oregon.gov/dor/forms/FormsPubs/publication-or-17_101-431_2025.pdf#page=96", ) defined_for = StateCode.OR @@ -19,13 +20,23 @@ def formula(tax_unit, period, parameters): # Core deduction based on filing status. filing_status = tax_unit("filing_status", period) initial_core_deduction = p.amount[filing_status] - # Replace if claimable as a dependent. - earned_income = tax_unit("tax_unit_earned_income", period) + # Replace if claimable as a dependent. Earned income is figured as in + # the federal worksheet, net of the deductible part of self-employment + # tax (Schedule 1, line 15). + earned_income = max_( + tax_unit("tax_unit_earned_income", period) + - tax_unit("self_employment_tax_ald", period), + 0, + ) claimable_dep_floor = p.claimable_as_dependent.min claimable_dep_earned_amount = ( earned_income + p.claimable_as_dependent.earned_income_addition ) - dependent_elsewhere = tax_unit("head_is_dependent_elsewhere", period) + # ORS 316.695(8) limits the deduction as IRC 63(c)(5) does, so a joint + # return uses the couple's earned income and the joint amount when + # either spouse can be claimed (Publication OR-17, "Standard deduction + # worksheet for married filing jointly (MFJ) dependents"). + dependent_elsewhere = tax_unit("head_or_spouse_is_dependent_elsewhere", period) # Set floor and ceiling around earned income plus additional amount. floored_claimable_dep_amount = max_( claimable_dep_earned_amount, claimable_dep_floor diff --git a/policyengine_us/variables/gov/states/ut/tax/property/homeowner_renter_relief/ut_homeowner_renter_relief_pre_one_claimant_eligible.py b/policyengine_us/variables/gov/states/ut/tax/property/homeowner_renter_relief/ut_homeowner_renter_relief_pre_one_claimant_eligible.py index 28c3dbbdf03..146db78e423 100644 --- a/policyengine_us/variables/gov/states/ut/tax/property/homeowner_renter_relief/ut_homeowner_renter_relief_pre_one_claimant_eligible.py +++ b/policyengine_us/variables/gov/states/ut/tax/property/homeowner_renter_relief/ut_homeowner_renter_relief_pre_one_claimant_eligible.py @@ -36,9 +36,12 @@ def formula(tax_unit, period, parameters): claimant_is_tax_unit_dependent = tax_unit.any( claimants & tax_unit.members("is_tax_unit_dependent", period) ) - claimant_is_dependent_elsewhere = tax_unit( - "head_is_dependent_elsewhere", period - ) | tax_unit("spouse_is_dependent_elsewhere", period) + # Utah Code 59-2a-205(4) and 59-2a-305(2)(a) turn on another + # taxpayer actually claiming the exemption or dependent credit. + claimant_is_dependent_elsewhere = tax_unit.any( + claimants + & tax_unit.members("claimed_as_dependent_on_another_return", period) + ) claimant_is_dependent = ( claimant_is_tax_unit_dependent | claimant_is_dependent_elsewhere ) diff --git a/policyengine_us/variables/gov/states/va/tax/income/credits/low_income_credit/va_low_income_tax_credit_eligible.py b/policyengine_us/variables/gov/states/va/tax/income/credits/low_income_credit/va_low_income_tax_credit_eligible.py index 82b96fe10d0..58934026c25 100644 --- a/policyengine_us/variables/gov/states/va/tax/income/credits/low_income_credit/va_low_income_tax_credit_eligible.py +++ b/policyengine_us/variables/gov/states/va/tax/income/credits/low_income_credit/va_low_income_tax_credit_eligible.py @@ -7,16 +7,24 @@ class va_low_income_tax_credit_eligible(Variable): label = "Eligible for the Virginia Low Income Tax Credit" definition_period = YEAR defined_for = StateCode.VA + reference = ( + "https://law.lis.virginia.gov/vacode/title58.1/chapter3/section58.1-339.8/", + "https://www.tax.virginia.gov/sites/default/files/vatax-pdf/2025-760-instructions.pdf#page=30", + ) def formula(tax_unit, period, parameters): # Criteria 1: Not qualified to claim EITC if anyone in the tax unit claimed any of the following: p = parameters(period).gov.states.va.tax.income.credits.eitc.low_income_tax program_eligible = add(tax_unit, period, p.ineligible_programs) == 0 - # Criteria 2: Not qualified to claim EITC if the filier is claimed as a dependent on another taxpayer's return - - head_is_not_dependent_elsewhere = ~tax_unit( - "head_is_dependent_elsewhere", period + # Criteria 2: Not qualified if a filer is claimed as a dependent on + # another taxpayer's return. Neither Va. Code 58.1-339.8 nor the + # instructions settle a joint return where only one spouse is + # claimed. We read the statute's bar on the credit "against such tax + # of a dependent" as reaching a joint return, which carries both + # spouses' tax, so either spouse being claimed bars it. + filer_is_dependent_elsewhere = tax_unit( + "head_or_spouse_is_dependent_elsewhere", period ) - return program_eligible & head_is_not_dependent_elsewhere + return program_eligible & ~filer_is_dependent_elsewhere diff --git a/policyengine_us/variables/household/demographic/person/claimable_as_dependent_on_another_return.py b/policyengine_us/variables/household/demographic/person/claimable_as_dependent_on_another_return.py new file mode 100644 index 00000000000..5643c2eb30d --- /dev/null +++ b/policyengine_us/variables/household/demographic/person/claimable_as_dependent_on_another_return.py @@ -0,0 +1,24 @@ +from policyengine_us.model_api import * + + +class claimable_as_dependent_on_another_return(Variable): + value_type = bool + entity = Person + definition_period = YEAR + label = "Can be claimed as a dependent elsewhere" + documentation = ( + "Whether another taxpayer, in another tax unit, can claim the person " + "as a dependent: a " + "deduction under IRC section 151 for the person is allowable to " + "another taxpayer, whether or not claimed. Defaults to the actual " + "or expected claim input for compatibility with existing callers. " + "The dependent_claimant_filing_exception does not change this fact " + "or the dependent standard-deduction limit." + ) + reference = ( + "https://www.law.cornell.edu/uscode/text/26/151#c", + "https://www.irs.gov/pub/irs-prior/p501--2025.pdf#page=24", + ) + + def formula(person, period, parameters): + return person("claimed_as_dependent_on_another_return", period) diff --git a/policyengine_us/variables/household/demographic/person/claimed_as_dependent_on_another_return.py b/policyengine_us/variables/household/demographic/person/claimed_as_dependent_on_another_return.py index a62ae0c4703..cfaa9d32825 100644 --- a/policyengine_us/variables/household/demographic/person/claimed_as_dependent_on_another_return.py +++ b/policyengine_us/variables/household/demographic/person/claimed_as_dependent_on_another_return.py @@ -6,4 +6,9 @@ class claimed_as_dependent_on_another_return(Variable): entity = Person definition_period = YEAR label = "Is claimed as a dependent elsewhere" - documentation = "Whether the person is claimed as a dependent in another tax unit." + documentation = ( + "Whether the person is actually claimed, or expected to be claimed, " + "as a dependent in another tax unit. Claimability, whether or not " + "another taxpayer claims the person, is represented separately by " + "claimable_as_dependent_on_another_return." + ) diff --git a/policyengine_us/variables/household/demographic/person/dependent_claimant_filing_exception.py b/policyengine_us/variables/household/demographic/person/dependent_claimant_filing_exception.py new file mode 100644 index 00000000000..d4acf034fba --- /dev/null +++ b/policyengine_us/variables/household/demographic/person/dependent_claimant_filing_exception.py @@ -0,0 +1,21 @@ +from policyengine_us.model_api import * + + +class dependent_claimant_filing_exception(Variable): + value_type = bool + entity = Person + definition_period = YEAR + label = "Dependent's would-be claimant meets the filing exception" + documentation = ( + "Whether the person who could claim this person (all such claimants " + "if more than one) is not required to file an income tax return and " + "either files no return or files solely to obtain a refund of income " + "tax withheld or estimated tax paid. A return filed to claim refundable " + "tax credits, including EITC, does not qualify. This fact permits EITC and claiming " + "one's own dependents under the Dependent Taxpayer Test; it does not " + "remove general claimability or its standard-deduction limitation." + ) + reference = ( + "https://www.irs.gov/pub/irs-prior/p596--2025.pdf#page=18", + "https://www.irs.gov/pub/irs-prior/p501--2025.pdf#page=11", + ) diff --git a/policyengine_us/variables/household/demographic/person/is_dependent_elsewhere_without_filing_exception.py b/policyengine_us/variables/household/demographic/person/is_dependent_elsewhere_without_filing_exception.py new file mode 100644 index 00000000000..79384c81b5e --- /dev/null +++ b/policyengine_us/variables/household/demographic/person/is_dependent_elsewhere_without_filing_exception.py @@ -0,0 +1,22 @@ +from policyengine_us.model_api import * + + +class is_dependent_elsewhere_without_filing_exception(Variable): + value_type = bool + entity = Person + definition_period = YEAR + label = "Claimable elsewhere without the claimant filing exception" + documentation = ( + "Whether the person is claimable as a dependent on another return " + "and the would-be claimant does not meet the nonrequired-filing " + "exception. Used for childless EITC and the Dependent Taxpayer Test." + ) + reference = ( + "https://www.irs.gov/pub/irs-prior/p596--2025.pdf#page=18", + "https://www.irs.gov/pub/irs-prior/p501--2025.pdf#page=11", + ) + + def formula(person, period, parameters): + return person("claimable_as_dependent_on_another_return", period) & ~person( + "dependent_claimant_filing_exception", period + ) diff --git a/policyengine_us/variables/household/demographic/tax_unit/every_filer_is_dependent_elsewhere.py b/policyengine_us/variables/household/demographic/tax_unit/every_filer_is_dependent_elsewhere.py new file mode 100644 index 00000000000..e2135a89019 --- /dev/null +++ b/policyengine_us/variables/household/demographic/tax_unit/every_filer_is_dependent_elsewhere.py @@ -0,0 +1,18 @@ +from policyengine_us.model_api import * + + +class every_filer_is_dependent_elsewhere(Variable): + value_type = bool + entity = TaxUnit + definition_period = YEAR + label = "Every head and spouse is claimable elsewhere" + documentation = ( + "Whether the tax unit has a head or spouse claimable as a dependent in " + "another tax unit and none who is not. False for a tax unit with " + "neither a head nor a spouse." + ) + + def formula(tax_unit, period, parameters): + return tax_unit("head_or_spouse_is_dependent_elsewhere", period) & ( + tax_unit("head_spouse_count_not_dependent_elsewhere", period) == 0 + ) diff --git a/policyengine_us/variables/household/demographic/tax_unit/head_is_dependent_elsewhere.py b/policyengine_us/variables/household/demographic/tax_unit/head_is_dependent_elsewhere.py index 04064b39948..b8d43c53e5e 100644 --- a/policyengine_us/variables/household/demographic/tax_unit/head_is_dependent_elsewhere.py +++ b/policyengine_us/variables/household/demographic/tax_unit/head_is_dependent_elsewhere.py @@ -6,12 +6,12 @@ class head_is_dependent_elsewhere(Variable): entity = TaxUnit definition_period = YEAR label = "Is tax-unit head a dependent elsewhere" - documentation = "Whether the filer for this tax unit is claimed as a dependent in another tax unit." + documentation = "Whether the filer for this tax unit is claimable as a dependent in another tax unit." def formula(tax_unit, period, parameters): person = tax_unit.members - claimed_on_another_return = person( - "claimed_as_dependent_on_another_return", period + claimable_on_another_return = person( + "claimable_as_dependent_on_another_return", period ) is_head = person("is_tax_unit_head", period) - return tax_unit.any(claimed_on_another_return & is_head) + return tax_unit.any(claimable_on_another_return & is_head) diff --git a/policyengine_us/variables/household/demographic/tax_unit/head_or_spouse_is_dependent_elsewhere.py b/policyengine_us/variables/household/demographic/tax_unit/head_or_spouse_is_dependent_elsewhere.py new file mode 100644 index 00000000000..34f5593350b --- /dev/null +++ b/policyengine_us/variables/household/demographic/tax_unit/head_or_spouse_is_dependent_elsewhere.py @@ -0,0 +1,21 @@ +from policyengine_us.model_api import * + + +class head_or_spouse_is_dependent_elsewhere(Variable): + value_type = bool + entity = TaxUnit + definition_period = YEAR + label = "Head or spouse is claimable elsewhere" + documentation = ( + "Whether the head or the spouse of this tax unit is claimable as a " + "dependent in another tax unit. Unlike head_is_dependent_elsewhere, " + "this does not depend on which spouse is labelled head." + ) + + def formula(tax_unit, period, parameters): + person = tax_unit.members + claimable_on_another_return = person( + "claimable_as_dependent_on_another_return", period + ) + filer = person("is_tax_unit_head_or_spouse", period) + return tax_unit.any(claimable_on_another_return & filer) diff --git a/policyengine_us/variables/household/demographic/tax_unit/head_or_spouse_is_dependent_elsewhere_without_filing_exception.py b/policyengine_us/variables/household/demographic/tax_unit/head_or_spouse_is_dependent_elsewhere_without_filing_exception.py new file mode 100644 index 00000000000..26a1d3d3eb4 --- /dev/null +++ b/policyengine_us/variables/household/demographic/tax_unit/head_or_spouse_is_dependent_elsewhere_without_filing_exception.py @@ -0,0 +1,23 @@ +from policyengine_us.model_api import * + + +class head_or_spouse_is_dependent_elsewhere_without_filing_exception(Variable): + value_type = bool + entity = TaxUnit + definition_period = YEAR + label = "Either filer claimable elsewhere without the claimant filing exception" + documentation = ( + "Whether a tax-unit head or spouse is claimable on another return " + "without the claimant filing exception. Symmetric Dependent Taxpayer " + "Test gate; other claimability rules use the ordinary helper." + ) + reference = ( + "https://www.irs.gov/pub/irs-prior/p596--2025.pdf#page=18", + "https://www.irs.gov/pub/irs-prior/p501--2025.pdf#page=11", + ) + + def formula(tax_unit, period, parameters): + person = tax_unit.members + dependent = person("is_dependent_elsewhere_without_filing_exception", period) + filer = person("is_tax_unit_head_or_spouse", period) + return tax_unit.any(dependent & filer) diff --git a/policyengine_us/variables/household/demographic/tax_unit/head_spouse_count_not_dependent_elsewhere.py b/policyengine_us/variables/household/demographic/tax_unit/head_spouse_count_not_dependent_elsewhere.py new file mode 100644 index 00000000000..3d040cf683d --- /dev/null +++ b/policyengine_us/variables/household/demographic/tax_unit/head_spouse_count_not_dependent_elsewhere.py @@ -0,0 +1,20 @@ +from policyengine_us.model_api import * + + +class head_spouse_count_not_dependent_elsewhere(Variable): + value_type = int + entity = TaxUnit + definition_period = YEAR + label = "Head and spouse count not dependent elsewhere" + documentation = ( + "Number of tax unit heads and spouses who are not claimable as a " + "dependent in another tax unit." + ) + + def formula(tax_unit, period, parameters): + person = tax_unit.members + claimable_on_another_return = person( + "claimable_as_dependent_on_another_return", period + ) + filer = person("is_tax_unit_head_or_spouse", period) + return tax_unit.sum(filer & ~claimable_on_another_return) diff --git a/policyengine_us/variables/household/demographic/tax_unit/spouse_is_dependent_elsewhere.py b/policyengine_us/variables/household/demographic/tax_unit/spouse_is_dependent_elsewhere.py index b8fb0bc9c5f..a8e0183e464 100644 --- a/policyengine_us/variables/household/demographic/tax_unit/spouse_is_dependent_elsewhere.py +++ b/policyengine_us/variables/household/demographic/tax_unit/spouse_is_dependent_elsewhere.py @@ -6,12 +6,12 @@ class spouse_is_dependent_elsewhere(Variable): entity = TaxUnit definition_period = YEAR label = "Tax-unit spouse is dependent elsewhere" - documentation = "Whether the spouse of the filer for this tax unit is claimed as a dependent in another tax unit." + documentation = "Whether the spouse of the filer for this tax unit is claimable as a dependent in another tax unit." def formula(tax_unit, period, parameters): person = tax_unit.members - claimed_on_another_return = person( - "claimed_as_dependent_on_another_return", period + claimable_on_another_return = person( + "claimable_as_dependent_on_another_return", period ) is_spouse = person("is_tax_unit_spouse", period) - return tax_unit.any(claimed_on_another_return & is_spouse) + return tax_unit.any(claimable_on_another_return & is_spouse)