From b21239d1da4334b17341711700cb7d735a7a401d Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Tue, 13 Oct 2026 09:32:50 -0400 Subject: [PATCH 1/4] Use the federal itemized total and deduction type for Montana from 2024 From 2024 Montana taxable income starts from federal taxable income (MCA 15-30-2120(1)). Form 2 line 2 takes the federal itemized deductions (Form 1040 line 12) less the state income tax deducted on Schedule A, only down to the federal standard deduction (MCA 15-30-2120(2)(j); 2024 Worksheet A; 2025 Schedule I Worksheet B), and the return must use the federal deduction type. The model rebuilt Montana's 2024+ itemized deductions from parts, which dropped federal casualty losses, ignored the 2026 section 68 limitation, capped each spouse's real estate taxes separately, and let Montana choose itemizing on its own terms. A federal non-itemizer's 2026 section 170(p) charitable deduction (26 U.S.C. 63(b)(4)) now reaches Montana too. Co-Authored-By: Claude Opus 5.5 --- .../mt-itemized-federal-total.fixed.md | 1 + .../deductions/itemized/misc_deductions.yaml | 2 + .../state_specific_deduction_applies.yaml | 4 +- ..._adjusted_federal_itemized_deductions.yaml | 243 ++++++++++++++++++ .../tax/income/mt_taxable_income_joint.yaml | 113 +++++++- ...eductions_for_federal_itemization_joint.py | 13 +- ...mt_adjusted_federal_itemized_deductions.py | 56 ++++ .../general/mt_itemized_deductions_joint.py | 13 +- .../mt_taxable_income_joint.py | 26 +- 9 files changed, 465 insertions(+), 6 deletions(-) create mode 100644 changelog.d/mt-itemized-federal-total.fixed.md create mode 100644 policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/deductions/itemized/mt_adjusted_federal_itemized_deductions.yaml create mode 100644 policyengine_us/variables/gov/states/mt/tax/income/deductions/itemized/general/mt_adjusted_federal_itemized_deductions.py diff --git a/changelog.d/mt-itemized-federal-total.fixed.md b/changelog.d/mt-itemized-federal-total.fixed.md new file mode 100644 index 00000000000..2dceb7aadc0 --- /dev/null +++ b/changelog.d/mt-itemized-federal-total.fixed.md @@ -0,0 +1 @@ +From 2024 Montana takes the federal itemized deductions (after the section 68 limitation, and including casualty losses) less the state income tax down to the federal standard deduction, uses the federal return's deduction type, and from 2026 gives standard deduction filers the federal charitable deduction for non-itemizers. diff --git a/policyengine_us/parameters/gov/states/mt/tax/income/deductions/itemized/misc_deductions.yaml b/policyengine_us/parameters/gov/states/mt/tax/income/deductions/itemized/misc_deductions.yaml index c4744269768..ff1dc55a8eb 100644 --- a/policyengine_us/parameters/gov/states/mt/tax/income/deductions/itemized/misc_deductions.yaml +++ b/policyengine_us/parameters/gov/states/mt/tax/income/deductions/itemized/misc_deductions.yaml @@ -6,6 +6,8 @@ description: Montana counts these sources as miscellaneous deductions. values: 2021-01-01: - casualty_loss_deduction + # From 2024 the federal itemized deductions replace the Itemized Deductions + # Schedule (mt_adjusted_federal_itemized_deductions). 2024-01-01: [] metadata: diff --git a/policyengine_us/parameters/gov/states/mt/tax/income/deductions/itemized/state_specific_deduction_applies.yaml b/policyengine_us/parameters/gov/states/mt/tax/income/deductions/itemized/state_specific_deduction_applies.yaml index 8b6d63360ab..3b5d690a526 100644 --- a/policyengine_us/parameters/gov/states/mt/tax/income/deductions/itemized/state_specific_deduction_applies.yaml +++ b/policyengine_us/parameters/gov/states/mt/tax/income/deductions/itemized/state_specific_deduction_applies.yaml @@ -1,4 +1,4 @@ -description: Montana computes itemized deductions on its own Itemized Deductions Schedule, in lieu of adjusted federal itemized deductions, if this is true. +description: Montana computes itemized deductions on its own Itemized Deductions Schedule and lets filers take the larger of those and the standard deduction, in lieu of following the federal return's deduction type and adjusted federal itemized deductions, if this is true. metadata: label: Montana state specific itemized deductions apply period: year @@ -10,6 +10,8 @@ metadata: href: https://revenuefiles.mt.gov/files/Forms/Montana-Individual-Income-Tax-Return-Form-2-Instructions/2024_Montana_Individual_Income_Tax_Return_Form_2_Instructions.pdf#page=8 - title: 2024 Montana Form 2 Instructions | Line 2 and Worksheet A, Adjusted Federal Itemized Deductions href: https://revenuefiles.mt.gov/files/Forms/Montana-Individual-Income-Tax-Return-Form-2-Instructions/2024_Montana_Individual_Income_Tax_Return_Form_2_Instructions.pdf#page=15 + - title: MCA 15-30-2120(1) and (2)(j) | Adjustments to federal taxable income + href: https://mca.legmt.gov/bills/mca/title_0150/chapter_0300/part_0210/section_0200/0150-0300-0210-0200.html values: 2021-01-01: true diff --git a/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/deductions/itemized/mt_adjusted_federal_itemized_deductions.yaml b/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/deductions/itemized/mt_adjusted_federal_itemized_deductions.yaml new file mode 100644 index 00000000000..e12331c2928 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/deductions/itemized/mt_adjusted_federal_itemized_deductions.yaml @@ -0,0 +1,243 @@ +# From 2024 Montana taxable income starts from federal taxable income (MCA +# 15-30-2120(1)). Form 2 line 2 takes the federal itemized deductions (2024 +# Worksheet A line 1: "Enter your total federal itemized deductions from Form +# 1040, line 12") and removes the state income tax "only to the extent that it +# does not reduce your deduction below your federal standard deduction". MCA +# 15-30-2120(2)(j) adds back the state income tax deduction claimed, "not to +# exceed the amount required to reduce the federal itemized amount ... to the +# amount of the federal standard deduction". The 2025 Schedule I Worksheet B +# finds the state income tax within a capped SALT deduction as line 5e less +# local income, real estate and personal property taxes. +# +# The 2024 federal standard deduction is $14,600 single and $29,200 joint; the +# SALT cap is $10,000. + +- name: Case 1, state income tax is removed only down to the federal standard deduction. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + employment_income: 100_000 + real_estate_taxes: 4_000 + deductible_mortgage_interest: 9_000 + tax_units: + tax_unit: + members: [person1] + state_withheld_income_tax: 3_000 + households: + household: + members: [person1] + state_code: MT + output: + # SALT: min(10,000, 3,000 + 4,000) = 7,000. + salt_deduction: 7_000 + # Worksheet A line 1: 9,000 + 7,000 = 16,000. + itemized_taxable_income_deductions: 16_000 + # Line 3: 16,000 - 3,000 = 13,000. Line 5: greater of 13,000 and the + # 14,600 standard deduction. + mt_adjusted_federal_itemized_deductions: 14_600 + mt_itemized_deductions_joint: 14_600 + +- name: Case 2, all of the state income tax is removed when the total stays above the standard deduction. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + employment_income: 100_000 + real_estate_taxes: 4_000 + deductible_mortgage_interest: 15_000 + tax_units: + tax_unit: + members: [person1] + state_withheld_income_tax: 3_000 + households: + household: + members: [person1] + state_code: MT + output: + itemized_taxable_income_deductions: 22_000 + # 22,000 - 3,000 = 19,000, above 14,600. + mt_adjusted_federal_itemized_deductions: 19_000 + +- name: Case 3, under the SALT cap only the state income tax left after real estate taxes is removed. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + employment_income: 100_000 + real_estate_taxes: 8_000 + deductible_mortgage_interest: 15_000 + tax_units: + tax_unit: + members: [person1] + state_withheld_income_tax: 5_000 + households: + household: + members: [person1] + state_code: MT + output: + # SALT: min(10,000, 5,000 + 8,000) = 10,000. + salt_deduction: 10_000 + itemized_taxable_income_deductions: 25_000 + # Worksheet B line 5: 10,000 - 8,000 = 2,000 of state income tax. + # 25,000 - 2,000 = 23,000. + mt_adjusted_federal_itemized_deductions: 23_000 + +- name: Case 4, no state income tax is removed when real estate taxes fill the SALT cap. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + employment_income: 100_000 + real_estate_taxes: 12_000 + deductible_mortgage_interest: 15_000 + tax_units: + tax_unit: + members: [person1] + state_withheld_income_tax: 5_000 + households: + household: + members: [person1] + state_code: MT + output: + salt_deduction: 10_000 + # Worksheet B line 5: 10,000 - 12,000 is below zero, so 0. + mt_adjusted_federal_itemized_deductions: 25_000 + +- name: Case 5, a filer who deducts sales tax instead of income tax keeps the whole federal total. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + employment_income: 100_000 + real_estate_taxes: 4_000 + deductible_mortgage_interest: 15_000 + tax_units: + tax_unit: + members: [person1] + state_withheld_income_tax: 1_000 + state_sales_tax: 4_000 + local_sales_tax: 0 + households: + household: + members: [person1] + state_code: MT + output: + # Schedule A line 5a takes the 4,000 of sales tax over the 1,000 of + # income tax: SALT = 4,000 + 4,000 = 8,000. No state income tax is + # deducted, so MCA 15-30-2120(2)(j) adds nothing back. + salt_deduction: 8_000 + itemized_taxable_income_deductions: 23_000 + mt_adjusted_federal_itemized_deductions: 23_000 + +- name: Case 6, a joint return's real estate taxes share one SALT cap. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 45 + employment_income: 60_000 + real_estate_taxes: 6_000 + deductible_mortgage_interest: 25_000 + person2: + age: 44 + employment_income: 40_000 + real_estate_taxes: 6_000 + tax_units: + tax_unit: + members: [person1, person2] + state_withheld_income_tax: 4_000 + households: + household: + members: [person1, person2] + state_code: MT + output: + # SALT: min(10,000, 4,000 + 12,000) = 10,000, with no state income tax + # left after the 12,000 of real estate taxes. + salt_deduction: 10_000 + itemized_taxable_income_deductions: 35_000 + mt_adjusted_federal_itemized_deductions: 35_000 + # Only the head carries the return's amount. + mt_itemized_deductions_joint: [35_000, 0] + mt_itemized_deductions_for_federal_itemization_joint: [35_000, 0] + +- name: Case 7, a federal casualty loss reaches Montana once per return. + period: 2024 + absolute_error_margin: 0.01 + input: + # The federal deduction for personal casualty losses outside federally + # declared disasters is suspended from 2018; switch it on to show the + # federal amount passes through. + gov.irs.deductions.itemized.casualty.active: true + people: + person1: + age: 45 + employment_income: 60_000 + casualty_loss: 30_000 + deductible_mortgage_interest: 15_000 + person2: + age: 44 + employment_income: 40_000 + person3: + age: 10 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [person1, person2, person3] + state_withheld_income_tax: 0 + households: + household: + members: [person1, person2, person3] + state_code: MT + output: + # 30,000 - 10% x 100,000 of AGI = 20,000. + casualty_loss_deduction: 20_000 + # 15,000 + 20,000 = 35,000. + itemized_taxable_income_deductions: 35_000 + mt_adjusted_federal_itemized_deductions: 35_000 + mt_itemized_deductions_joint: [35_000, 0, 0] + mt_itemized_deductions: 35_000 + +- name: Case 8, from 2026 Montana takes the federal itemized deductions after the section 68 limitation. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + employment_income: 1_000_000 + real_estate_taxes: 5_000 + deductible_mortgage_interest: 20_000 + tax_units: + tax_unit: + members: [person1] + state_withheld_income_tax: 30_000 + charitable_deduction: 40_000 + households: + household: + members: [person1] + state_code: MT + output: + # The SALT cap phases down to the 10,000 floor at 1,000,000 of income. + salt_deduction: 10_000 + # 40,000 + 20,000 + 10,000 = 70,000 before the limitation. + total_itemized_taxable_income_deductions: 70_000 + # Section 68 as amended by P.L. 119-21: 2/37 of the lesser of 70,000 and + # taxable income over the 640,600 start of the 37% bracket (1,000,000 - + # 640,600 = 359,400), using the 0.05405405 rate: 3,783.78. + itemized_taxable_income_deductions_reduction: 3_783.78 + itemized_taxable_income_deductions: 66_216.22 + # State income tax within SALT: 10,000 - 5,000 = 5,000, below 66,216.22 - + # 16,100. 66,216.22 - 5,000 = 61,216.22. + mt_adjusted_federal_itemized_deductions: 61_216.22 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/mt_taxable_income_joint.yaml b/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/mt_taxable_income_joint.yaml index 49a6ab57ab3..ed697e19b45 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/mt_taxable_income_joint.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/mt_taxable_income_joint.yaml @@ -155,6 +155,8 @@ tax_unit: members: [person1, person2] filing_status: JOINT + # From 2024 Montana uses the federal return's deduction type. + tax_unit_itemizes: true households: household: members: [person1, person2] @@ -164,7 +166,7 @@ mt_old_age_subtraction: [5_500, 5_500] # Federal AGI: 30,000 + 25,000 = 55,000 # Total AGI after subtractions: 55,000 - 11,000 = 44,000 - # Deductions: max(5,500, 7,000) = 7,000 + # Deductions: itemized, as on the federal return: 7,000 # Exemptions: 1,400 + 1,400 = 2,800 # Taxable income: 44,000 - 7,000 - 2,800 = 34,200 (allocated to head) mt_taxable_income_joint: [34_200, 0] @@ -276,3 +278,112 @@ # Exemptions: 1,000 + 1,000 = 2,000 # Taxable income: 39,500 - 6,000 - 2,000 = 31,500 mt_taxable_income_joint: [31_500, 0] + +# 2024 Form 2 instructions, line 2: "You must use the same type of deduction +# taken on your federal return to determine Montana taxable income." Montana +# taxable income starts from federal taxable income (MCA 15-30-2120(1)). +- name: From 2024 a federal standard deduction filer takes the standard deduction even if itemizing would give more. + period: 2024 + input: + people: + person1: + age: 45 + adjusted_gross_income_person: 80_000 + mt_standard_deduction_joint: 29_200 + mt_itemized_deductions_joint: 35_000 + person2: + age: 44 + mt_standard_deduction_joint: 0 + mt_itemized_deductions_joint: 0 + tax_units: + tax_unit: + members: [person1, person2] + tax_unit_itemizes: false + households: + household: + members: [person1, person2] + state_code: MT + output: + # 80,000 - 29,200 = 50,800. + mt_taxable_income_joint: [50_800, 0] + +- name: From 2024 a federal itemizer takes the itemized deductions even below the standard deduction. + period: 2024 + input: + people: + person1: + age: 45 + adjusted_gross_income_person: 80_000 + mt_standard_deduction_joint: 29_200 + mt_itemized_deductions_joint: 25_000 + person2: + age: 44 + mt_standard_deduction_joint: 0 + mt_itemized_deductions_joint: 0 + tax_units: + tax_unit: + members: [person1, person2] + tax_unit_itemizes: true + households: + household: + members: [person1, person2] + state_code: MT + output: + # 80,000 - 25,000 = 55,000. + mt_taxable_income_joint: [55_000, 0] + +- name: Through 2023 Montana filers take the larger deduction whatever the federal return did. + period: 2023 + input: + people: + person1: + age: 45 + adjusted_gross_income_person: 80_000 + mt_standard_deduction_joint: 10_000 + mt_itemized_deductions_joint: 25_000 + mt_personal_exemptions_joint: 0 + person2: + age: 44 + mt_standard_deduction_joint: 0 + mt_itemized_deductions_joint: 0 + mt_personal_exemptions_joint: 0 + tax_units: + tax_unit: + members: [person1, person2] + tax_unit_itemizes: false + households: + household: + members: [person1, person2] + state_code: MT + output: + # 80,000 - max(10,000, 25,000) = 55,000. + mt_taxable_income_joint: [55_000, 0] + +# 26 U.S.C. 63(b)(4): a filer who does not itemize also deducts the section +# 170(p) charitable contributions, up to $2,000 on a joint return from 2026. +- name: From 2026 a Montana standard deduction filer also takes the federal charitable deduction for non-itemizers. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 45 + employment_income: 50_000 + charitable_cash_donations: 3_000 + person2: + age: 44 + employment_income: 30_000 + tax_units: + tax_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: MT + output: + tax_unit_itemizes: false + standard_deduction: 32_200 + # min(2,000, 3,000 of cash gifts) = 2,000. + charitable_deduction_for_non_itemizers: 2_000 + # 80,000 - 32,200 - 2,000 = 45,800. + mt_taxable_income_joint: [45_800, 0] diff --git a/policyengine_us/variables/gov/states/mt/tax/income/deductions/itemized/federal_itemization/mt_itemized_deductions_for_federal_itemization_joint.py b/policyengine_us/variables/gov/states/mt/tax/income/deductions/itemized/federal_itemization/mt_itemized_deductions_for_federal_itemization_joint.py index ba977871a6f..0cda204173c 100644 --- a/policyengine_us/variables/gov/states/mt/tax/income/deductions/itemized/federal_itemization/mt_itemized_deductions_for_federal_itemization_joint.py +++ b/policyengine_us/variables/gov/states/mt/tax/income/deductions/itemized/federal_itemization/mt_itemized_deductions_for_federal_itemization_joint.py @@ -17,10 +17,22 @@ class mt_itemized_deductions_for_federal_itemization_joint(Variable): # 2023 Form 2 instructions, itemized deductions line 9: home mortgage # interest "allowed by federal law", limited to the first $750,000 of # acquisition debt incurred after Dec. 15, 2017 + "https://revenuefiles.mt.gov/files/Forms/Montana-Individual-Income-Tax-Return-Form-2-Instructions/2024_Montana_Individual_Income_Tax_Return_Form_2_Instructions.pdf#page=15", + # 2024 Form 2 instructions, line 2 and Worksheet A: adjusted federal + # itemized deductions ) defined_for = StateCode.MT def formula(person, period, parameters): + is_head = person("is_tax_unit_head", period) + p = parameters(period).gov.states.mt.tax.income.deductions.itemized + if not p.state_specific_deduction_applies: + # From 2024 there is no federal income tax deduction, so this + # equals mt_itemized_deductions_joint. + adjusted_federal_itemized_deductions = person.tax_unit( + "mt_adjusted_federal_itemized_deductions", period + ) + return is_head * adjusted_federal_itemized_deductions charitable_deduction = person.tax_unit("charitable_deduction", period) investment_interest = person("investment_interest_expense", period) # Montana allows the mortgage interest federal law allows, so interest @@ -39,5 +51,4 @@ def formula(person, period, parameters): "mt_federal_income_tax_deduction_for_federal_itemization", ], ) - is_head = person("is_tax_unit_head", period) return is_head * (interest_ded + other_deductions + charitable_deduction) diff --git a/policyengine_us/variables/gov/states/mt/tax/income/deductions/itemized/general/mt_adjusted_federal_itemized_deductions.py b/policyengine_us/variables/gov/states/mt/tax/income/deductions/itemized/general/mt_adjusted_federal_itemized_deductions.py new file mode 100644 index 00000000000..3c98f3d41fc --- /dev/null +++ b/policyengine_us/variables/gov/states/mt/tax/income/deductions/itemized/general/mt_adjusted_federal_itemized_deductions.py @@ -0,0 +1,56 @@ +from policyengine_us.model_api import * + + +class mt_adjusted_federal_itemized_deductions(Variable): + value_type = float + entity = TaxUnit + label = "Montana adjusted federal itemized deductions" + unit = USD + definition_period = YEAR + documentation = ( + "The federal itemized deductions (Form 1040, line 12) less the state " + "income tax deducted on Schedule A, with the state income tax removed " + "only down to the federal standard deduction. Montana uses this " + "amount from 2024, when its taxable income starts from federal " + "taxable income." + ) + reference = ( + # MCA 15-30-2120(1) and (2)(j) + "https://mca.legmt.gov/bills/mca/title_0150/chapter_0300/part_0210/section_0200/0150-0300-0210-0200.html", + # 2024 Form 2 instructions, line 2 and Worksheet A + "https://revenuefiles.mt.gov/files/Forms/Montana-Individual-Income-Tax-Return-Form-2-Instructions/2024_Montana_Individual_Income_Tax_Return_Form_2_Instructions.pdf#page=15", + # 2025 Form 2 instructions, line 2, and Schedule I line 4 Worksheet B + "https://revenuefiles.mt.gov/files/Forms/Montana-Individual-Income-Tax-Return-Form-2-Instructions/2025_Montana_Individual_Income_Tax_Return_Form_2_Instructions.pdf#page=13", + "https://revenuefiles.mt.gov/files/Forms/Montana-Individual-Income-Tax-Return-Form-2-Instructions/2025_Montana_Individual_Income_Tax_Return_Form_2_Instructions.pdf#page=22", + ) + defined_for = StateCode.MT + + def formula(tax_unit, period, parameters): + # Form 1040, line 12: Schedule A, line 17, after any section 68 + # limitation. + itemized_deductions = tax_unit("itemized_taxable_income_deductions", period) + # Worksheet B, lines 1-5: the state income tax within the capped SALT + # deduction, after the local income tax and real estate taxes. Schedule + # A line 5a holds the larger of income taxes and general sales taxes + # (state_and_local_sales_or_income_tax); a filer who deducts sales tax + # deducts no state income tax. + state_income_tax = tax_unit("state_withheld_income_tax", period) + local_income_tax = tax_unit("local_income_tax", period) + sales_tax = add(tax_unit, period, ["state_sales_tax", "local_sales_tax"]) + deducts_income_tax = state_income_tax + local_income_tax >= sales_tax + real_estate_taxes = add(tax_unit, period, ["real_estate_taxes"]) + salt_deduction = tax_unit("salt_deduction", period) + state_income_tax_deducted = where( + deducts_income_tax, + max_(salt_deduction - local_income_tax - real_estate_taxes, 0), + 0, + ) + # MCA 15-30-2120(2)(j): the state income tax is added back, but not + # beyond the amount that brings the itemized deductions down to the + # federal standard deduction (Worksheet B, lines 6-9). + standard_deduction = tax_unit("standard_deduction", period) + itemized_over_standard = max_(itemized_deductions - standard_deduction, 0) + state_income_tax_addition = min_( + state_income_tax_deducted, itemized_over_standard + ) + return itemized_deductions - state_income_tax_addition diff --git a/policyengine_us/variables/gov/states/mt/tax/income/deductions/itemized/general/mt_itemized_deductions_joint.py b/policyengine_us/variables/gov/states/mt/tax/income/deductions/itemized/general/mt_itemized_deductions_joint.py index 5f503e42a9b..c1add2f9e5d 100644 --- a/policyengine_us/variables/gov/states/mt/tax/income/deductions/itemized/general/mt_itemized_deductions_joint.py +++ b/policyengine_us/variables/gov/states/mt/tax/income/deductions/itemized/general/mt_itemized_deductions_joint.py @@ -17,10 +17,22 @@ class mt_itemized_deductions_joint(Variable): # 2023 Form 2 instructions, itemized deductions line 9: home mortgage # interest "allowed by federal law", limited to the first $750,000 of # acquisition debt incurred after Dec. 15, 2017 + "https://revenuefiles.mt.gov/files/Forms/Montana-Individual-Income-Tax-Return-Form-2-Instructions/2024_Montana_Individual_Income_Tax_Return_Form_2_Instructions.pdf#page=15", + # 2024 Form 2 instructions, line 2 and Worksheet A: adjusted federal + # itemized deductions ) defined_for = StateCode.MT def formula(person, period, parameters): + is_head = person("is_tax_unit_head", period) + p = parameters(period).gov.states.mt.tax.income.deductions.itemized + if not p.state_specific_deduction_applies: + # From 2024 Form 2 line 2 takes the federal itemized deductions, + # less the state income tax (Worksheet A). + adjusted_federal_itemized_deductions = person.tax_unit( + "mt_adjusted_federal_itemized_deductions", period + ) + return is_head * adjusted_federal_itemized_deductions charitable_deduction = person.tax_unit("charitable_deduction", period) investment_interest = person("investment_interest_expense", period) # Montana allows the mortgage interest federal law allows, so interest @@ -39,5 +51,4 @@ def formula(person, period, parameters): "mt_federal_income_tax_deduction_unit", ], ) - is_head = person("is_tax_unit_head", period) return is_head * (interest_ded + other_deductions + charitable_deduction) diff --git a/policyengine_us/variables/gov/states/mt/tax/income/tax_calculation/mt_taxable_income_joint.py b/policyengine_us/variables/gov/states/mt/tax/income/tax_calculation/mt_taxable_income_joint.py index 11cc56bc887..733c8dbe834 100644 --- a/policyengine_us/variables/gov/states/mt/tax/income/tax_calculation/mt_taxable_income_joint.py +++ b/policyengine_us/variables/gov/states/mt/tax/income/tax_calculation/mt_taxable_income_joint.py @@ -11,6 +11,14 @@ class mt_taxable_income_joint(Variable): reference = ( "https://revenuefiles.mt.gov/files/Forms/Montana-Individual-Income-Tax-Return-Form-2/2022_Montana_Individual_Income_Tax_Return_Form_2.pdf#page=1", "https://revenuefiles.mt.gov/files/Forms/Montana-Individual-Income-Tax-Return-Form-2-Instructions/2022_Montana_Individual_Income_Tax_Return_Form_2_Instructions.pdf#page=16", + # MCA 15-30-2120(1): from 2024 Montana starts from federal taxable income + "https://mca.legmt.gov/bills/mca/title_0150/chapter_0300/part_0210/section_0200/0150-0300-0210-0200.html", + # 2024 Form 2 instructions, line 2: "You must use the same type of + # deduction taken on your federal return" + "https://revenuefiles.mt.gov/files/Forms/Montana-Individual-Income-Tax-Return-Form-2-Instructions/2024_Montana_Individual_Income_Tax_Return_Form_2_Instructions.pdf#page=15", + # 26 U.S.C. 63(b)(4): a non-itemizer also deducts the section 170(p) + # charitable contributions + "https://www.law.cornell.edu/uscode/text/26/63#b_4", ) def formula(person, period, parameters): @@ -27,8 +35,22 @@ def formula(person, period, parameters): itemized_deductions = add( person.tax_unit, period, ["mt_itemized_deductions_joint"] ) - # Tax units can claim the larger of the itemized or standard deductions - deductions = max_(itemized_deductions, standard_deduction) + p = parameters(period).gov.states.mt.tax.income.deductions.itemized + if p.state_specific_deduction_applies: + # Tax units can claim the larger of the itemized or standard deductions + deductions = max_(itemized_deductions, standard_deduction) + else: + # Federal taxable income carries the deduction type of the federal + # return, and a federal non-itemizer's section 170(p) deduction. + itemizes = person.tax_unit("tax_unit_itemizes", period) + non_itemizer_charitable_deduction = person.tax_unit( + "charitable_deduction_for_non_itemizers", period + ) + deductions = where( + itemizes, + itemized_deductions, + standard_deduction + non_itemizer_charitable_deduction, + ) exemptions = add( person.tax_unit, period, From a1430962da716c9a25962f00e2335fa78964e30c Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Tue, 13 Oct 2026 09:46:54 -0400 Subject: [PATCH 2/4] Add property tests for Montana's adjusted federal itemized deductions Checks the statutory add-back against a Worksheet A restatement, bounds, head-only placement, the federal deduction type, and monotonicity in cash gifts, over generated returns for 2024-2026. Co-Authored-By: Claude Opus 5.5 --- ..._federal_itemized_deductions_properties.py | 200 ++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100644 policyengine_us/tests/core/test_mt_adjusted_federal_itemized_deductions_properties.py diff --git a/policyengine_us/tests/core/test_mt_adjusted_federal_itemized_deductions_properties.py b/policyengine_us/tests/core/test_mt_adjusted_federal_itemized_deductions_properties.py new file mode 100644 index 00000000000..9585bee779a --- /dev/null +++ b/policyengine_us/tests/core/test_mt_adjusted_federal_itemized_deductions_properties.py @@ -0,0 +1,200 @@ +"""Montana's deduction from 2024 is the federal one, less state income tax. + +From 2024 Montana taxable income starts from federal taxable income (MCA +15-30-2120(1)), so the return uses the federal deduction type. A federal +itemizer deducts the federal itemized deductions (Form 1040, line 12) less the +state income tax deducted on Schedule A, but MCA 15-30-2120(2)(j) adds the tax +back only down to the federal standard deduction. A federal non-itemizer +deducts the federal standard deduction and, from 2026, the section 170(p) +charitable deduction (26 U.S.C. 63(b)(4)). + +The model computes the add-back in statutory form, itemized - min(tax, +max(itemized - standard, 0)). This test restates it independently in the form +of the 2024 Form 2 Worksheet A, the greater of itemized - tax and the standard +deduction (for a total at or above the standard deduction), with the state +income tax found as the 2025 Schedule I Worksheet B does: the capped SALT +deduction less local income and real estate taxes, and nothing for a filer who +deducts sales tax. Generated returns vary the filing status, dependents, +wages, real estate taxes per spouse, mortgage interest, cash gifts, medical +expenses, casualty losses and the state income and sales taxes. + +Invariants, for every generated return and year: +1. The model equals the Worksheet A restatement (a differential check). +2. min(itemized, standard) <= Montana amount <= itemized. +3. The amount removed is at most the state income tax and the SALT deduction. +4. Only the head carries the joint amount, once per return, whatever the + number of members. +5. Montana taxable income uses the federal deduction type. +6. More cash gifts never lower the Montana amount. +""" + +import numpy as np +import pytest +from hypothesis import given, settings +from hypothesis import strategies as st + +from policyengine_us import Simulation +from policyengine_us.system import system + +YEARS = [2024, 2025, 2026] +# Values are float32; sums near $1,000,000 can differ by a few cents. +TOLERANCE = 0.1 + +dollars = lambda high: st.integers(min_value=0, max_value=high) # noqa: E731 + +returns = st.fixed_dictionaries( + { + "joint": st.booleans(), + "dependents": st.integers(min_value=0, max_value=2), + "head_wages": st.sampled_from([0, 40_000, 120_000, 650_000, 1_200_000]) + | dollars(400_000), + "spouse_wages": dollars(200_000), + "head_real_estate_taxes": dollars(25_000), + "spouse_real_estate_taxes": dollars(25_000), + "mortgage_interest": dollars(60_000), + "cash_gifts": dollars(30_000), + "medical_expenses": dollars(40_000), + "casualty_loss_deduction": st.sampled_from([0, 0, 15_000]), + # None leaves the model's own estimate of state income tax. + "state_income_tax": st.none() | dollars(40_000), + "sales_tax": st.none() | dollars(10_000), + "extra_cash_gifts": st.integers(min_value=1, max_value=20_000), + } +) + + +def _situation(year, specs): + """Two copies of each return; the second gives more cash gifts.""" + people, tax_units, households, marital_units = {}, {}, {}, {} + for i, spec in enumerate(specs): + for copy, extra_gifts in (("base", 0), ("more", spec["extra_cash_gifts"])): + unit = f"u{i}_{copy}" + head, spouse = f"{unit}_head", f"{unit}_spouse" + people[head] = { + "age": {year: 45}, + "employment_income": {year: spec["head_wages"]}, + "real_estate_taxes": {year: spec["head_real_estate_taxes"]}, + "deductible_mortgage_interest": {year: spec["mortgage_interest"]}, + "charitable_cash_donations": {year: spec["cash_gifts"] + extra_gifts}, + "other_medical_expenses": {year: spec["medical_expenses"]}, + } + couple = [head] + if spec["joint"]: + people[spouse] = { + "age": {year: 43}, + "employment_income": {year: spec["spouse_wages"]}, + "real_estate_taxes": {year: spec["spouse_real_estate_taxes"]}, + } + couple.append(spouse) + members = list(couple) + for d in range(spec["dependents"]): + child = f"{unit}_child{d}" + people[child] = { + "age": {year: 10}, + "is_tax_unit_dependent": {year: True}, + } + members.append(child) + marital_units[child] = {"members": [child]} + tax_unit = { + "members": members, + "casualty_loss_deduction": {year: spec["casualty_loss_deduction"]}, + } + if spec["state_income_tax"] is not None: + tax_unit["state_withheld_income_tax"] = {year: spec["state_income_tax"]} + if spec["sales_tax"] is not None: + tax_unit["state_sales_tax"] = {year: spec["sales_tax"]} + tax_unit["local_sales_tax"] = {year: 0} + tax_units[unit] = tax_unit + households[unit] = {"members": members, "state_code": {year: "MT"}} + marital_units[unit] = {"members": couple} + return { + "people": people, + "tax_units": tax_units, + "households": households, + "marital_units": marital_units, + } + + +def _real_estate_taxes(specs): + """Each return's real estate taxes, from the inputs.""" + return np.repeat( + [ + s["head_real_estate_taxes"] + s["joint"] * s["spouse_real_estate_taxes"] + for s in specs + ], + 2, + ) + + +@pytest.mark.parametrize("year", YEARS) +@settings(max_examples=6, deadline=None, derandomize=True) +@given(specs=st.lists(returns, min_size=3, max_size=6)) +def test_montana_deduction_follows_the_federal_return(year, specs): + sim = Simulation(tax_benefit_system=system, situation=_situation(year, specs)) + + def unit(variable): + return np.asarray(sim.calculate(variable, year), dtype=float) + + itemized = unit("itemized_taxable_income_deductions") + standard = unit("standard_deduction") + salt = unit("salt_deduction") + income_tax = unit("state_withheld_income_tax") + local_income_tax = unit("local_income_tax") + sales_tax = unit("state_sales_tax") + unit("local_sales_tax") + montana = unit("mt_adjusted_federal_itemized_deductions") + + # 1. Worksheet B lines 1-5 and Worksheet A lines 1-5. + deducts_sales_tax = sales_tax > income_tax + local_income_tax + state_income_tax_deducted = np.where( + deducts_sales_tax, + 0, + np.maximum(salt - local_income_tax - _real_estate_taxes(specs), 0), + ) + worksheet_a = np.where( + itemized >= standard, + np.maximum(itemized - state_income_tax_deducted, standard), + itemized, + ) + np.testing.assert_allclose(montana, worksheet_a, rtol=0, atol=TOLERANCE) + + # 2. Bounds. + assert np.all(montana <= itemized + TOLERANCE) + assert np.all(montana >= np.minimum(itemized, standard) - TOLERANCE) + + # 3. Only state income tax inside the SALT deduction is removed. + removed = itemized - montana + assert np.all(removed <= income_tax + TOLERANCE) + assert np.all(removed <= salt + TOLERANCE) + + # 4. The head carries the return's amount, once. + joint = np.asarray(sim.calculate("mt_itemized_deductions_joint", year)) + head = np.asarray(sim.calculate("is_tax_unit_head", year)) + assert np.all(joint[~head] == 0) + for variable in ( + "mt_itemized_deductions_joint", + "mt_itemized_deductions_for_federal_itemization_joint", + ): + np.testing.assert_allclose( + np.asarray(sim.calculate(variable, year, map_to="tax_unit")), + montana, + rtol=0, + atol=TOLERANCE, + err_msg=variable, + ) + + # 5. The federal deduction type. + itemizes = unit("tax_unit_itemizes").astype(bool) + non_itemizer_charity = unit("charitable_deduction_for_non_itemizers") + deduction = np.where(itemizes, montana, standard + non_itemizer_charity) + p = system.parameters(year).gov.states.mt.tax.income.exemptions + assert not p.applies + np.testing.assert_allclose( + unit("mt_taxable_income"), + np.maximum(unit("mt_agi") - deduction, 0), + rtol=0, + atol=TOLERANCE, + ) + + # 6. More cash gifts never lower the Montana amount. + base, more = montana[0::2], montana[1::2] + assert np.all(more >= base - TOLERANCE) From d3e59677cd927d2ff942be80f703fa093d39e9fb Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Tue, 13 Oct 2026 10:00:18 -0400 Subject: [PATCH 3/4] Test that a spouse's mortgage interest reaches a 2025 Montana joint return Co-Authored-By: Claude Opus 5.5 --- ..._adjusted_federal_itemized_deductions.yaml | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/deductions/itemized/mt_adjusted_federal_itemized_deductions.yaml b/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/deductions/itemized/mt_adjusted_federal_itemized_deductions.yaml index e12331c2928..e04f7c52129 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/deductions/itemized/mt_adjusted_federal_itemized_deductions.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/deductions/itemized/mt_adjusted_federal_itemized_deductions.yaml @@ -241,3 +241,31 @@ # State income tax within SALT: 10,000 - 5,000 = 5,000, below 66,216.22 - # 16,100. 66,216.22 - 5,000 = 61,216.22. mt_adjusted_federal_itemized_deductions: 61_216.22 + +- name: Case 9, a spouse's mortgage interest reaches a joint Montana return. + period: 2025 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 45 + employment_income: 80_000 + person2: + age: 44 + employment_income: 20_000 + deductible_mortgage_interest: 40_000 + tax_units: + tax_unit: + members: [person1, person2] + state_withheld_income_tax: 0 + households: + household: + members: [person1, person2] + state_code: MT + output: + itemized_taxable_income_deductions: 40_000 + # 40,000 is over the 31,500 joint standard deduction for 2025. + tax_unit_itemizes: true + mt_itemized_deductions_joint: [40_000, 0] + # 100,000 - 40,000. + mt_taxable_income: 60_000 From c1b942f276305d20c4926bf8fbe48eed1d843978 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 20:17:45 +0000 Subject: [PATCH 4/4] Leave Montana's misc_deductions.yaml as main has it The comment this branch added is the file's only change, and open PR #9888 deletes the file, so the comment would only cause a modify/delete conflict there. The variables' own comments already say the federal itemized total replaces the schedule from 2024. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01LwQq6GED1QuDGy4RPfx6fD --- .../mt/tax/income/deductions/itemized/misc_deductions.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/policyengine_us/parameters/gov/states/mt/tax/income/deductions/itemized/misc_deductions.yaml b/policyengine_us/parameters/gov/states/mt/tax/income/deductions/itemized/misc_deductions.yaml index ff1dc55a8eb..c4744269768 100644 --- a/policyengine_us/parameters/gov/states/mt/tax/income/deductions/itemized/misc_deductions.yaml +++ b/policyengine_us/parameters/gov/states/mt/tax/income/deductions/itemized/misc_deductions.yaml @@ -6,8 +6,6 @@ description: Montana counts these sources as miscellaneous deductions. values: 2021-01-01: - casualty_loss_deduction - # From 2024 the federal itemized deductions replace the Itemized Deductions - # Schedule (mt_adjusted_federal_itemized_deductions). 2024-01-01: [] metadata: