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