From b98f7b47693c45698923c12a19fe14f515ba9629 Mon Sep 17 00:00:00 2001 From: git_admin Date: Fri, 1 May 2026 15:00:24 +0000 Subject: [PATCH] Tower: upload laundry_management 19.0.19.0.4 (via marketplace) --- .../src/js/laundry_order_context_panel.js | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 addons/laundry_management/static/src/js/laundry_order_context_panel.js diff --git a/addons/laundry_management/static/src/js/laundry_order_context_panel.js b/addons/laundry_management/static/src/js/laundry_order_context_panel.js new file mode 100644 index 0000000..882ed0f --- /dev/null +++ b/addons/laundry_management/static/src/js/laundry_order_context_panel.js @@ -0,0 +1,117 @@ +/** @odoo-module + * + * LaundryOrderContextPanel — premium card showing the order's + * legacy laundry context (type / attributes / delivery) with inline edit. + * + * Visible only when `pos.config.enable_laundry_order_type` is True. + * Reactive source: pos.laundryContext.get(order.uuid) + * (see laundry_context_store.js). Falls back to order primitives when + * the store has no entry yet (e.g. on order recall). + */ +import { Component } from "@odoo/owl"; +import { usePos } from "@point_of_sale/app/hooks/pos_hook"; + +export class LaundryOrderContextPanel extends Component { + static template = "laundry_management.LaundryOrderContextPanel"; + static props = {}; + + setup() { + this.pos = usePos(); + } + + get order() { + return this.pos.getOrder(); + } + + get isFeatureEnabled() { + return !!this.pos.config?.enable_laundry_order_type; + } + + /** Reactive context for the current order, with order-primitive fallback. */ + get ctx() { + const order = this.order; + if (!order) return null; + const live = this.pos.laundryContext?.get(order.uuid); + if (live && (live.type_id || live.attribute_ids?.length || live.is_delivery)) { + return live; + } + return { + type_id: order.laundry_order_type_id || false, + attribute_ids: order.laundry_order_attribute_ids || [], + is_delivery: !!order.laundry_is_delivery, + delivery_address: order.laundry_delivery_address || "", + delivery_scheduled_at: order.laundry_delivery_scheduled_at || "", + }; + } + + get orderType() { + const id = this.ctx?.type_id; + if (!id) return null; + return this.pos.models["laundry.order.type"]?.get(id) || null; + } + + get attributes() { + const ids = this.ctx?.attribute_ids || []; + const model = this.pos.models["laundry.order.attribute"]; + if (!model) return []; + return ids.map((id) => model.get(id)).filter(Boolean); + } + + get isDelivery() { + return !!this.ctx?.is_delivery; + } + + get deliveryAddress() { + const a = this.ctx?.delivery_address || ""; + if (!a) return ""; + return a.length > 64 ? a.slice(0, 61) + "…" : a; + } + + get deliveryScheduledAt() { + const v = this.ctx?.delivery_scheduled_at; + if (!v) return ""; + try { + const d = new Date(v.replace(" ", "T")); + if (!isNaN(d.getTime())) { + return d.toLocaleString(undefined, { + weekday: "short", + month: "short", + day: "numeric", + hour: "2-digit", + minute: "2-digit", + }); + } + } catch (_e) { + // ignore — fall back to raw value + } + return String(v); + } + + get hasContent() { + return !!(this.orderType || this.attributes.length || this.isDelivery); + } + + get typeStyle() { + const c = this.orderType?.color; + return c ? `background-color:${c};color:#fff;` : ""; + } + + attrStyle(attr) { + const c = attr?.color; + return c ? `background-color:${c};color:#fff;` : ""; + } + + typeIcon(type) { + if (!type) return "fa-tag"; + if (type.priority === "urgent") return "fa-bolt"; + if (type.is_delivery) return "fa-truck"; + return "fa-tag"; + } + + onClickEdit() { + if (!this.order) return; + // Bypasses the allow_change guard because the cashier explicitly + // clicked the panel's edit affordance. + this.pos.runLaundryOrderTypeFlow(this.order); + } +}