Tower: upload laundry_management 19.0.19.0.4 (via marketplace)

This commit is contained in:
2026-05-01 15:00:32 +00:00
parent 0a63809482
commit 3e88c5783b

View File

@@ -0,0 +1,133 @@
/** @odoo-module
*
* PosOrder patch — laundry context fields.
*
* Two responsibilities:
* 1. setup(): seed primitive defaults on the reactive proxy so that
* OWL components (panel, receipt) which read these slots track
* future writes and re-render. Without seeding, assigning a fresh
* property after first render does NOT trigger reactivity.
* No fields are pulled from the loader — pure in-memory defaults.
*
* 2. serializeForORM(): inject the laundry slots into the sync_from_ui
* payload so the backend pos.order columns are populated, exactly
* like `general_customer_note`.
* - laundry_order_type_id → integer id (Many2one)
* - laundry_order_attribute_ids → [[6, 0, ids]] (Many2many "set")
* - laundry_is_delivery → boolean
* - laundry_delivery_address → string | false
* - laundry_delivery_scheduled_at → string | false
*
* NOTE: this patch does NOT touch order.lines, pricing, taxes, or any
* relational field exposed by _load_pos_data_fields.
*/
import { patch } from "@web/core/utils/patch";
import { PosOrder } from "@point_of_sale/app/models/pos_order";
function pickId(v) {
if (!v) return false;
if (typeof v === "number") return v;
if (typeof v === "object" && Number.isInteger(v.id)) return v.id;
return false;
}
function pickIds(arr) {
if (!Array.isArray(arr)) return [];
const out = [];
const seen = new Set();
for (const v of arr) {
const id = pickId(v);
if (id && !seen.has(id)) {
seen.add(id);
out.push(id);
}
}
return out;
}
function normalizeDateTime(value) {
if (!value || typeof value !== "string") return false;
let v = value;
// Remove timezone like +03:00 or -03:00
const tzMatch = v.match(/([+-]\d{2}:\d{2})$/);
if (tzMatch) {
v = v.replace(tzMatch[0], "");
}
// Remove Z
if (v.endsWith("Z")) {
v = v.slice(0, -1);
}
// Remove milliseconds
if (v.includes(".")) {
v = v.split(".")[0];
}
// Replace T with space
v = v.replace("T", " ");
// Ensure seconds exist
if (/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/.test(v)) {
v += ":00";
}
return v;
}
patch(PosOrder.prototype, {
setup(...args) {
super.setup(...args);
// Seed defaults on the reactive proxy. Assigning after first
// render only triggers OWL reactivity if the key already exists
// on the proxy at observation time.
if (this.laundry_order_type_id === undefined) {
this.laundry_order_type_id = false;
}
if (this.laundry_order_attribute_ids === undefined) {
this.laundry_order_attribute_ids = [];
}
if (this.laundry_is_delivery === undefined) {
this.laundry_is_delivery = false;
}
if (this.laundry_delivery_address === undefined) {
this.laundry_delivery_address = false;
}
if (this.laundry_delivery_scheduled_at === undefined) {
this.laundry_delivery_scheduled_at = false;
}
},
initState(...args) {
super.initState(...args);
// Reactive flag for POS Mode system: when true, the order is a
// dedicated settlement context — products cannot be added,
// quantities cannot be edited, line cannot be deleted.
// Seeded here so OWL reactivity tracks future writes.
if (this.uiState && !("is_laundry_settle_due" in this.uiState)) {
this.uiState.is_laundry_settle_due = false;
}
},
serializeForORM(opts = {}) {
const data = super.serializeForORM(opts);
const typeId = pickId(this.laundry_order_type_id);
data.laundry_order_type_id = typeId || false;
const attrIds = pickIds(this.laundry_order_attribute_ids);
data.laundry_order_attribute_ids = [[6, 0, attrIds]];
data.laundry_is_delivery = !!this.laundry_is_delivery;
data.laundry_delivery_address = this.laundry_delivery_address || false;
// ABSOLUTE GUARANTEE: nothing with a "T", ".000", or timezone reaches
// the backend. Run the full normalizer even if upstream already did;
// normalize(normalize(x)) === normalize(x), so this is a zero-cost net.
data.laundry_delivery_scheduled_at =
normalizeDateTime(this.laundry_delivery_scheduled_at) || false;
return data;
},
});