diff --git a/fusion_plating/fusion_plating_shopfloor/__manifest__.py b/fusion_plating/fusion_plating_shopfloor/__manifest__.py index a100a1e5..4e68d0c0 100644 --- a/fusion_plating/fusion_plating_shopfloor/__manifest__.py +++ b/fusion_plating/fusion_plating_shopfloor/__manifest__.py @@ -94,6 +94,8 @@ Copyright (c) 2026 Nexa Systems Inc. All rights reserved. 'fusion_plating_shopfloor/static/src/scss/tablet_lock.scss', 'fusion_plating_shopfloor/static/src/xml/tablet_lock.xml', 'fusion_plating_shopfloor/static/src/js/tablet_lock.js', + 'fusion_plating_shopfloor/static/src/xml/components/pin_setup.xml', + 'fusion_plating_shopfloor/static/src/js/components/pin_setup.js', # ---- Job Workspace (Phase 1 — tablet redesign) ---- 'fusion_plating_shopfloor/static/src/scss/job_workspace.scss', 'fusion_plating_shopfloor/static/src/xml/job_workspace.xml', diff --git a/fusion_plating/fusion_plating_shopfloor/static/src/js/components/pin_setup.js b/fusion_plating/fusion_plating_shopfloor/static/src/js/components/pin_setup.js new file mode 100644 index 00000000..db0a7f03 --- /dev/null +++ b/fusion_plating/fusion_plating_shopfloor/static/src/js/components/pin_setup.js @@ -0,0 +1,97 @@ +/** @odoo-module **/ +// ============================================================================= +// Fusion Plating — FpPinSetup (client action `fp_tablet_pin_setup`) +// +// Modal flow for setting OR changing the user's tablet PIN. Triggered +// from res.users preferences via action_open_tablet_pin_setup. Three +// stages: (1) old PIN (only if has_pin), (2) new PIN, (3) confirm new. +// ============================================================================= + +import { Component, useState, onMounted } from "@odoo/owl"; +import { rpc } from "@web/core/network/rpc"; +import { registry } from "@web/core/registry"; +import { useService } from "@web/core/utils/hooks"; +import { user } from "@web/core/user"; +import { FpPinPad } from "./pin_pad"; + +export class FpPinSetup extends Component { + static template = "fusion_plating_shopfloor.PinSetup"; + static components = { FpPinPad }; + static props = ["*"]; + + setup() { + this.notification = useService("notification"); + this.action = useService("action"); + this.state = useState({ + stage: "loading", // 'loading' | 'old' | 'new' | 'confirm' | 'done' + newPin: "", + hasExistingPin: false, + }); + onMounted(() => this._init()); + } + + async _init() { + // Cheap probe: search_count on the user's own record filtered + // by pin_set_date. Non-manager users can read their own set_date + // (not the hash). If the count is 1, they have a PIN; 0 = no PIN. + try { + const has = await rpc("/web/dataset/call_kw", { + model: "res.users", + method: "search_count", + args: [[ + ["id", "=", user.userId], + ["x_fc_tablet_pin_set_date", "!=", false], + ]], + kwargs: {}, + }); + this.state.hasExistingPin = has > 0; + } catch (e) { + this.state.hasExistingPin = false; + } + this.state.stage = this.state.hasExistingPin ? "old" : "new"; + } + + async onOldPinSubmit(pin) { + // Stash for the final call; set_pin verifies it server-side + this._oldPin = pin; + this.state.stage = "new"; + return { ok: true }; + } + + async onNewPinSubmit(pin) { + this.state.newPin = pin; + this.state.stage = "confirm"; + return { ok: true }; + } + + async onConfirmPinSubmit(pin) { + if (pin !== this.state.newPin) { + return { ok: false, error: "PINs don't match. Try again." }; + } + const params = { new_pin: this.state.newPin }; + if (this._oldPin) params.old_pin = this._oldPin; + const res = await rpc("/fp/tablet/set_pin", params); + if (res && res.ok) { + this.notification.add("Tablet PIN updated.", { type: "success" }); + this.state.stage = "done"; + setTimeout(() => this._close(), 1500); + return { ok: true }; + } + // Reset back to start on hard error so user can retry cleanly + this.notification.add((res && res.error) || "Failed to set PIN", { type: "danger" }); + this._oldPin = null; + this.state.newPin = ""; + this.state.stage = this.state.hasExistingPin ? "old" : "new"; + return { ok: false, error: (res && res.error) || "Failed" }; + } + + _close() { + this.action.doAction({ type: "ir.actions.act_window_close" }); + } + + onCancel() { + this._close(); + } +} + +registry.category("actions").add("fp_tablet_pin_setup", FpPinSetup); diff --git a/fusion_plating/fusion_plating_shopfloor/static/src/xml/components/pin_setup.xml b/fusion_plating/fusion_plating_shopfloor/static/src/xml/components/pin_setup.xml new file mode 100644 index 00000000..157f763b --- /dev/null +++ b/fusion_plating/fusion_plating_shopfloor/static/src/xml/components/pin_setup.xml @@ -0,0 +1,29 @@ + + + + +
+
+ Loading… +
+ + + +
+ +

PIN updated

+
+
+
+ +