diff --git a/changelog.d/ia-2018-misc-deduction.fixed.md b/changelog.d/ia-2018-misc-deduction.fixed.md new file mode 100644 index 00000000000..13ce8ba8d49 --- /dev/null +++ b/changelog.d/ia-2018-misc-deduction.fixed.md @@ -0,0 +1 @@ +Keep Iowa's 2018 miscellaneous itemized deduction above 2% of federal AGI, which Iowa did not suspend until 2019, and apply Iowa's 2018 limitation on the itemized deductions of filers with federal AGI above the IA 104 applicable amounts. diff --git a/policyengine_us/parameters/gov/states/ia/tax/income/deductions/itemized/limitation/agi_rate.yaml b/policyengine_us/parameters/gov/states/ia/tax/income/deductions/itemized/limitation/agi_rate.yaml new file mode 100644 index 00000000000..47f9f3a975b --- /dev/null +++ b/policyengine_us/parameters/gov/states/ia/tax/income/deductions/itemized/limitation/agi_rate.yaml @@ -0,0 +1,12 @@ +description: Iowa reduces itemized deductions by this fraction of federal adjusted gross income above the applicable amount, up to the itemized deductions cap. +values: + 2018-01-01: 0.03 +metadata: + unit: /1 + period: year + label: Iowa itemized deductions limitation adjusted gross income rate + reference: + - title: 2018 IA 104 Iowa Itemized Deductions Worksheet, line 10 + href: https://revenue.iowa.gov/media/2335/download?inline=#page=1 + - title: 26 U.S. Code § 68(a)(1) (2014 edition), in effect on January 1, 2015 + href: https://www.govinfo.gov/content/pkg/USCODE-2014-title26/html/USCODE-2014-title26-subtitleA-chap1-subchapB-partI-sec68.htm diff --git a/policyengine_us/parameters/gov/states/ia/tax/income/deductions/itemized/limitation/applicable_amount.yaml b/policyengine_us/parameters/gov/states/ia/tax/income/deductions/itemized/limitation/applicable_amount.yaml new file mode 100644 index 00000000000..e5e9ab9b803 --- /dev/null +++ b/policyengine_us/parameters/gov/states/ia/tax/income/deductions/itemized/limitation/applicable_amount.yaml @@ -0,0 +1,24 @@ +description: Iowa limits the itemized deductions of filers whose federal adjusted gross income exceeds this amount, based on federal filing status. +metadata: + propagate_metadata_to_children: true + label: Iowa itemized deductions limitation applicable amount + period: year + unit: currency-USD + breakdown: + - filing_status + reference: + - title: 2018 IA 104 Iowa Itemized Deductions Worksheet, line 8 + href: https://revenue.iowa.gov/media/2335/download?inline=#page=1 + - title: Rev. Proc. 2017-58, section 3.15, applicable amounts under section 68(b) for 2018 + href: https://www.irs.gov/pub/irs-drop/rp-17-58.pdf#page=14 + +SINGLE: + 2018-01-01: 266_700 +JOINT: + 2018-01-01: 320_000 +SEPARATE: + 2018-01-01: 160_000 +HEAD_OF_HOUSEHOLD: + 2018-01-01: 293_350 +SURVIVING_SPOUSE: + 2018-01-01: 320_000 diff --git a/policyengine_us/parameters/gov/states/ia/tax/income/deductions/itemized/limitation/itemized_deduction_rate.yaml b/policyengine_us/parameters/gov/states/ia/tax/income/deductions/itemized/limitation/itemized_deduction_rate.yaml new file mode 100644 index 00000000000..288542624dd --- /dev/null +++ b/policyengine_us/parameters/gov/states/ia/tax/income/deductions/itemized/limitation/itemized_deduction_rate.yaml @@ -0,0 +1,12 @@ +description: Iowa caps the reduction of itemized deductions at this fraction of the itemized deductions subject to the limitation. +values: + 2018-01-01: 0.8 +metadata: + unit: /1 + period: year + label: Iowa itemized deductions limitation cap rate + reference: + - title: 2018 IA 104 Iowa Itemized Deductions Worksheet, line 6 + href: https://revenue.iowa.gov/media/2335/download?inline=#page=1 + - title: 26 U.S. Code § 68(a)(2) (2014 edition), in effect on January 1, 2015 + href: https://www.govinfo.gov/content/pkg/USCODE-2014-title26/html/USCODE-2014-title26-subtitleA-chap1-subchapB-partI-sec68.htm diff --git a/policyengine_us/parameters/gov/states/ia/tax/income/deductions/itemized/limitation/retained.yaml b/policyengine_us/parameters/gov/states/ia/tax/income/deductions/itemized/limitation/retained.yaml new file mode 100644 index 00000000000..438b363bc0a --- /dev/null +++ b/policyengine_us/parameters/gov/states/ia/tax/income/deductions/itemized/limitation/retained.yaml @@ -0,0 +1,20 @@ +description: Iowa keeps the limitation on the itemized deductions of high-income filers, which federal law suspends from 2018, if this is true. +values: + 2017-01-01: false + 2018-01-01: true + 2019-01-01: false +metadata: + unit: bool + period: year + label: Iowa retains the itemized deductions limitation + reference: + - title: Iowa Code 422.3(5) (2018), Internal Revenue Code as amended and in effect on January 1, 2015 + href: https://www.legis.iowa.gov/docs/code/2018/422.3.pdf#page=1 + - title: 2018 Iowa Acts, chapter 1161, sections 58, 63, 66 and 69 + href: https://www.legis.iowa.gov/docs/acts/2018/CH1161.pdf#page=14 + - title: 2018 IA 1040 expanded instructions, Schedule A line 26 + href: https://revenue.iowa.gov/media/2228/download?inline=#page=55 + - title: 2018 IA 104 Iowa Itemized Deductions Worksheet + href: https://revenue.iowa.gov/media/2335/download?inline=#page=1 + - title: 2019 IA 1040 Schedule A, line 21 + href: https://revenue.iowa.gov/media/2490/download?inline=#page=1 diff --git a/policyengine_us/parameters/gov/states/ia/tax/income/deductions/itemized/misc_deduction_retained.yaml b/policyengine_us/parameters/gov/states/ia/tax/income/deductions/itemized/misc_deduction_retained.yaml new file mode 100644 index 00000000000..e562cacd79f --- /dev/null +++ b/policyengine_us/parameters/gov/states/ia/tax/income/deductions/itemized/misc_deduction_retained.yaml @@ -0,0 +1,20 @@ +description: Iowa keeps the miscellaneous itemized deduction above 2% of adjusted gross income, which federal law suspends from 2018, if this is true. +values: + 2017-01-01: false + 2018-01-01: true + 2019-01-01: false +metadata: + unit: bool + period: year + label: Iowa retains the miscellaneous itemized deduction + reference: + - title: Iowa Code 422.3(5) (2018), Internal Revenue Code as amended and in effect on January 1, 2015 + href: https://www.legis.iowa.gov/docs/code/2018/422.3.pdf#page=1 + - title: Iowa Code 422.9(2) (2018), miscellaneous expenses deductible under the Internal Revenue Code + href: https://www.legis.iowa.gov/docs/code/2018/422.9.pdf#page=1 + - title: 2018 IA 1040 expanded instructions, Schedule A job expenses and miscellaneous deductions + href: https://revenue.iowa.gov/media/2228/download?inline=#page=54 + - title: 2018 IA Schedule A, lines 19 to 24 + href: https://revenue.iowa.gov/media/2298/download?inline=#page=1 + - title: 2019 IA 1040 expanded instructions, Schedule A + href: https://revenue.iowa.gov/media/2492/download?inline=#page=49 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ia/tax/income/ia_itemized_deductions_indiv.yaml b/policyengine_us/tests/policy/baseline/gov/states/ia/tax/income/ia_itemized_deductions_indiv.yaml index d12afc7a38e..5bd746ec661 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ia/tax/income/ia_itemized_deductions_indiv.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ia/tax/income/ia_itemized_deductions_indiv.yaml @@ -21,3 +21,38 @@ state_code: IA output: ia_itemized_deductions_indiv: [24_000, 6_000] + +- name: 2018 spouses filing separately on a combined return split the limited total + period: 2018 + input: + people: + person1: + is_tax_unit_head: true + ia_net_income: 300_000 + real_estate_taxes: 8_000 + deductible_mortgage_interest: 14_000 + ia_withheld_income_tax: 15_000 + person2: + is_tax_unit_spouse: true + ia_net_income: 100_000 + ia_withheld_income_tax: 5_000 + tax_units: + tax_unit: + members: [person1, person2] + filing_status: JOINT + adjusted_gross_income: 400_000 + charitable_deduction: 6_000 + medical_expense_deduction: 4_000 + state_sales_tax: 0 + local_sales_tax: 0 + households: + household: + members: [person1, person2] + state_code: IA + output: + # 2018 IA 104 line 21 with the $320,000 joint amount: 30,600 (see + # ia_itemized_deductions_reduction.yaml). 2018 IA Schedule A lines 29 to + # 33 split it by net income: 300,000 / 400,000 = 75.0%, so 22,950 and + # 7,650: https://revenue.iowa.gov/media/2298/download?inline=#page=1 + ia_itemized_deductions_unit: 30_600 + ia_itemized_deductions_indiv: [22_950, 7_650] diff --git a/policyengine_us/tests/policy/baseline/gov/states/ia/tax/income/ia_itemized_deductions_reduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/ia/tax/income/ia_itemized_deductions_reduction.yaml new file mode 100644 index 00000000000..efaa2011ac1 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ia/tax/income/ia_itemized_deductions_reduction.yaml @@ -0,0 +1,281 @@ +# 2018 IA 104 Iowa Itemized Deductions Worksheet: +# https://revenue.iowa.gov/media/2335/download?inline=#page=1 +# 2018 IA Schedule A line 26 sends filers with federal AGI over $160,000 to +# the worksheet: https://revenue.iowa.gov/media/2298/download?inline=#page=1 +# Line 1 is Schedule A lines 1 to 25, line 2 the Iowa income tax on federal +# Schedule A line 5a, line 4 medical, investment interest and casualty losses. + +- name: 2018 single filer at $150,000 is not limited + period: 2018 + input: + filing_status: SINGLE + adjusted_gross_income: 150_000 + real_estate_taxes: 6_000 + deductible_mortgage_interest: 9_000 + charitable_deduction: 3_000 + ia_withheld_income_tax: 0 + state_code: IA + output: + # Schedule A line 26: federal AGI is not over $160,000, so add the lines. + ia_itemized_deductions_pre_limitation: 18_000 + ia_itemized_deductions_reduction: 0 + ia_itemized_deductions_unit: 18_000 + +- name: 2018 separate filer at exactly $160,000 is not limited + period: 2018 + input: + filing_status: SEPARATE + adjusted_gross_income: 160_000 + real_estate_taxes: 6_000 + deductible_mortgage_interest: 9_000 + charitable_deduction: 3_000 + ia_withheld_income_tax: 0 + state_code: IA + output: + # IA 104 line 9: line 8 ($160,000) is not less than line 7 ($160,000). + ia_itemized_deductions_reduction: 0 + ia_itemized_deductions_unit: 18_000 + +- name: 2018 separate filer above $160,000 is limited + period: 2018 + input: + filing_status: SEPARATE + adjusted_gross_income: 170_000 + real_estate_taxes: 6_000 + deductible_mortgage_interest: 9_000 + charitable_deduction: 3_000 + ia_withheld_income_tax: 0 + state_code: IA + output: + # Lines 1, 3 and 5: 18,000; line 6: 0.8 * 18,000 = 14,400. + # Line 9: 170,000 - 160,000 = 10,000; line 10: 3% = 300; line 11: 300. + # Line 12: 18,000 - 300 = 17,700; line 2 is zero, so line 21 = 17,700. + ia_itemized_deductions_reduction: 300 + ia_itemized_deductions_unit: 17_700 + +- name: 2018 single filer above $160,000 but below $266,700 is not limited + period: 2018 + input: + filing_status: SINGLE + adjusted_gross_income: 170_000 + real_estate_taxes: 6_000 + deductible_mortgage_interest: 9_000 + charitable_deduction: 3_000 + ia_withheld_income_tax: 0 + state_code: IA + output: + # IA 104 line 9: line 8 ($266,700 for single) is not less than line 7. + ia_itemized_deductions_reduction: 0 + ia_itemized_deductions_unit: 18_000 + +- name: 2018 head of household above $293,350 is limited + period: 2018 + input: + filing_status: HEAD_OF_HOUSEHOLD + adjusted_gross_income: 303_350 + real_estate_taxes: 6_000 + deductible_mortgage_interest: 9_000 + charitable_deduction: 3_000 + ia_withheld_income_tax: 0 + state_code: IA + output: + # Line 9: 303,350 - 293,350 = 10,000; line 10: 300; line 6: 14,400. + # Line 11: 300; line 21: 18,000 - 300 = 17,700. + ia_itemized_deductions_reduction: 300 + ia_itemized_deductions_unit: 17_700 + +- name: 2018 surviving spouse above $320,000 is limited + period: 2018 + input: + filing_status: SURVIVING_SPOUSE + adjusted_gross_income: 330_000 + real_estate_taxes: 6_000 + deductible_mortgage_interest: 9_000 + charitable_deduction: 3_000 + ia_withheld_income_tax: 0 + state_code: IA + output: + # Line 9: 330,000 - 320,000 = 10,000; line 10: 300; line 6: 14,400. + # Line 11: 300; line 21: 18,000 - 300 = 17,700. + ia_itemized_deductions_reduction: 300 + ia_itemized_deductions_unit: 17_700 + +- name: 2018 joint filer shares the reduction with Iowa income tax + period: 2018 + absolute_error_margin: 0.01 + input: + filing_status: JOINT + adjusted_gross_income: 400_000 + real_estate_taxes: 8_000 + deductible_mortgage_interest: 14_000 + charitable_deduction: 6_000 + medical_expense_deduction: 4_000 + ia_withheld_income_tax: 20_000 + state_sales_tax: 0 + local_sales_tax: 0 + state_code: IA + output: + # Line 1: 8,000 + 14,000 + 6,000 + 4,000 = 32,000; line 2: 20,000; + # line 3: 52,000; line 4: 4,000 (medical); line 5: 48,000; + # line 6: 38,400; line 9: 400,000 - 320,000 = 80,000; line 10: 2,400; + # line 11: 2,400; line 12: 49,600; line 15: 49,600 - 4,000 = 45,600; + # line 17: 52,000 - 4,000 = 48,000; line 18: 95.0%; + # line 20: 20,000 * 95% = 19,000; line 21: 49,600 - 19,000 = 30,600. + ia_itemized_deductions_pre_limitation: 32_000 + ia_itemized_deductions_reduction: 1_400 + ia_itemized_deductions_unit: 30_600 + +- name: 2018 line 18 rounds the Iowa income tax share to a tenth of a percent + period: 2018 + absolute_error_margin: 0.01 + input: + filing_status: JOINT + adjusted_gross_income: 400_000 + real_estate_taxes: 8_000 + deductible_mortgage_interest: 14_000 + charitable_deduction: 4_000 + medical_expense_deduction: 4_000 + ia_withheld_income_tax: 20_000 + state_sales_tax: 0 + local_sales_tax: 0 + state_code: IA + output: + # Line 1: 30,000; line 2: 20,000; line 3: 50,000; line 4: 4,000; + # line 5: 46,000; line 6: 36,800; line 10: 2,400; line 11: 2,400; + # line 12: 47,600; line 15: 43,600; line 17: 46,000; + # line 18: 94.78% enters as 94.8%; line 20: 18,960; + # line 21: 47,600 - 18,960 = 28,640. The exact share gives 28,643.48. + ia_itemized_deductions_pre_limitation: 30_000 + ia_itemized_deductions_reduction: 1_360 + ia_itemized_deductions_unit: 28_640 + +- name: 2018 rounding on line 18 can raise line 21 above line 1 + period: 2018 + absolute_error_margin: 0.01 + input: + filing_status: JOINT + adjusted_gross_income: 500_000 + medical_expense_deduction: 12_000 + investment_interest_expense: 8_000 + ia_withheld_income_tax: 9_700 + state_sales_tax: 0 + local_sales_tax: 0 + state_code: IA + output: + # Line 1: 20,000; line 3: 29,700; line 4: 20,000; line 5: 9,700; + # line 6: 7,760; line 10: 5,400; line 11: 5,400; line 12: 24,300; + # line 15: 4,300; line 17: 9,700; line 18: 44.33% enters as 44.3%; + # line 20: 4,297.10; line 21: 24,300 - 4,297.10 = 20,002.90. + ia_itemized_deductions_pre_limitation: 20_000 + ia_itemized_deductions_reduction: -2.90 + ia_itemized_deductions_unit: 20_002.90 + +- name: 2018 line 18 rounds an exact half up + period: 2018 + absolute_error_margin: 0.01 + input: + filing_status: JOINT + adjusted_gross_income: 410_000 + real_estate_taxes: 10_000 + deductible_mortgage_interest: 10_000 + ia_withheld_income_tax: 20_000 + state_sales_tax: 0 + local_sales_tax: 0 + state_code: IA + output: + # Line 1: 20,000; line 2: 20,000; line 3: 40,000; line 4: 0; line 5: 40,000; + # line 6: 32,000; line 9: 90,000; line 10: 2,700; line 11: 2,700; + # line 12: 37,300; line 15: 37,300; line 17: 40,000; + # line 18: 93.25% enters as 93.3%; line 20: 18,660; + # line 21: 37,300 - 18,660 = 18,640. + ia_itemized_deductions_reduction: 1_360 + ia_itemized_deductions_unit: 18_640 + +- name: 2018 joint filer with sales tax on federal Schedule A has no Iowa income tax on line 2 + period: 2018 + input: + filing_status: JOINT + adjusted_gross_income: 400_000 + real_estate_taxes: 8_000 + deductible_mortgage_interest: 14_000 + charitable_deduction: 6_000 + medical_expense_deduction: 4_000 + ia_withheld_income_tax: 20_000 + state_sales_tax: 25_000 + local_sales_tax: 0 + state_code: IA + output: + # Federal line 5a holds the larger sales tax, so line 2 is zero. + # Line 3: 32,000; line 5: 28,000; line 6: 22,400; line 11: 2,400; + # line 12 = line 21: 32,000 - 2,400 = 29,600. + ia_itemized_deductions_reduction: 2_400 + ia_itemized_deductions_unit: 29_600 + +- name: 2018 cap of 80% of the limited deductions binds + period: 2018 + input: + filing_status: SINGLE + adjusted_gross_income: 1_000_000 + real_estate_taxes: 4_000 + deductible_mortgage_interest: 6_000 + ia_withheld_income_tax: 0 + state_code: IA + output: + # Line 5: 10,000; line 6: 8,000; line 9: 1,000,000 - 266,700 = 733,300; + # line 10: 21,999; line 11: 8,000; line 21: 10,000 - 8,000 = 2,000. + ia_itemized_deductions_reduction: 8_000 + ia_itemized_deductions_unit: 2_000 + +- name: 2018 Iowa income tax absorbs the reduction when only excluded deductions remain + period: 2018 + absolute_error_margin: 0.01 + input: + filing_status: JOINT + adjusted_gross_income: 500_000 + medical_expense_deduction: 12_000 + investment_interest_expense: 8_000 + ia_withheld_income_tax: 10_000 + state_sales_tax: 0 + local_sales_tax: 0 + state_code: IA + output: + # Line 1: 20,000; line 3: 30,000; line 4: 12,000 + 8,000 = 20,000; + # line 5: 10,000; line 6: 8,000; line 9: 180,000; line 10: 5,400; + # line 11: 5,400; line 12: 24,600; line 15: 4,600; line 17: 10,000; + # line 18: 46.0%; line 20: 4,600; line 21: 24,600 - 4,600 = 20,000. + ia_itemized_deductions_pre_limitation: 20_000 + ia_itemized_deductions_reduction: 0 + ia_itemized_deductions_unit: 20_000 + +- name: 2018 limitation applies after the miscellaneous deduction + period: 2018 + input: + filing_status: SEPARATE + adjusted_gross_income: 200_000 + unreimbursed_business_employee_expenses: 10_000 + real_estate_taxes: 6_000 + deductible_mortgage_interest: 9_000 + ia_withheld_income_tax: 0 + state_code: IA + output: + # Schedule A line 24: 10,000 - 2% * 200,000 = 6,000; line 1: 21,000. + # Line 9: 40,000; line 10: 1,200; line 6: 16,800; line 11: 1,200. + # Line 21: 21,000 - 1,200 = 19,800. + ia_itemized_deductions_pre_limitation: 21_000 + ia_itemized_deductions_reduction: 1_200 + ia_itemized_deductions_unit: 19_800 + +- name: 2019 Iowa follows the federal suspension of the limitation + period: 2019 + input: + filing_status: SINGLE + adjusted_gross_income: 1_000_000 + real_estate_taxes: 4_000 + deductible_mortgage_interest: 6_000 + ia_withheld_income_tax: 0 + state_code: IA + output: + # 2019 IA Schedule A line 21 adds the lines with no worksheet. + ia_itemized_deductions_reduction: 0 + ia_itemized_deductions_unit: 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 9494a5b10c4..ccf3b010c3c 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 @@ -95,3 +95,56 @@ deductible_mortgage_insurance_premiums: 2_000 interest_deduction: 12_000 ia_itemized_deductions_unit: 12_000 + +# 2018 IA Schedule A lines 19 to 24: unreimbursed employee expenses and tax +# preparation fees above 2% of federal AGI, which the federal return suspends. +- name: 2018 Iowa keeps the miscellaneous deduction above 2% of federal AGI + period: 2018 + input: + unreimbursed_business_employee_expenses: 3_000 + tax_preparation_fees: 500 + adjusted_gross_income: 50_000 + real_estate_taxes: 2_000 + state_code: IA + output: + misc_deduction: 0 + # 2,000 + (3,500 - 2% * 50,000) + ia_itemized_deductions_unit: 4_500 + +- name: 2019 Iowa follows the federal suspension of the miscellaneous deduction + period: 2019 + input: + unreimbursed_business_employee_expenses: 3_000 + tax_preparation_fees: 500 + adjusted_gross_income: 50_000 + real_estate_taxes: 2_000 + state_code: IA + output: + ia_itemized_deductions_unit: 2_000 + +- name: 2018 Iowa miscellaneous deduction is zero when 2% of federal AGI exceeds the expenses + period: 2018 + input: + filing_status: SINGLE + unreimbursed_business_employee_expenses: 3_000 + tax_preparation_fees: 500 + adjusted_gross_income: 200_000 + real_estate_taxes: 2_000 + state_code: IA + output: + # 2018 IA Schedule A lines 22 to 24: 3,500 - 2% * 200,000 = -500, so 0. + # Federal AGI is below the $266,700 single amount on the IA 104, line 8. + ia_itemized_deductions_unit: 2_000 + +- name: 2017 Iowa takes the federal miscellaneous deduction once + period: 2017 + input: + unreimbursed_business_employee_expenses: 3_000 + tax_preparation_fees: 500 + adjusted_gross_income: 50_000 + real_estate_taxes: 2_000 + state_code: IA + output: + # The federal deduction still applies in 2017: 3,500 - 2% * 50,000. + misc_deduction: 2_500 + ia_itemized_deductions_unit: 4_500 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ia/tax/income/test_ia_itemized_deductions_reduction_invariants.py b/policyengine_us/tests/policy/baseline/gov/states/ia/tax/income/test_ia_itemized_deductions_reduction_invariants.py new file mode 100644 index 00000000000..53a9372ea5d --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ia/tax/income/test_ia_itemized_deductions_reduction_invariants.py @@ -0,0 +1,219 @@ +"""Invariants of Iowa's 2018 limitation on itemized deductions. + +The 2018 IA 104 worksheet limits the 2018 IA Schedule A total of a filer whose +federal adjusted gross income exceeds the applicable amount for the federal +filing status (line 8): +https://revenue.iowa.gov/media/2335/download?inline=#page=1 +Line 11 is the smaller of 80% of the deductions subject to the limitation +(line 6) and 3% of the excess income (line 10). Lines 12 to 21 then remove +the Iowa income tax on federal Schedule A line 5a in proportion, as +701 IAC 41.11(1) does, at a share that line 18 enters to the nearest tenth of +a percent: +https://www.legis.iowa.gov/docs/iac/chapter/03-28-2018.701.41.pdf#page=10 + +So each bound below allows ROUNDING of the Iowa income tax on line 2, the most +that line 18's rounding moves line 21 from the exact share. + +Hypothesis draws deduction inputs, Iowa income tax, sales tax and federal AGI +relative to the applicable amount, so every filing status gets limited cases. +Each draw becomes one simulation with four tax units per case: the drawn +case; the case without miscellaneous expenses ("fixed"), whose Schedule A +total does not depend on AGI; "fixed" with higher AGI; and "fixed" with more +Iowa income tax. For every tax unit: + +1. Accounting: ia_itemized_deductions_unit is the pre-limitation total minus + the reduction, because Schedule A line 26 takes IA 104 line 21. +2. Never raises: apart from rounding, the reduction is never negative, so + the limitation never raises the deduction, because line 11 is the smaller + of two nonnegative amounts and the exact share is at most 100%. +3. Bounded: apart from rounding, the reduction is at most 80% of the + deductions other than medical expenses and investment interest, because + line 6 caps line 11 at 80% of line 5 and Schedule A bears only its line-1 + share of line 11. +4. Threshold: no reduction when federal AGI is at most the applicable + amount, because line 9 then stops the worksheet. +5. Monotone in AGI: with the deductions fixed, higher AGI never lowers the + reduction, because line 9 grows with line 7. +6. Monotone in Iowa income tax: more Iowa income tax never raises the + reduction, because lines 15 to 20 give the Iowa income tax its share of + line 11. +7. Worksheet: the reduction stays within rounding of line 11 times the + Schedule A share, (line 1 - line 4) / line 5, with line 1 and AGI taken + from the model and lines 2 to 11 restated here from the draw. + +Comparisons allow ten cents: float32 spacing is under seven cents below +$1 million. +""" + +import hypothesis +import hypothesis.strategies as st +import numpy as np +import pytest + +from policyengine_us import Simulation + +YEAR = 2018 +# 2018 IA 104 line 8, by federal filing status. +APPLICABLE_AMOUNT = { + "SINGLE": 266_700, + "JOINT": 320_000, + "SEPARATE": 160_000, + "HEAD_OF_HOUSEHOLD": 293_350, + "SURVIVING_SPOUSE": 320_000, +} +# 2018 IA 104 line 6. +CAP_RATE = 0.8 +# 2018 IA 104 line 10. +AGI_RATE = 0.03 +# 2018 IA 104 line 18 rounds to a tenth of a percent, half of which is 0.05%. +ROUNDING = 0.0005 +TOLERANCE = 0.1 +VARIANTS = ("drawn", "fixed", "higher_agi", "more_iowa_tax") + +amount = st.integers(min_value=0, max_value=60_000) +case_strategy = st.fixed_dictionaries( + { + "filing_status": st.sampled_from(sorted(APPLICABLE_AMOUNT)), + "agi_offset": st.integers(min_value=-50_000, max_value=1_000_000), + "agi_increase": st.integers(min_value=0, max_value=500_000), + "real_estate_taxes": amount, + "mortgage_interest": amount, + "charity": amount, + "medical": amount, + "investment_interest": amount, + "misc_expenses": amount, + "iowa_income_tax": st.integers(min_value=0, max_value=120_000), + "iowa_income_tax_increase": st.integers(min_value=0, max_value=60_000), + "sales_tax": amount, + } +) + + +def base_agi(case): + return max(0, APPLICABLE_AMOUNT[case["filing_status"]] + case["agi_offset"]) + + +def line_2(case, variant): + """Iowa income tax on federal Schedule A line 5a, as the draw sets it.""" + iowa_income_tax = case["iowa_income_tax"] + if variant == "more_iowa_tax": + iowa_income_tax += case["iowa_income_tax_increase"] + return iowa_income_tax if iowa_income_tax >= case["sales_tax"] else 0 + + +def build_situation(cases): + people, tax_units, households = {}, {}, {} + for i, case in enumerate(cases): + for variant in VARIANTS: + key = f"{variant}_{i}" + person = f"person_{key}" + misc_expenses = case["misc_expenses"] if variant == "drawn" else 0 + iowa_income_tax = case["iowa_income_tax"] + if variant == "more_iowa_tax": + iowa_income_tax += case["iowa_income_tax_increase"] + agi = base_agi(case) + if variant == "higher_agi": + agi += case["agi_increase"] + people[person] = { + "age": {YEAR: 45}, + "real_estate_taxes": {YEAR: case["real_estate_taxes"]}, + "deductible_mortgage_interest": {YEAR: case["mortgage_interest"]}, + "investment_interest_expense": {YEAR: case["investment_interest"]}, + "unreimbursed_business_employee_expenses": {YEAR: misc_expenses}, + "ia_withheld_income_tax": {YEAR: iowa_income_tax}, + } + tax_units[f"tax_unit_{key}"] = { + "members": [person], + "filing_status": {YEAR: case["filing_status"]}, + "adjusted_gross_income": {YEAR: agi}, + "charitable_deduction": {YEAR: case["charity"]}, + "medical_expense_deduction": {YEAR: case["medical"]}, + "state_sales_tax": {YEAR: case["sales_tax"]}, + "local_sales_tax": {YEAR: 0}, + } + households[f"household_{key}"] = { + "members": [person], + "state_code": {YEAR: "IA"}, + } + return {"people": people, "tax_units": tax_units, "households": households} + + +def calculate(cases): + simulation = Simulation(situation=build_situation(cases)) + shape = (len(cases), len(VARIANTS)) + return { + name: np.asarray(simulation.calculate(name, YEAR)).reshape(shape) + for name in ( + "adjusted_gross_income", + "ia_itemized_deductions_pre_limitation", + "ia_itemized_deductions_reduction", + "ia_itemized_deductions_unit", + ) + } + + +@hypothesis.settings( + max_examples=20, + deadline=None, + derandomize=True, + suppress_health_check=[hypothesis.HealthCheck.too_slow], +) +@hypothesis.given(st.lists(case_strategy, min_size=1, max_size=8)) +def test_ia_itemized_deductions_reduction_invariants(cases): + values = calculate(cases) + agi = values["adjusted_gross_income"] + pre = values["ia_itemized_deductions_pre_limitation"] + reduction = values["ia_itemized_deductions_reduction"] + unit = values["ia_itemized_deductions_unit"] + fixed, higher_agi, more_iowa_tax = ( + VARIANTS.index(variant) for variant in ("fixed", "higher_agi", "more_iowa_tax") + ) + for i, case in enumerate(cases): + excluded = case["medical"] + case["investment_interest"] + applicable_amount = APPLICABLE_AMOUNT[case["filing_status"]] + for j, variant in enumerate(VARIANTS): + context = (variant, case) + # Line 2 and the most that rounding moves the result. + iowa_tax = line_2(case, variant) + rounding = ROUNDING * iowa_tax + # 1. Accounting. + assert unit[i, j] == pytest.approx( + pre[i, j] - reduction[i, j], abs=TOLERANCE + ), context + # 2. Never raises, apart from rounding. + assert reduction[i, j] >= -rounding - TOLERANCE, context + assert unit[i, j] <= pre[i, j] + rounding + TOLERANCE, context + # 3. Bounded by line 6, apart from rounding. + assert ( + reduction[i, j] + <= CAP_RATE * max(0, pre[i, j] - excluded) + rounding + TOLERANCE + ), context + # 4. Threshold. + if agi[i, j] <= applicable_amount: + assert reduction[i, j] == 0, context + # 7. Worksheet: lines 3, 5, 6, 9, 10 and 11, then the exact share. + line_5 = max(0, pre[i, j] + iowa_tax - excluded) + line_11 = min( + CAP_RATE * line_5, AGI_RATE * max(0, agi[i, j] - applicable_amount) + ) + exact = line_11 * max(0, pre[i, j] - excluded) / line_5 if line_5 else 0 + assert reduction[i, j] == pytest.approx(exact, abs=rounding + TOLERANCE), ( + context + ) + # The fixed variants share one Schedule A total. + assert pre[i, higher_agi] == pytest.approx(pre[i, fixed], abs=TOLERANCE), case + assert pre[i, more_iowa_tax] == pytest.approx(pre[i, fixed], abs=TOLERANCE), ( + case + ) + # 5. Monotone in AGI, apart from rounding at each of the two points. + fixed_rounding = ROUNDING * line_2(case, "fixed") + assert ( + reduction[i, higher_agi] + >= reduction[i, fixed] - 2 * fixed_rounding - TOLERANCE + ), case + # 6. Monotone in Iowa income tax, apart from rounding at each point. + more_rounding = ROUNDING * line_2(case, "more_iowa_tax") + assert ( + reduction[i, more_iowa_tax] + <= reduction[i, fixed] + fixed_rounding + more_rounding + TOLERANCE + ), case diff --git a/policyengine_us/variables/gov/states/ia/tax/income/including_married_filing_separately/deductions/ia_itemized_deductions_pre_limitation.py b/policyengine_us/variables/gov/states/ia/tax/income/including_married_filing_separately/deductions/ia_itemized_deductions_pre_limitation.py new file mode 100644 index 00000000000..cdc1f9adb0c --- /dev/null +++ b/policyengine_us/variables/gov/states/ia/tax/income/including_married_filing_separately/deductions/ia_itemized_deductions_pre_limitation.py @@ -0,0 +1,53 @@ +from policyengine_us.model_api import * + + +class ia_itemized_deductions_pre_limitation(Variable): + value_type = float + entity = TaxUnit + label = "Iowa itemized deductions for tax unit before the high-income limitation" + unit = USD + definition_period = YEAR + reference = ( + "https://revenue.iowa.gov/sites/default/files/2022-01/IA1040%2841-001%29.pdf", + "https://revenue.iowa.gov/media/2650/download?inline", + "https://revenue.iowa.gov/sites/default/files/2023-01/2022IA1040%2841001%29.pdf", + "https://revenue.iowa.gov/media/2721/download?inline", + "https://revenue.iowa.gov/taxes/tax-guidance/individual-income-tax/iowa-nonconformity-taxpayer-certainty-and-disaster-tax-relief-act-2019", + # 2018 IA Schedule A, lines 1 to 25. + "https://revenue.iowa.gov/media/2298/download?inline=#page=1", + ) + defined_for = StateCode.IA + + def formula(tax_unit, period, parameters): + """ + FROM THE 2021 INSTRUCTIONS (PAGE 46): + The $10,000 federal cap on the itemized deduction for state and + local taxes does not apply for Iowa purposes. Taxpayers may still + deduct eligible state and local taxes paid, independent of the + federal dollar limitation. + """ + # compute tax unit's itemized deductions + p = parameters(period).gov.states.ia.tax.income.deductions.itemized + if p.applies_federal: + p_fed = parameters(period).gov.irs.deductions + return add(tax_unit, period, p_fed.itemized_deductions) + deductions = add( + tax_unit, + period, + ["itemized_deductions_less_salt", "real_estate_taxes"], + ) + # Iowa did not adopt the retroactive extension of the mortgage + # insurance premium deduction for 2018 and 2019. + if p.disallowed_interest_components: + deductions = deductions - add( + tax_unit, period, p.disallowed_interest_components + ) + if p.misc_deduction_retained: + # 2018: Iowa did not adopt the federal suspension of miscellaneous + # deductions above 2% of federal AGI (Schedule A lines 19 to 24), + # so they are added to the federal amounts, which leave them out. + p_misc = parameters(period).gov.irs.deductions.itemized.misc + expenses = tax_unit("total_misc_deductions", period) + floor = p_misc.floor * tax_unit("positive_agi", period) + deductions = deductions + max_(0, expenses - floor) + return deductions diff --git a/policyengine_us/variables/gov/states/ia/tax/income/including_married_filing_separately/deductions/ia_itemized_deductions_reduction.py b/policyengine_us/variables/gov/states/ia/tax/income/including_married_filing_separately/deductions/ia_itemized_deductions_reduction.py new file mode 100644 index 00000000000..9acd938930f --- /dev/null +++ b/policyengine_us/variables/gov/states/ia/tax/income/including_married_filing_separately/deductions/ia_itemized_deductions_reduction.py @@ -0,0 +1,68 @@ +from policyengine_us.model_api import * + + +class ia_itemized_deductions_reduction(Variable): + value_type = float + entity = TaxUnit + label = "Iowa itemized deductions reduction for high-income filers" + unit = USD + definition_period = YEAR + reference = ( + # 2018 IA 104 Iowa Itemized Deductions Worksheet. + "https://revenue.iowa.gov/media/2335/download?inline=#page=1", + # 701 IAC 41.11 (IAC 2/28/18), PDF pages 9-10. + "https://www.legis.iowa.gov/docs/iac/chapter/03-28-2018.701.41.pdf#page=9", + # Iowa Code 422.9(2) (2018). + "https://www.legis.iowa.gov/docs/code/2018/422.9.pdf#page=1", + ) + defined_for = StateCode.IA + + def formula(tax_unit, period, parameters): + p = parameters(period).gov.states.ia.tax.income.deductions.itemized + if not p.limitation.retained: + return 0 + # IA 104 line 1: Iowa Schedule A lines 1 to 25. + deductions = tax_unit("ia_itemized_deductions_pre_limitation", period) + # IA 104 line 2: Iowa income tax on federal Schedule A line 5a, which + # holds state income taxes only when they exceed sales taxes. + income_taxes = add( + tax_unit, period, ["state_withheld_income_tax", "local_income_tax"] + ) + sales_taxes = add(tax_unit, period, ["state_sales_tax", "local_sales_tax"]) + iowa_income_tax = where( + income_taxes >= sales_taxes, + add(tax_unit, period, ["ia_withheld_income_tax"]), + 0, + ) + # IA 104 line 4: medical expenses, investment interest, and casualty, + # theft and gambling losses, which section 68(c) leaves unlimited. + p_fed = parameters(period).gov.irs.deductions.itemized.limitation + excluded = add(tax_unit, period, p_fed.excluded_deductions) + # IA 104 lines 3 and 5. Lines 3 to 11 run in float64, so float32 error + # can't move line 18 across a half. + reducible = max_(0, deductions + iowa_income_tax - excluded).astype(np.float64) + # IA 104 lines 7 to 9, with the federal filing status. + agi = tax_unit("adjusted_gross_income", period) + filing_status = tax_unit("filing_status", period) + excess_agi = max_( + 0, agi - p.limitation.applicable_amount[filing_status] + ).astype(np.float64) + # IA 104 lines 6, 10 and 11. + reduction = min_( + p.limitation.itemized_deduction_rate * reducible, + p.limitation.agi_rate * excess_agi, + ) + # IA 104 lines 12 to 21 subtract the Iowa income tax in the share of + # line 5 that the reduction leaves. Line 18 enters that share to the + # nearest tenth of a percent; neither Iowa Code 422.9(2) nor 701 IAC + # 41.11(1) says how to round it. So line 1 - line 21 = + # line 11 - line 2 * (1 - line 18), which can fall slightly below 0. + share = np.divide( + reducible - reduction, + reducible, + out=np.ones_like(reducible), + where=reducible > 0, + ) + # A half rounds up; the offset absorbs float64 error. + line_18 = np.floor(share * 1_000 + 0.5 + 1e-9) / 1_000 + return reduction - iowa_income_tax * (1 - line_18) diff --git a/policyengine_us/variables/gov/states/ia/tax/income/including_married_filing_separately/deductions/ia_itemized_deductions_unit.py b/policyengine_us/variables/gov/states/ia/tax/income/including_married_filing_separately/deductions/ia_itemized_deductions_unit.py index f0a8c46af27..43c365ab70f 100644 --- a/policyengine_us/variables/gov/states/ia/tax/income/including_married_filing_separately/deductions/ia_itemized_deductions_unit.py +++ b/policyengine_us/variables/gov/states/ia/tax/income/including_married_filing_separately/deductions/ia_itemized_deductions_unit.py @@ -12,33 +12,9 @@ class ia_itemized_deductions_unit(Variable): "https://revenue.iowa.gov/media/2650/download?inline", "https://revenue.iowa.gov/sites/default/files/2023-01/2022IA1040%2841001%29.pdf", "https://revenue.iowa.gov/media/2721/download?inline", - "https://revenue.iowa.gov/taxes/tax-guidance/individual-income-tax/iowa-nonconformity-taxpayer-certainty-and-disaster-tax-relief-act-2019", + # 2018 IA Schedule A, line 26. + "https://revenue.iowa.gov/media/2298/download?inline=#page=1", ) defined_for = StateCode.IA - - def formula(tax_unit, period, parameters): - """ - FROM THE 2021 INSTRUCTIONS (PAGE 46): - The $10,000 federal cap on the itemized deduction for state and - local taxes does not apply for Iowa purposes. Taxpayers may still - deduct eligible state and local taxes paid, independent of the - federal dollar limitation. - """ - # compute tax unit's itemized deductions - p = parameters(period).gov.states.ia.tax.income.deductions.itemized - if p.applies_federal: - p_fed = parameters(period).gov.irs.deductions - return add(tax_unit, period, p_fed.itemized_deductions) - else: - deductions = add( - tax_unit, - period, - ["itemized_deductions_less_salt", "real_estate_taxes"], - ) - # Iowa did not adopt the retroactive extension of the mortgage - # insurance premium deduction for 2018 and 2019. - if p.disallowed_interest_components: - deductions = deductions - add( - tax_unit, period, p.disallowed_interest_components - ) - return deductions + adds = ["ia_itemized_deductions_pre_limitation"] + subtracts = ["ia_itemized_deductions_reduction"] diff --git a/source_conflicts/candidates.yaml b/source_conflicts/candidates.yaml index c27bd7db7a0..77c7ef56302 100644 --- a/source_conflicts/candidates.yaml +++ b/source_conflicts/candidates.yaml @@ -2555,6 +2555,89 @@ candidates: state: ruled note: Ruled statute-first in October 2026; the rule requires a model change after confirming that the exclusion reaches alternate tax. +- id: us-ia:itemized-limitation-2018-federal-suspension + program: income-tax + years: + from: 2018 + to: 2018 + type: tax + question: Did Iowa limit the 2018 itemized deductions of high-income filers, although section 68(f) suspended the + federal limitation for 2018 and 701 IAC 41.11 ties the Iowa reduction to the years it applies federally? + sources: + - kind: statute + citation: Iowa Code 422.3(5) (2018) + url: https://www.legis.iowa.gov/docs/code/2018/422.3.pdf#page=1 + - kind: statute + citation: Iowa Code 422.9(2) (2018) + url: https://www.legis.iowa.gov/docs/code/2018/422.9.pdf#page=1 + - kind: regulation + citation: 701 IAC 41.11 (IAC 2/28/18) + url: https://www.legis.iowa.gov/docs/iac/chapter/03-28-2018.701.41.pdf#page=9 + - kind: statute + citation: 2018 Iowa Acts, chapter 1161, sections 58, 63, 66 and 69 + url: https://www.legis.iowa.gov/docs/acts/2018/CH1161.pdf#page=14 + - kind: worksheet + citation: 2018 IA 104 Iowa Itemized Deductions Worksheet + url: https://revenue.iowa.gov/media/2335/download?inline=#page=1 + readings: + statute: For 2018 Iowa Code 422.3(5) defines the Internal Revenue Code as the one in effect on January 1, 2015, so + section 68 still limits the deductions Iowa Code 422.9(2) allows. In 2018 Iowa Acts chapter 1161, the legislature + moved that date only from 2019 (section 69). For 2018, it applied named sections of Pub. L. 115-97 (section 63) + and the section 179 allowance as section 13101 amended it (section 58), none of them section 11045 or 11046. The + Iowa Department of Revenue applies the limitation in the 2018 IA 104 and instructions. + other: Rule 41.11 applies the reduction "for the same tax years that the provision applies for federal income tax + purposes"; read against federal law as amended, which suspends section 68 for 2018, Iowa would not limit 2018 + deductions. + example: A joint filer with federal AGI of $400,000, $32,000 of Schedule A deductions ($4,000 of them medical) and + $20,000 of Iowa income tax deducts $30,600 under the IA 104, versus $32,000 with no limitation. + pe_us: + follows: statute + code: + - policyengine_us/parameters/gov/states/ia/tax/income/deductions/itemized/limitation/retained.yaml + prs: + - https://github.com/PolicyEngine/policyengine-us/pull/10101 + state: unreviewed + note: Under Iowa's own definition of the Internal Revenue Code, section 68 applies to 2018, so the rule agrees with + the statute and the form. +- id: us-ia:itemized-limitation-iowa-tax-share-rounding + program: income-tax + years: + from: 2018 + to: 2018 + type: tax + question: Does Iowa subtract the Iowa income tax at its exact share of the limited deductions, or at the IA 104 line + 18 percentage rounded to the nearest tenth of a percent? + sources: + - kind: statute + citation: Iowa Code 422.9(2)(a) (2018) + url: https://www.legis.iowa.gov/docs/code/2018/422.9.pdf#page=1 + - kind: regulation + citation: 701 IAC 41.5(1) and 41.11(1) (IAC 2/28/18) + url: https://www.legis.iowa.gov/docs/iac/chapter/03-28-2018.701.41.pdf#page=10 + - kind: worksheet + citation: 2018 IA 104 Iowa Itemized Deductions Worksheet, line 18 + url: https://revenue.iowa.gov/media/2335/download?inline=#page=1 + readings: + statute: Iowa Code 422.9(2)(a) subtracts "the deduction for Iowa income taxes" and doesn't say how much of it stays + deducted after the overall reduction. Rule 41.11(1) requires high-income filers to complete Form 41-104. The copy + of the form it prints gives the share with no rounding instruction (line 6), and rule 41.5(1) subtracts the tax + "to the extent" included. + other: The IA 104 enters line 15 divided by line 17 "to the nearest tenth of a percent" before applying it to the + Iowa income tax. + example: A joint filer with federal AGI of $400,000, $30,000 of Schedule A deductions ($4,000 of them medical) and + $20,000 of Iowa income tax enters 94.8% and deducts $28,640; the exact share, 94.78%, gives $28,643.48. + pe_us: + follows: gap-fill + code: + - policyengine_us/variables/gov/states/ia/tax/income/including_married_filing_separately/deductions/ia_itemized_deductions_reduction.py + prs: + - https://github.com/PolicyEngine/policyengine-us/pull/10101 + state: unreviewed + note: Rounding moves the result by at most 0.05% of the Iowa income tax, but it can raise line 21 above line 1. A + joint filer at $500,000 with only $20,000 of medical expenses and investment interest and $9,700 of Iowa income + tax enters 44.3% and gets $20,002.90; the exact share gives $20,000. The statute and the rule's copy of the form + leave the rounding open, and the rule requires the form, so the model follows the 2018 form. The form gives no tie + rule, so the model rounds a half up, and a 93.25% share enters as 93.3%. - id: us-ia:low-income-exemption-claimant-income program: income-tax years: