fix(time-tracking): accept irrigator/driver roles in PIN verify
Deploy to route.crispygoat.com / deploy (push) Successful in 4m35s
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:
@@ -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')`,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user