From f3989f5cb12b0a130de0ad8dcf594cd121179046 Mon Sep 17 00:00:00 2001 From: "Adrien Widart (awt)" Date: Wed, 20 Mar 2024 15:10:19 +0000 Subject: [PATCH] [FIX] {sale_,}stock: avoid cancel propagation If the 'cancel next move' feature is enabled, canceling a SOL could lead to unexpected picking creation To reproduce the issue: (Debug mode enabled) 1. In Settings, enable "Multi-Step Routes" 2. Edit the warehouse: - Outgoing shipments: 2 steps 3. Edit the delivery route: - For each rule: - Cancel next move: True 4. Confirm a SO with one product 5. Set the SOL quantity to 0 Error: A third picking is created, from customer to output location Step 4, it creates two SM: \- SM_SO: from Stock to Ouput \- SM_OC: from Output to Customer Step 5, thanks to the procurement process, we create a stock move: \- SM_OC_neg, from Output to Customer with a negative qty. While confirming this SM, and thanks to the same process, we then create a second stock move : \- SM_SO_neg, from Stock to Output, with a negative qty. Both negative SM are linked. While confirming SM_SO_neg, we merge it with SM_SO. Therefore: \- Dest moves of SM_SO_neg are given to SM_SO \- SM_SO_neg is deleted (fully absorbed by SM_SO) \- SM_SO has now a zero demand, so we cancel it: https://github.com/odoo/odoo/blob/bd7aadf589ef1ba4556164bc70fc0fbb62928e48/addons/stock/models/stock_move.py#L1024-L1025 However, because of step 3, we also cancel its dest moves: https://github.com/odoo/odoo/blob/bd7aadf589ef1ba4556164bc70fc0fbb62928e48/addons/stock/models/stock_move.py#L1740-L1745 i.e., we cancel SM_OC *and* SM_OC_neg. This leads to an inconsistency: Back to the confirmation of SM_SO_neg. As explained, this SM has been canceled during the procurement process. Still, we keep processing its confirmation (we overwrite its state, we assign it to a picking, and so on). Hence the error. In `_merge_moves`, when deleting a stock move, we first clean it (for instance, we disable the cancel propagation): https://github.com/odoo/odoo/blob/bd7aadf589ef1ba4556164bc70fc0fbb62928e48/addons/stock/models/stock_move.py#L1018-L1020 We should do the same with SMs we are going to cancel. That way, we fix the root cause of the issue: when cancelling SM_SO, we don't cancel neither SM_OC neither SM_OC_neg, so we don't have any inconsistency when going back to the confirmation of SM_SO_neg, and everything will work correctly. OPW-3753453 closes odoo/odoo#158663 X-original-commit: 7f385686b35b36561cfe9d2a320ff1699547efd2 Signed-off-by: William Henrotin (whe) Signed-off-by: Adrien Widart (awt) --- addons/sale_stock/tests/test_sale_stock.py | 28 ++++++++++++++++++++++ addons/stock/models/stock_move.py | 5 ++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/addons/sale_stock/tests/test_sale_stock.py b/addons/sale_stock/tests/test_sale_stock.py index 389c06cfce7..7e750993692 100644 --- a/addons/sale_stock/tests/test_sale_stock.py +++ b/addons/sale_stock/tests/test_sale_stock.py @@ -1680,6 +1680,34 @@ class TestSaleStock(TestSaleCommon, ValuationReconciliationTestCommon): return_picking_2.button_validate() self.assertEqual(return_wizard.product_return_moves.quantity, 1) + def test_2_steps_decrease_sol_qty_to_zero(self): + """ + 2 steps delivery, 'cancel next move' enabled + SO with one product + On the SO, cancel the qty of the product + On each picking, the SM should be canceled + """ + warehouse = self.env['stock.warehouse'].search([('company_id', '=', self.env.company.id)], limit=1) + + warehouse.delivery_steps = 'pick_ship' + warehouse.delivery_route_id.rule_ids.propagate_cancel = True + + so = self.env['sale.order'].create({ + 'partner_id': self.partner_a.id, + 'order_line': [(0, 0, { + 'name': self.product_a.name, + 'product_id': self.product_a.id, + 'product_uom_qty': 1, + 'product_uom': self.product_a.uom_id.id, + 'price_unit': self.product_a.list_price, + })], + }) + so.action_confirm() + + so.order_line.product_uom_qty = 0 + + self.assertEqual(so.picking_ids.move_ids.mapped('state'), ['cancel', 'cancel']) + def test_2_steps_fixed_procurement_propagation_with_backorder(self): """ When validating a picking (partially coming from a backorder) linked to 2 destinations moves in a 2-steps delivery, diff --git a/addons/stock/models/stock_move.py b/addons/stock/models/stock_move.py index 63455f1fcb7..d362ae254d1 100644 --- a/addons/stock/models/stock_move.py +++ b/addons/stock/models/stock_move.py @@ -1053,9 +1053,10 @@ Please change the quantity done or the rounding precision of your unit of measur pos_move.product_uom_qty = 0 moves_to_cancel |= pos_move + # We are using propagate to False in order to not cancel destination moves merged in moves[0] + (moves_to_unlink | moves_to_cancel)._clean_merged() + if moves_to_unlink: - # We are using propagate to False in order to not cancel destination moves merged in moves[0] - moves_to_unlink._clean_merged() moves_to_unlink._action_cancel() moves_to_unlink.sudo().unlink()