Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/marriage-allowance-election.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Model Marriage Allowance as ITA 2007 ss. 55A-55E provide. The electing spouse gives up the fixed transferable amount (10% of the personal allowance, rounded up to £10) from their own allowance, and the other spouse's tax falls at Step 6 by the basic rate (or Scottish basic rate) times that amount, capped at their tax. Previously the recipient's taxable income fell by the transferor's unused allowance. The couple elects only when the conditions in s. 55B(2) and s. 55C(1) are met and it lowers their combined income tax. Universal Credit deducts the reduction from the recipient's tax on their earnings, so a pensioner's State Pension no longer changes the earning partner's earned income.
1 change: 1 addition & 0 deletions changelog.d/uc-earned-income-own-tax.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Deduct from each person's Universal Credit earnings only their own income tax and National Insurance on that employment or self-employment (UC Regs 2013 reg. 55(5)(b), reg. 57(2) step 3), not the whole benefit unit's tax on all income, so tax on pensions, State Pension, property, savings and dividends, the High Income Child Benefit Charge and voluntary Class 3 contributions no longer reduce earned income.
1 change: 1 addition & 0 deletions changelog.d/uc-state-pension-unearned-income.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Count State Pension as Universal Credit unearned income (retirement pension income, UC Regs 2013 regs 66(1)(a) and 67). Mixed-age couples on UC were having their State Pension ignored.
2 changes: 1 addition & 1 deletion docs/book/programs/gov/dwp/universal-credit.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
{
"cell_type": "markdown",
"metadata": {},
"source": "## How PolicyEngine computes Universal Credit\n\nPolicyEngine computes Universal Credit at the benefit-unit level in three stages:\n\n1. **Maximum entitlement** (`uc_maximum_amount`): sum the elements the benunit qualifies for — `uc_standard_allowance`, `uc_child_element`, `uc_disability_elements`, `uc_carer_element`, `uc_housing_costs_element`, and `uc_childcare_element`.\n2. **Means test**: the maximum entitlement is reduced by the benunit's countable earnings above its work allowance (`uc_work_allowance`) at the published taper rate (the `gov.dwp.universal_credit.means_test.reduction_rate` parameter, currently 55%), plus its unearned income. Asset rules apply on top of this — capital above the lower threshold deems a tariff income, and capital above the upper threshold disqualifies the benunit entirely.\n3. **Benefit cap**: the post-means-test award (`universal_credit_pre_benefit_cap`) is finally reduced by `benefit_cap_reduction` to produce `universal_credit`. The benefit cap only applies to benunits without a benefit-cap exemption (working enough hours, having a qualifying disability benefit, etc.).\n\nThe take-up step is handled by the input variable `would_claim_uc`, which is populated stochastically when the dataset is built so that PolicyEngine's caseload aggregates match published DWP claimant numbers rather than the full eligible population.\n\nParameters live in `policyengine_uk/parameters/gov/dwp/universal_credit/` and the per-element formulas in `policyengine_uk/variables/gov/dwp/universal_credit/`."
"source": "## How PolicyEngine computes Universal Credit\n\nPolicyEngine computes Universal Credit at the benefit-unit level in three stages:\n\n1. **Maximum entitlement** (`uc_maximum_amount`): sum the elements the benunit qualifies for — `uc_standard_allowance`, `uc_child_element`, `uc_disability_elements`, `uc_carer_element`, `uc_housing_costs_element`, and `uc_childcare_element`.\n2. **Means test**: the maximum entitlement is reduced by the benunit's countable earnings above its work allowance (`uc_work_allowance`) at the published taper rate. Each person's earnings (`uc_individual_earned_income`) are net of their own relievable pension contributions and the income tax and National Insurance they pay on that employment or self-employment (UC Regulations 2013 regs 55(5) and 57(2)): `uc_income_tax_on_earnings` treats earnings as the lowest slice of the person's non-savings income, so tax on pensions, property, savings and dividends is never deducted, and one partner's tax never reduces the other's earnings. The combined earnings above the work allowance are then tapered (the `gov.dwp.universal_credit.means_test.reduction_rate` parameter, currently 55%), plus all of its unearned income (`uc_unearned_income`), pound for pound. Unearned income is the list in `gov.dwp.universal_credit.means_test.income_definitions.unearned`: retirement pension income (State Pension and private pensions, UC Regulations 2013 regs 66(1)(a) and 67), Carer's Allowance, contribution-based JSA, savings, dividend and property income, and the tariff income deemed from capital (reg 72), which replaces the actual yield of the capital it is charged on. Asset rules apply on top of this — capital above the lower threshold deems a tariff income, and capital above the upper threshold disqualifies the benunit entirely.\n3. **Benefit cap**: the post-means-test award (`universal_credit_pre_benefit_cap`) is finally reduced by `benefit_cap_reduction` to produce `universal_credit`. The benefit cap only applies to benunits without a benefit-cap exemption (working enough hours, having a qualifying disability benefit, etc.).\n\nThe take-up step is handled by the input variable `would_claim_uc`, which is populated stochastically when the dataset is built so that PolicyEngine's caseload aggregates match published DWP claimant numbers rather than the full eligible population.\n\nParameters live in `policyengine_uk/parameters/gov/dwp/universal_credit/` and the per-element formulas in `policyengine_uk/variables/gov/dwp/universal_credit/`."
},
{
"cell_type": "code",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ metadata:
reference:
- title: The Universal Credit Regulations 2013 reg. 66
href: https://www.legislation.gov.uk/uksi/2013/376/regulation/66
- title: The Universal Credit Regulations 2013 reg. 67 (retirement pension income)
href: https://www.legislation.gov.uk/uksi/2013/376/regulation/67
- title: State Pension Credit Act 2002 s. 16(1)(za), (a) (State Pension counts as retirement pension income)
href: https://www.legislation.gov.uk/ukpga/2002/16/section/16
- title: The Universal Credit Regulations 2013 reg. 72
href: https://www.legislation.gov.uk/uksi/2013/376/regulation/72

Expand All @@ -17,6 +21,7 @@ values:
- carers_allowance
- jsa_contrib
- private_pension_income
- state_pension
- uc_tariff_income
- savings_interest_income
- dividend_income
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
description: Maximum Marriage Allowance taxable income reduction, as a percentage of the full Personal Allowance.
description: Marriage Allowance transferable amount, as a share of the personal allowance in ITA 2007 s. 35(1). For 2015-16 the Act fixes the amount at £1,060, which is 10% of that year's £10,600 allowance.
values:
2016-04-01: 0.1
2015-04-06: 0.1
metadata:
unit: /1
label: Marriage Allowance maximum percentage
label: Marriage Allowance transferable share of the personal allowance
reference:
- title: Income Tax Act 2007 s. 55B(4)(b)
href: https://www.legislation.gov.uk/ukpga/2007/3/part/3/chapter/3A
- title: Income Tax Act 2007 s. 55B(4)
href: https://www.legislation.gov.uk/ukpga/2007/3/section/55B
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
description: Youngest age at which anyone in the UK can be a party to a marriage, and so a spouse who can make or gain from a Marriage Allowance election. It is 16 in Scotland; England and Wales raised it to 18 from 27 February 2023.
values:
0000-01-01: 16
metadata:
unit: year
label: Minimum age of a Marriage Allowance spouse
reference:
- title: Marriage (Scotland) Act 1977 s. 1(1)
href: https://www.legislation.gov.uk/ukpga/1977/15/section/1
- title: Marriage and Civil Partnership (Minimum Age) Act 2022 s. 1
href: https://www.legislation.gov.uk/ukpga/2022/28/section/1
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
description: The Marriage Allowance is rounded up by this increment.
description: The Marriage Allowance transferable amount is rounded up to a multiple of this amount.
values:
2016-04-01: 10
metadata:
unit: currency-GBP
label: Marriage Allowance rounding increment
reference:
- title: Income Tax Act 2007 s. 55B(5)
href: https://www.legislation.gov.uk/ukpga/2007/3/part/3/chapter/3A
href: https://www.legislation.gov.uk/ukpga/2007/3/section/55B
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ values:
- taxable_dividend_income
- taxable_property_income
- received_allowances_earned_income
- marriage_allowance

metadata:
unit: list
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ metadata:
reference:
- title: Income Tax Act 2007 s. 23
href: https://www.legislation.gov.uk/ukpga/2007/3/section/23
- title: Income Tax Act 2007 s. 26(1)(a)
href: https://www.legislation.gov.uk/ukpga/2007/3/section/26
values:
0000-01-01:
- capped_mcad
- other_tax_credits
- marriage_allowance_tax_reduction
72 changes: 33 additions & 39 deletions policyengine_uk/reforms/cps/marriage_tax_reforms.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
from policyengine_uk.model_api import *
from policyengine_uk.variables.gov.hmrc.income_tax.allowances.meets_marriage_allowance_income_conditions import (
liable_only_at_marriage_allowance_rates,
)
from typing import Union, Optional


def create_expanded_ma_reform(
max_child_age: Optional[int] = None,
child_education_levels: Optional[List[str]] = None,
remove_income_condition: bool = True,
) -> Reform:
class meets_expanded_ma_conditions(Variable):
label = "Qualifies for an expanded Marriage Allowance"
Expand All @@ -28,66 +32,54 @@ def formula(person, period):

class meets_marriage_allowance_income_conditions(Variable):
label = "Meets Marriage Allowance income conditions"
documentation = "Whether this person (and their partner) meets the conditions for this person to be eligible for the Marriage Allowance, as set out in the Income Tax Act 2007 sections 55B and 55C"
documentation = (
"Whether this person pays income tax only at the rates that allow "
"a Marriage Allowance election, or meets the reform's expansion "
"conditions, which lift that restriction."
)
entity = Person
definition_period = YEAR
value_type = bool
reference = "https://www.legislation.gov.uk/ukpga/2007/3/section/55B"

def formula(person, period, parameters):
band = person("tax_band", period)
eligible_bands = parameters(
period
).gov.hmrc.income_tax.allowances.marriage_allowance.eligible_bands
base_eligible = np.isin(band.decode_to_str(), eligible_bands)
# Expand to higher bands if the reform's expansion conditions are met.
bands = band.possible_values
expansion = person("meets_expanded_ma_conditions", period) & (
(band == bands.HIGHER) | (band == bands.ADDITIONAL)
conditions = liable_only_at_marriage_allowance_rates(
person, period, parameters
)
return base_eligible | expansion
if not remove_income_condition:
return conditions
return conditions | person("meets_expanded_ma_conditions", period)

class marriage_allowance(Variable):
class marriage_allowance_transferable_amount(Variable):
value_type = float
entity = Person
label = "Marriage Allowance"
label = "Marriage Allowance transferable amount"
documentation = (
"The share of the personal allowance a Marriage Allowance election "
"transfers, at the reform's rate for couples meeting its expansion "
"conditions, rounded up to the rounding increment."
)
definition_period = YEAR
reference = "https://www.legislation.gov.uk/ukpga/2007/3/part/3/chapter/3A"
reference = "https://www.legislation.gov.uk/ukpga/2007/3/section/55B"
unit = GBP

def formula(person, period, parameters):
marital = person("marital_status", period)
married = marital == marital.possible_values.MARRIED
eligible = married & person(
"meets_marriage_allowance_income_conditions", period
)
transferable_amount = person("partners_unused_personal_allowance", period)
allowances = parameters(period).gov.hmrc.income_tax.allowances
capped_percentage = allowances.marriage_allowance.max
expanded_ma_cap = parameters(
period
).gov.contrib.cps.marriage_tax_reforms.expanded_ma.ma_rate
capped_percentage = where(
p = parameters(period)
allowances = p.gov.hmrc.income_tax.allowances
share = where(
person("meets_expanded_ma_conditions", period),
expanded_ma_cap,
capped_percentage,
)
max_amount = allowances.personal_allowance.amount * capped_percentage
amount_if_eligible_pre_rounding = min_(transferable_amount, max_amount)
# Round up.
rounding_increment = allowances.marriage_allowance.rounding_increment
amount_if_eligible = (
np.ceil(amount_if_eligible_pre_rounding / rounding_increment)
* rounding_increment
p.gov.contrib.cps.marriage_tax_reforms.expanded_ma.ma_rate,
allowances.marriage_allowance.max,
)
takes_up = person("would_claim_marriage_allowance", period)
return eligible * amount_if_eligible * takes_up
amount = allowances.personal_allowance.amount * share
increment = allowances.marriage_allowance.rounding_increment
return np.ceil(amount / increment) * increment

class reform(Reform):
def apply(self):
self.add_variable(meets_expanded_ma_conditions)
self.update_variable(meets_marriage_allowance_income_conditions)
self.update_variable(marriage_allowance)
self.update_variable(marriage_allowance_transferable_amount)

return reform

Expand Down Expand Up @@ -206,6 +198,7 @@ def create_marriage_tax_reform(parameters, period):
if remove_income_condition or rate != original_rate:
ma_reform = create_expanded_ma_reform(
max_child_age=ma_max_child_age if ma_max_child_age > 0 else None,
remove_income_condition=bool(remove_income_condition),
)
else:
ma_reform = None
Expand All @@ -232,4 +225,5 @@ def create_marriage_tax_reform(parameters, period):
# These build the reform classes with no child-age / education conditions, so the reform
# applies to every married couple regardless of child presence.
expanded_ma_reform = create_expanded_ma_reform()
expanded_ma_reform_rate_only = create_expanded_ma_reform(remove_income_condition=False)
marriage_neutral_it_reform = create_marriage_neutral_income_tax_reform()
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
employment_income: 1_000
miscellaneous_income: 100
uc_work_allowance: 10
benunit_tax: 1
uc_income_tax_on_earnings: 1
output:
uc_earned_income: 1100 - 11
uc_individual_earned_income: 1_100 - 1
uc_earned_income: 1_100 - 11

- name: Capped at 0
period: 2025
Expand All @@ -16,6 +17,45 @@
employment_income: 1_000
miscellaneous_income: 100
uc_work_allowance: 2_000
benunit_tax: 1
uc_income_tax_on_earnings: 1
output:
uc_earned_income: 0

- name: Each person's deductions come off only their own earnings
period: 2025
absolute_error_margin: 0
input:
people:
earner:
employment_income: 1_000
uc_income_tax_on_earnings: 100
uc_national_insurance_on_earnings: 10
partner:
uc_income_tax_on_earnings: 500
pension_contributions: 50
benunits:
benunit:
members: [earner, partner]
uc_work_allowance: 0
output:
uc_individual_earned_income: [890, 0]
uc_earned_income: 890

- name: Work allowance comes off combined earnings after each person's deductions
period: 2025
absolute_error_margin: 0
input:
people:
earner:
employment_income: 1_000
uc_income_tax_on_earnings: 100
partner:
employment_income: 500
pension_contributions: 50
benunits:
benunit:
members: [earner, partner]
uc_work_allowance: 300
output:
uc_individual_earned_income: [900, 450]
uc_earned_income: 1_050
Loading
Loading