mega-plant/docs/plant-clock.md
David df53130861 Add Workforce pages, Plant Clock tab bar and oil & diesel book-out
- Workforce: Employees register, per-employee dashboards, Attendance table
- Plant Clock: scan-only clock, bottom tab bar (Employees / Oil & Diesel / Job cards)
- Oil & Diesel stock: diesel tank (650 L, opening 589 L), level overwrite dialog,
  QR on each card, book-out screen with searchable vehicle picker,
  filterable "Booked out" table (today/week/month/all/range)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 11:39:18 +02:00

5.2 KiB
Raw Blame History

Employee clock

The Plant is not a site. Mega hires people out from the Plant to sites, and sites run their own clock-in (the Infrastructure app). Plant Clock records only when Plant staff — mechanics, operators and everyone else on the Plant roster — start and stop working at the Plant. Client ruling, 2026-10-22: it must be "just the scanning on the main screen".

So /clock opens straight into the camera. There is no site picker, no confirmation step and no GPS. Scan a badge and the scan is saved; a banner shows the name, IN or OUT, and the time, then the scanner is ready for the next person. Typing the employee code is the fallback when there is no camera.

  • The server decides IN or OUT (clockAction): the first scan of an Africa/Johannesburg day is IN, the next is OUT, the next IN again (lunch out and back works). An IN left open past midnight stays a missing clock-out for office review; it never becomes the next day's OUT.
  • A repeat within 60 seconds is refused, not saved (REPEAT_SCAN_MS). The camera reads a held badge several times a second, and without a confirm tap this is what stops one person flipping IN/OUT/IN. The screen says who and when the scan that counts was. The client also ignores the same QR for six seconds after any result.
  • A failed network save offers "Retry this scan", which reuses the original request ID, so a retry can never save twice.
  • clock_events.site is optional and left empty on Plant scans. It was required in the first build; setup-clock.mjs --apply relaxes it.

Existing operators and mechanics are the roster. Plant badges encode NAME | EMPLOYEE CODE; the scanner also accepts Infrastructure's code-only badges. Both resolve by employee code, and the saved name is the current roster name. Workforce → Employees & attendance (/employees) shows who is in, missing clock-outs, recent scans and the QR badges. Session duration is elapsed time between IN and OUT, rounded down to whole minutes. It is not payroll time.

Setup and validation

Run with the existing Plant environment:

node --env-file=.env.local scripts/setup-clock.mjs
node --env-file=.env.local scripts/setup-clock.mjs --apply
npm test
npm run typecheck
npm run build
# Start the local server before the following integration check:
node --env-file=.env.local scripts/verify-clock.mjs

Setup is additive and idempotent. It creates only private clock_events and clock_scan_locks, and reports incompatible schema drift rather than rewriting it. No seed is required. The existing operators, mechanics, users, sites and financial records are untouched. Any authenticated, active Plant user who has completed password onboarding can operate this first phase, matching Plant's current account policy. Direct PocketBase attendance access is superuser-only; app APIs enforce user authentication and same-origin writes.

The integration check creates one temporary mechanic, scans it IN, retries, checks the repeat refusal and an unknown badge, back-dates the IN and scans it OUT. It and removes only its own events, locks and employee in finally. It uses a short-lived impersonated session for an existing onboarded user without changing that user's password or records.

Cross-process locks are unique per worker. An interrupted scan lock can be reclaimed after two minutes. Ownership is the lock's record ID: releasing an old lock cannot delete a new one. A unique (employee, previous_event) index prevents a stale writer from branching the event history even if its lease expires during an outage. If PocketBase is unavailable, scans are not queued or shown as saved. Retry the same confirmation once service returns.

Raw events preserve the employee code/name at the time of scan, server time, the signed-in account that scanned and the request signature. Vehicle and oil job logging, photo capture and payroll integration are deferred.

Separate mobile app

Plant Clock has its own manifest (/clock.webmanifest), application ID (/clock), icons and standalone display mode. Install from /clock or /clock/login on the HTTPS Plant host. It opens directly into the employee scanner with its own compact screen, without the dashboard navigation. The dashboard keeps its existing separate manifest.

On Android Chrome/Edge, use Install Plant Clock when offered or the browser’s Install app menu. On iPhone/iPad Safari, use Share → Add to Home Screen. Sign-in and first-password setup stay within /clock, and successful sign-in returns to the scanner. The app still needs a network connection to save attendance; it never claims that an offline scan has been saved. Camera and PWA installation need HTTPS on a phone; localhost is only a development exception.

Forgejo integration (2026-10-21)

Local main was fast-forwarded from 2594dea to Forgejo b0c70e9 (13 commits). The recovery branch is codex/plant-before-forgejo-merge; the pre-merge clock work is also retained in the recovery stash. Upstream invoice, running-cost, operator, mechanic and oil features remain in place. The only QR package added beyond Forgejo is jsqr; the shared QrCode, qrMatrix, mechanic payload builder and vector PDF builder are reused. No seed or financial data migration was run.