feat(sub12b): OWL Rack Parts sub-dialog (Task 12)
Mirrors screens 7-8. Searchable empty-rack picker with debounced typeahead via /fp/tablet/rack/list_empty. QR Scan button prompts operator for FP-RACK:<name> token, resolves via /fp/tablet/rack/ scan_qr. Save commits the racking via /fp/tablet/rack_parts/commit. Save+Print opens /web/report/pdf/fp.rack.travel/<id> in a new tab — that report ships in Sub 12c, returns 404 until then. Plain Save works today. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,90 @@
|
|||||||
|
/** @odoo-module */
|
||||||
|
/*
|
||||||
|
* Sub 12b — Rack Parts sub-dialog (OWL).
|
||||||
|
*
|
||||||
|
* Mirrors Steelhead screens 7-8. Searchable empty-rack picker,
|
||||||
|
* QR-scan input, Unit + Amount fields. Save assigns the step → rack;
|
||||||
|
* Save + Print also opens the rack travel ticket PDF (Sub 12c).
|
||||||
|
*/
|
||||||
|
|
||||||
|
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 FpRackPartsDialog extends Component {
|
||||||
|
static template = "fusion_plating_shopfloor.FpRackPartsDialog";
|
||||||
|
static components = { Dialog };
|
||||||
|
static props = ["fromStepId", "qty", "onRacked?", "close"];
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
this.notification = useService("notification");
|
||||||
|
this.state = useState({
|
||||||
|
racks: [],
|
||||||
|
search: "",
|
||||||
|
selectedRackId: false,
|
||||||
|
unit: "Count",
|
||||||
|
amount: this.props.qty || 0,
|
||||||
|
saving: false,
|
||||||
|
});
|
||||||
|
onWillStart(async () => {
|
||||||
|
await this.refreshRacks("");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async refreshRacks(query) {
|
||||||
|
const data = await rpc("/fp/tablet/rack/list_empty", { query });
|
||||||
|
if (data.ok) {
|
||||||
|
this.state.racks = data.racks;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async onSearch(ev) {
|
||||||
|
this.state.search = ev.target.value;
|
||||||
|
await this.refreshRacks(this.state.search);
|
||||||
|
}
|
||||||
|
|
||||||
|
async onScan() {
|
||||||
|
const code = window.prompt(_t("Scan or type FP-RACK:<name>:"));
|
||||||
|
if (!code) return;
|
||||||
|
const data = await rpc("/fp/tablet/rack/scan_qr", { qr_code: code });
|
||||||
|
if (data.ok) {
|
||||||
|
this.state.selectedRackId = data.rack_id;
|
||||||
|
this.notification.add(
|
||||||
|
_t("Selected %s", data.rack_name), { type: "success" });
|
||||||
|
} else {
|
||||||
|
this.notification.add(data.error, { type: "danger" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async onSave(printAfter) {
|
||||||
|
if (!this.state.selectedRackId) return;
|
||||||
|
this.state.saving = true;
|
||||||
|
const result = await rpc("/fp/tablet/rack_parts/commit", {
|
||||||
|
from_step_id: this.props.fromStepId,
|
||||||
|
rack_id: this.state.selectedRackId,
|
||||||
|
qty: this.state.amount,
|
||||||
|
});
|
||||||
|
if (result.ok) {
|
||||||
|
this.notification.add(
|
||||||
|
_t("Racked onto %s", result.rack_name),
|
||||||
|
{ type: "success" });
|
||||||
|
if (this.props.onRacked) {
|
||||||
|
this.props.onRacked(result);
|
||||||
|
}
|
||||||
|
this.props.close();
|
||||||
|
if (printAfter) {
|
||||||
|
// Sub 12c report — until it ships, this returns 404.
|
||||||
|
window.open(
|
||||||
|
`/web/report/pdf/fp.rack.travel/${this.state.selectedRackId}`,
|
||||||
|
"_blank",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.notification.add(result.error, { type: "danger" });
|
||||||
|
this.state.saving = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<templates xml:space="preserve">
|
||||||
|
|
||||||
|
<t t-name="fusion_plating_shopfloor.FpRackPartsDialog">
|
||||||
|
<Dialog title.translate="Rack Parts" size="'md'">
|
||||||
|
<div class="o_fp_rack_dialog">
|
||||||
|
|
||||||
|
<div class="o_fp_move_field">
|
||||||
|
<label>To Rack</label>
|
||||||
|
<input type="text" placeholder="Search racks…"
|
||||||
|
t-on-input="onSearch" t-att-value="state.search"/>
|
||||||
|
<button class="btn btn-sm btn-secondary"
|
||||||
|
t-on-click="onScan">
|
||||||
|
QR Scan
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="o_fp_move_field">
|
||||||
|
<label/>
|
||||||
|
<select t-model.number="state.selectedRackId">
|
||||||
|
<option value="">— Select empty rack —</option>
|
||||||
|
<t t-foreach="state.racks" t-as="r" t-key="r.id">
|
||||||
|
<option t-att-value="r.id">
|
||||||
|
<t t-esc="r.name"/> (<t t-esc="r.rack_type"/>)
|
||||||
|
</option>
|
||||||
|
</t>
|
||||||
|
</select>
|
||||||
|
<span/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="o_fp_move_field">
|
||||||
|
<label>Unit</label>
|
||||||
|
<select t-model="state.unit">
|
||||||
|
<option value="Count">Count</option>
|
||||||
|
<option value="Pieces">Pieces</option>
|
||||||
|
<option value="Lbs">Lbs</option>
|
||||||
|
<option value="Kg">Kg</option>
|
||||||
|
</select>
|
||||||
|
<span/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="o_fp_move_field">
|
||||||
|
<label>Amount</label>
|
||||||
|
<input type="number" t-model.number="state.amount"/>
|
||||||
|
<span class="text-muted" t-esc="state.unit"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<t t-set-slot="footer">
|
||||||
|
<button class="btn btn-secondary" t-on-click="props.close">
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-primary"
|
||||||
|
t-att-disabled="!state.selectedRackId or state.saving"
|
||||||
|
t-on-click="() => this.onSave(false)">
|
||||||
|
Save
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-warning"
|
||||||
|
t-att-disabled="!state.selectedRackId or state.saving"
|
||||||
|
t-on-click="() => this.onSave(true)">
|
||||||
|
Save + Print
|
||||||
|
</button>
|
||||||
|
</t>
|
||||||
|
</Dialog>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
</templates>
|
||||||
Reference in New Issue
Block a user