diff --git a/changelog_entry.yaml b/changelog_entry.yaml index e69de29bb2d..2f12b7dc738 100644 --- a/changelog_entry.yaml +++ b/changelog_entry.yaml @@ -0,0 +1,4 @@ +- bump: minor + changes: + added: + - Split non-cash charitable deduction AGI cap by organization type per 26 USC 170(b)(1). Non-cash donations to 50-percent limit organizations are capped at 50% of AGI, while donations to other organizations are capped at 30% of AGI. diff --git a/policyengine_us/parameters/gov/irs/deductions/itemized/charity/ceiling/non_cash_to_non_50_pct_org.yaml b/policyengine_us/parameters/gov/irs/deductions/itemized/charity/ceiling/non_cash_to_non_50_pct_org.yaml new file mode 100644 index 00000000000..0b96ecd97c3 --- /dev/null +++ b/policyengine_us/parameters/gov/irs/deductions/itemized/charity/ceiling/non_cash_to_non_50_pct_org.yaml @@ -0,0 +1,16 @@ +description: >- + Ceiling (as a fraction of AGI) for noncash charitable contributions + to organizations other than 50-percent limit organizations. + Note: 170(b)(1)(D) sets a stricter 20% cap for capital gain property + to these organizations, which is not yet modeled separately. +metadata: + unit: /1 + period: year + label: Non-cash charitable deduction limit for non-50% limit organizations + reference: + - title: 26 U.S. Code 170(b)(1)(B) - General 30% limit for non-50% limit organizations + href: https://www.law.cornell.edu/uscode/text/26/170#b_1_B + - title: 26 U.S. Code 170(b)(1)(D) - 20% limit for capital gain property to non-50% limit organizations + href: https://www.law.cornell.edu/uscode/text/26/170#b_1_D +values: + 2013-01-01: 0.3 diff --git a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/itemizing/charitable_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/itemizing/charitable_deduction.yaml index 74d9406b340..4d0ee38f078 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/itemizing/charitable_deduction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/itemizing/charitable_deduction.yaml @@ -5,7 +5,7 @@ charitable_non_cash_donations: 500 positive_agi: 1_000 charitable_deduction_for_non_itemizers: 0 - output: # min(min(500, 1000 * 0.3) + 300, 1000 * 0.5) + output: # min(min(500, 1000 * 0.5) + 300, 1000 * 0.5) charitable_deduction: 500 - name: 2018 filer @@ -14,7 +14,7 @@ charitable_cash_donations: 500 charitable_non_cash_donations: 200 positive_agi: 1_000 - output: # min(min(200, 1000 * 0.3) + 500, 1000 * 0.6) + output: # min(min(200, 1000 * 0.5) + 500, 1000 * 0.6) charitable_deduction: 600 - name: 2020 filer @@ -24,7 +24,7 @@ charitable_non_cash_donations: 200 positive_agi: 1_000 charitable_deduction_for_non_itemizers: 0 - output: # min(min(200, 1000 * 0.3) + 500, 1000 * 1) + output: # min(min(200, 1000 * 0.5) + 500, 1000 * 1) charitable_deduction: 700 - name: 2021 filer @@ -34,7 +34,7 @@ charitable_non_cash_donations: 200 positive_agi: 1_000 charitable_deduction_for_non_itemizers: 300 - output: # min(min(200, 1000 * 0.3) + (500 - 300), 1000 * 0.6) + output: # min(min(200, 1000 * 0.5) + (500 - 300), 1000 * 0.6) charitable_deduction: 600 - name: 2021 filer 2 @@ -44,7 +44,7 @@ charitable_non_cash_donations: 200 positive_agi: 1_000 charitable_deduction_for_non_itemizers: 600 - output: # min(min(200, 1000 * 0.3) + min(500 - 600, 0), 1000 * 0.6) + output: # min(min(200, 1000 * 0.5) + max(500 - 600, 0), 1000 * 0.6) charitable_deduction: 600 - name: No AGI @@ -91,3 +91,73 @@ positive_agi: 20_000 output: charitable_deduction: 12_000 + +- name: Non-cash to non-50% limit orgs capped at 30% of AGI + period: 2024 + input: + charitable_cash_donations: 0 + charitable_non_cash_donations: 500 + charitable_non_cash_donations_non_50_pct_orgs: 500 + positive_agi: 1_000 + output: + # 50% org non-cash = 0 + # non-50% org: min(500, 0.3 * 1000) = 300 + # min(0 + 300, 0.6 * 1000) = 300 + charitable_deduction: 300 + +- name: Mix of 50% and non-50% org non-cash donations + period: 2024 + input: + charitable_cash_donations: 0 + charitable_non_cash_donations: 600 + charitable_non_cash_donations_non_50_pct_orgs: 200 + positive_agi: 1_000 + output: + # 50% org: min(400, 500) = 400 + # non-50% org: min(200, 300) = 200 + # min(400 + 200, 600) = 600 + charitable_deduction: 600 + +- name: Overall ceiling binds with mixed org types + period: 2024 + input: + charitable_cash_donations: 0 + charitable_non_cash_donations: 800 + charitable_non_cash_donations_non_50_pct_orgs: 400 + positive_agi: 1_000 + output: + # 50% org: min(400, 500) = 400 + # non-50% org: min(400, 300) = 300 + # min(400 + 300, 600) = 600 + charitable_deduction: 600 + +- name: Non-50% org only hits 30% cap + period: 2024 + input: + charitable_cash_donations: 0 + charitable_non_cash_donations: 400 + charitable_non_cash_donations_non_50_pct_orgs: 400 + positive_agi: 1_000 + output: + # non-50% org: min(400, 300) = 300 + # min(300, 600) = 300 + charitable_deduction: 300 + +- name: Non-50% org with floor and cash + period: 2026 + input: + charitable_cash_donations: 5_000 + charitable_non_cash_donations: 20_000 + charitable_non_cash_donations_non_50_pct_orgs: 5_000 + positive_agi: 100_000 + output: + # floor = 0.005 * 100000 = 500 + # 50% org non-cash = 15000, reduced = max(15000 - 500, 0) = 14500 + # capped 50% = min(14500, 50000) = 14500 + # remaining_floor_after_50 = max(500 - 15000, 0) = 0 + # non-50% non-cash = 5000, reduced = max(5000 - 0, 0) = 5000 + # capped 30% = min(5000, 30000) = 5000 + # remaining_floor = max(0 - 5000, 0) = 0 + # cash = max(5000 - 0, 0) = 5000 + # min(14500 + 5000 + 5000, 60000) = 24500 + charitable_deduction: 24_500 diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/deductions/itemizing/charitable_deduction.py b/policyengine_us/variables/gov/irs/income/taxable_income/deductions/itemizing/charitable_deduction.py index e18550282ec..c55323b7c7c 100644 --- a/policyengine_us/variables/gov/irs/income/taxable_income/deductions/itemizing/charitable_deduction.py +++ b/policyengine_us/variables/gov/irs/income/taxable_income/deductions/itemizing/charitable_deduction.py @@ -11,30 +11,74 @@ class charitable_deduction(Variable): reference = "https://www.law.cornell.edu/uscode/text/26/170" def formula(tax_unit, period, parameters): + # TODO: 26 USC 170(b)(1) defines 4 contribution categories with + # separate AGI caps: + # (G) Cash to 50% limit orgs: 60% AGI (post-TCJA) + # (B) Cash to non-50% limit orgs: 30% AGI + # (C) Capital gain property to 50% limit orgs: 30% AGI + # (or 50% with basis-reduction election) + # (D) Capital gain property to non-50% limit orgs: 20% AGI + # Currently we model two non-cash categories (50% vs 30% cap) + # and treat all cash as subject to the overall cap only. + # The cash split and the 20% category are not yet modeled. cash_donations = add(tax_unit, period, ["charitable_cash_donations"]) non_cash_donations = add( tax_unit, period, ["charitable_non_cash_donations"] ) + non_cash_to_non_50_pct = add( + tax_unit, + period, + ["charitable_non_cash_donations_non_50_pct_orgs"], + ) + non_cash_to_50_pct = non_cash_donations - non_cash_to_non_50_pct positive_agi = tax_unit("positive_agi", period) p = parameters(period).gov.irs.deductions.itemized.charity - capped_non_cash_donations = min_( - non_cash_donations, p.ceiling.non_cash * positive_agi + + # Non-cash to 50% limit orgs capped at 50% of AGI. + # Note: this models the basis-reduction election under + # 170(b)(1)(C)(iii); the default cap for capital gain + # property is 30% per 170(b)(1)(C)(i). + capped_non_cash_50 = min_( + non_cash_to_50_pct, p.ceiling.non_cash * positive_agi ) + # Non-cash to non-50% limit orgs capped at 30% of AGI + # per 170(b)(1)(B). Note: the stricter 20% cap for + # capital gain property under 170(b)(1)(D) is not yet + # modeled separately. + capped_non_cash_non_50 = min_( + non_cash_to_non_50_pct, + p.ceiling.non_cash_to_non_50_pct_org * positive_agi, + ) + capped_non_cash = capped_non_cash_50 + capped_non_cash_non_50 total_cap = p.ceiling.all * positive_agi if p.floor.applies: deduction_floor = p.floor.amount * positive_agi - reduced_non_cash_donations = max_( - non_cash_donations - deduction_floor, 0 + reduced_non_cash_50 = max_(non_cash_to_50_pct - deduction_floor, 0) + capped_reduced_non_cash_50 = min_( + reduced_non_cash_50, + p.ceiling.non_cash * positive_agi, ) - capped_reduced_non_cash_donations = min_( - reduced_non_cash_donations, p.ceiling.non_cash * positive_agi + + remaining_floor_after_50 = max_( + deduction_floor - non_cash_to_50_pct, 0 + ) + reduced_non_cash_non_50 = max_( + non_cash_to_non_50_pct - remaining_floor_after_50, 0 + ) + capped_reduced_non_cash_non_50 = min_( + reduced_non_cash_non_50, + p.ceiling.non_cash_to_non_50_pct_org * positive_agi, ) - remaining_floor = max_(deduction_floor - non_cash_donations, 0) + remaining_floor = max_( + remaining_floor_after_50 - non_cash_to_non_50_pct, 0 + ) reduced_cash_donations = max_(cash_donations - remaining_floor, 0) return min_( - capped_reduced_non_cash_donations + reduced_cash_donations, + capped_reduced_non_cash_50 + + capped_reduced_non_cash_non_50 + + reduced_cash_donations, total_cap, ) - return min_(capped_non_cash_donations + cash_donations, total_cap) + return min_(capped_non_cash + cash_donations, total_cap) diff --git a/policyengine_us/variables/household/expense/charitable/charitable_non_cash_donations_non_50_pct_orgs.py b/policyengine_us/variables/household/expense/charitable/charitable_non_cash_donations_non_50_pct_orgs.py new file mode 100644 index 00000000000..a33bea313d5 --- /dev/null +++ b/policyengine_us/variables/household/expense/charitable/charitable_non_cash_donations_non_50_pct_orgs.py @@ -0,0 +1,17 @@ +from policyengine_us.model_api import * + + +class charitable_non_cash_donations_non_50_pct_orgs(Variable): + value_type = float + entity = Person + label = "Charitable non-cash donations to non-50% limit organizations" + unit = USD + documentation = ( + "Non-cash charitable donations to organizations other than " + "50-percent limit organizations described in " + "26 USC 170(b)(1)(A), such as private non-operating " + "foundations. These are subject to a lower AGI ceiling " + "than donations to 50-percent limit organizations." + ) + definition_period = YEAR + reference = "https://www.law.cornell.edu/uscode/text/26/170#b_1_B"