diff --git a/changelog.d/az-ptc-line-d-loss-limit.fixed.md b/changelog.d/az-ptc-line-d-loss-limit.fixed.md new file mode 100644 index 00000000000..0a254ec33c0 --- /dev/null +++ b/changelog.d/az-ptc-line-d-loss-limit.fixed.md @@ -0,0 +1 @@ +Limit each Arizona household member's net capital loss to $1,500 in property tax credit household income (Form 140PTC line D), and let household income go negative, using zero only for the credit schedules (line J). diff --git a/changelog.d/fix-az-ptc-capital-gains.fixed.md b/changelog.d/fix-az-ptc-capital-gains.fixed.md new file mode 100644 index 00000000000..29547a2d693 --- /dev/null +++ b/changelog.d/fix-az-ptc-capital-gains.fixed.md @@ -0,0 +1 @@ +Arizona property tax credit household income counts capital gains and qualified dividends once, through federal AGI, instead of adding the federal preferential-rate amount again, and counts dependents' income, which federal AGI leaves out. diff --git a/policyengine_us/parameters/gov/states/az/tax/income/credits/property_tax/capital_loss_limit.yaml b/policyengine_us/parameters/gov/states/az/tax/income/credits/property_tax/capital_loss_limit.yaml new file mode 100644 index 00000000000..4c6e88e13f5 --- /dev/null +++ b/policyengine_us/parameters/gov/states/az/tax/income/credits/property_tax/capital_loss_limit.yaml @@ -0,0 +1,22 @@ +description: Arizona limits each household member's net capital loss to this amount when computing household income for the property tax credit. +values: + 2021-01-01: 1_500 +metadata: + unit: currency-USD + period: year + label: Arizona property tax credit capital loss limit per household member + reference: + - title: A.A.C. R15-2C-502(C)(3). Property Tax Credit + href: https://www.law.cornell.edu/regulations/arizona/Ariz-Admin-Code-SS-R15-2C-502 + - title: ITR 12-1. Income Tax Ruling - Property Tax Credit Household Income, item (7) + href: https://azdor.gov/sites/default/files/2023-03/RULINGS_INDV_2012_itr12-1.pdf#page=2 + - title: 2025 Form 140PTC Instructions, Part 1 line D + href: https://azdor.gov/sites/default/files/document/FORMS_INDIVIDUAL_2025_140PTCi.pdf#page=4 + - title: 2024 Form 140PTC Instructions, Part 1 line D + href: https://azdor.gov/sites/default/files/document/FORMS_INDIVIDUAL_2024_140PTCi.pdf#page=4 + - title: 2023 Form 140PTC Instructions, Part 1 line D + href: https://azdor.gov/sites/default/files/2023-12/FORMS_INDIVIDUAL_2023_140PTCi.pdf#page=4 + - title: 2022 Form 140PTC Instructions, Part 1 line D + href: https://azdor.gov/sites/default/files/2023-03/FORMS_INDIVIDUAL_2022_140PTCi-2D.pdf#page=4 + - title: 2021 Form 140PTC Instructions, Part 1 line D + href: https://azdor.gov/sites/default/files/2023-03/FORMS_INDIVIDUAL_2021_140PTCi-2D.pdf#page=4 diff --git a/policyengine_us/parameters/gov/states/az/tax/income/credits/property_tax/income_sources.yaml b/policyengine_us/parameters/gov/states/az/tax/income/credits/property_tax/income_sources.yaml index 76b29218242..8c819559cb7 100644 --- a/policyengine_us/parameters/gov/states/az/tax/income/credits/property_tax/income_sources.yaml +++ b/policyengine_us/parameters/gov/states/az/tax/income/credits/property_tax/income_sources.yaml @@ -1,16 +1,32 @@ description: Arizona accounts for the following income sources when computing household income for the property tax credit. values: 2021-01-01: - # Item (7): Capital gains - excluded gains not in Federal AGI - - capital_gains_excluded_from_taxable_income # Item (2): All interest income including tax-exempt - tax_exempt_interest_income # Item (9): Pension income - tax-exempt portions not in Federal AGI # (taxable pension is already in Federal AGI via az_property_tax_credit_agi) - tax_exempt_public_pension_income - tax_exempt_private_pension_income - # Federal AGI minus Social Security (per ARS 43-1072(I) exclusion) + # Federal AGI minus Social Security (per ARS 43-1072(I) exclusion). + # Item (7) capital gains and losses (Form 140PTC Part 1 line D) are counted + # here, once, for members who are not dependents: federal AGI's net capital + # gain and capital loss deduction (limited to $3,000 per return) are + # replaced by each member's combined gains and losses, with a net loss + # limited to $1,500 for each member (A.A.C. R15-2C-502(C)(3)). The total can + # be negative; az_property_tax_credit uses zero for the schedules. + # ARS 43-1072(H)(6)(b) also adds "the amount of capital gains excluded from + # adjusted gross income"; the model has no gain excluded from federal AGI + # (such as a section 121 home sale or section 1202 small business stock + # exclusion), so nothing is added for it. Do not add + # capital_gains_excluded_from_taxable_income: it is the part of federal + # taxable income taxed at the capital gains rates (at zero taxable income, + # the adjusted net capital gain), which federal AGI already holds. - az_property_tax_credit_agi + # Federal AGI leaves out dependents' income; Arizona counts every household + # member's income whether or not the member is a dependent + # (A.A.C. R15-2C-502(A)(2), (B)). This includes each dependent's line D + # amount, with the same $1,500 loss limit. + - az_property_tax_credit_dependent_income metadata: unit: list period: year @@ -18,13 +34,27 @@ metadata: reference: - title: ARS 43-1072. Property tax credit - Income definition href: https://www.azleg.gov/ars/43/01072.htm + - title: A.A.C. R15-2C-502. Property Tax Credit + href: https://www.law.cornell.edu/regulations/arizona/Ariz-Admin-Code-SS-R15-2C-502 - title: ITR 12-1. Income Tax Ruling - Property Tax Credit Household Income - href: https://azdor.gov/sites/default/files/2023-03/RULINGS_INDV_2012_itr12-1.pdf + href: https://azdor.gov/sites/default/files/2023-03/RULINGS_INDV_2012_itr12-1.pdf#page=2 + - title: 2025 Form 140PTC Instructions, Part 1 lines A to I + href: https://azdor.gov/sites/default/files/document/FORMS_INDIVIDUAL_2025_140PTCi.pdf#page=4 + - title: 2025 Property Tax Refund (Credit) Claim Form 140PTC, Part 1 + href: https://azdor.gov/sites/default/files/document/FORMS_INDIVIDUAL_2025_140PTC_f.pdf#page=2 + - title: 2024 Form 140PTC Instructions, Part 1 lines A to I + href: https://azdor.gov/sites/default/files/document/FORMS_INDIVIDUAL_2024_140PTCi.pdf#page=4 - title: 2024 Property Tax Refund (Credit) Claim Form 140PTC href: https://azdor.gov/forms/tax-credits-forms/property-tax-refund-credit-claim-form-fillable + - title: 2023 Form 140PTC Instructions, Part 1 lines A to I + href: https://azdor.gov/sites/default/files/2023-12/FORMS_INDIVIDUAL_2023_140PTCi.pdf#page=4 - title: 2023 Property Tax Refund (Credit) Claim Form 140PTC href: https://azdor.gov/forms/tax-credits-forms/property-tax-refund-credit-claim-form-fillable + - title: 2022 Form 140PTC Instructions, Part 1 lines A to I + href: https://azdor.gov/sites/default/files/2023-03/FORMS_INDIVIDUAL_2022_140PTCi-2D.pdf#page=4 - title: 2022 Property Tax Refund (Credit) Claim Form 140PTC href: https://azdor.gov/sites/default/files/2023-03/FORMS_INDIVIDUAL_2022_140PTC.pdf + - title: 2021 Form 140PTC Instructions, Part 1 lines A to I + href: https://azdor.gov/sites/default/files/2023-03/FORMS_INDIVIDUAL_2021_140PTCi-2D.pdf#page=4 - title: 2021 Property Tax Refund (Credit) Claim Form 140PTC href: https://azdor.gov/sites/default/files/2023-03/FORMS_INDIVIDUAL_2021_140PTC.pdf diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/tax/income/az_property_tax_credit_agi.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/tax/income/az_property_tax_credit_agi.yaml index 5016feb4b85..2f920a71272 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/az/tax/income/az_property_tax_credit_agi.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/az/tax/income/az_property_tax_credit_agi.yaml @@ -38,14 +38,16 @@ output: az_property_tax_credit_agi: 25_000 -- name: Capped at zero when Social Security exceeds other income +- name: Negative when losses leave other income below taxable Social Security period: 2024 input: state_code: AZ adjusted_gross_income: 5_000 tax_unit_taxable_social_security: 6_000 output: - az_property_tax_credit_agi: 0 + # $5,000 - $6,000 = -$1,000. Not floored: Form 140PTC line J can be + # negative, and az_property_tax_credit uses zero only for the schedules. + az_property_tax_credit_agi: -1_000 - name: Combined income types with Social Security exclusion period: 2024 @@ -56,3 +58,49 @@ output: # $22,250 - $4,250 = $18,000 az_property_tax_credit_agi: 18_000 + +# A.A.C. R15-2C-502(C)(3): each member's net capital loss is limited to +# $1,500, in place of federal AGI's $3,000 limit per return. +- name: Federal capital loss limit is replaced by the 1,500 member limit + period: 2025 + input: + people: + head: + age: 70 + employment_income: 10_000 + long_term_capital_gains: -5_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: AZ + output: + # Federal AGI: 10,000 - 3,000 = 7,000. + adjusted_gross_income: 7_000 + # Form 140PTC line A 10,000 + line D (1,500) = 8,500. + az_property_tax_credit_agi: 8_500 + +- name: A dependent's capital loss is left to dependent income + period: 2025 + input: + people: + head: + age: 70 + employment_income: 3_000 + child: + age: 12 + is_tax_unit_dependent: true + long_term_capital_gains: -2_500 + tax_units: + tax_unit: + members: [head, child] + households: + household: + members: [head, child] + state_code: AZ + output: + # Only the head's line A wages. The dependent's line D (1,500) is in + # az_property_tax_credit_dependent_income. + az_property_tax_credit_agi: 3_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/tax/income/credits/az_property_tax_credit.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/tax/income/credits/az_property_tax_credit.yaml index 2f4afe51114..40ef7dda72b 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/az/tax/income/credits/az_property_tax_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/az/tax/income/credits/az_property_tax_credit.yaml @@ -144,3 +144,20 @@ households: {household: {members: [head], state_code: AZ}} output: az_property_tax_credit: 78 + +# Form 140PTC line J note (2023-2025 instructions, page 5): a negative household +# income is entered as is, and household income is considered zero for the +# Schedule 1 and 2 amounts. +- name: negative household income uses the zero-income schedule amount + period: 2025 + input: + state_code: AZ + cohabitating_spouses: false + az_property_tax_credit_income: -1_000 + az_property_tax_credit_eligible: true + real_estate_taxes: 0 + rent: 4_000 + output: + # Line 11a: Schedule 1, 0 - 1,750 -> 502. + # Line 13: 0.15 x 4,000 = 600. Line 15: smaller of 502 and 600. + az_property_tax_credit: 502 diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/tax/income/credits/az_property_tax_credit_capital_gains.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/tax/income/credits/az_property_tax_credit_capital_gains.yaml new file mode 100644 index 00000000000..43feab97aea --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/az/tax/income/credits/az_property_tax_credit_capital_gains.yaml @@ -0,0 +1,57 @@ +# Form 140PTC Part 1 line D, for one household member: "combine gains and +# losses from the sale or exchange of property. If the result is a gain, enter +# the gain. If the result is a loss, enter the amount of the loss up to a +# maximum of ($1,500)." (2021-2025 instructions, page 4; +# A.A.C. R15-2C-502(C)(3); ITR 12-1 item (7).) + +- name: A net gain is entered in full + period: 2025 + input: + state_code: AZ + long_term_capital_gains: 3_000 + short_term_capital_gains: -500 + output: + # 3,000 - 500 = 2,500. + az_property_tax_credit_capital_gains: 2_500 + +- name: A net loss within the limit is entered in full + period: 2025 + input: + state_code: AZ + long_term_capital_gains: -1_000 + output: + az_property_tax_credit_capital_gains: -1_000 + +- name: A net loss equal to the limit is entered in full + period: 2025 + input: + state_code: AZ + short_term_capital_gains: -1_500 + output: + az_property_tax_credit_capital_gains: -1_500 + +- name: A net loss over the limit is entered as the limit + period: 2025 + input: + state_code: AZ + long_term_capital_gains: 2_000 + short_term_capital_gains: -6_000 + output: + # 2,000 - 6,000 = (4,000), limited to (1,500). + az_property_tax_credit_capital_gains: -1_500 + +- name: The limit applies in 2021 + period: 2021 + input: + state_code: AZ + long_term_capital_gains: -3_000 + output: + az_property_tax_credit_capital_gains: -1_500 + +- name: Not computed outside Arizona + period: 2025 + input: + state_code: CA + long_term_capital_gains: -3_000 + output: + az_property_tax_credit_capital_gains: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/tax/income/credits/az_property_tax_credit_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/tax/income/credits/az_property_tax_credit_income.yaml new file mode 100644 index 00000000000..07e440d33af --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/az/tax/income/credits/az_property_tax_credit_income.yaml @@ -0,0 +1,460 @@ +# Arizona property tax credit household income: Form 140PTC page 2, Part 1, +# Schedule of Household Income, lines A to J (ARS 43-1072(H)(6); +# A.A.C. R15-2C-502; ITR 12-1). Line D is "gain or loss from sale or exchange +# of property": each member's gains and losses combined, with a net loss +# limited to $1,500 (2021-2025 instructions, page 4). Federal AGI already holds +# those gains, so each dollar of gain must count once. +# +# Credit amounts: Form 140PTC line 11 is the Schedule 1 (lived alone) or +# Schedule 2 (lived with a spouse or others) amount at line J; line 13 is a +# renter's property taxes, modeled as 0.15 x rent; line 15 is the smaller. + +- name: Single renter with ordinary long-term gains counts them once + period: 2025 + input: + people: + head: + age: 70 + long_term_capital_gains: 3_000 + rent: 5_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: AZ + output: + # Line D: gain from the sale of property 3,000. + # Line J: total household income 3,000. + az_property_tax_credit_income: 3_000 + # Line 11a: Schedule 1, 2,951 - 3,050 -> 212. + # Line 13: 0.15 x 5,000 = 750. Line 15: smaller of 212 and 750. + az_property_tax_credit: 212 + +- name: Single renter with qualified dividends counts them once + period: 2025 + input: + people: + head: + age: 70 + qualified_dividend_income: 3_000 + rent: 5_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: AZ + output: + # Line B: dividend income 3,000. Line J: 3,000. + az_property_tax_credit_income: 3_000 + # Line 11a: Schedule 1, 2,951 - 3,050 -> 212. Line 13: 750. + az_property_tax_credit: 212 + +- name: Long-term gains above the federal deductions still count once + period: 2025 + input: + people: + head: + age: 70 + long_term_capital_gains: 40_000 + rent: 5_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: AZ + output: + # Federal taxable income is positive here (40,000 less the standard, + # additional and senior deductions); the gain still counts once. + # Line D: gain 40,000. Line J: 40,000. + az_property_tax_credit_income: 40_000 + # Line 11a: Schedule 1, 3,751 and up -> 0. + az_property_tax_credit: 0 + +- name: Married couple combines each spouse's gains on line D + period: 2025 + input: + people: + head: + age: 70 + employment_income: 1_000 + long_term_capital_gains: 1_500 + rent: 4_000 + spouse: + age: 70 + short_term_capital_gains: 1_000 + tax_units: + tax_unit: + members: [head, spouse] + households: + household: + members: [head, spouse] + state_code: AZ + output: + # Line A: wages 1,000 (column 1). + # Line D: 1,500 (column 1) + 1,000 (column 2) = 2,500. + # Line J: 1,000 + 2,500 = 3,500. + az_property_tax_credit_income: 3_500 + # Line 11b: Schedule 2, 3,401 - 3,550 -> 345. + # Line 13: 0.15 x 4,000 = 600. Line 15: smaller of 345 and 600. + az_property_tax_credit: 345 + +- name: Short-term loss nets against long-term gain on line D + period: 2025 + input: + people: + head: + age: 70 + long_term_capital_gains: 5_000 + short_term_capital_gains: -2_000 + rent: 5_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: AZ + output: + # Line D: 5,000 - 2,000 = 3,000 net gain. Line J: 3,000. + az_property_tax_credit_income: 3_000 + # Line 11a: Schedule 1, 2,951 - 3,050 -> 212. Line 13: 750. + az_property_tax_credit: 212 + +- name: Net loss within the 1,500 limit reduces household income + period: 2025 + input: + people: + head: + age: 70 + employment_income: 4_000 + long_term_capital_gains: -1_000 + rent: 5_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: AZ + output: + # Line A: wages 4,000. + # Line D: net loss (1,000), within the (1,500) limit. + # Line J: 4,000 - 1,000 = 3,000. + az_property_tax_credit_income: 3_000 + # Line 11a: Schedule 1, 2,951 - 3,050 -> 212. Line 13: 750. + az_property_tax_credit: 212 + +- name: Every income line together, with Social Security excluded + period: 2025 + input: + people: + head: + age: 72 + employment_income: 500 + taxable_interest_income: 200 + tax_exempt_interest_income: 300 + long_term_capital_gains: 400 + taxable_private_pension_income: 600 + tax_exempt_public_pension_income: 100 + social_security_retirement: 12_000 + rent: 6_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: AZ + output: + # Line A: wages 500. + # Line B: interest 200 + tax-exempt interest 300 = 500. + # Line D: gain 400. + # Line E: pension 600 + tax-exempt public pension 100 = 700. + # Social Security is not income for the credit. + # Line J: 500 + 500 + 400 + 700 = 2,100. + az_property_tax_credit_income: 2_100 + # Line 11a: Schedule 1, 2,051 - 2,150 -> 412. + # Line 13: 0.15 x 6,000 = 900. Line 15: smaller of 412 and 900. + az_property_tax_credit: 412 + +- name: Long-term gains count once in 2021 + period: 2021 + input: + people: + head: + age: 66 + long_term_capital_gains: 2_000 + rent: 3_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: AZ + output: + # Line D: gain 2,000. Line J: 2,000. + az_property_tax_credit_income: 2_000 + # Line 11a: 2021 Schedule 1, 1,951 - 2,050 -> 435. + # Line 13: 0.15 x 3,000 = 450. Line 15: smaller of 435 and 450. + az_property_tax_credit: 435 + +# A.A.C. R15-2C-502(A)(2) and (B): household income combines every member's +# income, whether or not the member is a dependent. Federal AGI leaves out a +# dependent's income, so it is added separately. +- name: A dependent's long-term gains count once + period: 2025 + input: + people: + head: + age: 70 + rent: 5_000 + child: + age: 10 + is_tax_unit_dependent: true + long_term_capital_gains: 3_000 + tax_units: + tax_unit: + members: [head, child] + households: + household: + members: [head, child] + state_code: AZ + output: + # Line D: column 3 (other persons) gain 3,000. Line J: 3,000. + az_property_tax_credit_income: 3_000 + # Line 11b: Schedule 2, 2,951 - 3,100 -> 412. + # Line 13: 0.15 x 5,000 = 750. Line 15: smaller of 412 and 750. + az_property_tax_credit: 412 + +- name: A dependent's qualified dividends count once + period: 2025 + input: + people: + head: + age: 70 + rent: 5_000 + child: + age: 10 + is_tax_unit_dependent: true + qualified_dividend_income: 3_000 + tax_units: + tax_unit: + members: [head, child] + households: + household: + members: [head, child] + state_code: AZ + output: + # Line B: column 3 dividends 3,000. Line J: 3,000. + az_property_tax_credit_income: 3_000 + # Line 11b: Schedule 2, 2,951 - 3,100 -> 412. Line 13: 750. + az_property_tax_credit: 412 + +- name: A dependent's wages count with the claimant's pension + period: 2025 + input: + people: + head: + age: 70 + taxable_private_pension_income: 1_000 + rent: 5_000 + grandchild: + age: 16 + is_tax_unit_dependent: true + employment_income: 2_000 + tax_units: + tax_unit: + members: [head, grandchild] + households: + household: + members: [head, grandchild] + state_code: AZ + output: + # Line A: column 3 wages 2,000. Line E: column 1 pension 1,000. + # Line J: 3,000. + az_property_tax_credit_income: 3_000 + # Line 11b: Schedule 2, 2,951 - 3,100 -> 412. Line 13: 750. + az_property_tax_credit: 412 + +# Line D loss limit. A.A.C. R15-2C-502(C)(3), ITR 12-1 item (7) and the line D +# instructions limit each member's net loss to (1,500). Federal AGI instead +# limits a return's net loss to (3,000), so household income replaces it. A +# negative line J is entered as is and considered zero for Schedules 1 and 2 +# (2023-2025 instructions, page 5, line J note). +- name: Single filer's net loss counts only up to the 1,500 limit + period: 2025 + input: + people: + head: + age: 70 + employment_income: 4_000 + long_term_capital_gains: -2_000 + rent: 5_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: AZ + output: + # Line A: wages 4,000. + # Line D: net loss (2,000), limited to (1,500). + # Line J: 4,000 - 1,500 = 2,500. + # Federal AGI would give 4,000 - 2,000 = 2,000. + az_property_tax_credit_income: 2_500 + # Line 11a: Schedule 1, 2,451 - 2,550 -> 323. + # Line 13: 0.15 x 5,000 = 750. Line 15: smaller of 323 and 750. + az_property_tax_credit: 323 + +- name: Each spouse's net loss counts up to that spouse's own 1,500 limit + period: 2025 + input: + people: + head: + age: 70 + employment_income: 6_000 + long_term_capital_gains: -1_500 + rent: 5_000 + spouse: + age: 68 + short_term_capital_gains: -1_500 + tax_units: + tax_unit: + members: [head, spouse] + households: + household: + members: [head, spouse] + state_code: AZ + output: + # Line A: wages 6,000 (column 1). + # Line D: (1,500) (column 1) + (1,500) (column 2) = (3,000). + # Line J: 6,000 - 3,000 = 3,000. + az_property_tax_credit_income: 3_000 + # Line 11b: Schedule 2, 2,951 - 3,100 -> 412. + # Line 13: 750. Line 15: smaller of 412 and 750. + az_property_tax_credit: 412 + +- name: One spouse's loss over the limit does not offset the other's gain + period: 2025 + input: + people: + head: + age: 70 + employment_income: 3_000 + long_term_capital_gains: -4_000 + rent: 5_000 + spouse: + age: 70 + long_term_capital_gains: 2_000 + tax_units: + tax_unit: + members: [head, spouse] + households: + household: + members: [head, spouse] + state_code: AZ + output: + # Line A: wages 3,000 (column 1). + # Line D: (4,000) limited to (1,500) (column 1) + 2,000 (column 2) = 500. + # Line J: 3,000 + 500 = 3,500. + # Federal AGI would net the return first: 3,000 + 2,000 - 3,000 = 2,000. + az_property_tax_credit_income: 3_500 + # Line 11b: Schedule 2, 3,401 - 3,550 -> 345. + # Line 13: 750. Line 15: smaller of 345 and 750. + az_property_tax_credit: 345 + +- name: A line D loss offsets a tax-exempt pension + period: 2025 + input: + people: + head: + age: 70 + long_term_capital_gains: -1_500 + tax_exempt_public_pension_income: 3_000 + rent: 5_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: AZ + output: + # Line D: net loss (1,500), at the limit. + # Line E: Arizona public pension 3,000. + # Line J: 3,000 - 1,500 = 1,500. Flooring federal AGI less Social + # Security at zero before adding the pension would give 3,000. + az_property_tax_credit_agi: -1_500 + az_property_tax_credit_income: 1_500 + # Line 11a: Schedule 1, 0 - 1,750 -> 502. + # Line 13: 750. Line 15: smaller of 502 and 750. + az_property_tax_credit: 502 + +- name: Negative line J with a tax-exempt pension uses zero for the schedule + period: 2025 + input: + people: + head: + age: 70 + long_term_capital_gains: -1_500 + tax_exempt_public_pension_income: 1_000 + rent: 4_000 + spouse: + age: 70 + short_term_capital_gains: -2_500 + tax_units: + tax_unit: + members: [head, spouse] + households: + household: + members: [head, spouse] + state_code: AZ + output: + # Line D: (1,500) (column 1) + (2,500) limited to (1,500) (column 2) + # = (3,000). + # Line E: Arizona public pension 1,000 (column 1). + # Line J: 1,000 - 3,000 = (2,000), entered as a negative amount. + az_property_tax_credit_agi: -3_000 + az_property_tax_credit_income: -2_000 + # Line 11b: household income is considered zero for the schedule. + # Schedule 2, 0 - 2,500 -> 502. + # Line 13: 0.15 x 4,000 = 600. Line 15: smaller of 502 and 600. + az_property_tax_credit: 502 + +- name: A dependent's net loss counts up to the dependent's own 1,500 limit + period: 2025 + input: + people: + head: + age: 70 + employment_income: 3_000 + rent: 5_000 + grandchild: + age: 15 + is_tax_unit_dependent: true + long_term_capital_gains: -2_500 + tax_units: + tax_unit: + members: [head, grandchild] + households: + household: + members: [head, grandchild] + state_code: AZ + output: + # Line A: wages 3,000 (column 1). + # Line D: column 3 net loss (2,500), limited to (1,500). + # Line J: 3,000 - 1,500 = 1,500. + az_property_tax_credit_agi: 3_000 + az_property_tax_credit_dependent_income: -1_500 + az_property_tax_credit_income: 1_500 + # Line 11b: Schedule 2, 0 - 2,500 -> 502. + # Line 13: 750. Line 15: smaller of 502 and 750. + az_property_tax_credit: 502 diff --git a/policyengine_us/tests/test_az_property_tax_credit_income.py b/policyengine_us/tests/test_az_property_tax_credit_income.py new file mode 100644 index 00000000000..6678f7c1f5d --- /dev/null +++ b/policyengine_us/tests/test_az_property_tax_credit_income.py @@ -0,0 +1,338 @@ +"""Sampled property tests for Arizona property tax credit household income. + +ARS 43-1072(H)(6) defines income for the credit, and A.A.C. R15-2C-502 and the +Form 140PTC instructions (2021-2025, page 4) apply it line by line: line D is +each member's gains and losses from the sale or exchange of property, combined, +with a net loss limited to $1,500 for each member. Federal AGI already holds +those gains, so az_property_tax_credit_income must count each dollar of gain +once. It used to add capital_gains_excluded_from_taxable_income as well, which +is the part of federal taxable income taxed at the capital gains rates (at zero +taxable income, the adjusted net capital gain), so gains and qualified dividends +counted twice. Federal AGI leaves out dependents' income, which Arizona counts +for every household member whether or not a dependent. Federal AGI also limits +a net capital loss to $3,000 per return rather than $1,500 per member, so line D +replaces its capital gains and losses. Household income (line J) can be +negative; the 2023-2025 instructions (page 5) then consider it zero for the +Schedule 1 and 2 amounts. + +Invariants, checked on a seeded sample of Arizona tax units (some with a child +dependent who has income) whose members' net capital gains run from well below +-$1,500 to large gains, including units whose line J is negative: + +1. Differential: the model equals an independent line A + B + D + E sum over + every member from Form 140PTC Part 1, with Social Security excluded and + each member's line D floored at -$1,500. +2. Counted once: adding d to a member's long-term gains changes household + income by exactly the change in that member's line D, which is d when the + member's net gain stays at or above -$1,500. The credit never rises. +3. Loss limit: lowering a member's gains changes household income by the + change in that member's line D, which is 0 once the member's net gain is at + or below -$1,500. +4. Schedule floor: the credit depends on line J only through max(line J, 0), + so a negative line J gets the zero-income schedule amount. +5. The income does not depend on capital_gains_excluded_from_taxable_income. +""" + +import numpy as np + +from policyengine_us import Simulation + +YEAR = 2025 +N = 80 +SEED = 43_1072 +# A.A.C. R15-2C-502(C)(3): net capital losses are limited to $1,500 for each +# household member. +MEMBER_LOSS_LIMIT = 1_500 +# Form 140PTC line 13 for renters: rent times the property tax factor, +# modeled as gov.states.az.tax.income.credits.property_tax.rent_property_tax_rate. +RENT_PROPERTY_TAX_RATE = 0.15 + + +def _capital_gains(rng: np.random.Generator, can_be_large: bool) -> tuple: + """Long- and short-term gains for one member.""" + draw = rng.random() + if draw < 0.3: + return 0.0, 0.0 + if draw < 0.55: + # A net loss beyond the $1,500 member limit, some beyond federal AGI's + # $3,000 per-return limit too. + return ( + round(rng.uniform(-9_000, -1_600), 2), + round(rng.uniform(-1_000, 500), 2), + ) + # Some large gains, so federal taxable income is positive. + large_gain = ( + round(rng.uniform(20_000, 80_000), 2) + if can_be_large and rng.random() < 0.15 + else 0 + ) + return ( + round(rng.uniform(-1_000, 6_000), 2) + large_gain, + round(rng.uniform(-500, 2_000), 2), + ) + + +def _sample_units(rng: np.random.Generator) -> list: + units = [] + for _ in range(N): + married = rng.random() < 0.4 + members = [] + for role in ["head", "spouse"] if married else ["head"]: + long_term, short_term = _capital_gains(rng, can_be_large=True) + members.append( + { + "role": role, + "age": int(rng.integers(65, 86)), + "employment_income": float( + rng.choice([0, round(rng.uniform(0, 6_000), 2)]) + ), + "taxable_interest_income": round(rng.uniform(0, 800), 2), + "tax_exempt_interest_income": round(rng.uniform(0, 500), 2), + "qualified_dividend_income": round(rng.uniform(0, 1_500), 2), + "long_term_capital_gains": long_term, + "short_term_capital_gains": short_term, + "taxable_private_pension_income": float( + rng.choice([0, round(rng.uniform(0, 3_000), 2)]) + ), + "tax_exempt_public_pension_income": float( + rng.choice([0, round(rng.uniform(0, 3_000), 2)]) + ), + "social_security_retirement": float( + rng.choice([0, round(rng.uniform(0, 30_000), 2)]) + ), + } + ) + if rng.random() < 0.35: + # A child dependent with income of their own, including net + # capital losses, which count up to the dependent's own limit. + long_term, short_term = _capital_gains(rng, can_be_large=False) + members.append( + { + "role": "dependent", + "age": int(rng.integers(5, 18)), + "is_tax_unit_dependent": True, + "employment_income": float( + rng.choice([0, round(rng.uniform(0, 4_000), 2)]) + ), + "taxable_interest_income": round(rng.uniform(0, 300), 2), + "tax_exempt_interest_income": round(rng.uniform(0, 200), 2), + "qualified_dividend_income": round(rng.uniform(0, 1_500), 2), + "long_term_capital_gains": long_term, + "short_term_capital_gains": short_term, + "taxable_private_pension_income": 0.0, + "tax_exempt_public_pension_income": 0.0, + "social_security_survivors": float( + rng.choice([0, round(rng.uniform(0, 8_000), 2)]) + ), + } + ) + if rng.random() < 0.15: + # Little income besides a capital loss and a small tax-exempt + # pension, so line J can be negative (2023-2025 instructions, + # page 5, line J note). + for member in members: + for key in [ + "employment_income", + "taxable_interest_income", + "qualified_dividend_income", + "taxable_private_pension_income", + ]: + member[key] = 0.0 + member["tax_exempt_interest_income"] = round(rng.uniform(0, 200), 2) + members[0]["long_term_capital_gains"] = round( + rng.uniform(-6_000, -1_000), 2 + ) + members[0]["tax_exempt_public_pension_income"] = round( + rng.uniform(0, 1_500), 2 + ) + units.append( + { + "members": members, + "rent": round(rng.uniform(1_000, 12_000), 2), + "gain_change": round(rng.uniform(1, 5_000), 2), + } + ) + return units + + +def _with_head_gains_changed(units: list, sign: int) -> list: + changed = [] + for unit in units: + members = [dict(m) for m in unit["members"]] + members[0]["long_term_capital_gains"] = round( + members[0]["long_term_capital_gains"] + sign * unit["gain_change"], 2 + ) + changed.append({**unit, "members": members}) + return changed + + +def _situation(units: list) -> dict: + people, tax_units, households = {}, {}, {} + year = str(YEAR) + for i, unit in enumerate(units): + names = [] + for member in unit["members"]: + name = f"{member['role']}_{i}" + names.append(name) + person = { + key: {year: value} for key, value in member.items() if key != "role" + } + if member["role"] == "head": + person["rent"] = {year: unit["rent"]} + people[name] = person + tax_units[f"tax_unit_{i}"] = {"members": names} + households[f"household_{i}"] = { + "members": names, + "state_code": {year: "AZ"}, + } + return {"people": people, "tax_units": tax_units, "households": households} + + +def _net_gain(member: dict) -> float: + return member["long_term_capital_gains"] + member["short_term_capital_gains"] + + +def _line_d(member: dict) -> float: + """Form 140PTC line D for one member: a net loss is limited to (1,500).""" + return max(_net_gain(member), -MEMBER_LOSS_LIMIT) + + +def _form_140ptc_line_j(units: list) -> np.ndarray: + """Form 140PTC Part 1 line J from the line A to I instructions.""" + totals = [] + for unit in units: + total = 0.0 + for m in unit["members"]: + line_a = m["employment_income"] + line_b = ( + m["taxable_interest_income"] + + m["tax_exempt_interest_income"] + + m["qualified_dividend_income"] + ) + line_e = ( + m["taxable_private_pension_income"] + + m["tax_exempt_public_pension_income"] + ) + # Social Security benefits are not income for the credit. Every + # member counts, dependent or not (A.A.C. R15-2C-502(A)(2), (B)). + total += line_a + line_b + _line_d(m) + line_e + totals.append(total) + return np.array(totals) + + +UNITS = _sample_units(np.random.default_rng(SEED)) +RAISED = _with_head_gains_changed(UNITS, sign=1) +LOWERED = _with_head_gains_changed(UNITS, sign=-1) +LINE_J = _form_140ptc_line_j(UNITS) + + +def test_sample_covers_the_loss_limit_and_negative_line_j(): + members = [m for unit in UNITS for m in unit["members"]] + beyond_limit = [m for m in members if _net_gain(m) < -MEMBER_LOSS_LIMIT] + assert len(beyond_limit) >= 20 + assert any(m["role"] == "dependent" for m in beyond_limit) + # Units where federal AGI's capital gains and losses (non-dependents' net + # gains, less losses limited to $3,000 per return) differ from line D. + federal = np.array( + [ + sum( + max(0, _net_gain(m)) + for m in unit["members"] + if m["role"] != "dependent" + ) + - min(3_000, sum(max(0, -_net_gain(m)) for m in unit["members"])) + for unit in UNITS + ] + ) + line_d = np.array([sum(_line_d(m) for m in unit["members"]) for unit in UNITS]) + assert (np.abs(federal - line_d) > 0.01).sum() >= 20 + assert (LINE_J < 0).sum() >= 3 + + +def test_household_income_matches_form_140ptc_lines(): + sim = Simulation(situation=_situation(UNITS)) + income = sim.calculate("az_property_tax_credit_income", YEAR) + np.testing.assert_allclose(income, LINE_J, atol=0.01) + + +def test_gains_count_once_up_to_the_loss_limit(): + base = Simulation(situation=_situation(UNITS)) + raised = Simulation(situation=_situation(RAISED)) + expected = np.array( + [ + _line_d(r["members"][0]) - _line_d(u["members"][0]) + for u, r in zip(UNITS, RAISED) + ] + ) + income_change = raised.calculate( + "az_property_tax_credit_income", YEAR + ) - base.calculate("az_property_tax_credit_income", YEAR) + np.testing.assert_allclose(income_change, expected, atol=0.01) + # Where the head's net gain stays at or above the limit, the change is d. + stays_above = np.array( + [_net_gain(u["members"][0]) >= -MEMBER_LOSS_LIMIT for u in UNITS] + ) + increase = np.array([unit["gain_change"] for unit in UNITS]) + assert stays_above.sum() >= 20 + np.testing.assert_allclose( + income_change[stays_above], increase[stays_above], atol=0.01 + ) + credit_change = raised.calculate("az_property_tax_credit", YEAR) - base.calculate( + "az_property_tax_credit", YEAR + ) + assert (credit_change <= 0.01).all() + + +def test_member_loss_limit(): + base = Simulation(situation=_situation(UNITS)) + lowered = Simulation(situation=_situation(LOWERED)) + income_change = lowered.calculate( + "az_property_tax_credit_income", YEAR + ) - base.calculate("az_property_tax_credit_income", YEAR) + expected = np.array( + [ + _line_d(w["members"][0]) - _line_d(u["members"][0]) + for u, w in zip(UNITS, LOWERED) + ] + ) + np.testing.assert_allclose(income_change, expected, atol=0.01) + # Once a member's net gain is at or below the limit, more loss is ignored. + at_limit = np.array( + [_net_gain(u["members"][0]) <= -MEMBER_LOSS_LIMIT for u in UNITS] + ) + assert at_limit.sum() >= 10 + np.testing.assert_allclose(income_change[at_limit], 0, atol=0.01) + # Each member's line D is bounded below by the limit. + line_d = base.calculate("az_property_tax_credit_capital_gains", YEAR) + assert (line_d >= -MEMBER_LOSS_LIMIT - 0.01).all() + + +def test_credit_uses_zero_for_negative_household_income(): + sim = Simulation(situation=_situation(UNITS)) + credit = sim.calculate("az_property_tax_credit", YEAR) + floored = Simulation(situation=_situation(UNITS)) + floored.set_input("az_property_tax_credit_income", YEAR, np.maximum(LINE_J, 0)) + np.testing.assert_allclose( + credit, floored.calculate("az_property_tax_credit", YEAR), atol=0.01 + ) + # A negative line J gets the zero-income schedule amount ($502 on both + # schedules), limited by the renter's property taxes. + negative = LINE_J < 0 + rent = np.array([unit["rent"] for unit in UNITS]) + np.testing.assert_allclose( + credit[negative], + np.minimum(502, RENT_PROPERTY_TAX_RATE * rent[negative]), + atol=0.01, + ) + + +def test_income_does_not_read_preferential_rate_amount(): + sim = Simulation(situation=_situation(UNITS)) + rng = np.random.default_rng(SEED + 1) + sim.set_input( + "capital_gains_excluded_from_taxable_income", + YEAR, + rng.uniform(0, 50_000, len(UNITS)), + ) + income = sim.calculate("az_property_tax_credit_income", YEAR) + np.testing.assert_allclose(income, LINE_J, atol=0.01) diff --git a/policyengine_us/variables/gov/states/az/tax/income/credits/property_tax_credit/az_property_tax_credit.py b/policyengine_us/variables/gov/states/az/tax/income/credits/property_tax_credit/az_property_tax_credit.py index da5ead3a06e..6ffe7f57b75 100644 --- a/policyengine_us/variables/gov/states/az/tax/income/credits/property_tax_credit/az_property_tax_credit.py +++ b/policyengine_us/variables/gov/states/az/tax/income/credits/property_tax_credit/az_property_tax_credit.py @@ -7,6 +7,10 @@ class az_property_tax_credit(Variable): label = "Arizona Property Tax Credit" unit = USD definition_period = YEAR + reference = [ + "https://www.azleg.gov/ars/43/01072.htm", # ARS 43-1072(B) + "https://azdor.gov/sites/default/files/document/FORMS_INDIVIDUAL_2025_140PTCi.pdf#page=5", + ] defined_for = "az_property_tax_credit_eligible" def formula(tax_unit, period, parameters): @@ -27,10 +31,18 @@ def formula(tax_unit, period, parameters): | (household_size > 1) ) + # Household income (Form 140PTC line J) can be negative, for example + # after a line D capital loss. The 2023-2025 instructions (page 5, line + # J note) then consider household income to be zero for the Schedule 1 + # and 2 lookup. The 2021 and 2022 instructions have no such note; the + # schedules start at 0, so the same floor is applied in every year. A + # single-amount scale returns 0 below its first threshold, so a + # negative income must be floored here rather than passed to calc. + schedule_income = max_(income, 0) cap = where( lives_with_others, - p.amount.cohabitating.calc(income), - p.amount.living_alone.calc(income), + p.amount.cohabitating.calc(schedule_income), + p.amount.living_alone.calc(schedule_income), ) # ARS 43-1072(B): the credit is the lesser of the table amount and diff --git a/policyengine_us/variables/gov/states/az/tax/income/credits/property_tax_credit/az_property_tax_credit_agi.py b/policyengine_us/variables/gov/states/az/tax/income/credits/property_tax_credit/az_property_tax_credit_agi.py index 90e6818719f..3d36fc1cb5e 100644 --- a/policyengine_us/variables/gov/states/az/tax/income/credits/property_tax_credit/az_property_tax_credit_agi.py +++ b/policyengine_us/variables/gov/states/az/tax/income/credits/property_tax_credit/az_property_tax_credit_agi.py @@ -13,11 +13,17 @@ class az_property_tax_credit_agi(Variable): "excludes items specifically listed in ARS 43-1072(I): Social Security, " "railroad retirement, workers compensation, Arizona unemployment, veterans " "disability pensions, welfare, and gifts. Unlike regular Arizona income tax, " - "this does NOT exclude pension income, capital gains, or Arizona exemptions." + "this does NOT exclude pension income, capital gains, or Arizona exemptions. " + "Capital gains and losses are each member's Form 140PTC line D amount, with " + "a net loss limited per member, instead of federal AGI's net capital gain " + "less a capital loss limited per return. Covers members who are not " + "dependents, as federal AGI does, and can be negative." ) reference = [ "https://www.azleg.gov/ars/43/01072.htm", # ARS 43-1072 "https://azdor.gov/sites/default/files/2023-03/RULINGS_INDV_2012_itr12-1.pdf", # ITR 12-1 + "https://www.law.cornell.edu/regulations/arizona/Ariz-Admin-Code-SS-R15-2C-502", + "https://azdor.gov/sites/default/files/document/FORMS_INDIVIDUAL_2025_140PTCi.pdf#page=4", ] defined_for = StateCode.AZ @@ -48,4 +54,31 @@ def formula(tax_unit, period, parameters): # - US Government interest should be INCLUDED per ITR 12-1 item (2) # - Arizona exemptions (aged, blind) should NOT be subtracted - return max_(0, federal_agi - taxable_social_security) + # Capital gains and losses (Form 140PTC Part 1 line D). Federal AGI + # adds each non-dependent's net capital gain (irs_gross_income) and + # subtracts the capital loss deduction, limited to $3,000 per return + # (limited_capital_loss, part of loss_ald). A.A.C. R15-2C-502(C)(3) + # and ITR 12-1 item (7) instead limit each member's net loss to + # $1,500, so the federal amounts are replaced by each member's line D + # amount. Dependents' line D amounts are in + # az_property_tax_credit_dependent_income, with their other income. + person = tax_unit.members + is_dependent = person("is_tax_unit_dependent", period) + federal_capital_gains = tax_unit.sum( + ~is_dependent * max_(0, person("capital_gains", period)) + ) + federal_capital_loss = tax_unit("limited_capital_loss", period) + line_d = tax_unit.sum( + ~is_dependent * person("az_property_tax_credit_capital_gains", period) + ) + + # Not floored at zero: household income (Form 140PTC line J) can be + # negative, and az_property_tax_credit treats it as zero only for the + # Schedule 1 and 2 lookup. + return ( + federal_agi + - taxable_social_security + - federal_capital_gains + + federal_capital_loss + + line_d + ) diff --git a/policyengine_us/variables/gov/states/az/tax/income/credits/property_tax_credit/az_property_tax_credit_capital_gains.py b/policyengine_us/variables/gov/states/az/tax/income/credits/property_tax_credit/az_property_tax_credit_capital_gains.py new file mode 100644 index 00000000000..b5e8ceb9dce --- /dev/null +++ b/policyengine_us/variables/gov/states/az/tax/income/credits/property_tax_credit/az_property_tax_credit_capital_gains.py @@ -0,0 +1,29 @@ +from policyengine_us.model_api import * + + +class az_property_tax_credit_capital_gains(Variable): + value_type = float + entity = Person + label = "Arizona property tax credit capital gains and losses" + unit = USD + definition_period = YEAR + documentation = ( + "A household member's gains and losses from the sale or exchange of " + "property for the Arizona property tax credit (Form 140PTC Part 1 line D): " + "the year's gains and losses combined, with a net loss counted only up to " + "the per-member limit. No prior-year capital loss carryover is used." + ) + reference = [ + "https://www.law.cornell.edu/regulations/arizona/Ariz-Admin-Code-SS-R15-2C-502", + "https://azdor.gov/sites/default/files/2023-03/RULINGS_INDV_2012_itr12-1.pdf#page=2", + "https://azdor.gov/sites/default/files/document/FORMS_INDIVIDUAL_2025_140PTCi.pdf#page=4", + ] + defined_for = StateCode.AZ + + def formula(person, period, parameters): + p = parameters(period).gov.states.az.tax.income.credits.property_tax + # A.A.C. R15-2C-502(C)(3): "Income from capital gains is the net capital + # gains and losses for each member of the household. Net losses are + # limited to $1,500 for each household member." The limit applies to + # each member separately, not to the return as the federal limit does. + return max_(person("capital_gains", period), -p.capital_loss_limit) diff --git a/policyengine_us/variables/gov/states/az/tax/income/credits/property_tax_credit/az_property_tax_credit_dependent_income.py b/policyengine_us/variables/gov/states/az/tax/income/credits/property_tax_credit/az_property_tax_credit_dependent_income.py new file mode 100644 index 00000000000..5d3a4fc0d22 --- /dev/null +++ b/policyengine_us/variables/gov/states/az/tax/income/credits/property_tax_credit/az_property_tax_credit_dependent_income.py @@ -0,0 +1,44 @@ +from policyengine_us.model_api import * + + +class az_property_tax_credit_dependent_income(Variable): + value_type = float + entity = TaxUnit + label = "Arizona property tax credit income of dependents" + unit = USD + definition_period = YEAR + documentation = ( + "Income of the tax unit's dependents for the Arizona property tax credit. " + "Federal AGI, which az_property_tax_credit_agi starts from, holds only the " + "income of members who are not dependents; Arizona combines the income of " + "every member of the household, whether or not the member is a dependent. " + "A dependent's capital gains and losses are the dependent's Form 140PTC " + "line D amount, so a net loss counts up to the per-member limit." + ) + reference = [ + "https://www.azleg.gov/ars/43/01072.htm", # ARS 43-1072(H)(4)-(6), (I) + "https://www.law.cornell.edu/regulations/arizona/Ariz-Admin-Code-SS-R15-2C-502", + "https://azdor.gov/sites/default/files/document/FORMS_INDIVIDUAL_2025_140PTCi.pdf#page=4", + ] + defined_for = StateCode.AZ + + def formula(tax_unit, period, parameters): + person = tax_unit.members + # A.A.C. R15-2C-502(A)(2) and (B): household income combines the + # separately determined income of each member, "whether or not the + # person is related to, or a dependent of, the claimant". irs_gross_income + # counts the same federal sources for members who are not dependents. + sources = parameters(period).gov.irs.gross_income.sources + income = 0 + for source in sources: + # ARS 43-1072(I): Social Security benefits are not income. + # Capital gains and losses are added below as line D. + if source in ("taxable_social_security", "capital_gains"): + continue + income += max_(0, add(person, period, [source])) + # Form 140PTC line D: a dependent is a household member like any + # other, so a net capital loss counts, limited to $1,500 for the + # dependent (A.A.C. R15-2C-502(C)(3)), as it is for the claimant. + income += person("az_property_tax_credit_capital_gains", period) + is_dependent = person("is_tax_unit_dependent", period) + return tax_unit.sum(is_dependent * income) diff --git a/policyengine_us/variables/gov/states/az/tax/income/credits/property_tax_credit/az_property_tax_credit_income.py b/policyengine_us/variables/gov/states/az/tax/income/credits/property_tax_credit/az_property_tax_credit_income.py index fb47ebd3bf1..dbcca77417f 100644 --- a/policyengine_us/variables/gov/states/az/tax/income/credits/property_tax_credit/az_property_tax_credit_income.py +++ b/policyengine_us/variables/gov/states/az/tax/income/credits/property_tax_credit/az_property_tax_credit_income.py @@ -4,9 +4,19 @@ class az_property_tax_credit_income(Variable): value_type = float entity = TaxUnit - label = "Income for the Arizona property tax the credit" + label = "Income for the Arizona property tax credit" unit = USD definition_period = YEAR + documentation = ( + "Household income for the Arizona property tax credit, Form 140PTC Part 1 " + "line J. It can be negative after a capital loss; the credit schedules " + "then use zero." + ) + reference = [ + "https://www.azleg.gov/ars/43/01072.htm", # ARS 43-1072(H)(6) + "https://www.law.cornell.edu/regulations/arizona/Ariz-Admin-Code-SS-R15-2C-502", + "https://azdor.gov/sites/default/files/document/FORMS_INDIVIDUAL_2025_140PTCi.pdf#page=4", + ] defined_for = StateCode.AZ adds = "gov.states.az.tax.income.credits.property_tax.income_sources"