Personnel

Staff, timesheets and shifts. Part of the business vertical. Legend: βœ… done Β· 🟑 in progress Β· ⬜ pending.

Checklist

βœ… Timesheets

Staff, timesheets and shifts.

βœ… Staff records

Roster CRUD plus an append-only effective-dated pay history over /v1/staff/:id/compensation β€” a raise is a new row.

βœ… Shifts

Shift-type vocabulary and staff-on-date assignments; the joined roster read renders a week grid from one call, and double-booking is a DB refusal (409).

βœ… Time tracking

Kiosk punch clock over /v1/time-tracking β€” clock-in guarded against a double punch (409), a break toggle that accumulates closed breaks, approval stamps and ranged reads.

βœ… Leave

Leave balance is always SUM(delta_days) over a signed ledger; approval writes the negative row in the request-flip transaction, and day counts exclude weekends and public holidays.

βœ… Overtime

Computed from approved time entries against the day's shift window (worked βˆ’ scheduled βˆ’ grace) with upsert on recompute; manual entries and approve/reject.

βœ… Payroll

Runs over /v1/payroll calculate deterministically from compensation + approved time/overtime/paid leave; posting writes a balanced Salary Expense / Wages Payable journal entry in the status-flip transaction, mark-paid moves Wages Payable to Cash.

Evidence

Personnel is real: staff_members + staff_compensation (V08_03) behind /v1/staff and shift_types + shift_assignments (V08_04) behind /v1/shifts, company-scoped. Live-proven on the dev catalog β€” two staff created (one salaried, one hourly with a raise as a new effective-dated row, never a mutation), Morning and Evening shift types, six assignments across three days read back as a joined roster, completed/absent transitions applied, and a duplicate assignment refused by the DB unique as 409; a foreign company sees nothing. Rows verified in Postgres. The full-HR wave is live too: time_entries (V08_05) behind /v1/time-tracking β€” kiosk clock-in/out with a double clock-in refused as 409 and a break toggle β€” leave_types + leave_requests + leave_ledger + public_holidays (V08_06) behind /v1/leave and /v1/holidays, and overtime_entries (V08_07) behind /v1/overtime. Proven end to end: a 9h entry against a Morning assignment computed 55 overtime minutes (8h shift, 5min grace) and recomputes upsert; a Vacation allowance of 15 allocated, a Thu-to-Tue request counted 3 days because the weekend and a created Friday holiday are excluded, and approval wrote the -3 ledger row in the same transaction, leaving balance 12 as SUM(delta_days). And the epic closes with Payroll: payroll_runs + payroll_run_lines (V08_08) behind /v1/payroll, runs that calculate deterministically from compensation plus approved time, overtime and paid leave, and post REAL balanced journal entries to the finance ledger. Proven live: the 2026-08 run paid the salaried monthly rate plus a 3-day vacation at monthly/21.67 and the hourly 9h plus 55 overtime minutes at 1.5x, a 10.67% CCSS rule withheld per line, and posting wrote journal entry Salary Expense 288,760,060 = Wages Payable 257,949,362 + Tax Payable 30,810,698 β€” debits equal credits in journal_entry_lines, a re-post was refused with exactly one entry under the idempotency key, and mark-paid posted the second balanced entry moving Wages Payable to Cash. Timesheets stays on the hospitality time-clock registration; staff, shifts, time-tracking, leave, overtime and payroll are commerce registrations.