From 252bc396b445d1635e8dac1f3cfa1dd5de8cc36e Mon Sep 17 00:00:00 2001 From: Simon Lejeune Date: Mon, 9 Jul 2018 15:34:08 +0200 Subject: [PATCH] [FIX] stock: button_validate: forced move lines When a move line is not linked to a move, it'll be linked to the first move of the same product at validation time. We slightly improve this logic by linking it to the first non-completed move. To be completely correct, we should split the move line if necessary and link it to all needed moves, but at this time we don't want to make the logic too complex. Use case: create a sale order with three sale order lines of the same products, use the detailed operations view and add three move lines of the same products, validate the picking. Before the patch, all move lines were linked to the first move and thus only the first sale order line had delivered quantities. Fixes #25488 opw-1861765 --- addons/sale_stock/tests/test_sale_stock.py | 73 ++++++++++++++++++++++ addons/stock/models/stock_picking.py | 7 ++- 2 files changed, 77 insertions(+), 3 deletions(-) diff --git a/addons/sale_stock/tests/test_sale_stock.py b/addons/sale_stock/tests/test_sale_stock.py index c4a32bbf4dc..1081e18f153 100644 --- a/addons/sale_stock/tests/test_sale_stock.py +++ b/addons/sale_stock/tests/test_sale_stock.py @@ -421,3 +421,76 @@ class TestSaleStock(TestSale): # check the delivered quantity self.assertEqual(so1.order_line.qty_delivered, 3.0) + def test_07_forced_qties(self): + """ Make multiple sale order lines of the same product which isn't available in stock. On + the picking, create new move lines (through the detailed operations view). See that the move + lines are correctly dispatched through the moves. + """ + uom_unit = self.env.ref('product.product_uom_unit') + uom_dozen = self.env.ref('product.product_uom_dozen') + item1 = self.products['prod_order'] + + self.assertEqual(item1.uom_id.id, uom_unit.id) + + # sell a dozen + so1 = self.env['sale.order'].create({ + 'partner_id': self.partner.id, + 'order_line': [ + (0, 0, { + 'name': item1.name, + 'product_id': item1.id, + 'product_uom_qty': 1, + 'product_uom': uom_dozen.id, + 'price_unit': item1.list_price, + }), + (0, 0, { + 'name': item1.name, + 'product_id': item1.id, + 'product_uom_qty': 1, + 'product_uom': uom_dozen.id, + 'price_unit': item1.list_price, + }), + (0, 0, { + 'name': item1.name, + 'product_id': item1.id, + 'product_uom_qty': 1, + 'product_uom': uom_dozen.id, + 'price_unit': item1.list_price, + }), + ], + }) + so1.action_confirm() + + self.assertEqual(len(so1.picking_ids.move_lines), 3) + so1.picking_ids.write({ + 'move_line_ids': [ + (0, 0, { + 'product_id': item1.id, + 'product_uom_qty': 0, + 'qty_done': 1, + 'product_uom_id': uom_dozen.id, + 'location_id': so1.picking_ids.location_id.id, + 'location_dest_id': so1.picking_ids.location_dest_id.id, + }), + (0, 0, { + 'product_id': item1.id, + 'product_uom_qty': 0, + 'qty_done': 1, + 'product_uom_id': uom_dozen.id, + 'location_id': so1.picking_ids.location_id.id, + 'location_dest_id': so1.picking_ids.location_dest_id.id, + }), + (0, 0, { + 'product_id': item1.id, + 'product_uom_qty': 0, + 'qty_done': 1, + 'product_uom_id': uom_dozen.id, + 'location_id': so1.picking_ids.location_id.id, + 'location_dest_id': so1.picking_ids.location_dest_id.id, + }), + ], + }) + so1.picking_ids.button_validate() + self.assertEqual(so1.picking_ids.state, 'done') + self.assertEqual(so1.order_line.mapped('qty_delivered'), [1, 1, 1]) + diff --git a/addons/stock/models/stock_picking.py b/addons/stock/models/stock_picking.py index 314fee31e06..f99e59b9c9b 100644 --- a/addons/stock/models/stock_picking.py +++ b/addons/stock/models/stock_picking.py @@ -631,8 +631,9 @@ class Picking(models.Model): # # Link existing moves or add moves when no one is related for ops in pick.move_line_ids.filtered(lambda x: not x.move_id): # Search move with this product - moves = pick.move_lines.filtered(lambda x: x.product_id == ops.product_id) - if moves: #could search move that needs it the most (that has some quantities left) + moves = pick.move_lines.filtered(lambda x: x.product_id == ops.product_id) + moves = sorted(moves, key=lambda m: m.quantity_done < m.product_qty, reverse=True) + if moves: ops.move_id = moves[0].id else: new_move = self.env['stock.move'].create({ @@ -802,7 +803,7 @@ class Picking(models.Model): quantity_done[quant.product_id.id] += quant.qty for pack in self.mapped('move_line_ids').filtered(lambda x: x.product_id and not x.move_id): quantity_done.setdefault(pack.product_id.id, 0) - quantity_done[pack.product_id.id] += pack.qty_done + quantity_done[pack.product_id.id] += pack.product_uom_id._compute_quantity(pack.qty_done, pack.product_id.uom_id) return any(quantity_done[x] < quantity_todo.get(x, 0) for x in quantity_done) @api.multi