From 8a497cdd5ff4cf93067d0519a9839ea6ec9adc21 Mon Sep 17 00:00:00 2001 From: Pierre Masereel Date: Wed, 26 Jul 2017 12:18:33 +0200 Subject: [PATCH] [FIX] stock: quant: _update_reserved_quantity and negative quantity If we have multiple quants returned when we want to decrease the reservation of a move line, the min function will always return the quantity passed in argument because 'reserved_quantity' is always a positive number and quantity a negative one. This can lead to negative reserved quantity on a quant, if the quantity to reserve on the first quant is lower than the absolute value of the quantity to decrease. To fix this, we correctly separate the behaviors for decrease and increase. We add a test that show the, now fixed, bug. --- addons/stock/models/stock_quant.py | 18 ++++++++------ addons/stock/tests/test_move.py | 39 ++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/addons/stock/models/stock_quant.py b/addons/stock/models/stock_quant.py index 15084292a5e..641e3e3f5b4 100644 --- a/addons/stock/models/stock_quant.py +++ b/addons/stock/models/stock_quant.py @@ -183,15 +183,17 @@ class StockQuant(models.Model): max_quantity_on_quant = quant.quantity - quant.reserved_quantity if max_quantity_on_quant <= 0: continue + max_quantity_on_quant = min(max_quantity_on_quant, quantity) + quant.reserved_quantity += max_quantity_on_quant + reserved_quants.append((quant, max_quantity_on_quant)) + quantity -= max_quantity_on_quant + available_quantity -= max_quantity_on_quant else: - max_quantity_on_quant = quant.reserved_quantity - max_quantity_on_quant = min(max_quantity_on_quant, quantity) - - quant.reserved_quantity += max_quantity_on_quant - reserved_quants.append((quant, max_quantity_on_quant)) - - quantity -= max_quantity_on_quant - available_quantity -= max_quantity_on_quant + max_quantity_on_quant = min(quant.reserved_quantity, abs(quantity)) + quant.reserved_quantity -= max_quantity_on_quant + reserved_quants.append((quant, -max_quantity_on_quant)) + quantity += max_quantity_on_quant + available_quantity += max_quantity_on_quant if quantity == 0 or available_quantity == 0: break diff --git a/addons/stock/tests/test_move.py b/addons/stock/tests/test_move.py index 597945466f4..c08a63a2f8c 100644 --- a/addons/stock/tests/test_move.py +++ b/addons/stock/tests/test_move.py @@ -831,6 +831,45 @@ class StockMove(TransactionCase): self.assertEqual(quants.reserved_quantity, 0.0) self.assertEqual(len(move1.move_line_ids), 0.0) + def test_unreserve_5(self): + """ Check the unreservation of a stock move reserved on multiple quants. + """ + # make some stock + self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 3) + self.env['stock.quant'].create({ + 'product_id': self.product1.id, + 'location_id': self.stock_location.id, + 'quantity': 2, + }) + self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.stock_location)), 2) + self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), 5) + + # creation + move1 = self.env['stock.move'].create({ + 'name': 'test_unreserve_5', + '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, + }) + self.assertEqual(move1.state, 'draft') + + # confirmation + move1.action_confirm() + self.assertEqual(move1.state, 'confirmed') + + # assignment + move1.action_assign() + self.assertEqual(move1.state, 'assigned') + self.assertEqual(len(move1.move_line_ids), 1) + move1.do_unreserve() + + quants = self.env['stock.quant']._gather(self.product1, self.stock_location) + self.assertEqual(len(quants), 2.0) + for quant in quants: + self.assertEqual(quant.reserved_quantity, 0) + def test_link_assign_1(self): """ Test the assignment mechanism when two chained stock moves try to move one unit of an untracked product.