Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions changelog_entry.yaml
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Original file line number Diff line number Diff line change
@@ -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"