diff --git a/addons/laundry_management/static/src/js/payment_screen_patch.js b/addons/laundry_management/static/src/js/payment_screen_patch.js new file mode 100644 index 0000000..02f1600 --- /dev/null +++ b/addons/laundry_management/static/src/js/payment_screen_patch.js @@ -0,0 +1,166 @@ +/** @odoo-module + * + * PaymentScreen patch — minimal laundry behaviors: + * 1. Settlement orders: hide pay-later methods, custom validateOrder + * that calls settle_laundry_dues_rpc and bypasses normal sync. + * 2. Legacy laundry orders: trigger order-type popup at payment time + * (NOT on add-line). Block payment if type is required and missing. + * + * STRICT CONTRACT: + * - Does NOT touch order.lines, pricing, taxes + * - Does NOT override PosOrder.setup + * - Selections stored as primitives only (handled in pos_store_patch.js) + * - For non-laundry orders, super.validateOrder runs unchanged. + */ +import { patch } from "@web/core/utils/patch"; +import { _t } from "@web/core/l10n/translation"; +import { PaymentScreen } from "@point_of_sale/app/screens/payment_screen/payment_screen"; +import { AlertDialog } from "@web/core/confirmation_dialog/confirmation_dialog"; +import { LaundrySettlementReceipt } from "@laundry_management/js/settlement_receipt"; + +function isSettlementOrder(order) { + return ( + order && + order.lines && + order.lines.some( + (line) => line.product_id?.product_tmpl_id?.is_laundry_settlement + ) + ); +} + +patch(PaymentScreen.prototype, { + setup() { + super.setup(...arguments); + // For settlement orders, hide Customer Account / pay-later methods. + if (isSettlementOrder(this.currentOrder)) { + this.payment_methods_from_config = + this.payment_methods_from_config.filter( + (pm) => !pm.split_transactions + ); + } + }, + + /** + * Pre-payment gate for the legacy laundry order-type flow. + * - true → continue with payment + * - false → block payment + * + * Skipped for: feature disabled, settlement orders, non-laundry orders, + * and orders that already have a type. If required + missing, opens the + * popup; blocks if the user closes/cancels. + */ + async beforePaymentFlow(order) { + const pos = this.pos; + const config = pos.config; + if (!order || !config?.enable_laundry_order_type) { + return true; + } + if (isSettlementOrder(order)) { + return true; + } + if (!pos.orderHasLaundryServiceLine(order)) { + return true; + } + if (order.laundry_order_type_id) { + return true; + } + await pos.runLaundryOrderTypeFlow(order); + if (config.require_laundry_order_type && !order.laundry_order_type_id) { + pos.notification.add( + _t("Order type is required to proceed to payment."), + { type: "warning" } + ); + return false; + } + return true; + }, + + async validateOrder(isForceValidate) { + const order = this.currentOrder; + + // ── Settlement validation — bypass normal POS sync ── + if (isSettlementOrder(order)) { + return this._validateSettlementOrder(order); + } + + // ── Legacy laundry order-type gate ── + const allowed = await this.beforePaymentFlow(order); + if (!allowed) { + return false; + } + + return super.validateOrder(isForceValidate); + }, + + async _validateSettlementOrder(order) { + const paymentLines = order.payment_ids + .filter((pl) => pl.getAmount() > 0) + .map((pl) => ({ + pos_payment_method_id: pl.payment_method_id.id, + amount: pl.getAmount(), + })); + + if (paymentLines.length === 0) { + this.notification.add( + _t("Please add at least one payment."), + { type: "warning" } + ); + return false; + } + + const partnerId = order.partner_id?.id; + if (!partnerId) { + this.notification.add(_t("No customer selected."), { + type: "danger", + }); + return false; + } + + const sessionId = this.pos.session?.id || null; + + let response; + try { + this.ui.block(); + response = await this.pos.data.call( + "res.partner", + "settle_laundry_dues_rpc", + [partnerId, paymentLines, sessionId] + ); + } catch (err) { + this.dialog.add(AlertDialog, { + title: _t("Settlement Failed"), + body: + err?.data?.message || + err?.message || + _t("Unknown error."), + }); + return false; + } finally { + this.ui.unblock(); + } + + if ( + order.payment_ids.some( + (pl) => pl.payment_method_id.is_cash_count && pl.getAmount() > 0 + ) + ) { + this.pos.hardwareProxy.openCashbox(); + } + + this.dialog.add(LaundrySettlementReceipt, { + partnerName: order.partner_id.name, + settledTotal: response.settled_total || 0, + remainingDue: response.remaining_due || 0, + payments: response.payments || [], + settledOrders: response.settled_orders || [], + }); + + this.pos._lastSettlementMethodId = + paymentLines[0]?.pos_payment_method_id || null; + + this.pos.removeOrder(order, false); + this.pos.addNewOrder(); + this.pos.navigate("ProductScreen"); + return true; + }, +});