[FIX] sale_mrp : wrong delivered quantities on kit return

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#78915

X-original-commit: 206c9c8da4d8001525a540561174326e49d1188d
Signed-off-by: Arnold Moyaux <arm@odoo.com>
Signed-off-by: William Henrotin (whe) <whe@odoo.com>
This commit is contained in:
Nathan Marotte (nama)
2021-10-28 12:21:23 +00:00
committed by William Henrotin
parent 6a7c1c51ce
commit 707cd830f8
2 changed files with 91 additions and 1 deletions
+2 -1
View File
@@ -114,7 +114,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
@@ -1755,3 +1755,92 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
check_cogs_entry_values(self.invoice_1, 40)
# 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',
})
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})]
})
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_ids.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
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")