diff --git a/changelog.d/2151.fixed.md b/changelog.d/2151.fixed.md new file mode 100644 index 0000000000..5e300b1805 --- /dev/null +++ b/changelog.d/2151.fixed.md @@ -0,0 +1 @@ +- Corrected the 2020 over-75 TV licence Pension Credit transition to start on 1 August while preserving its Boolean eligibility rule. diff --git a/policyengine_uk/parameters/gov/dcms/bbc/tv_licence/discount/aged/discount.yaml b/policyengine_uk/parameters/gov/dcms/bbc/tv_licence/discount/aged/discount.yaml index 618d617809..019ba291b7 100644 --- a/policyengine_uk/parameters/gov/dcms/bbc/tv_licence/discount/aged/discount.yaml +++ b/policyengine_uk/parameters/gov/dcms/bbc/tv_licence/discount/aged/discount.yaml @@ -2,8 +2,9 @@ description: Percentage discount for qualifying aged households. values: 2003-01-01: 1 metadata: + preserve_calendar_dates: true unit: /1 label: Aged TV Licence discount reference: - title: "Free and reduced fee TV Licences | TV Licensing" - href: https://www.tvlicensing.co.uk/reducedfee \ No newline at end of file + href: https://www.tvlicensing.co.uk/reducedfee diff --git a/policyengine_uk/parameters/gov/dcms/bbc/tv_licence/discount/aged/min_age.yaml b/policyengine_uk/parameters/gov/dcms/bbc/tv_licence/discount/aged/min_age.yaml index 1125466471..ce4b410ea1 100644 --- a/policyengine_uk/parameters/gov/dcms/bbc/tv_licence/discount/aged/min_age.yaml +++ b/policyengine_uk/parameters/gov/dcms/bbc/tv_licence/discount/aged/min_age.yaml @@ -2,6 +2,7 @@ description: Minimum age required to qualify for a TV licence discount. values: 2003-01-01: 75 metadata: + preserve_calendar_dates: true unit: year label: Aged TV Licence discounted minimum age reference: diff --git a/policyengine_uk/parameters/gov/dcms/bbc/tv_licence/discount/aged/must_claim_pc.yaml b/policyengine_uk/parameters/gov/dcms/bbc/tv_licence/discount/aged/must_claim_pc.yaml index 6f2b4749d9..a3c9f3b21b 100644 --- a/policyengine_uk/parameters/gov/dcms/bbc/tv_licence/discount/aged/must_claim_pc.yaml +++ b/policyengine_uk/parameters/gov/dcms/bbc/tv_licence/discount/aged/must_claim_pc.yaml @@ -1,10 +1,16 @@ -description: Whether aged individuals must claim Pension Credit in order to qualify for a free TV licence. +description: Whether aged individuals must receive Pension Credit to qualify for a free TV licence. values: 2003-01-01: false - 2020-04-01: true + 2020-08-01: + value: true + reference: + - title: Government and BBC joint statement on delaying the TV licence fee for over 75s + href: https://www.gov.uk/government/news/bbc-and-governments-joint-statement-on-delaying-the-tv-licence-fee-for-over-75s metadata: + preserve_calendar_dates: true unit: bool + period: year label: Aged TV Licence Pension Credit requirement reference: - - title: "Free and reduced fee TV Licences | TV Licensing" - href: https://www.tvlicensing.co.uk/reducedfee \ No newline at end of file + - title: Get a free or discounted TV licence + href: https://www.gov.uk/free-discount-tv-licence diff --git a/policyengine_uk/tests/policy/baseline/gov/dcms/bbc/tv_licence_discount.yaml b/policyengine_uk/tests/policy/baseline/gov/dcms/bbc/tv_licence_discount.yaml index 35a1b110ba..e3f117baa7 100644 --- a/policyengine_uk/tests/policy/baseline/gov/dcms/bbc/tv_licence_discount.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/dcms/bbc/tv_licence_discount.yaml @@ -41,4 +41,41 @@ pension_credit: 1 is_blind: true output: - tv_licence_discount: 1 \ No newline at end of file + tv_licence_discount: 1 + +- name: All aged households qualify before the Pension Credit condition. + period: 2019 + input: + age: 75 + pension_credit: 0 + is_blind: false + output: + tv_licence_discount: 1 + +- name: The 2020-21 discount reflects the 1 August transition. + period: 2020 + absolute_error_margin: 0.000001 + input: + age: 75 + pension_credit: 0 + is_blind: false + output: + tv_licence_discount: 0.3205479452 + +- name: Pension Credit recipients retain the full 2020-21 discount. + period: 2020 + input: + age: 75 + pension_credit: 1 + is_blind: false + output: + tv_licence_discount: 1 + +- name: The Pension Credit condition applies for all of 2021-22. + period: 2021 + input: + age: 75 + pension_credit: 0 + is_blind: false + output: + tv_licence_discount: 0 diff --git a/policyengine_uk/tests/test_tv_licence_transition.py b/policyengine_uk/tests/test_tv_licence_transition.py new file mode 100644 index 0000000000..881ac67d51 --- /dev/null +++ b/policyengine_uk/tests/test_tv_licence_transition.py @@ -0,0 +1,81 @@ +"""Tests for the 2020 over-75 TV licence concession transition.""" + +from pathlib import Path + +import pytest +from policyengine_core.parameters import ParameterNode + +import policyengine_uk +from policyengine_uk import CountryTaxBenefitSystem, Simulation +from policyengine_uk.utils.excise import fiscal_year_segments + + +def _requirement(parameter_root): + return parameter_root.gov.dcms.bbc.tv_licence.discount.aged.must_claim_pc + + +def test_pension_credit_condition_uses_the_statutory_start_date(): + """The BBC deferred the condition from 1 June to 1 August 2020.""" + parameters = ParameterNode( + directory_path=str(Path(policyengine_uk.__file__).parent / "parameters") + ) + requirement = _requirement(parameters) + + assert requirement("2020-07-31") is False + assert requirement("2020-08-01") is True + + +def test_pension_credit_condition_keeps_its_date_after_processing(): + """Processing preserves the 1 August Boolean policy transition.""" + system = CountryTaxBenefitSystem() + requirement = _requirement(system.parameters) + + assert requirement("2020-07-31") is False + assert requirement("2020-08-01") is True + + +def test_pension_credit_condition_segments_the_2020_21_fiscal_year(): + """The fiscal year has 117 unrestricted and 248 restricted days.""" + system = CountryTaxBenefitSystem() + aged_rules = system.parameters.gov.dcms.bbc.tv_licence.discount.aged + segments = list(fiscal_year_segments(aged_rules, 2020)) + + unrestricted_share = sum( + share for rules, share in segments if not rules.must_claim_pc + ) + restricted_share = sum(share for rules, share in segments if rules.must_claim_pc) + + assert unrestricted_share == pytest.approx(117 / 365) + assert restricted_share == pytest.approx(248 / 365) + assert all(isinstance(rules.must_claim_pc, bool) for rules, _ in segments) + + +def test_year_keyed_minimum_age_reform_applies_to_one_fiscal_year(): + """A bare-year age reform must not be split across adjacent years.""" + years = (2024, 2025, 2026) + situation = { + "people": { + "person": { + "age": {str(year): 72 for year in years}, + }, + }, + "benunits": { + "benunit": { + "members": ["person"], + "pension_credit": {str(year): 1 for year in years}, + }, + }, + "households": { + "household": { + "members": ["person"], + }, + }, + } + reform = { + "gov.dcms.bbc.tv_licence.discount.aged.min_age": {"2025": 70}, + } + simulation = Simulation(situation=situation, reform=reform) + + discounts = [simulation.calculate("tv_licence_discount", year)[0] for year in years] + + assert discounts == pytest.approx([0, 1, 0]) diff --git a/policyengine_uk/variables/gov/dcms/bbc/tv_licence/tv_licence_discount.py b/policyengine_uk/variables/gov/dcms/bbc/tv_licence/tv_licence_discount.py index ffc499db15..655b1eca18 100644 --- a/policyengine_uk/variables/gov/dcms/bbc/tv_licence/tv_licence_discount.py +++ b/policyengine_uk/variables/gov/dcms/bbc/tv_licence/tv_licence_discount.py @@ -1,4 +1,5 @@ from policyengine_uk.model_api import * +from policyengine_uk.utils.excise import fiscal_year_segments class tv_licence_discount(Variable): @@ -9,7 +10,8 @@ class tv_licence_discount(Variable): unit = "/1" reference = ( "https://www.legislation.gov.uk/ukpga/2003/21/section/365A", - "https://www.tvlicensing.co.uk/reducedfee", + "https://www.gov.uk/government/news/bbc-and-governments-joint-statement-on-delaying-the-tv-licence-fee-for-over-75s", + "https://www.gov.uk/free-discount-tv-licence", ) def formula(household, period, parameters): @@ -17,12 +19,18 @@ def formula(household, period, parameters): tv_licence = parameters(period).gov.dcms.bbc.tv_licence # Aged discount - aged = person("age", period) >= tv_licence.discount.aged.min_age - has_aged = household.any(aged) claims_pc = add(household, period, ["pension_credit"]) > 0 - meets_pc_requirement = not_(tv_licence.discount.aged.must_claim_pc) | claims_pc - eligible_for_aged_discount = has_aged & meets_pc_requirement - aged_discount = eligible_for_aged_discount * tv_licence.discount.aged.discount + aged_discount = 0 + for aged_rules, share in fiscal_year_segments( + parameters.gov.dcms.bbc.tv_licence.discount.aged, + period.start.year, + ): + aged = person("age", period) >= aged_rules.min_age + has_aged = household.any(aged) + meets_pc_requirement = not_(aged_rules.must_claim_pc) | claims_pc + aged_discount += ( + has_aged * meets_pc_requirement * aged_rules.discount * share + ) # Blind discount is_blind = person("is_blind", period)