{ "annotations": { "list": [ { "builtIn": 1, "datasource": { "type": "datasource", "uid": "grafana" }, "enable": true, "hide": true, "iconColor": "rgba(0, 211, 255, 1)", "name": "Annotations & Alerts", "type": "dashboard" } ] }, "description": "UK payslip breakdown \u2014 tax-year YTD hero, monthly cash flow, effective-rate trend, data-integrity flags, and full payslip table.", "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 1, "id": null, "links": [], "panels": [ { "id": 1, "title": "YTD sources \u2014 income composition", "description": "Year-to-date cumulative breakdown of gross pay by source. Stacked \u2014 top of the stack equals gross_pay. Reset at each tax-year boundary.", "type": "timeseries", "datasource": { "type": "grafana-postgresql-datasource", "uid": "payslips-pg" }, "gridPos": { "h": 10, "w": 12, "x": 0, "y": 0 }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "unit": "currencyGBP", "custom": { "axisPlacement": "auto", "drawStyle": "line", "fillOpacity": 70, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "lineWidth": 1, "pointSize": 4, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "normal" }, "thresholdsStyle": { "mode": "off" } } }, "overrides": [ { "matcher": { "id": "byName", "options": "ytd_salary" }, "properties": [ { "id": "color", "value": { "mode": "fixed", "fixedColor": "green" } }, { "id": "displayName", "value": "Salary" } ] }, { "matcher": { "id": "byName", "options": "ytd_bonus" }, "properties": [ { "id": "color", "value": { "mode": "fixed", "fixedColor": "yellow" } }, { "id": "displayName", "value": "Bonus" } ] }, { "matcher": { "id": "byName", "options": "ytd_rsu" }, "properties": [ { "id": "color", "value": { "mode": "fixed", "fixedColor": "blue" } }, { "id": "displayName", "value": "RSU (notional)" } ] }, { "matcher": { "id": "byName", "options": "ytd_other" }, "properties": [ { "id": "color", "value": { "mode": "fixed", "fixedColor": "text" } }, { "id": "displayName", "value": "Other / residual" } ] } ] }, "options": { "legend": { "calcs": [ "last", "max" ], "displayMode": "table", "placement": "bottom" }, "tooltip": { "mode": "multi", "sort": "desc" } }, "targets": [ { "datasource": { "type": "grafana-postgresql-datasource", "uid": "payslips-pg" }, "rawSql": "SELECT pay_date AS \"time\", SUM(salary) OVER w AS ytd_salary, SUM(bonus) OVER w AS ytd_bonus, SUM(rsu_vest) OVER w AS ytd_rsu, SUM(GREATEST(gross_pay - salary - bonus - rsu_vest, 0)) OVER w AS ytd_other FROM payslip_ingest.payslip WHERE $__timeFilter(pay_date) WINDOW w AS (PARTITION BY tax_year ORDER BY pay_date) ORDER BY pay_date", "format": "time_series", "refId": "A", "rawQuery": true, "editorMode": "code" } ] }, { "id": 7, "title": "YTD uses \u2014 where gross went", "description": "Year-to-date cumulative breakdown of where the gross went. Stacked \u2014 top equals gross_pay minus student loan and RSU offset (both small; shown on Panel 8 Sankey). Green = take-home; red = cash income tax; orange = RSU-attributed income tax + NI; purple = pension.", "type": "timeseries", "datasource": { "type": "grafana-postgresql-datasource", "uid": "payslips-pg" }, "gridPos": { "h": 10, "w": 12, "x": 12, "y": 0 }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "unit": "currencyGBP", "custom": { "axisPlacement": "auto", "drawStyle": "line", "fillOpacity": 70, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "lineWidth": 1, "pointSize": 4, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "normal" }, "thresholdsStyle": { "mode": "off" } } }, "overrides": [ { "matcher": { "id": "byName", "options": "ytd_net" }, "properties": [ { "id": "color", "value": { "mode": "fixed", "fixedColor": "green" } }, { "id": "displayName", "value": "Net (take-home)" } ] }, { "matcher": { "id": "byName", "options": "ytd_cash_income_tax" }, "properties": [ { "id": "color", "value": { "mode": "fixed", "fixedColor": "#C4162A" } }, { "id": "displayName", "value": "Income Tax (cash pay)" } ] }, { "matcher": { "id": "byName", "options": "ytd_rsu_income_tax" }, "properties": [ { "id": "color", "value": { "mode": "fixed", "fixedColor": "#E0652E" } }, { "id": "displayName", "value": "Income Tax (RSU-attributed)" } ] }, { "matcher": { "id": "byName", "options": "ytd_ni" }, "properties": [ { "id": "color", "value": { "mode": "fixed", "fixedColor": "orange" } }, { "id": "displayName", "value": "National Insurance" } ] }, { "matcher": { "id": "byName", "options": "ytd_pension_employee" }, "properties": [ { "id": "color", "value": { "mode": "fixed", "fixedColor": "purple" } }, { "id": "displayName", "value": "Pension (employee)" } ] } ] }, "options": { "legend": { "calcs": [ "last", "max" ], "displayMode": "table", "placement": "bottom" }, "tooltip": { "mode": "multi", "sort": "desc" } }, "targets": [ { "datasource": { "type": "grafana-postgresql-datasource", "uid": "payslips-pg" }, "rawSql": "SELECT pay_date AS \"time\", SUM(net_pay) OVER w AS ytd_net, SUM(COALESCE(cash_income_tax, income_tax)) OVER w AS ytd_cash_income_tax, SUM(income_tax - COALESCE(cash_income_tax, income_tax)) OVER w AS ytd_rsu_income_tax, SUM(national_insurance) OVER w AS ytd_ni, SUM(pension_employee) OVER w AS ytd_pension_employee FROM payslip_ingest.payslip WHERE $__timeFilter(pay_date) WINDOW w AS (PARTITION BY tax_year ORDER BY pay_date) ORDER BY pay_date", "format": "time_series", "refId": "A", "rawQuery": true, "editorMode": "code" } ] }, { "id": 2, "title": "Monthly cash flow (RSU stripped)", "type": "timeseries", "datasource": { "type": "grafana-postgresql-datasource", "uid": "payslips-pg" }, "gridPos": { "h": 9, "w": 12, "x": 0, "y": 10 }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "unit": "currencyGBP", "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "drawStyle": "line", "fillOpacity": 10, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "lineWidth": 2, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } } }, "overrides": [] }, "options": { "legend": { "calcs": [ "last", "mean" ], "displayMode": "table", "placement": "bottom" }, "tooltip": { "mode": "multi", "sort": "desc" } }, "targets": [ { "datasource": { "type": "grafana-postgresql-datasource", "uid": "payslips-pg" }, "rawSql": "SELECT pay_date AS \"time\", (gross_pay - rsu_vest) AS cash_gross, net_pay, COALESCE(cash_income_tax, income_tax) AS income_tax, national_insurance FROM payslip_ingest.payslip WHERE $__timeFilter(pay_date) ORDER BY pay_date", "format": "time_series", "refId": "A", "rawQuery": true, "editorMode": "code" } ] }, { "id": 3, "title": "Effective rate & take-home % (YTD cumulative)", "description": "YTD-cumulative rates. PAYE rate uses reported taxable_pay as the base; all-deductions rate uses gross_pay. Computed from cumulative SUM over the tax year, so vest-month RSU tax is blended proportionally with RSU value \u2014 no per-slip attribution hack, no spikes.", "type": "timeseries", "datasource": { "type": "grafana-postgresql-datasource", "uid": "payslips-pg" }, "gridPos": { "h": 9, "w": 12, "x": 12, "y": 10 }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "unit": "percent", "min": 0, "max": 100, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "drawStyle": "line", "fillOpacity": 10, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "lineWidth": 2, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } } }, "overrides": [] }, "options": { "legend": { "calcs": [ "last", "mean" ], "displayMode": "table", "placement": "bottom" }, "tooltip": { "mode": "multi", "sort": "desc" } }, "targets": [ { "datasource": { "type": "grafana-postgresql-datasource", "uid": "payslips-pg" }, "rawSql": "SELECT pay_date AS \"time\", ROUND(((SUM(income_tax) OVER w)::numeric / NULLIF(SUM(COALESCE(taxable_pay, gross_pay)) OVER w, 0)) * 100, 2) AS \"ytd_paye_rate_pct\", ROUND((((SUM(income_tax) OVER w) + (SUM(national_insurance) OVER w) + (SUM(student_loan) OVER w))::numeric / NULLIF(SUM(gross_pay) OVER w, 0)) * 100, 2) AS \"ytd_all_deductions_pct\", ROUND(((SUM(net_pay) OVER w)::numeric / NULLIF(SUM(gross_pay) OVER w, 0)) * 100, 2) AS \"ytd_take_home_pct\" FROM payslip_ingest.payslip WHERE $__timeFilter(pay_date) WINDOW w AS (PARTITION BY tax_year ORDER BY pay_date) ORDER BY pay_date", "format": "time_series", "refId": "A", "rawQuery": true, "editorMode": "code" } ] }, { "id": 11, "title": "Tax & pension \u2014 monthly", "description": "Per-month deductions and pension contributions. Stacked \u2014 top equals total tax + pension (both sides). Red = cash income tax; orange = RSU-attributed income tax; amber = NI; brown = student loan; purple = employee pension; light purple = employer pension (paid on top of salary).", "type": "timeseries", "datasource": { "type": "grafana-postgresql-datasource", "uid": "payslips-pg" }, "gridPos": { "h": 10, "w": 12, "x": 0, "y": 19 }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "unit": "currencyGBP", "custom": { "axisPlacement": "auto", "drawStyle": "line", "fillOpacity": 70, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "lineWidth": 1, "pointSize": 4, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "normal" }, "thresholdsStyle": { "mode": "off" } } }, "overrides": [ { "matcher": { "id": "byName", "options": "cash_income_tax" }, "properties": [ { "id": "color", "value": { "mode": "fixed", "fixedColor": "#C4162A" } }, { "id": "displayName", "value": "Income Tax (cash pay)" } ] }, { "matcher": { "id": "byName", "options": "rsu_income_tax" }, "properties": [ { "id": "color", "value": { "mode": "fixed", "fixedColor": "#E0652E" } }, { "id": "displayName", "value": "Income Tax (RSU-attributed)" } ] }, { "matcher": { "id": "byName", "options": "ni" }, "properties": [ { "id": "color", "value": { "mode": "fixed", "fixedColor": "orange" } }, { "id": "displayName", "value": "National Insurance" } ] }, { "matcher": { "id": "byName", "options": "student_loan" }, "properties": [ { "id": "color", "value": { "mode": "fixed", "fixedColor": "#8B4513" } }, { "id": "displayName", "value": "Student Loan" } ] }, { "matcher": { "id": "byName", "options": "pension_employee" }, "properties": [ { "id": "color", "value": { "mode": "fixed", "fixedColor": "purple" } }, { "id": "displayName", "value": "Pension (employee)" } ] }, { "matcher": { "id": "byName", "options": "pension_employer" }, "properties": [ { "id": "color", "value": { "mode": "fixed", "fixedColor": "#CE96D8" } }, { "id": "displayName", "value": "Pension (employer)" } ] } ] }, "options": { "legend": { "calcs": [ "last", "max" ], "displayMode": "table", "placement": "bottom" }, "tooltip": { "mode": "multi", "sort": "desc" } }, "targets": [ { "datasource": { "type": "grafana-postgresql-datasource", "uid": "payslips-pg" }, "rawSql": "SELECT pay_date AS \"time\", COALESCE(cash_income_tax, income_tax) AS cash_income_tax, income_tax - COALESCE(cash_income_tax, income_tax) AS rsu_income_tax, national_insurance AS ni, student_loan, pension_employee, pension_employer FROM payslip_ingest.payslip WHERE $__timeFilter(pay_date) ORDER BY pay_date", "format": "time_series", "refId": "A", "rawQuery": true, "editorMode": "code" } ] }, { "id": 12, "title": "Tax & pension \u2014 YTD cumulative", "description": "Year-to-date cumulative tax and pension. Same series and colors as the monthly panel; resets on 6-April tax year boundary.", "type": "timeseries", "datasource": { "type": "grafana-postgresql-datasource", "uid": "payslips-pg" }, "gridPos": { "h": 10, "w": 12, "x": 12, "y": 19 }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "unit": "currencyGBP", "custom": { "axisPlacement": "auto", "drawStyle": "line", "fillOpacity": 70, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "lineWidth": 1, "pointSize": 4, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "normal" }, "thresholdsStyle": { "mode": "off" } } }, "overrides": [ { "matcher": { "id": "byName", "options": "ytd_cash_income_tax" }, "properties": [ { "id": "color", "value": { "mode": "fixed", "fixedColor": "#C4162A" } }, { "id": "displayName", "value": "Income Tax (cash pay)" } ] }, { "matcher": { "id": "byName", "options": "ytd_rsu_income_tax" }, "properties": [ { "id": "color", "value": { "mode": "fixed", "fixedColor": "#E0652E" } }, { "id": "displayName", "value": "Income Tax (RSU-attributed)" } ] }, { "matcher": { "id": "byName", "options": "ytd_ni" }, "properties": [ { "id": "color", "value": { "mode": "fixed", "fixedColor": "orange" } }, { "id": "displayName", "value": "National Insurance" } ] }, { "matcher": { "id": "byName", "options": "ytd_student_loan" }, "properties": [ { "id": "color", "value": { "mode": "fixed", "fixedColor": "#8B4513" } }, { "id": "displayName", "value": "Student Loan" } ] }, { "matcher": { "id": "byName", "options": "ytd_pension_employee" }, "properties": [ { "id": "color", "value": { "mode": "fixed", "fixedColor": "purple" } }, { "id": "displayName", "value": "Pension (employee)" } ] }, { "matcher": { "id": "byName", "options": "ytd_pension_employer" }, "properties": [ { "id": "color", "value": { "mode": "fixed", "fixedColor": "#CE96D8" } }, { "id": "displayName", "value": "Pension (employer)" } ] } ] }, "options": { "legend": { "calcs": [ "last", "max" ], "displayMode": "table", "placement": "bottom" }, "tooltip": { "mode": "multi", "sort": "desc" } }, "targets": [ { "datasource": { "type": "grafana-postgresql-datasource", "uid": "payslips-pg" }, "rawSql": "SELECT pay_date AS \"time\", SUM(COALESCE(cash_income_tax, income_tax)) OVER w AS ytd_cash_income_tax, SUM(income_tax - COALESCE(cash_income_tax, income_tax)) OVER w AS ytd_rsu_income_tax, SUM(national_insurance) OVER w AS ytd_ni, SUM(student_loan) OVER w AS ytd_student_loan, SUM(pension_employee) OVER w AS ytd_pension_employee, SUM(pension_employer) OVER w AS ytd_pension_employer FROM payslip_ingest.payslip WHERE $__timeFilter(pay_date) WINDOW w AS (PARTITION BY tax_year ORDER BY pay_date) ORDER BY pay_date", "format": "time_series", "refId": "A", "rawQuery": true, "editorMode": "code" } ] }, { "id": 13, "title": "Monthly cash flow \u2014 tax impact (RSU excluded)", "description": "Cash-only deductions. RSU PAYE excluded \u2014 it's withheld at source via sell-to-cover and never hits the bank account. See Panel 11 for the full PAYE view including RSU-attributed tax.", "type": "timeseries", "datasource": { "type": "grafana-postgresql-datasource", "uid": "payslips-pg" }, "gridPos": { "h": 9, "w": 24, "x": 0, "y": 29 }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "unit": "currencyGBP", "custom": { "axisPlacement": "auto", "drawStyle": "bars", "fillOpacity": 100, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "lineWidth": 0, "pointSize": 4, "scaleDistribution": { "type": "linear" }, "showPoints": "never", "spanNulls": false, "stacking": { "group": "A", "mode": "normal" }, "thresholdsStyle": { "mode": "off" } } }, "overrides": [ { "matcher": { "id": "byName", "options": "cash_income_tax" }, "properties": [ { "id": "color", "value": { "mode": "fixed", "fixedColor": "#C4162A" } }, { "id": "displayName", "value": "Income Tax (cash pay)" } ] }, { "matcher": { "id": "byName", "options": "ni" }, "properties": [ { "id": "color", "value": { "mode": "fixed", "fixedColor": "orange" } }, { "id": "displayName", "value": "National Insurance" } ] }, { "matcher": { "id": "byName", "options": "student_loan" }, "properties": [ { "id": "color", "value": { "mode": "fixed", "fixedColor": "#8B4513" } }, { "id": "displayName", "value": "Student Loan" } ] }, { "matcher": { "id": "byName", "options": "pension_employee" }, "properties": [ { "id": "color", "value": { "mode": "fixed", "fixedColor": "purple" } }, { "id": "displayName", "value": "Pension (employee)" } ] } ] }, "options": { "legend": { "calcs": [ "last", "max", "sum" ], "displayMode": "table", "placement": "bottom" }, "tooltip": { "mode": "multi", "sort": "desc" } }, "targets": [ { "datasource": { "type": "grafana-postgresql-datasource", "uid": "payslips-pg" }, "rawSql": "SELECT pay_date AS \"time\", COALESCE(cash_income_tax, income_tax) AS cash_income_tax, national_insurance AS ni, student_loan, pension_employee FROM payslip_ingest.payslip WHERE $__timeFilter(pay_date) ORDER BY pay_date", "format": "time_series", "refId": "A", "rawQuery": true, "editorMode": "code" } ] }, { "id": 4, "title": "Data integrity \u2014 missing months & parser flags", "type": "table", "datasource": { "type": "grafana-postgresql-datasource", "uid": "payslips-pg" }, "gridPos": { "h": 6, "w": 24, "x": 0, "y": 38 }, "fieldConfig": { "defaults": { "custom": { "align": "right", "cellOptions": { "type": "auto" }, "inspect": false }, "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null } ] } }, "overrides": [ { "matcher": { "id": "byName", "options": "month_start" }, "properties": [ { "id": "custom.width", "value": 120 }, { "id": "custom.align", "value": "left" } ] }, { "matcher": { "id": "byName", "options": "status" }, "properties": [ { "id": "custom.width", "value": 140 }, { "id": "custom.align", "value": "center" }, { "id": "custom.cellOptions", "value": { "type": "color-background", "mode": "basic" } }, { "id": "mappings", "value": [ { "type": "value", "options": { "MISSING": { "color": "red", "index": 0, "text": "MISSING" }, "ZERO_SALARY": { "color": "red", "index": 1, "text": "ZERO_SALARY" }, "RSU_NO_SALARY": { "color": "red", "index": 2, "text": "RSU_NO_SALARY" }, "NULL_CASH_TAX": { "color": "orange", "index": 3, "text": "NULL_CASH_TAX" }, "ok": { "color": "green", "index": 4, "text": "ok" } } } ] } ] }, { "matcher": { "id": "byName", "options": "pay_date" }, "properties": [ { "id": "custom.width", "value": 120 }, { "id": "custom.align", "value": "left" } ] }, { "matcher": { "id": "byType", "options": "number" }, "properties": [ { "id": "unit", "value": "currencyGBP" } ] }, { "matcher": { "id": "byName", "options": "paperless_doc_id" }, "properties": [ { "id": "unit", "value": "none" }, { "id": "custom.width", "value": 100 } ] } ] }, "options": { "cellHeight": "sm", "footer": { "show": false } }, "targets": [ { "refId": "A", "datasource": { "type": "grafana-postgresql-datasource", "uid": "payslips-pg" }, "rawQuery": true, "editorMode": "code", "format": "table", "rawSql": "WITH expected AS (SELECT generate_series(DATE '2019-07-01', DATE_TRUNC('month', CURRENT_DATE), '1 month'::interval)::date AS month_start), actual AS (SELECT DATE_TRUNC('month', pay_date)::date AS month_start, pay_date, salary, gross_pay, rsu_vest, cash_income_tax, income_tax, paperless_doc_id FROM payslip_ingest.payslip) SELECT e.month_start, CASE WHEN a.pay_date IS NULL THEN 'MISSING' WHEN a.salary = 0 AND a.gross_pay > 5000 THEN 'ZERO_SALARY' WHEN a.rsu_vest > 0 AND a.salary = 0 THEN 'RSU_NO_SALARY' WHEN a.rsu_vest > 0 AND a.cash_income_tax IS NULL THEN 'NULL_CASH_TAX' ELSE 'ok' END AS status, a.pay_date, a.salary, a.gross_pay, a.rsu_vest, a.cash_income_tax, a.paperless_doc_id FROM expected e LEFT JOIN actual a ON a.month_start = e.month_start WHERE e.month_start >= DATE '2019-07-01' ORDER BY e.month_start DESC" } ] }, { "id": 5, "title": "All payslips \u2014 detailed breakdown", "type": "table", "datasource": { "type": "grafana-postgresql-datasource", "uid": "payslips-pg" }, "gridPos": { "h": 14, "w": 24, "x": 0, "y": 44 }, "fieldConfig": { "defaults": { "custom": { "align": "right", "cellOptions": { "type": "auto" }, "inspect": true }, "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null } ] } }, "overrides": [ { "matcher": { "id": "byName", "options": "pay_date" }, "properties": [ { "id": "custom.width", "value": 110 }, { "id": "custom.align", "value": "left" } ] }, { "matcher": { "id": "byName", "options": "employer" }, "properties": [ { "id": "custom.width", "value": 150 }, { "id": "custom.align", "value": "left" } ] }, { "matcher": { "id": "byName", "options": "tax_year" }, "properties": [ { "id": "custom.width", "value": 80 } ] }, { "matcher": { "id": "byName", "options": "validated" }, "properties": [ { "id": "custom.width", "value": 80 }, { "id": "custom.cellOptions", "value": { "type": "color-text" } }, { "id": "mappings", "value": [ { "type": "value", "options": { "true": { "color": "green", "text": "\u2713" }, "false": { "color": "red", "text": "\u26a0" } } } ] } ] }, { "matcher": { "id": "byType", "options": "number" }, "properties": [ { "id": "unit", "value": "currencyGBP" } ] }, { "matcher": { "id": "byName", "options": "paperless_doc_id" }, "properties": [ { "id": "unit", "value": "none" }, { "id": "custom.width", "value": 80 } ] } ] }, "options": { "footer": { "show": true, "reducer": [ "sum" ], "fields": [ "gross_pay", "net_pay", "income_tax", "national_insurance", "pension_employee", "pension_employer", "student_loan" ] } }, "targets": [ { "refId": "A", "datasource": { "type": "grafana-postgresql-datasource", "uid": "payslips-pg" }, "rawQuery": true, "editorMode": "code", "format": "table", "rawSql": "SELECT pay_date, employer, tax_year, gross_pay, (gross_pay - rsu_vest) AS cash_gross, salary, bonus, rsu_vest, rsu_offset, pension_sacrifice, taxable_pay, income_tax, national_insurance, pension_employee, pension_employer, student_loan, COALESCE(other_deductions, '{}'::jsonb) AS other_deductions, net_pay, validated, paperless_doc_id FROM payslip_ingest.payslip WHERE $__timeFilter(pay_date) ORDER BY pay_date DESC" } ] }, { "id": 6, "title": "YTD reconciliation \u2014 reported vs computed", "description": "Reconciles each payslip's reported YTD summary block against the cumulative sum of extracted per-payslip values within the same tax year. Any \u0394 > \u00a30.02 is a parser regression, a missing slip, or a duplicate ingest. Variant A payslips (pre-mid-2022) have no YTD block and are excluded.", "type": "table", "datasource": { "type": "grafana-postgresql-datasource", "uid": "payslips-pg" }, "gridPos": { "h": 12, "w": 24, "x": 0, "y": 58 }, "fieldConfig": { "defaults": { "custom": { "align": "right", "cellOptions": { "type": "auto" }, "inspect": false }, "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null } ] } }, "overrides": [ { "matcher": { "id": "byName", "options": "pay_date" }, "properties": [ { "id": "custom.width", "value": 110 }, { "id": "custom.align", "value": "left" } ] }, { "matcher": { "id": "byName", "options": "tax_year" }, "properties": [ { "id": "custom.width", "value": 80 } ] }, { "matcher": { "id": "byName", "options": "gross_pay" }, "properties": [ { "id": "unit", "value": "currencyGBP" } ] }, { "matcher": { "id": "byName", "options": "ytd_gross_reported" }, "properties": [ { "id": "unit", "value": "currencyGBP" } ] }, { "matcher": { "id": "byName", "options": "ytd_gross_computed" }, "properties": [ { "id": "unit", "value": "currencyGBP" } ] }, { "matcher": { "id": "byName", "options": "delta_gross" }, "properties": [ { "id": "unit", "value": "currencyGBP" }, { "id": "custom.cellOptions", "value": { "type": "color-background", "mode": "basic" } }, { "id": "thresholds", "value": { "mode": "absolute", "steps": [ { "color": "red", "value": null }, { "color": "green", "value": -0.02 }, { "color": "red", "value": 0.02 } ] } } ] }, { "matcher": { "id": "byName", "options": "taxable_pay" }, "properties": [ { "id": "unit", "value": "currencyGBP" } ] }, { "matcher": { "id": "byName", "options": "ytd_taxable_reported" }, "properties": [ { "id": "unit", "value": "currencyGBP" } ] }, { "matcher": { "id": "byName", "options": "ytd_taxable_computed" }, "properties": [ { "id": "unit", "value": "currencyGBP" } ] }, { "matcher": { "id": "byName", "options": "delta_taxable" }, "properties": [ { "id": "unit", "value": "currencyGBP" }, { "id": "custom.cellOptions", "value": { "type": "color-background", "mode": "basic" } }, { "id": "thresholds", "value": { "mode": "absolute", "steps": [ { "color": "red", "value": null }, { "color": "green", "value": -0.02 }, { "color": "red", "value": 0.02 } ] } } ] }, { "matcher": { "id": "byName", "options": "income_tax" }, "properties": [ { "id": "unit", "value": "currencyGBP" } ] }, { "matcher": { "id": "byName", "options": "ytd_tax_reported" }, "properties": [ { "id": "unit", "value": "currencyGBP" } ] }, { "matcher": { "id": "byName", "options": "ytd_tax_computed" }, "properties": [ { "id": "unit", "value": "currencyGBP" } ] }, { "matcher": { "id": "byName", "options": "delta_tax" }, "properties": [ { "id": "unit", "value": "currencyGBP" }, { "id": "custom.cellOptions", "value": { "type": "color-background", "mode": "basic" } }, { "id": "thresholds", "value": { "mode": "absolute", "steps": [ { "color": "red", "value": null }, { "color": "green", "value": -0.02 }, { "color": "red", "value": 0.02 } ] } } ] } ] }, "options": { "cellHeight": "sm", "footer": { "show": false } }, "targets": [ { "refId": "A", "datasource": { "type": "grafana-postgresql-datasource", "uid": "payslips-pg" }, "rawQuery": true, "editorMode": "code", "format": "table", "rawSql": "SELECT pay_date, tax_year, gross_pay, ytd_gross AS ytd_gross_reported, SUM(gross_pay) OVER w AS ytd_gross_computed, (ytd_gross - SUM(gross_pay) OVER w) AS delta_gross, taxable_pay, ytd_taxable_pay AS ytd_taxable_reported, SUM(taxable_pay) OVER w AS ytd_taxable_computed, (ytd_taxable_pay - SUM(taxable_pay) OVER w) AS delta_taxable, income_tax, ytd_tax_paid AS ytd_tax_reported, SUM(income_tax) OVER w AS ytd_tax_computed, (ytd_tax_paid - SUM(income_tax) OVER w) AS delta_tax FROM payslip_ingest.payslip WHERE $__timeFilter(pay_date) AND ytd_gross IS NOT NULL WINDOW w AS (PARTITION BY tax_year ORDER BY pay_date) ORDER BY pay_date DESC" } ] }, { "id": 8, "title": "Sankey \u2014 where the money went", "description": "Income sources flow into gross, then out to deductions and take-home. Aggregated over the selected time range (use the time picker to scope to a single tax year).", "type": "netsage-sankey-panel", "datasource": { "type": "grafana-postgresql-datasource", "uid": "payslips-pg" }, "gridPos": { "h": 14, "w": 24, "x": 0, "y": 70 }, "options": { "monochrome": false, "monochromeColor": "#7294d4", "showHeader": true, "displayValues": "show", "valueFormat": "currencyGBP", "nodeWidth": 20, "nodePadding": 15 }, "targets": [ { "refId": "A", "datasource": { "type": "grafana-postgresql-datasource", "uid": "payslips-pg" }, "rawQuery": true, "editorMode": "code", "format": "table", "rawSql": "WITH agg AS (SELECT COALESCE(SUM(salary), 0) AS salary, COALESCE(SUM(bonus), 0) AS bonus, COALESCE(SUM(rsu_vest), 0) AS rsu_vest, COALESCE(SUM(GREATEST(gross_pay - salary - bonus - rsu_vest, 0)), 0) AS other_income, COALESCE(SUM(net_pay), 0) AS net_pay, COALESCE(SUM(COALESCE(cash_income_tax, income_tax)), 0) AS cash_income_tax, COALESCE(SUM(income_tax - COALESCE(cash_income_tax, income_tax)), 0) AS rsu_income_tax, COALESCE(SUM(national_insurance), 0) AS ni, COALESCE(SUM(pension_employee), 0) AS pension, COALESCE(SUM(student_loan), 0) AS student_loan, COALESCE(SUM(rsu_offset), 0) AS rsu_offset FROM payslip_ingest.payslip WHERE $__timeFilter(pay_date)) SELECT 'Salary' AS source, 'Gross' AS target, salary AS value FROM agg WHERE salary > 0 UNION ALL SELECT 'Bonus', 'Gross', bonus FROM agg WHERE bonus > 0 UNION ALL SELECT 'RSU', 'Gross', rsu_vest FROM agg WHERE rsu_vest > 0 UNION ALL SELECT 'Other income', 'Gross', other_income FROM agg WHERE other_income > 0 UNION ALL SELECT 'Gross', 'Net pay', net_pay FROM agg WHERE net_pay > 0 UNION ALL SELECT 'Gross', 'Income Tax (cash)', cash_income_tax FROM agg WHERE cash_income_tax > 0 UNION ALL SELECT 'Gross', 'Income Tax (RSU)', rsu_income_tax FROM agg WHERE rsu_income_tax > 0 UNION ALL SELECT 'Gross', 'National Insurance', ni FROM agg WHERE ni > 0 UNION ALL SELECT 'Gross', 'Pension', pension FROM agg WHERE pension > 0 UNION ALL SELECT 'Gross', 'Student Loan', student_loan FROM agg WHERE student_loan > 0 UNION ALL SELECT 'Gross', 'RSU Offset', rsu_offset FROM agg WHERE rsu_offset > 0" } ] }, { "id": 9, "title": "P60 reconciliation \u2014 HMRC annual vs summed payslips", "description": "Per-tax-year comparison of the figures HMRC printed on the P60 vs what we summed from individual payslips. Delta columns: |\u0394|<1 green (exact match), 1-50 yellow (rounding), >50 red (missing month or parser drift). Always shows all years \u2014 ignores the time picker.", "type": "table", "datasource": { "type": "grafana-postgresql-datasource", "uid": "payslips-pg" }, "gridPos": { "h": 10, "w": 24, "x": 0, "y": 84 }, "fieldConfig": { "defaults": { "unit": "currencyGBP", "custom": { "align": "right", "displayMode": "auto" } }, "overrides": [ { "matcher": { "id": "byRegexp", "options": "^delta_" }, "properties": [ { "id": "custom.displayMode", "value": "color-background" }, { "id": "custom.cellOptions", "value": { "type": "color-background", "mode": "gradient" } }, { "id": "thresholds", "value": { "mode": "absolute", "steps": [ { "color": "green", "value": null }, { "color": "green", "value": -1 }, { "color": "yellow", "value": 1 }, { "color": "red", "value": 50 }, { "color": "red", "value": -50 } ] } } ] }, { "matcher": { "id": "byName", "options": "tax_year" }, "properties": [ { "id": "unit", "value": "string" }, { "id": "custom.align", "value": "left" } ] }, { "matcher": { "id": "byName", "options": "employer" }, "properties": [ { "id": "unit", "value": "string" }, { "id": "custom.align", "value": "left" } ] }, { "matcher": { "id": "byName", "options": "tax_code" }, "properties": [ { "id": "unit", "value": "string" }, { "id": "custom.align", "value": "left" } ] }, { "matcher": { "id": "byName", "options": "paperless_doc_id" }, "properties": [ { "id": "unit", "value": "none" }, { "id": "custom.align", "value": "left" } ] } ] }, "options": { "showHeader": true, "cellHeight": "sm", "footer": { "show": false } }, "targets": [ { "refId": "A", "datasource": { "type": "grafana-postgresql-datasource", "uid": "payslips-pg" }, "rawQuery": true, "editorMode": "code", "format": "table", "rawSql": "SELECT p.tax_year, p.employer, p.gross_pay AS p60_gross, c.sum_gross AS computed_gross, (p.gross_pay - c.sum_gross) AS delta_gross, p.income_tax AS p60_tax, c.sum_tax AS computed_tax, (p.income_tax - c.sum_tax) AS delta_tax, p.national_insurance AS p60_ni, c.sum_ni AS computed_ni, (p.national_insurance - c.sum_ni) AS delta_ni, p.tax_code, p.paperless_doc_id FROM payslip_ingest.p60_reference p LEFT JOIN LATERAL (SELECT COALESCE(SUM(gross_pay), 0) AS sum_gross, COALESCE(SUM(income_tax), 0) AS sum_tax, COALESCE(SUM(national_insurance), 0) AS sum_ni FROM payslip_ingest.payslip WHERE tax_year = p.tax_year) c ON true ORDER BY p.tax_year DESC" } ] }, { "id": 10, "title": "HMRC Tax Year Reconciliation \u2014 Individual Tax API", "description": "Latest snapshot from HMRC Individual Tax API v1.1 vs SUM(payslip.income_tax) per tax year. Delta > \u00a310 turns red \u2014 that's parser drift vs HMRC's held figures, the authoritative ground truth. Shown only for years where hmrc-sync has pulled a snapshot.", "type": "table", "datasource": { "type": "grafana-postgresql-datasource", "uid": "payslips-pg" }, "gridPos": { "h": 10, "w": 24, "x": 0, "y": 94 }, "fieldConfig": { "defaults": { "unit": "currencyGBP", "custom": { "align": "right", "displayMode": "auto" } }, "overrides": [ { "matcher": { "id": "byRegexp", "options": "^delta_" }, "properties": [ { "id": "custom.displayMode", "value": "color-background" }, { "id": "thresholds", "value": { "mode": "absolute", "steps": [ { "color": "green", "value": null }, { "color": "green", "value": -10 }, { "color": "red", "value": 10 }, { "color": "red", "value": -10 } ] } } ] }, { "matcher": { "id": "byName", "options": "tax_year" }, "properties": [ { "id": "unit", "value": "string" }, { "id": "custom.align", "value": "left" } ] }, { "matcher": { "id": "byName", "options": "employer_paye_ref" }, "properties": [ { "id": "unit", "value": "string" }, { "id": "custom.align", "value": "left" } ] }, { "matcher": { "id": "byName", "options": "snapshot_date" }, "properties": [ { "id": "unit", "value": "dateTimeAsIso" } ] } ] }, "options": { "showHeader": true, "cellHeight": "sm", "footer": { "show": false } }, "targets": [ { "refId": "A", "datasource": { "type": "grafana-postgresql-datasource", "uid": "payslips-pg" }, "rawQuery": true, "editorMode": "code", "format": "table", "rawSql": "WITH latest AS (SELECT DISTINCT ON (tax_year, employer_paye_ref) tax_year, employer_paye_ref, snapshot_date, gross_pay, income_tax, ni_contributions FROM hmrc_sync.tax_year_snapshot ORDER BY tax_year, employer_paye_ref, snapshot_date DESC), summed AS (SELECT tax_year, COALESCE(SUM(gross_pay), 0) AS sum_gross, COALESCE(SUM(income_tax), 0) AS sum_tax, COALESCE(SUM(national_insurance), 0) AS sum_ni FROM payslip_ingest.payslip GROUP BY tax_year) SELECT l.tax_year, l.employer_paye_ref, l.snapshot_date, l.gross_pay AS hmrc_gross, s.sum_gross AS computed_gross, (l.gross_pay - s.sum_gross) AS delta_gross, l.income_tax AS hmrc_tax, s.sum_tax AS computed_tax, (l.income_tax - s.sum_tax) AS delta_tax, l.ni_contributions AS hmrc_ni, s.sum_ni AS computed_ni, (l.ni_contributions - s.sum_ni) AS delta_ni FROM latest l LEFT JOIN summed s ON s.tax_year = l.tax_year ORDER BY l.tax_year DESC" } ] }, { "id": 14, "title": "Meta payroll: bank deposit vs payslip net pay", "description": "Cross-check between ActualBudget bank deposits (META/FACEBOOK payee) and payslip net_pay. |delta| > \u00a350 flags likely parser or bank-sync drift. Synced daily 02:00 UTC.", "type": "timeseries", "datasource": { "type": "grafana-postgresql-datasource", "uid": "payslips-pg" }, "gridPos": { "h": 9, "w": 24, "x": 0, "y": 160 }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "unit": "currencyGBP", "custom": { "axisPlacement": "auto", "drawStyle": "line", "fillOpacity": 0, "lineWidth": 2, "pointSize": 6, "showPoints": "always", "spanNulls": false, "thresholdsStyle": { "mode": "off" } } }, "overrides": [ { "matcher": { "id": "byName", "options": "deposit_sum" }, "properties": [ { "id": "color", "value": { "mode": "fixed", "fixedColor": "green" } }, { "id": "displayName", "value": "Bank deposit (ActualBudget)" } ] }, { "matcher": { "id": "byName", "options": "payslip_net_pay" }, "properties": [ { "id": "color", "value": { "mode": "fixed", "fixedColor": "blue" } }, { "id": "displayName", "value": "Payslip net pay" } ] }, { "matcher": { "id": "byName", "options": "delta" }, "properties": [ { "id": "color", "value": { "mode": "fixed", "fixedColor": "red" } }, { "id": "displayName", "value": "Delta (deposit \u2212 payslip)" }, { "id": "custom.drawStyle", "value": "bars" } ] } ] }, "options": { "legend": { "calcs": [ "last" ], "displayMode": "table", "placement": "bottom" }, "tooltip": { "mode": "multi", "sort": "desc" } }, "targets": [ { "datasource": { "type": "grafana-postgresql-datasource", "uid": "payslips-pg" }, "rawSql": "WITH deposits AS (SELECT DATE_TRUNC('month', deposit_date)::date AS month_start, SUM(amount) AS deposit_sum FROM payslip_ingest.external_meta_deposits GROUP BY 1), payslip_net AS (SELECT DATE_TRUNC('month', pay_date)::date AS month_start, SUM(net_pay) AS payslip_net_pay FROM payslip_ingest.payslip GROUP BY 1) SELECT COALESCE(p.month_start, d.month_start) AS \"time\", d.deposit_sum, p.payslip_net_pay, COALESCE(d.deposit_sum, 0) - COALESCE(p.payslip_net_pay, 0) AS delta FROM deposits d FULL OUTER JOIN payslip_net p ON p.month_start = d.month_start WHERE $__timeFilter(COALESCE(p.month_start, d.month_start)) ORDER BY \"time\"", "format": "time_series", "refId": "A", "rawQuery": true, "editorMode": "code" } ] }, { "id": 15, "title": "RSU vest reconciliation \u2014 payslip vs Schwab", "description": "Per-vest-month join between payslip.rsu_vest (what HMRC reporting shows) and Schwab's vest-confirmation email data (what actually happened at the broker). Empty rows until the broker-sync IMAP ingest runs and VestEvents are persisted.", "type": "table", "datasource": { "type": "grafana-postgresql-datasource", "uid": "payslips-pg" }, "gridPos": { "h": 10, "w": 24, "x": 0, "y": 169 }, "fieldConfig": { "defaults": { "custom": { "align": "right", "cellOptions": { "type": "auto" }, "inspect": false }, "unit": "currencyGBP", "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null } ] } }, "overrides": [ { "matcher": { "id": "byName", "options": "vest_month" }, "properties": [ { "id": "custom.width", "value": 140 }, { "id": "custom.align", "value": "left" }, { "id": "unit", "value": "none" } ] }, { "matcher": { "id": "byName", "options": "ticker" }, "properties": [ { "id": "custom.width", "value": 80 }, { "id": "unit", "value": "none" } ] }, { "matcher": { "id": "byName", "options": "shares_vested" }, "properties": [ { "id": "unit", "value": "none" } ] }, { "matcher": { "id": "byName", "options": "tax_delta_pct" }, "properties": [ { "id": "unit", "value": "percent" }, { "id": "custom.cellOptions", "value": { "type": "color-background", "mode": "basic" } }, { "id": "thresholds", "value": { "mode": "absolute", "steps": [ { "color": "green", "value": null }, { "color": "orange", "value": 2 }, { "color": "red", "value": 5 } ] } } ] } ] }, "options": { "cellHeight": "sm", "footer": { "show": false } }, "targets": [ { "refId": "A", "datasource": { "type": "grafana-postgresql-datasource", "uid": "payslips-pg" }, "rawQuery": true, "editorMode": "code", "format": "table", "rawSql": "WITH vest_by_month AS (SELECT DATE_TRUNC('month', vest_date)::date AS vest_month, ticker, SUM(shares_vested) AS shares_vested, SUM(gross_value_gbp) AS broker_gross_gbp, SUM(tax_withheld_gbp) AS broker_tax_gbp FROM payslip_ingest.rsu_vest_events GROUP BY 1, 2), payslip_by_month AS (SELECT DATE_TRUNC('month', pay_date)::date AS vest_month, SUM(rsu_vest) AS payslip_rsu_gbp, SUM(income_tax - COALESCE(cash_income_tax, income_tax)) AS payslip_rsu_tax_gbp FROM payslip_ingest.payslip WHERE rsu_vest > 0 GROUP BY 1) SELECT COALESCE(v.vest_month, p.vest_month) AS vest_month, v.ticker, v.shares_vested, v.broker_gross_gbp, p.payslip_rsu_gbp, (p.payslip_rsu_gbp - v.broker_gross_gbp) AS gross_delta_gbp, v.broker_tax_gbp, p.payslip_rsu_tax_gbp, (p.payslip_rsu_tax_gbp - v.broker_tax_gbp) AS tax_delta_gbp, CASE WHEN v.broker_tax_gbp IS NULL OR v.broker_tax_gbp = 0 THEN NULL ELSE ABS(p.payslip_rsu_tax_gbp - v.broker_tax_gbp) * 100.0 / v.broker_tax_gbp END AS tax_delta_pct FROM vest_by_month v FULL OUTER JOIN payslip_by_month p ON p.vest_month = v.vest_month ORDER BY vest_month DESC" } ] } ], "refresh": "5m", "schemaVersion": 39, "tags": [ "finance", "personal", "uk-tax" ], "templating": { "list": [] }, "time": { "from": "now-10y", "to": "now" }, "timepicker": {}, "timezone": "browser", "title": "UK Payslip", "uid": "uk-payslip", "version": 1 }