parser + P60 ingest: split income_tax cash/RSU, add P60 ground-truth

Meta variant-B payslips gross up Taxable Pay for RSU and compute PAYE on
the grossed-up figure, so `income_tax` on the slip is the total PAYE
(cash + RSU-attributed). Dashboards that stacked the raw figure made
vest-month tax look ~2x higher than "cash tax paid". Introduce
`cash_income_tax = income_tax * (gross_pay - pension_sacrifice) /
taxable_pay` as a derived column alongside the raw figure. Dashboards
can now stack cash vs RSU-attributed tax as separate segments.

Also capture YTD column values of `RSU Tax Offset` and `RSU Excs Refund`
from the Payments grid — needed for reconciliation against HMRC annual
figures.

P60 ingest: new parser under `parsers/p60.py` anchoring on statutory
HMRC line labels (`Tax year to 5 April YYYY`, `Employer PAYE reference`,
`In this employment` pay/tax row, NI letter bands). Processor routes
documents carrying the `p60` Paperless tag to `_handle_p60` which
writes to the new `payslip_ingest.p60_reference` table (one row per
tax_year+employer). App lifespan resolves the tag id at startup; missing
tag disables dispatch without breaking payslip ingest. Paperless tag
creation + webhook config are manual follow-ups.

Migrations:
- 0004 — cash_income_tax + ytd_rsu_tax_offset + ytd_rsu_excs_refund on
  payslip, all nullable.
- 0005 — p60_reference table with (tax_year, employer) unique +
  paperless_doc_id unique for idempotent re-uploads.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Viktor Barzin 2026-04-19 15:23:05 +00:00
parent d91f34ddb4
commit 26e43b1055
14 changed files with 644 additions and 15 deletions

51
tests/fixtures/meta_uk_p60_2024_25.txt vendored Normal file
View file

@ -0,0 +1,51 @@
P60 End of Year Certificate
Tax year to 5 April 2025
Employee's details
Surname BARZIN
First two forenames VIKTOR
National Insurance number AA 12 34 56 A
Works/Payroll number 254680
Pay and Income Tax details
Pay Tax deducted
In previous employment(s) £0.00 £0.00
In this employment £232,630.34 £95,820.11
Total for year £232,630.34 £95,820.11
Final tax code 1257L
National Insurance contributions in this employment
NI table letter Earnings at Earnings above Total of employee's
LEL LEL up to UEL contributions in
this employment
A £6,396.00 £47,268.00 £5,172.40
Statutory payments included in the pay 'In this employment' figure
Statutory Maternity Pay £0.00
Statutory Paternity Pay £0.00
Student Loan deductions in this employment £0.00
Other details
Your employer's full name and address
Facebook UK Limited
10 Brock Street
London
NW1 3FG
Employer PAYE reference 120/FA12345
This form shows your total pay for Income Tax purposes in this employment
for the year. Any overtime, bonus, commission etc, Statutory Sick Pay,
Statutory Maternity Pay, Statutory Paternity Pay or Shared Parental Pay,
Statutory Parental Bereavement Pay is included.