From 7bcbf4452f58cef2ee6ff0f0d610961548252fb0 Mon Sep 17 00:00:00 2001 From: Alex Rand Date: Thu, 12 Jan 2023 10:49:41 -0500 Subject: [PATCH 01/24] initial commit --- .../gov/cra/benefits/gis/gis_eligible | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 policyengine_canada/variables/gov/cra/benefits/gis/gis_eligible diff --git a/policyengine_canada/variables/gov/cra/benefits/gis/gis_eligible b/policyengine_canada/variables/gov/cra/benefits/gis/gis_eligible new file mode 100644 index 000000000..0a198b437 --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis/gis_eligible @@ -0,0 +1,18 @@ +from policyengine_canada.model_api import * + +class gis_eligible(Variable): + value_type = bool + entity = Person + label = "Eligible for guaranteed income supplement" + definition_period = YEAR + + def formula(person, period, parameters): + p = parameters(period).gov.cra.benefits.gis + age = person("age", period) + # using the pre-repayment amount here because the documentation says "receives the OAS". + oas = person("old_age_security_pension_pre_repayment", period) + income = + + # MAKE A 'type of household' variable!! + # MAKE A 'maximum amount' parameter based on 'type of household'!! + # MAKE A 'reduction rate' parameter based on 'type of household'? Copy GISRLS for example. From 5df6bd15ef97431069dd684502e85c060dbdc429 Mon Sep 17 00:00:00 2001 From: Alex Rand Date: Fri, 13 Jan 2023 10:15:55 -0500 Subject: [PATCH 02/24] add gis_category template --- .../gov/cra/benefits/gis/gis_category.py | 28 +++++++++++++++++++ .../gis/{gis_eligible => gis_eligible.py} | 0 2 files changed, 28 insertions(+) create mode 100644 policyengine_canada/variables/gov/cra/benefits/gis/gis_category.py rename policyengine_canada/variables/gov/cra/benefits/gis/{gis_eligible => gis_eligible.py} (100%) diff --git a/policyengine_canada/variables/gov/cra/benefits/gis/gis_category.py b/policyengine_canada/variables/gov/cra/benefits/gis/gis_category.py new file mode 100644 index 000000000..bc7ffd891 --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis/gis_category.py @@ -0,0 +1,28 @@ +rom policyengine_canada.model_api import * + + +# Categories as listed in SPSD/M variable 'imoldtyp' +class GISCategory(Enum): + SINGLE_WITH_OAS = "single_with_oas" + WIDOW_60_64 = "widow_60_64" + COUPLE_BOTH_OAS = "couple_both_oas" + COUPLE_ELDER_OAS_YOUNGER_60_64 = "couple_elder_oas_younger_60_64" + COUPLE_YOUNGER_OAS = "couple_younger_oas" + +class gst_credit_category(Variable): + value_type = Enum + entity = Person + possible_values = GSTCreditCategory + default_value = GSTCreditCategory.HEAD + definition_period = YEAR + + def formula(person, period, parameters): + return select( + [ + ~person("is_married", period) & person("old_age_security_pension_pre_repayment") > 0 + ], + [ + GISCategory.SINGLE_WITH_OAS + ], + default=0, + ) diff --git a/policyengine_canada/variables/gov/cra/benefits/gis/gis_eligible b/policyengine_canada/variables/gov/cra/benefits/gis/gis_eligible.py similarity index 100% rename from policyengine_canada/variables/gov/cra/benefits/gis/gis_eligible rename to policyengine_canada/variables/gov/cra/benefits/gis/gis_eligible.py From 0e7d9145b63aa30581e7c8aae3c3ccbcedfc5ead Mon Sep 17 00:00:00 2001 From: Alex Rand Date: Sat, 14 Jan 2023 10:03:52 -0500 Subject: [PATCH 03/24] add is_widow.py --- .../gov/cra/benefits/gis/gis_category.py | 18 +++++++++++------- .../variables/household/person/is_widow.py | 8 ++++++++ 2 files changed, 19 insertions(+), 7 deletions(-) create mode 100644 policyengine_canada/variables/household/person/is_widow.py diff --git a/policyengine_canada/variables/gov/cra/benefits/gis/gis_category.py b/policyengine_canada/variables/gov/cra/benefits/gis/gis_category.py index bc7ffd891..e8f49ea09 100644 --- a/policyengine_canada/variables/gov/cra/benefits/gis/gis_category.py +++ b/policyengine_canada/variables/gov/cra/benefits/gis/gis_category.py @@ -1,28 +1,32 @@ -rom policyengine_canada.model_api import * - +from policyengine_canada.model_api import * # Categories as listed in SPSD/M variable 'imoldtyp' class GISCategory(Enum): + NONE = "none" SINGLE_WITH_OAS = "single_with_oas" WIDOW_60_64 = "widow_60_64" COUPLE_BOTH_OAS = "couple_both_oas" COUPLE_ELDER_OAS_YOUNGER_60_64 = "couple_elder_oas_younger_60_64" COUPLE_YOUNGER_OAS = "couple_younger_oas" -class gst_credit_category(Variable): +class gis_credit_category(Variable): value_type = Enum entity = Person - possible_values = GSTCreditCategory - default_value = GSTCreditCategory.HEAD + possible_values = GISCategory + default_value = GISCategory.NONE definition_period = YEAR def formula(person, period, parameters): return select( [ - ~person("is_married", period) & person("old_age_security_pension_pre_repayment") > 0 + ~person("is_married", period) & person("old_age_security_pension_pre_repayment") > 0, + person("is_widow", period) & person("age") < 65 & person("age") >= 60 + # household("is_married", period) & add(household, period, ["is_spouse"] & ["old_age_security_pension_eligibility"]) > 0 ], [ - GISCategory.SINGLE_WITH_OAS + GISCategory.SINGLE_WITH_OAS, + GISCategory.WIDOW_60_64, + GISCategory.COUPLE_BOTH_OAS ], default=0, ) 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..4638071df --- /dev/null +++ b/policyengine_canada/variables/household/person/is_widow.py @@ -0,0 +1,8 @@ +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 From ff388397b769f84c29ab827bbbbcb1e0fb5adff4 Mon Sep 17 00:00:00 2001 From: Alex Rand Date: Mon, 16 Jan 2023 09:31:43 -0500 Subject: [PATCH 04/24] more eligibility categories --- .../gis_spa/widows_eligibility_age.yaml | 8 ++++ .../benefits/gis_spa/gis_spa_category.yaml | 17 ++++++++ .../gov/cra/benefits/gis/gis_category.py | 32 --------------- .../benefits/{gis => gis_spa}/gis_eligible.py | 2 +- .../cra/benefits/gis_spa/gis_spa_category.py | 39 +++++++++++++++++++ setup.py | 2 +- 6 files changed, 66 insertions(+), 34 deletions(-) create mode 100644 policyengine_canada/parameters/gov/cra/benefits/gis_spa/widows_eligibility_age.yaml create mode 100644 policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_spa_category.yaml delete mode 100644 policyengine_canada/variables/gov/cra/benefits/gis/gis_category.py rename policyengine_canada/variables/gov/cra/benefits/{gis => gis_spa}/gis_eligible.py (97%) create mode 100644 policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_spa_category.py diff --git a/policyengine_canada/parameters/gov/cra/benefits/gis_spa/widows_eligibility_age.yaml b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/widows_eligibility_age.yaml new file mode 100644 index 000000000..5009e2c12 --- /dev/null +++ b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/widows_eligibility_age.yaml @@ -0,0 +1,8 @@ +description: Age at which a widow becomes eligible for the GIS +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/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..9445c0bac --- /dev/null +++ b/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_spa_category.yaml @@ -0,0 +1,17 @@ +- name: Single OAS Pensioner + period: 2022 + input: + is_married: false + old_age_security_pension_eligibility: true + output: + gis_spa_category: "SINGLE_WITH_OAS" + +- name: Single OAS Pensioner + period: 2022 + input: + is_widow: true + old_age_security_pension_eligibility: false + age: 61 + output: + gis_spa_category: "WIDOW_60_64" + diff --git a/policyengine_canada/variables/gov/cra/benefits/gis/gis_category.py b/policyengine_canada/variables/gov/cra/benefits/gis/gis_category.py deleted file mode 100644 index e8f49ea09..000000000 --- a/policyengine_canada/variables/gov/cra/benefits/gis/gis_category.py +++ /dev/null @@ -1,32 +0,0 @@ -from policyengine_canada.model_api import * - -# Categories as listed in SPSD/M variable 'imoldtyp' -class GISCategory(Enum): - NONE = "none" - SINGLE_WITH_OAS = "single_with_oas" - WIDOW_60_64 = "widow_60_64" - COUPLE_BOTH_OAS = "couple_both_oas" - COUPLE_ELDER_OAS_YOUNGER_60_64 = "couple_elder_oas_younger_60_64" - COUPLE_YOUNGER_OAS = "couple_younger_oas" - -class gis_credit_category(Variable): - value_type = Enum - entity = Person - possible_values = GISCategory - default_value = GISCategory.NONE - definition_period = YEAR - - def formula(person, period, parameters): - return select( - [ - ~person("is_married", period) & person("old_age_security_pension_pre_repayment") > 0, - person("is_widow", period) & person("age") < 65 & person("age") >= 60 - # household("is_married", period) & add(household, period, ["is_spouse"] & ["old_age_security_pension_eligibility"]) > 0 - ], - [ - GISCategory.SINGLE_WITH_OAS, - GISCategory.WIDOW_60_64, - GISCategory.COUPLE_BOTH_OAS - ], - default=0, - ) diff --git a/policyengine_canada/variables/gov/cra/benefits/gis/gis_eligible.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_eligible.py similarity index 97% rename from policyengine_canada/variables/gov/cra/benefits/gis/gis_eligible.py rename to policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_eligible.py index 0a198b437..cec7f6538 100644 --- a/policyengine_canada/variables/gov/cra/benefits/gis/gis_eligible.py +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_eligible.py @@ -11,7 +11,7 @@ def formula(person, period, parameters): age = person("age", period) # using the pre-repayment amount here because the documentation says "receives the OAS". oas = person("old_age_security_pension_pre_repayment", period) - income = + income = 3 # MAKE A 'type of household' variable!! # MAKE A 'maximum amount' parameter based on 'type of household'!! 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..f7e22c4d7 --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_spa_category.py @@ -0,0 +1,39 @@ +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_60_64 = "widow_60_64" + COUPLE_BOTH_OAS = "couple_both_oas" + COUPLE_ELDER_OAS_YOUNGER_60_64 = "couple_elder_oas_younger_60_64" + COUPLE_YOUNGER_OAS = "couple_younger_oas" + +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): + age = person("age", period) + married = person("is_spouse", period) + widow = person("is_widow", period) + oas_eligible = person("old_age_security_pension_eligibility", period) + # spouse_oas_eligible = + p = parameters(period).gov.cra.benefits.gis_spa + 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 & (age >= p.widows_eligibility_age) + # married & oas_eligible & spouse_oas_eligible + ], + [ + GISSPACategory.SINGLE_WITH_OAS, + GISSPACategory.WIDOW_60_64, + # GISCategory.COUPLE_BOTH_OAS + ], + default=0, + ) diff --git a/setup.py b/setup.py index 4cc0c323b..e79a1a145 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ "tqdm>=4.46.0,<5", "requests>=2.27.1,<3", "pandas>=1.4.2,<2", - "policyengine_core>=1.10.18,<2", + "policyengine_core>=1.11.4,<2", "plotly>=5.6.0,<6", ] From 0cdf46e453cf39d80173bbeb7e441a5165135c61 Mon Sep 17 00:00:00 2001 From: Alex Rand Date: Tue, 17 Jan 2023 09:27:33 -0500 Subject: [PATCH 05/24] Add spouse_oas_eligible.py --- .../benefits/gis_spa/gis_spa_category.yaml | 17 ++++++++++ .../benefits/gis_spa/spouse_oas_eligible.yaml | 32 +++++++++++++++++++ .../cra/benefits/gis_spa/gis_spa_category.py | 11 ++++--- .../benefits/gis_spa/spouse_oas_eligible.py | 17 ++++++++++ 4 files changed, 72 insertions(+), 5 deletions(-) create mode 100644 policyengine_canada/tests/gov/cra/benefits/gis_spa/spouse_oas_eligible.yaml create mode 100644 policyengine_canada/variables/gov/cra/benefits/gis_spa/spouse_oas_eligible.py 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 index 9445c0bac..125efc36a 100644 --- 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 @@ -15,3 +15,20 @@ output: gis_spa_category: "WIDOW_60_64" +- name: Couple, both are OAS eligible + period: 2022 + input: + is_married: true + people: + head: + age: 70 + old_age_security_pension_eligibility: true + spouse: + age: 70 + old_age_security_pension_eligibility: true + household: + is_married: true + members: [head, spouse] + output: + gis_spa_category: ["COUPLE_BOTH_OAS", "COUPLE_BOTH_OAS"] + 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..bea600ed9 --- /dev/null +++ b/policyengine_canada/tests/gov/cra/benefits/gis_spa/spouse_oas_eligible.yaml @@ -0,0 +1,32 @@ +- name: Spouse is OAS eligible + period: 2022 + input: + is_married: true + people: + head: + age: 70 + old_age_security_pension_eligibility: true + spouse: + age: 70 + old_age_security_pension_eligibility: true + household: + members: [head, spouse] + output: + spouse_oas_eligible: [true, true] + +- name: Spouse is not OAS eligible + period: 2022 + input: + is_married: true + people: + head: + age: 70 + old_age_security_pension_eligibility: false + spouse: + age: 70 + old_age_security_pension_eligibility: true + household: + members: [head, spouse] + output: + spouse_oas_eligible: [false, true] + 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 index f7e22c4d7..339215491 100644 --- 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 @@ -17,23 +17,24 @@ class gis_spa_category(Variable): definition_period = YEAR def formula(person, period, parameters): + household = person.household age = person("age", period) - married = person("is_spouse", period) + married = household("is_married", period) widow = person("is_widow", period) oas_eligible = person("old_age_security_pension_eligibility", period) - # spouse_oas_eligible = + spouse_oas_eligible = person("spouse_oas_eligible", period) p = parameters(period).gov.cra.benefits.gis_spa 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 & (age >= p.widows_eligibility_age) - # married & oas_eligible & spouse_oas_eligible + widow & ~married & ~oas_eligible & (age >= p.widows_eligibility_age), + married & oas_eligible & spouse_oas_eligible ], [ GISSPACategory.SINGLE_WITH_OAS, GISSPACategory.WIDOW_60_64, - # GISCategory.COUPLE_BOTH_OAS + GISSPACategory.COUPLE_BOTH_OAS ], default=0, ) diff --git a/policyengine_canada/variables/gov/cra/benefits/gis_spa/spouse_oas_eligible.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/spouse_oas_eligible.py new file mode 100644 index 000000000..11ca6321e --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/spouse_oas_eligible.py @@ -0,0 +1,17 @@ +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("old_age_security_pension_eligibility", 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) + spouse_oas_eligible = count_head_or_spouse_oas_eligible - where(oas_eligible, 1, 0) + + return spouse_oas_eligible \ No newline at end of file From 98b5c3471a5ea4a5e185e7bedc1e9c7c63e4bd07 Mon Sep 17 00:00:00 2001 From: Alex Rand Date: Wed, 18 Jan 2023 09:39:28 -0500 Subject: [PATCH 06/24] spa_eligibility_age.py and age_spouse.py --- .../benefits/gis_spa/spa_eligibility_age.yaml | 8 ++++++++ ...e.yaml => spa_widows_eligibility_age.yaml} | 2 +- .../benefits/gis_spa/gis_spa_category.yaml | 20 +++++++++++++++++++ .../cra/benefits/gis_spa/gis_spa_category.py | 15 +++++++++++--- .../benefits/gis_spa/spouse_oas_eligible.py | 3 +-- .../variables/household/person/age_spouse.py | 20 +++++++++++++++++++ 6 files changed, 62 insertions(+), 6 deletions(-) create mode 100644 policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_eligibility_age.yaml rename policyengine_canada/parameters/gov/cra/benefits/gis_spa/{widows_eligibility_age.yaml => spa_widows_eligibility_age.yaml} (82%) create mode 100644 policyengine_canada/variables/household/person/age_spouse.py diff --git a/policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_eligibility_age.yaml b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_eligibility_age.yaml new file mode 100644 index 000000000..48656badd --- /dev/null +++ b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_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 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/widows_eligibility_age.yaml b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_widows_eligibility_age.yaml similarity index 82% rename from policyengine_canada/parameters/gov/cra/benefits/gis_spa/widows_eligibility_age.yaml rename to policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_widows_eligibility_age.yaml index 5009e2c12..5e01ac88d 100644 --- a/policyengine_canada/parameters/gov/cra/benefits/gis_spa/widows_eligibility_age.yaml +++ b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_widows_eligibility_age.yaml @@ -1,4 +1,4 @@ -description: Age at which a widow becomes eligible for the GIS +description: Age at which a widow becomes eligible for the SPA values: 2020-01-01: 60 metadata: 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 index 125efc36a..28fbb29b0 100644 --- 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 @@ -32,3 +32,23 @@ output: gis_spa_category: ["COUPLE_BOTH_OAS", "COUPLE_BOTH_OAS"] +- name: Couple, elder is OAS eligible, younger is aged 60-64 + period: 2022 + input: + is_married: true + people: + head: + is_head: true + age: 70 + old_age_security_pension_eligibility: true + spouse: + is_spouse: true + age: 61 + old_age_security_pension_eligibility: false + household: + is_married: true + members: [head, spouse] + output: + age_spouse: 61 + gis_spa_category: ["COUPLE_ELDER_OAS_YOUNGER_60_64", "COUPLE_ELDER_OAS_YOUNGER_60_64"] + 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 index 339215491..eff0c0466 100644 --- 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 @@ -8,6 +8,7 @@ class GISSPACategory(Enum): COUPLE_BOTH_OAS = "couple_both_oas" COUPLE_ELDER_OAS_YOUNGER_60_64 = "couple_elder_oas_younger_60_64" COUPLE_YOUNGER_OAS = "couple_younger_oas" + COUPLE_ELDER_OAS = "couple_elder_oas" class gis_spa_category(Variable): value_type = Enum @@ -19,7 +20,10 @@ class gis_spa_category(Variable): def formula(person, period, parameters): household = person.household age = person("age", period) + age_spouse = household("age_spouse", period) married = household("is_married", period) + head = person("is_head", period) + spouse = person("is_spouse", period) widow = person("is_widow", period) oas_eligible = person("old_age_security_pension_eligibility", period) spouse_oas_eligible = person("spouse_oas_eligible", period) @@ -28,13 +32,18 @@ def formula(person, period, parameters): [ ~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 & (age >= p.widows_eligibility_age), - married & oas_eligible & spouse_oas_eligible + widow & ~married & ~oas_eligible & (age >= p.spa_widows_eligibility_age), + married & oas_eligible & spouse_oas_eligible, + married & (head & oas_eligible & ~spouse_oas_eligible & (age_spouse >= p.spa_eligibility_age)) | (spouse & ~oas_eligible & spouse_oas_eligible & (age >= p.spa_eligibility_age)), + # married & (head & oas_eligible & ~spouse_oas_eligible & (age_spouse >= p.spa_eligibility_age)) ], [ GISSPACategory.SINGLE_WITH_OAS, GISSPACategory.WIDOW_60_64, - GISSPACategory.COUPLE_BOTH_OAS + GISSPACategory.COUPLE_BOTH_OAS, + GISSPACategory.COUPLE_ELDER_OAS_YOUNGER_60_64, + # GISSPACategory.COUPLE_YOUNGER_OAS, + ], default=0, ) diff --git a/policyengine_canada/variables/gov/cra/benefits/gis_spa/spouse_oas_eligible.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/spouse_oas_eligible.py index 11ca6321e..5071b2627 100644 --- a/policyengine_canada/variables/gov/cra/benefits/gis_spa/spouse_oas_eligible.py +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/spouse_oas_eligible.py @@ -12,6 +12,5 @@ def formula(person, period, parameters): 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) - spouse_oas_eligible = count_head_or_spouse_oas_eligible - where(oas_eligible, 1, 0) - return spouse_oas_eligible \ No newline at end of file + return count_head_or_spouse_oas_eligible - where(oas_eligible, 1, 0) \ No newline at end of file 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..55af09ea8 --- /dev/null +++ b/policyengine_canada/variables/household/person/age_spouse.py @@ -0,0 +1,20 @@ +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) + + + + From e63ab6d14ba7ea4f6442cef188082dd4d01e31d7 Mon Sep 17 00:00:00 2001 From: Alex Rand Date: Thu, 19 Jan 2023 10:21:08 -0500 Subject: [PATCH 07/24] spa eligibility variables + tests + gis categories --- .../gis_spa/spa_residency_requirement.yaml | 8 ++++ .../benefits/gis_spa/gis_spa_category.yaml | 38 +++++++++++++-- .../cra/benefits/gis_spa/spa_eligible.yaml | 47 +++++++++++++++++++ .../benefits/gis_spa/spouse_spa_eligible.yaml | 40 ++++++++++++++++ .../cra/benefits/gis_spa/gis_spa_category.py | 25 +++++----- .../gov/cra/benefits/gis_spa/spa_eligible.py | 16 +++++++ .../benefits/gis_spa/spouse_spa_eligible.py | 16 +++++++ .../person/adult_years_in_canada_spouse.py | 16 +++++++ 8 files changed, 188 insertions(+), 18 deletions(-) create mode 100644 policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_residency_requirement.yaml create mode 100644 policyengine_canada/tests/gov/cra/benefits/gis_spa/spa_eligible.yaml create mode 100644 policyengine_canada/tests/gov/cra/benefits/gis_spa/spouse_spa_eligible.yaml create mode 100644 policyengine_canada/variables/gov/cra/benefits/gis_spa/spa_eligible.py create mode 100644 policyengine_canada/variables/gov/cra/benefits/gis_spa/spouse_spa_eligible.py create mode 100644 policyengine_canada/variables/household/person/adult_years_in_canada_spouse.py 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/tests/gov/cra/benefits/gis_spa/gis_spa_category.yaml b/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_spa_category.yaml index 28fbb29b0..10608324e 100644 --- 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 @@ -11,9 +11,20 @@ input: is_widow: true old_age_security_pension_eligibility: false + adult_years_in_canada: 12 age: 61 output: - gis_spa_category: "WIDOW_60_64" + gis_spa_category: "WIDOW_SPA_ELIGIBLE" + +- name: Single OAS Pensioner, ineligible because not enough adult years in canada + period: 2022 + input: + is_widow: true + old_age_security_pension_eligibility: false + adult_years_in_canada: 2 + age: 61 + output: + gis_spa_category: "NONE" - name: Couple, both are OAS eligible period: 2022 @@ -32,7 +43,7 @@ output: gis_spa_category: ["COUPLE_BOTH_OAS", "COUPLE_BOTH_OAS"] -- name: Couple, elder is OAS eligible, younger is aged 60-64 +- name: Couple, elder is OAS eligible, younger is SPA eligible period: 2022 input: is_married: true @@ -45,10 +56,29 @@ is_spouse: true age: 61 old_age_security_pension_eligibility: false + adult_years_in_canada: 20 household: is_married: true members: [head, spouse] output: - age_spouse: 61 - gis_spa_category: ["COUPLE_ELDER_OAS_YOUNGER_60_64", "COUPLE_ELDER_OAS_YOUNGER_60_64"] + 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 + old_age_security_pension_eligibility: true + spouse: + is_spouse: true + age: 44 + spa_eligible: false + old_age_security_pension_eligibility: 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/spa_eligible.yaml b/policyengine_canada/tests/gov/cra/benefits/gis_spa/spa_eligible.yaml new file mode 100644 index 000000000..afb668e93 --- /dev/null +++ b/policyengine_canada/tests/gov/cra/benefits/gis_spa/spa_eligible.yaml @@ -0,0 +1,47 @@ +- name: Is SPA eligible because spouse has OAS + period: 2022 + input: + is_married: true + spouse_oas_eligible: true + age: 61 + adult_years_in_canada: 12 + output: + spouse_spa_eligible: true + +- name: Is SPA eligible because is widow + period: 2022 + input: + is_married: false + is_widow: true + age: 61 + adult_years_in_canada: 12 + output: + spouse_spa_eligible: true + +- name: Isn't SPA eligible because not enough years in canada + period: 2022 + input: + is_married: false + is_widow: true + age: 61 + adult_years_in_canada: 9 + output: + spouse_spa_eligible: false + +- name: Isn't SPA eligible because too old + period: 2022 + input: + is_married: false + is_widow: true + age: 80 + adult_years_in_canada: 40 + output: + spouse_spa_eligible: false + +- name: Isn't SPA eligible because is OAS pensioner + period: 2022 + input: + is_widow: true + old_age_security_pension_eligibility: true + output: + spouse_spa_eligible: false 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..f729d262b --- /dev/null +++ b/policyengine_canada/tests/gov/cra/benefits/gis_spa/spouse_spa_eligible.yaml @@ -0,0 +1,40 @@ +- name: Spouse is SPA eligible + period: 2022 + input: + is_married: true + people: + head: + is_head: true + age: 70 + old_age_security_pension_eligibility: true + adult_years_in_canada: 40 + spouse: + is_spouse: true + age: 61 + old_age_security_pension_eligibility: false + adult_years_in_canada: 12 + household: + members: [head, spouse] + output: + is_spouse: [false, true] + spouse_spa_eligible: [true, false] + +- name: Spouse is not SPA eligible + period: 2022 + input: + is_married: true + people: + head: + is_head: true + age: 70 + old_age_security_pension_eligibility: true + spouse: + is_spouse: false + age: 70 + old_age_security_pension_eligibility: false + adult_years_in_canada: 9 + household: + members: [head, spouse] + output: + spouse_spa_eligible: [false, false] + 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 index eff0c0466..67f29ea83 100644 --- 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 @@ -4,11 +4,10 @@ class GISSPACategory(Enum): NONE = "none" SINGLE_WITH_OAS = "single_with_oas" - WIDOW_60_64 = "widow_60_64" + WIDOW_SPA_ELIGIBLE = "widow_spa_eligible" COUPLE_BOTH_OAS = "couple_both_oas" - COUPLE_ELDER_OAS_YOUNGER_60_64 = "couple_elder_oas_younger_60_64" - COUPLE_YOUNGER_OAS = "couple_younger_oas" - COUPLE_ELDER_OAS = "couple_elder_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 @@ -22,28 +21,26 @@ def formula(person, period, parameters): age = person("age", period) age_spouse = household("age_spouse", period) married = household("is_married", period) - head = person("is_head", period) - spouse = person("is_spouse", period) widow = person("is_widow", period) oas_eligible = person("old_age_security_pension_eligibility", period) + spa_eligible = person("spa_eligible", period) spouse_oas_eligible = person("spouse_oas_eligible", period) + spouse_spa_eligible = person("spouse_spa_eligible", period) p = parameters(period).gov.cra.benefits.gis_spa 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 & (age >= p.spa_widows_eligibility_age), + widow & ~married & ~oas_eligible & spa_eligible, married & oas_eligible & spouse_oas_eligible, - married & (head & oas_eligible & ~spouse_oas_eligible & (age_spouse >= p.spa_eligibility_age)) | (spouse & ~oas_eligible & spouse_oas_eligible & (age >= p.spa_eligibility_age)), - # married & (head & oas_eligible & ~spouse_oas_eligible & (age_spouse >= p.spa_eligibility_age)) + 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_60_64, + GISSPACategory.WIDOW_SPA_ELIGIBLE, GISSPACategory.COUPLE_BOTH_OAS, - GISSPACategory.COUPLE_ELDER_OAS_YOUNGER_60_64, - # GISSPACategory.COUPLE_YOUNGER_OAS, - + GISSPACategory.COUPLE_ONE_OAS_SPA_ELIGIBLE, + GISSPACategory.COUPLE_ONE_OAS_SPA_INELIGIBLE ], - default=0, ) diff --git a/policyengine_canada/variables/gov/cra/benefits/gis_spa/spa_eligible.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/spa_eligible.py new file mode 100644 index 000000000..c0cb6d069 --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/spa_eligible.py @@ -0,0 +1,16 @@ +from policyengine_canada.model_api import * + +class spa_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): + 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("old_age_security_pension_eligibility", period) + spouse_oas_eligible = person("spouse_oas_eligible", period) + return ~oas_eligible & (widow | spouse_oas_eligible) & (age >= p.spa_widows_eligibility_age) & (adult_years_in_canada >= p.spa_residency_requirement) \ No newline at end of file diff --git a/policyengine_canada/variables/gov/cra/benefits/gis_spa/spouse_spa_eligible.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/spouse_spa_eligible.py new file mode 100644 index 000000000..1f8503e0d --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/spouse_spa_eligible.py @@ -0,0 +1,16 @@ +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) \ No newline at end of file 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..8a4aa0c9e --- /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) From 00b3d4ac930aa7d94c4616cd33cc1368bf4425b7 Mon Sep 17 00:00:00 2001 From: Alex Rand Date: Wed, 1 Feb 2023 11:00:16 -0500 Subject: [PATCH 08/24] fix the unit tests --- ...e.yaml => spa_spouse_eligibility_age.yaml} | 2 +- .../cra/benefits/gis_spa/spa_eligible.yaml | 45 +++++++------------ .../benefits/gis_spa/spouse_oas_eligible.yaml | 23 +++++----- .../benefits/gis_spa/spouse_spa_eligible.yaml | 26 ++++------- .../gov/cra/benefits/gis_spa/spa_eligible.py | 5 ++- 5 files changed, 37 insertions(+), 64 deletions(-) rename policyengine_canada/parameters/gov/cra/benefits/gis_spa/{spa_eligibility_age.yaml => spa_spouse_eligibility_age.yaml} (90%) diff --git a/policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_eligibility_age.yaml b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_spouse_eligibility_age.yaml similarity index 90% rename from policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_eligibility_age.yaml rename to policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_spouse_eligibility_age.yaml index 48656badd..942e9e618 100644 --- a/policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_eligibility_age.yaml +++ b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_spouse_eligibility_age.yaml @@ -2,7 +2,7 @@ description: Age at which the spouse of an OAS pensioner becomes eligible for th values: 2020-01-01: 60 metadata: - label: SPA Eligibility Age + 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/tests/gov/cra/benefits/gis_spa/spa_eligible.yaml b/policyengine_canada/tests/gov/cra/benefits/gis_spa/spa_eligible.yaml index afb668e93..f95d75c23 100644 --- a/policyengine_canada/tests/gov/cra/benefits/gis_spa/spa_eligible.yaml +++ b/policyengine_canada/tests/gov/cra/benefits/gis_spa/spa_eligible.yaml @@ -1,47 +1,32 @@ - name: Is SPA eligible because spouse has OAS period: 2022 input: - is_married: true - spouse_oas_eligible: true age: 61 - adult_years_in_canada: 12 + adult_years_in_canada: 40 + is_widow: FALSE + old_age_security_pension_eligibility: FALSE + spouse_oas_eligible: TRUE output: - spouse_spa_eligible: true + spa_eligible: TRUE - name: Is SPA eligible because is widow period: 2022 input: - is_married: false - is_widow: true age: 61 - adult_years_in_canada: 12 + adult_years_in_canada: 40 + is_widow: TRUE + old_age_security_pension_eligibility: FALSE + spouse_oas_eligible: FALSE output: - spouse_spa_eligible: true + spa_eligible: TRUE -- name: Isn't SPA eligible because not enough years in canada +- name: Isn't SPA eligible because, despite being a widow, not enough years in canada period: 2022 input: - is_married: false - is_widow: true age: 61 adult_years_in_canada: 9 + is_widow: TRUE + old_age_security_pension_eligibility: FALSE + spouse_oas_eligible: FALSE output: - spouse_spa_eligible: false - -- name: Isn't SPA eligible because too old - period: 2022 - input: - is_married: false - is_widow: true - age: 80 - adult_years_in_canada: 40 - output: - spouse_spa_eligible: false - -- name: Isn't SPA eligible because is OAS pensioner - period: 2022 - input: - is_widow: true - old_age_security_pension_eligibility: true - output: - spouse_spa_eligible: false + spa_eligible: FALSE 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 index bea600ed9..86ff0ab91 100644 --- 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 @@ -1,32 +1,29 @@ -- name: Spouse is OAS eligible +- name: Both are eligible period: 2022 input: - is_married: true people: head: - age: 70 + is_head: true old_age_security_pension_eligibility: true spouse: - age: 70 + is_spouse: true old_age_security_pension_eligibility: true household: members: [head, spouse] output: - spouse_oas_eligible: [true, true] + spouse_oas_eligible: [true, true] -- name: Spouse is not OAS eligible +- name: Spouse is not SPA eligible period: 2022 input: - is_married: true people: head: - age: 70 - old_age_security_pension_eligibility: false - spouse: - age: 70 + is_head: true old_age_security_pension_eligibility: true + spouse: + is_spouse: true + old_age_security_pension_eligibility: false household: members: [head, spouse] output: - spouse_oas_eligible: [false, true] - + 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 index f729d262b..2533c644e 100644 --- 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 @@ -1,40 +1,30 @@ -- name: Spouse is SPA eligible +- name: Both are eligible period: 2022 input: - is_married: true people: head: is_head: true - age: 70 - old_age_security_pension_eligibility: true - adult_years_in_canada: 40 + spa_eligible: true spouse: is_spouse: true - age: 61 - old_age_security_pension_eligibility: false - adult_years_in_canada: 12 + spa_eligible: true household: members: [head, spouse] output: - is_spouse: [false, true] - spouse_spa_eligible: [true, false] + spouse_spa_eligible: [true, true] - name: Spouse is not SPA eligible period: 2022 input: - is_married: true people: head: is_head: true - age: 70 - old_age_security_pension_eligibility: true + spa_eligible: true spouse: - is_spouse: false - age: 70 - old_age_security_pension_eligibility: false - adult_years_in_canada: 9 + is_spouse: true + spa_eligible: false household: members: [head, spouse] output: - spouse_spa_eligible: [false, false] + spouse_spa_eligible: [false, true] diff --git a/policyengine_canada/variables/gov/cra/benefits/gis_spa/spa_eligible.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/spa_eligible.py index c0cb6d069..207309f11 100644 --- a/policyengine_canada/variables/gov/cra/benefits/gis_spa/spa_eligible.py +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/spa_eligible.py @@ -3,7 +3,7 @@ class spa_eligible(Variable): value_type = bool entity = Person - label = "Spouse is eligible for the Old Age Security Pension" + label = "Person is eligible for the Spousal Allowance" definition_period = YEAR def formula(person, period, parameters): @@ -13,4 +13,5 @@ def formula(person, period, parameters): widow = person("is_widow", period) oas_eligible = person("old_age_security_pension_eligibility", period) spouse_oas_eligible = person("spouse_oas_eligible", period) - return ~oas_eligible & (widow | spouse_oas_eligible) & (age >= p.spa_widows_eligibility_age) & (adult_years_in_canada >= p.spa_residency_requirement) \ No newline at end of file + 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))) \ No newline at end of file From ed3a1c975f176173e45ec03d03c916b75684e049 Mon Sep 17 00:00:00 2001 From: Alex Rand Date: Fri, 3 Feb 2023 09:29:31 -0500 Subject: [PATCH 09/24] add gis cap values --- .../cra/benefits/gis_spa/gis_cap/two_pensioners.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 policyengine_canada/parameters/gov/cra/benefits/gis_spa/gis_cap/two_pensioners.yaml 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..9a7755a60 --- /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-12-31: 6_240.18 +metadata: + period: year + label: GIS Cap for pensioners married to a pensioner. + documentataion: Corresponds to BGISS in the SPSD/M Variable Guide + reference: + - title: SPSD/M Variable Guide + \ No newline at end of file From 33ef72f52cb98e6725473b716d3540d5ac8f6f5d Mon Sep 17 00:00:00 2001 From: Alex Rand Date: Fri, 3 Feb 2023 09:29:31 -0500 Subject: [PATCH 10/24] add gis cap values --- .../gov/cra/benefits/gis_spa/gis_cap.yaml | 19 +++++++++++++++++++ .../gis_spa/gis_cap/one_pensioner.yaml | 10 ++++++++++ .../gis_spa/gis_cap/two_pensioners.yaml | 10 ++++++++++ .../benefits/gis_spa/gis_spa_category.yaml | 18 +++++++++--------- .../cra/benefits/gis_spa/spa_eligible.yaml | 6 +++--- .../benefits/gis_spa/spouse_oas_eligible.yaml | 8 ++++---- .../gov/cra/benefits/oas/integration.yaml | 6 +++--- .../gov/cra/benefits/oas/oas_eligibility.yaml | 6 +++--- .../gov/cra/benefits/gis_spa/base_amount.py | 0 .../gis_spa/{ => eligibility}/spa_eligible.py | 2 +- .../{ => eligibility}/spouse_oas_eligible.py | 2 +- .../{ => eligibility}/spouse_spa_eligible.py | 0 .../gov/cra/benefits/gis_spa/gis_eligible.py | 18 ------------------ .../cra/benefits/gis_spa/gis_spa_category.py | 3 +-- .../{oas_eligibility.py => oas_eligibile.py} | 2 +- .../oas_pre_repayment.py | 2 +- 16 files changed, 66 insertions(+), 46 deletions(-) create mode 100644 policyengine_canada/parameters/gov/cra/benefits/gis_spa/gis_cap.yaml create mode 100644 policyengine_canada/parameters/gov/cra/benefits/gis_spa/gis_cap/one_pensioner.yaml create mode 100644 policyengine_canada/parameters/gov/cra/benefits/gis_spa/gis_cap/two_pensioners.yaml create mode 100644 policyengine_canada/variables/gov/cra/benefits/gis_spa/base_amount.py rename policyengine_canada/variables/gov/cra/benefits/gis_spa/{ => eligibility}/spa_eligible.py (91%) rename policyengine_canada/variables/gov/cra/benefits/gis_spa/{ => eligibility}/spouse_oas_eligible.py (88%) rename policyengine_canada/variables/gov/cra/benefits/gis_spa/{ => eligibility}/spouse_spa_eligible.py (100%) delete mode 100644 policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_eligible.py rename policyengine_canada/variables/gov/cra/benefits/old_age_security_pension/{oas_eligibility.py => oas_eligibile.py} (95%) diff --git a/policyengine_canada/parameters/gov/cra/benefits/gis_spa/gis_cap.yaml b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/gis_cap.yaml new file mode 100644 index 000000000..7485596aa --- /dev/null +++ b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/gis_cap.yaml @@ -0,0 +1,19 @@ +description: The maximum benefit available for +brackets: + - amount: + 2021-07-01: 6_997 + threshold: + 2022-07-01: 0 + - amount: + 2022-07-01: 5_903 + threshold: + 2022-07-01: 6 + - amount: + 2022-07-01: 0 + threshold: + 2022-07-01: 18 +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/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..f626128fa --- /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-12-31: 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 + \ No newline at end of file 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..9a7755a60 --- /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-12-31: 6_240.18 +metadata: + period: year + label: GIS Cap for pensioners married to a pensioner. + documentataion: Corresponds to BGISS in the SPSD/M Variable Guide + reference: + - title: SPSD/M Variable Guide + \ No newline at end of file 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 index 10608324e..6c4affc3b 100644 --- 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 @@ -2,7 +2,7 @@ period: 2022 input: is_married: false - old_age_security_pension_eligibility: true + oas_eligible: true output: gis_spa_category: "SINGLE_WITH_OAS" @@ -10,7 +10,7 @@ period: 2022 input: is_widow: true - old_age_security_pension_eligibility: false + oas_eligible: false adult_years_in_canada: 12 age: 61 output: @@ -20,7 +20,7 @@ period: 2022 input: is_widow: true - old_age_security_pension_eligibility: false + oas_eligible: false adult_years_in_canada: 2 age: 61 output: @@ -33,10 +33,10 @@ people: head: age: 70 - old_age_security_pension_eligibility: true + oas_eligible: true spouse: age: 70 - old_age_security_pension_eligibility: true + oas_eligible: true household: is_married: true members: [head, spouse] @@ -51,11 +51,11 @@ head: is_head: true age: 70 - old_age_security_pension_eligibility: true + oas_eligible: true spouse: is_spouse: true age: 61 - old_age_security_pension_eligibility: false + oas_eligible: false adult_years_in_canada: 20 household: is_married: true @@ -71,12 +71,12 @@ head: is_head: true age: 70 - old_age_security_pension_eligibility: true + oas_eligible: true spouse: is_spouse: true age: 44 spa_eligible: false - old_age_security_pension_eligibility: false + oas_eligible: false household: members: [head, spouse] output: 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 index f95d75c23..fcb1e0591 100644 --- a/policyengine_canada/tests/gov/cra/benefits/gis_spa/spa_eligible.yaml +++ b/policyengine_canada/tests/gov/cra/benefits/gis_spa/spa_eligible.yaml @@ -4,7 +4,7 @@ age: 61 adult_years_in_canada: 40 is_widow: FALSE - old_age_security_pension_eligibility: FALSE + oas_eligible: FALSE spouse_oas_eligible: TRUE output: spa_eligible: TRUE @@ -15,7 +15,7 @@ age: 61 adult_years_in_canada: 40 is_widow: TRUE - old_age_security_pension_eligibility: FALSE + oas_eligible: FALSE spouse_oas_eligible: FALSE output: spa_eligible: TRUE @@ -26,7 +26,7 @@ age: 61 adult_years_in_canada: 9 is_widow: TRUE - old_age_security_pension_eligibility: FALSE + oas_eligible: FALSE spouse_oas_eligible: FALSE output: spa_eligible: FALSE 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 index 86ff0ab91..be0c14004 100644 --- 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 @@ -4,10 +4,10 @@ people: head: is_head: true - old_age_security_pension_eligibility: true + oas_eligible: true spouse: is_spouse: true - old_age_security_pension_eligibility: true + oas_eligible: true household: members: [head, spouse] output: @@ -19,10 +19,10 @@ people: head: is_head: true - old_age_security_pension_eligibility: true + oas_eligible: true spouse: is_spouse: true - old_age_security_pension_eligibility: false + oas_eligible: false household: members: [head, spouse] output: 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/base_amount.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/base_amount.py new file mode 100644 index 000000000..e69de29bb diff --git a/policyengine_canada/variables/gov/cra/benefits/gis_spa/spa_eligible.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/eligibility/spa_eligible.py similarity index 91% rename from policyengine_canada/variables/gov/cra/benefits/gis_spa/spa_eligible.py rename to policyengine_canada/variables/gov/cra/benefits/gis_spa/eligibility/spa_eligible.py index 207309f11..ebab52c70 100644 --- a/policyengine_canada/variables/gov/cra/benefits/gis_spa/spa_eligible.py +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/eligibility/spa_eligible.py @@ -11,7 +11,7 @@ def formula(person, period, parameters): age = person("age", period) adult_years_in_canada = person("adult_years_in_canada", period) widow = person("is_widow", period) - oas_eligible = person("old_age_security_pension_eligibility", 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))) \ No newline at end of file diff --git a/policyengine_canada/variables/gov/cra/benefits/gis_spa/spouse_oas_eligible.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/eligibility/spouse_oas_eligible.py similarity index 88% rename from policyengine_canada/variables/gov/cra/benefits/gis_spa/spouse_oas_eligible.py rename to policyengine_canada/variables/gov/cra/benefits/gis_spa/eligibility/spouse_oas_eligible.py index 5071b2627..2dfd378cf 100644 --- a/policyengine_canada/variables/gov/cra/benefits/gis_spa/spouse_oas_eligible.py +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/eligibility/spouse_oas_eligible.py @@ -7,7 +7,7 @@ class spouse_oas_eligible(Variable): definition_period = YEAR def formula(person, period, parameters): - oas_eligible = person("old_age_security_pension_eligibility", period) + 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 diff --git a/policyengine_canada/variables/gov/cra/benefits/gis_spa/spouse_spa_eligible.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/eligibility/spouse_spa_eligible.py similarity index 100% rename from policyengine_canada/variables/gov/cra/benefits/gis_spa/spouse_spa_eligible.py rename to policyengine_canada/variables/gov/cra/benefits/gis_spa/eligibility/spouse_spa_eligible.py diff --git a/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_eligible.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_eligible.py deleted file mode 100644 index cec7f6538..000000000 --- a/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_eligible.py +++ /dev/null @@ -1,18 +0,0 @@ -from policyengine_canada.model_api import * - -class gis_eligible(Variable): - value_type = bool - entity = Person - label = "Eligible for guaranteed income supplement" - definition_period = YEAR - - def formula(person, period, parameters): - p = parameters(period).gov.cra.benefits.gis - age = person("age", period) - # using the pre-repayment amount here because the documentation says "receives the OAS". - oas = person("old_age_security_pension_pre_repayment", period) - income = 3 - - # MAKE A 'type of household' variable!! - # MAKE A 'maximum amount' parameter based on 'type of household'!! - # MAKE A 'reduction rate' parameter based on 'type of household'? Copy GISRLS for example. 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 index 67f29ea83..b5cc04005 100644 --- 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 @@ -22,11 +22,10 @@ def formula(person, period, parameters): age_spouse = household("age_spouse", period) married = household("is_married", period) widow = person("is_widow", period) - oas_eligible = person("old_age_security_pension_eligibility", 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) - p = parameters(period).gov.cra.benefits.gis_spa return select( [ ~married & oas_eligible, 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 From 81749faeadcd974b0915d7b3f9f936baffa24340 Mon Sep 17 00:00:00 2001 From: Alex Rand Date: Sat, 4 Feb 2023 10:01:42 -0500 Subject: [PATCH 11/24] base amount and unit tests --- .../cra/benefits/gis_spa/gis_base_amount.yaml | 41 +++++++++++++++++++ .../benefits/gis_spa/gis_spa_category.yaml | 2 +- .../cra/benefits/gis_spa/gis_base_amount.py | 9 ++-- 3 files changed, 47 insertions(+), 5 deletions(-) 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 index 75cfa9899..32b88ddd6 100644 --- 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 @@ -5,3 +5,44 @@ oas_eligible: true output: gis_base_amount: 9_449.87 + +- name: Widow + period: 2022 + input: + gis_spa_category: "WIDOW_SPA_ELIGIBLE" + output: + gis_base_amount: 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_base_amount: 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_base_amount: 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_base_amount: 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_base_amount: 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 index 6c4affc3b..f586198a2 100644 --- 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 @@ -6,7 +6,7 @@ output: gis_spa_category: "SINGLE_WITH_OAS" -- name: Single OAS Pensioner +- name: Widow period: 2022 input: is_widow: true diff --git a/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_base_amount.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_base_amount.py index 544900393..9948cf7dc 100644 --- a/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_base_amount.py +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_base_amount.py @@ -10,14 +10,15 @@ class gis_base_amount(Variable): 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 == "SINGLE_WITH_OAS", - gis_spa_category == "COUPLE_BOTH_OAS", - (gis_spa_category == "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 == "COUPLE_ONE_OAS_SPA_INELIGIBLE" # & oas_eligible, + 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, From e1619950d2256a98a3019d802b918e1f22473a4d Mon Sep 17 00:00:00 2001 From: Alex Rand Date: Sat, 4 Feb 2023 10:01:42 -0500 Subject: [PATCH 12/24] base amount and unit tests --- .../cra/benefits/gis_spa/gis_reduction.yaml | 19 ------- .../gis_spa/gis_reduction/one_pensioner.yaml | 17 +++++++ .../gis_spa/gis_reduction/two_pensioners.yaml | 17 +++++++ .../cra/benefits/gis_spa/gis_base_amount.yaml | 41 ++++++++++++++++ .../cra/benefits/gis_spa/gis_reduction.yaml | 49 +++++++++++++++++++ .../benefits/gis_spa/gis_spa_category.yaml | 2 +- .../cra/benefits/gis_spa/gis_base_amount.py | 9 ++-- .../gov/cra/benefits/gis_spa/gis_reduction.py | 42 ++++++++++++++++ .../income/individual/spouse_net_income.py | 16 ++++++ 9 files changed, 188 insertions(+), 24 deletions(-) delete mode 100644 policyengine_canada/parameters/gov/cra/benefits/gis_spa/gis_reduction.yaml create mode 100644 policyengine_canada/parameters/gov/cra/benefits/gis_spa/gis_reduction/one_pensioner.yaml create mode 100644 policyengine_canada/parameters/gov/cra/benefits/gis_spa/gis_reduction/two_pensioners.yaml create mode 100644 policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_reduction.yaml create mode 100644 policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_reduction.py create mode 100644 policyengine_canada/variables/household/income/individual/spouse_net_income.py diff --git a/policyengine_canada/parameters/gov/cra/benefits/gis_spa/gis_reduction.yaml b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/gis_reduction.yaml deleted file mode 100644 index 7485596aa..000000000 --- a/policyengine_canada/parameters/gov/cra/benefits/gis_spa/gis_reduction.yaml +++ /dev/null @@ -1,19 +0,0 @@ -description: The maximum benefit available for -brackets: - - amount: - 2021-07-01: 6_997 - threshold: - 2022-07-01: 0 - - amount: - 2022-07-01: 5_903 - threshold: - 2022-07-01: 6 - - amount: - 2022-07-01: 0 - threshold: - 2022-07-01: 18 -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/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..0bd3d655b --- /dev/null +++ b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/gis_reduction/one_pensioner.yaml @@ -0,0 +1,17 @@ +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 +brackets: + - threshold: + 2021-07-01: 0 + rate: + 2022-07-01: 0 + - threshold: + 2022-07-01: 24 + rate: + 2022-07-01: 0.5 +metadata: + period: year + label: GIS reduction 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_reduction/two_pensioners.yaml b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/gis_reduction/two_pensioners.yaml new file mode 100644 index 000000000..56837a1e0 --- /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 or COUPLE_ONE_OAS_SPA_ELIGIBLE. Note that this calculates on couples combined income, whereas the other GIS cateogires calculate on the lone pensioner's income. +brackets: + - threshold: + 2021-07-01: 0 + rate: + 2022-07-01: 0 + - threshold: + 2022-07-01: 48 + rate: + 2022-07-01: 0.5 +metadata: + period: year + label: GIS reduction for pensioners married to a pensioner. + documentataion: Corresponds to GISRRS and GISRLS 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 index 75cfa9899..32b88ddd6 100644 --- 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 @@ -5,3 +5,44 @@ oas_eligible: true output: gis_base_amount: 9_449.87 + +- name: Widow + period: 2022 + input: + gis_spa_category: "WIDOW_SPA_ELIGIBLE" + output: + gis_base_amount: 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_base_amount: 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_base_amount: 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_base_amount: 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_base_amount: 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..1aa374c70 --- /dev/null +++ b/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_reduction.yaml @@ -0,0 +1,49 @@ +- name: Single Pensioner + period: 2022 + input: + # gis_spa_category: "SINGLE_WITH_OAS" + individual_net_income: 224 + # gis_base_amount: 9_449.87 + output: + gis_reduction: 100 + +- name: Widow + period: 2022 + input: + gis_spa_category: "WIDOW_SPA_ELIGIBLE" + output: + gis_base_amount: 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_base_amount: 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_base_amount: 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_base_amount: 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_base_amount: 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 index 6c4affc3b..f586198a2 100644 --- 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 @@ -6,7 +6,7 @@ output: gis_spa_category: "SINGLE_WITH_OAS" -- name: Single OAS Pensioner +- name: Widow period: 2022 input: is_widow: true diff --git a/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_base_amount.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_base_amount.py index 544900393..9948cf7dc 100644 --- a/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_base_amount.py +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_base_amount.py @@ -10,14 +10,15 @@ class gis_base_amount(Variable): 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 == "SINGLE_WITH_OAS", - gis_spa_category == "COUPLE_BOTH_OAS", - (gis_spa_category == "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 == "COUPLE_ONE_OAS_SPA_INELIGIBLE" # & oas_eligible, + 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, diff --git a/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_reduction.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_reduction.py new file mode 100644 index 000000000..d3ff257b1 --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_reduction.py @@ -0,0 +1,42 @@ +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): + # gis_spa_category = person("gis_spa_category", period) + # gis_spa_categories = gis_spa_category.possible_values + individual_net_income = person("individual_net_income", period) + # household = person.household + # spouse_net_income = household("spouse_net_income", period) + # gis_base = person("gis_base_amount", period) + p = parameters(period).gov.cra.benefits.gis_spa.gis_reduction + + reduction = p.one_pensioner.calc(individual_net_income) + + return(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 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) & (gis_base > 0) + ], + [ + p.one_pensioner.calc(individual_net_income), + p.two_pensioners.calc(individual_net_income + spouse_net_income), + p.two_pensioners.calc(individual_net_income + spouse_net_income), + p.one_pensioner.calc(individual_net_income) + ], + default=0, + ) + + 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) From 057522142c6b2e9cafd331a74fc899f0c57a6a17 Mon Sep 17 00:00:00 2001 From: Alex Rand Date: Sun, 12 Feb 2023 10:11:21 -0500 Subject: [PATCH 13/24] GIS reduction and unit tests --- .../gis_spa/gis_reduction/one_pensioner.yaml | 13 +++-- .../gis_spa/gis_reduction/two_pensioners.yaml | 4 +- .../cra/benefits/gis_spa/gis_reduction.yaml | 55 +++++++++---------- .../gov/cra/benefits/gis_spa/gis_reduction.py | 22 +++----- 4 files changed, 45 insertions(+), 49 deletions(-) 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 index 0bd3d655b..fd0642d5d 100644 --- 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 @@ -1,17 +1,20 @@ -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 +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-07-01: 0 rate: - 2022-07-01: 0 + 2022-07-01: 0 - threshold: 2022-07-01: 24 rate: 2022-07-01: 0.5 metadata: + type: marginal_rate + rate_unit: /1 + threshold_unit: currency-CAD period: year label: GIS reduction 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 + 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 index 56837a1e0..c2bd15fe0 100644 --- 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 @@ -1,4 +1,4 @@ -description: Reduction threshold and rate for pensioners married to pensioners. Applies to COUPLE_BOTH_OAS or COUPLE_ONE_OAS_SPA_ELIGIBLE. Note that this calculates on couples combined income, whereas the other GIS cateogires calculate on the lone pensioner's income. +description: Reduction threshold and rate for pensioners married to pensioners. Applies to COUPLE_BOTH_OAS or COUPLE_ONE_OAS_SPA_ELIGIBLE. 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-07-01: 0 @@ -7,7 +7,7 @@ brackets: - threshold: 2022-07-01: 48 rate: - 2022-07-01: 0.5 + 2022-07-01: 0.25 metadata: period: year label: GIS reduction for pensioners married to a pensioner. 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 index 1aa374c70..40d211f6c 100644 --- a/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_reduction.yaml +++ b/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_reduction.yaml @@ -1,49 +1,46 @@ - name: Single Pensioner - period: 2022 + period: 2023 input: - # gis_spa_category: "SINGLE_WITH_OAS" + gis_spa_category: "SINGLE_WITH_OAS" individual_net_income: 224 - # gis_base_amount: 9_449.87 output: gis_reduction: 100 -- name: Widow - period: 2022 - input: - gis_spa_category: "WIDOW_SPA_ELIGIBLE" - output: - gis_base_amount: 0 - - name: OAS pensioner whose spouse is SPA eligible - period: 2022 + period: 2023 input: gis_spa_category: "COUPLE_ONE_OAS_SPA_ELIGIBLE" - oas_eligible: true - output: + individual_net_income: 2_048 + spouse_net_income: 2_000 gis_base_amount: 6_240.18 + output: + gis_reduction: 1_000 -- name: OAS pensioner whose spouse is not SPA eligible - period: 2022 +- name: Two OAS pensioners + period: 2023 input: - gis_spa_category: "COUPLE_ONE_OAS_SPA_INELIGIBLE" - oas_eligible: true + gis_spa_category: "COUPLE_BOTH_OAS" + individual_net_income: 2_048 + spouse_net_income: 2_000 output: - gis_base_amount: 9_449.87 + gis_reduction: 1_000 -- name: SPA-eligible person whose spouse has OAS - period: 2022 +- name: OAS pensioner whose spouse is not eligible for SPA. + period: 2023 input: - gis_spa_category: "COUPLE_ONE_OAS_SPA_ELIGIBLE" - oas_eligible: false + gis_spa_category: "COUPLE_ONE_OAS_SPA_INELIGIBLE" + individual_net_income: 2_024 + gis_base_amount: 6_240.18 + spouse_net_income: 2_000 output: - gis_base_amount: 0 + gis_reduction: 1_000 -- name: Person not eligible for OAS or SPA, but is married to an OAS pensioner - period: 2022 +- 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" - oas_eligible: false - output: + individual_net_income: 2_024 gis_base_amount: 0 - - + spouse_net_income: 2_000 + output: + gis_reduction: 0 \ No newline at end of file diff --git a/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_reduction.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_reduction.py index d3ff257b1..bd207b088 100644 --- a/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_reduction.py +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_reduction.py @@ -9,25 +9,19 @@ class gis_reduction(Variable): definition_period = YEAR def formula(person, period, parameters): - # gis_spa_category = person("gis_spa_category", period) - # gis_spa_categories = gis_spa_category.possible_values + gis_spa_category = person("gis_spa_category", period) + gis_spa_categories = gis_spa_category.possible_values individual_net_income = person("individual_net_income", period) - # household = person.household - # spouse_net_income = household("spouse_net_income", period) - # gis_base = person("gis_base_amount", period) + household = person.household + spouse_net_income = household("spouse_net_income", period) + gis_base = person("gis_base_amount", period) p = parameters(period).gov.cra.benefits.gis_spa.gis_reduction - reduction = p.one_pensioner.calc(individual_net_income) - - return(reduction) - - - - select( + 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 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_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) ], [ @@ -39,4 +33,6 @@ def formula(person, period, parameters): default=0, ) + return(max(reduction, 0)) + From af039575ea9c1afde79848243dad51554255cb83 Mon Sep 17 00:00:00 2001 From: Alex Rand Date: Mon, 13 Feb 2023 09:33:41 -0500 Subject: [PATCH 14/24] Add the SPA amounts and reductions --- .../cra/benefits/gis_spa/spa_cap/gis_portion.yaml | 9 +++++++++ .../cra/benefits/gis_spa/spa_cap/oas_portion.yaml | 9 +++++++++ .../gov/cra/benefits/gis_spa/spa_gis_portion.yaml | 7 +++++++ .../gov/cra/benefits/gis_spa/spa_oas_portion.yaml | 7 +++++++ .../gov/cra/benefits/gis_spa/spa_gis_portion.py | 15 +++++++++++++++ .../gov/cra/benefits/gis_spa/spa_oas_portion.py | 15 +++++++++++++++ 6 files changed, 62 insertions(+) create mode 100644 policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_cap/gis_portion.yaml create mode 100644 policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_cap/oas_portion.yaml create mode 100644 policyengine_canada/tests/gov/cra/benefits/gis_spa/spa_gis_portion.yaml create mode 100644 policyengine_canada/tests/gov/cra/benefits/gis_spa/spa_oas_portion.yaml create mode 100644 policyengine_canada/variables/gov/cra/benefits/gis_spa/spa_gis_portion.py create mode 100644 policyengine_canada/variables/gov/cra/benefits/gis_spa/spa_oas_portion.py 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/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_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/variables/gov/cra/benefits/gis_spa/spa_gis_portion.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/spa_gis_portion.py new file mode 100644 index 000000000..038c91a83 --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/spa_gis_portion.py @@ -0,0 +1,15 @@ +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_oas_portion.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/spa_oas_portion.py new file mode 100644 index 000000000..99a1a8888 --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/spa_oas_portion.py @@ -0,0 +1,15 @@ +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) From 9ff20678cd628eadb235d005e90726b771f94d22 Mon Sep 17 00:00:00 2001 From: Alex Rand Date: Mon, 13 Feb 2023 09:33:41 -0500 Subject: [PATCH 15/24] Add the SPA amounts and reductions --- .../benefits/gis_spa/spa_cap/gis_portion.yaml | 9 +++++++++ .../benefits/gis_spa/spa_cap/oas_portion.yaml | 9 +++++++++ .../spa_reduction/spa_gis_reduction.yaml | 9 +++++++++ .../spa_reduction/spa_oas_reduction.yaml | 9 +++++++++ .../cra/benefits/gis_spa/gis_base_amount.yaml | 12 ++++++------ .../gov/cra/benefits/gis_spa/gis_net.yaml | 15 +++++++++++++++ .../cra/benefits/gis_spa/gis_reduction.yaml | 6 +++--- .../cra/benefits/gis_spa/spa_gis_portion.yaml | 7 +++++++ .../benefits/gis_spa/spa_gis_reduction.yaml | 8 ++++++++ .../cra/benefits/gis_spa/spa_oas_portion.yaml | 7 +++++++ .../benefits/gis_spa/spa_oas_reduction.yaml | 7 +++++++ .../{gis_base_amount.py => gis/gis_cap.py} | 4 ++-- .../gov/cra/benefits/gis_spa/gis/gis_net.py | 18 ++++++++++++++++++ .../gis_spa/{ => gis}/gis_reduction.py | 4 ++-- .../benefits/gis_spa/spa/spa_gis_portion.py | 18 ++++++++++++++++++ .../benefits/gis_spa/spa/spa_gis_reduction.py | 17 +++++++++++++++++ .../benefits/gis_spa/spa/spa_oas_portion.py | 15 +++++++++++++++ .../benefits/gis_spa/spa/spa_oas_reduction.py | 17 +++++++++++++++++ 18 files changed, 178 insertions(+), 13 deletions(-) create mode 100644 policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_cap/gis_portion.yaml create mode 100644 policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_cap/oas_portion.yaml create mode 100644 policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_reduction/spa_gis_reduction.yaml create mode 100644 policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_reduction/spa_oas_reduction.yaml create mode 100644 policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_net.yaml create mode 100644 policyengine_canada/tests/gov/cra/benefits/gis_spa/spa_gis_portion.yaml create mode 100644 policyengine_canada/tests/gov/cra/benefits/gis_spa/spa_gis_reduction.yaml create mode 100644 policyengine_canada/tests/gov/cra/benefits/gis_spa/spa_oas_portion.yaml create mode 100644 policyengine_canada/tests/gov/cra/benefits/gis_spa/spa_oas_reduction.yaml rename policyengine_canada/variables/gov/cra/benefits/gis_spa/{gis_base_amount.py => gis/gis_cap.py} (94%) create mode 100644 policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_net.py rename policyengine_canada/variables/gov/cra/benefits/gis_spa/{ => gis}/gis_reduction.py (95%) create mode 100644 policyengine_canada/variables/gov/cra/benefits/gis_spa/spa/spa_gis_portion.py create mode 100644 policyengine_canada/variables/gov/cra/benefits/gis_spa/spa/spa_gis_reduction.py create mode 100644 policyengine_canada/variables/gov/cra/benefits/gis_spa/spa/spa_oas_portion.py create mode 100644 policyengine_canada/variables/gov/cra/benefits/gis_spa/spa/spa_oas_reduction.py 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..54d7c59e0 --- /dev/null +++ b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/spa_reduction/spa_oas_reduction.yaml @@ -0,0 +1,9 @@ +description: Reduction rate for the OAS portion of the SPA. +values: + 1999-12-31: .75 +metadata: + period: year + label: Reduction rate for the OAS portion of the SPA. + reference: + - title: SPSD/M Algorithm 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 index 32b88ddd6..27dc8a26f 100644 --- 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 @@ -4,14 +4,14 @@ gis_spa_category: "SINGLE_WITH_OAS" oas_eligible: true output: - gis_base_amount: 9_449.87 + gis_cap: 9_449.87 - name: Widow period: 2022 input: gis_spa_category: "WIDOW_SPA_ELIGIBLE" output: - gis_base_amount: 0 + gis_cap: 0 - name: OAS pensioner whose spouse is SPA eligible period: 2022 @@ -19,7 +19,7 @@ gis_spa_category: "COUPLE_ONE_OAS_SPA_ELIGIBLE" oas_eligible: true output: - gis_base_amount: 6_240.18 + gis_cap: 6_240.18 - name: OAS pensioner whose spouse is not SPA eligible period: 2022 @@ -27,7 +27,7 @@ gis_spa_category: "COUPLE_ONE_OAS_SPA_INELIGIBLE" oas_eligible: true output: - gis_base_amount: 9_449.87 + gis_cap: 9_449.87 - name: SPA-eligible person whose spouse has OAS period: 2022 @@ -35,7 +35,7 @@ gis_spa_category: "COUPLE_ONE_OAS_SPA_ELIGIBLE" oas_eligible: false output: - gis_base_amount: 0 + gis_cap: 0 - name: Person not eligible for OAS or SPA, but is married to an OAS pensioner period: 2022 @@ -43,6 +43,6 @@ gis_spa_category: "COUPLE_ONE_OAS_SPA_INELIGIBLE" oas_eligible: false output: - gis_base_amount: 0 + gis_cap: 0 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..62daac570 --- /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 \ No newline at end of file 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 index 40d211f6c..c915ef8d4 100644 --- a/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_reduction.yaml +++ b/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_reduction.yaml @@ -12,7 +12,7 @@ gis_spa_category: "COUPLE_ONE_OAS_SPA_ELIGIBLE" individual_net_income: 2_048 spouse_net_income: 2_000 - gis_base_amount: 6_240.18 + gis_cap: 6_240.18 output: gis_reduction: 1_000 @@ -30,7 +30,7 @@ input: gis_spa_category: "COUPLE_ONE_OAS_SPA_INELIGIBLE" individual_net_income: 2_024 - gis_base_amount: 6_240.18 + gis_cap: 6_240.18 spouse_net_income: 2_000 output: gis_reduction: 1_000 @@ -40,7 +40,7 @@ input: gis_spa_category: "COUPLE_ONE_OAS_SPA_INELIGIBLE" individual_net_income: 2_024 - gis_base_amount: 0 + gis_cap: 0 spouse_net_income: 2_000 output: gis_reduction: 0 \ No newline at end of file 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..8d0406935 --- /dev/null +++ b/policyengine_canada/tests/gov/cra/benefits/gis_spa/spa_gis_reduction.yaml @@ -0,0 +1,8 @@ +- name: The default rate of 25% of net household income + period: 2022 + input: + individual_net_income: 500 + spouse_net_income: 500 + output: + spa_gis_reduction: 250 + 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..5122f8253 --- /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 + period: 2022 + input: + individual_net_income: 1000 + output: + spa_oas_reduction: 750 + diff --git a/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_base_amount.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_cap.py similarity index 94% rename from policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_base_amount.py rename to policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_cap.py index 9948cf7dc..5eea3c298 100644 --- a/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_base_amount.py +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_cap.py @@ -1,9 +1,9 @@ from policyengine_canada.model_api import * -class gis_base_amount(Variable): +class gis_cap(Variable): value_type = float entity = Person - label = "Guaranteed Income Supplement Base Amount" + 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 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..051895959 --- /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_reduction.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_reduction.py similarity index 95% rename from policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_reduction.py rename to policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_reduction.py index bd207b088..16d241426 100644 --- a/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_reduction.py +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_reduction.py @@ -14,7 +14,7 @@ def formula(person, period, parameters): individual_net_income = person("individual_net_income", period) household = person.household spouse_net_income = household("spouse_net_income", period) - gis_base = person("gis_base_amount", period) + gis_base = person("gis_cap", period) p = parameters(period).gov.cra.benefits.gis_spa.gis_reduction reduction = select( @@ -33,6 +33,6 @@ def formula(person, period, parameters): default=0, ) - return(max(reduction, 0)) + return(reduction) 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..de0f86bde --- /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..19de627de --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/spa/spa_gis_reduction.py @@ -0,0 +1,17 @@ +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) + p = parameters(period).gov.cra.benefits.gis_spa.spa_reduction + + return((individual_net_income + spouse_net_income) * p.spa_gis_reduction) \ No newline at end of file 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..99a1a8888 --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/spa/spa_oas_portion.py @@ -0,0 +1,15 @@ +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..774f70bb9 --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/spa/spa_oas_reduction.py @@ -0,0 +1,17 @@ +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((individual_net_income + spouse_net_income) * p.spa_oas_reduction) \ No newline at end of file From de4d21f1a5fe138730d9aa715da6dd90881b0604 Mon Sep 17 00:00:00 2001 From: Alex Rand Date: Sun, 19 Feb 2023 09:52:53 -0500 Subject: [PATCH 16/24] breakeven variables --- .../gis_spa/gis_reduction/one_pensioner.yaml | 1 + .../gis_spa/gis_reduction/two_pensioners.yaml | 2 +- .../benefits/gis_spa/breakeven-spa-eligible.py | 15 +++++++++++++++ .../benefits/gis_spa/breakeven-spa-ineligible.py | 15 +++++++++++++++ 4 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 policyengine_canada/variables/gov/cra/benefits/gis_spa/breakeven-spa-eligible.py create mode 100644 policyengine_canada/variables/gov/cra/benefits/gis_spa/breakeven-spa-ineligible.py 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 index fd0642d5d..9321404a3 100644 --- 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 @@ -13,6 +13,7 @@ metadata: 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 index c2bd15fe0..f587b0ce1 100644 --- 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 @@ -11,7 +11,7 @@ brackets: metadata: period: year label: GIS reduction for pensioners married to a pensioner. - documentataion: Corresponds to GISRRS and GISRLS in the SPSD/M Variable Guide + documentataion: Corresponds to GISRRM and GISRLM in the SPSD/M Variable Guide reference: - title: SPSD/M Variable Guide 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..802f832a6 --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/breakeven-spa-eligible.py @@ -0,0 +1,15 @@ +from policyengine_canada.model_api import * + +class breakeven_spa_ineligible(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[rate])) + (p_oas.amount.base / p_gis_spa.spa_reduction_spa_oas_reduction[rate[2]]) + p_gis_spa.gis_reduction.two.pensioners[threshold]) \ No newline at end of file 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..a96c84417 --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/breakeven-spa-ineligible.py @@ -0,0 +1,15 @@ +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[rate]) + p_oas.amount.base + p_gis_spa.gis_reduction.one.pensioner[threshold]) \ No newline at end of file From 445ed44b626432ced7cc7d4aeab3aa3beaeb32d9 Mon Sep 17 00:00:00 2001 From: Alex Rand Date: Mon, 20 Feb 2023 09:05:31 -0500 Subject: [PATCH 17/24] update entities.py to include 'state' --- policyengine_canada/entities.py | 16 +++++++++++++++- .../benefits/gis_spa/breakeven_spa_eligible.yaml | 5 +++++ .../benefits/gis_spa/breakeven-spa-eligible.py | 6 ++++-- 3 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 policyengine_canada/tests/gov/cra/benefits/gis_spa/breakeven_spa_eligible.yaml 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/tests/gov/cra/benefits/gis_spa/breakeven_spa_eligible.yaml b/policyengine_canada/tests/gov/cra/benefits/gis_spa/breakeven_spa_eligible.yaml new file mode 100644 index 000000000..69b5e0ead --- /dev/null +++ b/policyengine_canada/tests/gov/cra/benefits/gis_spa/breakeven_spa_eligible.yaml @@ -0,0 +1,5 @@ +- name: testing + period: 2023 + output: + breakeven_spa_eligible: .25 + 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 index 802f832a6..1cae0ac0a 100644 --- 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 @@ -1,6 +1,6 @@ from policyengine_canada.model_api import * -class breakeven_spa_ineligible(Variable): +class breakeven_spa_eligible(Variable): value_type = float entity = State label = "Breakeven household income for SPA-eligible couples" @@ -12,4 +12,6 @@ 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[rate])) + (p_oas.amount.base / p_gis_spa.spa_reduction_spa_oas_reduction[rate[2]]) + p_gis_spa.gis_reduction.two.pensioners[threshold]) \ No newline at end of file + return(p_gis_spa.gis_reduction.two_pensioners[1].rate) + + # return(((2 * p_gis_spa.gis_cap.two_pensioners) / (2 * p_gis_spa.gis_reduction.two.pensioners[rate])) + (p_oas.amount.base / p_gis_spa.spa_reduction_spa_oas_reduction[rate[2]]) + p_gis_spa.gis_reduction.two.pensioners[threshold]) \ No newline at end of file From fe38e712a204d0deb7622ebb9a6075c3f6db0c63 Mon Sep 17 00:00:00 2001 From: Alex Rand Date: Tue, 21 Feb 2023 10:07:37 -0500 Subject: [PATCH 18/24] fix some years, add crossover variable --- .../benefits/gis_spa/gis_cap/one_pensioner.yaml | 2 +- .../benefits/gis_spa/gis_cap/two_pensioners.yaml | 2 +- .../gis_spa/gis_reduction/one_pensioner.yaml | 8 ++++---- .../gis_spa/gis_reduction/two_pensioners.yaml | 8 ++++---- .../gis_spa/spa_reduction/spa_oas_reduction.yaml | 8 ++++---- .../benefits/gis_spa/breakeven_spa_eligible.yaml | 8 +++++--- .../benefits/gis_spa/breakeven-spa-eligible.py | 4 +--- .../benefits/gis_spa/breakeven-spa-ineligible.py | 2 +- .../gov/cra/benefits/gis_spa/crossover.py | 15 +++++++++++++++ .../gov/cra/benefits/gis_spa/gis/gis_reduction.py | 2 +- 10 files changed, 37 insertions(+), 22 deletions(-) create mode 100644 policyengine_canada/variables/gov/cra/benefits/gis_spa/crossover.py 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 index 9a8e492d6..fc613fe6f 100644 --- 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 @@ -1,6 +1,6 @@ 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: - 1999-12-31: 9_449.87 + 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 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 index 89021ddd4..14ab05724 100644 --- 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 @@ -1,6 +1,6 @@ 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-12-31: 6_240.18 + 2021-01-01: 6_240.18 metadata: period: year label: GIS Cap for pensioners married to a pensioner. 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 index 9321404a3..d76b0397d 100644 --- 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 @@ -1,13 +1,13 @@ 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-07-01: 0 + 2021-01-01: 0 rate: - 2022-07-01: 0 + 2021-01-01: 0 - threshold: - 2022-07-01: 24 + 2021-01-01: 24 rate: - 2022-07-01: 0.5 + 2021-01-01: 0.5 metadata: type: marginal_rate rate_unit: /1 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 index f587b0ce1..22d170bbb 100644 --- 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 @@ -1,13 +1,13 @@ description: Reduction threshold and rate for pensioners married to pensioners. Applies to COUPLE_BOTH_OAS or COUPLE_ONE_OAS_SPA_ELIGIBLE. 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-07-01: 0 + 2021-01-01: 0 rate: - 2022-07-01: 0 + 2021-01-01: 0 - threshold: - 2022-07-01: 48 + 2021-01-01: 48 rate: - 2022-07-01: 0.25 + 2021-01-01: 0.25 metadata: period: year label: GIS reduction for pensioners married to a pensioner. 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 index 9bffdccce..793214c62 100644 --- 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 @@ -1,13 +1,13 @@ description: Reduction rate for the OAS portion of the SPA. See SPARL in the SPSD/M Parameter Guide. brackets: - threshold: - 2021-07-01: 0 + 2021-01-01: 0 rate: - 2022-07-01: 0 + 2021-01-01: 0 - threshold: - 2022-07-01: 48 + 2021-01-01: 48 rate: - 2022-07-01: 0.75 + 2021-01-01: 0.75 metadata: type: marginal_rate rate_unit: /1 diff --git a/policyengine_canada/tests/gov/cra/benefits/gis_spa/breakeven_spa_eligible.yaml b/policyengine_canada/tests/gov/cra/benefits/gis_spa/breakeven_spa_eligible.yaml index 69b5e0ead..0f5d53dd1 100644 --- a/policyengine_canada/tests/gov/cra/benefits/gis_spa/breakeven_spa_eligible.yaml +++ b/policyengine_canada/tests/gov/cra/benefits/gis_spa/breakeven_spa_eligible.yaml @@ -1,5 +1,7 @@ -- name: testing +- 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: .25 - + breakeven_spa_eligible: 35976.72 + breakeven_spa_ineligible: 46025.98 + gis_spa_crossover: 25927.45 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 index 1cae0ac0a..b57a803a1 100644 --- 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 @@ -12,6 +12,4 @@ 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_reduction.two_pensioners[1].rate) - - # return(((2 * p_gis_spa.gis_cap.two_pensioners) / (2 * p_gis_spa.gis_reduction.two.pensioners[rate])) + (p_oas.amount.base / p_gis_spa.spa_reduction_spa_oas_reduction[rate[2]]) + p_gis_spa.gis_reduction.two.pensioners[threshold]) \ No newline at end of file + 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]) \ No newline at end of file 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 index a96c84417..1a788d6e4 100644 --- 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 @@ -12,4 +12,4 @@ 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[rate]) + p_oas.amount.base + p_gis_spa.gis_reduction.one.pensioner[threshold]) \ No newline at end of file + 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]) \ No newline at end of file diff --git a/policyengine_canada/variables/gov/cra/benefits/gis_spa/crossover.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/crossover.py new file mode 100644 index 000000000..83761825d --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/crossover.py @@ -0,0 +1,15 @@ +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) \ No newline at end of file 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 index 16d241426..e3cca1981 100644 --- 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 @@ -21,7 +21,7 @@ def formula(person, period, parameters): [ 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_ELIGIBLE) & (gis_base > 0), # THIS NEEDS TO CHANGE BASED ON THE 'CROSSOVER' THING. # 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) ], [ From 6bc88e5655a0a43dd0c7911452bf31d4d7419901 Mon Sep 17 00:00:00 2001 From: Alex Rand Date: Thu, 23 Feb 2023 11:24:49 -0500 Subject: [PATCH 19/24] added gis-spa couples gis reduction plateau --- .../gis_spa_couple_plateau_rate.yaml | 10 ++++++ .../gis_spa/gis_reduction/two_pensioners.yaml | 2 +- .../cra/benefits/gis_spa/gis_reduction.yaml | 18 +++++----- .../gis_spa/gis_reduction_spa_couple.yaml | 32 +++++++++++++++++ ...le.yaml => gis_spa_couple_thresholds.yaml} | 0 .../cra/benefits/gis_spa/gis/gis_reduction.py | 5 +-- .../gis_spa/gis/gis_reduction_spa_couple.py | 35 +++++++++++++++++++ .../{crossover.py => gis_spa_crossover.py} | 0 8 files changed, 90 insertions(+), 12 deletions(-) create mode 100644 policyengine_canada/parameters/gov/cra/benefits/gis_spa/gis_reduction/gis_spa_couple_plateau_rate.yaml create mode 100644 policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_reduction_spa_couple.yaml rename policyengine_canada/tests/gov/cra/benefits/gis_spa/{breakeven_spa_eligible.yaml => gis_spa_couple_thresholds.yaml} (100%) create mode 100644 policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_reduction_spa_couple.py rename policyengine_canada/variables/gov/cra/benefits/gis_spa/{crossover.py => gis_spa_crossover.py} (100%) 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/two_pensioners.yaml b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/gis_reduction/two_pensioners.yaml index 22d170bbb..09dd06050 100644 --- 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 @@ -1,4 +1,4 @@ -description: Reduction threshold and rate for pensioners married to pensioners. Applies to COUPLE_BOTH_OAS or COUPLE_ONE_OAS_SPA_ELIGIBLE. 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. +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 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 index c915ef8d4..006a5131a 100644 --- a/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_reduction.yaml +++ b/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_reduction.yaml @@ -6,15 +6,15 @@ output: gis_reduction: 100 -- name: OAS pensioner whose spouse is SPA eligible - period: 2023 - input: - gis_spa_category: "COUPLE_ONE_OAS_SPA_ELIGIBLE" - individual_net_income: 2_048 - spouse_net_income: 2_000 - gis_cap: 6_240.18 - output: - gis_reduction: 1_000 +# - name: OAS pensioner whose spouse is SPA eligible +# period: 2023 +# input: +# gis_spa_category: "COUPLE_ONE_OAS_SPA_ELIGIBLE" +# individual_net_income: 2_048 +# spouse_net_income: 2_000 +# gis_cap: 6_240.18 +# output: +# gis_reduction: 1_000 - name: Two OAS pensioners period: 2023 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..cb4c1c61d --- /dev/null +++ b/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_reduction_spa_couple.yaml @@ -0,0 +1,32 @@ +- 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" + individual_net_income: 2_048 + spouse_net_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" + individual_net_income: 15_048 + spouse_net_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" + individual_net_income: 20_048 + spouse_net_income: 20_000 + output: + gis_reduction_spa_couple: 7487.68 + + + diff --git a/policyengine_canada/tests/gov/cra/benefits/gis_spa/breakeven_spa_eligible.yaml b/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_spa_couple_thresholds.yaml similarity index 100% rename from policyengine_canada/tests/gov/cra/benefits/gis_spa/breakeven_spa_eligible.yaml rename to policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_spa_couple_thresholds.yaml 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 index e3cca1981..a04bf194d 100644 --- 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 @@ -21,14 +21,15 @@ def formula(person, period, parameters): [ 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), # THIS NEEDS TO CHANGE BASED ON THE 'CROSSOVER' THING. # 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_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(individual_net_income), p.two_pensioners.calc(individual_net_income + spouse_net_income), - p.two_pensioners.calc(individual_net_income + spouse_net_income), + p.two_pensioners.calc(individual_net_income + spouse_net_income), # THIS NEEDS TO CHANGE BASED ON THE 'CROSSOVER' THING. p.one_pensioner.calc(individual_net_income) + #person("gis_reduction_spa_couple", period) ], default=0, ) 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..cbcfca83d --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_reduction_spa_couple.py @@ -0,0 +1,35 @@ +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): + gis_spa_category = person("gis_spa_category", period) + gis_spa_categories = gis_spa_category.possible_values + individual_net_income = person("individual_net_income", period) + household = person.household + spouse_net_income = household("spouse_net_income", period) + combined_net_income = individual_net_income + spouse_net_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_net_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_net_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_net_income - (crossover - 48) - plateau_range) * ((combined_net_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/crossover.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_spa_crossover.py similarity index 100% rename from policyengine_canada/variables/gov/cra/benefits/gis_spa/crossover.py rename to policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_spa_crossover.py From 94d680ab5949f4d7d38fa2e3bd17b7dffe55d70c Mon Sep 17 00:00:00 2001 From: Alex Rand Date: Thu, 23 Feb 2023 11:24:49 -0500 Subject: [PATCH 20/24] added gis-spa couples gis reduction plateau --- .../gis_spa_couple_plateau_rate.yaml | 10 +++++ .../gis_spa/gis_reduction/two_pensioners.yaml | 2 +- .../cra/benefits/gis_spa/gis_reduction.yaml | 13 +++++- .../gis_spa/gis_reduction_spa_couple.yaml | 42 +++++++++++++++++++ ...le.yaml => gis_spa_couple_thresholds.yaml} | 0 .../cra/benefits/gis_spa/gis/gis_reduction.py | 5 ++- .../gis_spa/gis/gis_reduction_spa_couple.py | 35 ++++++++++++++++ .../{crossover.py => gis_spa_crossover.py} | 0 8 files changed, 103 insertions(+), 4 deletions(-) create mode 100644 policyengine_canada/parameters/gov/cra/benefits/gis_spa/gis_reduction/gis_spa_couple_plateau_rate.yaml create mode 100644 policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_reduction_spa_couple.yaml rename policyengine_canada/tests/gov/cra/benefits/gis_spa/{breakeven_spa_eligible.yaml => gis_spa_couple_thresholds.yaml} (100%) create mode 100644 policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_reduction_spa_couple.py rename policyengine_canada/variables/gov/cra/benefits/gis_spa/{crossover.py => gis_spa_crossover.py} (100%) 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/two_pensioners.yaml b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/gis_reduction/two_pensioners.yaml index 22d170bbb..09dd06050 100644 --- 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 @@ -1,4 +1,4 @@ -description: Reduction threshold and rate for pensioners married to pensioners. Applies to COUPLE_BOTH_OAS or COUPLE_ONE_OAS_SPA_ELIGIBLE. 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. +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 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 index c915ef8d4..29a9c8ceb 100644 --- a/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_reduction.yaml +++ b/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_reduction.yaml @@ -6,7 +6,7 @@ output: gis_reduction: 100 -- name: OAS pensioner whose spouse is SPA eligible +- 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" @@ -16,6 +16,17 @@ 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" + individual_net_income: 20_048 + spouse_net_income: 20_000 + gis_cap: 30000 + output: + gis_reduction: 7487.68 + - name: Two OAS pensioners period: 2023 input: 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..e09d74f58 --- /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" + individual_net_income: 2_048 + spouse_net_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" + individual_net_income: 15_048 + spouse_net_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" + individual_net_income: 20_048 + spouse_net_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" + individual_net_income: 20_048 + spouse_net_income: 20_000 + output: + gis_reduction_spa_couple: 0 + + + diff --git a/policyengine_canada/tests/gov/cra/benefits/gis_spa/breakeven_spa_eligible.yaml b/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_spa_couple_thresholds.yaml similarity index 100% rename from policyengine_canada/tests/gov/cra/benefits/gis_spa/breakeven_spa_eligible.yaml rename to policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_spa_couple_thresholds.yaml 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 index e3cca1981..21e519eda 100644 --- 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 @@ -15,19 +15,20 @@ def formula(person, period, parameters): household = person.household spouse_net_income = household("spouse_net_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), # THIS NEEDS TO CHANGE BASED ON THE 'CROSSOVER' THING. # 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_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(individual_net_income), p.two_pensioners.calc(individual_net_income + spouse_net_income), - p.two_pensioners.calc(individual_net_income + spouse_net_income), + gis_reduction_spa_couple, p.one_pensioner.calc(individual_net_income) ], default=0, 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..cbcfca83d --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_reduction_spa_couple.py @@ -0,0 +1,35 @@ +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): + gis_spa_category = person("gis_spa_category", period) + gis_spa_categories = gis_spa_category.possible_values + individual_net_income = person("individual_net_income", period) + household = person.household + spouse_net_income = household("spouse_net_income", period) + combined_net_income = individual_net_income + spouse_net_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_net_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_net_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_net_income - (crossover - 48) - plateau_range) * ((combined_net_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/crossover.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_spa_crossover.py similarity index 100% rename from policyengine_canada/variables/gov/cra/benefits/gis_spa/crossover.py rename to policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_spa_crossover.py From dc7bae116233948d931e6ead0ea7501be5dc1347 Mon Sep 17 00:00:00 2001 From: Alex Rand Date: Mon, 13 Mar 2023 10:03:44 -0400 Subject: [PATCH 21/24] add GIS/SPA top-up caps --- .../gis_spa/topup_cap copy/married.yaml | 10 ++++++ .../gis_spa/topup_cap copy/singles.yaml | 11 +++++++ .../gov/cra/benefits/gis_spa/gis/gis_topup.py | 32 +++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 policyengine_canada/parameters/gov/cra/benefits/gis_spa/topup_cap copy/married.yaml create mode 100644 policyengine_canada/parameters/gov/cra/benefits/gis_spa/topup_cap copy/singles.yaml create mode 100644 policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_topup.py diff --git a/policyengine_canada/parameters/gov/cra/benefits/gis_spa/topup_cap copy/married.yaml b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/topup_cap copy/married.yaml new file mode 100644 index 000000000..2ec7fdc45 --- /dev/null +++ b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/topup_cap copy/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 copy/singles.yaml b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/topup_cap copy/singles.yaml new file mode 100644 index 000000000..c01779465 --- /dev/null +++ b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/topup_cap copy/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/variables/gov/cra/benefits/gis_spa/gis/gis_topup.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_topup.py new file mode 100644 index 000000000..dc24e60fd --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_topup.py @@ -0,0 +1,32 @@ +from policyengine_canada.model_api import * + +class gis_topup(Variable): + value_type = float + entity = Person + label = "Guaranteed Income Supplement Top-up" + 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, + ) From 60f73f01af42ae19d35c7ddcacbbff075960f0b7 Mon Sep 17 00:00:00 2001 From: Alex Rand Date: Tue, 14 Mar 2023 09:59:36 -0400 Subject: [PATCH 22/24] GIS top-up reduction var, params, test --- .../married.yaml | 0 .../singles.yaml | 0 .../gis_spa/topup_reduction/married.yaml | 17 ++++++ .../gis_spa/topup_reduction/singles.yaml | 17 ++++++ .../cra/benefits/gis_spa/gis_topup_cap.yaml | 54 +++++++++++++++++++ .../benefits/gis_spa/gis_topup_reduction.yaml | 41 ++++++++++++++ .../cra/benefits/gis_spa/gis/gis_reduction.py | 1 - .../gis/{gis_topup.py => gis_topup_cap.py} | 4 +- .../gis_spa/gis/gis_topup_reduction.py | 33 ++++++++++++ 9 files changed, 164 insertions(+), 3 deletions(-) rename policyengine_canada/parameters/gov/cra/benefits/gis_spa/{topup_cap copy => topup_cap}/married.yaml (100%) rename policyengine_canada/parameters/gov/cra/benefits/gis_spa/{topup_cap copy => topup_cap}/singles.yaml (100%) create mode 100644 policyengine_canada/parameters/gov/cra/benefits/gis_spa/topup_reduction/married.yaml create mode 100644 policyengine_canada/parameters/gov/cra/benefits/gis_spa/topup_reduction/singles.yaml create mode 100644 policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_topup_cap.yaml create mode 100644 policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_topup_reduction.yaml rename policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/{gis_topup.py => gis_topup_cap.py} (93%) create mode 100644 policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_topup_reduction.py diff --git a/policyengine_canada/parameters/gov/cra/benefits/gis_spa/topup_cap copy/married.yaml b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/topup_cap/married.yaml similarity index 100% rename from policyengine_canada/parameters/gov/cra/benefits/gis_spa/topup_cap copy/married.yaml rename to policyengine_canada/parameters/gov/cra/benefits/gis_spa/topup_cap/married.yaml diff --git a/policyengine_canada/parameters/gov/cra/benefits/gis_spa/topup_cap copy/singles.yaml b/policyengine_canada/parameters/gov/cra/benefits/gis_spa/topup_cap/singles.yaml similarity index 100% rename from policyengine_canada/parameters/gov/cra/benefits/gis_spa/topup_cap copy/singles.yaml rename to policyengine_canada/parameters/gov/cra/benefits/gis_spa/topup_cap/singles.yaml 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_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/variables/gov/cra/benefits/gis_spa/gis/gis_reduction.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_reduction.py index 3ebfcfe24..21e519eda 100644 --- 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 @@ -30,7 +30,6 @@ def formula(person, period, parameters): p.two_pensioners.calc(individual_net_income + spouse_net_income), gis_reduction_spa_couple, p.one_pensioner.calc(individual_net_income) - #person("gis_reduction_spa_couple", period) ], default=0, ) diff --git a/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_topup.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_topup_cap.py similarity index 93% rename from policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_topup.py rename to policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_topup_cap.py index dc24e60fd..7b2f877e8 100644 --- a/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_topup.py +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_topup_cap.py @@ -1,9 +1,9 @@ from policyengine_canada.model_api import * -class gis_topup(Variable): +class gis_topup_cap(Variable): value_type = float entity = Person - label = "Guaranteed Income Supplement Top-up" + 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 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..e69fb1063 --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_topup_reduction.py @@ -0,0 +1,33 @@ +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, + ) From 8dfe339fb4292652cd443d2db7c749787f3f837d Mon Sep 17 00:00:00 2001 From: Alex Rand Date: Wed, 15 Mar 2023 12:03:32 -0400 Subject: [PATCH 23/24] first full draft of gis/spa --- .../gis_spa/gis_exemption/gis_exemption.yaml | 25 +++++++++++++++++++ .../cra/benefits/gis_spa/gis_exemption.yaml | 7 ++++++ .../gov/cra/benefits/gis_spa/gis_income.yaml | 8 ++++++ .../cra/benefits/gis_spa/gis_reduction.yaml | 22 ++++++++-------- .../gis_spa/gis_reduction_spa_couple.yaml | 16 ++++++------ .../cra/benefits/gis_spa/gis/gis_exemption.py | 21 ++++++++++++++++ .../cra/benefits/gis_spa/gis/gis_income.py | 18 +++++++++++++ .../cra/benefits/gis_spa/gis/gis_reduction.py | 12 ++++----- .../gis_spa/gis/gis_reduction_spa_couple.py | 14 +++++------ .../cra/benefits/gis_spa/gis/gis_topup_net.py | 18 +++++++++++++ .../benefits/gis_spa/gis/spouse_gis_income.py | 17 +++++++++++++ 11 files changed, 146 insertions(+), 32 deletions(-) create mode 100644 policyengine_canada/parameters/gov/cra/benefits/gis_spa/gis_exemption/gis_exemption.yaml create mode 100644 policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_exemption.yaml create mode 100644 policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_income.yaml create mode 100644 policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_exemption.py create mode 100644 policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_income.py create mode 100644 policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_topup_net.py create mode 100644 policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/spouse_gis_income.py 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/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_reduction.yaml b/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_reduction.yaml index 29a9c8ceb..ed4b614a2 100644 --- a/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_reduction.yaml +++ b/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_reduction.yaml @@ -2,7 +2,7 @@ period: 2023 input: gis_spa_category: "SINGLE_WITH_OAS" - individual_net_income: 224 + gis_income: 224 output: gis_reduction: 100 @@ -10,8 +10,8 @@ period: 2023 input: gis_spa_category: "COUPLE_ONE_OAS_SPA_ELIGIBLE" - individual_net_income: 2_048 - spouse_net_income: 2_000 + gis_income: 2_048 + spouse_gis_income: 2_000 gis_cap: 6_240.18 output: gis_reduction: 1_000 @@ -21,8 +21,8 @@ absolute_error_margin: .01 input: gis_spa_category: "COUPLE_ONE_OAS_SPA_ELIGIBLE" - individual_net_income: 20_048 - spouse_net_income: 20_000 + gis_income: 20_048 + spouse_gis_income: 20_000 gis_cap: 30000 output: gis_reduction: 7487.68 @@ -31,8 +31,8 @@ period: 2023 input: gis_spa_category: "COUPLE_BOTH_OAS" - individual_net_income: 2_048 - spouse_net_income: 2_000 + gis_income: 2_048 + spouse_gis_income: 2_000 output: gis_reduction: 1_000 @@ -40,9 +40,9 @@ period: 2023 input: gis_spa_category: "COUPLE_ONE_OAS_SPA_INELIGIBLE" - individual_net_income: 2_024 + gis_income: 2_024 gis_cap: 6_240.18 - spouse_net_income: 2_000 + spouse_gis_income: 2_000 output: gis_reduction: 1_000 @@ -50,8 +50,8 @@ period: 2023 input: gis_spa_category: "COUPLE_ONE_OAS_SPA_INELIGIBLE" - individual_net_income: 2_024 + gis_income: 2_024 gis_cap: 0 - spouse_net_income: 2_000 + spouse_gis_income: 2_000 output: gis_reduction: 0 \ No newline at end of file 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 index e09d74f58..5758d3b8f 100644 --- 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 @@ -3,8 +3,8 @@ absolute_error_margin: .01 input: gis_spa_category: "COUPLE_ONE_OAS_SPA_ELIGIBLE" - individual_net_income: 2_048 - spouse_net_income: 2_000 + gis_income: 2_048 + spouse_gis_income: 2_000 output: gis_reduction_spa_couple: 1_000 @@ -13,8 +13,8 @@ absolute_error_margin: .01 input: gis_spa_category: "COUPLE_ONE_OAS_SPA_ELIGIBLE" - individual_net_income: 15_048 - spouse_net_income: 15_000 + gis_income: 15_048 + spouse_gis_income: 15_000 output: gis_reduction_spa_couple: 6_469.86 @@ -23,8 +23,8 @@ absolute_error_margin: .01 input: gis_spa_category: "COUPLE_ONE_OAS_SPA_ELIGIBLE" - individual_net_income: 20_048 - spouse_net_income: 20_000 + gis_income: 20_048 + spouse_gis_income: 20_000 output: gis_reduction_spa_couple: 7487.68 @@ -33,8 +33,8 @@ absolute_error_margin: .01 input: gis_spa_category: "COUPLE_ONE_OAS_SPA_INELIGIBLE" - individual_net_income: 20_048 - spouse_net_income: 20_000 + gis_income: 20_048 + spouse_gis_income: 20_000 output: gis_reduction_spa_couple: 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..1b18c01ca --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_exemption.py @@ -0,0 +1,21 @@ +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..3ac53edd5 --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_income.py @@ -0,0 +1,18 @@ +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_reduction.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_reduction.py index 21e519eda..b1da2f229 100644 --- 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 @@ -9,11 +9,11 @@ class gis_reduction(Variable): 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 - individual_net_income = person("individual_net_income", period) - household = person.household - spouse_net_income = household("spouse_net_income", period) + 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 @@ -26,10 +26,10 @@ def formula(person, period, parameters): (gis_spa_category == gis_spa_categories.COUPLE_ONE_OAS_SPA_INELIGIBLE) & (gis_base > 0) ], [ - p.one_pensioner.calc(individual_net_income), - p.two_pensioners.calc(individual_net_income + spouse_net_income), + p.one_pensioner.calc(gis_income), + p.two_pensioners.calc(gis_income + spouse_gis_income), gis_reduction_spa_couple, - p.one_pensioner.calc(individual_net_income) + p.one_pensioner.calc(gis_income) ], default=0, ) 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 index cbcfca83d..99d8a2796 100644 --- 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 @@ -9,12 +9,12 @@ class gis_reduction_spa_couple(Variable): 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 - individual_net_income = person("individual_net_income", period) - household = person.household - spouse_net_income = household("spouse_net_income", period) - combined_net_income = individual_net_income + spouse_net_income + 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) @@ -23,11 +23,11 @@ def formula(person, period, parameters): 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_net_income), p.two_pensioners.calc(crossover)) + 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_net_income - first_part) * p.gis_spa_couple_plateau_rate, plateau_range * p.gis_spa_couple_plateau_rate) * (first_part == (p.two_pensioners.calc(crossover))) + 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_net_income - (crossover - 48) - plateau_range) * ((combined_net_income > (crossover - plateau_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_net.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_topup_net.py new file mode 100644 index 000000000..2972985a4 --- /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/spouse_gis_income.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/spouse_gis_income.py new file mode 100644 index 000000000..236968384 --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/spouse_gis_income.py @@ -0,0 +1,17 @@ +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) + + From ef84c1bbaac90c2a9851dd25fb3a261c2f628b6e Mon Sep 17 00:00:00 2001 From: Alex Rand Date: Wed, 15 Mar 2023 12:05:43 -0400 Subject: [PATCH 24/24] make format --- .../gov/cra/benefits/gis_spa/gis_net.yaml | 2 +- .../cra/benefits/gis_spa/gis_reduction.yaml | 2 +- .../gis_spa/breakeven-spa-eligible.py | 13 +++++- .../gis_spa/breakeven-spa-ineligible.py | 10 ++++- .../gov/cra/benefits/gis_spa/gis/gis_cap.py | 35 +++++++++------ .../cra/benefits/gis_spa/gis/gis_exemption.py | 12 +++--- .../cra/benefits/gis_spa/gis/gis_income.py | 5 +-- .../gov/cra/benefits/gis_spa/gis/gis_net.py | 10 ++--- .../cra/benefits/gis_spa/gis/gis_reduction.py | 43 +++++++++++-------- .../gis_spa/gis/gis_reduction_spa_couple.py | 27 ++++++++---- .../cra/benefits/gis_spa/gis/gis_topup_cap.py | 38 +++++++++------- .../cra/benefits/gis_spa/gis/gis_topup_net.py | 10 ++--- .../gis_spa/gis/gis_topup_reduction.py | 38 +++++++++------- .../benefits/gis_spa/gis/spouse_gis_income.py | 3 +- .../cra/benefits/gis_spa/gis_spa_crossover.py | 7 +-- .../cra/benefits/gis_spa/spa/spa_gis_net.py | 10 ++--- .../benefits/gis_spa/spa/spa_gis_portion.py | 7 ++- .../benefits/gis_spa/spa/spa_gis_reduction.py | 7 ++- .../cra/benefits/gis_spa/spa/spa_oas_net.py | 10 ++--- .../benefits/gis_spa/spa/spa_oas_portion.py | 7 ++- .../benefits/gis_spa/spa/spa_oas_reduction.py | 5 ++- 21 files changed, 187 insertions(+), 114 deletions(-) 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 index 62daac570..948f6e34b 100644 --- a/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_net.yaml +++ b/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_net.yaml @@ -12,4 +12,4 @@ gis_cap: 300 gis_reduction: 500 output: - gis_net: 0 \ No newline at end of file + 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 index ed4b614a2..141c76bcc 100644 --- a/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_reduction.yaml +++ b/policyengine_canada/tests/gov/cra/benefits/gis_spa/gis_reduction.yaml @@ -54,4 +54,4 @@ gis_cap: 0 spouse_gis_income: 2_000 output: - gis_reduction: 0 \ No newline at end of file + gis_reduction: 0 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 index b57a803a1..03b60fb40 100644 --- 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 @@ -1,5 +1,6 @@ from policyengine_canada.model_api import * + class breakeven_spa_eligible(Variable): value_type = float entity = State @@ -12,4 +13,14 @@ 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]) \ No newline at end of file + 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 index 1a788d6e4..a51ace6d6 100644 --- 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 @@ -1,5 +1,6 @@ from policyengine_canada.model_api import * + class breakeven_spa_ineligible(Variable): value_type = float entity = State @@ -12,4 +13,11 @@ 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]) \ No newline at end of file + 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/gis/gis_cap.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis/gis_cap.py index 5eea3c298..b15e9c22c 100644 --- 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 @@ -1,5 +1,6 @@ from policyengine_canada.model_api import * + class gis_cap(Variable): value_type = float entity = Person @@ -14,17 +15,25 @@ def formula(person, period, parameters): 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, + [ + 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 index 1b18c01ca..2887067b8 100644 --- 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 @@ -1,5 +1,6 @@ from policyengine_canada.model_api import * + class gis_exemption(Variable): value_type = float entity = Person @@ -11,11 +12,8 @@ class gis_exemption(Variable): 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) + 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 index 3ac53edd5..de447c746 100644 --- 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 @@ -1,5 +1,6 @@ from policyengine_canada.model_api import * + class gis_income(Variable): value_type = float entity = Person @@ -11,8 +12,6 @@ class gis_income(Variable): 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) + 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 index 051895959..18398496b 100644 --- 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 @@ -1,18 +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. " + 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)) - - + 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 index b1da2f229..07d6631a5 100644 --- 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 @@ -1,5 +1,6 @@ from policyengine_canada.model_api import * + class gis_reduction(Variable): value_type = float entity = Person @@ -17,23 +18,31 @@ def formula(person, period, parameters): 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, + [ + 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) - - + 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 index 99d8a2796..0b4f62c95 100644 --- 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 @@ -1,5 +1,6 @@ from policyengine_canada.model_api import * + class gis_reduction_spa_couple(Variable): value_type = float entity = Person @@ -22,14 +23,22 @@ def formula(person, period, parameters): 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)) + # 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) - + 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 index 7b2f877e8..f47215031 100644 --- 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 @@ -1,5 +1,6 @@ from policyengine_canada.model_api import * + class gis_topup_cap(Variable): value_type = float entity = Person @@ -14,19 +15,26 @@ def formula(person, period, parameters): 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, + [ + 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 index 2972985a4..eb9809144 100644 --- 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 @@ -1,18 +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. " + 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)) - - + 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 index e69fb1063..a203c25ff 100644 --- 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 @@ -1,5 +1,6 @@ from policyengine_canada.model_api import * + class gis_topup_reduction(Variable): value_type = float entity = Person @@ -15,19 +16,26 @@ def formula(person, period, parameters): 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, + [ + 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 index 236968384..232ff4c63 100644 --- 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 @@ -1,5 +1,6 @@ from policyengine_canada.model_api import * + class spouse_gis_income(Variable): value_type = int entity = Household @@ -13,5 +14,3 @@ def formula(household, period, parameters): 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_crossover.py b/policyengine_canada/variables/gov/cra/benefits/gis_spa/gis_spa_crossover.py index 83761825d..f0f8eb9e9 100644 --- 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 @@ -1,8 +1,9 @@ from policyengine_canada.model_api import * + class gis_spa_crossover(Variable): value_type = float - entity = State + 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." @@ -11,5 +12,5 @@ class gis_spa_crossover(Variable): 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) \ No newline at end of file + + 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 index 6272d5988..a6d82a580 100644 --- 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 @@ -1,18 +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." + 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)) - - + 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 index 00d32ca85..c4745b011 100644 --- 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 @@ -1,5 +1,6 @@ from policyengine_canada.model_api import * + class spa_gis_portion(Variable): value_type = float entity = Person @@ -10,6 +11,8 @@ class spa_gis_portion(Variable): def formula(person, period, parameters): spa_eligible = person("spa_eligible", period) - base_amount = parameters(period).gov.cra.benefits.gis_spa.spa_cap.gis_portion + base_amount = parameters( + period + ).gov.cra.benefits.gis_spa.spa_cap.gis_portion - return(base_amount * spa_eligible) \ No newline at end of file + 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 index ef3b17e4a..44c0825b6 100644 --- 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 @@ -1,5 +1,6 @@ from policyengine_canada.model_api import * + class spa_gis_reduction(Variable): value_type = float entity = Person @@ -17,4 +18,8 @@ def formula(person, period, parameters): 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)) \ No newline at end of file + 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 index 224a612af..3f8843b02 100644 --- 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 @@ -1,18 +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." + 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)) - - + 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 index 99a1a8888..aae1d08d7 100644 --- 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 @@ -1,5 +1,6 @@ from policyengine_canada.model_api import * + class spa_oas_portion(Variable): value_type = float entity = Person @@ -10,6 +11,8 @@ class spa_oas_portion(Variable): def formula(person, period, parameters): spa_eligible = person("spa_eligible", period) - base_amount = parameters(period).gov.cra.benefits.gis_spa.spa_cap.oas_portion + base_amount = parameters( + period + ).gov.cra.benefits.gis_spa.spa_cap.oas_portion - return(base_amount * spa_eligible) + 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 index 025dc80a8..4367edcc7 100644 --- 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 @@ -1,5 +1,6 @@ from policyengine_canada.model_api import * + class spa_oas_reduction(Variable): value_type = float entity = Person @@ -14,4 +15,6 @@ def formula(person, period, parameters): 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)) \ No newline at end of file + return p.spa_oas_reduction.calc( + individual_net_income + spouse_net_income + )