Kiosk work across this session (19.0.3.6.0 -> 19.0.3.10.0): - Program-from-unknown-tap: amber prompt -> Manager PIN -> pick/create employee -> binds the captured UID (no re-tap). Reassign moves a card between employees. - Manager page (gear, when unlocked): search employees + tag status; assign/re-tag, clear tag, archive employee, + new employee. Server-gated by the enroll password. - Screen lock: kiosk starts locked (tap-only); Unlock -> Manager PIN, Lock button; PIN remembered for the session so the gear never re-prompts. - Sounds: pleasant + loud sine chimes (rising in / descending out) + a low "denied" tone for wrong/unknown taps. Gated by fusion_clock.enable_sounds. - Guided profile-photo capture for employees with no picture (clock-in or enroll): live camera + oval face guide -> capture -> preview -> save to hr.employee. - PIN no longer re-renders per digit; centered result card; 12h time; clock-out shows "Worked Xh Ym this shift"; modern clock idle icon; faster animations/result timers; session keep-alive so the kiosk login never expires. - New endpoints: create_employee, clear_tag, delete_employee (archive), verify_pin, save_profile_photo; enroll gains force-reassign. - Docs: fusion_clock is now developed in Claude Code (dropped Cursor references). Spec/plan under fusion_clock/docs/superpowers/. Deployed live on entech (odoo-entech / LXC 111 on pve-worker5), v19.0.3.10.0. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
5.0 KiB
NFC Kiosk — Enrollment UX, PIN fix, Speed, Clock-out Hours
Date: 2026-05-30
Module: fusion_clock (NFC tap kiosk at /fusion_clock/kiosk/nfc)
Status: Approved design, ready for implementation plan.
Context
The NFC kiosk (static/src/js/fusion_clock_nfc_kiosk.js, an IIFE state machine) handles
tap-to-clock on a wall tablet at the entech client. Four issues to address, all driven by
real shop-floor use (lines of 10–20 people).
Implementation approach: extend the existing IIFE in place. A migration to an Odoo 19
Interaction (per repo CLAUDE.md guidance) is deliberately out of scope — the file is a
large, working state machine on a live client device and the four changes here are
surgical; a rewrite would be high-risk for no functional gain. Noted deviation.
Requirements & Design
1. PIN entry: stop the per-digit full re-render
Problem: in renderEnroll(phase:"password"), every numpad press calls
renderEnroll(...) which rebuilds the whole panel via stateContainer.innerHTML = ... and
replays the 400ms nfc-state-in entrance animation → the screen visibly "refreshes" on each
digit (entry is preserved, but it flickers).
Design: a reusable PIN-pad component that renders the panel once, then on
digit/backspace mutates only the masked .pin-display text node + an in-memory buffer.
No innerHTML rebuild, no re-animation. Used by both the ⚙ enroll PIN and the new
Manager-PIN step (§2). OK/Cancel callbacks are parameters.
2. Program a tag from an unknown tap
Problem: an unknown card tap returns {error:"card_unknown"} and shows a red error that
auto-dismisses. Programming requires the separate ⚙ flow (enter password → search → re-tap).
Design: the tapped UID is already captured, so program that card with no re-tap:
- Unknown tap → amber "This card isn't programmed yet" panel with "Program this card" and "Cancel" buttons. Auto-cancel to idle after ~8s of inactivity.
- "Program this card" → Manager PIN step (reuses §1 component; credential =
fusion_clock.nfc_enroll_password, currently1120; labelled "Manager PIN" in UI). - Employee step: search-and-pick an existing employee or "+ New employee" →
enter a name → create a minimal
hr.employee. - Assign: bind the captured UID to that employee → success confirmation.
- The ⚙ enroll mode stays as a proactive path, reusing the same fixed PIN component.
Backend:
- Reuse
POST /fusion_clock/kiosk/nfc/enroll(employee_id,card_uid,enroll_password) for the bind. Already manager/Kiosk-Operator + password gated, sudo data ops. - New endpoint
POST /fusion_clock/kiosk/nfc/create_employee(name,enroll_password): Kiosk-Operator-gated + password-gated; createshr.employeevia sudo withname,x_fclk_enable_clock=True,company_id = request.env.company.id; returns{employee_id, employee_name}(or{error}). JS then callsenrollwith the captured UID. Minimal fields only — department/contract/etc. are completed later in HR.
3. Faster clock-in/out ("Fast")
Problem: result card lingers 3s (errors 4s) and entrance animations are 0.4–0.7s →
slow throughput for long lines.
Design (JS timers): success result display 3000 → 1800 ms; error 4000 → 3000 ms.
Design (SCSS durations): nfc-state-in 400→200ms; nfc-success-burst 700→350ms;
nfc-avatar-in 600→300ms. Ambient idle wave/chip loop unchanged (does not gate throughput).
prefers-reduced-motion fallback preserved.
4. Clock-out shows shift hours, clearly
Problem: clock-out shows ${net_hours_today.toFixed(1)}h today — mislabelled "today",
small, and hidden when it rounds to 0.
Design: on clock-out always show a prominent "Worked Xh Ym this shift" computed from
net_hours_today (the just-closed attendance's net hours = worked − break). Render h+m;
show even when 0 (e.g. "Worked 0h 4m this shift"). Backend already returns the value; this is
a JS label/format + SCSS prominence change. Clock-in unchanged.
Files
static/src/js/fusion_clock_nfc_kiosk.js— PIN component; unknown-tap → program flow; create-employee call; result timers; clock-out hours formatting.static/src/scss/nfc_kiosk.scss— animation durations; amber "unknown card" panel + create-employee styles; prominent clock-out hours.controllers/clock_nfc_kiosk.py— newnfc_create_employeeendpoint.__manifest__.py— version bump (assets changed).
Out of scope / non-goals
- No migration of the kiosk JS to an
Interaction. - No new employee fields beyond name/clock-enabled/company at kiosk-create time.
- Classic PIN kiosk (
/fusion_clock/kiosk) untouched (disabled at entech).
Test / verify
- Local:
pyflakesthe controller;xmllint/manifest parse; review the JS by hand (no local Odoo container available this session). - entech: deploy, upgrade, then on the tablet — PIN entry no longer flickers; unknown tap → program (existing + new employee) binds without re-tap; clock-in/out visibly faster; clock-out shows "Worked Xh Ym this shift".