Tower: upload laundry_management 19.0.19.0.4 (via marketplace)
This commit is contained in:
363
addons/laundry_management/static/src/js/pos_store_patch.js.bak
Normal file
363
addons/laundry_management/static/src/js/pos_store_patch.js.bak
Normal file
@@ -0,0 +1,363 @@
|
|||||||
|
/** @odoo-module */
|
||||||
|
import { patch } from "@web/core/utils/patch";
|
||||||
|
import { _t } from "@web/core/l10n/translation";
|
||||||
|
import { PosStore } from "@point_of_sale/app/services/pos_store";
|
||||||
|
import { ask, makeAwaitable } from "@point_of_sale/app/utils/make_awaitable_dialog";
|
||||||
|
import { LaundryQuickCreatePartner } from "@laundry_management/js/quick_create_partner";
|
||||||
|
import { LaundryOrdersViewPopup } from "@laundry_management/js/view_laundry_orders";
|
||||||
|
import { LaundryOrderTypePopup } from "@laundry_management/js/popups/laundry_order_type_popup";
|
||||||
|
import { LaundryOrderAttributePopup } from "@laundry_management/js/popups/laundry_order_attribute_popup";
|
||||||
|
import { LaundryDeliveryDetailsPopup } from "@laundry_management/js/popups/laundry_delivery_details_popup";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether a POS order contains at least one laundry-service product.
|
||||||
|
*/
|
||||||
|
function hasLaundryProduct(order) {
|
||||||
|
return order.lines.some(
|
||||||
|
(line) => line.product_id?.product_tmpl_id?.is_laundry_service
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A line is a laundry SERVICE line — excludes settlement product. */
|
||||||
|
function isLaundryServiceLine(line) {
|
||||||
|
const tmpl = line.product_id?.product_tmpl_id;
|
||||||
|
return tmpl?.is_laundry_service && !tmpl?.is_laundry_settlement;
|
||||||
|
}
|
||||||
|
|
||||||
|
patch(PosStore.prototype, {
|
||||||
|
// -- Laundry notification after sync --
|
||||||
|
async postSyncAllOrders(orders) {
|
||||||
|
await super.postSyncAllOrders(orders);
|
||||||
|
for (const order of orders) {
|
||||||
|
if (order.laundry_order_id) {
|
||||||
|
this.notification.add(_t("Laundry Order Created"), {
|
||||||
|
type: "success",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// -- Force customer before payment screen (laundry orders only) --
|
||||||
|
async pay() {
|
||||||
|
const currentOrder = this.getOrder();
|
||||||
|
if (!currentOrder) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (hasLaundryProduct(currentOrder) && !currentOrder.getPartner()) {
|
||||||
|
const confirmed = await ask(this.env.services.dialog, {
|
||||||
|
title: _t("Customer Required"),
|
||||||
|
body: _t(
|
||||||
|
"This order contains laundry items. Please select or create a customer."
|
||||||
|
),
|
||||||
|
});
|
||||||
|
if (confirmed) {
|
||||||
|
const partner = await this.selectPartner();
|
||||||
|
if (!partner) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.pay();
|
||||||
|
},
|
||||||
|
|
||||||
|
// -- Quick create partner (name + phone + street, with duplicate check) --
|
||||||
|
async editPartner(partner) {
|
||||||
|
if (partner) {
|
||||||
|
return super.editPartner(partner);
|
||||||
|
}
|
||||||
|
// New partner: show lightweight popup
|
||||||
|
const result = await makeAwaitable(this.dialog, LaundryQuickCreatePartner, {});
|
||||||
|
if (!result) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Check for existing partner by phone
|
||||||
|
const existing = await this.data.call(
|
||||||
|
"res.partner",
|
||||||
|
"laundry_find_by_phone",
|
||||||
|
[result.phone]
|
||||||
|
);
|
||||||
|
if (existing) {
|
||||||
|
const useExisting = await ask(this.env.services.dialog, {
|
||||||
|
title: _t("Customer Already Exists"),
|
||||||
|
body: _t(
|
||||||
|
'A customer "%s" already exists with this phone number. Use existing customer?',
|
||||||
|
existing.name
|
||||||
|
),
|
||||||
|
});
|
||||||
|
if (useExisting) {
|
||||||
|
const partners = await this.data.read("res.partner", [existing.id]);
|
||||||
|
return partners[0] || false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Create new partner
|
||||||
|
const partnerId = await this.data.call(
|
||||||
|
"res.partner",
|
||||||
|
"laundry_quick_create",
|
||||||
|
[{ name: result.name, phone: result.phone, street: result.street || "" }]
|
||||||
|
);
|
||||||
|
const partners = await this.data.read("res.partner", [partnerId]);
|
||||||
|
return partners[0] || false;
|
||||||
|
},
|
||||||
|
|
||||||
|
// -- Settle laundry dues via native POS PaymentScreen --
|
||||||
|
async settleLaundryDues() {
|
||||||
|
const currentOrder = this.getOrder();
|
||||||
|
const partner = currentOrder?.getPartner();
|
||||||
|
if (!partner) {
|
||||||
|
this.notification.add(_t("Please select a customer first."), {
|
||||||
|
type: "warning",
|
||||||
|
});
|
||||||
|
const selectedPartner = await this.selectPartner();
|
||||||
|
if (!selectedPartner) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return this.settleLaundryDues();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch outstanding dues
|
||||||
|
const dues = await this.data.call(
|
||||||
|
"res.partner",
|
||||||
|
"get_laundry_dues",
|
||||||
|
[partner.id]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!dues || dues.total_due <= 0) {
|
||||||
|
this.notification.add(
|
||||||
|
_t('"%s" has no outstanding laundry dues.', partner.name),
|
||||||
|
{ type: "info" }
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the settlement product (is_laundry_settlement = true)
|
||||||
|
const settlementProduct = this.models["product.product"]
|
||||||
|
.getAll()
|
||||||
|
.find((p) => p.product_tmpl_id?.is_laundry_settlement);
|
||||||
|
|
||||||
|
if (!settlementProduct) {
|
||||||
|
this.notification.add(
|
||||||
|
_t("Settlement product not configured. Please check laundry settings."),
|
||||||
|
{ type: "danger" }
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a dedicated settlement order
|
||||||
|
const order = this.addNewOrder();
|
||||||
|
order.setPartner(partner);
|
||||||
|
|
||||||
|
// Add settlement product with total_due as price
|
||||||
|
await this.addLineToCurrentOrder(
|
||||||
|
{
|
||||||
|
product_id: settlementProduct,
|
||||||
|
product_tmpl_id: settlementProduct.product_tmpl_id,
|
||||||
|
price_unit: dues.total_due,
|
||||||
|
qty: 1,
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
// Navigate to native POS PaymentScreen
|
||||||
|
this.navigate("PaymentScreen", {
|
||||||
|
orderUuid: order.uuid,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────
|
||||||
|
// Laundry order type popup chain
|
||||||
|
// ─────────────────────────────────────────────────────────────────
|
||||||
|
async addLineToCurrentOrder(vals, opts, configure) {
|
||||||
|
const result = await super.addLineToCurrentOrder(vals, opts, configure);
|
||||||
|
try {
|
||||||
|
await this.maybeAskLaundryOrderType(this.getOrder());
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Laundry order-type prompt failed:", e);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trigger the type/attributes/delivery popup chain when the FIRST
|
||||||
|
* laundry-service line is added to an order — and only when:
|
||||||
|
* - feature is enabled in pos.config
|
||||||
|
* - order type not already chosen
|
||||||
|
* - the order has at least one laundry-service line (excludes settlement)
|
||||||
|
* - ask-on-first-line is enabled
|
||||||
|
*/
|
||||||
|
async maybeAskLaundryOrderType(order) {
|
||||||
|
if (!order || !order.lines.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const config = this.config;
|
||||||
|
if (!config?.enable_laundry_order_type) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (order.laundry_order_type_id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (config.ask_laundry_order_type_on_first_line === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const laundryLines = order.lines.filter(isLaundryServiceLine);
|
||||||
|
if (!laundryLines.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Only trigger when this is the FIRST laundry line.
|
||||||
|
if (laundryLines.length > 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await this.runLaundryOrderTypeFlow(order);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The popup chain itself — also reusable from the inline edit button
|
||||||
|
* in the order summary.
|
||||||
|
*/
|
||||||
|
async runLaundryOrderTypeFlow(order) {
|
||||||
|
if (!order) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const config = this.config;
|
||||||
|
|
||||||
|
// Step 1 — Main type
|
||||||
|
const types = (this.models["laundry.order.type"]?.getAll() || [])
|
||||||
|
.slice()
|
||||||
|
.sort((a, b) => (a.sequence || 0) - (b.sequence || 0));
|
||||||
|
if (!types.length) {
|
||||||
|
this.notification.add(
|
||||||
|
_t("No laundry order types configured. Ask a manager to add some."),
|
||||||
|
{ type: "warning" }
|
||||||
|
);
|
||||||
|
if (config.require_laundry_order_type) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const partner = order.getPartner();
|
||||||
|
const partnerDefaultType = partner?.default_laundry_order_type_id;
|
||||||
|
const defaultTypeId =
|
||||||
|
order.laundry_order_type_id?.id ||
|
||||||
|
(partnerDefaultType?.id ?? partnerDefaultType) ||
|
||||||
|
(config.default_laundry_order_type_id?.id ??
|
||||||
|
config.default_laundry_order_type_id) ||
|
||||||
|
false;
|
||||||
|
|
||||||
|
const chosenType = await makeAwaitable(this.dialog, LaundryOrderTypePopup, {
|
||||||
|
types: types,
|
||||||
|
defaultTypeId: defaultTypeId,
|
||||||
|
showIcons: !!config.show_order_type_icons,
|
||||||
|
allowSkip: !config.require_laundry_order_type,
|
||||||
|
title: _t("Select Order Type"),
|
||||||
|
});
|
||||||
|
if (chosenType === undefined) {
|
||||||
|
return; // user closed without confirming
|
||||||
|
}
|
||||||
|
order.laundry_order_type_id = chosenType
|
||||||
|
? this.models["laundry.order.type"].get(chosenType.id)
|
||||||
|
: false;
|
||||||
|
|
||||||
|
// Step 2 — Attributes (skip if disabled or nothing to show)
|
||||||
|
let chosenAttributes = [];
|
||||||
|
if (config.enable_laundry_attributes) {
|
||||||
|
const allAttrs = (this.models["laundry.order.attribute"]?.getAll() || [])
|
||||||
|
.slice()
|
||||||
|
.sort((a, b) => (a.sequence || 0) - (b.sequence || 0));
|
||||||
|
if (allAttrs.length) {
|
||||||
|
const suggestedIds = chosenType?.attribute_ids
|
||||||
|
? chosenType.attribute_ids
|
||||||
|
.map((a) => (typeof a === "object" ? a.id : a))
|
||||||
|
: [];
|
||||||
|
const partnerDefaultIds = (partner?.default_laundry_attribute_ids || [])
|
||||||
|
.map((a) => (typeof a === "object" ? a.id : a));
|
||||||
|
const preselected = [
|
||||||
|
...new Set([...suggestedIds, ...partnerDefaultIds]),
|
||||||
|
];
|
||||||
|
const result = await makeAwaitable(
|
||||||
|
this.dialog,
|
||||||
|
LaundryOrderAttributePopup,
|
||||||
|
{
|
||||||
|
attributes: allAttrs,
|
||||||
|
preselectedIds: preselected,
|
||||||
|
title: _t("Select Attributes (optional)"),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (result !== undefined) {
|
||||||
|
chosenAttributes = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
order.laundry_order_attribute_ids = chosenAttributes.map((a) =>
|
||||||
|
this.models["laundry.order.attribute"].get(a.id)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Step 3 — Delivery details (only when needed)
|
||||||
|
const typeIsDelivery = !!chosenType?.is_delivery;
|
||||||
|
const attrIsDelivery = chosenAttributes.some((a) => a.is_delivery_related);
|
||||||
|
const isDelivery = typeIsDelivery || attrIsDelivery;
|
||||||
|
order.laundry_is_delivery = isDelivery;
|
||||||
|
|
||||||
|
if (isDelivery && config.require_delivery_details_if_needed) {
|
||||||
|
const requireAddress =
|
||||||
|
!!chosenType?.requires_address ||
|
||||||
|
attrIsDelivery; // delivery attribute implies need for address
|
||||||
|
const requireTime = !!chosenType?.requires_scheduled_time;
|
||||||
|
const result = await makeAwaitable(
|
||||||
|
this.dialog,
|
||||||
|
LaundryDeliveryDetailsPopup,
|
||||||
|
{
|
||||||
|
defaultAddress: order.laundry_delivery_address || "",
|
||||||
|
defaultScheduledAt: order.laundry_delivery_scheduled_at || "",
|
||||||
|
requireAddress: requireAddress,
|
||||||
|
requireScheduledTime: requireTime,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (result) {
|
||||||
|
order.laundry_delivery_address = result.address || false;
|
||||||
|
order.laundry_delivery_scheduled_at = result.scheduledAt || false;
|
||||||
|
}
|
||||||
|
} else if (!isDelivery) {
|
||||||
|
order.laundry_delivery_address = false;
|
||||||
|
order.laundry_delivery_scheduled_at = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/** Inline edit handler from the order-summary header. */
|
||||||
|
async editLaundryOrderType() {
|
||||||
|
const order = this.getOrder();
|
||||||
|
if (!order) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.config?.allow_change_laundry_order_type_before_payment) {
|
||||||
|
this.notification.add(_t("Changing order type is disabled."), {
|
||||||
|
type: "warning",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await this.runLaundryOrderTypeFlow(order);
|
||||||
|
},
|
||||||
|
|
||||||
|
// -- View laundry orders for current customer --
|
||||||
|
async viewLaundryOrders() {
|
||||||
|
const currentOrder = this.getOrder();
|
||||||
|
let partner = currentOrder?.getPartner();
|
||||||
|
if (!partner) {
|
||||||
|
partner = await this.selectPartner();
|
||||||
|
if (!partner) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const orders = await this.data.call(
|
||||||
|
"res.partner",
|
||||||
|
"get_laundry_orders_for_pos",
|
||||||
|
[partner.id, 30]
|
||||||
|
);
|
||||||
|
this.dialog.add(LaundryOrdersViewPopup, {
|
||||||
|
partnerName: partner.name,
|
||||||
|
orders: orders || [],
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user