UK payslips for equity-comp employees report RSU vests as notional pay for HMRC only. A paired same-magnitude deduction (Shares Retained / Stock Tax Withholding / RSU Offset) nets it back out of cash. The UK payslip's income_tax line shows tax on the grossed-up total, but the actual RSU tax is handled by Schwab (US broker) via share sale. No cash flows through UK payroll for RSU. Previously the extractor folded RSU notional into gross_pay and income_tax, which inflated the dashboard numbers — a payslip with £25k RSU vest looked like 2x salary with 80% tax rate. Changes: - schema: add rsu_vest + rsu_offset fields (default 0). - db + alembic 0002: add two new NUMERIC(12,2) columns with server default 0 (backward-compatible; existing rows get 0). - validate_totals: include rsu_offset in deductions sum so the gross + rsu_vest inflation is properly netted out. - extraction prompt: explicit rules for identifying RSU lines by the common Meta/Sage/Workday labels, and to NOT put them in other_deductions. Dashboards in a follow-up commit: cash_gross = gross_pay - rsu_vest, effective tax rate based on cash metrics. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
33 lines
1,011 B
Python
33 lines
1,011 B
Python
"""Add rsu_vest and rsu_offset columns.
|
|
|
|
UK payslips for Meta report RSU grants as notional pay (gross inflation)
|
|
and offset them via a same-magnitude deduction. The cash gross Viktor
|
|
cares about for dashboarding is gross_pay - rsu_vest. Track both for
|
|
reporting + exactness; cash and tax-rate charts compute from them.
|
|
"""
|
|
import sqlalchemy as sa
|
|
|
|
from alembic import op
|
|
|
|
revision = "0002_add_rsu_columns"
|
|
down_revision = "0001_initial"
|
|
branch_labels = None
|
|
depends_on = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.add_column(
|
|
"payslip",
|
|
sa.Column("rsu_vest", sa.Numeric(12, 2), nullable=False, server_default=sa.text("0")),
|
|
schema="payslip_ingest",
|
|
)
|
|
op.add_column(
|
|
"payslip",
|
|
sa.Column("rsu_offset", sa.Numeric(12, 2), nullable=False, server_default=sa.text("0")),
|
|
schema="payslip_ingest",
|
|
)
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.drop_column("payslip", "rsu_offset", schema="payslip_ingest")
|
|
op.drop_column("payslip", "rsu_vest", schema="payslip_ingest")
|