From 433666b2d98e7e161f9694716e63741acc1d02d1 Mon Sep 17 00:00:00 2001 From: Arnold Moyaux Date: Thu, 18 Oct 2018 11:26:58 +0000 Subject: [PATCH] [FIX] stock: free reservation over reserve Usecase to reproduce: - 6 units in stock - reserve 3 units on a move - reserve 3 units on another move - validate a third move with 1 unit It raise the error could not reserve more than you have in stock. It happens because when free reservation stole reservation from an exisiting move with too much quantity available, it continues the loop instead of stopping. This commit stop the loop once we have enough quantity taken. opw-1895578 closes odoo/odoo#27935 --- addons/stock/models/stock_move_line.py | 4 +-- addons/stock/tests/test_move.py | 42 ++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/addons/stock/models/stock_move_line.py b/addons/stock/models/stock_move_line.py index e965ea28d0b..3f966d3e5b1 100644 --- a/addons/stock/models/stock_move_line.py +++ b/addons/stock/models/stock_move_line.py @@ -510,6 +510,8 @@ class StockMoveLine(models.Model): candidate.product_uom_qty = 0.0 else: candidate.unlink() + if float_is_zero(quantity, precision_rounding=rounding): + break else: # split this move line and assign the new part to our extra move quantity_split = float_round( @@ -517,8 +519,6 @@ class StockMoveLine(models.Model): precision_rounding=self.product_uom_id.rounding, rounding_method='UP') candidate.product_uom_qty = self.product_id.uom_id._compute_quantity(quantity_split, candidate.product_uom_id, rounding_method='HALF-UP') - quantity -= quantity_split move_to_recompute_state |= candidate.move_id - if quantity == 0.0: break move_to_recompute_state._recompute_state() diff --git a/addons/stock/tests/test_move.py b/addons/stock/tests/test_move.py index 1ce26f22f1f..9a48f814abe 100644 --- a/addons/stock/tests/test_move.py +++ b/addons/stock/tests/test_move.py @@ -1854,6 +1854,48 @@ class StockMove(TransactionCase): self.assertAlmostEqual(move_pack_cust.reserved_availability, 1.0) self.assertEqual(move_pack_cust.state, 'partially_available') + def test_use_reserved_move_line_1(self): + """ Test that _free_reservation work when quantity is only available on + reserved move lines. + """ + self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 10.0) + move1 = self.env['stock.move'].create({ + 'name': 'test_use_unreserved_move_line_1_1', + 'location_id': self.stock_location.id, + 'location_dest_id': self.customer_location.id, + 'product_id': self.product1.id, + 'product_uom': self.uom_unit.id, + 'product_uom_qty': 5.0, + }) + move2 = self.env['stock.move'].create({ + 'name': 'test_use_unreserved_move_line_1_1', + 'location_id': self.stock_location.id, + 'location_dest_id': self.customer_location.id, + 'product_id': self.product1.id, + 'product_uom': self.uom_unit.id, + 'product_uom_qty': 5.0, + }) + move1._action_confirm() + move1._action_assign() + move2._action_confirm() + move2._action_assign() + move3 = self.env['stock.move'].create({ + 'name': 'test_use_unreserved_move_line_1_1', + 'location_id': self.stock_location.id, + 'location_dest_id': self.customer_location.id, + 'product_id': self.product1.id, + 'product_uom': self.uom_unit.id, + 'product_uom_qty': 0.0, + 'quantity_done': 1.0, + }) + move3._action_confirm() + move3._action_assign() + move3._action_done() + self.assertEqual(move3.state, 'done') + quant = self.env['stock.quant']._gather(self.product1, self.stock_location) + self.assertEqual(quant.quantity, 9.0) + self.assertEqual(quant.reserved_quantity, 9.0) + def test_use_unreserved_move_line_1(self): """ Test that validating a stock move linked to an untracked product reserved by another one correctly unreserves the other one.