diff --git a/__manifest__.py b/__manifest__.py index a4e0517..1c85065 100644 --- a/__manifest__.py +++ b/__manifest__.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- { 'name': 'POS Reverse Downpayment', - 'version': '1.0', + 'version': '19.0.1.0.0', 'category': 'Sales/Point of Sale', 'summary': 'Create Quotation from POS Cart and Zero-out Standard Items', 'description': """ diff --git a/models/pos_order.py b/models/pos_order.py index 13ea75e..05d64e4 100644 --- a/models/pos_order.py +++ b/models/pos_order.py @@ -77,19 +77,6 @@ class PosOrder(models.Model): pickings = self.env['stock.picking']._create_picking_from_pos_order_lines(destination_id, lines_to_pick, picking_type, self.partner_id) pickings.write({'pos_session_id': self.session_id.id, 'pos_order_id': self.id, 'origin': self.name}) - def _process_preparation_changes(self, cancelled=False, note_history=None): - """ - Intercepts preparation display generation. If this POS order is actually a Quotation - we don't want it to be processed by the kitchen display. - """ - if any(line.is_quotation_line for line in self.lines): - return {'change': False, 'sound': False, 'category_ids': []} - # Check if pos_preparation_display is installed and we can call super - if hasattr(super(), '_process_preparation_changes'): - return super()._process_preparation_changes(cancelled=cancelled, note_history=note_history) - return {'change': False, 'sound': False, 'category_ids': []} - - class PosOrderLine(models.Model): _inherit = 'pos.order.line' @@ -99,14 +86,8 @@ class PosOrderLine(models.Model): default=False ) - def _export_for_ui(self, orderline): - result = super()._export_for_ui(orderline) - result['is_quotation_line'] = orderline.is_quotation_line + @api.model + def _load_pos_data_fields(self, config): + result = super()._load_pos_data_fields(config) + result.append('is_quotation_line') return result - - def _order_line_fields(self, line, session_id=None): - result = super()._order_line_fields(line, session_id) - if 'is_quotation_line' in line[2]: - result[2]['is_quotation_line'] = line[2]['is_quotation_line'] - return result - diff --git a/static/src/overrides/models.js b/static/src/overrides/models.js index 78ade28..7947bac 100644 --- a/static/src/overrides/models.js +++ b/static/src/overrides/models.js @@ -1,6 +1,7 @@ /** @odoo-module */ -import { Order, Orderline } from "@point_of_sale/app/store/models"; +import { PosStore } from "@point_of_sale/app/services/pos_store"; +import { PosOrderline } from "@point_of_sale/app/models/pos_order_line"; import { patch } from "@web/core/utils/patch"; import { ConfirmPopup } from "@point_of_sale/app/utils/confirm_popup/confirm_popup"; import { ErrorPopup } from "@point_of_sale/app/errors/popups/error_popup"; @@ -8,35 +9,38 @@ import { _t } from "@web/core/l10n/translation"; import { OrderReceipt } from "@point_of_sale/app/screens/receipt_screen/receipt/order_receipt"; import { onError } from "@odoo/owl"; -patch(Order.prototype, { - setup() { - super.setup(...arguments); - }, +PosOrderline.extraFields.push("is_quotation_line"); +patch(PosStore.prototype, { async pay() { + const order = this.getOrder(); + if (!order) { + return super.pay(...arguments); + } + // Check if down payment product is in the cart - const downPaymentProductId = this.pos.config.down_payment_product_id && this.pos.config.down_payment_product_id[0]; + const downPaymentProductId = this.config.down_payment_product_id && this.config.down_payment_product_id[0]; if (!downPaymentProductId) { return super.pay(...arguments); } - const lines = this.get_orderlines(); + const lines = order.lines; // Only consider it a "quotation creation" downpayment if it has a positive quantity and price. // If pos_sale adds it to deduct a downpayment, it will use negative qty or subtotal. const downPaymentLines = lines.filter(line => - line.product.id === downPaymentProductId && - line.get_quantity() > 0 && - line.get_unit_price() >= 0 + line.product_id.id === downPaymentProductId && + line.qty > 0 && + line.price_unit >= 0 ); - const standardLines = lines.filter(line => line.product.id !== downPaymentProductId); + const standardLines = lines.filter(line => line.product_id.id !== downPaymentProductId); // If there is a down payment, we require a customer and automatically // create a quotation without prompting. const needsQuotation = downPaymentLines.length > 0; - if (needsQuotation && !this.is_quotation_line_converted) { - if (!this.get_partner()) { + if (needsQuotation && !order.is_quotation_line_converted) { + if (!order.partner_id) { await this.env.services.popup.add(ErrorPopup, { title: _t("Customer Required"), body: _t("Please select a customer before creating a quotation with a down payment."), @@ -47,23 +51,23 @@ patch(Order.prototype, { try { // Prepare data for backend const linesData = standardLines.map(line => ({ - product_id: line.product.id, - qty: line.get_quantity(), - price_unit: line.get_unit_price(), - discount: line.get_discount(), - tax_ids: line.tax_ids || line.get_product().taxes_id || [], + product_id: line.product_id.id, + qty: line.qty, + price_unit: line.price_unit, + discount: line.discount, + tax_ids: line.tax_ids ? line.tax_ids.map(t => t.id) : [], })); - // Call backend to create sale.order (removed the first empty array for @api.model) + // Call backend to create sale.order const saleOrderResult = await this.env.services.orm.call( "pos.order", "create_quotation_from_pos_lines", - [this.get_partner().id, linesData, this.pos.config.id] + [order.partner_id.id, linesData, this.config.id] ); if (saleOrderResult) { // Link the down payment line to the newly created sale order - const dpLine = lines.find(line => line.product.id === downPaymentProductId); + const dpLine = lines.find(line => line.product_id.id === downPaymentProductId); if (dpLine) { dpLine.sale_order_origin_id = { id: saleOrderResult.id, @@ -73,11 +77,11 @@ patch(Order.prototype, { // Zero out the standard lines to not charge the customer twice for (const line of standardLines) { - line.set_unit_price(0); + line.setUnitPrice(0); // Set a flag to easily skip stock delivery later if needed line.is_quotation_line = true; } - this.is_quotation_line_converted = true; + order.is_quotation_line_converted = true; } else { await this.env.services.popup.add(ErrorPopup, { title: _t("Error"), @@ -99,28 +103,20 @@ patch(Order.prototype, { // Override preparation display / kitchen printing // If we have a down payment, we consider this a quote, not an immediate order for the kitchen - async sendChanges(cancelled) { - const downPaymentProductId = this.pos.config.down_payment_product_id && this.pos.config.down_payment_product_id[0]; + async sendOrderInPreparationUpdateLastChange(order, opts = {}) { + const downPaymentProductId = this.config.down_payment_product_id && this.config.down_payment_product_id[0]; // Only block if there's a positive down payment line (indicating quotation creation, not settlement) - if (downPaymentProductId && this.get_orderlines().some(line => line.product.id === downPaymentProductId && line.get_quantity() > 0 && line.get_unit_price() > 0)) { + if (downPaymentProductId && order.lines.some(line => line.product_id.id === downPaymentProductId && line.qty > 0 && line.price_unit > 0)) { // It's a quotation line, we don't send it to preparation display - this.noteHistory = {}; - this.orderlines.forEach(line => line.setHasChange(false)); + order.noteHistory = {}; + order.lines.forEach(line => { + if(line.uiState) { + line.uiState.hasChange = false; + } + }); return true; } - return super.sendChanges(...arguments); - } -}); - -patch(Orderline.prototype, { - init_from_JSON(json) { - super.init_from_JSON(...arguments); - this.is_quotation_line = json.is_quotation_line || false; - }, - export_as_JSON() { - const json = super.export_as_JSON(...arguments); - json.is_quotation_line = this.is_quotation_line || false; - return json; + return super.sendOrderInPreparationUpdateLastChange(...arguments); } });