From dee4c7a4f289d2ed3db9faf2716283d406d54952 Mon Sep 17 00:00:00 2001 From: PavelMakarchuk Date: Wed, 8 Apr 2026 18:45:31 -0400 Subject: [PATCH] Fix Georgia retirement exclusion capital loss handling --- .../fix-ga-retirement-capital-loss.fixed.md | 1 + .../agi/exclusions/retirement/sources.yaml | 5 +- .../gov/states/ga/tax/income/integration.yaml | 75 +++++++++++++++++++ ...nt_income_exclusion_retirement_income.yaml | 19 +++++ ...ement_exclusion_countable_capital_gains.py | 17 +++++ .../ga_retirement_exclusion_person.py | 2 +- ...ment_income_exclusion_retirement_income.py | 12 ++- 7 files changed, 126 insertions(+), 5 deletions(-) create mode 100644 changelog.d/fix-ga-retirement-capital-loss.fixed.md create mode 100644 policyengine_us/tests/policy/baseline/gov/states/ga/tax/income/subtractions/retirement/ga_retirement_income_exclusion_retirement_income.yaml create mode 100644 policyengine_us/variables/gov/states/ga/tax/income/subtractions/retirement/ga_retirement_exclusion_countable_capital_gains.py diff --git a/changelog.d/fix-ga-retirement-capital-loss.fixed.md b/changelog.d/fix-ga-retirement-capital-loss.fixed.md new file mode 100644 index 00000000000..ac97535b72b --- /dev/null +++ b/changelog.d/fix-ga-retirement-capital-loss.fixed.md @@ -0,0 +1 @@ +Georgia's retirement income exclusion now uses federally loss-limited capital gains and matches the Schedule 1 worksheet by flooring earned income and other retirement income separately before applying the exclusion cap. diff --git a/policyengine_us/parameters/gov/states/ga/tax/income/agi/exclusions/retirement/sources.yaml b/policyengine_us/parameters/gov/states/ga/tax/income/agi/exclusions/retirement/sources.yaml index 4055ae20a9c..849f9a67c47 100644 --- a/policyengine_us/parameters/gov/states/ga/tax/income/agi/exclusions/retirement/sources.yaml +++ b/policyengine_us/parameters/gov/states/ga/tax/income/agi/exclusions/retirement/sources.yaml @@ -1,4 +1,4 @@ -description: Georgia accounts for the following income sources under the retirement exclusion. +description: Georgia accounts for these non-earned income sources on lines 6 through 13 of the retirement income exclusion worksheet. metadata: label: Georgia retirement income exclusion income sources period: year @@ -17,8 +17,7 @@ values: - interest_income - dividend_income - rental_income # royalties included in the rental income - - capital_gains + - ga_retirement_exclusion_countable_capital_gains - alimony_income - taxable_pension_income - taxable_ira_distributions - - ga_retirement_exclusion_countable_earned_income # earned income: capped diff --git a/policyengine_us/tests/policy/baseline/gov/states/ga/tax/income/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/ga/tax/income/integration.yaml index 13161acc0b3..c09382cf3b2 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ga/tax/income/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ga/tax/income/integration.yaml @@ -73,3 +73,78 @@ state_fips: 13 output: ga_income_tax: 0 + +- name: Capital loss does not wipe out Georgia retirement exclusion earned income + absolute_error_margin: 1 + period: 2024 + input: + people: + person1: + age: 69 + employment_income: 10_000 + short_term_capital_gains: -10_000 + is_tax_unit_head: true + tax_units: + tax_unit: + members: [person1] + premium_tax_credit: 0 + local_income_tax: 0 + state_sales_tax: 0 + spm_units: + spm_unit: + members: [person1] + snap: 0 + tanf: 0 + households: + household: + members: [person1] + state_fips: 13 + output: + ga_retirement_exclusion: 5_000 + ga_subtractions: 5_000 + ga_agi: 2_000 + +- name: Georgia retirement exclusion uses loss-limited capital losses + absolute_error_margin: 10 + period: 2024 + input: + people: + primary: + age: 69 + employment_income: 123_105 + taxable_interest_income: 8_156 + long_term_capital_gains: 52_909 + short_term_capital_gains: -248_110 + taxable_private_pension_income: 13_440 + is_tax_unit_head: true + spouse: + age: 63 + taxable_interest_income: 8_156 + long_term_capital_gains: 52_909 + short_term_capital_gains: -248_110 + taxable_private_pension_income: 13_440 + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [primary, spouse] + premium_tax_credit: 0 + local_income_tax: 0 + state_sales_tax: 0 + marital_units: + marital_unit: + members: [primary, spouse] + families: + family: + members: [primary, spouse] + spm_units: + spm_unit: + members: [primary, spouse] + snap: 0 + tanf: 0 + households: + household: + members: [primary, spouse] + state_code: GA + output: + ga_retirement_exclusion: 45_192 + ga_agi: 118_105 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ga/tax/income/subtractions/retirement/ga_retirement_income_exclusion_retirement_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/ga/tax/income/subtractions/retirement/ga_retirement_income_exclusion_retirement_income.yaml new file mode 100644 index 00000000000..3912059a7b1 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ga/tax/income/subtractions/retirement/ga_retirement_income_exclusion_retirement_income.yaml @@ -0,0 +1,19 @@ +- name: Earned income is preserved when other retirement income is negative + period: 2024 + input: + state_code: GA + ga_retirement_exclusion_countable_earned_income: 5_000 + ga_retirement_exclusion_countable_capital_gains: -3_000 + output: + ga_retirement_income_exclusion_retirement_income: 5_000 + +- name: Retirement income adds earned income and floored other income separately + period: 2024 + input: + state_code: GA + ga_retirement_exclusion_countable_earned_income: 5_000 + ga_retirement_exclusion_countable_capital_gains: -1_500 + taxable_interest_income: 8_156 + taxable_private_pension_income: 13_440 + output: + ga_retirement_income_exclusion_retirement_income: 25_096 diff --git a/policyengine_us/variables/gov/states/ga/tax/income/subtractions/retirement/ga_retirement_exclusion_countable_capital_gains.py b/policyengine_us/variables/gov/states/ga/tax/income/subtractions/retirement/ga_retirement_exclusion_countable_capital_gains.py new file mode 100644 index 00000000000..ac17b2bbae7 --- /dev/null +++ b/policyengine_us/variables/gov/states/ga/tax/income/subtractions/retirement/ga_retirement_exclusion_countable_capital_gains.py @@ -0,0 +1,17 @@ +from policyengine_us.model_api import * + + +class ga_retirement_exclusion_countable_capital_gains(Variable): + value_type = float + entity = Person + label = "Countable capital gains (losses) for the Georgia retirement exclusion" + unit = USD + definition_period = YEAR + reference = ( + "https://dor.georgia.gov/document/document/2024-it-511-individual-income-tax-booklet/download", # Schedule 1, Page 2 + "https://www.irs.gov/pub/irs-pdf/f1040sd.pdf", + ) + defined_for = StateCode.GA + + def formula(person, period, parameters): + return person("loss_limited_net_capital_gains_person", period) diff --git a/policyengine_us/variables/gov/states/ga/tax/income/subtractions/retirement/ga_retirement_exclusion_person.py b/policyengine_us/variables/gov/states/ga/tax/income/subtractions/retirement/ga_retirement_exclusion_person.py index a027044f152..1f47ead0d79 100644 --- a/policyengine_us/variables/gov/states/ga/tax/income/subtractions/retirement/ga_retirement_exclusion_person.py +++ b/policyengine_us/variables/gov/states/ga/tax/income/subtractions/retirement/ga_retirement_exclusion_person.py @@ -23,4 +23,4 @@ def formula(person, period, parameters): retirement_income = person( "ga_retirement_income_exclusion_retirement_income", period ) - return min_(retirement_income, cap) + return max_(0, min_(retirement_income, cap)) diff --git a/policyengine_us/variables/gov/states/ga/tax/income/subtractions/retirement/ga_retirement_income_exclusion_retirement_income.py b/policyengine_us/variables/gov/states/ga/tax/income/subtractions/retirement/ga_retirement_income_exclusion_retirement_income.py index d36f2b07002..7b81a1610bd 100644 --- a/policyengine_us/variables/gov/states/ga/tax/income/subtractions/retirement/ga_retirement_income_exclusion_retirement_income.py +++ b/policyengine_us/variables/gov/states/ga/tax/income/subtractions/retirement/ga_retirement_income_exclusion_retirement_income.py @@ -10,8 +10,18 @@ class ga_retirement_income_exclusion_retirement_income(Variable): reference = ( "https://dor.georgia.gov/document/booklet/2021-it-511-individual-income-tax-booklet/download", # Page 15 "https://dor.georgia.gov/document/document/2022-it-511-individual-income-tax-booklet/download", # Page 15 + "https://dor.georgia.gov/document/document/2024-it-511-individual-income-tax-booklet/download", # Schedule 1, Page 2 "https://advance.lexis.com/documentpage/?pdmfid=1000516&crid=fb5db531-a80f-4790-bddb-eefc8327ef60&config=00JAA1MDBlYzczZi1lYjFlLTQxMTgtYWE3OS02YTgyOGM2NWJlMDYKAFBvZENhdGFsb2feed0oM9qoQOMCSJFX5qkd&pddocfullpath=%2Fshared%2Fdocument%2Fstatutes-legislation%2Furn%3AcontentItem%3A65D2-CDH3-CGX8-044N-00008-00&pdcontentcomponentid=234186&pdteaserkey=sr1&pditab=allpods&ecomp=8s65kkk&earg=sr1&prid=66f02b0a-c5ae-4162-9535-127751546807", ) defined_for = StateCode.GA - adds = "gov.states.ga.tax.income.agi.exclusions.retirement.sources" + def formula(person, period, parameters): + p = parameters(period).gov.states.ga.tax.income.agi.exclusions.retirement + # Georgia Schedule 1 Page 2 floors the earned-income portion and the + # remaining retirement-income sources separately before adding them. + countable_earned_income = max_( + 0, person("ga_retirement_exclusion_countable_earned_income", period) + ) + other_retirement_income = add(person, period, p.sources) + countable_other_retirement_income = max_(0, other_retirement_income) + return countable_earned_income + countable_other_retirement_income