93bcbc29a0
Replaces the in-progress stub (RPC + brandId params) with Drizzle + the shared SECURITY DEFINER wrappers in db/client. - field.ts: cookie session, scrypt PIN, clock-in/out, getOpenClockIn, getWorkerPayPeriodHours. Cookie payload now 7 fields; parseSessionCookie re-resolves name/role/lang/brand_id from DB on every read so a stale or tampered cookie cannot impersonate a different brand. - index.ts: admin CRUD for workers / tasks / settings / logs / notifications + getWorkerPeriodTotals. All admin actions drop the brandId param; the helpers pull it from the admin session via getAdminUser(). - notifications.ts: overtime check inserts with status='pending' (not optimistic 'sent') since email/SMS dispatch is out of scope until the notification_targets columns land in a follow-up migration. - export/route: cast fix for the changed TimeLog shape. DB: - 0094 partial unique index on time_tracking_logs (worker_id) WHERE clock_out IS NULL — enforces one open clock-in per worker against concurrent / retried requests (READ COMMITTED cannot).
24 lines
1.2 KiB
SQL
24 lines
1.2 KiB
SQL
-- ============================================================================
|
|
-- 0094_time_tracking_one_open_clock_in.sql
|
|
--
|
|
-- Cycle 2 of the water-log/time-tracking refactor. A field worker must not
|
|
-- have two open clock-ins at once — concurrent requests, offline retries,
|
|
-- and double-taps on a slow phone all create the same hazard. A partial
|
|
-- unique index on (worker_id) WHERE clock_out IS NULL is the only DB-level
|
|
-- guard, since READ COMMITTED transactions can both pass an
|
|
-- "is there an open log?" SELECT before either INSERT commits.
|
|
--
|
|
-- The application catches the unique violation and returns
|
|
-- "Already clocked in" to the worker. The `clockOutWorker` action picks
|
|
-- the most-recent open log (ORDER BY clock_in DESC LIMIT 1) — that
|
|
-- behavior is unchanged.
|
|
-- ============================================================================
|
|
|
|
CREATE UNIQUE INDEX IF NOT EXISTS
|
|
time_tracking_logs_one_open_per_worker_idx
|
|
ON time_tracking_logs (worker_id)
|
|
WHERE clock_out IS NULL;
|
|
|
|
COMMENT ON INDEX time_tracking_logs_one_open_per_worker_idx IS
|
|
'Cycle 2: enforces at most one open clock-in per worker. Catches concurrent / retry race in field app.';
|