diff --git a/changelog.d/8314.added.md b/changelog.d/8314.added.md new file mode 100644 index 00000000000..f95f97ff202 --- /dev/null +++ b/changelog.d/8314.added.md @@ -0,0 +1 @@ +Added the North Dakota Renter's Refund. diff --git a/policyengine_us/parameters/gov/states/household/state_property_tax_credits.yaml b/policyengine_us/parameters/gov/states/household/state_property_tax_credits.yaml index b5c719c31cf..014556dd3d6 100644 --- a/policyengine_us/parameters/gov/states/household/state_property_tax_credits.yaml +++ b/policyengine_us/parameters/gov/states/household/state_property_tax_credits.yaml @@ -114,6 +114,26 @@ values: - wi_homestead_credit - wi_property_tax_credit - wv_homestead_excess_property_tax_credit + 2026-01-01: + - az_property_tax_credit + - ct_property_tax_credit + - dc_ptc + - il_property_tax_credit + - ma_senior_circuit_breaker + - me_property_tax_fairness_credit + - mi_homestead_property_tax_credit + - mo_property_tax_credit + - mt_elderly_homeowner_or_renter_credit + - mt_property_tax_rebate + - nd_renters_refund + - nj_property_tax_credit + - nm_property_tax_rebate + - ny_real_property_tax_credit + - ok_ptc + - ri_property_tax_credit + - wi_homestead_credit + - wi_property_tax_credit + - wv_homestead_excess_property_tax_credit metadata: unit: list diff --git a/policyengine_us/parameters/gov/states/nd/tax/property/renters_refund/age_threshold.yaml b/policyengine_us/parameters/gov/states/nd/tax/property/renters_refund/age_threshold.yaml new file mode 100644 index 00000000000..cce114fbeff --- /dev/null +++ b/policyengine_us/parameters/gov/states/nd/tax/property/renters_refund/age_threshold.yaml @@ -0,0 +1,10 @@ +description: North Dakota limits age-based eligibility to filers at or above this age under the Renter's Refund program. +values: + 2026-01-01: 65 +metadata: + unit: year + period: year + label: North Dakota Renter's Refund age threshold + reference: + - title: North Dakota Office of State Tax Commissioner | Renter's Refund + href: https://www.tax.nd.gov/renters-refund diff --git a/policyengine_us/parameters/gov/states/nd/tax/property/renters_refund/cap.yaml b/policyengine_us/parameters/gov/states/nd/tax/property/renters_refund/cap.yaml new file mode 100644 index 00000000000..1a51d1101eb --- /dev/null +++ b/policyengine_us/parameters/gov/states/nd/tax/property/renters_refund/cap.yaml @@ -0,0 +1,10 @@ +description: North Dakota caps the refund at this amount under the Renter's Refund program. +values: + 2026-01-01: 600 +metadata: + unit: currency-USD + period: year + label: North Dakota Renter's Refund cap + reference: + - title: North Dakota Office of State Tax Commissioner | Renter's Refund + href: https://www.tax.nd.gov/renters-refund diff --git a/policyengine_us/parameters/gov/states/nd/tax/property/renters_refund/income_limit.yaml b/policyengine_us/parameters/gov/states/nd/tax/property/renters_refund/income_limit.yaml new file mode 100644 index 00000000000..26c0ae99f10 --- /dev/null +++ b/policyengine_us/parameters/gov/states/nd/tax/property/renters_refund/income_limit.yaml @@ -0,0 +1,10 @@ +description: North Dakota limits income to this amount under the Renter's Refund program. +values: + 2026-01-01: 70_000 +metadata: + unit: currency-USD + period: year + label: North Dakota Renter's Refund income limit + reference: + - title: North Dakota Office of State Tax Commissioner | Renter's Refund + href: https://www.tax.nd.gov/renters-refund diff --git a/policyengine_us/parameters/gov/states/nd/tax/property/renters_refund/income_rate.yaml b/policyengine_us/parameters/gov/states/nd/tax/property/renters_refund/income_rate.yaml new file mode 100644 index 00000000000..7452215a1b6 --- /dev/null +++ b/policyengine_us/parameters/gov/states/nd/tax/property/renters_refund/income_rate.yaml @@ -0,0 +1,10 @@ +description: North Dakota subtracts this share of income under the Renter's Refund program. +values: + 2026-01-01: 0.04 +metadata: + unit: /1 + period: year + label: North Dakota Renter's Refund income share + reference: + - title: North Dakota Office of State Tax Commissioner | Renter's Refund + href: https://www.tax.nd.gov/renters-refund diff --git a/policyengine_us/parameters/gov/states/nd/tax/property/renters_refund/rent_rate.yaml b/policyengine_us/parameters/gov/states/nd/tax/property/renters_refund/rent_rate.yaml new file mode 100644 index 00000000000..5959d8be9d2 --- /dev/null +++ b/policyengine_us/parameters/gov/states/nd/tax/property/renters_refund/rent_rate.yaml @@ -0,0 +1,10 @@ +description: North Dakota counts this share of annual rent under the Renter's Refund program. +values: + 2026-01-01: 0.2 +metadata: + unit: /1 + period: year + label: North Dakota Renter's Refund rent share + reference: + - title: North Dakota Office of State Tax Commissioner | Renter's Refund + href: https://www.tax.nd.gov/renters-refund diff --git a/policyengine_us/tests/policy/baseline/gov/states/nd/tax/property/renters_refund/nd_renters_refund.yaml b/policyengine_us/tests/policy/baseline/gov/states/nd/tax/property/renters_refund/nd_renters_refund.yaml new file mode 100644 index 00000000000..4d60a1cf3f6 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/nd/tax/property/renters_refund/nd_renters_refund.yaml @@ -0,0 +1,53 @@ +- name: Case 1, refund follows the state example. + period: 2026 + input: + people: + person1: + age: 65 + rent: 9_600 + tax_units: + tax_unit: + members: [person1] + adjusted_gross_income: 35_000 + households: + household: + members: [person1] + state_code: ND + output: + nd_renters_refund: 520 + +- name: Case 2, refund is capped. + period: 2026 + input: + people: + person1: + age: 65 + rent: 18_000 + tax_units: + tax_unit: + members: [person1] + adjusted_gross_income: 10_000 + households: + household: + members: [person1] + state_code: ND + output: + nd_renters_refund: 600 + +- name: Case 3, ineligible filer gets no refund. + period: 2026 + input: + people: + person1: + age: 64 + rent: 9_600 + tax_units: + tax_unit: + members: [person1] + adjusted_gross_income: 35_000 + households: + household: + members: [person1] + state_code: ND + output: + nd_renters_refund: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nd/tax/property/renters_refund/nd_renters_refund_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/nd/tax/property/renters_refund/nd_renters_refund_eligible.yaml new file mode 100644 index 00000000000..f106df3f8e4 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/nd/tax/property/renters_refund/nd_renters_refund_eligible.yaml @@ -0,0 +1,90 @@ +- name: Case 1, senior renter with sufficient rent burden is eligible. + period: 2026 + input: + people: + person1: + age: 65 + rent: 9_600 + tax_units: + tax_unit: + members: [person1] + adjusted_gross_income: 35_000 + households: + household: + members: [person1] + state_code: ND + output: + nd_renters_refund_eligible: true + +- name: Case 2, disabled renter under age 65 is eligible. + period: 2026 + input: + people: + person1: + age: 40 + is_disabled: true + rent: 9_600 + tax_units: + tax_unit: + members: [person1] + adjusted_gross_income: 35_000 + households: + household: + members: [person1] + state_code: ND + output: + nd_renters_refund_eligible: true + +- name: Case 3, income above the limit is ineligible. + period: 2026 + input: + people: + person1: + age: 65 + rent: 20_000 + tax_units: + tax_unit: + members: [person1] + adjusted_gross_income: 70_001 + households: + household: + members: [person1] + state_code: ND + output: + nd_renters_refund_eligible: false + +- name: Case 4, renter without sufficient rent burden is ineligible. + period: 2026 + input: + people: + person1: + age: 65 + rent: 4_000 + tax_units: + tax_unit: + members: [person1] + adjusted_gross_income: 35_000 + households: + household: + members: [person1] + state_code: ND + output: + nd_renters_refund_eligible: false + +- name: Case 5, non-senior renter without disability is ineligible. + period: 2026 + input: + people: + person1: + age: 64 + rent: 9_600 + tax_units: + tax_unit: + members: [person1] + adjusted_gross_income: 35_000 + households: + household: + members: [person1] + state_code: ND + output: + nd_renters_refund_eligible: false diff --git a/policyengine_us/variables/gov/states/nd/tax/property/renters_refund/nd_renters_refund.py b/policyengine_us/variables/gov/states/nd/tax/property/renters_refund/nd_renters_refund.py new file mode 100644 index 00000000000..76993c72d2a --- /dev/null +++ b/policyengine_us/variables/gov/states/nd/tax/property/renters_refund/nd_renters_refund.py @@ -0,0 +1,22 @@ +from policyengine_us.model_api import * + + +class nd_renters_refund(Variable): + value_type = float + entity = TaxUnit + label = "North Dakota Renter's Refund" + unit = USD + definition_period = YEAR + reference = "https://www.tax.nd.gov/renters-refund" + defined_for = "nd_renters_refund_eligible" + + def formula(tax_unit, period, parameters): + p = parameters(period).gov.states.nd.tax.property.renters_refund + return min_( + p.cap, + max_( + add(tax_unit, period, ["rent"]) * p.rent_rate + - tax_unit("adjusted_gross_income", period) * p.income_rate, + 0, + ), + ) diff --git a/policyengine_us/variables/gov/states/nd/tax/property/renters_refund/nd_renters_refund_eligible.py b/policyengine_us/variables/gov/states/nd/tax/property/renters_refund/nd_renters_refund_eligible.py new file mode 100644 index 00000000000..fff10002b8a --- /dev/null +++ b/policyengine_us/variables/gov/states/nd/tax/property/renters_refund/nd_renters_refund_eligible.py @@ -0,0 +1,29 @@ +from policyengine_us.model_api import * + + +class nd_renters_refund_eligible(Variable): + value_type = bool + entity = TaxUnit + label = "Eligible for the North Dakota Renter's Refund" + definition_period = YEAR + reference = "https://www.tax.nd.gov/renters-refund" + defined_for = StateCode.ND + + def formula(tax_unit, period, parameters): + p = parameters(period).gov.states.nd.tax.property.renters_refund + person = tax_unit.members + head_or_spouse = person("is_tax_unit_head_or_spouse", period) + age = person("age", period.this_year) + is_disabled = person("is_disabled", period) + age_or_disability_eligible = tax_unit.any( + ((age >= p.age_threshold) | is_disabled) & head_or_spouse, + ) + income = tax_unit("adjusted_gross_income", period) + rent = add(tax_unit, period, ["rent"]) + + return ( + age_or_disability_eligible + & (income <= p.income_limit) + & (rent > 0) + & ((rent * p.rent_rate - income * p.income_rate) > 0) + )