From e98bbc220f0e1348a4ef4191f716e1a6796b1917 Mon Sep 17 00:00:00 2001 From: Nora Date: Sat, 4 Jul 2026 01:04:05 -0600 Subject: [PATCH] fix(time-tracking): accept irrigator/driver roles in PIN verify MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The /water unified entrypoint (MobileWaterApp) calls verifyTimeTrackingPin best-effort right after verifyWaterPin. If the role filter inside verifyTimeTrackingPin only accepted 'worker' and 'time_admin', then: 1. Worker enters PIN at /water 2. verifyWaterPin succeeds → wl_session cookie set 3. verifyTimeTrackingPin silently no-ops (role mismatch) → no time_tracking_session cookie 4. User taps the Time tab → TimeTrackingFieldClient mounts → init() finds no time-tracking session → dispatches 'pin' screen 5. User re-enters the same PIN → verifyTimeTrackingPin rejects it again with 'Invalid PIN' The unified-PIN flow depended on the same PIN being accepted by both verifiers, which only works if the role filter here matches every clocking-in role. Expanding to ('worker', 'time_admin', 'irrigator', 'driver') covers all field crew including the legacy 'irrigator' role (cycle 10 unified water+time workers but kept the role label) and the cycle 12 'driver' role. 'water_admin' and 'supervisor' stay excluded — they're admins who manage / approve, not clocking-in field workers. --- src/actions/time-tracking/field.ts | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/actions/time-tracking/field.ts b/src/actions/time-tracking/field.ts index 35b57af..123608f 100644 --- a/src/actions/time-tracking/field.ts +++ b/src/actions/time-tracking/field.ts @@ -94,8 +94,24 @@ export async function verifyTimeTrackingPin( // a small worker pool this is fine; if it grows we could index a // lookup table, but per-brand counts are typically <100. // Cycle 10: workers now live in the unified `field_workers` table. - // We filter to time-domain roles here so a water-only worker can't - // accidentally be matched against a time-tracking PIN. + // We accept anyone with a clocking-in role: 'worker' (default), + // 'time_admin' (manages tasks + workers), 'irrigator' (legacy role + // for water workers, can also clock in/out — these are field crew + // who do both jobs), and 'driver' (cycle 12 driver/crew role). + // + // Excluded on purpose: + // - 'water_admin' — admin who manages headgates/workers, not a + // clocking-in field worker + // - 'supervisor' — cycle 12 approver role; doesn't clock in/out, + // only reviews and approves timesheets + // + // This also matters because MobileWaterApp (the /water unified + // entrypoint) calls verifyTimeTrackingPin best-effort right after + // verifyWaterPin — if the role filter here is too narrow, the + // worker enters their PIN once, the Time tab mounts, asks for the + // PIN again, and the second attempt fails the same way. Allowing + // the broader set keeps the unified PIN flow working for everyone + // who's registered as a field crew member. const rows = await withBrand(brandId, async (db) => { return db .select() @@ -104,7 +120,7 @@ export async function verifyTimeTrackingPin( and( eq(fieldWorkers.brandId, brandId), eq(fieldWorkers.active, true), - sql`${fieldWorkers.role} IN ('worker', 'time_admin')`, + sql`${fieldWorkers.role} IN ('worker', 'time_admin', 'irrigator', 'driver')`, ), ); });