From 631dea3998dfa82a16b2cdb8afc4f799ef887cd6 Mon Sep 17 00:00:00 2001 From: roen-odoo Date: Tue, 12 Sep 2023 14:21:12 +0000 Subject: [PATCH] [FIX] pos_sale: Correctly split lines when product are not groupable Current behavior: When importing a sale order with a product that is not groupable, the line is not split and the whole quantity is added to one line. Steps to reproduce: - Create a new uom category that is not groupable - Add a new uom to the category - Create a new product with the uom - Create a new sale order with the product - Settle the order in the PoS - All quantity is on one line This is also an issue if the product is tracked by serial number. The popup to fill the serial number will only have one field, even if the quantity is greater than 1. opw-3493378 closes odoo/odoo#141302 X-original-commit: 957942fe64391f9df7bee38d179cd1ce75fff787 Signed-off-by: Vlad Stroia (vlst) Signed-off-by: Robin Engels (roen) --- .../tours/helpers/ProductScreenTourMethods.js | 15 +++++++ .../sale_order_management_screen.js | 42 ++++++++++++------- .../static/tests/tours/PosSaleTour.js | 14 +++++++ addons/pos_sale/tests/test_pos_sale_flow.py | 33 +++++++++++++++ 4 files changed, 88 insertions(+), 16 deletions(-) diff --git a/addons/point_of_sale/static/tests/tours/helpers/ProductScreenTourMethods.js b/addons/point_of_sale/static/tests/tours/helpers/ProductScreenTourMethods.js index f495068e6ae..02a082c8954 100644 --- a/addons/point_of_sale/static/tests/tours/helpers/ProductScreenTourMethods.js +++ b/addons/point_of_sale/static/tests/tours/helpers/ProductScreenTourMethods.js @@ -412,3 +412,18 @@ export function addCustomerNote(note) { ].flat() ); } + +export function checkOrderlinesNumber(number) { + return [ + { + content: `check orderlines number`, + trigger: `.order .orderlines .orderline`, + run: () => { + const orderline_amount = $('.order .orderlines .orderline').length; + if (orderline_amount !== number) { + throw new Error(`Expected ${number} orderlines, got ${orderline_amount}`); + } + }, + }, + ]; +} diff --git a/addons/pos_sale/static/src/app/order_management_screen/sale_order_management_screen/sale_order_management_screen.js b/addons/pos_sale/static/src/app/order_management_screen/sale_order_management_screen/sale_order_management_screen.js index 20ee787f496..0d2f3b384b9 100644 --- a/addons/pos_sale/static/src/app/order_management_screen/sale_order_management_screen/sale_order_management_screen.js +++ b/addons/pos_sale/static/src/app/order_management_screen/sale_order_management_screen/sale_order_management_screen.js @@ -207,21 +207,19 @@ export class SaleOrderManagementScreen extends ControlButtonsMixin(Component) { continue; } - const new_line = new Orderline( - { env: this.env }, - { - pos: this.pos, - order: this.pos.get_order(), - product: this.pos.db.get_product_by_id(line.product_id[0]), - description: line.name, - price: line.price_unit, - tax_ids: orderFiscalPos ? undefined : line.tax_id, - price_type: "manual", - sale_order_origin_id: clickedOrder, - sale_order_line_id: line, - customer_note: line.customer_note, - } - ); + const line_values = { + pos: this.pos, + order: this.pos.get_order(), + product: this.pos.db.get_product_by_id(line.product_id[0]), + description: line.name, + price: line.price_unit, + tax_ids: orderFiscalPos ? undefined : line.tax_id, + price_manually_set: false, + sale_order_origin_id: clickedOrder, + sale_order_line_id: line, + customer_note: line.customer_note, + }; + let new_line = new Orderline({ env: this.env }, line_values); if ( new_line.get_product().tracking !== "none" && @@ -254,7 +252,19 @@ export class SaleOrderManagementScreen extends ControlButtonsMixin(Component) { new_line.setQuantityFromSOL(line); new_line.set_unit_price(line.price_unit); new_line.set_discount(line.discount); - this.pos.get_order().add_orderline(new_line); + const product = this.pos.db.get_product_by_id(line.product_id[0]); + const product_unit = product.get_unit(); + if (product_unit && !product.get_unit().is_pos_groupable) { + //loop for value of quantity + for (let j = 0; j < new_line.quantity; j++) { + let splitted_line = new Orderline({}, line_values); + splitted_line.quantity = 1; + this.pos.get_order().add_orderline(splitted_line); + } + } + else { + this.pos.get_order().add_orderline(new_line); + } } } else { // apply a downpayment diff --git a/addons/pos_sale/static/tests/tours/PosSaleTour.js b/addons/pos_sale/static/tests/tours/PosSaleTour.js index d8a1620becd..c32c86d461e 100644 --- a/addons/pos_sale/static/tests/tours/PosSaleTour.js +++ b/addons/pos_sale/static/tests/tours/PosSaleTour.js @@ -134,3 +134,17 @@ registry.category("web_tour.tours").add("PosSettleOrder3", { ReceiptScreen.isShown(), ].flat(), }); + +registry + .category("web_tour.tours") + .add('PosSettleOrderNotGroupable', { + test: true, + url: '/pos/ui', + steps: () => [ + ProductScreen.confirmOpeningPopup(), + ProductScreen.clickQuotationButton(), + ProductScreen.selectFirstOrder(), + ProductScreen.totalAmountIs(34.5), + ProductScreen.checkOrderlinesNumber(3), + ].flat(), + }); \ No newline at end of file diff --git a/addons/pos_sale/tests/test_pos_sale_flow.py b/addons/pos_sale/tests/test_pos_sale_flow.py index 435ae7c55e8..1f08ab13603 100644 --- a/addons/pos_sale/tests/test_pos_sale_flow.py +++ b/addons/pos_sale/tests/test_pos_sale_flow.py @@ -294,3 +294,36 @@ class TestPoSSale(TestPointOfSaleHttpCommon): self.assertEqual(sale_order.order_line[0].qty_delivered, 1) self.assertEqual(sale_order.picking_ids.mapped('state'), ['cancel', 'cancel', 'cancel']) + + def test_pos_not_groupable_product(self): + #Create a UoM Category that is not pos_groupable + uom_category = self.env['uom.category'].create({ + 'name': 'Test', + 'is_pos_groupable': False, + }) + uom = self.env['uom.uom'].create({ + 'name': 'Test', + 'category_id': uom_category.id, + 'uom_type': 'reference', + }) + product_a = self.env['product.product'].create({ + 'name': 'Product A', + 'available_in_pos': True, + 'type': 'product', + 'lst_price': 10.0, + 'uom_id': uom.id, + 'uom_po_id': uom.id, + }) + #create a sale order with product_a + self.env['sale.order'].create({ + 'partner_id': self.env.ref('base.res_partner_2').id, + 'order_line': [(0, 0, { + 'product_id': product_a.id, + 'name': product_a.name, + 'product_uom_qty': 3, + 'product_uom': product_a.uom_id.id, + 'price_unit': product_a.lst_price, + })], + }) + self.main_pos_config.open_ui() + self.start_tour("/pos/ui?config_id=%d" % self.main_pos_config.id, 'PosSettleOrderNotGroupable', login="accountman")