From 81e983443c63147c4f5a823225e1a9c19d81400a Mon Sep 17 00:00:00 2001 From: Arnold Moyaux Date: Mon, 11 Dec 2017 15:21:32 +0100 Subject: [PATCH] [FIX] stock: unlink move line don't update move state Use case to reproduce: - Go for a delivery order and use show detailed operations mode - Have something reserved on a picking - Manually delete the stock.move.line -> The move state is not adapted to the new state of the stock move lines It happens because the move state is not computed not update on the move line unlink method. This commit use the existing move function in order to compute the state and add a test. A part of test_stock_flow_74 is updated. Since procurement refactoring make a move mto and confirm it will not work since purchase or mrp are not install (Usererror). Also do an unreserve on a draft move is not a real use case and should not pass it in waiting since moves orig are never created. --- addons/stock/models/stock_move.py | 13 +++------ addons/stock/models/stock_move_line.py | 6 +++- addons/stock/tests/test_move2.py | 39 ++++++++++++++++++++++++++ addons/stock/tests/test_stock_flow.py | 2 +- 4 files changed, 49 insertions(+), 11 deletions(-) diff --git a/addons/stock/models/stock_move.py b/addons/stock/models/stock_move.py index 7ef8306d976..b6fa82a3b19 100644 --- a/addons/stock/models/stock_move.py +++ b/addons/stock/models/stock_move.py @@ -466,14 +466,7 @@ class StockMove(models.Model): def _do_unreserve(self): if any(move.state in ('done', 'cancel') for move in self): raise UserError(_('Cannot unreserve a done move')) - for move in self: - move.move_line_ids.unlink() - if move.procure_method == 'make_to_order' and not move.move_orig_ids: - move.state = 'waiting' - elif move.move_orig_ids and not all(orig.state in ('done', 'cancel') for orig in move.move_orig_ids): - move.state = 'waiting' - else: - move.state = 'confirmed' + self.mapped('move_line_ids').unlink() return True def _push_apply(self): @@ -1136,7 +1129,9 @@ class StockMove(models.Model): elif move.reserved_availability and move.reserved_availability <= move.product_uom_qty: move.state = 'partially_available' else: - if move.move_orig_ids: + if move.procure_method == 'make_to_order' and not move.move_orig_ids: + move.state = 'waiting' + elif move.move_orig_ids and not all(orig.state in ('done', 'cancel') for orig in move.move_orig_ids): move.state = 'waiting' else: move.state = 'confirmed' diff --git a/addons/stock/models/stock_move_line.py b/addons/stock/models/stock_move_line.py index c5d712b8bc4..ba9383473ef 100644 --- a/addons/stock/models/stock_move_line.py +++ b/addons/stock/models/stock_move_line.py @@ -348,7 +348,11 @@ class StockMoveLine(models.Model): if ml.product_id.type == 'product' and not ml.location_id.should_bypass_reservation() and not float_is_zero(ml.product_qty, precision_digits=precision): self.env['stock.quant']._update_reserved_quantity(ml.product_id, ml.location_id, -ml.product_qty, lot_id=ml.lot_id, package_id=ml.package_id, owner_id=ml.owner_id, strict=True) - return super(StockMoveLine, self).unlink() + moves = self.mapped('move_id') + res = super(StockMoveLine, self).unlink() + if moves: + moves._recompute_state() + return res def _action_done(self): """ This method is called during a move's `action_done`. It'll actually move a quant from diff --git a/addons/stock/tests/test_move2.py b/addons/stock/tests/test_move2.py index 30868ec2dae..43a92522339 100644 --- a/addons/stock/tests/test_move2.py +++ b/addons/stock/tests/test_move2.py @@ -608,6 +608,45 @@ class TestPickShip(TestStockCommon): picking_client.action_confirm() self.assertEqual(len(picking_client.move_lines), 2, 'Moves should not be merged') + def test_mto_cancel_move_line(self): + """ Create a pick ship situation. Then process the pick picking + with a backorder. Then try to unlink the move line created on + the ship and check if the picking and move state are updated. + Then validate the backorder and unlink the ship move lines in + order to check again if the picking and state are updated. + """ + picking_pick, picking_client = self.create_pick_ship() + location = self.env['stock.location'].browse(self.stock_location) + + # make some stock + self.env['stock.quant']._update_available_quantity(self.productA, location, 10.0) + picking_pick.move_lines.quantity_done = 5.0 + backorder_wizard_values = picking_pick.button_validate() + backorder_wizard = self.env[(backorder_wizard_values.get('res_model'))].browse(backorder_wizard_values.get('res_id')) + backorder_wizard.process() + + self.assertTrue(picking_client.move_line_ids, 'A move line should be created.') + self.assertEqual(picking_client.move_line_ids.product_uom_qty, 5, 'The move line should have 5 unit reserved.') + + # Directly delete the move lines on the picking. (Use show detail operation on picking type) + # Should do the same behavior than unreserve + picking_client.move_line_ids.unlink() + + self.assertEqual(picking_client.move_lines.state, 'waiting', 'The move state should be waiting since nothing is reserved and another origin move still in progess.') + self.assertEqual(picking_client.state, 'waiting', 'The picking state should not be ready anymore.') + + picking_client.action_assign() + + back_order = self.env['stock.picking'].search([('backorder_id', '=', picking_pick.id)]) + back_order.move_lines.quantity_done = 5 + back_order.button_validate() + + self.assertEqual(picking_client.move_lines.reserved_availability, 10, 'The total quantity should be reserved since everything is available.') + picking_client.move_line_ids.unlink() + + self.assertEqual(picking_client.move_lines.state, 'confirmed', 'The move should be confirmed since all the origin moves are processed.') + self.assertEqual(picking_client.state, 'confirmed', 'The picking should be confirmed since all the moves are confirmed.') + class TestSinglePicking(TestStockCommon): def test_backorder_1(self): diff --git a/addons/stock/tests/test_stock_flow.py b/addons/stock/tests/test_stock_flow.py index d625d9227f9..022e319f95f 100644 --- a/addons/stock/tests/test_stock_flow.py +++ b/addons/stock/tests/test_stock_flow.py @@ -1796,6 +1796,6 @@ class TestStockFlow(TestStockCommon): move_with_ancestors._do_unreserve() other_move._do_unreserve() - self.assertEquals(move_mto_alone.state, "waiting") + self.assertEquals(move_mto_alone.state, "draft") self.assertEquals(move_with_ancestors.state, "waiting") self.assertEquals(other_move.state, "confirmed")