fix(time-tracking): accept irrigator/driver roles in PIN verify
Deploy to route.crispygoat.com / deploy (push) Successful in 4m35s

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.
This commit is contained in:
Nora
2026-07-04 01:04:05 -06:00
parent 3db642e76b
commit e98bbc220f
+19 -3
View File
@@ -94,8 +94,24 @@ export async function verifyTimeTrackingPin(
// a small worker pool this is fine; if it grows we could index a // a small worker pool this is fine; if it grows we could index a
// lookup table, but per-brand counts are typically <100. // lookup table, but per-brand counts are typically <100.
// Cycle 10: workers now live in the unified `field_workers` table. // 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 // We accept anyone with a clocking-in role: 'worker' (default),
// accidentally be matched against a time-tracking PIN. // '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) => { const rows = await withBrand(brandId, async (db) => {
return db return db
.select() .select()
@@ -104,7 +120,7 @@ export async function verifyTimeTrackingPin(
and( and(
eq(fieldWorkers.brandId, brandId), eq(fieldWorkers.brandId, brandId),
eq(fieldWorkers.active, true), eq(fieldWorkers.active, true),
sql`${fieldWorkers.role} IN ('worker', 'time_admin')`, sql`${fieldWorkers.role} IN ('worker', 'time_admin', 'irrigator', 'driver')`,
), ),
); );
}); });