diff --git a/changelog.d/2783.fixed.md b/changelog.d/2783.fixed.md new file mode 100644 index 0000000000..09a43a987e --- /dev/null +++ b/changelog.d/2783.fixed.md @@ -0,0 +1 @@ +Corrected Scottish income tax rates and limits for 2018-19 to 2021-22 to match Parliament's annual resolutions, including the 41% higher rate from 2018-19. diff --git a/policyengine_uk/parameters/gov/hmrc/income_tax/rates/scotland/rates.yaml b/policyengine_uk/parameters/gov/hmrc/income_tax/rates/scotland/rates.yaml index c5526f3564..09df4d2f2f 100644 --- a/policyengine_uk/parameters/gov/hmrc/income_tax/rates/scotland/rates.yaml +++ b/policyengine_uk/parameters/gov/hmrc/income_tax/rates/scotland/rates.yaml @@ -1,104 +1,144 @@ description: Rates for Scottish Income Tax. -# The Scottish Budget 2026-27 announces higher, advanced and top threshold -# freezes through 2028-29. These future values model announced policy; -# Scottish rates and bands are still subject to annual rate resolutions. -# CPI uprating after 2028-29 is the existing model projection, not a Budget -# commitment to subsequent indexation. +# The Scottish Parliament sets these rates and limits by resolution, for one +# tax year at a time (Scotland Act 1998 s. 80C(3)). Beside each dated value a +# comment cites the motion and paragraph. In the motions Parliament charges +# each rate on "income" up to a limit. The taxpayer deducts the personal +# allowance at Step 3 and works out the tax at Step 4 on the income that +# remains (ITA 2007 s. 23), so this file holds limits of income after the +# allowance. From 2024-25 Parliament adds in the motions that s. 11A charges +# this income "above the personal allowance". +# The Scottish Government adds the standard personal allowance to each limit +# in its tables (gov.scot), except at the £150,000 and £125,140 tops, where no +# allowance remains (s. 35(2)). +# The Scottish Government announced higher, advanced and top threshold +# freezes through 2028-29 in the Scottish Budget 2026-27. Parliament sets +# Scottish rates and bands by annual resolutions. The model projects CPI +# uprating after 2028-29; the government has made no commitment to subsequent +# indexation. brackets: - threshold: values: - 2017-04-06: null - 2018-04-06: 0 + 2017-04-06: null # Parliament adopted no starter rate in S5M-03912 + 2018-04-06: 0 # S5M-10397 (a) and paragraph (a) of each later motion 2021-04-06: 0 metadata: uprating: gov.economic_assumptions.indices.obr.consumer_price_index rate: 2017-04-01: null - 2018-04-06: 0.19 # Starter rate + 2018-04-06: 0.19 # Starter rate: paragraph (a) from S5M-10397 - threshold: values: - 2017-04-06: 0 - 2018-04-06: 2_000 - 2021-04-06: 2_306 - 2022-04-06: 2_162 - 2023-04-06: 2_162 - 2024-04-06: 2_306 - 2025-04-06: 2_827 - 2026-04-06: 3_967 + 2017-04-06: 0 # S5M-03912 (a) + 2018-04-06: 2_000 # S5M-10397 (a) + 2019-04-06: 2_049 # S5M-15879 (a) + 2020-04-06: 2_085 # S5M-21090 (a) + 2021-04-06: 2_097 # S5M-24225 (a) + 2022-04-06: 2_162 # S6M-03019 (a) + 2023-04-06: 2_162 # S6M-07853 (a) + 2024-04-06: 2_306 # S6M-12252 (a) + 2025-04-06: 2_827 # S6M-16531 (a) + 2026-04-06: 3_967 # S6M-20844 (a) metadata: uprating: gov.economic_assumptions.indices.obr.consumer_price_index rate: - 2017-04-06: 0.2 # Basic rate + 2017-04-06: 0.2 # Basic rate: S5M-03912 (a), then paragraph (b) - threshold: values: - 2017-04-06: null - 2018-04-06: 12_150 - 2021-04-06: 13_991 - 2022-04-06: 13_118 - 2023-04-06: 13_118 - 2024-04-06: 13_991 - 2025-04-06: 14_921 - 2026-04-06: 16_956 + 2017-04-06: null # Parliament adopted no intermediate rate in S5M-03912 + 2018-04-06: 12_150 # S5M-10397 (b) + 2019-04-06: 12_444 # S5M-15879 (b) + 2020-04-06: 12_658 # S5M-21090 (b) + 2021-04-06: 12_726 # S5M-24225 (b) + 2022-04-06: 13_118 # S6M-03019 (b) + 2023-04-06: 13_118 # S6M-07853 (b) + 2024-04-06: 13_991 # S6M-12252 (b) + 2025-04-06: 14_921 # S6M-16531 (b) + 2026-04-06: 16_956 # S6M-20844 (b) metadata: uprating: gov.economic_assumptions.indices.obr.consumer_price_index rate: 2017-04-06: null - 2018-04-06: 0.21 # Intermediate rate + 2018-04-06: 0.21 # Intermediate rate: paragraph (c) from S5M-10397 - threshold: values: - 2017-04-06: 31_500 - 2021-04-06: 31_092 - 2022-04-06: 31_092 - 2023-04-06: 31_092 - 2024-04-06: 31_092 - 2025-04-06: 31_092 - # Scottish Budget 2026-27 announced freeze through 2028-29. - 2026-04-06: 31_092 + 2017-04-06: 31_500 # S5M-03912 (a) + 2018-04-06: 31_580 # S5M-10397 (c) + 2019-04-06: 30_930 # S5M-15879 (c) + 2020-04-06: 30_930 # S5M-21090 (c) + 2021-04-06: 31_092 # S5M-24225 (c) + 2022-04-06: 31_092 # S6M-03019 (c) + 2023-04-06: 31_092 # S6M-07853 (c) + 2024-04-06: 31_092 # S6M-12252 (c) + 2025-04-06: 31_092 # S6M-16531 (c) + # The Scottish Government announced this freeze through 2028-29 in + # the Scottish Budget 2026-27. + 2026-04-06: 31_092 # S6M-20844 (c) 2027-04-06: 31_092 2028-04-06: 31_092 metadata: uprating: gov.economic_assumptions.indices.obr.consumer_price_index rate: - 2017-04-06: 0.4 - 2021-04-06: 0.41 # Higher rate - 2023-04-06: 0.42 + 2017-04-06: 0.4 # S5M-03912 (b) + 2018-04-06: 0.41 # Higher rate: paragraph (d), S5M-10397 to S6M-03019 + 2023-04-06: 0.42 # S6M-07853 (d), then paragraph (d) to S6M-20844 - threshold: values: - 2017-04-06: 150_000 - 2023-04-06: 125_140 - 2024-04-06: 62_430 - 2025-04-06: 62_430 - # Scottish Budget 2026-27 announced freeze through 2028-29. - 2026-04-06: 62_430 + 2017-04-06: 150_000 # S5M-03912 (b) + 2018-04-06: 150_000 # S5M-10397 (d) + 2019-04-06: 150_000 # S5M-15879 (d) + 2020-04-06: 150_000 # S5M-21090 (d) + 2021-04-06: 150_000 # S5M-24225 (d) + 2022-04-06: 150_000 # S6M-03019 (d) + 2023-04-06: 125_140 # S6M-07853 (d) + 2024-04-06: 62_430 # S6M-12252 (d) + 2025-04-06: 62_430 # S6M-16531 (d) + # The Scottish Government announced this freeze through 2028-29 in + # the Scottish Budget 2026-27. + 2026-04-06: 62_430 # S6M-20844 (d) 2027-04-06: 62_430 2028-04-06: 62_430 metadata: uprating: gov.economic_assumptions.indices.obr.consumer_price_index rate: - 2017-04-06: 0.45 - 2018-04-06: 0.46 # Additional rate - 2023-04-06: 0.47 - 2024-04-06: 0.45 + 2017-04-06: 0.45 # S5M-03912 (c) + 2018-04-06: 0.46 # Top rate: paragraph (e), S5M-10397 to S6M-03019 + 2023-04-06: 0.47 # S6M-07853 (e) + 2024-04-06: 0.45 # Advanced rate: paragraph (e) from S6M-12252 - threshold: values: 2017-04-06: null - # The personal allowance is fully withdrawn at £125,140, so the - # taxable-income top limit equals total income: do not subtract PA. - 2024-04-06: 125_140 - 2025-04-06: 125_140 - 2026-04-06: 125_140 + # At income of £125,140, no personal allowance remains under + # s. 35(2), so the taxable-income top limit equals total income: do + # not subtract PA. + 2024-04-06: 125_140 # S6M-12252 (e) + 2025-04-06: 125_140 # S6M-16531 (e) + 2026-04-06: 125_140 # S6M-20844 (e) 2027-04-06: 125_140 2028-04-06: 125_140 metadata: uprating: gov.economic_assumptions.indices.obr.consumer_price_index rate: 2017-04-06: null - 2024-04-06: 0.48 + 2024-04-06: 0.48 # Top rate: paragraph (f) from S6M-12252 metadata: threshold_unit: currency-GBP rate_unit: /1 label: Scottish Income Tax rates reference: + - title: Scottish Rate Resolution 2017-18 (S5M-03912), paragraphs (a)-(c) + href: https://www.parliament.scot/chamber-and-committees/votes-and-motions/S5M-03912 + - title: Scottish Rate Resolution 2018-19 (S5M-10397), paragraphs (a)-(e) + href: https://www.parliament.scot/chamber-and-committees/votes-and-motions/S5M-10397 + - title: Scottish Rate Resolution 2019-20 (S5M-15879), paragraphs (a)-(e) + href: https://www.parliament.scot/chamber-and-committees/votes-and-motions/S5M-15879 + - title: Scottish Rate Resolution 2020-21 (S5M-21090), paragraphs (a)-(e) + href: https://www.parliament.scot/chamber-and-committees/votes-and-motions/S5M-21090 + - title: Scottish Rate Resolution 2021-22 (S5M-24225), paragraphs (a)-(e) + href: https://www.parliament.scot/chamber-and-committees/votes-and-motions/S5M-24225 + - title: Scottish Rate Resolution 2022-23 (S6M-03019), paragraphs (a)-(e) + href: https://www.parliament.scot/chamber-and-committees/votes-and-motions/S6M-03019 + - title: Scottish Rate Resolution 2023-24 (S6M-07853), paragraphs (a)-(e) + href: https://www.parliament.scot/chamber-and-committees/votes-and-motions/S6M-07853 - title: Scottish Rate Resolution 2024-25 (S6M-12252), paragraphs (a)-(f) href: https://www.parliament.scot/chamber-and-committees/votes-and-motions/S6M-12252 - title: Scottish Rate Resolution 2025-26 (S6M-16531), paragraphs (a)-(f) diff --git a/policyengine_uk/programs.yaml b/policyengine_uk/programs.yaml index 748b16ec31..c80b24cbd1 100644 --- a/policyengine_uk/programs.yaml +++ b/policyengine_uk/programs.yaml @@ -775,7 +775,7 @@ programs: agency: HMRC status: complete coverage: Scotland - verified_start_year: 2019 + verified_start_year: 2017 notes: Devolved tax bands applied via income tax system - id: lbtt diff --git a/policyengine_uk/tests/policy/baseline/finance/tax/income_tax/scottish_rate_resolutions.yaml b/policyengine_uk/tests/policy/baseline/finance/tax/income_tax/scottish_rate_resolutions.yaml new file mode 100644 index 0000000000..79a9874b3a --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/finance/tax/income_tax/scottish_rate_resolutions.yaml @@ -0,0 +1,772 @@ +# Scottish income tax for 2018-19 through 2021-22: pay alone, and married +# couples at the intermediate rate limit. The Scottish Parliament set each +# year's rates and limits by motion: S5M-10397, S5M-15879, S5M-21090 and +# S5M-24225, at +# https://www.parliament.scot/chamber-and-committees/votes-and-motions/ +# In the motions Parliament charges each rate on "income" up to a limit. The +# taxpayer deducts the personal allowance at Step 3 and works out the tax at +# Step 4 on the income that remains (ITA 2007 s. 23), so the limits apply to +# income after the allowance. The allowance was £11,850 in 2018-19, £12,500 in +# 2019-20 and 2020-21 and £12,570 in 2021-22 (s. 35(1)). It falls by £1 for +# every £2 of income above £100,000, leaving none at £160,000 (s. 35(2)). The +# Scottish Government adds the standard allowance to the lower limits in its +# tables (gov.scot). No allowance remains at the £150,000 top (s. 35(2)), so +# its tables give the limit itself. + +# 2018-19, S5M-10397: 19% to £2,000, 20% to £12,150, 21% to £31,580, +# 41% to £150,000, 46% above. + +- name: Scottish 2018-19 pay of 13000 bears only the starter rate + period: 2018 + absolute_error_margin: 0.01 + input: + age: 40 + employment_income: 13_000 + region: SCOTLAND + output: + # 1,150 x 19% = 218.50. + earned_taxable_income: 1_150 + income_tax: 218.50 + +- name: Scottish 2018-19 pay of 20000 reaches the basic rate + period: 2018 + absolute_error_margin: 0.01 + input: + age: 40 + employment_income: 20_000 + region: SCOTLAND + output: + # 2,000 x 19% + 6,150 x 20% = 380 + 1,230 = 1,610. + earned_taxable_income: 8_150 + income_tax: 1_610 + +- name: Scottish 2018-19 pay of 30000 reaches the intermediate rate + period: 2018 + absolute_error_margin: 0.01 + input: + age: 40 + employment_income: 30_000 + region: SCOTLAND + output: + # 380 + 10,150 x 20% + 6,000 x 21% = 380 + 2,030 + 1,260 = 3,670. + earned_taxable_income: 18_150 + income_tax: 3_670 + +- name: Scottish 2018-19 taxable income at the 31580 intermediate rate limit has no tax at 41 percent + period: 2018 + absolute_error_margin: 0.01 + input: + age: 40 + employment_income: 43_430 + region: SCOTLAND + output: + # gov.scot: intermediate rate "Over £24,000 to £43,430". + # 380 + 2,030 + 19,430 x 21% = 380 + 2,030 + 4,080.30 = 6,490.30. + earned_taxable_income: 31_580 + income_tax: 6_490.30 + +- name: Scottish 2018-19 pay of 50000 reaches the 41 percent higher rate + period: 2018 + absolute_error_margin: 0.01 + input: + age: 40 + employment_income: 50_000 + region: SCOTLAND + output: + # The issue's case: 6,490.30 + 6,570 x 41% = 6,490.30 + 2,693.70 = 9,184. + earned_taxable_income: 38_150 + income_tax: 9_184 + +- name: Scottish 2018-19 pay of 160000 reaches the 46 percent top rate + period: 2018 + absolute_error_margin: 0.01 + input: + age: 40 + employment_income: 160_000 + region: SCOTLAND + output: + # 6,490.30 + 118,420 x 41% + 10,000 x 46% + # = 6,490.30 + 48,552.20 + 4,600 = 59,642.50. + earned_taxable_income: 160_000 + income_tax: 59_642.50 + +# 2019-20, S5M-15879: 19% to £2,049, 20% to £12,444, 21% to £30,930, +# 41% to £150,000, 46% above. + +- name: Scottish 2019-20 pay of 13500 bears only the starter rate + period: 2019 + absolute_error_margin: 0.01 + input: + age: 40 + employment_income: 13_500 + region: SCOTLAND + output: + # 1,000 x 19% = 190. + earned_taxable_income: 1_000 + income_tax: 190 + +- name: Scottish 2019-20 taxable income at the 2049 starter rate limit is all at 19 percent + period: 2019 + absolute_error_margin: 0.01 + input: + age: 40 + employment_income: 14_549 + region: SCOTLAND + output: + # gov.scot: starter rate "Over £12,500-£14,549". 2,049 x 19% = 389.31. + earned_taxable_income: 2_049 + income_tax: 389.31 + +- name: Scottish 2019-20 pay of 20000 reaches the basic rate + period: 2019 + absolute_error_margin: 0.01 + input: + age: 40 + employment_income: 20_000 + region: SCOTLAND + output: + # 389.31 + 5,451 x 20% = 389.31 + 1,090.20 = 1,479.51. + earned_taxable_income: 7_500 + income_tax: 1_479.51 + +- name: Scottish 2019-20 taxable income at the 12444 basic rate limit has no tax at 21 percent + period: 2019 + absolute_error_margin: 0.01 + input: + age: 40 + employment_income: 24_944 + region: SCOTLAND + output: + # gov.scot: basic rate "Over £14,549-£24,944". + # 389.31 + 10,395 x 20% = 389.31 + 2,079 = 2,468.31. + earned_taxable_income: 12_444 + income_tax: 2_468.31 + +- name: Scottish 2019-20 pay of 35000 reaches the intermediate rate + period: 2019 + absolute_error_margin: 0.01 + input: + age: 40 + employment_income: 35_000 + region: SCOTLAND + output: + # 2,468.31 + 10,056 x 21% = 2,468.31 + 2,111.76 = 4,580.07. + earned_taxable_income: 22_500 + income_tax: 4_580.07 + +- name: Scottish 2019-20 taxable income at the 30930 intermediate rate limit has no tax at 41 percent + period: 2019 + absolute_error_margin: 0.01 + input: + age: 40 + employment_income: 43_430 + region: SCOTLAND + output: + # gov.scot: intermediate rate "Over £24,944-£43,430". + # 2,468.31 + 18,486 x 21% = 2,468.31 + 3,882.06 = 6,350.37. + earned_taxable_income: 30_930 + income_tax: 6_350.37 + +- name: Scottish 2019-20 pay of 50000 reaches the 41 percent higher rate + period: 2019 + absolute_error_margin: 0.01 + input: + age: 40 + employment_income: 50_000 + region: SCOTLAND + output: + # 6,350.37 + 6,570 x 41% = 6,350.37 + 2,693.70 = 9,044.07. + earned_taxable_income: 37_500 + income_tax: 9_044.07 + +- name: Scottish 2019-20 pay of 160000 reaches the 46 percent top rate + period: 2019 + absolute_error_margin: 0.01 + input: + age: 40 + employment_income: 160_000 + region: SCOTLAND + output: + # 6,350.37 + 119,070 x 41% + 10,000 x 46% + # = 6,350.37 + 48,818.70 + 4,600 = 59,769.07. + earned_taxable_income: 160_000 + income_tax: 59_769.07 + +# 2020-21, S5M-21090: 19% to £2,085, 20% to £12,658, 21% to £30,930, +# 41% to £150,000, 46% above. + +- name: Scottish 2020-21 pay of 13500 bears only the starter rate + period: 2020 + absolute_error_margin: 0.01 + input: + age: 40 + employment_income: 13_500 + region: SCOTLAND + output: + # 1,000 x 19% = 190. + earned_taxable_income: 1_000 + income_tax: 190 + +- name: Scottish 2020-21 taxable income at the 2085 starter rate limit is all at 19 percent + period: 2020 + absolute_error_margin: 0.01 + input: + age: 40 + employment_income: 14_585 + region: SCOTLAND + output: + # gov.scot: starter rate "Over £12,500 - £14,585". 2,085 x 19% = 396.15. + earned_taxable_income: 2_085 + income_tax: 396.15 + +- name: Scottish 2020-21 pay of 20000 reaches the basic rate + period: 2020 + absolute_error_margin: 0.01 + input: + age: 40 + employment_income: 20_000 + region: SCOTLAND + output: + # 396.15 + 5,415 x 20% = 396.15 + 1,083 = 1,479.15. + earned_taxable_income: 7_500 + income_tax: 1_479.15 + +- name: Scottish 2020-21 taxable income at the 12658 basic rate limit has no tax at 21 percent + period: 2020 + absolute_error_margin: 0.01 + input: + age: 40 + employment_income: 25_158 + region: SCOTLAND + output: + # gov.scot: basic rate "Over £14,585 - £25,158". + # 396.15 + 10,573 x 20% = 396.15 + 2,114.60 = 2,510.75. + earned_taxable_income: 12_658 + income_tax: 2_510.75 + +- name: Scottish 2020-21 pay of 35000 reaches the intermediate rate + period: 2020 + absolute_error_margin: 0.01 + input: + age: 40 + employment_income: 35_000 + region: SCOTLAND + output: + # 2,510.75 + 9,842 x 21% = 2,510.75 + 2,066.82 = 4,577.57. + earned_taxable_income: 22_500 + income_tax: 4_577.57 + +- name: Scottish 2020-21 taxable income at the 30930 intermediate rate limit has no tax at 41 percent + period: 2020 + absolute_error_margin: 0.01 + input: + age: 40 + employment_income: 43_430 + region: SCOTLAND + output: + # gov.scot: intermediate rate "Over £25,158 - £43,430". + # 2,510.75 + 18,272 x 21% = 2,510.75 + 3,837.12 = 6,347.87. + earned_taxable_income: 30_930 + income_tax: 6_347.87 + +- name: Scottish 2020-21 pay of 50000 reaches the 41 percent higher rate + period: 2020 + absolute_error_margin: 0.01 + input: + age: 40 + employment_income: 50_000 + region: SCOTLAND + output: + # 6,347.87 + 6,570 x 41% = 6,347.87 + 2,693.70 = 9,041.57. + earned_taxable_income: 37_500 + income_tax: 9_041.57 + +- name: Scottish 2020-21 pay of 160000 reaches the 46 percent top rate + period: 2020 + absolute_error_margin: 0.01 + input: + age: 40 + employment_income: 160_000 + region: SCOTLAND + output: + # 6,347.87 + 119,070 x 41% + 10,000 x 46% + # = 6,347.87 + 48,818.70 + 4,600 = 59,766.57. + earned_taxable_income: 160_000 + income_tax: 59_766.57 + +# 2021-22, S5M-24225: 19% to £2,097, 20% to £12,726, 21% to £31,092, +# 41% to £150,000, 46% above. + +- name: Scottish 2021-22 pay of 13570 bears only the starter rate + period: 2021 + absolute_error_margin: 0.01 + input: + age: 40 + employment_income: 13_570 + region: SCOTLAND + output: + # 1,000 x 19% = 190. + earned_taxable_income: 1_000 + income_tax: 190 + +- name: Scottish 2021-22 taxable income at the 2097 starter rate limit is all at 19 percent + period: 2021 + absolute_error_margin: 0.01 + input: + age: 40 + employment_income: 14_667 + region: SCOTLAND + output: + # gov.scot: starter rate "Over £12,570* - £14,667". 2,097 x 19% = 398.43. + earned_taxable_income: 2_097 + income_tax: 398.43 + +- name: Scottish 2021-22 pay of 20000 reaches the basic rate + period: 2021 + absolute_error_margin: 0.01 + input: + age: 40 + employment_income: 20_000 + region: SCOTLAND + output: + # 398.43 + 5,333 x 20% = 398.43 + 1,066.60 = 1,465.03. + earned_taxable_income: 7_430 + income_tax: 1_465.03 + +- name: Scottish 2021-22 taxable income at the 12726 basic rate limit has no tax at 21 percent + period: 2021 + absolute_error_margin: 0.01 + input: + age: 40 + employment_income: 25_296 + region: SCOTLAND + output: + # gov.scot: basic rate "Over £14,667 - £25,296". + # 398.43 + 10,629 x 20% = 398.43 + 2,125.80 = 2,524.23. + earned_taxable_income: 12_726 + income_tax: 2_524.23 + +- name: Scottish 2021-22 pay of 35000 reaches the intermediate rate + period: 2021 + absolute_error_margin: 0.01 + input: + age: 40 + employment_income: 35_000 + region: SCOTLAND + output: + # 2,524.23 + 9,704 x 21% = 2,524.23 + 2,037.84 = 4,562.07. + earned_taxable_income: 22_430 + income_tax: 4_562.07 + +- name: Scottish 2021-22 pay of 50000 reaches the 41 percent higher rate + period: 2021 + absolute_error_margin: 0.01 + input: + age: 40 + employment_income: 50_000 + region: SCOTLAND + output: + # 2,524.23 + 18,366 x 21% + 6,338 x 41% + # = 2,524.23 + 3,856.86 + 2,598.58 = 8,979.67. + earned_taxable_income: 37_430 + income_tax: 8_979.67 + +- name: Scottish 2021-22 pay of 160000 reaches the 46 percent top rate + period: 2021 + absolute_error_margin: 0.01 + input: + age: 40 + employment_income: 160_000 + region: SCOTLAND + output: + # 6,381.09 + 118,908 x 41% + 10,000 x 46% + # = 6,381.09 + 48,752.28 + 4,600 = 59,733.37. + earned_taxable_income: 160_000 + income_tax: 59_733.37 + +# Married Scottish couples at the intermediate rate limit in 2018-19 to +# 2020-21. The spouse's £5,000 of pay stays below the allowance left after the +# election (ITA 2007 s. 55C(1)(c)), so the earner can receive the Marriage +# Allowance if they pay tax only at the rates s. 55B(2)(b) lists: the Scottish +# starter, basic and intermediate rates, and the starting, nil and savings +# basic rates for savings (FA 2016 s. 6(15)(a), from 2017-18). s. 55B(4)(b) +# and (5) set the transferable amount at 10% of the personal allowance, +# rounded up to £10. s. 55B(1) and (3) give the earner a tax reduction of 20% +# of it, which s. 55A(2) applies at Step 6 of s. 23, after Step 4 charges tax +# at each rate. +# +# These cases check the claim, the amount transferred, the reduction and the +# earner's income tax, and check the tax at each rate on a twin couple that +# makes no election. + +# 2018-19, S5M-10397: 21% to £31,580, then 41%. Allowance £11,850: the +# spouse transfers £1,190 and keeps £10,660. + +- name: Scottish 2018-19 earner at the 31580 limit can receive the Marriage Allowance + period: 2018 + absolute_error_margin: 0.01 + input: + people: + earner: + age: 40 + employment_income: 43_430 + spouse: + age: 40 + employment_income: 5_000 + benunits: + couple: + members: [earner, spouse] + is_married: true + households: + home: + members: [earner, spouse] + region: SCOTLAND + output: + # 43,430 - 11,850 = 31,580, none of it above the intermediate rate + # limit. Without the election the earner pays 6,490.30, the single + # case above; with it, the law gives 6,490.30 - 1,190 x 20% = 6,252.30. + non_savings_income_after_allowances: [31_580, 0] + tax_band: [INTERMEDIATE, NONE] + meets_marriage_allowance_income_conditions: [true, true] + marriage_allowance: [1_190, 0] + marriage_allowance_tax_reduction: [238, 0] + income_tax: [6_252.30, 0] + +- name: Scottish 2018-19 earner 1 pound above the 31580 limit cannot receive the Marriage Allowance + period: 2018 + absolute_error_margin: 0.01 + input: + people: + earner: + age: 40 + employment_income: 43_431 + spouse: + age: 40 + employment_income: 5_000 + benunits: + couple: + members: [earner, spouse] + is_married: true + households: + home: + members: [earner, spouse] + region: SCOTLAND + output: + # 31,581 of taxable income puts 1 at the 41% higher rate, which + # s. 55B(2)(b) does not list. 6,490.30 + 1 x 41% = 6,490.71. + tax_band: [HIGHER, NONE] + meets_marriage_allowance_income_conditions: [false, true] + marriage_allowance: [0, 0] + income_tax: [6_490.71, 0] + +# 2019-20, S5M-15879: 19% to £2,049, 20% to £12,444, 21% to £30,930, +# 41% to £150,000, 46% above. Allowance £12,500: the spouse transfers £1,250 +# and keeps £11,250. Savings: £5,000 starting rate limit +# (s. 12(3)), £1,000 savings allowance (s. 12B(4)), £37,500 basic rate limit +# (s. 10(5)), 20% savings basic rate. + +- name: Scottish 2019-20 earner at the 30930 limit can receive the Marriage Allowance + period: 2019 + absolute_error_margin: 0.01 + input: + people: + earner: + age: 40 + employment_income: 43_430 + spouse: + age: 40 + employment_income: 5_000 + benunits: + couple: + members: [earner, spouse] + is_married: true + households: + home: + members: [earner, spouse] + region: SCOTLAND + output: + # 43,430 - 12,500 = 30,930, all at or below the intermediate rate limit, + # so none at the 41% higher rate. Without the election the earner pays + # 6,350.37, the next case; with it, 6,350.37 - 1,250 x 20% = 6,100.37. + non_savings_income_after_allowances: [30_930, 0] + tax_band: [INTERMEDIATE, NONE] + meets_marriage_allowance_income_conditions: [true, true] + marriage_allowance: [1_250, 0] + marriage_allowance_tax_reduction: [250, 0] + income_tax: [6_100.37, 0] + +- name: Scottish 2019-20 earner at the 30930 limit without the election pays the intermediate rate on the last pound + period: 2019 + absolute_error_margin: 0.01 + input: + people: + earner: + age: 40 + employment_income: 43_430 + would_claim_marriage_allowance: false + spouse: + age: 40 + employment_income: 5_000 + benunits: + couple: + members: [earner, spouse] + is_married: true + households: + home: + members: [earner, spouse] + region: SCOTLAND + output: + # 2,049 x 19% + 10,395 x 20% + 18,486 x 21% + # = 389.31 + 2,079 + 3,882.06 = 6,350.37. + # With the election, the law gives 6,350.37 - 250. + earned_income_tax: [6_350.37, 0] + income_tax: [6_350.37, 0] + marriage_allowance: [0, 0] + +- name: Scottish 2019-20 earner 1 pound above the 30930 limit cannot receive the Marriage Allowance + period: 2019 + absolute_error_margin: 0.01 + input: + people: + earner: + age: 40 + employment_income: 43_431 + spouse: + age: 40 + employment_income: 5_000 + benunits: + couple: + members: [earner, spouse] + is_married: true + households: + home: + members: [earner, spouse] + region: SCOTLAND + output: + # 30,931 of taxable income puts 1 at the 41% higher rate, which + # s. 55B(2)(b) does not list. 6,350.37 + 1 x 41% = 6,350.78. + tax_band: [HIGHER, NONE] + meets_marriage_allowance_income_conditions: [false, true] + marriage_allowance: [0, 0] + income_tax: [6_350.78, 0] + +- name: Scottish 2019-20 earner with pay of 14000 and savings of 29500 can receive the Marriage Allowance + period: 2019 + absolute_error_margin: 0.01 + input: + people: + earner: + age: 40 + employment_income: 14_000 + savings_interest_income: 29_500 + spouse: + age: 40 + employment_income: 5_000 + benunits: + couple: + members: [earner, spouse] + is_married: true + households: + home: + members: [earner, spouse] + region: SCOTLAND + output: + # 1,500 of pay above the allowance bears the starter rate. Savings sit + # on top (s. 16) and take the rest-of-UK bands (s. 11D(6)): 31,000 of + # taxable income stays below the 37,500 basic rate limit, though it + # passes the 30,930 Scottish limit, so nothing bears a higher rate. + # Without the election the earner pays 5,285, the next case; with it, + # 5,285 - 1,250 x 20% = 5,035. + non_savings_income_after_allowances: [1_500, 0] + tax_band: [BASIC, NONE] + meets_marriage_allowance_income_conditions: [true, true] + marriage_allowance: [1_250, 0] + marriage_allowance_tax_reduction: [250, 0] + income_tax: [5_035, 0] + +- name: Scottish 2019-20 earner with pay of 14000 and savings of 29500 without the election pays the starter and savings basic rates + period: 2019 + absolute_error_margin: 0.01 + input: + people: + earner: + age: 40 + employment_income: 14_000 + savings_interest_income: 29_500 + would_claim_marriage_allowance: false + spouse: + age: 40 + employment_income: 5_000 + benunits: + couple: + members: [earner, spouse] + is_married: true + households: + home: + members: [earner, spouse] + region: SCOTLAND + output: + # Pay: 1,500 x 19% = 285. Savings: 5,000 - 1,500 = 3,500 at the 0% + # starting rate (s. 12), 1,000 at the savings nil rate (s. 12A, 12B(4)), + # and 29,500 - 3,500 - 1,000 = 25,000 x 20% = 5,000. + # With the election, the law gives 5,285 - 250 = 5,035. + earned_income_tax: [285, 0] + savings_income_tax: [5_000, 0] + income_tax: [5_285, 0] + marriage_allowance: [0, 0] + +# 2020-21, S5M-21090: 19% to £2,085, 20% to £12,658, 21% to £30,930, +# 41% to £150,000, 46% above. Allowance £12,500: the spouse transfers £1,250 +# and keeps £11,250. Savings: £5,000 starting rate limit +# (s. 12(3)), £1,000 savings allowance (s. 12B(4)), £37,500 basic rate limit +# (s. 10(5)), 20% savings basic rate. + +- name: Scottish 2020-21 earner at the 30930 limit can receive the Marriage Allowance + period: 2020 + absolute_error_margin: 0.01 + input: + people: + earner: + age: 40 + employment_income: 43_430 + spouse: + age: 40 + employment_income: 5_000 + benunits: + couple: + members: [earner, spouse] + is_married: true + households: + home: + members: [earner, spouse] + region: SCOTLAND + output: + # 43,430 - 12,500 = 30,930, all at or below the intermediate rate limit, + # so none at the 41% higher rate. Without the election the earner pays + # 6,347.87, the next case; with it, 6,347.87 - 1,250 x 20% = 6,097.87. + non_savings_income_after_allowances: [30_930, 0] + tax_band: [INTERMEDIATE, NONE] + meets_marriage_allowance_income_conditions: [true, true] + marriage_allowance: [1_250, 0] + marriage_allowance_tax_reduction: [250, 0] + income_tax: [6_097.87, 0] + +- name: Scottish 2020-21 earner at the 30930 limit without the election pays the intermediate rate on the last pound + period: 2020 + absolute_error_margin: 0.01 + input: + people: + earner: + age: 40 + employment_income: 43_430 + would_claim_marriage_allowance: false + spouse: + age: 40 + employment_income: 5_000 + benunits: + couple: + members: [earner, spouse] + is_married: true + households: + home: + members: [earner, spouse] + region: SCOTLAND + output: + # 2,085 x 19% + 10,573 x 20% + 18,272 x 21% + # = 396.15 + 2,114.60 + 3,837.12 = 6,347.87. + # With the election, the law gives 6,347.87 - 250. + earned_income_tax: [6_347.87, 0] + income_tax: [6_347.87, 0] + marriage_allowance: [0, 0] + +- name: Scottish 2020-21 earner 1 pound above the 30930 limit cannot receive the Marriage Allowance + period: 2020 + absolute_error_margin: 0.01 + input: + people: + earner: + age: 40 + employment_income: 43_431 + spouse: + age: 40 + employment_income: 5_000 + benunits: + couple: + members: [earner, spouse] + is_married: true + households: + home: + members: [earner, spouse] + region: SCOTLAND + output: + # 30,931 of taxable income puts 1 at the 41% higher rate, which + # s. 55B(2)(b) does not list. 6,347.87 + 1 x 41% = 6,348.28. + tax_band: [HIGHER, NONE] + meets_marriage_allowance_income_conditions: [false, true] + marriage_allowance: [0, 0] + income_tax: [6_348.28, 0] + +- name: Scottish 2020-21 earner with pay of 14000 and savings of 29500 can receive the Marriage Allowance + period: 2020 + absolute_error_margin: 0.01 + input: + people: + earner: + age: 40 + employment_income: 14_000 + savings_interest_income: 29_500 + spouse: + age: 40 + employment_income: 5_000 + benunits: + couple: + members: [earner, spouse] + is_married: true + households: + home: + members: [earner, spouse] + region: SCOTLAND + output: + # 1,500 of pay above the allowance bears the starter rate. Savings sit + # on top (s. 16) and take the rest-of-UK bands (s. 11D(6)): 31,000 of + # taxable income stays below the 37,500 basic rate limit, though it + # passes the 30,930 Scottish limit, so nothing bears a higher rate. + # Without the election the earner pays 5,285, the next case; with it, + # 5,285 - 1,250 x 20% = 5,035. + non_savings_income_after_allowances: [1_500, 0] + tax_band: [BASIC, NONE] + meets_marriage_allowance_income_conditions: [true, true] + marriage_allowance: [1_250, 0] + marriage_allowance_tax_reduction: [250, 0] + income_tax: [5_035, 0] + +- name: Scottish 2020-21 earner with pay of 14000 and savings of 29500 without the election pays the starter and savings basic rates + period: 2020 + absolute_error_margin: 0.01 + input: + people: + earner: + age: 40 + employment_income: 14_000 + savings_interest_income: 29_500 + would_claim_marriage_allowance: false + spouse: + age: 40 + employment_income: 5_000 + benunits: + couple: + members: [earner, spouse] + is_married: true + households: + home: + members: [earner, spouse] + region: SCOTLAND + output: + # Pay: 1,500 x 19% = 285. Savings: 5,000 - 1,500 = 3,500 at the 0% + # starting rate (s. 12), 1,000 at the savings nil rate (s. 12A, 12B(4)), + # and 29,500 - 3,500 - 1,000 = 25,000 x 20% = 5,000. + # With the election, the law gives 5,285 - 250 = 5,035. + earned_income_tax: [285, 0] + savings_income_tax: [5_000, 0] + income_tax: [5_285, 0] + marriage_allowance: [0, 0] diff --git a/policyengine_uk/tests/test_council_tax_reduction_pensioner_children_tariff_properties.py b/policyengine_uk/tests/test_council_tax_reduction_pensioner_children_tariff_properties.py index fcfd1a8dff..338bb6133c 100644 --- a/policyengine_uk/tests/test_council_tax_reduction_pensioner_children_tariff_properties.py +++ b/policyengine_uk/tests/test_council_tax_reduction_pensioner_children_tariff_properties.py @@ -350,6 +350,9 @@ def test_northern_ireland_gets_no_new_amounts(): # tariff income, over the personal allowance and child amounts. For example, the # Welsh family with savings of 8,000 in 2026 has 9,000 - 5 * 52 + 416 = 9,156 # against 101.85 * 52 = 5,296.20, so an award of 1,228.04. +# +# Three 2019 Scottish rows use 2019-20's Scottish limits, which main lacked; +# the notes beside them give the arithmetic. ORIGIN_MAIN = { 2019: [ (9_412, 12_000, 1_482.4), @@ -359,15 +362,22 @@ def test_northern_ireland_gets_no_new_amounts(): (5_972.2, 22_574.64, 0), (3_801.2, 8_955.84, 0), (9_412, 12_000, 1_482.4), - (14_071.2, 17_170, 1_380.24), + # Scottish taxpayers pay 19% on the first 2,049 of taxable income in + # 2019-20 and 20% up to 12,444 (S5M-15879), where main used 2018-19's + # 2,000 and 12,150. This couple's 5,500 of taxable income pays 19% + # rather than 20% on 49 more, so the tax falls by 0.49 and the award + # by 20% of that. + (14_071.2, 17_170.49, 1_380.14), (9_412, 9_592.34, 1_963.93), - (3_801.2, 16_080.24, 889.22), + # The same 0.49 on this parent's 2,500 of taxable income. + (3_801.2, 16_080.73, 889.13), # The Scottish couple on 25,000 of pay. The Marriage Allowance comes # off the tax at Step 6, so taxable income stays 12,500 and its last - # 350 reach the 21% band from 12,150: 380 + 2,030 + 73.50 - 250 = - # 2,233.50 of tax. With the allowance off taxable income the tax was - # 2,230, so income falls by 3.50 and the award rises by 20% of that. - (5_972.2, 24_671.14, 461.48), + # 56 reach the 21% band from 12,444: 389.31 + 2,079 + 11.76 - 250 = + # 2,230.07 of tax. At 2018-19's limits the tax was 2,233.50, so income + # rises by 3.43 and the award falls by 20% of that. With the allowance + # off taxable income the tax would be 2,229.51. + (5_972.2, 24_674.57, 460.79), (3_801.2, 9_371.84, 937.87), (9_412, 12_000, 1_482.4), (14_071.2, 17_150, 1_384.24), diff --git a/policyengine_uk/tests/test_scottish_income_tax_rates.py b/policyengine_uk/tests/test_scottish_income_tax_rates.py index 88621ba922..b349fb7fb2 100644 --- a/policyengine_uk/tests/test_scottish_income_tax_rates.py +++ b/policyengine_uk/tests/test_scottish_income_tax_rates.py @@ -1,6 +1,112 @@ +import math + +import numpy as np import pytest +from hypothesis import HealthCheck, given, settings +from hypothesis import strategies as st +from policyengine_core.parameters import Parameter +from policyengine_uk import Simulation from policyengine_uk.system import system +from policyengine_uk.tax_benefit_system import statutory_parameters + +# The Scottish Parliament's rates and limits for each year, paragraph by +# paragraph, from each motion at +# https://www.parliament.scot/chamber-and-committees/votes-and-motions/. +# Each pair gives the rate and the upper taxable-income limit; `None` marks the +# final rate. Taxable income excludes the personal allowance. +SCOTTISH_RATE_RESOLUTIONS = { + 2017: ("S5M-03912", [(0.20, 31_500), (0.40, 150_000), (0.45, None)]), + 2018: ( + "S5M-10397", + [(0.19, 2_000), (0.20, 12_150), (0.21, 31_580), (0.41, 150_000), (0.46, None)], + ), + 2019: ( + "S5M-15879", + [(0.19, 2_049), (0.20, 12_444), (0.21, 30_930), (0.41, 150_000), (0.46, None)], + ), + 2020: ( + "S5M-21090", + [(0.19, 2_085), (0.20, 12_658), (0.21, 30_930), (0.41, 150_000), (0.46, None)], + ), + 2021: ( + "S5M-24225", + [(0.19, 2_097), (0.20, 12_726), (0.21, 31_092), (0.41, 150_000), (0.46, None)], + ), + 2022: ( + "S6M-03019", + [(0.19, 2_162), (0.20, 13_118), (0.21, 31_092), (0.41, 150_000), (0.46, None)], + ), + 2023: ( + "S6M-07853", + [(0.19, 2_162), (0.20, 13_118), (0.21, 31_092), (0.42, 125_140), (0.47, None)], + ), + 2024: ( + "S6M-12252", + [ + (0.19, 2_306), + (0.20, 13_991), + (0.21, 31_092), + (0.42, 62_430), + (0.45, 125_140), + (0.48, None), + ], + ), + 2025: ( + "S6M-16531", + [ + (0.19, 2_827), + (0.20, 14_921), + (0.21, 31_092), + (0.42, 62_430), + (0.45, 125_140), + (0.48, None), + ], + ), + 2026: ( + "S6M-20844", + [ + (0.19, 3_967), + (0.20, 16_956), + (0.21, 31_092), + (0.42, 62_430), + (0.45, 125_140), + (0.48, None), + ], + ), +} + +# gov.scot's table for each year: the standard personal allowance it assumes +# and the top of each band but the last, as income including that allowance. +# 2017-18: https://www.gov.scot/news/income-tax-rates-set/ +# 2018-19: https://www.gov.scot/publications/scottish-income-tax-2018-2019/ +# 2019-20 to 2021-22: https://www.gov.scot/publications/scottish-income-tax-2019-2020/ +# and the 2020-2021 and 2021-2022 pages, now in the National Records of +# Scotland web archive. +# 2022-23 onwards: https://www.gov.scot/publications/scottish-income-tax-rates-and-bands/ +GOV_SCOT_TABLES = { + 2017: (11_500, [43_000, 150_000]), + 2018: (11_850, [13_850, 24_000, 43_430, 150_000]), + 2019: (12_500, [14_549, 24_944, 43_430, 150_000]), + 2020: (12_500, [14_585, 25_158, 43_430, 150_000]), + 2021: (12_570, [14_667, 25_296, 43_662, 150_000]), + 2022: (12_570, [14_732, 25_688, 43_662, 150_000]), + 2023: (12_570, [14_732, 25_688, 43_662, 125_140]), + 2024: (12_570, [14_876, 26_561, 43_662, 75_000, 125_140]), + 2025: (12_570, [15_397, 27_491, 43_662, 75_000, 125_140]), + 2026: (12_570, [16_537, 29_526, 43_662, 75_000, 125_140]), +} +# ITA 2007 s. 35(2): the allowance falls by half the income above £100,000. +ALLOWANCE_TAPER_START = 100_000 + +YEARS = sorted(SCOTTISH_RATE_RESOLUTIONS) + + +def resolution_scale(year): + """Build thresholds and rates from Parliament's schedule for the year.""" + bands = SCOTTISH_RATE_RESOLUTIONS[year][1] + thresholds = [0] + [limit for _, limit in bands[:-1]] + return thresholds, [rate for rate, _ in bands] @pytest.mark.parametrize( @@ -20,9 +126,10 @@ def test_scottish_income_tax_thresholds_match_the_resolution(year, thresholds): @pytest.mark.parametrize("year", [2026, 2027, 2028, 2029, 2030]) def test_scottish_announced_threshold_freeze_then_cpi_projection(year): - # Scottish Budget 2026-27, Income Tax Policy: the higher, advanced and - # top cash thresholds stay fixed through 2028-29. Future CPI uprating - # is the model's existing projection, not a Budget commitment. + # The Scottish Government announced fixed higher, advanced and top cash + # thresholds through 2028-29 in the Scottish Budget 2026-27. The model + # projects CPI uprating afterwards; the government has made no commitment + # to it. # https://www.gov.scot/publications/scottish-budget-2026-2027/pages/4/ rates = system.parameters.gov.hmrc.income_tax.rates.scotland.rates(year) cpi = system.parameters.gov.economic_assumptions.indices.obr.consumer_price_index @@ -37,3 +144,177 @@ def test_scottish_announced_threshold_freeze_then_cpi_projection(year): abs=1e-6, ) assert rates.rates == [0.19, 0.20, 0.21, 0.42, 0.45, 0.48] + + +@pytest.mark.parametrize("year", YEARS) +def test_scottish_rates_and_limits_match_each_resolution(year): + """Check each model year against the schedule the Scottish Parliament set for that year. + + The model reads each tax year's parameters at 30 April, so the year + label 2019 carries the 2019-20 resolution. + """ + thresholds, rates = resolution_scale(year) + scale = system.parameters(year).gov.hmrc.income_tax.rates.scotland.rates + + assert list(scale.thresholds) == pytest.approx(thresholds, abs=0) + assert list(scale.rates) == pytest.approx(rates, abs=0) + + +@pytest.fixture(scope="module") +def statutory_scottish_rates(): + return statutory_parameters().gov.hmrc.income_tax.rates.scotland.rates + + +def dated_values(scale): + """Every rate and limit the scale's YAML dates, as (date, value) pairs.""" + return [ + (value.instant_str, value.value) + for parameter in scale.get_descendants() + if isinstance(parameter, Parameter) + for value in parameter.values_list + ] + + +@pytest.mark.parametrize("year", YEARS) +def test_each_resolution_holds_for_its_whole_tax_year(year, statutory_scottish_rates): + """A resolution applies for the whole of one tax year (SA 1998 s. 80C(3)), + 6 April to the following 5 April (ITA 2007 s. 4). The scale changes only on + the dates its YAML gives, so read it on 6 April, on every such date within + the year and on 5 April.""" + thresholds, rates = resolution_scale(year) + start, end = f"{year}-04-06", f"{year + 1}-04-05" + changes = {date for date, _ in dated_values(statutory_scottish_rates)} + for instant in sorted({start, end} | {d for d in changes if start < d <= end}): + scale = statutory_scottish_rates(instant) + assert list(scale.thresholds) == pytest.approx(thresholds, abs=0), instant + assert list(scale.rates) == pytest.approx(rates, abs=0), instant + + +def test_scottish_rates_take_effect_on_6_april_2017(statutory_scottish_rates): + """The scale starts with S5M-03912, Parliament's resolution for 2017-18, + and that tax year starts on 6 April 2017 (ITA 2007 s. 4(3)). The scale + dates no rate or limit earlier.""" + numeric = [ + date + for date, value in dated_values(statutory_scottish_rates) + if value is not None + ] + assert min(numeric) == f"{YEARS[0]}-04-06" + + +@pytest.mark.parametrize("year", YEARS) +def test_scottish_limits_plus_personal_allowance_match_gov_scot_tables(year): + """gov.scot adds the standard personal allowance to each limit of taxable + income in its table. Above the point where s. 35(2) withdraws the + allowance in full, taxable income equals income.""" + allowance, band_tops = GOV_SCOT_TABLES[year] + parameters = system.parameters(year).gov.hmrc.income_tax + scale = parameters.rates.scotland.rates + assert parameters.allowances.personal_allowance.amount == allowance + + assert len(scale.thresholds) == len(band_tops) + 1 + for limit, top in zip(scale.thresholds[1:], band_tops): + if top <= ALLOWANCE_TAPER_START: + assert limit + allowance == top + else: + assert top >= ALLOWANCE_TAPER_START + 2 * allowance + assert limit == top + + +# Hand-built Scottish taxpayers with pay alone, in every year from 2017-18. +# Each example draws many people, as a calculation costs about the same for +# one person as for many. +PEOPLE = 48 +PROPERTY_SETTINGS = settings( + max_examples=4, + deadline=None, + derandomize=True, + suppress_health_check=[HealthCheck.too_slow], +) +pay = st.one_of( + st.integers(0, 300_000), + st.sampled_from( + sorted( + { + top + for allowance, tops in GOV_SCOT_TABLES.values() + for top in [allowance, *tops] + } + | {ALLOWANCE_TAPER_START, 160_000} + ) + ), +) +pays = st.lists(pay, min_size=PEOPLE // 2, max_size=PEOPLE // 2) +raises = st.lists(st.integers(0, 20_000), min_size=PEOPLE // 2, max_size=PEOPLE // 2) + + +def reference_tax(year, amount): + """Income tax on pay alone, from the resolution and gov.scot's allowance.""" + # s. 35(3) rounds the tapered allowance up to a whole pound. + allowance = max( + 0, + math.ceil( + GOV_SCOT_TABLES[year][0] - max(0, amount - ALLOWANCE_TAPER_START) / 2 + ), + ) + taxable = max(0, amount - allowance) + thresholds, rates = resolution_scale(year) + tops = thresholds[1:] + [np.inf] + return sum( + rate * max(0, min(taxable, top) - bottom) + for bottom, top, rate in zip(thresholds, tops, rates) + ) + + +@pytest.fixture(scope="module") +def scottish_sim(): + situation = { + "people": { + f"p{i}": { + "age": {year: 40 for year in YEARS}, + "employment_income": {year: 0 for year in YEARS}, + } + for i in range(PEOPLE) + }, + "benunits": {f"b{i}": {"members": [f"p{i}"]} for i in range(PEOPLE)}, + "households": { + f"h{i}": { + "members": [f"p{i}"], + "region": {year: "SCOTLAND" for year in YEARS}, + } + for i in range(PEOPLE) + }, + } + return Simulation(situation=situation) + + +def income_tax_by_year(sim, amounts): + for year in YEARS: + # The simulation moves pay to the source of its labour-supply formula. + sim.set_input("employment_income_before_lsr", year, amounts) + sim.reset_calculations() + for year in YEARS: + np.testing.assert_array_equal(sim.calculate("employment_income", year), amounts) + assert sim.calculate("pays_scottish_income_tax", year).all() + return {year: sim.calculate("income_tax", year).astype(float) for year in YEARS} + + +def tolerance(*amounts): + """A penny plus rounding of the model's float32 monetary values.""" + return 0.01 + 4e-7 * sum(np.abs(amount) for amount in amounts) + + +@PROPERTY_SETTINGS +@given(pays, raises) +def test_scottish_pay_is_taxed_as_each_resolution_sets(scottish_sim, drawn, extra): + """Every year's income tax on pay equals the resolution's bands applied to + pay less the allowance, and it never falls as pay rises.""" + lower = np.array(drawn, dtype=float) + upper = lower + np.array(extra) + amounts = np.concatenate([lower, upper]) + taxes = income_tax_by_year(scottish_sim, amounts) + for year, tax in taxes.items(): + expected = np.array([reference_tax(year, amount) for amount in amounts]) + assert (np.abs(tax - expected) <= tolerance(tax, expected)).all(), year + below, above = np.split(tax, 2) + assert (above >= below - tolerance(below, above)).all(), year