diff --git a/changelog.d/fix-va-sta-social-security.changed.md b/changelog.d/fix-va-sta-social-security.changed.md new file mode 100644 index 00000000000..67036b24ba1 --- /dev/null +++ b/changelog.d/fix-va-sta-social-security.changed.md @@ -0,0 +1 @@ +Restructured Virginia income tax subtractions to attribute each subtraction to the recipient spouse through a shared person-level subtractions list used by both the tax-unit and per-person totals, leaving all calculated values unchanged. diff --git a/changelog.d/fix-va-sta-social-security.fixed.md b/changelog.d/fix-va-sta-social-security.fixed.md new file mode 100644 index 00000000000..72754de3a56 --- /dev/null +++ b/changelog.d/fix-va-sta-social-security.fixed.md @@ -0,0 +1 @@ +Corrected the Virginia per-person adjusted gross income used by the Spouse Tax Adjustment to attribute each Virginia subtraction (Social Security, railroad retirement, unemployment, US government interest, military and disability subtractions, and the age deduction) to the spouse who received the income, preventing the adjustment from being wrongly granted to couples where one spouse only has Virginia-exempt income. diff --git a/policyengine_us/parameters/gov/states/va/tax/income/subtractions/subtractions.yaml b/policyengine_us/parameters/gov/states/va/tax/income/subtractions/subtractions.yaml index 82f8bf354b4..72db59b04dc 100644 --- a/policyengine_us/parameters/gov/states/va/tax/income/subtractions/subtractions.yaml +++ b/policyengine_us/parameters/gov/states/va/tax/income/subtractions/subtractions.yaml @@ -1,16 +1,18 @@ description: Virginia subtracts these sources from federal adjusted gross income. +# Each entry is a person-level variable so the same list drives both the tax-unit +# total (va_subtractions) and the per-spouse split (va_subtractions_person). values: 2021-01-01: - - us_govt_interest - - va_military_basic_pay_subtraction # Special instructions for members of the military - - va_disability_income_subtraction # Certification Number 5 + - us_govt_interest_person + - va_military_basic_pay_subtraction_person # Special instructions for members of the military + - va_disability_income_subtraction_person # Certification Number 5 - railroad_benefits # Certification Number 22 - - va_national_guard_subtraction # Certification Number 28 + - va_national_guard_subtraction_person # Certification Number 28 - unemployment_compensation # Certification Number 37 - - va_federal_state_employees_subtraction # Certification Number 39 - - va_military_benefit_subtraction # Certification Number 60 - - va_529_plan_deduction # Certification Number 34 - - va_age_deduction # Certification Number 4 + - va_federal_state_employees_subtraction_person # Certification Number 39 + - va_military_benefit_subtraction_person # Certification Number 60 + - va_529_plan_deduction_person # Certification Number 34 + - va_age_deduction_person # Certification Number 4 - taxable_social_security # Certification Number 5 @@ -19,7 +21,7 @@ metadata: period: year label: Virginia adjusted gross income subtractions reference: - - title: 2024 Form 760 Instructions, What's New + - title: 2024 Form 760 Instructions, What's New href: https://www.tax.virginia.gov/sites/default/files/vatax-pdf/2024-760-instructions.pdf#page=7 - title: 2024 Form 760 Instructions, Subtractions from Income # latest list of subtractions as of 2025-02-14 href: https://www.tax.virginia.gov/sites/default/files/vatax-pdf/2024-760-instructions.pdf#page=24 @@ -27,7 +29,7 @@ metadata: href: https://www.tax.virginia.gov/sites/default/files/taxforms/individual-income-tax/2023/schedule-adj-2023.pdf#page=1 - title: 2022 Virginia Schedule ADJ (Form 760-ADJ) href: https://www.tax.virginia.gov/sites/default/files/taxforms/individual-income-tax/2022/schedule-adj-2022.pdf#page=1 - - title: 2021 Virginia Schedule ADJ (Form 760-ADJ) + - title: 2021 Virginia Schedule ADJ (Form 760-ADJ) href: https://www.tax.virginia.gov/sites/default/files/taxforms/individual-income-tax/2021/schedule-adj-2021.pdf#page=1 - title: 2022 Form 760 Resident Individual Income Tax Booklet href: https://www.tax.virginia.gov/sites/default/files/vatax-pdf/2022-760-instructions.pdf#page=25 diff --git a/policyengine_us/tests/policy/baseline/gov/states/va/tax/income/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/va/tax/income/integration.yaml index 9eea69a76ae..e2f9869d1cb 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/va/tax/income/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/va/tax/income/integration.yaml @@ -140,6 +140,89 @@ va_itemized_deductions: 37_049.81 va_income_tax: 18175.545 +- name: MFJ both age 70 - Social Security recipient spouse does not trigger Spouse Tax Adjustment + # Regression test for https://github.com/PolicyEngine/policyengine-us/issues/8743. + # Virginia exempts Social Security, so a spouse whose only income is Social + # Security has no separate Virginia taxable income and the couple does not + # qualify for the Spouse Tax Adjustment. Confirmed against the Virginia Form + # 760 Spouse Tax Adjustment worksheet, TaxAct, and TAXSIM-35. + absolute_error_margin: 0.01 + period: 2025 + input: + people: + person1: + age: 70 + employment_income: 125_714 + taxable_interest_income: 255.5 + social_security_retirement: 14_391.5 + person2: + age: 70 + taxable_interest_income: 255.5 + social_security_retirement: 14_391.5 + tax_units: + tax_unit: + members: [person1, person2] + va_rebate: 0 + households: + household: + members: [person1, person2] + state_code: VA + output: + va_spouse_tax_adjustment_eligible: false + va_spouse_tax_adjustment: 0 + va_income_tax: 5_795.24 + +- name: MFJ - spouse with only Virginia-exempt unemployment does not trigger Spouse Tax Adjustment + # Virginia subtracts unemployment compensation (separate-VAGI worksheet, Line + # 17), so a spouse whose only income is unemployment has no separate Virginia + # taxable income and the couple does not qualify for the Spouse Tax Adjustment. + absolute_error_margin: 0.01 + period: 2025 + input: + people: + person1: + age: 45 + employment_income: 125_714 + person2: + age: 45 + unemployment_compensation: 25_000 + tax_units: + tax_unit: + members: [person1, person2] + va_rebate: 0 + households: + household: + members: [person1, person2] + state_code: VA + output: + va_spouse_tax_adjustment_eligible: false + va_spouse_tax_adjustment: 0 + +- name: MFJ - two earners still receive the Spouse Tax Adjustment (no over-correction) + # Guard against over-correcting: a genuinely two-earner couple (no exempt + # income) must still qualify for and receive the Spouse Tax Adjustment. + absolute_error_margin: 0.01 + period: 2025 + input: + people: + person1: + age: 45 + employment_income: 100_000 + person2: + age: 45 + employment_income: 40_000 + tax_units: + tax_unit: + members: [person1, person2] + va_rebate: 0 + households: + household: + members: [person1, person2] + state_code: VA + output: + va_spouse_tax_adjustment_eligible: true + va_spouse_tax_adjustment: 257.5 + - name: Joint couple with one child, 81k income each absolute_error_margin: 2 period: 2023 diff --git a/policyengine_us/tests/policy/baseline/gov/states/va/tax/income/va_agi_person.yaml b/policyengine_us/tests/policy/baseline/gov/states/va/tax/income/va_agi_person.yaml index bdb25acb603..b27b791a35c 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/va/tax/income/va_agi_person.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/va/tax/income/va_agi_person.yaml @@ -1,15 +1,16 @@ -- name: Attributing the state adjusted gross income based on federal AGI +- name: Each person's VAGI is their federal AGI less their own Virginia subtractions period: 2021 input: people: person1: adjusted_gross_income_person: 10_000 + va_subtractions_person: 2_500 person2: adjusted_gross_income_person: 30_000 + va_subtractions_person: 7_500 tax_units: tax_unit: members: [person1, person2] - va_subtractions: 10_000 households: household: members: [person1, person2] @@ -23,12 +24,13 @@ people: person1: adjusted_gross_income_person: 10_000 + va_subtractions_person: 2_000 person2: adjusted_gross_income_person: 0 + va_subtractions_person: 0 tax_units: tax_unit: members: [person1, person2] - va_subtractions: 2_000 households: household: members: [person1, person2] @@ -36,6 +38,52 @@ output: va_agi_person: [8_000, 0] +- name: Social Security subtraction applied directly to the recipient, not prorated + period: 2025 + input: + people: + person1: + age: 70 + employment_income: 100_000 + person2: + age: 70 + social_security_retirement: 20_000 + tax_units: + tax_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: VA + output: + # person2's $17,000 taxable Social Security is exempt in Virginia and is + # subtracted from her own Virginia AGI (not prorated to person1), so her + # separate VAGI is 0. The two columns still sum to the tax unit's VAGI. + va_agi_person: [100_000, 0] + +- name: Unemployment subtraction applied directly to the recipient, not prorated + period: 2025 + input: + people: + person1: + age: 45 + employment_income: 100_000 + person2: + age: 45 + unemployment_compensation: 15_000 + tax_units: + tax_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: VA + output: + # Virginia subtracts unemployment compensation; person2's $15,000 is + # attributed to her, so her separate VAGI is 0 rather than a prorated + # share of the subtraction. The columns still sum to the tax unit's VAGI. + va_agi_person: [100_000, 0] + - name: Age deduction applied directly to eligible person, not prorated period: 2021 input: diff --git a/policyengine_us/tests/policy/baseline/gov/states/va/tax/income/va_subtractions_person.yaml b/policyengine_us/tests/policy/baseline/gov/states/va/tax/income/va_subtractions_person.yaml new file mode 100644 index 00000000000..9e8338c315d --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/va/tax/income/va_subtractions_person.yaml @@ -0,0 +1,60 @@ +- name: Unemployment compensation is attributed to the recipient spouse + period: 2025 + input: + people: + person1: + age: 45 + employment_income: 100_000 + person2: + age: 45 + unemployment_compensation: 15_000 + tax_units: + tax_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: VA + output: + va_subtractions_person: [0, 15_000] + +- name: Disability income subtraction is attributed to the recipient spouse + period: 2025 + input: + people: + person1: + age: 45 + employment_income: 100_000 + person2: + age: 45 + disability_benefits: 15_000 + tax_units: + tax_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: VA + output: + va_subtractions_person: [0, 15_000] + +- name: Military basic pay subtraction is attributed to the service member + period: 2025 + input: + people: + person1: + age: 45 + military_basic_pay: 15_000 + employment_income: 15_000 + person2: + age: 45 + employment_income: 50_000 + tax_units: + tax_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: VA + output: + va_subtractions_person: [15_000, 0] diff --git a/policyengine_us/tests/policy/baseline/gov/states/va/tax/income/va_us_govt_bond_interest.yaml b/policyengine_us/tests/policy/baseline/gov/states/va/tax/income/va_us_govt_bond_interest.yaml index 7fc00ea5d09..ac003b0c7f8 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/va/tax/income/va_us_govt_bond_interest.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/va/tax/income/va_us_govt_bond_interest.yaml @@ -2,6 +2,6 @@ period: 2024 input: state_code: VA - us_govt_interest: 5_000 + us_govt_interest_person: 5_000 output: va_subtractions: 5_000 diff --git a/policyengine_us/variables/gov/states/va/tax/income/subtractions/va_529_plan_deduction_person.py b/policyengine_us/variables/gov/states/va/tax/income/subtractions/va_529_plan_deduction_person.py new file mode 100644 index 00000000000..6ce145c89e9 --- /dev/null +++ b/policyengine_us/variables/gov/states/va/tax/income/subtractions/va_529_plan_deduction_person.py @@ -0,0 +1,26 @@ +from policyengine_us.model_api import * + + +class va_529_plan_deduction_person(Variable): + value_type = float + entity = Person + label = "Virginia 529 plan deduction allocated to each person" + defined_for = StateCode.VA + unit = USD + definition_period = YEAR + reference = "https://law.lis.virginia.gov/vacodefull/title58.1/chapter3/article2/" + + def formula(person, period, parameters): + # The 529 deduction is a household contribution, not a person's income, + # so prorate it by federal AGI share. When the household has no positive + # federal AGI, assign it to the head so the per-person amounts still sum + # to the household deduction. + total = person.tax_unit("va_529_plan_deduction", period) + person_fagi = person("adjusted_gross_income_person", period) + total_federal_agi = person.tax_unit.sum(person_fagi) + share = where( + total_federal_agi > 0, + person_fagi / total_federal_agi, + person("is_tax_unit_head", period), + ) + return total * share diff --git a/policyengine_us/variables/gov/states/va/tax/income/subtractions/va_disability_income_subtraction.py b/policyengine_us/variables/gov/states/va/tax/income/subtractions/va_disability_income_subtraction.py index e5abdad83d6..6ca2e0f30bf 100644 --- a/policyengine_us/variables/gov/states/va/tax/income/subtractions/va_disability_income_subtraction.py +++ b/policyengine_us/variables/gov/states/va/tax/income/subtractions/va_disability_income_subtraction.py @@ -9,15 +9,4 @@ class va_disability_income_subtraction(Variable): unit = USD definition_period = YEAR reference = "https://law.lis.virginia.gov/vacodefull/title58.1/chapter3/article2/" - - def formula(tax_unit, period, parameters): - person = tax_unit.members - p = parameters(period).gov.states.va.tax.income.subtractions.disability_income - # Subtraction phases in and then out dollar for dollar with respect to disability income, at a given threshold. - subtractable_disability_income = min_( - person("disability_benefits", period), p.amount - ) - head = person("is_tax_unit_head", period) - spouse = person("is_tax_unit_spouse", period) - is_head_or_spouse = head | spouse - return tax_unit.sum(subtractable_disability_income * is_head_or_spouse) + adds = ["va_disability_income_subtraction_person"] diff --git a/policyengine_us/variables/gov/states/va/tax/income/subtractions/va_disability_income_subtraction_person.py b/policyengine_us/variables/gov/states/va/tax/income/subtractions/va_disability_income_subtraction_person.py new file mode 100644 index 00000000000..bf06f572a97 --- /dev/null +++ b/policyengine_us/variables/gov/states/va/tax/income/subtractions/va_disability_income_subtraction_person.py @@ -0,0 +1,16 @@ +from policyengine_us.model_api import * + + +class va_disability_income_subtraction_person(Variable): + value_type = float + entity = Person + label = "Virginia disability income subtraction for each person" + defined_for = StateCode.VA + unit = USD + definition_period = YEAR + reference = "https://law.lis.virginia.gov/vacodefull/title58.1/chapter3/article2/" + + def formula(person, period, parameters): + p = parameters(period).gov.states.va.tax.income.subtractions.disability_income + subtractable = min_(person("disability_benefits", period), p.amount) + return subtractable * person("is_tax_unit_head_or_spouse", period) diff --git a/policyengine_us/variables/gov/states/va/tax/income/subtractions/va_federal_state_employees_subtraction.py b/policyengine_us/variables/gov/states/va/tax/income/subtractions/va_federal_state_employees_subtraction.py index e4ca0a2037b..ccdb86780a5 100644 --- a/policyengine_us/variables/gov/states/va/tax/income/subtractions/va_federal_state_employees_subtraction.py +++ b/policyengine_us/variables/gov/states/va/tax/income/subtractions/va_federal_state_employees_subtraction.py @@ -9,17 +9,4 @@ class va_federal_state_employees_subtraction(Variable): unit = USD definition_period = YEAR reference = "https://law.lis.virginia.gov/vacodefull/title58.1/chapter3/article2/" - - def formula(tax_unit, period, parameters): - person = tax_unit.members - p = parameters(period).gov.states.va.tax.income.subtractions.disability_income - - employment_income = person("irs_employment_income", period) - state_or_federal_salary = person("state_or_federal_salary", period) - subtractable_federal_state_salary = where( - employment_income > p.amount, 0, state_or_federal_salary - ) - head = person("is_tax_unit_head", period) - spouse = person("is_tax_unit_spouse", period) - is_head_or_spouse = head | spouse - return tax_unit.sum(subtractable_federal_state_salary * is_head_or_spouse) + adds = ["va_federal_state_employees_subtraction_person"] diff --git a/policyengine_us/variables/gov/states/va/tax/income/subtractions/va_federal_state_employees_subtraction_person.py b/policyengine_us/variables/gov/states/va/tax/income/subtractions/va_federal_state_employees_subtraction_person.py new file mode 100644 index 00000000000..51fdad16aa8 --- /dev/null +++ b/policyengine_us/variables/gov/states/va/tax/income/subtractions/va_federal_state_employees_subtraction_person.py @@ -0,0 +1,21 @@ +from policyengine_us.model_api import * + + +class va_federal_state_employees_subtraction_person(Variable): + value_type = float + entity = Person + label = "Virginia federal and state employees subtraction for each person" + defined_for = StateCode.VA + unit = USD + definition_period = YEAR + reference = "https://law.lis.virginia.gov/vacodefull/title58.1/chapter3/article2/" + + def formula(person, period, parameters): + p = parameters(period).gov.states.va.tax.income.subtractions.disability_income + employment_income = person("irs_employment_income", period) + subtractable = where( + employment_income > p.amount, + 0, + person("state_or_federal_salary", period), + ) + return subtractable * person("is_tax_unit_head_or_spouse", period) diff --git a/policyengine_us/variables/gov/states/va/tax/income/subtractions/va_military_basic_pay/va_military_basic_pay_subtraction.py b/policyengine_us/variables/gov/states/va/tax/income/subtractions/va_military_basic_pay/va_military_basic_pay_subtraction.py index c31caa60991..47b4f13152f 100644 --- a/policyengine_us/variables/gov/states/va/tax/income/subtractions/va_military_basic_pay/va_military_basic_pay_subtraction.py +++ b/policyengine_us/variables/gov/states/va/tax/income/subtractions/va_military_basic_pay/va_military_basic_pay_subtraction.py @@ -9,20 +9,4 @@ class va_military_basic_pay_subtraction(Variable): unit = USD definition_period = YEAR reference = "https://law.lis.virginia.gov/vacodefull/title58.1/chapter3/article2/" - - def formula(tax_unit, period, parameters): - person = tax_unit.members - p = parameters(period).gov.states.va.tax.income.subtractions.military_basic_pay - # Compute subtractable military pay for head and spouse separately. - military_pay = person("military_basic_pay", period) - # Subtraction phases in and then out dollar for dollar with respect to military pay, at a given threshold. - subtractable_military_pay = where( - military_pay < p.threshold, - military_pay, - max_(0, (2 * p.threshold) - military_pay), - ) - is_head_or_spouse = person("is_tax_unit_head", period) | person( - "is_tax_unit_spouse", period - ) - # Sum subtractable military pay for heads and spouses. - return tax_unit.sum(subtractable_military_pay * is_head_or_spouse) + adds = ["va_military_basic_pay_subtraction_person"] diff --git a/policyengine_us/variables/gov/states/va/tax/income/subtractions/va_military_basic_pay/va_military_basic_pay_subtraction_person.py b/policyengine_us/variables/gov/states/va/tax/income/subtractions/va_military_basic_pay/va_military_basic_pay_subtraction_person.py new file mode 100644 index 00000000000..e7b84ef07ba --- /dev/null +++ b/policyengine_us/variables/gov/states/va/tax/income/subtractions/va_military_basic_pay/va_military_basic_pay_subtraction_person.py @@ -0,0 +1,22 @@ +from policyengine_us.model_api import * + + +class va_military_basic_pay_subtraction_person(Variable): + value_type = float + entity = Person + label = "Virginia military basic pay subtraction for each person" + defined_for = StateCode.VA + unit = USD + definition_period = YEAR + reference = "https://law.lis.virginia.gov/vacodefull/title58.1/chapter3/article2/" + + def formula(person, period, parameters): + p = parameters(period).gov.states.va.tax.income.subtractions.military_basic_pay + military_pay = person("military_basic_pay", period) + # Phases in then out dollar for dollar with respect to military pay. + subtractable = where( + military_pay < p.threshold, + military_pay, + max_(0, (2 * p.threshold) - military_pay), + ) + return subtractable * person("is_tax_unit_head_or_spouse", period) diff --git a/policyengine_us/variables/gov/states/va/tax/income/subtractions/va_military_benefit_subtraction.py b/policyengine_us/variables/gov/states/va/tax/income/subtractions/va_military_benefit_subtraction.py index 048a4a17f0a..19afdb38e88 100644 --- a/policyengine_us/variables/gov/states/va/tax/income/subtractions/va_military_benefit_subtraction.py +++ b/policyengine_us/variables/gov/states/va/tax/income/subtractions/va_military_benefit_subtraction.py @@ -9,19 +9,4 @@ class va_military_benefit_subtraction(Variable): unit = USD definition_period = YEAR reference = "https://law.lis.virginia.gov/vacodefull/title58.1/chapter3/article2/" - - def formula(tax_unit, period, parameters): - person = tax_unit.members - p = parameters(period).gov.states.va.tax.income.subtractions.military_benefit - - subtractable_military_benefit = min_( - person("military_retirement_pay", period), p.amount - ) - head_or_spouse = person("is_tax_unit_head_or_spouse", period) - if p.availability: - age = person("age", period) - age_eligible = age >= p.age_threshold - head_or_spouse_eligible = head_or_spouse & age_eligible - else: - head_or_spouse_eligible = head_or_spouse - return tax_unit.sum(subtractable_military_benefit * head_or_spouse_eligible) + adds = ["va_military_benefit_subtraction_person"] diff --git a/policyengine_us/variables/gov/states/va/tax/income/subtractions/va_military_benefit_subtraction_person.py b/policyengine_us/variables/gov/states/va/tax/income/subtractions/va_military_benefit_subtraction_person.py new file mode 100644 index 00000000000..7196e7e2112 --- /dev/null +++ b/policyengine_us/variables/gov/states/va/tax/income/subtractions/va_military_benefit_subtraction_person.py @@ -0,0 +1,19 @@ +from policyengine_us.model_api import * + + +class va_military_benefit_subtraction_person(Variable): + value_type = float + entity = Person + label = "Virginia military benefit subtraction for each person" + defined_for = StateCode.VA + unit = USD + definition_period = YEAR + reference = "https://law.lis.virginia.gov/vacodefull/title58.1/chapter3/article2/" + + def formula(person, period, parameters): + p = parameters(period).gov.states.va.tax.income.subtractions.military_benefit + subtractable = min_(person("military_retirement_pay", period), p.amount) + eligible = person("is_tax_unit_head_or_spouse", period) + if p.availability: + eligible = eligible & (person("age", period) >= p.age_threshold) + return subtractable * eligible diff --git a/policyengine_us/variables/gov/states/va/tax/income/subtractions/va_national_guard_subtraction_person.py b/policyengine_us/variables/gov/states/va/tax/income/subtractions/va_national_guard_subtraction_person.py new file mode 100644 index 00000000000..94b5274c607 --- /dev/null +++ b/policyengine_us/variables/gov/states/va/tax/income/subtractions/va_national_guard_subtraction_person.py @@ -0,0 +1,24 @@ +from policyengine_us.model_api import * + + +class va_national_guard_subtraction_person(Variable): + value_type = float + entity = Person + label = "Virginia National Guard pay subtraction allocated to each person" + defined_for = StateCode.VA + unit = USD + definition_period = YEAR + reference = "https://law.lis.virginia.gov/vacodefull/title58.1/chapter3/article2/" + + def formula(person, period, parameters): + # The subtraction caps the household's military service income, so + # allocate the capped tax-unit amount by each person's share. + total = person.tax_unit("va_national_guard_subtraction", period) + military_service_income = person("military_service_income", period) + total_military_service_income = person.tax_unit.sum(military_service_income) + share = where( + total_military_service_income > 0, + military_service_income / total_military_service_income, + 0, + ) + return total * share diff --git a/policyengine_us/variables/gov/states/va/tax/income/va_agi_person.py b/policyengine_us/variables/gov/states/va/tax/income/va_agi_person.py index 469082775e1..bbf929fa323 100644 --- a/policyengine_us/variables/gov/states/va/tax/income/va_agi_person.py +++ b/policyengine_us/variables/gov/states/va/tax/income/va_agi_person.py @@ -13,18 +13,18 @@ class va_agi_person(Variable): def formula(person, period, parameters): person_fagi = person("adjusted_gross_income_person", period) - # Apply person-level age deduction directly - age_deduction = person("va_age_deduction_person", period) - # Prorate non-age subtractions by federal AGI share - total_subtractions = person.tax_unit("va_subtractions", period) - total_age_deduction = person.tax_unit("va_age_deduction", period) - non_age_subtractions = max_(total_subtractions - total_age_deduction, 0) + # Virginia's "Worksheet for Determining Separate Virginia Adjusted Gross + # Income" (Form 760 instructions) attributes each subtraction to the + # spouse who received the underlying income (va_subtractions_person), + # then sets each spouse's separate VAGI. This matters for the Spouse Tax + # Adjustment: a spouse whose only income is Virginia-exempt (e.g. Social + # Security, railroad retirement, unemployment) has no separate Virginia + # taxable income, so the couple should not qualify for the adjustment. + person_subtractions = person("va_subtractions_person", period) + # Additions are only defined at the tax-unit level, so prorate them by + # federal AGI share. + additions = person.tax_unit("va_additions", period) total_federal_agi = person.tax_unit.sum(person_fagi) prorate = where(total_federal_agi > 0, person_fagi / total_federal_agi, 0) - person_non_age_subtractions = non_age_subtractions * prorate - # Prorate additions the same way - additions = person.tax_unit("va_additions", period) person_additions = additions * prorate - return ( - person_fagi + person_additions - age_deduction - person_non_age_subtractions - ) + return person_fagi + person_additions - person_subtractions diff --git a/policyengine_us/variables/gov/states/va/tax/income/va_subtractions_person.py b/policyengine_us/variables/gov/states/va/tax/income/va_subtractions_person.py new file mode 100644 index 00000000000..da551291e2c --- /dev/null +++ b/policyengine_us/variables/gov/states/va/tax/income/va_subtractions_person.py @@ -0,0 +1,22 @@ +from policyengine_us.model_api import * + + +class va_subtractions_person(Variable): + value_type = float + entity = Person + label = "Virginia adjusted gross income subtractions attributed to each person" + unit = USD + definition_period = YEAR + defined_for = StateCode.VA + reference = "https://www.tax.virginia.gov/sites/default/files/vatax-pdf/2022-760-instructions.pdf#page=19" + + def formula(person, period, parameters): + # Virginia's "Worksheet for Determining Separate Virginia Adjusted Gross + # Income" (Form 760 instructions, STEP 2) attributes each subtraction to + # the spouse who received the income. This sums the same person-level + # subtractions list that drives the tax-unit total (va_subtractions), so + # the per-person amounts sum to it exactly. + p = parameters(period).gov.states.va.tax.income.subtractions + total_subtractions = add(person, period, p.subtractions) + # Prevent negative subtractions from acting as additions. + return max_(0, total_subtractions)