diff --git a/changelog.d/irs-credit-limit-worksheet-order.fixed.md b/changelog.d/irs-credit-limit-worksheet-order.fixed.md new file mode 100644 index 00000000000..6b5d8f27f6f --- /dev/null +++ b/changelog.d/irs-credit-limit-worksheet-order.fixed.md @@ -0,0 +1 @@ +Order the federal non-refundable credits as their IRS credit limit worksheets do: the elderly or disabled credit is limited only by the foreign tax and child and dependent care credits (Schedule R) and precedes the education, retirement savings and energy efficient home improvement credits (Forms 8863, 8880 and 5695); the previously owned clean vehicle credit precedes the new clean vehicle credit (Form 8936) and is now limited by tax; the foreign tax credit applies against tax before 2022; the refundable 2021 child and dependent care credit no longer reduces other credits' limits; and Oklahoma's federal Child Tax Credit applies the credits that Schedule 8812 Credit Limit Worksheet A subtracts before the CTC, not those listed before it. diff --git a/policyengine_us/parameters/gov/irs/credits/cdcc/preceding_credits.yaml b/policyengine_us/parameters/gov/irs/credits/cdcc/preceding_credits.yaml index 9e1420d5878..6e81ead28a7 100644 --- a/policyengine_us/parameters/gov/irs/credits/cdcc/preceding_credits.yaml +++ b/policyengine_us/parameters/gov/irs/credits/cdcc/preceding_credits.yaml @@ -1,12 +1,16 @@ description: Non-refundable tax credits which precede the Child and Dependent Care Credit. values: + # Form 2441's Credit Limit Worksheet subtracts Schedule 3 lines 1 (foreign + # tax credit) and 6l (Form 8978, not modeled). 2013-01-01: - - foreign_tax_credit + - foreign_tax_credit metadata: unit: list period: year label: Non-refundable tax credits preceding the CDCC reference: - - title: IRS Schedule 3, Additional Credits and Payments - href: https://www.irs.gov/pub/irs-pdf/f1040s3.pdf + - title: 2025 Instructions for Form 2441, Credit Limit Worksheet, line 2 + href: https://www.irs.gov/pub/irs-pdf/i2441.pdf#page=5 + - title: 2013 Instructions for Form 2441, Credit Limit Worksheet, line 2 + href: https://www.irs.gov/pub/irs-prior/i2441--2013.pdf#page=4 diff --git a/policyengine_us/parameters/gov/irs/credits/clean_vehicle/new/preceding_credits.yaml b/policyengine_us/parameters/gov/irs/credits/clean_vehicle/new/preceding_credits.yaml index 91bee8a4966..f7cf065c79e 100644 --- a/policyengine_us/parameters/gov/irs/credits/clean_vehicle/new/preceding_credits.yaml +++ b/policyengine_us/parameters/gov/irs/credits/clean_vehicle/new/preceding_credits.yaml @@ -1,23 +1,49 @@ description: Non-refundable tax credits which precede the New Clean Vehicle Credit. values: - # Section 25D(c) orders the residential clean energy credit after these. + # Form 8936 line 11 (line 21 before 2023) subtracts the foreign tax, child + # and dependent care, education, retirement savings, energy efficient home + # improvement and elderly or disabled credits, Schedule 3 line 6l (Form + # 8978, not modeled) and, from 2023, the previously owned clean vehicle + # credit (Schedule 3, line 6m). Section 25D(c) orders the residential clean + # energy credit after this credit. 2013-01-01: - foreign_tax_credit - cdcc + - elderly_disabled_credit - non_refundable_american_opportunity_credit - lifetime_learning_credit - savers_credit - energy_efficient_home_improvement_credit + # The Child and Dependent Care Credit was refundable for 2021 only. + 2021-01-01: + - foreign_tax_credit + - elderly_disabled_credit + - non_refundable_american_opportunity_credit + - lifetime_learning_credit + - savers_credit + - energy_efficient_home_improvement_credit + 2022-01-01: + - foreign_tax_credit + - cdcc - elderly_disabled_credit + - non_refundable_american_opportunity_credit + - lifetime_learning_credit + - savers_credit + - energy_efficient_home_improvement_credit + - used_clean_vehicle_credit metadata: unit: list period: year label: Non-refundable tax credits preceding the New Clean Vehicle Credit reference: - - title: IRS Schedule 3, Additional Credits and Payments - href: https://www.irs.gov/pub/irs-pdf/f1040s3.pdf - - title: 26 U.S. Code § 25D(c) - Carryforward of unused credit - href: https://www.law.cornell.edu/uscode/text/26/25D#c - title: 2025 Instructions for Form 8936, line 11 href: https://www.irs.gov/pub/irs-pdf/i8936.pdf#page=6 + - title: 2023 Instructions for Form 8936, line 11 + href: https://www.irs.gov/pub/irs-prior/i8936--2023.pdf#page=5 + - title: 2022 Instructions for Form 8936, line 21 + href: https://www.irs.gov/pub/irs-prior/i8936--2022.pdf#page=3 + - title: 2017 Instructions for Form 8936, line 21 + href: https://www.irs.gov/pub/irs-prior/i8936--2017.pdf#page=2 + - title: 26 U.S. Code § 25D(c) - Carryforward of unused credit + href: https://www.law.cornell.edu/uscode/text/26/25D#c diff --git a/policyengine_us/parameters/gov/irs/credits/clean_vehicle/used/preceding_credits.yaml b/policyengine_us/parameters/gov/irs/credits/clean_vehicle/used/preceding_credits.yaml index 12e0f8b7348..7749d9d3279 100644 --- a/policyengine_us/parameters/gov/irs/credits/clean_vehicle/used/preceding_credits.yaml +++ b/policyengine_us/parameters/gov/irs/credits/clean_vehicle/used/preceding_credits.yaml @@ -1,24 +1,44 @@ description: Non-refundable tax credits which precede the Used Clean Vehicle Credit. values: - # Section 25D(c) orders the residential clean energy credit after these. + # Form 8936 line 16 subtracts Schedule 3 lines 1 through 4 (foreign tax, + # child and dependent care, education and retirement savings credits), 5b + # (energy efficient home improvement credit), 6d (elderly or disabled + # credit) and 6l (Form 8978, not modeled). It does not subtract line 6f, so + # this credit precedes the new clean vehicle credit. Section 25D(c) orders + # the residential clean energy credit after this credit. 2013-01-01: - foreign_tax_credit - cdcc + - elderly_disabled_credit - non_refundable_american_opportunity_credit - lifetime_learning_credit - savers_credit - energy_efficient_home_improvement_credit + # The Child and Dependent Care Credit was refundable for 2021 only. + 2021-01-01: + - foreign_tax_credit + - elderly_disabled_credit + - non_refundable_american_opportunity_credit + - lifetime_learning_credit + - savers_credit + - energy_efficient_home_improvement_credit + 2022-01-01: + - foreign_tax_credit + - cdcc - elderly_disabled_credit - - new_clean_vehicle_credit + - non_refundable_american_opportunity_credit + - lifetime_learning_credit + - savers_credit + - energy_efficient_home_improvement_credit metadata: unit: list period: year label: Non-refundable tax credits preceding the Used Clean Vehicle Credit reference: - - title: IRS Schedule 3, Additional Credits and Payments - href: https://www.irs.gov/pub/irs-pdf/f1040s3.pdf - - title: 26 U.S. Code § 25D(c) - Carryforward of unused credit - href: https://www.law.cornell.edu/uscode/text/26/25D#c - title: 2025 Instructions for Form 8936, line 16 href: https://www.irs.gov/pub/irs-pdf/i8936.pdf#page=6 + - title: 2023 Instructions for Form 8936, line 16 + href: https://www.irs.gov/pub/irs-prior/i8936--2023.pdf#page=5 + - title: 26 U.S. Code § 25D(c) - Carryforward of unused credit + href: https://www.law.cornell.edu/uscode/text/26/25D#c diff --git a/policyengine_us/parameters/gov/irs/credits/ctc_tax_liability_limit/preceding_credits.yaml b/policyengine_us/parameters/gov/irs/credits/ctc_tax_liability_limit/preceding_credits.yaml index 7c1d642f8cb..58a051467ff 100644 --- a/policyengine_us/parameters/gov/irs/credits/ctc_tax_liability_limit/preceding_credits.yaml +++ b/policyengine_us/parameters/gov/irs/credits/ctc_tax_liability_limit/preceding_credits.yaml @@ -1,21 +1,23 @@ description: The IRS subtracts these non-refundable credits from income tax before credits to find the tax liability that limits the non-refundable Child Tax Credit. values: - # The foreign tax credit also precedes the CTC before 2022, but the model - # applies it against regular tax only from 2022 (see non_refundable.yaml). + # Before 2021 the new clean vehicle credit is not modeled; Publication 972 + # also subtracted it (Form 8936, line 23). 2013-01-01: + - foreign_tax_credit - cdcc + - elderly_disabled_credit - non_refundable_american_opportunity_credit - lifetime_learning_credit - savers_credit - energy_efficient_home_improvement_credit - - elderly_disabled_credit # The Child and Dependent Care Credit was refundable for 2021 only. 2021-01-01: + - foreign_tax_credit + - elderly_disabled_credit - non_refundable_american_opportunity_credit - lifetime_learning_credit - savers_credit - energy_efficient_home_improvement_credit - - elderly_disabled_credit - new_clean_vehicle_credit # Line numbers are those of the 2023-2025 Schedule 3. In 2022 the energy # efficient home improvement credit came from Form 5695, line 30, and the @@ -25,13 +27,13 @@ values: 2022-01-01: - foreign_tax_credit # Schedule 3, line 1 - cdcc # Schedule 3, line 2 + - elderly_disabled_credit # Schedule 3, line 6d - non_refundable_american_opportunity_credit # Schedule 3, line 3 - lifetime_learning_credit # Schedule 3, line 3 - savers_credit # Schedule 3, line 4 - energy_efficient_home_improvement_credit # Schedule 3, line 5b - - elderly_disabled_credit # Schedule 3, line 6d - - new_clean_vehicle_credit # Schedule 3, line 6f - used_clean_vehicle_credit # Schedule 3, line 6m + - new_clean_vehicle_credit # Schedule 3, line 6f # The residential clean energy credit (Schedule 3, line 5a) is not listed: # section 25D(c) orders it after every other credit in subpart A, including # the Child Tax Credit. See subsequent_credits.yaml for the case in which it @@ -48,6 +50,8 @@ metadata: href: https://www.irs.gov/pub/irs-prior/i1040s8--2022.pdf#page=4 - title: 2020 Publication 972, Child Tax Credit and Credit for Other Dependents Worksheet, line 12 href: https://www.irs.gov/pub/irs-prior/p972--2020.pdf#page=7 + - title: 2013 Publication 972, Child Tax Credit Worksheet, line 10 + href: https://www.irs.gov/pub/irs-prior/p972--2013.pdf#page=5 - title: 26 U.S. Code § 26(a) - Limitation based on amount of tax href: https://www.law.cornell.edu/uscode/text/26/26#a - title: 26 U.S. Code § 25D(c) - Carryforward of unused credit diff --git a/policyengine_us/parameters/gov/irs/credits/education/american_opportunity_credit/preceding_credits.yaml b/policyengine_us/parameters/gov/irs/credits/education/american_opportunity_credit/preceding_credits.yaml index d61f853230a..498255eddb1 100644 --- a/policyengine_us/parameters/gov/irs/credits/education/american_opportunity_credit/preceding_credits.yaml +++ b/policyengine_us/parameters/gov/irs/credits/education/american_opportunity_credit/preceding_credits.yaml @@ -1,13 +1,31 @@ description: Non-refundable tax credits which precede the American Opportunity Credit. values: + # Form 8863's Credit Limit Worksheet subtracts Schedule 3 lines 1 (foreign + # tax credit), 2 (child and dependent care credit), 6d (elderly or disabled + # credit) and 6l (Form 8978, not modeled). 2013-01-01: - - foreign_tax_credit + - foreign_tax_credit - cdcc + - elderly_disabled_credit + # The Child and Dependent Care Credit was refundable for 2021 only. + 2021-01-01: + - foreign_tax_credit + - elderly_disabled_credit + 2022-01-01: + - foreign_tax_credit + - cdcc + - elderly_disabled_credit metadata: unit: list period: year label: Non-refundable tax credits preceding the American Opportunity Credit reference: - - title: IRS Schedule 3, Additional Credits and Payments - href: https://www.irs.gov/pub/irs-pdf/f1040s3.pdf + - title: 2025 Instructions for Form 8863, Credit Limit Worksheet, line 5 + href: https://www.irs.gov/pub/irs-pdf/i8863.pdf#page=8 + - title: 2021 Instructions for Form 8863, Credit Limit Worksheet, line 5 + href: https://www.irs.gov/pub/irs-prior/i8863--2021.pdf#page=7 + - title: 2013 Instructions for Form 8863, Credit Limit Worksheet, line 5 + href: https://www.irs.gov/pub/irs-prior/i8863--2013.pdf#page=6 + - title: 26 U.S. Code § 21(g) - Special rules for 2021 + href: https://www.law.cornell.edu/uscode/text/26/21#g diff --git a/policyengine_us/parameters/gov/irs/credits/education/lifetime_learning_credit/preceding_credits.yaml b/policyengine_us/parameters/gov/irs/credits/education/lifetime_learning_credit/preceding_credits.yaml index b7c1387950e..190394b35bb 100644 --- a/policyengine_us/parameters/gov/irs/credits/education/lifetime_learning_credit/preceding_credits.yaml +++ b/policyengine_us/parameters/gov/irs/credits/education/lifetime_learning_credit/preceding_credits.yaml @@ -1,8 +1,23 @@ description: Non-refundable tax credits which precede the Lifetime Learning Credit. values: + # Form 8863's Credit Limit Worksheet limits the non-refundable American + # opportunity and lifetime learning credits together (lines 1 to 3) to tax + # less Schedule 3 lines 1, 2, 6d and 6l. Applying the American opportunity + # credit first gives the same total. 2013-01-01: - - foreign_tax_credit + - foreign_tax_credit - cdcc + - elderly_disabled_credit + - non_refundable_american_opportunity_credit + # The Child and Dependent Care Credit was refundable for 2021 only. + 2021-01-01: + - foreign_tax_credit + - elderly_disabled_credit + - non_refundable_american_opportunity_credit + 2022-01-01: + - foreign_tax_credit + - cdcc + - elderly_disabled_credit - non_refundable_american_opportunity_credit metadata: @@ -10,5 +25,11 @@ metadata: period: year label: Non-refundable tax credits preceding the Lifetime Learning Credit reference: - - title: IRS Schedule 3, Additional Credits and Payments - href: https://www.irs.gov/pub/irs-pdf/f1040s3.pdf + - title: 2025 Instructions for Form 8863, Credit Limit Worksheet, lines 1 to 7 + href: https://www.irs.gov/pub/irs-pdf/i8863.pdf#page=8 + - title: 2021 Instructions for Form 8863, Credit Limit Worksheet, lines 1 to 7 + href: https://www.irs.gov/pub/irs-prior/i8863--2021.pdf#page=7 + - title: 2013 Instructions for Form 8863, Credit Limit Worksheet, lines 1 to 7 + href: https://www.irs.gov/pub/irs-prior/i8863--2013.pdf#page=6 + - title: 26 U.S. Code § 21(g) - Special rules for 2021 + href: https://www.law.cornell.edu/uscode/text/26/21#g diff --git a/policyengine_us/parameters/gov/irs/credits/elderly_or_disabled/preceding_credits.yaml b/policyengine_us/parameters/gov/irs/credits/elderly_or_disabled/preceding_credits.yaml index 4bf6a376585..92765d6a2a2 100644 --- a/policyengine_us/parameters/gov/irs/credits/elderly_or_disabled/preceding_credits.yaml +++ b/policyengine_us/parameters/gov/irs/credits/elderly_or_disabled/preceding_credits.yaml @@ -1,22 +1,29 @@ description: Non-refundable tax credits which precede the Elderly or Disabled Credit. values: - # Section 25D(c) orders the residential clean energy credit after these. + # Schedule R's Credit Limit Worksheet subtracts only Schedule 3 lines 1 + # (foreign tax credit), 2 (child and dependent care credit) and 6l (Form + # 8978, not modeled). The education, retirement savings, energy and clean + # vehicle credits come after this credit. 2013-01-01: - foreign_tax_credit - cdcc - - non_refundable_american_opportunity_credit - - lifetime_learning_credit - - savers_credit - - energy_efficient_home_improvement_credit + # The Child and Dependent Care Credit was refundable for 2021 only. + 2021-01-01: + - foreign_tax_credit + 2022-01-01: + - foreign_tax_credit + - cdcc metadata: unit: list period: year label: Non-refundable tax credits preceding the Elderly or Disabled Credit reference: - - title: IRS Schedule 3, Additional Credits and Payments - href: https://www.irs.gov/pub/irs-pdf/f1040s3.pdf - - title: 26 U.S. Code § 25D(c) - Carryforward of unused credit - href: https://www.law.cornell.edu/uscode/text/26/25D#c - title: 2025 Instructions for Schedule R (Form 1040), Credit Limit Worksheet, line 2 href: https://www.irs.gov/pub/irs-pdf/i1040sr.pdf#page=4 + - title: 2021 Instructions for Schedule R (Form 1040), Credit Limit Worksheet—Line 21, line 2 + href: https://www.irs.gov/pub/irs-prior/i1040sr--2021.pdf#page=3 + - title: 2013 Instructions for Schedule R (Form 1040A or 1040), Credit Limit Worksheet—Line 21, line 2 + href: https://www.irs.gov/pub/irs-prior/i1040sr--2013.pdf#page=3 + - title: 26 U.S. Code § 21(g) - Special rules for 2021 + href: https://www.law.cornell.edu/uscode/text/26/21#g diff --git a/policyengine_us/parameters/gov/irs/credits/energy_efficient_home_improvement/preceding_credits.yaml b/policyengine_us/parameters/gov/irs/credits/energy_efficient_home_improvement/preceding_credits.yaml index 5cd5596097c..ef7987f71e6 100644 --- a/policyengine_us/parameters/gov/irs/credits/energy_efficient_home_improvement/preceding_credits.yaml +++ b/policyengine_us/parameters/gov/irs/credits/energy_efficient_home_improvement/preceding_credits.yaml @@ -1,9 +1,29 @@ description: Non-refundable tax credits which precede the Energy Efficient Home Improvement Credit. values: - # Section 25D(c) orders the residential clean energy credit after these. + # Form 5695's Energy Efficient Home Improvement Credit Limit Worksheet (the + # Nonbusiness Energy Property Credit Limit Worksheet before 2022) subtracts + # the foreign tax, child and dependent care, elderly or disabled, education + # and retirement savings credits, and Schedule 3 line 6l (Form 8978, not + # modeled). Section 25D(c) orders the residential clean energy credit after + # this credit. 2013-01-01: - foreign_tax_credit - cdcc + - elderly_disabled_credit + - non_refundable_american_opportunity_credit + - lifetime_learning_credit + - savers_credit + # The Child and Dependent Care Credit was refundable for 2021 only. + 2021-01-01: + - foreign_tax_credit + - elderly_disabled_credit + - non_refundable_american_opportunity_credit + - lifetime_learning_credit + - savers_credit + 2022-01-01: + - foreign_tax_credit + - cdcc + - elderly_disabled_credit - non_refundable_american_opportunity_credit - lifetime_learning_credit - savers_credit @@ -13,9 +33,13 @@ metadata: period: year label: Non-refundable tax credits preceding the Energy Efficient Home Improvement Credit reference: - - title: IRS Schedule 3, Additional Credits and Payments - href: https://www.irs.gov/pub/irs-pdf/f1040s3.pdf + - title: 2025 Instructions for Form 5695, Energy Efficient Home Improvement Credit Limit Worksheet—Line 31, line 2 + href: https://www.irs.gov/pub/irs-pdf/i5695.pdf#page=7 + - title: 2022 Instructions for Form 5695, Energy Efficient Home Improvement Credit Limit Worksheet—Line 29, line 2 + href: https://www.irs.gov/pub/irs-prior/i5695--2022.pdf#page=7 + - title: 2013 Form 5695, Nonbusiness Energy Property Credit Limit Worksheet—Line 29, line 2 + href: https://www.irs.gov/pub/irs-prior/f5695--2013.pdf#page=6 - title: 26 U.S. Code § 25D(c) - Carryforward of unused credit href: https://www.law.cornell.edu/uscode/text/26/25D#c - - title: 2025 Instructions for Form 5695, Energy Efficient Home Improvement Credit Limit Worksheet, line 2 - href: https://www.irs.gov/pub/irs-pdf/i5695.pdf#page=7 + - title: 26 U.S. Code § 21(g) - Special rules for 2021 + href: https://www.law.cornell.edu/uscode/text/26/21#g diff --git a/policyengine_us/parameters/gov/irs/credits/non_refundable.yaml b/policyengine_us/parameters/gov/irs/credits/non_refundable.yaml index 1e103d12d6b..e1b804c2c00 100644 --- a/policyengine_us/parameters/gov/irs/credits/non_refundable.yaml +++ b/policyengine_us/parameters/gov/irs/credits/non_refundable.yaml @@ -1,47 +1,53 @@ -description: Non-refundable tax credits. +description: Non-refundable tax credits, in the order the IRS credit limit worksheets apply them against income tax. values: + # Each credit's limit subtracts the credits listed before it (see each + # credit's preceding_credits.yaml). The order follows the credit limit + # worksheets, not the Schedule 3 line order: the elderly or disabled credit + # precedes the education credits, the previously owned clean vehicle credit + # precedes the new clean vehicle credit, and the residential clean energy + # credit comes last (26 U.S.C. 25D(c)). 2013-01-01: + - foreign_tax_credit - cdcc - elderly_disabled_credit # Adoption expense credit - - non_refundable_ctc - # Home mortgage credit - non_refundable_american_opportunity_credit - lifetime_learning_credit - savers_credit - - residential_clean_energy_credit - energy_efficient_home_improvement_credit + - non_refundable_ctc + # Home mortgage credit + - residential_clean_energy_credit # CDCC became refundable for 2021 only. # And we only define the EV credit for 2021 onward because the law changed # then. 2021-01-01: + - foreign_tax_credit - elderly_disabled_credit # Adoption expense credit - - non_refundable_ctc - # Home mortgage credit - non_refundable_american_opportunity_credit - lifetime_learning_credit - savers_credit - - residential_clean_energy_credit - energy_efficient_home_improvement_credit - new_clean_vehicle_credit + - non_refundable_ctc + # Home mortgage credit + - residential_clean_energy_credit # CDCC became non-refundable again in 2022. 2022-01-01: - # Sequencing the credits based on Schedule 3 (Form 1040) - - foreign_tax_credit # 1 - - cdcc # 2 - - non_refundable_american_opportunity_credit # 3 - - lifetime_learning_credit # 3 - - savers_credit # 4 - - residential_clean_energy_credit # 5a - - energy_efficient_home_improvement_credit # 5b - - elderly_disabled_credit # 6d + - foreign_tax_credit # Schedule 3, line 1 + - cdcc # Schedule 3, line 2 + - elderly_disabled_credit # Schedule 3, line 6d + - non_refundable_american_opportunity_credit # Schedule 3, line 3 + - lifetime_learning_credit # Schedule 3, line 3 + - savers_credit # Schedule 3, line 4 + - energy_efficient_home_improvement_credit # Schedule 3, line 5b # Inflation Reduction Act introduces a used EV credit. - - new_clean_vehicle_credit # 6f - - used_clean_vehicle_credit # 6f - # Adoption expense credit - - non_refundable_ctc - # Home mortgage credit + - used_clean_vehicle_credit # Schedule 3, line 6m + - new_clean_vehicle_credit # Schedule 3, line 6f + - non_refundable_ctc # Form 1040, line 19 + # Adoption expense credit, home mortgage credit + - residential_clean_energy_credit # Schedule 3, line 5a metadata: unit: list @@ -51,5 +57,11 @@ metadata: href: https://www.law.cornell.edu/uscode/text/26/subtitle-A/chapter-1/subchapter-A/part-IV/subpart-A - title: 26 U.S. Code § 21 - Expenses for household and dependent care services necessary for gainful employment (g)(1) href: https://www.law.cornell.edu/uscode/text/26/21#g_1 + - title: 26 U.S. Code § 25D(c) - Carryforward of unused credit + href: https://www.law.cornell.edu/uscode/text/26/25D#c - title: 26 U.S. Code § 30D - New qualified plug-in electric drive motor vehicles (c)(2) href: https://www.law.cornell.edu/uscode/text/26/30D#c_2 + - title: 2025 Schedule 3 (Form 1040), Additional Credits and Payments + href: https://www.irs.gov/pub/irs-pdf/f1040s3.pdf + - title: 2020 Publication 972, Child Tax Credit and Credit for Other Dependents Worksheet, line 11 + href: https://www.irs.gov/pub/irs-prior/p972--2020.pdf#page=7 diff --git a/policyengine_us/parameters/gov/irs/credits/residential_clean_energy/preceding_credits.yaml b/policyengine_us/parameters/gov/irs/credits/residential_clean_energy/preceding_credits.yaml index 89841582ab1..9b4ee84f1e5 100644 --- a/policyengine_us/parameters/gov/irs/credits/residential_clean_energy/preceding_credits.yaml +++ b/policyengine_us/parameters/gov/irs/credits/residential_clean_energy/preceding_credits.yaml @@ -3,30 +3,30 @@ values: 2013-01-01: - foreign_tax_credit - cdcc + - elderly_disabled_credit - non_refundable_american_opportunity_credit - lifetime_learning_credit - savers_credit - energy_efficient_home_improvement_credit - - elderly_disabled_credit # The Child and Dependent Care Credit was refundable for 2021 only. 2021-01-01: - foreign_tax_credit + - elderly_disabled_credit - non_refundable_american_opportunity_credit - lifetime_learning_credit - savers_credit - energy_efficient_home_improvement_credit - - elderly_disabled_credit - new_clean_vehicle_credit 2022-01-01: - foreign_tax_credit # Schedule 3, line 1 - cdcc # Schedule 3, line 2 + - elderly_disabled_credit # Schedule 3, line 6d - non_refundable_american_opportunity_credit # Schedule 3, line 3 - lifetime_learning_credit # Schedule 3, line 3 - savers_credit # Schedule 3, line 4 - energy_efficient_home_improvement_credit # Form 5695, line 32 (line 30 in 2022) - - elderly_disabled_credit # Schedule 3, line 6d - - new_clean_vehicle_credit # Schedule 3, line 6f - used_clean_vehicle_credit # Schedule 3, line 6m + - new_clean_vehicle_credit # Schedule 3, line 6f metadata: unit: list @@ -37,3 +37,7 @@ metadata: href: https://www.law.cornell.edu/uscode/text/26/25D#c - title: 2025 Instructions for Form 5695, Residential Clean Energy Credit Limit Worksheet, line 2 href: https://www.irs.gov/pub/irs-pdf/i5695.pdf#page=5 + - title: 2022 Instructions for Form 5695, Residential Clean Energy Credit Limit Worksheet—Line 14, line 2 + href: https://www.irs.gov/pub/irs-prior/i5695--2022.pdf#page=4 + - title: 2013 Form 5695, Residential Energy Efficient Property Credit Limit Worksheet—Line 14, lines 2 to 9 + href: https://www.irs.gov/pub/irs-prior/f5695--2013.pdf#page=5 diff --git a/policyengine_us/parameters/gov/irs/credits/retirement_saving/preceding_credits.yaml b/policyengine_us/parameters/gov/irs/credits/retirement_saving/preceding_credits.yaml index c8acae364b7..34d76e18611 100644 --- a/policyengine_us/parameters/gov/irs/credits/retirement_saving/preceding_credits.yaml +++ b/policyengine_us/parameters/gov/irs/credits/retirement_saving/preceding_credits.yaml @@ -1,8 +1,24 @@ description: Non-refundable tax credits which precede the Retirement Savings Credit. values: + # Form 8880's Credit Limit Worksheet subtracts Schedule 3 lines 1 through 3 + # (foreign tax, child and dependent care, and education credits), 6d + # (elderly or disabled credit) and 6l (Form 8978, not modeled). 2013-01-01: - - foreign_tax_credit + - foreign_tax_credit - cdcc + - elderly_disabled_credit + - non_refundable_american_opportunity_credit + - lifetime_learning_credit + # The Child and Dependent Care Credit was refundable for 2021 only. + 2021-01-01: + - foreign_tax_credit + - elderly_disabled_credit + - non_refundable_american_opportunity_credit + - lifetime_learning_credit + 2022-01-01: + - foreign_tax_credit + - cdcc + - elderly_disabled_credit - non_refundable_american_opportunity_credit - lifetime_learning_credit @@ -11,5 +27,11 @@ metadata: period: year label: Non-refundable tax credits preceding the Retirement Savings Credit reference: - - title: IRS Schedule 3, Additional Credits and Payments - href: https://www.irs.gov/pub/irs-pdf/f1040s3.pdf + - title: 2025 Form 8880, Credit Limit Worksheet, line 2 + href: https://www.irs.gov/pub/irs-pdf/f8880.pdf#page=2 + - title: 2021 Form 8880, Credit Limit Worksheet, line 2 + href: https://www.irs.gov/pub/irs-prior/f8880--2021.pdf#page=2 + - title: 2013 Form 8880, Credit Limit Worksheet, line 2 + href: https://www.irs.gov/pub/irs-prior/f8880--2013.pdf#page=2 + - title: 26 U.S. Code § 21(g) - Special rules for 2021 + href: https://www.law.cornell.edu/uscode/text/26/21#g diff --git a/policyengine_us/reforms/congress/afa/afa_other_dependent_credit.py b/policyengine_us/reforms/congress/afa/afa_other_dependent_credit.py index b858c81db6a..ee1bf39d602 100644 --- a/policyengine_us/reforms/congress/afa/afa_other_dependent_credit.py +++ b/policyengine_us/reforms/congress/afa/afa_other_dependent_credit.py @@ -200,15 +200,18 @@ def formula(person, period, parameters): def modify_parameters(parameters): credits = parameters.gov.irs.credits + # The baseline order of gov.irs.credits.non_refundable, with the + # separate other dependent credit in the Child Tax Credit's place. credits_before_residential_clean_energy = [ + "foreign_tax_credit", "cdcc", "elderly_disabled_credit", "non_refundable_american_opportunity_credit", "lifetime_learning_credit", "savers_credit", "energy_efficient_home_improvement_credit", - "new_clean_vehicle_credit", "used_clean_vehicle_credit", + "new_clean_vehicle_credit", "other_dependent_credit", ] credits.non_refundable.update( diff --git a/policyengine_us/reforms/crfb/non_refundable_ss_credit.py b/policyengine_us/reforms/crfb/non_refundable_ss_credit.py index e7c4baa5626..8977c44c42a 100644 --- a/policyengine_us/reforms/crfb/non_refundable_ss_credit.py +++ b/policyengine_us/reforms/crfb/non_refundable_ss_credit.py @@ -88,44 +88,35 @@ def formula(tax_unit, period, parameters): return highest_rate def modify_parameters(parameters): - parameters.gov.irs.credits.non_refundable.update( + # The baseline order of gov.irs.credits.non_refundable, with the + # Social Security credit after the other credits that precede the + # Child Tax Credit. It precedes the CTC and, like every credit, the + # residential clean energy credit (26 U.S.C. 25D(c)). + credits_before_ctc = [ + "foreign_tax_credit", + "cdcc", + "elderly_disabled_credit", + "non_refundable_american_opportunity_credit", + "lifetime_learning_credit", + "savers_credit", + "energy_efficient_home_improvement_credit", + "used_clean_vehicle_credit", + "new_clean_vehicle_credit", + "ss_credit", + ] + credits = parameters.gov.irs.credits + credits.non_refundable.update( start=instant("2026-01-01"), - value=[ - "foreign_tax_credit", - "cdcc", - "non_refundable_american_opportunity_credit", - "lifetime_learning_credit", - "savers_credit", - "residential_clean_energy_credit", - "energy_efficient_home_improvement_credit", - "elderly_disabled_credit", - "new_clean_vehicle_credit", - "used_clean_vehicle_credit", - "non_refundable_ctc", - "ss_credit", - ], + value=credits_before_ctc + + ["non_refundable_ctc", "residential_clean_energy_credit"], ) - # The credit precedes the Child Tax Credit and, like every credit, - # the residential clean energy credit (26 U.S.C. 25D(c)). - credits = parameters.gov.irs.credits for preceding_credits in ( credits.ctc_tax_liability_limit.preceding_credits, credits.residential_clean_energy.preceding_credits, ): preceding_credits.update( start=instant("2026-01-01"), - value=[ - "foreign_tax_credit", - "cdcc", - "non_refundable_american_opportunity_credit", - "lifetime_learning_credit", - "savers_credit", - "energy_efficient_home_improvement_credit", - "elderly_disabled_credit", - "new_clean_vehicle_credit", - "used_clean_vehicle_credit", - "ss_credit", - ], + value=credits_before_ctc, ) return parameters diff --git a/policyengine_us/tests/policy/baseline/gov/irs/credits/clean_vehicle/new/new_clean_vehicle_credit_credit_limit.yaml b/policyengine_us/tests/policy/baseline/gov/irs/credits/clean_vehicle/new/new_clean_vehicle_credit_credit_limit.yaml index 7429c6c5aa2..7f43d203806 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/credits/clean_vehicle/new/new_clean_vehicle_credit_credit_limit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/credits/clean_vehicle/new/new_clean_vehicle_credit_credit_limit.yaml @@ -1,4 +1,4 @@ -- name: Income tax reduced by previous credits +- name: Income tax reduced by previous credits; the CDCC was refundable in 2021 and does not precede. period: 2021 input: new_clean_vehicle_credit_eligible: true @@ -6,4 +6,14 @@ cdcc: 1_000 savers_credit: 300 output: - new_clean_vehicle_credit_credit_limit: 1_700 + new_clean_vehicle_credit_credit_limit: 2_700 + +- name: The previously owned clean vehicle credit precedes the new clean vehicle credit (Form 8936, line 11). + period: 2025 + input: + new_clean_vehicle_credit_eligible: true + income_tax_before_credits: 3_000 + cdcc: 1_000 + used_clean_vehicle_credit: 800 + output: + new_clean_vehicle_credit_credit_limit: 1_200 diff --git a/policyengine_us/tests/policy/baseline/gov/irs/credits/clean_vehicle/used/used_clean_vehicle_credit.yaml b/policyengine_us/tests/policy/baseline/gov/irs/credits/clean_vehicle/used/used_clean_vehicle_credit.yaml index d57645b226e..e208d95bb2d 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/credits/clean_vehicle/used/used_clean_vehicle_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/credits/clean_vehicle/used/used_clean_vehicle_credit.yaml @@ -1,33 +1,19 @@ -- name: No credit if no sale price. - period: 2022 +- name: Limited by tax after preceding credits; the unused credit is lost (Form 8936, lines 16-18). + period: 2025 input: used_clean_vehicle_credit_eligible: true - used_clean_vehicle_sale_price: 0 + used_clean_vehicle_credit_potential: 4_000 + income_tax_before_credits: 3_000 + elderly_disabled_credit: 500 output: - used_clean_vehicle_credit: 0 + used_clean_vehicle_credit_credit_limit: 2_500 + used_clean_vehicle_credit: 2_500 -- name: 30% of sale price up to $4,000. - period: 2023 - absolute_error_margin: 0.01 +- name: Potential below the limit. + period: 2025 input: used_clean_vehicle_credit_eligible: true - used_clean_vehicle_sale_price: 100 + used_clean_vehicle_credit_potential: 1_000 + income_tax_before_credits: 3_000 output: - used_clean_vehicle_credit: 30 - -- name: 30% up to just below the cap. - period: 2023 - absolute_error_margin: 0.01 - input: - used_clean_vehicle_credit_eligible: true - used_clean_vehicle_sale_price: 13_333 - output: - used_clean_vehicle_credit: 3_999.9 - -- name: Capped at $4,000. - period: 2023 - input: - used_clean_vehicle_credit_eligible: true - used_clean_vehicle_sale_price: 13_334 - output: - used_clean_vehicle_credit: 4_000 + used_clean_vehicle_credit: 1_000 diff --git a/policyengine_us/tests/policy/baseline/gov/irs/credits/clean_vehicle/used/used_clean_vehicle_credit_potential.yaml b/policyengine_us/tests/policy/baseline/gov/irs/credits/clean_vehicle/used/used_clean_vehicle_credit_potential.yaml new file mode 100644 index 00000000000..615bb1db58d --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/irs/credits/clean_vehicle/used/used_clean_vehicle_credit_potential.yaml @@ -0,0 +1,33 @@ +- name: No credit if no sale price. + period: 2022 + input: + used_clean_vehicle_credit_eligible: true + used_clean_vehicle_sale_price: 0 + output: + used_clean_vehicle_credit_potential: 0 + +- name: 30% of sale price up to $4,000. + period: 2023 + absolute_error_margin: 0.01 + input: + used_clean_vehicle_credit_eligible: true + used_clean_vehicle_sale_price: 100 + output: + used_clean_vehicle_credit_potential: 30 + +- name: 30% up to just below the cap. + period: 2023 + absolute_error_margin: 0.01 + input: + used_clean_vehicle_credit_eligible: true + used_clean_vehicle_sale_price: 13_333 + output: + used_clean_vehicle_credit_potential: 3_999.9 + +- name: Capped at $4,000. + period: 2023 + input: + used_clean_vehicle_credit_eligible: true + used_clean_vehicle_sale_price: 13_334 + output: + used_clean_vehicle_credit_potential: 4_000 diff --git a/policyengine_us/tests/policy/baseline/gov/irs/credits/credit_limit_order.yaml b/policyengine_us/tests/policy/baseline/gov/irs/credits/credit_limit_order.yaml new file mode 100644 index 00000000000..957dce46a41 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/irs/credits/credit_limit_order.yaml @@ -0,0 +1,129 @@ +# Each non-refundable credit's limit is income tax before credits less the +# credits its own credit limit worksheet subtracts. The worksheets order the +# credits: foreign tax, child and dependent care, elderly or disabled, +# education, retirement savings, energy efficient home improvement, previously +# owned clean vehicle, new clean vehicle, Child Tax Credit, residential clean +# energy. Expected values are worked by hand from the worksheet lines cited. + +- name: Schedule R subtracts only the foreign tax and child and dependent care credits. + period: 2025 + input: + income_tax_before_credits: 3_000 + foreign_tax_credit: 200 + cdcc: 300 + non_refundable_american_opportunity_credit: 1_000 + savers_credit: 500 + energy_efficient_home_improvement_credit: 400 + output: + # 2025 Schedule R instructions, Credit Limit Worksheet: line 1 = 3,000; + # line 2 = Schedule 3 lines 1, 2 and 6l = 200 + 300; line 3 = 2,500. + elderly_disabled_credit_credit_limit: 2_500 + +- name: Form 8863 subtracts the elderly or disabled credit. + period: 2025 + input: + income_tax_before_credits: 3_000 + foreign_tax_credit: 200 + cdcc: 300 + elderly_disabled_credit: 400 + savers_credit: 500 + output: + # 2025 Form 8863 instructions, Credit Limit Worksheet: line 5 = Schedule 3 + # lines 1, 2, 6d and 6l = 200 + 300 + 400; line 6 = 3,000 - 900 = 2,100. + non_refundable_american_opportunity_credit_credit_limit: 2_100 + +- name: Form 8863 limits the lifetime learning credit together with the American opportunity credit. + period: 2025 + input: + income_tax_before_credits: 3_000 + foreign_tax_credit: 200 + cdcc: 300 + elderly_disabled_credit: 400 + non_refundable_american_opportunity_credit: 1_000 + output: + # Credit Limit Worksheet line 6 = 2,100, of which the American + # opportunity credit (line 2) uses 1,000. + lifetime_learning_credit_credit_limit: 1_100 + +- name: Form 8880 subtracts the elderly or disabled credit. + period: 2025 + input: + income_tax_before_credits: 3_000 + foreign_tax_credit: 200 + cdcc: 300 + elderly_disabled_credit: 400 + non_refundable_american_opportunity_credit: 1_000 + lifetime_learning_credit: 100 + energy_efficient_home_improvement_credit: 600 + output: + # 2025 Form 8880, Credit Limit Worksheet: line 2 = Schedule 3 lines 1 + # through 3, 6d and 6l = 200 + 300 + 1,100 + 400; line 3 = 1,000. + savers_credit_credit_limit: 1_000 + +- name: Form 5695 Part II subtracts the elderly or disabled credit. + period: 2025 + input: + income_tax_before_credits: 3_000 + foreign_tax_credit: 200 + cdcc: 300 + elderly_disabled_credit: 400 + non_refundable_american_opportunity_credit: 1_000 + savers_credit: 300 + used_clean_vehicle_credit: 700 + output: + # 2025 Form 5695 instructions, line 31 worksheet: line 2 = 6l + foreign + # tax + child and dependent care + elderly + education + retirement + # savings = 200 + 300 + 400 + 1,000 + 300; line 3 = 800. + energy_efficient_home_improvement_credit_credit_limit: 800 + +- name: The elderly or disabled credit takes priority over the retirement savings credit. + period: 2025 + input: + income_tax_before_credits: 1_000 + elderly_disabled_credit_potential: 600 + savers_credit_potential: 800 + output: + # Schedule R: 1,000 - 0 = 1,000, so the full 600. Form 8880: 1,000 - 600 + # = 400, so 400 of the 800. Before, the retirement savings credit took + # 800 first and left 200 for the elderly or disabled credit. + elderly_disabled_credit: 600 + savers_credit: 400 + income_tax_capped_non_refundable_credits: 1_000 + +- name: In 2021 the refundable child and dependent care credit does not reduce later limits. + period: 2021 + input: + income_tax_before_credits: 3_000 + foreign_tax_credit: 200 + cdcc: 1_000 + elderly_disabled_credit: 400 + output: + # The 2021 credit was refundable (26 U.S.C. 21(g)) and claimed on + # Schedule 3, line 13g, not line 2. + elderly_disabled_credit_credit_limit: 2_800 + non_refundable_american_opportunity_credit_credit_limit: 2_400 + +- name: The foreign tax credit reduces income tax before 2022. + period: 2020 + absolute_error_margin: 0.01 + input: + people: + head: + age: 40 + employment_income: 50_000 + tax_units: + tax_unit: + members: [head] + foreign_tax_credit_potential: 500 + households: + household: + members: [head] + state_code: TX + output: + # 2020 single: taxable income 50,000 - 12,400 = 37,600; tax = 10% x + # 9,875 + 12% x 27,725 = 4,314.50 (Form 1040, line 16). The 500 foreign + # tax credit (Schedule 3, line 1) is within that limit. + income_tax_before_credits: 4_314.5 + foreign_tax_credit: 500 + income_tax_capped_non_refundable_credits: 500 + income_tax_before_refundable_credits: 3_814.5 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ok/tax/income/credits/ok_federal_ctc.yaml b/policyengine_us/tests/policy/baseline/gov/states/ok/tax/income/credits/ok_federal_ctc.yaml new file mode 100644 index 00000000000..4f8d7ea0d38 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ok/tax/income/credits/ok_federal_ctc.yaml @@ -0,0 +1,113 @@ +# Federal Child Tax Credit allowed for Oklahoma's Child Care/Child Tax Credit: +# the refundable CTC plus the non-refundable CTC actually applied against +# federal income tax. non_refundable_ctc is the CTC that is not refunded, +# before the tax limit; when the non-refundable credits exceed income tax, the +# credits that Schedule 8812 Credit Limit Worksheet A subtracts are applied +# before the CTC (line 2, and on line 4 the residential clean energy credit +# when Credit Limit Worksheet B applies). Otherwise the residential clean +# energy credit follows the CTC (26 U.S.C. 25D(c)): +# https://www.irs.gov/pub/irs-pdf/i1040s8.pdf#page=4 + +- name: The residential clean energy credit does not displace the CTC when credits exceed tax. + period: 2025 + input: + state_code: OK + income_tax_before_credits: 1_000 + non_refundable_ctc: 500 + refundable_ctc: 0 + residential_clean_energy_credit: 1_000 + output: + # Credits 500 + 1,000 = 1,500 exceed tax of 1,000, so 1,000 is applied. + # No credit precedes the CTC, so all 500 of it is applied. + income_tax_capped_non_refundable_credits: 1_000 + ok_federal_ctc: 500 + +- name: A credit on Credit Limit Worksheet A, line 2, is applied before the CTC when credits exceed tax. + period: 2025 + input: + state_code: OK + income_tax_before_credits: 1_000 + elderly_disabled_credit: 800 + non_refundable_ctc: 500 + refundable_ctc: 0 + output: + # Credits 800 + 500 = 1,300 exceed tax of 1,000; the elderly or disabled + # credit takes 800 first, leaving 200 for the CTC. + income_tax_capped_non_refundable_credits: 1_000 + ok_federal_ctc: 200 + +- name: With Credit Limit Worksheet B, the residential clean energy credit is applied before the CTC. + period: 2025 + input: + state_code: OK + income_tax_before_credits: 1_000 + ctc_credit_limit_worksheet_b_applies: true + non_refundable_ctc: 500 + refundable_ctc: 0 + residential_clean_energy_credit: 800 + output: + # Worksheet A: line 3 = 1,000; line 4 = 800 (Worksheet B, line 15); + # line 5 = 200. + income_tax_capped_non_refundable_credits: 1_000 + ok_federal_ctc: 200 + +- name: Credit for other dependents larger than tax, computed in full. + period: 2025 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 45 + employment_income: 26_625 + child: + age: 17 + tax_units: + tax_unit: + members: [parent, child] + solar_electric_property_expenditures: 10_000 + households: + household: + members: [parent, child] + state_code: OK + output: + # Head of household: taxable income 26,625 - 23,625 = 3,000; tax = 300. + income_tax_before_credits: 300 + # The 500 credit for other dependents is limited to 300 (Worksheet A, + # line 5), which leaves nothing for the residential clean energy credit. + non_refundable_ctc: 500 + refundable_ctc: 0 + residential_clean_energy_credit: 0 + income_tax_capped_non_refundable_credits: 300 + ok_federal_ctc: 300 + +- name: Credit for other dependents and a residential clean energy credit, computed in full. + period: 2025 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 45 + employment_income: 33_625 + child: + age: 17 + tax_units: + tax_unit: + members: [parent, child] + solar_electric_property_expenditures: 10_000 + households: + household: + members: [parent, child] + state_code: OK + output: + # Head of household: taxable income 33,625 - 23,625 = 10,000; tax 10% = + # 1,000 (Form 1040, line 18). + income_tax_before_credits: 1_000 + # A 17-year-old dependent gets the 500 credit for other dependents, none of + # which is refundable; nothing on Worksheet A, line 2. + non_refundable_ctc: 500 + refundable_ctc: 0 + # Form 5695, line 14 worksheet: 1,000 - 500 = 500; the credit before the + # limit is 30% x 10,000 = 3,000. + residential_clean_energy_credit: 500 + income_tax_capped_non_refundable_credits: 1_000 + ok_federal_ctc: 500 diff --git a/policyengine_us/tests/test_federal_credit_limit_order.py b/policyengine_us/tests/test_federal_credit_limit_order.py new file mode 100644 index 00000000000..bb05a1a844c --- /dev/null +++ b/policyengine_us/tests/test_federal_credit_limit_order.py @@ -0,0 +1,336 @@ +"""The federal non-refundable credits are applied in one order. + +Each credit's own credit limit worksheet subtracts the credits that precede it +from income tax before credits. Read together, the 2013 to 2025 worksheets +give a single order (LEGAL_ORDER below, written from the worksheets and not +from the model's parameters): + +- Form 2441, Credit Limit Worksheet: the foreign tax credit. +- Schedule R, Credit Limit Worksheet: Schedule 3 lines 1 and 2. +- Form 8863, Credit Limit Worksheet: Schedule 3 lines 1, 2 and 6d. +- Form 8880, Credit Limit Worksheet: Schedule 3 lines 1 through 3 and 6d. +- Form 5695, line 31 worksheet: those and Schedule 3 line 4. +- Form 8936, line 16: Schedule 3 lines 1 through 4, 5b and 6d. +- Form 8936, line 11: the same and line 6m. +- Schedule 8812, Credit Limit Worksheet A: lines 1 through 4, 5b, 6d, 6f, 6m. +- Form 5695, line 14 worksheet: every other credit, including the CTC. + +These tests check that the parameters encode that order, and that the model's +credits match a direct sequential application of 26 U.S.C. 26(a) in that +order. The households take the standard deduction, so the CTC limit's no-SALT +liability equals actual liability. +""" + +import numpy as np +import pytest +from hypothesis import HealthCheck, given, settings +from hypothesis import strategies as st + +from policyengine_us import CountryTaxBenefitSystem, Simulation +from policyengine_us.reforms.congress.afa.afa_other_dependent_credit import ( + create_afa_other_dependent_credit, +) +from policyengine_us.reforms.crfb.non_refundable_ss_credit import ( + non_refundable_ss_credit_reform, +) +from policyengine_us.tools.pinned_tbs import get_2020_irc_tbs + +LEGAL_ORDER = [ + "foreign_tax_credit", + "cdcc", + "elderly_disabled_credit", + "non_refundable_american_opportunity_credit", + "lifetime_learning_credit", + "savers_credit", + "energy_efficient_home_improvement_credit", + "used_clean_vehicle_credit", + "new_clean_vehicle_credit", + "non_refundable_ctc", + "residential_clean_energy_credit", +] + +# The parameter listing the credits that precede each credit. The foreign tax +# credit has none: its limit is the whole tax. +PRECEDING_CREDITS = { + "cdcc": "cdcc.preceding_credits", + "elderly_disabled_credit": "elderly_or_disabled.preceding_credits", + "non_refundable_american_opportunity_credit": ( + "education.american_opportunity_credit.preceding_credits" + ), + "lifetime_learning_credit": "education.lifetime_learning_credit.preceding_credits", + "savers_credit": "retirement_saving.preceding_credits", + "energy_efficient_home_improvement_credit": ( + "energy_efficient_home_improvement.preceding_credits" + ), + "used_clean_vehicle_credit": "clean_vehicle.used.preceding_credits", + "new_clean_vehicle_credit": "clean_vehicle.new.preceding_credits", + "non_refundable_ctc": "ctc_tax_liability_limit.preceding_credits", + "residential_clean_energy_credit": "residential_clean_energy.preceding_credits", +} + +DATES = [ + "2013-01-01", + "2017-06-01", + "2020-06-01", + "2021-06-01", + "2022-06-01", + "2023-06-01", + "2025-06-01", + "2026-06-01", + "2035-06-01", +] + + +def credit_list(credits, path, date): + node = credits + for part in path.split("."): + node = getattr(node, part) + return list(node(date)) + + +def expected_preceding(non_refundable, credit): + before = non_refundable[: non_refundable.index(credit)] + if credit == "residential_clean_energy_credit": + # Its limit subtracts the CTC in the formula (Form 1040, line 19, or + # Credit Limit Worksheet B, line 14), not through the list. + before = [c for c in before if c != "non_refundable_ctc"] + return before + + +@pytest.fixture(scope="module") +def baseline_system(): + return CountryTaxBenefitSystem() + + +@pytest.mark.parametrize("date", DATES) +def test_lists_follow_the_worksheet_order(baseline_system, date): + credits = baseline_system.parameters.gov.irs.credits + non_refundable = list(credits.non_refundable(date)) + assert non_refundable == [c for c in LEGAL_ORDER if c in non_refundable] + assert non_refundable[0] == "foreign_tax_credit" + for credit in non_refundable[1:]: + assert credit in PRECEDING_CREDITS, f"{credit} has no preceding list" + assert credit_list( + credits, PRECEDING_CREDITS[credit], date + ) == expected_preceding(non_refundable, credit), (credit, date) + + +def test_2020_irc_pin_keeps_the_order_in_2021(baseline_system): + credits = get_2020_irc_tbs(baseline_system).parameters.gov.irs.credits + date = "2021-06-01" + non_refundable = list(credits.non_refundable(date)) + assert "cdcc" in non_refundable + assert non_refundable == [c for c in LEGAL_ORDER if c in non_refundable] + for credit in non_refundable[1:]: + assert credit_list( + credits, PRECEDING_CREDITS[credit], date + ) == expected_preceding(non_refundable, credit), credit + + +@pytest.mark.parametrize( + "reform, credit_before_25d", + [ + (create_afa_other_dependent_credit(), "other_dependent_credit"), + (non_refundable_ss_credit_reform(), "non_refundable_ctc"), + ], +) +def test_reforms_order_the_ctc_and_25d_lists(reform, credit_before_25d): + credits = reform(CountryTaxBenefitSystem()).parameters.gov.irs.credits + date = "2026-06-01" + non_refundable = list(credits.non_refundable(date)) + assert non_refundable[0] == "foreign_tax_credit" + assert non_refundable[-2:] == [credit_before_25d, "residential_clean_energy_credit"] + before_ctc = non_refundable[: non_refundable.index(credit_before_25d)] + if credit_before_25d == "other_dependent_credit": + # The separate credit for other dependents takes the CTC's place. + before_ctc = before_ctc + ["other_dependent_credit"] + for path in ( + "ctc_tax_liability_limit.preceding_credits", + "residential_clean_energy.preceding_credits", + ): + assert credit_list(credits, path, date) == before_ctc, path + + +# Households with every modeled non-refundable credit at once. The potentials +# (each credit before its tax-liability limit) are inputs. +POTENTIALS = { + "foreign_tax_credit": "foreign_tax_credit_potential", + "cdcc": "cdcc_potential", + "elderly_disabled_credit": "elderly_disabled_credit_potential", + "non_refundable_american_opportunity_credit": ( + "non_refundable_american_opportunity_credit_potential" + ), + "lifetime_learning_credit": "lifetime_learning_credit_potential", + "savers_credit": "savers_credit_potential", + "energy_efficient_home_improvement_credit": ( + "energy_efficient_home_improvement_credit_potential" + ), + "used_clean_vehicle_credit": "used_clean_vehicle_credit_potential", + "new_clean_vehicle_credit": "new_clean_vehicle_credit_potential", +} +ELIGIBILITY = { + "used_clean_vehicle_credit": "used_clean_vehicle_credit_eligible", + "new_clean_vehicle_credit": "new_clean_vehicle_credit_eligible", +} + + +def build_situation(households, year): + people, tax_units, marital_units = {}, {}, {} + groups = {"households": {}, "spm_units": {}, "families": {}} + for i, h in enumerate(households): + head = f"head_{i}" + people[head] = { + "age": {year: h["age"]}, + "employment_income": {year: h["wages"]}, + "taxable_interest_income": {year: h["interest"]}, + } + members = [head] + marital_units[f"marital_unit_{i}"] = {"members": [head]} + if h["married"]: + spouse = f"spouse_{i}" + people[spouse] = {"age": {year: h["age"]}} + members.append(spouse) + marital_units[f"marital_unit_{i}"]["members"].append(spouse) + for j, age in enumerate(h["dependent_ages"]): + dependent = f"dependent_{i}_{j}" + people[dependent] = {"age": {year: age}} + members.append(dependent) + marital_units[f"marital_unit_{i}_{j}"] = {"members": [dependent]} + tax_unit = { + "members": members, + "solar_electric_property_expenditures": {year: h["solar"]}, + } + for credit, potential in POTENTIALS.items(): + tax_unit[potential] = {year: h[credit]} + for credit, eligible in ELIGIBILITY.items(): + tax_unit[eligible] = {year: h[credit] > 0} + tax_units[f"tax_unit_{i}"] = tax_unit + groups["households"][f"household_{i}"] = { + "members": members, + "state_code": {year: "TX"}, + } + groups["spm_units"][f"spm_unit_{i}"] = {"members": members} + groups["families"][f"family_{i}"] = {"members": members} + return { + "people": people, + "tax_units": tax_units, + "marital_units": marital_units, + **groups, + } + + +def check_households(households, year): + simulation = Simulation(situation=build_situation(households, year)) + + def calc(variable): + return np.asarray(simulation.calculate(variable, year), dtype=float) + + credits = simulation.tax_benefit_system.parameters(year).gov.irs.credits + non_refundable = list(credits.non_refundable) + tax = calc("income_tax_before_credits") + tol = 0.01 + + # Apply 26(a) in the worksheet order: each credit is the smaller of its + # potential and the tax the earlier credits leave. + remaining = np.maximum(tax, 0) + limited = {} + for credit in LEGAL_ORDER: + if credit not in non_refundable or credit not in POTENTIALS: + continue + potential = calc(POTENTIALS[credit]) + expected = np.minimum(potential, remaining) + np.testing.assert_allclose(calc(credit), expected, atol=tol, err_msg=credit) + limited[credit] = (expected > 0) & (expected < potential - tol) + remaining = remaining - expected + if "non_refundable_ctc" in non_refundable: + # Schedule 8812 Credit Limit Worksheet A, line 3. + np.testing.assert_allclose( + calc("ctc_tax_liability_after_preceding_credits"), remaining, atol=tol + ) + + # The per-credit amounts add up to the capped total: no credit is allowed + # tax that an earlier credit already used. non_refundable_ctc is the CTC + # that is not refunded, before its tax limit (intended), so its applied + # amount is the smaller of it and Credit Limit Worksheet A, line 5. + applied = {credit: calc(credit) for credit in non_refundable} + unused_ctc = 0 + if "non_refundable_ctc" in non_refundable: + not_refunded = applied["non_refundable_ctc"] + applied["non_refundable_ctc"] = np.minimum( + not_refunded, calc("ctc_limiting_tax_liability") + ) + unused_ctc = not_refunded - applied["non_refundable_ctc"] + capped = calc("income_tax_capped_non_refundable_credits") + np.testing.assert_allclose(sum(applied.values()), capped, atol=tol) + assert (capped <= np.maximum(tax, 0) + tol).all() + # The aggregate 26(a) cap removes only the unused CTC. + np.testing.assert_allclose( + calc("income_tax_unavailable_non_refundable_credits"), unused_ctc, atol=tol + ) + return limited + + +credit_amount = st.one_of(st.just(0), st.integers(1, 5_000)) +household_strategy = st.fixed_dictionaries( + { + "age": st.integers(25, 80), + "married": st.booleans(), + "dependent_ages": st.lists(st.integers(0, 18), max_size=3), + "wages": st.integers(0, 150_000), + "interest": st.integers(0, 60_000), + "solar": st.one_of(st.just(0), st.integers(1, 30_000)), + **{credit: credit_amount for credit in POTENTIALS}, + } +) + +GRID = [ + { + "age": 70, + "married": married, + "dependent_ages": ages, + "wages": wages, + "interest": 0, + "solar": solar, + **{credit: amount for credit in POTENTIALS}, + } + for married in (True, False) + for ages in ([], [5, 8], [17]) + for wages in (0, 25_000, 45_000, 80_000, 150_000) + for solar in (0, 10_000) + for amount in (0, 300, 2_000) +] + [ + # A wage ladder with 300 of every credit: tax rises by about 50 a step, so + # it stops inside each credit's 300 slot in turn. + { + "age": 40, + "married": False, + "dependent_ages": [], + "wages": wages, + "interest": 0, + "solar": 0, + **{credit: 300 for credit in POTENTIALS}, + } + for wages in range(12_000, 45_000, 500) +] + + +@pytest.mark.parametrize("year", [2018, 2021, 2025]) +def test_grid_applies_credits_in_worksheet_order(year): + limited = check_households(GRID, year) + # The grid partly limits every credit, so each list binds. + assert len(limited) >= 6 + for credit, partly_limited in limited.items(): + assert partly_limited.any(), credit + + +@settings( + max_examples=10, + deadline=None, + suppress_health_check=[HealthCheck.too_slow, HealthCheck.data_too_large], +) +@given( + st.lists(household_strategy, min_size=1, max_size=20), + st.sampled_from([2018, 2021, 2023, 2025, 2026]), +) +def test_random_households_apply_credits_in_worksheet_order(households, year): + check_households(households, year) diff --git a/policyengine_us/tools/pinned_tbs.py b/policyengine_us/tools/pinned_tbs.py index ab0b7e445c1..139e4bb7ec8 100644 --- a/policyengine_us/tools/pinned_tbs.py +++ b/policyengine_us/tools/pinned_tbs.py @@ -103,23 +103,28 @@ def _pin_2020_irc(tbs): ) except Exception: pass - # The 2020 CTC & ODC Worksheet (Pub. 972, p.7) uses the CDCC before the CTC - # when computing the CTC's tax-liability limit, so the recomputed CDCC must - # reduce that limit (`ctc_tax_liability_after_preceding_credits`) and the - # residential clean energy credit's limit, and count among the - # non-refundable credits. The 2021 lists omit `cdcc` (it was refundable - # under ARPA). Add `cdcc` to each current 2021 membership rather than - # pinning the whole 2020 lists, which would drop `new_clean_vehicle_credit` - # (2021+). - for credit_list in ( - credits.non_refundable, - credits.ctc_tax_liability_limit.preceding_credits, - credits.residential_clean_energy.preceding_credits, - ): + # The 2020 credit limit worksheets (e.g. the CTC & ODC Worksheet, Pub. 972, + # p.7) subtract the CDCC from tax before every credit but the foreign tax + # credit, so the recomputed CDCC must reduce each later credit's limit and + # count among the non-refundable credits. The 2021 lists omit `cdcc` (it + # was refundable under ARPA). Add `cdcc` after the foreign tax credit in + # each current 2021 list whose 2020 version had it, rather than pinning + # the whole 2020 lists, which would drop `new_clean_vehicle_credit` (2021+). + credit_lists = [credits.non_refundable] + [ + param + for param in credits.get_descendants() + if isinstance(param, Parameter) and param.name.endswith(".preceding_credits") + ] + for credit_list in credit_lists: try: members_2021 = list(credit_list(start)) - if "cdcc" not in members_2021: - members_2021 = ["cdcc"] + members_2021 + if "cdcc" in credit_list(pin_date) and "cdcc" not in members_2021: + position = ( + members_2021.index("foreign_tax_credit") + 1 + if "foreign_tax_credit" in members_2021 + else 0 + ) + members_2021.insert(position, "cdcc") credit_list.update(start=start, stop=stop, value=members_2021) except Exception: pass diff --git a/policyengine_us/variables/gov/irs/credits/clean_vehicle/used/used_clean_vehicle_credit.py b/policyengine_us/variables/gov/irs/credits/clean_vehicle/used/used_clean_vehicle_credit.py index 802f79169b6..31a48bb01fb 100644 --- a/policyengine_us/variables/gov/irs/credits/clean_vehicle/used/used_clean_vehicle_credit.py +++ b/policyengine_us/variables/gov/irs/credits/clean_vehicle/used/used_clean_vehicle_credit.py @@ -14,8 +14,8 @@ class used_clean_vehicle_credit(Variable): defined_for = "used_clean_vehicle_credit_eligible" def formula(tax_unit, period, parameters): - p = parameters(period).gov.irs.credits.clean_vehicle.used - sale_price = tax_unit("used_clean_vehicle_sale_price", period) - # Amount is lesser of $4,000 and 30% of sale price. - uncapped_amount = sale_price * p.amount.percent_of_sale_price - return min_(uncapped_amount, p.amount.max) + # Form 8936, line 18: the smaller of the credit and the tax liability + # limit; the unused credit is lost. + credit_limit = tax_unit("used_clean_vehicle_credit_credit_limit", period) + potential = tax_unit("used_clean_vehicle_credit_potential", period) + return min_(credit_limit, potential) diff --git a/policyengine_us/variables/gov/states/ok/tax/income/credits/ok_federal_ctc.py b/policyengine_us/variables/gov/states/ok/tax/income/credits/ok_federal_ctc.py index 4b5858a96ab..36d9c1f0ded 100644 --- a/policyengine_us/variables/gov/states/ok/tax/income/credits/ok_federal_ctc.py +++ b/policyengine_us/variables/gov/states/ok/tax/income/credits/ok_federal_ctc.py @@ -13,6 +13,9 @@ class ok_federal_ctc(Variable): "https://www.oscn.net/applications/oscn/DeliverDocument.asp?CiteID=92568", # 2025 Form 511 packet, pages 11 and 25. "https://oklahoma.gov/content/dam/ok/en/tax/documents/forms/individuals/current/511-Pkt.pdf#page=11", + # 2025 Instructions for Schedule 8812, Credit Limit Worksheet A. + "https://www.irs.gov/pub/irs-pdf/i1040s8.pdf#page=4", + "https://www.law.cornell.edu/uscode/text/26/25D#c", ) documentation = ( "Federal Child Tax Credit allowed for Oklahoma's Child Care/Child " @@ -21,7 +24,8 @@ class ok_federal_ctc(Variable): ) def formula(tax_unit, period, parameters): - non_refundable_credits = parameters(period).gov.irs.credits.non_refundable + credits = parameters(period).gov.irs.credits + non_refundable_credits = credits.non_refundable refundable_ctc = tax_unit("refundable_ctc", period) # NOTE: Fully-refundable-CTC reforms (e.g. the American Family Act # contrib) drop non_refundable_ctc from the federal list, so there is @@ -32,9 +36,21 @@ def formula(tax_unit, period, parameters): # choice); the baseline keeps the ODC inside ctc, so it is unaffected. if "non_refundable_ctc" not in non_refundable_credits: return refundable_ctc - ctc_index = non_refundable_credits.index("non_refundable_ctc") - credits_before_ctc = non_refundable_credits[:ctc_index] - preceding_credits = add(tax_unit, period, credits_before_ctc) + # The credits applied before the CTC are those Schedule 8812 Credit + # Limit Worksheet A subtracts, not those listed before it in the + # non-refundable list: line 2, and on line 4 the residential clean + # energy credit only when Credit Limit Worksheet B applies. Otherwise + # that credit follows the CTC (26 U.S.C. 25D(c)). + limit = credits.ctc_tax_liability_limit + + def applied(credit_list): + credit_list = [c for c in credit_list if c in non_refundable_credits] + return add(tax_unit, period, credit_list) if credit_list else 0 + + worksheet_b_applies = tax_unit("ctc_credit_limit_worksheet_b_applies", period) + preceding_credits = applied(limit.preceding_credits) + where( + worksheet_b_applies, applied(limit.subsequent_credits), 0 + ) non_refundable_ctc = tax_unit("non_refundable_ctc", period) total_non_refundable_credits = tax_unit(