Repository: PolicyEngine/policyengine-us

Issue:
Limit reported SALT to the amount that would zero out regular tax liability
Otherwise incremental SALT can increase tax liability via the AMT. Filers will optimally report only the SALT that zeros out their regular tax liability. There is no legal requirement to itemize your entire SALT.

This should replace the code added in https://github.com/PolicyEngine/policyengine-us/pull/5883

Discussion:

Solution patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e74a1d0c64..10041d8378 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## [1.260.0] - 2025-04-28 16:42:01
+
+### Added
+
+- Adjust ctc_value when activating fully refundable CTC.
+
 ## [1.259.0] - 2025-04-28 16:19:52
 
 ### Added
@@ -11481,6 +11487,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 
 
+[1.260.0]: https://github.com/PolicyEngine/policyengine-us/compare/1.259.0...1.260.0
 [1.259.0]: https://github.com/PolicyEngine/policyengine-us/compare/1.258.4...1.259.0
 [1.258.4]: https://github.com/PolicyEngine/policyengine-us/compare/1.258.3...1.258.4
 [1.258.3]: https://github.com/PolicyEngine/policyengine-us/compare/1.258.2...1.258.3
diff --git a/changelog.yaml b/changelog.yaml
index 396bd9003a..7d56202f96 100644
--- a/changelog.yaml
+++ b/changelog.yaml
@@ -9738,3 +9738,8 @@
     added:
     - Include taxable IRA distributions in market income.
   date: 2025-04-28 16:19:52
+- bump: minor
+  changes:
+    added:
+    - Adjust ctc_value when activating fully refundable CTC.
+  date: 2025-04-28 16:42:01
diff --git a/changelog_entry.yaml b/changelog_entry.yaml
index e69de29bb2..da1d867c1e 100644
--- a/changelog_entry.yaml
+++ b/changelog_entry.yaml
@@ -0,0 +1,7 @@
+- bump: minor
+  changes:
+    added:
+    - Limit reported SALT to the amount that would zero out regular tax liability.
+    - Create SALT and reported SALT variables.
+    fixes:
+    - Remove itemized deductions limitation at taxable income.
diff --git a/policyengine_us/reforms/salt_phase_out/salt_phase_out_reform.py b/policyengine_us/reforms/salt_phase_out/salt_phase_out_reform.py
index 97ff4348aa..a22c4a6aea 100644
--- a/policyengine_us/reforms/salt_phase_out/salt_phase_out_reform.py
+++ b/policyengine_us/reforms/salt_phase_out/salt_phase_out_reform.py
@@ -16,11 +16,7 @@ def create_salt_phase_out() -> Reform:
             p = parameters(
                 period
             ).gov.irs.deductions.itemized.salt_and_real_estate
-            salt_amount = add(
-                tax_unit,
-                period,
-                p.sources,
-            )
+            salt_amount = tax_unit("reported_salt", period)
             filing_status = tax_unit("filing_status", period)
             cap = p.cap[filing_status]
             p_ref = parameters(period).gov.contrib.salt_phase_out
diff --git a/policyengine_us/variables/gov/irs/credits/ctc/ctc_value.py b/policyengine_us/variables/gov/irs/credits/ctc/ctc_value.py
index 6819da3d10..b5ae83d0a2 100644
--- a/policyengine_us/variables/gov/irs/credits/ctc/ctc_value.py
+++ b/policyengine_us/variables/gov/irs/credits/ctc/ctc_value.py
@@ -10,7 +10,9 @@ class ctc_value(Variable):
     definition_period = YEAR
 
     def formula(tax_unit, period, parameters):
-        return min_(
-            tax_unit("ctc", period),
-            tax_unit("ctc_phase_in", period),
-        )
+        ctc = tax_unit("ctc", period)
+        p = parameters(period).gov.irs.credits.ctc.refundable
+        if not p.fully_refundable:
+            phase_in = tax_unit("ctc_phase_in", period)
+            return min_(ctc, phase_in)
+        return ctc
diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/deductions/itemizing/reported_salt.py b/policyengine_us/variables/gov/irs/income/taxable_income/deductions/itemizing/reported_salt.py
new file mode 100644
index 0000000000..1fbf159b1f
--- /dev/null
+++ b/policyengine_us/variables/gov/irs/income/taxable_income/deductions/itemizing/reported_salt.py
@@ -0,0 +1,19 @@
+from policyengine_us.model_api import *
+
+
+class reported_salt(Variable):
+    value_type = float
+    entity = TaxUnit
+    definition_period = YEAR
+    label = "Reported State and local sales or income tax and real estate taxes subject to the SALT deduction limited to taxable income"
+    unit = USD
+
+    def formula(tax_unit, period, parameters):
+        salt = tax_unit("salt", period)
+        p = parameters(period).gov.simulation
+        if p.limit_itemized_deductions_to_taxable_income:
+            agi = tax_unit("adjusted_gross_income", period)
+            exemptions = tax_unit("exemptions", period)
+            agi_less_exemptions = max_(0, agi - exemptions)
+            return min_(salt, agi_less_exemptions)
+        return salt
diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/deductions/itemizing/salt.py b/policyengine_us/variables/gov/irs/income/taxable_income/deductions/itemizing/salt.py
new file mode 100644
index 0000000000..254660a8cb
--- /dev/null
+++ b/policyengine_us/variables/gov/irs/income/taxable_income/deductions/itemizing/salt.py
@@ -0,0 +1,11 @@
+from policyengine_us.model_api import *
+
+
+class salt(Variable):
+    value_type = float
+    entity = TaxUnit
+    definition_period = YEAR
+    label = "State and local sales or income tax and real estate taxes subject to the SALT deduction"
+    unit = USD
+
+    adds = "gov.irs.deductions.itemized.salt_and_real_estate.sources"
diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/deductions/itemizing/salt_deduction.py b/policyengine_us/variables/gov/irs/income/taxable_income/deductions/itemizing/salt_deduction.py
index 87db6a4166..149e898ebc 100644
--- a/policyengine_us/variables/gov/irs/income/taxable_income/deductions/itemizing/salt_deduction.py
+++ b/policyengine_us/variables/gov/irs/income/taxable_income/deductions/itemizing/salt_deduction.py
@@ -12,10 +12,6 @@ class salt_deduction(Variable):
 
     def formula(tax_unit, period, parameters):
         p = parameters(period).gov.irs.deductions.itemized.salt_and_real_estate
-        salt_amount = add(
-            tax_unit,
-            period,
-            p.sources,
-        )
+        reported_salt = tax_unit("reported_salt", period)
         cap = p.cap[tax_unit("filing_status", period)]
-        return min_(cap, salt_amount)
+        return min_(cap, reported_salt)
diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/deductions/taxable_income_deductions.py b/policyengine_us/variables/gov/irs/income/taxable_income/deductions/taxable_income_deductions.py
index b1cf7a6ec7..30f2ac8fad 100644
--- a/policyengine_us/variables/gov/irs/income/taxable_income/deductions/taxable_income_deductions.py
+++ b/policyengine_us/variables/gov/irs/income/taxable_income/deductions/taxable_income_deductions.py
@@ -16,21 +16,6 @@ class taxable_income_deductions(Variable):
         deductions_if_not_itemizing = tax_unit(
             "taxable_income_deductions_if_not_itemizing", period
         )
-        # Limiting itemized deductions to taxable income, if itemizing
-        p = parameters(period).gov.simulation
-        if p.limit_itemized_deductions_to_taxable_income:
-            agi = tax_unit("adjusted_gross_income", period)
-            exemptions = tax_unit("exemptions", period)
-            itemizes = tax_unit("tax_unit_itemizes", period)
-            agi_less_exemptions = max_(0, agi - exemptions)
-            capped_deductions_if_itemizing = min_(
-                deductions_if_itemizing, agi_less_exemptions
-            )
-            return where(
-                itemizes,
-                capped_deductions_if_itemizing,
-                deductions_if_not_itemizing,
-            )
         return where(
             itemizes, deductions_if_itemizing, deductions_if_not_itemizing
         )
diff --git a/setup.py b/setup.py
index 7fa37fd750..ed069fe55e 100644
--- a/setup.py
+++ b/setup.py
@@ -7,7 +7,7 @@ with open("README.md", "r") as readme_file:
 
 setup(
     name="policyengine-us",
-    version="1.259.0",
+    version="1.260.0",
     author="PolicyEngine",
     author_email="hello@policyengine.org",
     long_description=readme,


Test patch:
diff --git a/policyengine_us/tests/policy/baseline/gov/irs/credits/ctc/refundable/ctc_value.yaml b/policyengine_us/tests/policy/baseline/gov/irs/credits/ctc/refundable/ctc_value.yaml
index e33f1759c4..1a0ae8edc0 100644
--- a/policyengine_us/tests/policy/baseline/gov/irs/credits/ctc/refundable/ctc_value.yaml
+++ b/policyengine_us/tests/policy/baseline/gov/irs/credits/ctc/refundable/ctc_value.yaml
@@ -59,3 +59,26 @@
     refundable_ctc: 1_410
     non_refundable_ctc: 2_590
     ctc_value: 4_000
+
+- name: Fully refundable CTC
+  period: 2023
+  input:
+    gov.irs.credits.ctc.refundable.fully_refundable: true
+    people:
+      person1:
+        age:
+        employment_income: 1_000
+      person2:
+        age: 10
+      person3:
+        age: 5
+    tax_units:
+      tax_unit:
+        members: [person1, person2, person3]
+  output:
+    ctc: 4_000
+    ctc_limiting_tax_liability: 0
+    ctc_phase_in: 0
+    refundable_ctc: 4_000
+    non_refundable_ctc: 0
+    ctc_value: 4_000
diff --git a/policyengine_us/tests/policy/baseline/gov/irs/deductions/itemized/salt_and_real_estate/reported_salt.yaml b/policyengine_us/tests/policy/baseline/gov/irs/deductions/itemized/salt_and_real_estate/reported_salt.yaml
new file mode 100644
index 0000000000..69a25bacd5
--- /dev/null
+++ b/policyengine_us/tests/policy/baseline/gov/irs/deductions/itemized/salt_and_real_estate/reported_salt.yaml
@@ -0,0 +1,53 @@
+- name: SALT capped at taxable income
+  period: 2023
+  input:
+    salt: 100_000
+    adjusted_gross_income: 100_000
+    exemptions: 2_000
+  output:
+    reported_salt: 98_000
+
+- name: SALT below cap
+  period: 2023
+  input:
+    salt: 23_000
+    adjusted_gross_income: 100_000
+    exemptions: 2_000
+  output:
+    reported_salt: 23_000
+
+- name: SALT uncapped
+  period: 2023
+  input:
+    gov.simulation.limit_itemized_deductions_to_taxable_income: False
+    salt: 100_000
+    adjusted_gross_income: 100_000
+    exemptions: 2_000
+  output:
+    reported_salt: 100_000
+
+- name: Integration test with amt, limit is applied
+  period: 2026
+  input:
+    gov.simulation.limit_itemized_deductions_to_taxable_income: True
+    salt: 45_000
+    employment_income: 40_000
+  output:
+    adjusted_gross_income: 40_000
+    exemptions: 5_300
+    reported_salt: 34_700
+    amt_income: 40_000
+    taxable_income: 0
+
+- name: Integration test with amt, limit is not applied
+  period: 2026
+  input:
+    gov.simulation.limit_itemized_deductions_to_taxable_income: False
+    salt: 45_000
+    employment_income: 40_000
+  output:
+    adjusted_gross_income: 40_000
+    exemptions: 5_300
+    reported_salt: 45_000
+    amt_income: 50_300
+    taxable_income: 0
diff --git a/policyengine_us/tests/policy/baseline/gov/irs/deductions/itemized/salt_and_real_estate/salt_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/irs/deductions/itemized/salt_and_real_estate/salt_deduction.yaml
new file mode 100644
index 0000000000..8cf7f3665c
--- /dev/null
+++ b/policyengine_us/tests/policy/baseline/gov/irs/deductions/itemized/salt_and_real_estate/salt_deduction.yaml
@@ -0,0 +1,15 @@
+- name: Capped SALT deduction
+  period: 2023
+  input:
+    reported_salt: 100_000
+    filing_status: SINGLE
+  output:
+    salt_deduction: 10_000
+
+- name: Cap does not apply
+  period: 2023
+  input:
+    reported_salt: 4_000
+    filing_status: SINGLE
+  output:
+    salt_deduction: 4_000
diff --git a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/taxable_income_deductions.yaml b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/taxable_income_deductions.yaml
index 44ba914cca..004480f241 100644
--- a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/taxable_income_deductions.yaml
+++ b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/taxable_income_deductions.yaml
@@ -21,39 +21,3 @@
     exemptions: 12_000
   output:
     taxable_income_deductions: 10_000
-
-- name: Unit itemizes, sim, capped itemized deductions
-  period: 2024
-  input:
-    gov.simulation.limit_itemized_deductions_to_taxable_income: true
-    tax_unit_itemizes: true
-    taxable_income_deductions_if_itemizing: 11_000
-    taxable_income_deductions_if_not_itemizing: 10_000
-    adjusted_gross_income: 3_000
-    exemptions: 1_000
-  output:
-    taxable_income_deductions: 2_000
-
-- name: Unit itemizes, sim, un-capped itemized deductions
-  period: 2024
-  input:
-    gov.simulation.limit_itemized_deductions_to_taxable_income: true
-    tax_unit_itemizes: true
-    taxable_income_deductions_if_itemizing: 11_000
-    taxable_income_deductions_if_not_itemizing: 10_000
-    adjusted_gross_income: 23_000
-    exemptions: 1_000
-  output:
-    taxable_income_deductions: 11_000
-
-- name: Unit does not itemize, sim, un-capped itemized deductions
-  period: 2024
-  input:
-    gov.simulation.limit_itemized_deductions_to_taxable_income: true
-    tax_unit_itemizes: false
-    taxable_income_deductions_if_itemizing: 11_000
-    taxable_income_deductions_if_not_itemizing: 10_000
-    adjusted_gross_income: 1_000
-    exemptions: 1_000
-  output:
-    taxable_income_deductions: 10_000
diff --git a/policyengine_us/tests/policy/baseline/gov/states/ia/tax/income/ia_itemized_deductions_unit.yaml b/policyengine_us/tests/policy/baseline/gov/states/ia/tax/income/ia_itemized_deductions_unit.yaml
index 002ffa27dd..ad62b3d64e 100644
--- a/policyengine_us/tests/policy/baseline/gov/states/ia/tax/income/ia_itemized_deductions_unit.yaml
+++ b/policyengine_us/tests/policy/baseline/gov/states/ia/tax/income/ia_itemized_deductions_unit.yaml
@@ -29,7 +29,7 @@
     state_code: IA
     filing_status: JOINT
   output:
-    ia_itemized_deductions_unit: 10_000
+    ia_itemized_deductions_unit: 0
 
 - name: Uncapped SALT deduction pre 2023
   period: 2022
diff --git a/policyengine_us/tests/policy/contrib/deductions/salt/limit_salt_deduction_to_property_taxes.yaml b/policyengine_us/tests/policy/contrib/deductions/salt/limit_salt_deduction_to_property_taxes.yaml
index 0ce8b9de5d..7c14b37ca2 100644
--- a/policyengine_us/tests/policy/contrib/deductions/salt/limit_salt_deduction_to_property_taxes.yaml
+++ b/policyengine_us/tests/policy/contrib/deductions/salt/limit_salt_deduction_to_property_taxes.yaml
@@ -5,6 +5,7 @@
     gov.contrib.deductions.salt.limit_salt_deduction_to_property_taxes: true
     real_estate_taxes: 5_000
     state_and_local_sales_or_income_tax: 6_000
+    adjusted_gross_income: 100_000
     filing_status: JOINT
   output:
     salt_deduction: 5_000
@@ -15,6 +16,7 @@
     gov.contrib.deductions.salt.limit_salt_deduction_to_property_taxes: false
     real_estate_taxes: 5_000
     state_and_local_sales_or_income_tax: 6_000
+    adjusted_gross_income: 100_000
     filing_status: JOINT
   output:
     salt_deduction: 10_000
@@ -26,6 +28,7 @@
     gov.contrib.deductions.salt.limit_salt_deduction_to_property_taxes: true
     real_estate_taxes: 5_000
     state_and_local_sales_or_income_tax: 6_000
+    adjusted_gross_income: 100_000
     filing_status: JOINT
   output:
     salt_deduction: 5_000
@@ -36,6 +39,7 @@
     gov.contrib.deductions.salt.limit_salt_deduction_to_property_taxes: false
     real_estate_taxes: 5_000
     state_and_local_sales_or_income_tax: 6_000
+    adjusted_gross_income: 100_000
     filing_status: JOINT
   output:
     salt_deduction: 11_000