From 05b6dfa0b1e4daf46d569c2e0a330b35e6437f8d Mon Sep 17 00:00:00 2001 From: Adrien Widart Date: Tue, 11 May 2021 09:16:35 +0000 Subject: [FIX] stock: reset stock move state On a delivery, when changing the initial demand of a stock move, if its value was 0 and becomes > 0, its state will be 'Partially Available' but the reserved quantity will still be 0. To reproduce the error: 1. Create 2 products P01, P02 - Product Type: Storable - Qty On Hand: 1 2. Create + Confirm a SO with 1xP01 and 1xP02 3. Open SO's delivery 4. Unlock, set P01's initial demand to 0, Save, Lock 5. Unreserve, Check Availability 6. Unlock, set P01's initial demand to 1, Save 7. Click on PO1 line Error: The stock move state is "Partially Available", but the initial demand is 1 and the reserved quantity is 0. It should be "Waiting Availability" On step 5, since the wanted quantity of P01 is 0, the state of the stock move becomes "Available". Then, when increasing the requested quantity, the state automatically becomes "Partially Available" because the module does not consider the case where the state is "Available" with a reserved quantity equal to 0. OPW-2488580 closes odoo/odoo#71371 X-original-commit: 5773a1e0658d014e6d93a8728f324d76f51fbe25 Signed-off-by: Arnold Moyaux Signed-off-by: Adrien Widart --- addons/stock/models/stock_move.py | 5 ++++ addons/stock/tests/test_move.py | 49 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/addons/stock/models/stock_move.py b/addons/stock/models/stock_move.py index 03f8254e685..a01e7f3967d 100644 --- a/addons/stock/models/stock_move.py +++ b/addons/stock/models/stock_move.py @@ -552,7 +552,9 @@ class StockMove(models.Model): # Handle the write on the initial demand by updating the reserved quantity and logging # messages according to the state of the stock.move records. receipt_moves_to_reassign = self.env['stock.move'] + move_to_recompute_state = self.env['stock.move'] if 'product_uom_qty' in vals: + move_to_unreserve = self.env['stock.move'] for move in self.filtered(lambda m: m.state not in ('done', 'draft') and m.picking_id): if float_compare(vals['product_uom_qty'], move.product_uom_qty, precision_rounding=move.product_uom.rounding): self.env['stock.move.line']._log_message(move.picking_id, move, 'stock.track_move_template', vals) @@ -565,9 +567,12 @@ class StockMove(models.Model): # When editing the initial demand, directly run again action assign on receipt moves. receipt_moves_to_reassign |= move_to_unreserve.filtered(lambda m: m.location_id.usage == 'supplier') receipt_moves_to_reassign |= (self - move_to_unreserve).filtered(lambda m: m.location_id.usage == 'supplier' and m.state in ('partially_available', 'assigned')) + move_to_recompute_state |= self - move_to_unreserve - receipt_moves_to_reassign if 'date_deadline' in vals: self._set_date_deadline(vals.get('date_deadline')) res = super(StockMove, self).write(vals) + if move_to_recompute_state: + move_to_recompute_state._recompute_state() if receipt_moves_to_reassign: receipt_moves_to_reassign._action_assign() return res diff --git a/addons/stock/tests/test_move.py b/addons/stock/tests/test_move.py index 5aca352da75..12d5360a199 100644 --- a/addons/stock/tests/test_move.py +++ b/addons/stock/tests/test_move.py @@ -2431,6 +2431,55 @@ class StockMove(SavepointCase): self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.customer_location), 3.0) + def test_use_unreserved_move_line_4(self): + product_01 = self.env['product.product'].create({ + 'name': 'Product 01', + 'type': 'product', + 'categ_id': self.env.ref('product.product_category_all').id, + }) + product_02 = self.env['product.product'].create({ + 'name': 'Product 02', + 'type': 'product', + 'categ_id': self.env.ref('product.product_category_all').id, + }) + self.env['stock.quant']._update_available_quantity(product_01, self.stock_location, 1) + self.env['stock.quant']._update_available_quantity(product_02, self.stock_location, 1) + + customer = self.env['res.partner'].create({'name': 'SuperPartner'}) + picking = self.env['stock.picking'].create({ + 'location_id': self.stock_location.id, + 'location_dest_id': self.customer_location.id, + 'partner_id': customer.id, + 'picking_type_id': self.env.ref('stock.picking_type_out').id, + }) + + p01_move = self.env['stock.move'].create({ + 'name': 'SuperMove01', + 'location_id': picking.location_id.id, + 'location_dest_id': picking.location_dest_id.id, + 'picking_id': picking.id, + 'product_id': product_01.id, + 'product_uom_qty': 1, + 'product_uom': product_01.uom_id.id, + }) + p02_move = self.env['stock.move'].create({ + 'name': 'SuperMove02', + 'location_id': picking.location_id.id, + 'location_dest_id': picking.location_dest_id.id, + 'picking_id': picking.id, + 'product_id': product_02.id, + 'product_uom_qty': 1, + 'product_uom': product_02.uom_id.id, + }) + + picking.action_confirm() + picking.action_assign() + p01_move.product_uom_qty = 0 + picking.do_unreserve() + picking.action_assign() + p01_move.product_uom_qty = 1 + self.assertEqual(p01_move.state, 'confirmed') + def test_edit_reserved_move_line_1(self): """ Test that editing a stock move line linked to an untracked product correctly and directly adapts the reservation. In this case, we edit the sublocation where we take the -- cgit v1.2.3