diff --git a/changelog_entry.yaml b/changelog_entry.yaml index e69de29bb..8b1378917 100644 --- a/changelog_entry.yaml +++ b/changelog_entry.yaml @@ -0,0 +1 @@ + diff --git a/policyengine_canada/entities.py b/policyengine_canada/entities.py index 30e5adeb0..36c2d50fc 100644 --- a/policyengine_canada/entities.py +++ b/policyengine_canada/entities.py @@ -8,6 +8,20 @@ from policyengine_core.entities import build_entity +State = build_entity( + key="state", + plural="states", + label="State", + roles=[ + { + "key": "member", + "plural": "members", + "label": "Member", + "doc": "A person who is a citizen of a country.", + } + ], +) + Household = build_entity( key="household", plural="households", @@ -32,4 +46,4 @@ ) -entities = [Household, Person] +entities = [Household, Person, State] diff --git a/policyengine_canada/parameters/gov/cra/benefits/gis_spa/gis_cap/one_pensioner.yaml b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/gis_cap/one_pensioner.yaml new file mode 100644 index 000000000..fc613fe6f --- /dev/null +++ b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/gis_cap/one_pensioner.yaml @@ -0,0 +1,10 @@ +description: Guaranteed Income Supplement maximum benefit for single OAS pensioners or married pensioners whose spouse does not qualify for OAS or SPA. Corresponds to categories SINGLE_WITH_OAS or COUPLE_ONE_OAS_SPA_INELIGIBLE +values: + 2021-01-01: 9_449.87 +metadata: + period: year + label: GIS Cap for Single Pensioners and Married Pensioners whose spouse doesn't qualify for GIS or SPA + documentataion: Corresponds to BGISS in the SPSD/M Variable Guide + reference: + - title: SPSD/M Variable Guide + diff --git a/policyengine_canada/parameters/gov/cra/benefits/gis_spa/gis_cap/two_pensioners.yaml b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/gis_cap/two_pensioners.yaml new file mode 100644 index 000000000..14ab05724 --- /dev/null +++ b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/gis_cap/two_pensioners.yaml @@ -0,0 +1,10 @@ +description: Guaranteed Income Supplement maximum rate in dollars for each OAS pensioner in a married couple. Corresponds to COUPLE_BOTH_OAS or COUPLE_ONE_OAS_SPA_ELIGIBLE +values: + 2021-01-01: 6_240.18 +metadata: + period: year + label: GIS Cap for pensioners married to a pensioner. + documentataion: Corresponds to BGISM in the SPSD/M Variable Guide + reference: + - title: SPSD/M Variable Guide + diff --git a/policyengine_canada/parameters/gov/cra/benefits/gis_spa/gis_exemption/gis_exemption.yaml b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/gis_exemption/gis_exemption.yaml new file mode 100644 index 000000000..55e9cd121 --- /dev/null +++ b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/gis_exemption/gis_exemption.yaml @@ -0,0 +1,25 @@ +description: Percentage of income exempt from inclusion in calculating the GIS reduction. +brackets: + - threshold: + 2021-01-01: 0 + rate: + 2021-01-01: 1 + - threshold: + 2021-01-01: 5_000 # GISEMPEXM + rate: + 2021-01-01: 0.5 # GISPEMPEXP + - threshold: + 2021-01-01: 15_000 # GISPEMPEXM + GISEMPEXM + rate: + 2021-01-01: 0 +metadata: + type: marginal_rate + rate_unit: /1 + threshold_unit: currency-CAD + period: year + documentation: Corresponds to GISEMPEXP and GISEMPEXM + label: Percentage of income exempt from inclusion in calculating the GIS reduction. + name: gis_basic_income_exemption_rate + + + diff --git a/policyengine_canada/parameters/gov/cra/benefits/gis_spa/gis_reduction/gis_spa_couple_plateau_rate.yaml b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/gis_reduction/gis_spa_couple_plateau_rate.yaml new file mode 100644 index 000000000..b1df74019 --- /dev/null +++ b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/gis_reduction/gis_spa_couple_plateau_rate.yaml @@ -0,0 +1,10 @@ +description: GIS reduction rate for the 'plateau' range for a GIS recipient married to a SPA recipient. +values: + 2021-01-01: 0 +metadata: + period: year + label: GIS reduction rate for the 'plateau' range for a GIS recipient married to a SPA recipient. + documentataion: Corresponds to GISRRM and GISRLM in the SPSD/M Variable Guide + reference: + - title: SPSD/M Variable Guide + diff --git a/policyengine_canada/parameters/gov/cra/benefits/gis_spa/gis_reduction/one_pensioner.yaml b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/gis_reduction/one_pensioner.yaml new file mode 100644 index 000000000..d76b0397d --- /dev/null +++ b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/gis_reduction/one_pensioner.yaml @@ -0,0 +1,21 @@ +description: Guaranteed Income Supplement reduction for single OAS pensioners or married pensioners whose spouse does not qualify for OAS or SPA. Corresponds to categories SINGLE_WITH_OAS or COUPLE_ONE_OAS_SPA_INELIGIBLE. Corresponds to GISRRS in the SPSD/M Variable Guide +brackets: + - threshold: + 2021-01-01: 0 + rate: + 2021-01-01: 0 + - threshold: + 2021-01-01: 24 + rate: + 2021-01-01: 0.5 +metadata: + type: marginal_rate + rate_unit: /1 + threshold_unit: currency-CAD + period: year + documentation: Corresponds to GISRRS and GISRLS + label: GIS reduction for Single Pensioners and Married Pensioners whose spouse doesn't qualify for GIS or SPA + name: gis_reduction_rate_for_single_pensioner_households + + + diff --git a/policyengine_canada/parameters/gov/cra/benefits/gis_spa/gis_reduction/two_pensioners.yaml b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/gis_reduction/two_pensioners.yaml new file mode 100644 index 000000000..09dd06050 --- /dev/null +++ b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/gis_reduction/two_pensioners.yaml @@ -0,0 +1,17 @@ +description: Reduction threshold and rate for pensioners married to pensioners. Applies to COUPLE_BOTH_OAS. Note that this calculates on couples combined income, whereas the other GIS cateogires calculate on the lone pensioner's income. Correponds to GISRRM in the SPSD/M. +brackets: + - threshold: + 2021-01-01: 0 + rate: + 2021-01-01: 0 + - threshold: + 2021-01-01: 48 + rate: + 2021-01-01: 0.25 +metadata: + period: year + label: GIS reduction for pensioners married to a pensioner. + documentataion: Corresponds to GISRRM and GISRLM in the SPSD/M Variable Guide + reference: + - title: SPSD/M Variable Guide + diff --git a/policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_cap/gis_portion.yaml b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_cap/gis_portion.yaml new file mode 100644 index 000000000..fd7e763e6 --- /dev/null +++ b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_cap/gis_portion.yaml @@ -0,0 +1,9 @@ +description: Maximum amount of the 'GIS-portion' of the SPA. By default equal to the GIS family amount. +values: + 1999-12-31: 6_240.18 +metadata: + period: year + label: Maximum amount of the GIS-portion of the SPA. + reference: + - title: SPSD/M Variable Guide + diff --git a/policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_cap/oas_portion.yaml b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_cap/oas_portion.yaml new file mode 100644 index 000000000..453856745 --- /dev/null +++ b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_cap/oas_portion.yaml @@ -0,0 +1,9 @@ +description: Maximum amount of the 'OAS-portion' of the SPA. By default equal to the OAS maximum amount. +values: + 1999-12-31: 8_226 +metadata: + period: year + label: Maximum amount of the OAS-portion of the SPA. + reference: + - title: SPSD/M Variable Guide + diff --git a/policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_reduction/spa_gis_reduction.yaml b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_reduction/spa_gis_reduction.yaml new file mode 100644 index 000000000..6ea70f668 --- /dev/null +++ b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_reduction/spa_gis_reduction.yaml @@ -0,0 +1,9 @@ +description: Reduction rate for the GIS portion of the SPA. +values: + 1999-12-31: .25 +metadata: + period: year + label: Reduction rate for the GIS portion of the SPA. + reference: + - title: SPSD/M Algorithm Guide + diff --git a/policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_reduction/spa_oas_reduction.yaml b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_reduction/spa_oas_reduction.yaml new file mode 100644 index 000000000..793214c62 --- /dev/null +++ b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_reduction/spa_oas_reduction.yaml @@ -0,0 +1,19 @@ +description: Reduction rate for the OAS portion of the SPA. See SPARL in the SPSD/M Parameter Guide. +brackets: + - threshold: + 2021-01-01: 0 + rate: + 2021-01-01: 0 + - threshold: + 2021-01-01: 48 + rate: + 2021-01-01: 0.75 +metadata: + type: marginal_rate + rate_unit: /1 + threshold_unit: currency-CAD + period: year + label: Reduction rate for the OAS portion of the SPA. + reference: + - title: SPSD/M Algorithm Guide + diff --git a/policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_residency_requirement.yaml b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_residency_requirement.yaml new file mode 100644 index 000000000..5482a9a59 --- /dev/null +++ b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_residency_requirement.yaml @@ -0,0 +1,8 @@ +description: Number of years of canadian residency since the age of 18 required to recieve the Spousal Allowance +values: + 2020-01-01: 10 +metadata: + label: SPA Residency Requirement + reference: + - title: Government of Canada - Spousal Allowance + href: https://www.canada.ca/en/services/benefits/publicpensions/cpp/old-age-security/guaranteed-income-supplement/allowance/eligibility.html diff --git a/policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_spouse_eligibility_age.yaml b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_spouse_eligibility_age.yaml new file mode 100644 index 000000000..942e9e618 --- /dev/null +++ b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_spouse_eligibility_age.yaml @@ -0,0 +1,8 @@ +description: Age at which the spouse of an OAS pensioner becomes eligible for the SPA +values: + 2020-01-01: 60 +metadata: + label: SPA Spouse Eligibility Age + reference: + - title: Government of Canada - Guaranteed Income Supplement + href: https://www.canada.ca/en/services/benefits/publicpensions/cpp/old-age-security/guaranteed-income-supplement/allowance-survivor.html diff --git a/policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_widows_eligibility_age.yaml b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_widows_eligibility_age.yaml new file mode 100644 index 000000000..5e01ac88d --- /dev/null +++ b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_widows_eligibility_age.yaml @@ -0,0 +1,8 @@ +description: Age at which a widow becomes eligible for the SPA +values: + 2020-01-01: 60 +metadata: + label: SPA Widow Eligibility Age + reference: + - title: Government of Canada - Guaranteed Income Supplement + href: https://www.canada.ca/en/services/benefits/publicpensions/cpp/old-age-security/guaranteed-income-supplement/allowance-survivor.html diff --git a/policyengine_canada/parameters/gov/cra/benefits/gis_spa/topup_cap/married.yaml b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/topup_cap/married.yaml new file mode 100644 index 000000000..2ec7fdc45 --- /dev/null +++ b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/topup_cap/married.yaml @@ -0,0 +1,10 @@ +description: Guaranteed Income Supplement maximum top-up for married recipients of GIS or SPA. Corresponds to categories SINGLE_WITH_OAS or WIDOW_SPA_ELIGIBLE +values: + 2021-01-01: 490.98 +metadata: + period: year + label: GIS Top-up Cap for Married GIS or SPA Recipients + documentataion: Corresponds to GISTOPMAR in the SPSD/M Variable Guide + reference: + - title: SPSD/M Variable Guide + diff --git a/policyengine_canada/parameters/gov/cra/benefits/gis_spa/topup_cap/singles.yaml b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/topup_cap/singles.yaml new file mode 100644 index 000000000..c01779465 --- /dev/null +++ b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/topup_cap/singles.yaml @@ -0,0 +1,11 @@ +description: Guaranteed Income Supplement maximum top-up for single recipients of GIS or SPA. Corresponds to categories SINGLE_WITH_OAS or WIDOW_SPA_ELIGIBLE +values: + 2021-01-01: 856.32 + 2021-07-01: 875.70 +metadata: + period: year + label: GIS Top-up Cap for Single GIS or SPA Recipients + documentataion: Corresponds to GISTOPSNG1 and GISTOPSNG2 in the SPSD/M Variable Guide + reference: + - title: SPSD/M Variable Guide + diff --git a/policyengine_canada/parameters/gov/cra/benefits/gis_spa/topup_reduction/married.yaml b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/topup_reduction/married.yaml new file mode 100644 index 000000000..6de6175d1 --- /dev/null +++ b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/topup_reduction/married.yaml @@ -0,0 +1,17 @@ +description: Guaranteed Income Supplement Top-up turnaround point for categories SINGLE_WITH_OAS or WIDOW_SPA_ELIGIBLE +brackets: + - threshold: + 2021-01-01: 0 + rate: + 2021-01-01: 0 + - threshold: + 2021-01-01: 4000 + rate: + 2021-01-01: 0.125 +metadata: + period: year + label: GIS Top-up Income Exemption for Single GIS or SPA Recipients + documentataion: Corresponds to GISTOPMINGIS and GISTOPMRRGIS in the SPSD/M Variable Guide + reference: + - title: SPSD/M Variable Guide + diff --git a/policyengine_canada/parameters/gov/cra/benefits/gis_spa/topup_reduction/singles.yaml b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/topup_reduction/singles.yaml new file mode 100644 index 000000000..83411cde9 --- /dev/null +++ b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/topup_reduction/singles.yaml @@ -0,0 +1,17 @@ +description: Guaranteed Income Supplement Top-up turnaround point for categories SINGLE_WITH_OAS or WIDOW_SPA_ELIGIBLE +brackets: + - threshold: + 2021-01-01: 0 + rate: + 2021-01-01: 0 + - threshold: + 2021-01-01: 2000 + rate: + 2021-01-01: 0.25 +metadata: + period: year + label: GIS Top-up Income Exemption for Single GIS or SPA Recipients + documentataion: Corresponds to GISTOPSINGIS and GISTOPSRRGIS in the SPSD/M Variable Guide + reference: + - title: SPSD/M Variable Guide + diff --git a/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_base_amount.yaml b/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_base_amount.yaml new file mode 100644 index 000000000..27dc8a26f --- /dev/null +++ b/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_base_amount.yaml @@ -0,0 +1,48 @@ +- name: Single Pensioner + period: 2022 + input: + gis_spa_category: "SINGLE_WITH_OAS" + oas_eligible: true + output: + gis_cap: 9_449.87 + +- name: Widow + period: 2022 + input: + gis_spa_category: "WIDOW_SPA_ELIGIBLE" + output: + gis_cap: 0 + +- name: OAS pensioner whose spouse is SPA eligible + period: 2022 + input: + gis_spa_category: "COUPLE_ONE_OAS_SPA_ELIGIBLE" + oas_eligible: true + output: + gis_cap: 6_240.18 + +- name: OAS pensioner whose spouse is not SPA eligible + period: 2022 + input: + gis_spa_category: "COUPLE_ONE_OAS_SPA_INELIGIBLE" + oas_eligible: true + output: + gis_cap: 9_449.87 + +- name: SPA-eligible person whose spouse has OAS + period: 2022 + input: + gis_spa_category: "COUPLE_ONE_OAS_SPA_ELIGIBLE" + oas_eligible: false + output: + gis_cap: 0 + +- name: Person not eligible for OAS or SPA, but is married to an OAS pensioner + period: 2022 + input: + gis_spa_category: "COUPLE_ONE_OAS_SPA_INELIGIBLE" + oas_eligible: false + output: + gis_cap: 0 + + diff --git a/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_exemption.yaml b/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_exemption.yaml new file mode 100644 index 000000000..f51816a6b --- /dev/null +++ b/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_exemption.yaml @@ -0,0 +1,7 @@ +- name: Basic + period: 2023 + input: + employment_income: 20000 + self_employment_income: 5000 + output: + gis_exemption: 10000 diff --git a/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_income.yaml b/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_income.yaml new file mode 100644 index 000000000..0db79f3f6 --- /dev/null +++ b/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_income.yaml @@ -0,0 +1,8 @@ +- name: Basic + period: 2023 + input: + employment_income: 20000 + self_employment_income: 5000 + gis_exemption: 5000 + output: + gis_income: 10000 diff --git a/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_net.yaml b/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_net.yaml new file mode 100644 index 000000000..948f6e34b --- /dev/null +++ b/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_net.yaml @@ -0,0 +1,15 @@ +- name: Basic reduction + period: 2023 + input: + gis_cap: 500 + gis_reduction: 200 + output: + gis_net: 300 + +- name: Negative reduction + period: 2023 + input: + gis_cap: 300 + gis_reduction: 500 + output: + gis_net: 0 diff --git a/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_reduction.yaml b/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_reduction.yaml new file mode 100644 index 000000000..141c76bcc --- /dev/null +++ b/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_reduction.yaml @@ -0,0 +1,57 @@ +- name: Single Pensioner + period: 2023 + input: + gis_spa_category: "SINGLE_WITH_OAS" + gis_income: 224 + output: + gis_reduction: 100 + +- name: OAS pensioner whose spouse is SPA eligible, with combined income too low to trigger the plateau in the reduction + period: 2023 + input: + gis_spa_category: "COUPLE_ONE_OAS_SPA_ELIGIBLE" + gis_income: 2_048 + spouse_gis_income: 2_000 + gis_cap: 6_240.18 + output: + gis_reduction: 1_000 + +- name: Couple with combined income that exceeds the upper plateau threshold given by breakeven_spa_eligible. So the GIS reduction starts again after the plateau. + period: 2023 + absolute_error_margin: .01 + input: + gis_spa_category: "COUPLE_ONE_OAS_SPA_ELIGIBLE" + gis_income: 20_048 + spouse_gis_income: 20_000 + gis_cap: 30000 + output: + gis_reduction: 7487.68 + +- name: Two OAS pensioners + period: 2023 + input: + gis_spa_category: "COUPLE_BOTH_OAS" + gis_income: 2_048 + spouse_gis_income: 2_000 + output: + gis_reduction: 1_000 + +- name: OAS pensioner whose spouse is not eligible for SPA. + period: 2023 + input: + gis_spa_category: "COUPLE_ONE_OAS_SPA_INELIGIBLE" + gis_income: 2_024 + gis_cap: 6_240.18 + spouse_gis_income: 2_000 + output: + gis_reduction: 1_000 + +- name: Person who is ineligible for SPA, but is married to an OAS pensioner + period: 2023 + input: + gis_spa_category: "COUPLE_ONE_OAS_SPA_INELIGIBLE" + gis_income: 2_024 + gis_cap: 0 + spouse_gis_income: 2_000 + output: + gis_reduction: 0 diff --git a/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_reduction_spa_couple.yaml b/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_reduction_spa_couple.yaml new file mode 100644 index 000000000..5758d3b8f --- /dev/null +++ b/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_reduction_spa_couple.yaml @@ -0,0 +1,42 @@ +- name: Couple with combined income less than the plateau threshold. + period: 2023 + absolute_error_margin: .01 + input: + gis_spa_category: "COUPLE_ONE_OAS_SPA_ELIGIBLE" + gis_income: 2_048 + spouse_gis_income: 2_000 + output: + gis_reduction_spa_couple: 1_000 + +- name: Couple with combined income inside the plateau threshold. So the GIS reduction is frozen at the reduction you would have if your income were the crossover amount. + period: 2023 + absolute_error_margin: .01 + input: + gis_spa_category: "COUPLE_ONE_OAS_SPA_ELIGIBLE" + gis_income: 15_048 + spouse_gis_income: 15_000 + output: + gis_reduction_spa_couple: 6_469.86 + +- name: Couple with combined income that exceeds the upper plateau threshold given by breakeven_spa_eligible. So the GIS reduction starts again after the plateau. + period: 2023 + absolute_error_margin: .01 + input: + gis_spa_category: "COUPLE_ONE_OAS_SPA_ELIGIBLE" + gis_income: 20_048 + spouse_gis_income: 20_000 + output: + gis_reduction_spa_couple: 7487.68 + +- name: Automatically 0 if you're not the right gis-spa category + period: 2023 + absolute_error_margin: .01 + input: + gis_spa_category: "COUPLE_ONE_OAS_SPA_INELIGIBLE" + gis_income: 20_048 + spouse_gis_income: 20_000 + output: + gis_reduction_spa_couple: 0 + + + diff --git a/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_spa_category.yaml b/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_spa_category.yaml new file mode 100644 index 000000000..f586198a2 --- /dev/null +++ b/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_spa_category.yaml @@ -0,0 +1,84 @@ +- name: Single OAS Pensioner + period: 2022 + input: + is_married: false + oas_eligible: true + output: + gis_spa_category: "SINGLE_WITH_OAS" + +- name: Widow + period: 2022 + input: + is_widow: true + oas_eligible: false + adult_years_in_canada: 12 + age: 61 + output: + gis_spa_category: "WIDOW_SPA_ELIGIBLE" + +- name: Single OAS Pensioner, ineligible because not enough adult years in canada + period: 2022 + input: + is_widow: true + oas_eligible: false + adult_years_in_canada: 2 + age: 61 + output: + gis_spa_category: "NONE" + +- name: Couple, both are OAS eligible + period: 2022 + input: + is_married: true + people: + head: + age: 70 + oas_eligible: true + spouse: + age: 70 + oas_eligible: true + household: + is_married: true + members: [head, spouse] + output: + gis_spa_category: ["COUPLE_BOTH_OAS", "COUPLE_BOTH_OAS"] + +- name: Couple, elder is OAS eligible, younger is SPA eligible + period: 2022 + input: + is_married: true + people: + head: + is_head: true + age: 70 + oas_eligible: true + spouse: + is_spouse: true + age: 61 + oas_eligible: false + adult_years_in_canada: 20 + household: + is_married: true + members: [head, spouse] + output: + gis_spa_category: ["COUPLE_ONE_OAS_SPA_ELIGIBLE", "COUPLE_ONE_OAS_SPA_ELIGIBLE"] + +- name: Couple, elder is OAS eligible, younger is _not_ SPA eligible + period: 2022 + input: + is_married: true + people: + head: + is_head: true + age: 70 + oas_eligible: true + spouse: + is_spouse: true + age: 44 + spa_eligible: false + oas_eligible: false + household: + members: [head, spouse] + output: + gis_spa_category: ["COUPLE_ONE_OAS_SPA_INELIGIBLE", "COUPLE_ONE_OAS_SPA_INELIGIBLE"] + diff --git a/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_spa_couple_thresholds.yaml b/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_spa_couple_thresholds.yaml new file mode 100644 index 000000000..0f5d53dd1 --- /dev/null +++ b/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_spa_couple_thresholds.yaml @@ -0,0 +1,7 @@ +- name: This isn't a real test -- I just used it to calculate these amounts, to make sure they make sense. + period: 2023 + absolute_error_margin: .01 + output: + breakeven_spa_eligible: 35976.72 + breakeven_spa_ineligible: 46025.98 + gis_spa_crossover: 25927.45 diff --git a/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_topup_cap.yaml b/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_topup_cap.yaml new file mode 100644 index 000000000..90210ab41 --- /dev/null +++ b/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_topup_cap.yaml @@ -0,0 +1,54 @@ +- name: Single Pensioner + period: 2022 + absolute_error_margin: 0.01 + input: + gis_spa_category: "SINGLE_WITH_OAS" + oas_eligible: true + output: + gis_topup_cap: 875.70 + +- name: Widow + period: 2022 + absolute_error_margin: 0.01 + input: + gis_spa_category: "WIDOW_SPA_ELIGIBLE" + output: + gis_topup_cap: 875.70 + +- name: OAS pensioner whose spouse is SPA eligible + period: 2022 + absolute_error_margin: 0.01 + input: + gis_spa_category: "COUPLE_ONE_OAS_SPA_ELIGIBLE" + oas_eligible: true + output: + gis_topup_cap: 490.98 + +- name: OAS pensioner whose spouse is not SPA eligible + period: 2022 + absolute_error_margin: 0.01 + input: + gis_spa_category: "COUPLE_ONE_OAS_SPA_INELIGIBLE" + oas_eligible: true + output: + gis_topup_cap: 490.98 + +- name: SPA-eligible person whose spouse has OAS + period: 2022 + absolute_error_margin: 0.01 + input: + gis_spa_category: "COUPLE_ONE_OAS_SPA_ELIGIBLE" + oas_eligible: false + output: + gis_topup_cap: 490.98 + +- name: Person not eligible for OAS or SPA, but is married to an OAS pensioner + period: 2022 + absolute_error_margin: 0.1 + input: + gis_spa_category: "COUPLE_ONE_OAS_SPA_INELIGIBLE" + oas_eligible: false + output: + gis_topup_cap: 0 + + diff --git a/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_topup_reduction.yaml b/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_topup_reduction.yaml new file mode 100644 index 000000000..ba8fd65fb --- /dev/null +++ b/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_topup_reduction.yaml @@ -0,0 +1,41 @@ +- name: Single Pensioner + period: 2023 + input: + gis_spa_category: "SINGLE_WITH_OAS" + individual_net_income: 2_100 + output: + gis_topup_reduction: 25 + +- name: Widow + period: 2023 + input: + gis_spa_category: "SINGLE_WITH_OAS" + individual_net_income: 2_100 + output: + gis_topup_reduction: 25 + +- name: OAS pensioner whose spouse is SPA eligible + period: 2023 + input: + gis_spa_category: "COUPLE_ONE_OAS_SPA_ELIGIBLE" + individual_net_income: 4_100 + output: + gis_topup_reduction: 12.50 + +- name: Two OAS pensioners + period: 2023 + input: + gis_spa_category: "COUPLE_BOTH_OAS" + individual_net_income: 4_100 + output: + gis_topup_reduction: 12.50 + +- name: OAS pensioner whose spouse is not eligible for SPA. + period: 2023 + input: + gis_spa_category: "COUPLE_ONE_OAS_SPA_INELIGIBLE" + oas_eligible: True + individual_net_income: 4_100 + output: + gis_topup_reduction: 12.50 + diff --git a/policyengine_canada/tests/gov/cra/benefits/gis_spa/spa_eligible.yaml b/policyengine_canada/tests/gov/cra/benefits/gis_spa/spa_eligible.yaml new file mode 100644 index 000000000..fcb1e0591 --- /dev/null +++ b/policyengine_canada/tests/gov/cra/benefits/gis_spa/spa_eligible.yaml @@ -0,0 +1,32 @@ +- name: Is SPA eligible because spouse has OAS + period: 2022 + input: + age: 61 + adult_years_in_canada: 40 + is_widow: FALSE + oas_eligible: FALSE + spouse_oas_eligible: TRUE + output: + spa_eligible: TRUE + +- name: Is SPA eligible because is widow + period: 2022 + input: + age: 61 + adult_years_in_canada: 40 + is_widow: TRUE + oas_eligible: FALSE + spouse_oas_eligible: FALSE + output: + spa_eligible: TRUE + +- name: Isn't SPA eligible because, despite being a widow, not enough years in canada + period: 2022 + input: + age: 61 + adult_years_in_canada: 9 + is_widow: TRUE + oas_eligible: FALSE + spouse_oas_eligible: FALSE + output: + spa_eligible: FALSE diff --git a/policyengine_canada/tests/gov/cra/benefits/gis_spa/spa_gis_net.yaml b/policyengine_canada/tests/gov/cra/benefits/gis_spa/spa_gis_net.yaml new file mode 100644 index 000000000..2594bb094 --- /dev/null +++ b/policyengine_canada/tests/gov/cra/benefits/gis_spa/spa_gis_net.yaml @@ -0,0 +1,15 @@ +- name: Not a total reduction + period: 2022 + input: + spa_gis_portion: 2000 + spa_gis_reduction: 1000 + output: + spa_gis_net: 1000 + +- name: Total reduction + period: 2022 + input: + spa_gis_portion: 1000 + spa_gis_reduction: 2000 + output: + spa_gis_net: 0 diff --git a/policyengine_canada/tests/gov/cra/benefits/gis_spa/spa_gis_portion.yaml b/policyengine_canada/tests/gov/cra/benefits/gis_spa/spa_gis_portion.yaml new file mode 100644 index 000000000..f63162575 --- /dev/null +++ b/policyengine_canada/tests/gov/cra/benefits/gis_spa/spa_gis_portion.yaml @@ -0,0 +1,7 @@ +- name: Anyone who is SPA-eligible gets the full amount prior to reduction. + period: 2022 + input: + spa_eligible: TRUE + output: + spa_gis_portion: 6_240.18 + diff --git a/policyengine_canada/tests/gov/cra/benefits/gis_spa/spa_gis_reduction.yaml b/policyengine_canada/tests/gov/cra/benefits/gis_spa/spa_gis_reduction.yaml new file mode 100644 index 000000000..92180c916 --- /dev/null +++ b/policyengine_canada/tests/gov/cra/benefits/gis_spa/spa_gis_reduction.yaml @@ -0,0 +1,18 @@ +- name: The default rate of 25% of net household income + period: 2022 + input: + individual_net_income: 500 + spouse_net_income: 500 + spa_oas_net: 0 + output: + spa_gis_reduction: 250 + +- name: No reduction happens until the SPA OAS-portion is already reduced to 0 + period: 2022 + input: + individual_net_income: 500 + spouse_net_income: 500 + spa_oas_net: 100 + output: + spa_gis_reduction: 0 + diff --git a/policyengine_canada/tests/gov/cra/benefits/gis_spa/spa_oas_net.yaml b/policyengine_canada/tests/gov/cra/benefits/gis_spa/spa_oas_net.yaml new file mode 100644 index 000000000..2e6d6ec92 --- /dev/null +++ b/policyengine_canada/tests/gov/cra/benefits/gis_spa/spa_oas_net.yaml @@ -0,0 +1,15 @@ +- name: Not a total reduction + period: 2022 + input: + spa_oas_portion: 2000 + spa_oas_reduction: 1000 + output: + spa_oas_net: 1000 + +- name: Total reduction + period: 2022 + input: + spa_oas_portion: 1000 + spa_oas_reduction: 2000 + output: + spa_oas_net: 0 diff --git a/policyengine_canada/tests/gov/cra/benefits/gis_spa/spa_oas_portion.yaml b/policyengine_canada/tests/gov/cra/benefits/gis_spa/spa_oas_portion.yaml new file mode 100644 index 000000000..6baef2edc --- /dev/null +++ b/policyengine_canada/tests/gov/cra/benefits/gis_spa/spa_oas_portion.yaml @@ -0,0 +1,7 @@ +- name: Anyone who is SPA-eligible gets the full amount prior to reduction. + period: 2022 + input: + spa_eligible: TRUE + output: + spa_oas_portion: 8_226 + diff --git a/policyengine_canada/tests/gov/cra/benefits/gis_spa/spa_oas_reduction.yaml b/policyengine_canada/tests/gov/cra/benefits/gis_spa/spa_oas_reduction.yaml new file mode 100644 index 000000000..88bc4a651 --- /dev/null +++ b/policyengine_canada/tests/gov/cra/benefits/gis_spa/spa_oas_reduction.yaml @@ -0,0 +1,7 @@ +- name: The default rate of 75% of net household income after the first 48 dollars. + period: 2023 + input: + individual_net_income: 1048 + output: + spa_oas_reduction: 750 + diff --git a/policyengine_canada/tests/gov/cra/benefits/gis_spa/spouse_oas_eligible.yaml b/policyengine_canada/tests/gov/cra/benefits/gis_spa/spouse_oas_eligible.yaml new file mode 100644 index 000000000..be0c14004 --- /dev/null +++ b/policyengine_canada/tests/gov/cra/benefits/gis_spa/spouse_oas_eligible.yaml @@ -0,0 +1,29 @@ +- name: Both are eligible + period: 2022 + input: + people: + head: + is_head: true + oas_eligible: true + spouse: + is_spouse: true + oas_eligible: true + household: + members: [head, spouse] + output: + spouse_oas_eligible: [true, true] + +- name: Spouse is not SPA eligible + period: 2022 + input: + people: + head: + is_head: true + oas_eligible: true + spouse: + is_spouse: true + oas_eligible: false + household: + members: [head, spouse] + output: + spouse_oas_eligible: [false, true] diff --git a/policyengine_canada/tests/gov/cra/benefits/gis_spa/spouse_spa_eligible.yaml b/policyengine_canada/tests/gov/cra/benefits/gis_spa/spouse_spa_eligible.yaml new file mode 100644 index 000000000..2533c644e --- /dev/null +++ b/policyengine_canada/tests/gov/cra/benefits/gis_spa/spouse_spa_eligible.yaml @@ -0,0 +1,30 @@ +- name: Both are eligible + period: 2022 + input: + people: + head: + is_head: true + spa_eligible: true + spouse: + is_spouse: true + spa_eligible: true + household: + members: [head, spouse] + output: + spouse_spa_eligible: [true, true] + +- name: Spouse is not SPA eligible + period: 2022 + input: + people: + head: + is_head: true + spa_eligible: true + spouse: + is_spouse: true + spa_eligible: false + household: + members: [head, spouse] + output: + spouse_spa_eligible: [false, true] + diff --git a/policyengine_canada/tests/gov/cra/benefits/oas/integration.yaml b/policyengine_canada/tests/gov/cra/benefits/oas/integration.yaml index 05b993c6f..82c440cdf 100644 --- a/policyengine_canada/tests/gov/cra/benefits/oas/integration.yaml +++ b/policyengine_canada/tests/gov/cra/benefits/oas/integration.yaml @@ -5,7 +5,7 @@ adult_years_in_canada: 47 individual_net_income: 91_761 output: - oas_eligibility: TRUE + oas_eligible: TRUE oas_pre_repayment: 8_226 oas_repayment: 1_500 oas_net: 6_726 @@ -17,7 +17,7 @@ adult_years_in_canada: 47 individual_net_income: 40_000 output: - oas_eligibility: TRUE + oas_eligible: TRUE oas_pre_repayment: 8_226 oas_repayment: 0 oas_net: 8_226 @@ -29,7 +29,7 @@ adult_years_in_canada: 20 individual_net_income: 10_000_000 output: - oas_eligibility: TRUE + oas_eligible: TRUE oas_pre_repayment: 4_113 oas_repayment: 4_113 oas_net: 0 diff --git a/policyengine_canada/tests/gov/cra/benefits/oas/oas_eligibility.yaml b/policyengine_canada/tests/gov/cra/benefits/oas/oas_eligibility.yaml index 1a73764cd..f16905ef3 100644 --- a/policyengine_canada/tests/gov/cra/benefits/oas/oas_eligibility.yaml +++ b/policyengine_canada/tests/gov/cra/benefits/oas/oas_eligibility.yaml @@ -4,7 +4,7 @@ age: 66 adult_years_in_canada: 47 output: - oas_eligibility: TRUE + oas_eligible: TRUE - name: 65 year old but with only 5 years of residency, so not eligible period: 2022 @@ -12,7 +12,7 @@ age: 65 adult_years_in_canada: 5 output: - oas_eligibility: FALSE + oas_eligible: FALSE - name: only 64 years old so not eligible period: 2022 @@ -20,4 +20,4 @@ age: 64 adult_years_in_canada: 46 output: - oas_eligibility: FALSE + oas_eligible: FALSE diff --git a/policyengine_canada/variables/gov/cra/benefits/gis_spa/breakeven-spa-eligible.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/breakeven-spa-eligible.py new file mode 100644 index 000000000..03b60fb40 --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/breakeven-spa-eligible.py @@ -0,0 +1,26 @@ +from policyengine_canada.model_api import * + + +class breakeven_spa_eligible(Variable): + value_type = float + entity = State + label = "Breakeven household income for SPA-eligible couples" + unit = CAD + documentation = "The level of family income at which the combination of GIS and SPA benefits have been reduced to exactly zero, which depends on the gis_spa_category of the household. We need this in order to add the 'crossover' part of the GIS reduction, as explained on page 65 of the SPSD/M Algorithm Guide. Corresponds to GISBE2 in the SPSD/M." + definition_period = YEAR + + def formula(person, period, parameters): + p_gis_spa = parameters(period).gov.cra.benefits.gis_spa + p_oas = parameters(period).gov.cra.benefits.old_age_security_pension + + return ( + ( + (2 * p_gis_spa.gis_cap.two_pensioners) + / (2 * p_gis_spa.gis_reduction.two_pensioners.rates[1]) + ) + + ( + p_oas.amount.base + / p_gis_spa.spa_reduction.spa_oas_reduction.rates[1] + ) + + p_gis_spa.gis_reduction.two_pensioners.thresholds[1] + ) diff --git a/policyengine_canada/variables/gov/cra/benefits/gis_spa/breakeven-spa-ineligible.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/breakeven-spa-ineligible.py new file mode 100644 index 000000000..a51ace6d6 --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/breakeven-spa-ineligible.py @@ -0,0 +1,23 @@ +from policyengine_canada.model_api import * + + +class breakeven_spa_ineligible(Variable): + value_type = float + entity = State + label = "Breakeven household income for SPA-ineligible couples" + unit = CAD + documentation = "The level of family income at which the combination of GIS and SPA benefits have been reduced to exactly zero, which depends on the gis_spa_category of the household. We need this in order to add the 'crossover' part of the GIS reduction, as explained on page 65 of the SPSD/M Algorithm Guide. Corresponds to GISBE1 in the SPSD/M." + definition_period = YEAR + + def formula(person, period, parameters): + p_gis_spa = parameters(period).gov.cra.benefits.gis_spa + p_oas = parameters(period).gov.cra.benefits.old_age_security_pension + + return ( + ( + p_gis_spa.gis_cap.one_pensioner + / p_gis_spa.gis_reduction.two_pensioners.rates[1] + ) + + p_oas.amount.base + + p_gis_spa.gis_reduction.one_pensioner.rates[1] + ) diff --git a/policyengine_canada/variables/gov/cra/benefits/gis_spa/eligibility/spa_eligible.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/eligibility/spa_eligible.py new file mode 100644 index 000000000..56eb2613c --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/eligibility/spa_eligible.py @@ -0,0 +1,27 @@ +from policyengine_canada.model_api import * + + +class spa_eligible(Variable): + value_type = bool + entity = Person + label = "Person is eligible for the Spousal Allowance" + definition_period = YEAR + + def formula(person, period, parameters): + p = parameters(period).gov.cra.benefits.gis_spa + age = person("age", period) + adult_years_in_canada = person("adult_years_in_canada", period) + widow = person("is_widow", period) + oas_eligible = person("oas_eligible", period) + spouse_oas_eligible = person("spouse_oas_eligible", period) + meets_residency_requirement = ( + adult_years_in_canada >= p.spa_residency_requirement + ) + return ( + ~oas_eligible + & meets_residency_requirement + & ( + (widow & (age >= p.spa_widows_eligibility_age)) + | (spouse_oas_eligible & (age >= p.spa_spouse_eligibility_age)) + ) + ) diff --git a/policyengine_canada/variables/gov/cra/benefits/gis_spa/eligibility/spouse_oas_eligible.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/eligibility/spouse_oas_eligible.py new file mode 100644 index 000000000..6bfac5f57 --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/eligibility/spouse_oas_eligible.py @@ -0,0 +1,21 @@ +from policyengine_canada.model_api import * + + +class spouse_oas_eligible(Variable): + value_type = bool + entity = Person + label = "Spouse is eligible for the Old Age Security Pension" + definition_period = YEAR + + def formula(person, period, parameters): + oas_eligible = person("oas_eligible", period) + head_or_spouse = person("is_head", period) | person( + "is_spouse", period + ) + head_or_spouse_oas_eligible = head_or_spouse & oas_eligible + household = person.household + count_head_or_spouse_oas_eligible = household.sum( + head_or_spouse_oas_eligible + ) + + return count_head_or_spouse_oas_eligible - where(oas_eligible, 1, 0) diff --git a/policyengine_canada/variables/gov/cra/benefits/gis_spa/eligibility/spouse_spa_eligible.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/eligibility/spouse_spa_eligible.py new file mode 100644 index 000000000..39ad1f9a1 --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/eligibility/spouse_spa_eligible.py @@ -0,0 +1,21 @@ +from policyengine_canada.model_api import * + + +class spouse_spa_eligible(Variable): + value_type = bool + entity = Person + label = "Spouse is eligible for the Spousal Allowance" + definition_period = YEAR + + def formula(person, period, parameters): + spa_eligible = person("spa_eligible", period) + head_or_spouse = person("is_head", period) | person( + "is_spouse", period + ) + head_or_spouse_spa_eligible = head_or_spouse & spa_eligible + household = person.household + count_head_or_spouse_spa_eligible = household.sum( + head_or_spouse_spa_eligible + ) + + return count_head_or_spouse_spa_eligible - where(spa_eligible, 1, 0) diff --git a/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_cap.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_cap.py new file mode 100644 index 000000000..b15e9c22c --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_cap.py @@ -0,0 +1,39 @@ +from policyengine_canada.model_api import * + + +class gis_cap(Variable): + value_type = float + entity = Person + label = "Guaranteed Income Supplement Maximum Amount" + unit = CAD + documentation = "The highest GIS amount an individual could receive, based on the number of pensioners in the household, before the amount is reduced based on income. Equivalent of imgismax in the SPSD/M variable guide." + definition_period = YEAR + + def formula(person, period, parameters): + gis_spa_category = person("gis_spa_category", period) + gis_spa_categories = gis_spa_category.possible_values + oas_eligible = person("oas_eligible", period) + p = parameters(period).gov.cra.benefits.gis_spa.gis_cap + return select( + [ + gis_spa_category == gis_spa_categories.SINGLE_WITH_OAS, + gis_spa_category == gis_spa_categories.COUPLE_BOTH_OAS, + ( + gis_spa_category + == gis_spa_categories.COUPLE_ONE_OAS_SPA_ELIGIBLE + ) + & oas_eligible, # the oas_eligible makes sure this person is the eligible one in the couple, since both people in the couple will have the same category. + ( + gis_spa_category + == gis_spa_categories.COUPLE_ONE_OAS_SPA_INELIGIBLE + ) + & oas_eligible, + ], + [ + p.one_pensioner, + p.two_pensioners, + p.two_pensioners, + p.one_pensioner, + ], + default=0, + ) diff --git a/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_exemption.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_exemption.py new file mode 100644 index 000000000..2887067b8 --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_exemption.py @@ -0,0 +1,19 @@ +from policyengine_canada.model_api import * + + +class gis_exemption(Variable): + value_type = float + entity = Person + label = "GIS Exemption" + unit = CAD + documentation = "Income exempt from GIS reductions" + definition_period = YEAR + + def formula(person, period, parameters): + p = parameters(period).gov.cra.benefits.gis_spa.gis_exemption + employment_income = person("employment_income", period) + self_employment_income = person("self_employment_income", period) + exemption = p.gis_exemption.calc( + employment_income - self_employment_income + ) + return exemption diff --git a/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_income.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_income.py new file mode 100644 index 000000000..de447c746 --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_income.py @@ -0,0 +1,17 @@ +from policyengine_canada.model_api import * + + +class gis_income(Variable): + value_type = float + entity = Person + label = "Employment income" + unit = CAD + documentation = "Income after GIS income exemptions, for use in calculating a person's GIS reduction. SPSD/M 29.0 Parameter Guide pg 368: the full definition of earnings for determining GIS include wages and salaries (idiemp), less other employment expenses (idalexp), clergy residence deduction (idclergy), and non-negative self-employment earnings (idise)" + definition_period = YEAR + + def formula(person, period, parameters): + p = parameters(period).gov.cra.benefits.gis_spa.gis_exemption + employment_income = person("employment_income", period) + self_employment_income = person("self_employment_income", period) + exemption = person("gis_exemption", period) + return max(employment_income - self_employment_income - exemption, 0) diff --git a/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_net.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_net.py new file mode 100644 index 000000000..18398496b --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_net.py @@ -0,0 +1,18 @@ +from policyengine_canada.model_api import * + + +class gis_net(Variable): + value_type = float + entity = Person + label = "Net Guaranteed Income Supplement" + unit = CAD + documentation = ( + "Highest GIS a person is eligible for, minus their reduction. " + ) + definition_period = YEAR + + def formula(person, period, parameters): + gis_cap = person("gis_cap", period) + gis_reduction = person("gis_reduction", period) + + return max(gis_cap - gis_reduction, 0) diff --git a/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_reduction.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_reduction.py new file mode 100644 index 000000000..07d6631a5 --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_reduction.py @@ -0,0 +1,48 @@ +from policyengine_canada.model_api import * + + +class gis_reduction(Variable): + value_type = float + entity = Person + label = "Guaranteed Income Supplement Reduction" + unit = CAD + documentation = "The amount by which the base GIS is reduced, based on personal net income and the number of pensioners in the household. Equivalent of imgismax in the SPSD/M variable guide." + definition_period = YEAR + + def formula(person, period, parameters): + household = person.household + gis_spa_category = person("gis_spa_category", period) + gis_spa_categories = gis_spa_category.possible_values + gis_income = person("gis_income", period) + spouse_gis_income = household("spouse_gis_income", period) + gis_base = person("gis_cap", period) + gis_reduction_spa_couple = person("gis_reduction_spa_couple", period) + p = parameters(period).gov.cra.benefits.gis_spa.gis_reduction + + reduction = select( + [ + gis_spa_category == gis_spa_categories.SINGLE_WITH_OAS, + gis_spa_category == gis_spa_categories.COUPLE_BOTH_OAS, + ( + gis_spa_category + == gis_spa_categories.COUPLE_ONE_OAS_SPA_ELIGIBLE + ) + & ( + gis_base > 0 + ), # the gis_base > 0 makes sure this person is the eligible one in the couple, since both people in the couple will have the same category. + ( + gis_spa_category + == gis_spa_categories.COUPLE_ONE_OAS_SPA_INELIGIBLE + ) + & (gis_base > 0), + ], + [ + p.one_pensioner.calc(gis_income), + p.two_pensioners.calc(gis_income + spouse_gis_income), + gis_reduction_spa_couple, + p.one_pensioner.calc(gis_income), + ], + default=0, + ) + + return reduction diff --git a/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_reduction_spa_couple.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_reduction_spa_couple.py new file mode 100644 index 000000000..0b4f62c95 --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_reduction_spa_couple.py @@ -0,0 +1,44 @@ +from policyengine_canada.model_api import * + + +class gis_reduction_spa_couple(Variable): + value_type = float + entity = Person + label = "Guaranteed Income Supplement Reduction for a Recipient with SPA-eligible Spouse" + unit = CAD + documentation = "The amount by which the base GIS is reduced, based on personal net income and the number of pensioners in the household. This is implemented as a variable, not a parameter, due to the complexity and dependencies of the reduction rate." + definition_period = YEAR + + def formula(person, period, parameters): + household = person.household + gis_spa_category = person("gis_spa_category", period) + gis_spa_categories = gis_spa_category.possible_values + gis_income = person("gis_income", period) + spouse_gis_income = household("spouse_gis_income", period) + combined_gis_income = gis_income + spouse_gis_income + state = person.state + crossover = state("gis_spa_crossover", period) + breakeven_spa_eligible = state("breakeven_spa_eligible", period) + breakeven_spa_ineligible = state("breakeven_spa_ineligible", period) + plateau_range = breakeven_spa_eligible - crossover + p = parameters(period).gov.cra.benefits.gis_spa.gis_reduction + + # Reduce like other pensioner couples up to the crossover point + first_part = min( + p.two_pensioners.calc(combined_gis_income), + p.two_pensioners.calc(crossover), + ) + # Plateau until the spouse's SPA-gis-portion is exhausted, as captured by the variable plateau_range. The first part must be maxed-out for this to be non-zero. + second_part = min( + (combined_gis_income - first_part) * p.gis_spa_couple_plateau_rate, + plateau_range * p.gis_spa_couple_plateau_rate, + ) * (first_part == (p.two_pensioners.calc(crossover))) + # Start reducing again at the married couple breakeven point for income beyond the crossover maximum. Only be non-zero if there's any income in that range. + third_part = p.two_pensioners.calc( + combined_gis_income - (crossover - 48) - plateau_range + ) * ((combined_gis_income > (crossover - plateau_range))) + + # return(third_part) + return (first_part + second_part + third_part) * ( + gis_spa_category == gis_spa_categories.COUPLE_ONE_OAS_SPA_ELIGIBLE + ) diff --git a/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_topup_cap.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_topup_cap.py new file mode 100644 index 000000000..f47215031 --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_topup_cap.py @@ -0,0 +1,40 @@ +from policyengine_canada.model_api import * + + +class gis_topup_cap(Variable): + value_type = float + entity = Person + label = "Guaranteed Income Supplement Top-up Maximum Value" + unit = CAD + documentation = "The highest GIS Top-up amount an individual could receive, based on the number of pensioners in the household, before the amount is reduced based on income. Roughly equivalent to imigistop in the SPSD/M variable guide." + definition_period = YEAR + + def formula(person, period, parameters): + gis_spa_category = person("gis_spa_category", period) + gis_spa_categories = gis_spa_category.possible_values + oas_eligible = person("oas_eligible", period) + p = parameters(period).gov.cra.benefits.gis_spa.topup_cap + return select( + [ + gis_spa_category == gis_spa_categories.SINGLE_WITH_OAS, + gis_spa_category == gis_spa_categories.WIDOW_SPA_ELIGIBLE, + gis_spa_category == gis_spa_categories.COUPLE_BOTH_OAS, + ( + gis_spa_category + == gis_spa_categories.COUPLE_ONE_OAS_SPA_ELIGIBLE + ), + ( + gis_spa_category + == gis_spa_categories.COUPLE_ONE_OAS_SPA_INELIGIBLE + ) + & oas_eligible, # the oas_eligible makes sure this person is the eligible one in the couple, since both people in the couple will have the same category. + ], + [ + p.singles, + p.singles, + p.married, + p.married, + p.married, + ], + default=0, + ) diff --git a/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_topup_net.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_topup_net.py new file mode 100644 index 000000000..eb9809144 --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_topup_net.py @@ -0,0 +1,18 @@ +from policyengine_canada.model_api import * + + +class gis_topup_net(Variable): + value_type = float + entity = Person + label = "Net Guaranteed Income Supplement Top-Up" + unit = CAD + documentation = ( + "Highest GIS Topup a person is eligible for, minus their reduction. " + ) + definition_period = YEAR + + def formula(person, period, parameters): + gis_topup_cap = person("gis_topup_cap", period) + gis_topup_reduction = person("gis_topup_reduction", period) + + return max(gis_topup_cap - gis_topup_reduction, 0) diff --git a/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_topup_reduction.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_topup_reduction.py new file mode 100644 index 000000000..a203c25ff --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_topup_reduction.py @@ -0,0 +1,41 @@ +from policyengine_canada.model_api import * + + +class gis_topup_reduction(Variable): + value_type = float + entity = Person + label = "Guaranteed Income Supplement Top-up Reduction" + unit = CAD + documentation = "The amount reduced from a person's maximum GIS top-up, based on income." + definition_period = YEAR + + def formula(person, period, parameters): + gis_spa_category = person("gis_spa_category", period) + gis_spa_categories = gis_spa_category.possible_values + oas_eligible = person("oas_eligible", period) + individual_net_income = person("individual_net_income", period) + p = parameters(period).gov.cra.benefits.gis_spa.topup_reduction + return select( + [ + gis_spa_category == gis_spa_categories.SINGLE_WITH_OAS, + gis_spa_category == gis_spa_categories.WIDOW_SPA_ELIGIBLE, + gis_spa_category == gis_spa_categories.COUPLE_BOTH_OAS, + ( + gis_spa_category + == gis_spa_categories.COUPLE_ONE_OAS_SPA_ELIGIBLE + ), + ( + gis_spa_category + == gis_spa_categories.COUPLE_ONE_OAS_SPA_INELIGIBLE + ) + & oas_eligible, # the oas_eligible makes sure this person is the eligible one in the couple, since both people in the couple will have the same category. + ], + [ + p.singles.calc(individual_net_income), + p.singles.calc(individual_net_income), + p.married.calc(individual_net_income), + p.married.calc(individual_net_income), + p.married.calc(individual_net_income), + ], + default=0, + ) diff --git a/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/spouse_gis_income.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/spouse_gis_income.py new file mode 100644 index 000000000..232ff4c63 --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/spouse_gis_income.py @@ -0,0 +1,16 @@ +from policyengine_canada.model_api import * + + +class spouse_gis_income(Variable): + value_type = int + entity = Household + label = "Spouse's Individual Net Income" + unit = CAD + documentation = "Spouse's Individual Net Income" + definition_period = YEAR + + def formula(household, period, parameters): + person = household.members + gis_income = person("gis_income", period) + spouse = person("is_spouse", period) + return household.max(gis_income * spouse) diff --git a/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_spa_category.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_spa_category.py new file mode 100644 index 000000000..628530d88 --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_spa_category.py @@ -0,0 +1,59 @@ +from policyengine_canada.model_api import * + + +# Categories as listed in SPSD/M variable 'imoldtyp' +class GISSPACategory(Enum): + NONE = "none" + SINGLE_WITH_OAS = "single_with_oas" + WIDOW_SPA_ELIGIBLE = "widow_spa_eligible" + COUPLE_BOTH_OAS = "couple_both_oas" + COUPLE_ONE_OAS_SPA_ELIGIBLE = "couple_one_oas_spa_eligible" + COUPLE_ONE_OAS_SPA_INELIGIBLE = "couple_one_oas_spa_ineligible" + + +class gis_spa_category(Variable): + value_type = Enum + entity = Person + possible_values = GISSPACategory + default_value = GISSPACategory.NONE + definition_period = YEAR + + def formula(person, period, parameters): + household = person.household + age = person("age", period) + age_spouse = household("age_spouse", period) + married = household("is_married", period) + widow = person("is_widow", period) + oas_eligible = person("oas_eligible", period) + spa_eligible = person("spa_eligible", period) + spouse_oas_eligible = person("spouse_oas_eligible", period) + spouse_spa_eligible = person("spouse_spa_eligible", period) + return select( + [ + ~married & oas_eligible, + # The law specifies widows aged 60-64, but that cutoff of 64 is just because at 65 you become eligible for OAS. I just reference OAS eligibilty directly. + widow & ~married & ~oas_eligible & spa_eligible, + married & oas_eligible & spouse_oas_eligible, + married + & ( + (oas_eligible & ~spouse_oas_eligible & spouse_spa_eligible) + | (~oas_eligible & spouse_oas_eligible & spa_eligible) + ), + married + & ( + ( + oas_eligible + & ~spouse_oas_eligible + & ~spouse_spa_eligible + ) + | (~oas_eligible & spouse_oas_eligible & ~spa_eligible) + ), + ], + [ + GISSPACategory.SINGLE_WITH_OAS, + GISSPACategory.WIDOW_SPA_ELIGIBLE, + GISSPACategory.COUPLE_BOTH_OAS, + GISSPACategory.COUPLE_ONE_OAS_SPA_ELIGIBLE, + GISSPACategory.COUPLE_ONE_OAS_SPA_INELIGIBLE, + ], + ) diff --git a/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_spa_crossover.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_spa_crossover.py new file mode 100644 index 000000000..f0f8eb9e9 --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_spa_crossover.py @@ -0,0 +1,16 @@ +from policyengine_canada.model_api import * + + +class gis_spa_crossover(Variable): + value_type = float + entity = State + label = "GIS-SPA couple crossover" + unit = CAD + documentation = "Corresponds to SPAXO in the SPSD/M parameter guide. This is the point at which a single GIS pensioner would receive a higher benefit than a couple with combined GIS/SPA, creating the incentive for a couple to just not apply for the SPA. See page 65 of the SPSD/M 29.0 algorithm guide." + definition_period = YEAR + + def formula(state, period, parameters): + breakeven_spa_eligible = state("breakeven_spa_eligible", period) + breakeven_spa_ineligible = state("breakeven_spa_ineligible", period) + + return 2 * breakeven_spa_eligible - breakeven_spa_ineligible diff --git a/policyengine_canada/variables/gov/cra/benefits/gis_spa/spa/spa_gis_net.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/spa/spa_gis_net.py new file mode 100644 index 000000000..a6d82a580 --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/spa/spa_gis_net.py @@ -0,0 +1,18 @@ +from policyengine_canada.model_api import * + + +class spa_gis_net(Variable): + value_type = float + entity = Person + label = "Net GIS-portion of the SPA" + unit = CAD + documentation = ( + "An individual's full GIS-portion of the SPA, minus their reduction." + ) + definition_period = YEAR + + def formula(person, period, parameters): + spa_gis_portion = person("spa_gis_portion", period) + spa_gis_reduction = person("spa_gis_reduction", period) + + return max(spa_gis_portion - spa_gis_reduction, 0) diff --git a/policyengine_canada/variables/gov/cra/benefits/gis_spa/spa/spa_gis_portion.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/spa/spa_gis_portion.py new file mode 100644 index 000000000..c4745b011 --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/spa/spa_gis_portion.py @@ -0,0 +1,18 @@ +from policyengine_canada.model_api import * + + +class spa_gis_portion(Variable): + value_type = float + entity = Person + label = "Spousal Allowance GIS-equivalence portion" + unit = CAD + documentation = "The 'GIS-portion' of the SPA program. Set to the value of the GIS at the married rate by default." + definition_period = YEAR + + def formula(person, period, parameters): + spa_eligible = person("spa_eligible", period) + base_amount = parameters( + period + ).gov.cra.benefits.gis_spa.spa_cap.gis_portion + + return base_amount * spa_eligible diff --git a/policyengine_canada/variables/gov/cra/benefits/gis_spa/spa/spa_gis_reduction.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/spa/spa_gis_reduction.py new file mode 100644 index 000000000..44c0825b6 --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/spa/spa_gis_reduction.py @@ -0,0 +1,25 @@ +from policyengine_canada.model_api import * + + +class spa_gis_reduction(Variable): + value_type = float + entity = Person + label = "Reduction of the Spousal Allowance GIS-equivalence portion" + unit = CAD + documentation = "Reduction of the 'GIS-portion' of the GIS program." + definition_period = YEAR + + def formula(person, period, parameters): + household = person.household + individual_net_income = person("individual_net_income", period) + spouse_net_income = household("spouse_net_income", period) + # Note that the GIS portion doesn't start getting reduced until the OAS portion is first reduced to 0. + spa_oas_net = person("spa_oas_net", period) + + p = parameters(period).gov.cra.benefits.gis_spa.spa_reduction + + return ( + (individual_net_income + spouse_net_income) + * p.spa_gis_reduction + * (spa_oas_net == 0) + ) diff --git a/policyengine_canada/variables/gov/cra/benefits/gis_spa/spa/spa_oas_net.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/spa/spa_oas_net.py new file mode 100644 index 000000000..3f8843b02 --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/spa/spa_oas_net.py @@ -0,0 +1,18 @@ +from policyengine_canada.model_api import * + + +class spa_oas_net(Variable): + value_type = float + entity = Person + label = "Net OAS-portion of the SPA" + unit = CAD + documentation = ( + "An individual's full OAS-portion of the SPA, minus their reduction." + ) + definition_period = YEAR + + def formula(person, period, parameters): + spa_oas_portion = person("spa_oas_portion", period) + spa_oas_reduction = person("spa_oas_reduction", period) + + return max(spa_oas_portion - spa_oas_reduction, 0) diff --git a/policyengine_canada/variables/gov/cra/benefits/gis_spa/spa/spa_oas_portion.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/spa/spa_oas_portion.py new file mode 100644 index 000000000..aae1d08d7 --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/spa/spa_oas_portion.py @@ -0,0 +1,18 @@ +from policyengine_canada.model_api import * + + +class spa_oas_portion(Variable): + value_type = float + entity = Person + label = "Spousal Allowance OAS-equivalence portion" + unit = CAD + documentation = "The 'OAS-portion' of the SPA program." + definition_period = YEAR + + def formula(person, period, parameters): + spa_eligible = person("spa_eligible", period) + base_amount = parameters( + period + ).gov.cra.benefits.gis_spa.spa_cap.oas_portion + + return base_amount * spa_eligible diff --git a/policyengine_canada/variables/gov/cra/benefits/gis_spa/spa/spa_oas_reduction.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/spa/spa_oas_reduction.py new file mode 100644 index 000000000..4367edcc7 --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/spa/spa_oas_reduction.py @@ -0,0 +1,20 @@ +from policyengine_canada.model_api import * + + +class spa_oas_reduction(Variable): + value_type = float + entity = Person + label = "Reduction of the Spousal Allowance OAS-equivalence portion" + unit = CAD + documentation = "Reduction of the 'OAS-portion' of the SPA program." + definition_period = YEAR + + def formula(person, period, parameters): + household = person.household + individual_net_income = person("individual_net_income", period) + spouse_net_income = household("spouse_net_income", period) + p = parameters(period).gov.cra.benefits.gis_spa.spa_reduction + + return p.spa_oas_reduction.calc( + individual_net_income + spouse_net_income + ) diff --git a/policyengine_canada/variables/gov/cra/benefits/old_age_security_pension/oas_eligibility.py b/policyengine_canada/variables/gov/cra/benefits/old_age_security_pension/oas_eligibile.py similarity index 95% rename from policyengine_canada/variables/gov/cra/benefits/old_age_security_pension/oas_eligibility.py rename to policyengine_canada/variables/gov/cra/benefits/old_age_security_pension/oas_eligibile.py index 7c0a29a48..1cbfc55f0 100644 --- a/policyengine_canada/variables/gov/cra/benefits/old_age_security_pension/oas_eligibility.py +++ b/policyengine_canada/variables/gov/cra/benefits/old_age_security_pension/oas_eligibile.py @@ -1,7 +1,7 @@ from policyengine_canada.model_api import * -class oas_eligibility(Variable): +class oas_eligible(Variable): value_type = bool entity = Person label = "Old age security pension eligibility" diff --git a/policyengine_canada/variables/gov/cra/benefits/old_age_security_pension/oas_pre_repayment.py b/policyengine_canada/variables/gov/cra/benefits/old_age_security_pension/oas_pre_repayment.py index b49374fbc..a82fcdca8 100644 --- a/policyengine_canada/variables/gov/cra/benefits/old_age_security_pension/oas_pre_repayment.py +++ b/policyengine_canada/variables/gov/cra/benefits/old_age_security_pension/oas_pre_repayment.py @@ -14,7 +14,7 @@ class oas_pre_repayment(Variable): def formula(person, period, parameters): age = person("age", period) adult_years_in_canada = person("adult_years_in_canada", period) - oas_eligible = person("oas_eligibility", period) + oas_eligible = person("oas_eligible", period) p = parameters(period).gov.cra.benefits.old_age_security_pension # Age at which you get the percentage boost. older_increase_age_threshold = p.eligibility.age.older_seniors_increase diff --git a/policyengine_canada/variables/household/income/individual/spouse_net_income.py b/policyengine_canada/variables/household/income/individual/spouse_net_income.py new file mode 100644 index 000000000..92b8cd793 --- /dev/null +++ b/policyengine_canada/variables/household/income/individual/spouse_net_income.py @@ -0,0 +1,16 @@ +from policyengine_canada.model_api import * + + +class spouse_net_income(Variable): + value_type = int + entity = Household + label = "Spouse's Individual Net Income" + unit = CAD + documentation = "Spouse's Individual Net Income" + definition_period = YEAR + + def formula(household, period, parameters): + person = household.members + individual_net_income = person("individual_net_income", period) + spouse = person("is_spouse", period) + return household.max(individual_net_income * spouse) diff --git a/policyengine_canada/variables/household/person/adult_years_in_canada_spouse.py b/policyengine_canada/variables/household/person/adult_years_in_canada_spouse.py new file mode 100644 index 000000000..3c4580445 --- /dev/null +++ b/policyengine_canada/variables/household/person/adult_years_in_canada_spouse.py @@ -0,0 +1,16 @@ +from policyengine_canada.model_api import * + + +class adult_years_in_canada_spouse(Variable): + value_type = int + entity = Household + label = "Spouse's Adult Years In Canada" + unit = CAD + documentation = "Spouse's years spent in Canada since the age of 18" + definition_period = YEAR + + def formula(household, period, parameters): + person = household.members + adult_years_in_canada = person("adult_years_in_canada", period) + spouse = person("is_spouse", period) + return household.max(adult_years_in_canada * spouse) diff --git a/policyengine_canada/variables/household/person/age_spouse.py b/policyengine_canada/variables/household/person/age_spouse.py new file mode 100644 index 000000000..2894447bc --- /dev/null +++ b/policyengine_canada/variables/household/person/age_spouse.py @@ -0,0 +1,16 @@ +from policyengine_canada.model_api import * + + +class age_spouse(Variable): + value_type = int + entity = Household + label = "Spouse's Age" + unit = CAD + documentation = "Age of spouse in years since birth" + definition_period = YEAR + + def formula(household, period, parameters): + person = household.members + age = person("age", period) + spouse = person("is_spouse", period) + return household.max(age * spouse) diff --git a/policyengine_canada/variables/household/person/is_widow.py b/policyengine_canada/variables/household/person/is_widow.py new file mode 100644 index 000000000..d540b3352 --- /dev/null +++ b/policyengine_canada/variables/household/person/is_widow.py @@ -0,0 +1,9 @@ +from policyengine_canada.model_api import * + + +class is_widow(Variable): + value_type = bool + entity = Person + label = "Widow or widower" + documentation = "Spouse or common-law partner has died and since their death the person has not remarried or become a common-law partner to another person" + definition_period = YEAR