From 206c9c8da4d8001525a540561174326e49d1188d Mon Sep 17 00:00:00 2001 From: "Nathan Marotte (nama)" <nama@odoo.com> Date: Wed, 18 Aug 2021 12:49:44 +0000 Subject: [PATCH] [FIX] sale_mrp : wrong delivered quantities on kit return MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue: When returning a product sold that is only made of kits and validating the return, the delivered quantities of the sales order was set back to the full amount delivered Steps to reproduce : 1) Install MRP and Sales 2) Create a BoM Kit for a new product [Nested Kit] that has one or more consumable or storable product as component 3) Create a BoM Kit for a new product [Main Kit] that has [Nested Kit] as component 4) Create a SO for [Main Kit], confirm, validate delivery 5) Check SO, 1 product is delivered (correct) 6) Go back to the Delivery, create a return for the delivery (don't validate) 7) Check SO, 0 product is delivered (correct) 8) Now validate the return for the delivery -> Check SO, 1 product is delivered (bug) Why is that a bug: Since the Main Kit was returned, the delivered should be 0 and not the full amount initially delivered. It was set back to 1 because we didn't look at the type of picking, when computing the quantity delivered, the fall back considered that if all the moves were done, everything was delivered, but it is the opposite when returning opw-2542337 closes odoo/odoo#78838 X-original-commit: e90452bf203bf720cb5532df605eca2cfd085134 Signed-off-by: Arnold Moyaux <arm@odoo.com> Signed-off-by: Rémy Voet <ryv@odoo.com> --- addons/sale_mrp/models/sale.py | 3 +- addons/sale_mrp/tests/test_sale_mrp_flow.py | 88 +++++++++++++++++++++ 2 files changed, 90 insertions(+), 1 deletion(-) diff --git a/addons/sale_mrp/models/sale.py b/addons/sale_mrp/models/sale.py index 2c34ca383a2b..87bf820240a5 100644 --- a/addons/sale_mrp/models/sale.py +++ b/addons/sale_mrp/models/sale.py @@ -109,7 +109,8 @@ class SaleOrderLine(models.Model): # when the product sold is made only of kits. In this case, the BOM of the stock moves # do not correspond to the product sold => no relevant BOM. elif boms: - if all(m.state == 'done' for m in order_line.move_ids): + # if the move is ingoing, the product **sold** has delivered qty 0 + if all(m.state == 'done' and m.location_dest_id.usage == 'customer' for m in order_line.move_ids): order_line.qty_delivered = order_line.product_uom_qty else: order_line.qty_delivered = 0.0 diff --git a/addons/sale_mrp/tests/test_sale_mrp_flow.py b/addons/sale_mrp/tests/test_sale_mrp_flow.py index 6e39cba0d768..8787610642ba 100644 --- a/addons/sale_mrp/tests/test_sale_mrp_flow.py +++ b/addons/sale_mrp/tests/test_sale_mrp_flow.py @@ -1769,3 +1769,91 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon): # Check that the cost of Good Sold entries for variant 2 are equal to 2 * 10 = 20 check_cogs_entry_values(self.invoice_2, 20) + def test_13_so_return_kit(self): + """ + Test that when returning a SO containing only a kit that contains another kit, the + SO delivered quantities is set to 0 (with the all-or-nothing policy). + Products : + Main Kit + Nested Kit + Screw + BoMs : + Main Kit BoM (kit), recipe : + Nested Kit Bom (kit), recipe : + Screw + Business flow : + Create those + Create a Sales order selling one Main Kit BoM + Confirm the sales order + Validate the delivery (outgoing) (qty_delivered = 1) + Create a return for the delivery + Validate return for delivery (ingoing) (qty_delivered = 0) + """ + main_kit_product = self.env['product.product'].create({ + 'name': 'Main Kit', + 'type': 'product', + }) + + nested_kit_product = self.env['product.product'].create({ + 'name': 'Nested Kit', + 'type': 'product', + }) + + product = self.env['product.product'].create({ + 'name': 'Screw', + 'type': 'product', + }) + + nested_kit_bom = self.env['mrp.bom'].create({ + 'product_id': nested_kit_product.id, + 'product_tmpl_id': nested_kit_product.product_tmpl_id.id, + 'product_qty': 1.0, + 'type': 'phantom', + 'bom_line_ids': [(5, 0), (0, 0, {'product_id': product.id})] + }) + + main_bom = self.env['mrp.bom'].create({ + 'product_id': main_kit_product.id, + 'product_tmpl_id': main_kit_product.product_tmpl_id.id, + 'product_qty': 1.0, + 'type': 'phantom', + 'bom_line_ids': [(5, 0), (0, 0, {'product_id': nested_kit_product.id})] + }) + + # Create a SO for product Main Kit Product + order_form = Form(self.env['sale.order']) + order_form.partner_id = self.env.ref('base.res_partner_2') + with order_form.order_line.new() as line: + line.product_id = main_kit_product + line.product_uom_qty = 1 + order = order_form.save() + order.action_confirm() + qty_del_not_yet_validated = sum(sol.qty_delivered for sol in order.order_line) + self.assertEqual(qty_del_not_yet_validated, 0.0, 'No delivery validated yet') + + # Validate delivery + pick = order.picking_ids + pick.move_lines.write({'quantity_done': 1}) + pick.button_validate() + qty_del_validated = sum(sol.qty_delivered for sol in order.order_line) + self.assertEqual(qty_del_validated, 1.0, 'The order went from warehouse to client, so it has been delivered') + + # 1 was delivered, now create a return + stock_return_picking_form = Form(self.env['stock.return.picking'].with_context( + active_ids=pick.ids, active_id=pick.ids[0], active_model='stock.picking')) + return_wiz = stock_return_picking_form.save() + for return_move in return_wiz.product_return_moves: + return_move.write({ + 'quantity': 1, + 'to_refund': True + }) + res = return_wiz.create_returns() + return_pick = self.env['stock.picking'].browse(res['res_id']) + return_pick.move_line_ids.qty_done = 1 + wiz_act = return_pick.button_validate() # validate return + + # Delivered quantities to the client should be 0 + qty_del_return_validated = sum(sol.qty_delivered for sol in order.order_line) + self.assertNotEqual(qty_del_return_validated, 1.0, "The return was validated, therefore the delivery from client to" + " company was successful, and the client is left without his 1 product.") + self.assertEqual(qty_del_return_validated, 0.0, "The return has processed, client doesn't have any quantity anymore") -- GitLab