From a59e5f71cd54936f7bacb159c1bfb14790a9094a Mon Sep 17 00:00:00 2001 From: roen-odoo Date: Mon, 8 Jan 2024 16:03:31 +0100 Subject: [PATCH] [FIX] point_of_sale: correctly order lots in picking Current behavior: When you make an order in the PoS with different lots for the same product, the lots are not correctly selected in the picking. And only one lot is affected by the order. Steps to reproduce: - Create a product with tracking by lot, and 2 lots with some quantity - Create a PoS order with 2 lines of the same product, and select a different lot for each line - Validate the order - Close the PoS session - Check the picking, and the lot quantities Note: This partly revert this part of commit : https://github.com/odoo/odoo/commit/7dda6bb92715ea25b2818a62fec5e646f3678b81#diff-0ef4eb66998f308afe5f09748bc2af04ad79e9647507c25fe9007e03a79a1249L265-L303 And also make sure that the original created line quantity is set to 0 so that the each lot has a line, and the total quantity is correct. opw-3621363 closes odoo/odoo#148517 Signed-off-by: Robin Heinz (rhe) --- addons/point_of_sale/models/stock_picking.py | 27 +++++- .../tests/test_point_of_sale_flow.py | 82 +++++++++++++++++++ 2 files changed, 108 insertions(+), 1 deletion(-) diff --git a/addons/point_of_sale/models/stock_picking.py b/addons/point_of_sale/models/stock_picking.py index b08d9b5f926..281aa3312d9 100644 --- a/addons/point_of_sale/models/stock_picking.py +++ b/addons/point_of_sale/models/stock_picking.py @@ -242,9 +242,34 @@ class StockMove(models.Model): move.quantity = move.product_uom_qty moves_remaining = self - moves_to_assign existing_lots = moves_remaining._create_production_lots_for_pos_order(related_order_lines) + move_lines_to_create = [] + mls_qties = [] if are_qties_done: for move in moves_remaining: - move.quantity = move.product_uom_qty + move.move_line_ids.quantity = 0 + for line in lines_data[move.product_id.id]['order_lines']: + sum_of_lots = 0 + for lot in line.pack_lot_ids.filtered(lambda l: l.lot_name): + qty = 1 if line.product_id.tracking == 'serial' else abs(line.qty) + ml_vals = dict(move._prepare_move_line_vals(qty)) + if existing_lots: + existing_lot = existing_lots.filtered_domain([('product_id', '=', line.product_id.id), ('name', '=', lot.lot_name)]) + quant = self.env['stock.quant'] + if existing_lot: + quant = self.env['stock.quant'].search( + [('lot_id', '=', existing_lot.id), ('quantity', '>', '0.0'), ('location_id', 'child_of', move.location_id.id)], + order='id desc', + limit=1 + ) + ml_vals.update({ + 'quant_id': quant.id, + }) + else: + ml_vals.update({'lot_name': lot.lot_name}) + move_lines_to_create.append(ml_vals) + mls_qties.append(qty) + sum_of_lots += qty + self.env['stock.move.line'].create(move_lines_to_create) else: for move in moves_remaining: for line in lines_data[move.product_id.id]['order_lines']: diff --git a/addons/point_of_sale/tests/test_point_of_sale_flow.py b/addons/point_of_sale/tests/test_point_of_sale_flow.py index 6d4e6d5931f..4b3fcf18beb 100644 --- a/addons/point_of_sale/tests/test_point_of_sale_flow.py +++ b/addons/point_of_sale/tests/test_point_of_sale_flow.py @@ -1918,3 +1918,85 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon): self.assertEqual(refund.amount_total, -49.99) self.assertEqual(refund.amount_paid, -50.0) self.assertEqual(current_session.state, 'closed') + def test_order_different_lots(self): + self.pos_config.open_ui() + current_session = self.pos_config.current_session_id + self.stock_location = self.company_data['default_warehouse'].lot_stock_id + self.product2 = self.env['product.product'].create({ + 'name': 'Product A', + 'type': 'product', + 'tracking': 'lot', + }) + + lot1 = self.env['stock.lot'].create({ + 'name': '1001', + 'product_id': self.product2.id, + }) + lot2 = self.env['stock.lot'].create({ + 'name': '1002', + 'product_id': self.product2.id, + }) + + quant1 = self.env['stock.quant'].with_context(inventory_mode=True).create({ + 'product_id': self.product2.id, + 'inventory_quantity': 5, + 'location_id': self.stock_location.id, + 'lot_id': lot1.id + }) + quant1.action_apply_inventory() + quant2 = self.env['stock.quant'].with_context(inventory_mode=True).create({ + 'product_id': self.product2.id, + 'inventory_quantity': 5, + 'location_id': self.stock_location.id, + 'lot_id': lot2.id + }) + quant2.action_apply_inventory() + + order = self.PosOrder.create({ + 'company_id': self.env.company.id, + 'session_id': current_session.id, + 'partner_id': self.partner1.id, + 'lines': [(0, 0, { + 'name': "OL/0001", + 'product_id': self.product2.id, + 'price_unit': 6, + 'discount': 0, + 'qty': 2, + 'tax_ids': [[6, False, []]], + 'price_subtotal': 12, + 'price_subtotal_incl': 12, + 'pack_lot_ids': [ + [0, 0, {'lot_name': '1001'}], + ] + }), + (0, 0, { + 'name': "OL/0002", + 'product_id': self.product2.id, + 'price_unit': 6, + 'discount': 0, + 'qty': 1, + 'tax_ids': [[6, False, []]], + 'price_subtotal': 6, + 'price_subtotal_incl': 6, + 'pack_lot_ids': [ + [0, 0, {'lot_name': '1002'}], + ] + })], + 'pricelist_id': self.pos_config.pricelist_id.id, + 'amount_paid': 18.0, + 'amount_total': 18.0, + 'amount_tax': 0.0, + 'amount_return': 0.0, + 'to_invoice': False, + 'last_order_preparation_change': '{}' + }) + + payment_context = {"active_ids": order.ids, "active_id": order.id} + order_payment = self.PosMakePayment.with_context(**payment_context).create({ + 'amount': order.amount_total, + 'payment_method_id': self.bank_payment_method.id + }) + order_payment.with_context(**payment_context).check() + self.pos_config.current_session_id.action_pos_session_closing_control() + self.assertEqual(quant2.quantity, 4) + self.assertEqual(quant1.quantity, 3)