diff --git a/fusion_plating/CLAUDE.md b/fusion_plating/CLAUDE.md index 44d62c08..d73311c8 100644 --- a/fusion_plating/CLAUDE.md +++ b/fusion_plating/CLAUDE.md @@ -192,6 +192,9 @@ The kiosk user's actual `res.users.password` AND `ir.config_parameter['fp.tablet - **Both set to the same value** — kiosk password is plaintext-readable in DB but lock-back works automatically. - **ICP key deleted entirely** — `DELETE FROM ir_config_parameter WHERE key = 'fp.tablet.kiosk_password';` — accepts manual re-login after every lock event in exchange for no plaintext in DB or backups. +**Identify the kiosk user by xmlid, NEVER by login string:** +The kiosk login (`fp_tablet_kiosk@enplating.local` at creation time) is a `noupdate="1"` data record — admins can rename it on the user form for memorability (entech's actual kiosk login is `tablet@enplating.ca` as of 2026-05-24), and the rename PERSISTS through `-u`. Any code that hardcoded `'fp_tablet_kiosk@enplating.local'` as a string silently breaks after a rename — caught when Phase G's `lock_session` had the login hardcoded and broke after the user renamed the kiosk; fixed by resolving via `env.ref('fusion_plating_shopfloor.user_fp_tablet_kiosk').sudo().login`. Same pattern applies to any other user/group/record an admin might rename on the form. The xmlid is the stable identity; the display fields are not. + **Audit log** (`fp.tablet.session.event`): append-only model with Owner-only read ACL + Python `write`/`unlink` overrides (only the force-lock cron + retention crons bypass via context flags `fp_tablet_audit_admin_write` / `fp_tablet_audit_admin_purge`). Captures every unlock / failed_unlock / manual_lock / idle_lock / ceiling_lock / force_lock / admin_reset event with sha256(session sid), ip, user-agent, acting_uid, duration. View under Plating → Configuration → Tablet Audit Log (Owner-only menu). Per-user 7-day count smart button on `res.users` form. ## Removing menus/records — Odoo does NOT auto-delete orphans diff --git a/fusion_plating/fusion_plating_shopfloor/controllers/tablet_controller.py b/fusion_plating/fusion_plating_shopfloor/controllers/tablet_controller.py index 427efaaa..0085ba4d 100644 --- a/fusion_plating/fusion_plating_shopfloor/controllers/tablet_controller.py +++ b/fusion_plating/fusion_plating_shopfloor/controllers/tablet_controller.py @@ -323,10 +323,25 @@ class FpTabletController(http.Controller): # Destroy the tech session request.session.logout(keep_db=True) - # Re-authenticate as the kiosk user. Credential comes from + # Re-authenticate as the kiosk user. Resolve the login via xmlid + # so admins can rename the kiosk user (e.g. for memorability) on + # the user form without breaking lock-back. Password comes from # ir.config_parameter (auto-generated on first install in - # post-migrate.py). - kiosk_login = 'fp_tablet_kiosk@enplating.local' + # post-migrate.py; must be kept in sync with the user-record + # password by anyone who resets it on the user form). + kiosk_user = env.ref( + 'fusion_plating_shopfloor.user_fp_tablet_kiosk', + raise_if_not_found=False, + ) + kiosk_login = kiosk_user.sudo().login if kiosk_user else None + if not kiosk_login: + _logger.error( + 'fusion_plating_shopfloor.user_fp_tablet_kiosk xmlid not ' + 'found; cannot re-auth tablet as kiosk. The browser will ' + 'need manual login.' + ) + return {'ok': True, 'locked_at': now.isoformat(), + 'needs_kiosk_relogin': True} kiosk_password = env['ir.config_parameter'].sudo().get_param( 'fp.tablet.kiosk_password', '' )