From 9a5df70861362428d316083b1aed3db9d7fd7e8e Mon Sep 17 00:00:00 2001 From: "Adrien Widart (awt)" Date: Wed, 9 Aug 2023 13:56:59 +0000 Subject: [PATCH] [FIX] stock_picking_batch: ensure batch consistency When processing a batch without backorder, if a picking is not fully done, it will be removed from the batch To reproduce the issue: 1. Create two delivery pickings P1 et P2 with demands = 5 2. Have the quantity in the stock 3. Add both to a batch and confirm it 4. Set the done quantity on P1 5. On P2, set a done quantity to 4 6. Process the batch without backorder Error: Both pickings are done, and so does the batch, but P2 is not linked to that batch anymore When processing the batch, we will call `button_validate` on the pickings recordset https://github.com/odoo/odoo/blob/213b74be16cecc50e6e99c00f47786a9cbf327a8/addons/stock_picking_batch/models/stock_picking_batch.py#L252-L258 Later on, there is an override of `StockPicking._action_done` in the batch module. In this override, once we have called `super`, we then ensure that all pickings of the batch are done, else we detach some of them: https://github.com/odoo/odoo/blob/213b74be16cecc50e6e99c00f47786a9cbf327a8/addons/stock_picking_batch/models/stock_picking_batch.py#L252-L258 But here is the issue: as mentionned above, we first call `button_validate` on pickings recordset. In this method, we will call `StockPicking._action_done` two times: https://github.com/odoo/odoo/blob/ca168f7fbcbb8a300a8fa0b46f79a6c116b5aa93/addons/stock/models/stock_picking.py#L1088-L1089 The first time, for the pickings without the backorder option (P2 in the above use case), and the second time, for the pickings with the backorder option (P1 in the above use case). As a result, during the first call, we reach the override in batch module, and of course, P1 is not yet processed (it will be done during the second call of `_action_done`). Therefore, we think there is an inconsistency, and we detach P2. OPW-3346255 closes odoo/odoo#132037 X-original-commit: c2817285adf9dd44ad7155963f08d324995ba072 Signed-off-by: William Henrotin (whe) Signed-off-by: Adrien Widart (awt) --- .../models/stock_picking.py | 6 +++-- .../tests/test_batch_picking.py | 27 +++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/addons/stock_picking_batch/models/stock_picking.py b/addons/stock_picking_batch/models/stock_picking.py index b05e945cea1..dbeb3ef1852 100644 --- a/addons/stock_picking_batch/models/stock_picking.py +++ b/addons/stock_picking_batch/models/stock_picking.py @@ -117,8 +117,10 @@ class StockPicking(models.Model): picking._find_auto_batch() return res - def _action_done(self): - res = super()._action_done() + def button_validate(self): + res = super().button_validate() + if res is not True: + return res to_assign_ids = set() if self and self.env.context.get('pickings_to_detach'): self.env['stock.picking'].browse(self.env.context['pickings_to_detach']).batch_id = False diff --git a/addons/stock_picking_batch/tests/test_batch_picking.py b/addons/stock_picking_batch/tests/test_batch_picking.py index 844692fb053..e83afa6a953 100644 --- a/addons/stock_picking_batch/tests/test_batch_picking.py +++ b/addons/stock_picking_batch/tests/test_batch_picking.py @@ -621,6 +621,33 @@ class TestBatchPicking(TransactionCase): self.batch.write({'picking_ids': [[5, 0, 0]]}) self.assertEqual(self.batch.state, 'cancel', 'Batch Transfers should be cancelled when there are no transfers.') + def test_backorder_on_one_picking(self): + """ + Two pickings. The first only is fully done. The second one is not. The + user validates the batch without any backorder. Both pickings should be + done and still part of the batch + """ + self.env['stock.quant']._update_available_quantity(self.productA, self.stock_location, 10.0) + self.env['stock.quant']._update_available_quantity(self.productB, self.stock_location, 8.0) + + self.batch.action_confirm() + + self.batch.action_assign() + self.picking_client_1.move_ids.quantity_done = 10 + self.picking_client_2.move_ids.quantity_done = 7 + + action = self.batch.action_done() + wizard = Form(self.env[action['res_model']].with_context(action['context'])).save() + wizard.process_cancel_backorder() + + self.assertEqual(self.picking_client_1.state, 'done') + self.assertEqual(self.picking_client_2.state, 'done') + self.assertEqual(self.batch.picking_ids, self.picking_client_1 | self.picking_client_2) + self.assertRecordValues(self.batch.move_ids.sorted('id'), [ + {'product_id': self.productA.id, 'product_uom_qty': 10.0, 'quantity_done': 10.0, 'state': 'done'}, + {'product_id': self.productB.id, 'product_uom_qty': 7.0, 'quantity_done': 7.0, 'state': 'done'}, + {'product_id': self.productB.id, 'product_uom_qty': 3.0, 'quantity_done': 0.0, 'state': 'cancel'}, + ]) @tagged('-at_install', 'post_install') class TestBatchPicking02(TransactionCase):