From 6d046f2881d7a159afc07badc8e9d5af17c49514 Mon Sep 17 00:00:00 2001 From: gsinghpal Date: Mon, 27 Apr 2026 21:11:49 -0400 Subject: [PATCH] feat(sub12b): OWL Move Parts dialog (Task 11) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirrors Steelhead screens 1-3, 14-15. Loads preview on mount, re-checks hard-blockers on commit. MOVE (n) button disabled when hard-blocked OR required prompt blank — improvement over Steelhead's silent disabled state (we show a tooltip listing reasons). Inline 'Resolve' button next to each blocker. For rack-required, fires a window CustomEvent ('fp-resolve-rack') the parent tablet catches to open the Rack Parts sub-dialog. Typed input rendering by input_type — text/number/checkbox/select/ datetime, plus support for time_hms and signature/photo (text input for now; full upload widget in Sub 12c). Co-Authored-By: Claude Opus 4.7 (1M context) --- .../static/src/js/move_parts_dialog.js | 155 ++++++++++++++++++ .../static/src/xml/move_parts_dialog.xml | 135 +++++++++++++++ 2 files changed, 290 insertions(+) create mode 100644 fusion_plating/fusion_plating_shopfloor/static/src/js/move_parts_dialog.js create mode 100644 fusion_plating/fusion_plating_shopfloor/static/src/xml/move_parts_dialog.xml diff --git a/fusion_plating/fusion_plating_shopfloor/static/src/js/move_parts_dialog.js b/fusion_plating/fusion_plating_shopfloor/static/src/js/move_parts_dialog.js new file mode 100644 index 00000000..e0c16421 --- /dev/null +++ b/fusion_plating/fusion_plating_shopfloor/static/src/js/move_parts_dialog.js @@ -0,0 +1,155 @@ +/** @odoo-module */ +/* + * Sub 12b — Move Parts dialog (OWL). + * + * Mirror of Steelhead screens 1-3, 14-15. Loads preview on mount, + * re-checks hard-blockers on commit. MOVE (n) button disabled when + * hard-blocked OR required prompt blank — improvement over Steelhead's + * silent disabled state (we show a tooltip listing blockers). + * + * Inline 'Resolve' button next to each blocker with a resolve_action. + * For rack-required, fires a window CustomEvent ('fp-resolve-rack') + * the parent tablet catches to open the Rack Parts sub-dialog. + */ + +import { Component, onWillStart, useState } from "@odoo/owl"; +import { Dialog } from "@web/core/dialog/dialog"; +import { rpc } from "@web/core/network/rpc"; +import { useService } from "@web/core/utils/hooks"; +import { _t } from "@web/core/l10n/translation"; + + +export class FpMovePartsDialog extends Component { + static template = "fusion_plating_shopfloor.FpMovePartsDialog"; + static components = { Dialog }; + static props = ["fromStepId", "toStepId", "onCommit?", "close"]; + + setup() { + this.notification = useService("notification"); + this.state = useState({ + loading: true, + qty: 0, + qtyAvailable: 0, + fromStep: { tank_options: [] }, + toStep: { tank_options: [] }, + transferType: "step", + toTankId: false, + toLocation: "global", + customerWoCount: 0, + transitionPrompts: [], + promptValues: {}, + blockers: [], + committing: false, + }); + onWillStart(async () => { + await this.loadPreview(); + }); + } + + async loadPreview() { + this.state.loading = true; + const data = await rpc("/fp/tablet/move_parts/preview", { + from_step_id: this.props.fromStepId, + to_step_id: this.props.toStepId, + }); + if (!data.ok) { + this.notification.add(data.error || _t("Preview failed"), + { type: "danger" }); + this.props.close(); + return; + } + this.state.qtyAvailable = data.qty_available; + this.state.qty = data.qty_available; + this.state.fromStep = data.from_step; + this.state.toStep = data.to_step; + this.state.transitionPrompts = data.transition_prompts; + this.state.blockers = data.blockers; + const opts = data.to_step.tank_options || []; + this.state.toTankId = opts.length ? opts[0].id : false; + this.state.loading = false; + } + + get hardBlocked() { + return this.state.blockers.some((b) => b.severity === "hard"); + } + + get requiredPromptsBlank() { + return this.state.transitionPrompts.some((p) => { + return p.required && !this.state.promptValues[p.id]; + }); + } + + get blockerTooltip() { + const reasons = []; + if (this.hardBlocked) { + reasons.push(...this.state.blockers + .filter((b) => b.severity === "hard") + .map((b) => b.message)); + } + if (this.requiredPromptsBlank) { + reasons.push(_t("Required compliance prompt(s) blank.")); + } + if (this.state.qty <= 0) { + reasons.push(_t("Part Count must be > 0.")); + } + if (this.state.qty > this.state.qtyAvailable) { + reasons.push(_t("Part Count exceeds available.")); + } + return reasons.join(" · "); + } + + get canCommit() { + return !this.state.committing && + !this.hardBlocked && + !this.requiredPromptsBlank && + this.state.qty > 0 && + this.state.qty <= this.state.qtyAvailable; + } + + async onCommit() { + if (!this.canCommit) return; + this.state.committing = true; + const result = await rpc("/fp/tablet/move_parts/commit", { + from_step_id: this.props.fromStepId, + to_step_id: this.props.toStepId, + qty: this.state.qty, + transfer_type: this.state.transferType, + to_tank_id: this.state.toTankId || false, + to_location: this.state.toLocation, + customer_wo_count: this.state.customerWoCount, + prompt_values: this.state.promptValues, + }); + if (result.ok) { + this.notification.add( + _t("Move %s committed", result.move_name), + { type: "success" }, + ); + if (this.props.onCommit) { + this.props.onCommit(result); + } + this.props.close(); + } else { + this.notification.add( + result.error || _t("Move failed"), + { type: "danger" }); + this.state.committing = false; + } + } + + onResolveBlocker(blocker) { + if (blocker.resolve_action === "open_rack_parts_dialog") { + const ev = new CustomEvent("fp-resolve-rack", { + detail: { + fromStepId: this.props.fromStepId, + qty: this.state.qty, + }, + }); + window.dispatchEvent(ev); + this.props.close(); + } else { + this.notification.add( + blocker.message + " " + _t("(Open the related step manually.)"), + { type: "warning" }); + } + } +} diff --git a/fusion_plating/fusion_plating_shopfloor/static/src/xml/move_parts_dialog.xml b/fusion_plating/fusion_plating_shopfloor/static/src/xml/move_parts_dialog.xml new file mode 100644 index 00000000..24accb48 --- /dev/null +++ b/fusion_plating/fusion_plating_shopfloor/static/src/xml/move_parts_dialog.xml @@ -0,0 +1,135 @@ + + + + + +
+ +
+ + + Available: +
+ +
+ + + +
+ +
+ + + +
+ +
+ + + +
+ +
+ + + +
+ +
+ + + +
+ +
+ + + +
+ +
+
Compliance Prompts
+ +
+ + + + + + + + +
+
+
+ +
+
Blockers
+ +
+ + + +
+
+
+
+ +
Loading…
+ + + + + +
+
+ +