feat(dimensions): PR8 salary — employees.default_dimensions, per-employee cost lines, aggregation re-key (#869)

Employees carry a default dimensions bag and the salary booking puts each
employee's cost on their kostnadsställe/projekt
(dev_docs/dimensions_implementation_plan.md PR8):

- employees.default_dimensions (migration 20260702220000; jsonb DEFAULT
  '{}' + object CHECK)
- salary-entries: the one-line-per-account aggregation is re-keyed to
  account+bag — P&L cost lines (löner incl. line items + base remainder,
  arbetsgivaravgifter, semesteravsättning + dess avgifter, pension, SLP)
  split per employee bag while every balance-sheet/settlement leg (2710,
  1930, 2731, 29xx, 2740, 2514) stays aggregated; liability credits equal
  the sum of the rounded debit buckets so entries balance by construction;
  dimension-less runs book byte-identically to before. Replaces the dead
  SalaryRunEmployee.cost_center/project pair (never wired)
- both book routes (dashboard + v1) read the bag via the employees join —
  read-at-book, so the run review shows exactly what will book
- employee form (new + edit) gets a gated Kostnadsställe/Projekt card;
  run review shows per-employee dims chips; run GET + v1 employee
  routes/schemas + MCP list_employees carry the field
- pre-merge audit: all salary reports (salary-journal, AGI,
  avgifter-basis, vacation-liability) read salary_run_employees — not
  journal lines — and every ledger consumer sums per account, so the
  line split breaks nothing; SIE export + dimension P&L pick the split
  up as intended

8 new engine propagation tests + book-route dims flow test.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Jakob Wennberg
2026-07-02 22:21:46 +02:00
committed by GitHub
co-authored by Claude Fable 5
parent 755e0f7e47
commit 163fbd8222
16 changed files with 604 additions and 51 deletions
+3
View File
@@ -1682,6 +1682,9 @@ const EmployeeSchemaBase = z.object({
vaxa_stod_eligible: z.boolean().default(false),
vaxa_stod_start: isoDate.optional(),
vaxa_stod_end: isoDate.optional(),
// Dimensions PR8: bag applied to the employee's P&L cost lines when a
// salary run is booked. {} clears (the UI always sends the field).
default_dimensions: DimensionsBagSchema.optional(),
})
export const CreateEmployeeSchema = EmployeeSchemaBase.superRefine((data, ctx) => {