[FIX] sale_mrp: decrease SOL qty with kit after return
If a user decrease the SOL qty of a kit once the return is done, an unexpcted picking will be created To reproduce the issue: 1. Create a kit K: - With one component - There is one component in the stock 2. Create and confirm a SO with 1 x K 3. Process the delivery 4. Return the delivery 5. Set the SOL qty to 0 Error: a useless and unexpected picking is created to return the kit When setting a new SOL qty, we check if we should create and run a new procurement to adapt the qty on delivery-side. To do so, we get the qty being delivered, and we compute the difference with the new SOL qty. Here is the issue, when getting the qty being delivered, it returns the old SOL qty (1.0): https://github.com/odoo/odoo/blob/797f67bcb222142b79a9eee3e4a882ff679af9db/addons/sale_mrp/models/sale.py#L164-L167 This is not true. Since the kit has been returned, the qty being delivered is zero. As a result, a procurement is created to compensate that incorrect qty. The incorrect return value (1.0) comes from [1]. However, that fix is not correct. Here was the initial issue: To get the qty being delivered, we use the moves of the components, include the BoM lines qties and try to compote the kit quantity. To do so, we call `_compute_kit_quantities` with the components' moves and the kit qty. However, there is an error: the moves are based on the old kit quantity, and we give the new kit quantity to the method. This is why the result was incorrect in the use case of [1]. [1] bafad58353c562a9b79735640c23d3cca004e0b0 OPW-3077209 closes odoo/odoo#113438 X-original-commit: 0b8af1d63d496489ac1608141c9b2dbccc8b7966 Signed-off-by: Adrien Widart <awt@odoo.com> Signed-off-by: Tiffany Chang <tic@odoo.com>
This commit is contained in:
@@ -119,14 +119,13 @@ class SaleOrderLine(models.Model):
|
||||
# kits that are currently in delivery
|
||||
bom = self.env['mrp.bom']._bom_find(self.product_id, bom_type='phantom')[self.product_id]
|
||||
if bom:
|
||||
if not self.product_uom_qty or (previous_product_uom_qty and not previous_product_uom_qty.get(self.id, 0.0)):
|
||||
return (previous_product_uom_qty and previous_product_uom_qty.get(self.id, 0.0)) or self.qty_delivered
|
||||
moves = self.move_ids.filtered(lambda r: r.state != 'cancel' and not r.scrapped)
|
||||
filters = {
|
||||
'incoming_moves': lambda m: m.location_dest_id.usage == 'customer' and (not m.origin_returned_move_id or (m.origin_returned_move_id and m.to_refund)),
|
||||
'outgoing_moves': lambda m: m.location_dest_id.usage != 'customer' and m.to_refund
|
||||
}
|
||||
order_qty = self.product_uom._compute_quantity(self.product_uom_qty, bom.product_uom_id)
|
||||
order_qty = previous_product_uom_qty.get(self.id, 0) if previous_product_uom_qty else self.product_uom_qty
|
||||
order_qty = self.product_uom._compute_quantity(order_qty, bom.product_uom_id)
|
||||
qty = moves._compute_kit_quantities(self.product_id, order_qty, bom, filters)
|
||||
return bom.product_uom_id._compute_quantity(qty, self.product_uom)
|
||||
return super(SaleOrderLine, self)._get_qty_procurement(previous_product_uom_qty=previous_product_uom_qty)
|
||||
|
||||
@@ -2110,3 +2110,52 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
|
||||
{'product_id': self.component_f.id, 'product_uom_qty': 0},
|
||||
{'product_id': self.component_g.id, 'product_uom_qty': 0},
|
||||
])
|
||||
|
||||
def test_kit_return_and_decrease_sol_qty_to_zero(self):
|
||||
"""
|
||||
Create and confirm a SO with a kit product.
|
||||
Deliver & Return the components
|
||||
Set the SOL qty to 0
|
||||
"""
|
||||
stock_location = self.company_data['default_warehouse'].lot_stock_id
|
||||
|
||||
grp_uom = self.env.ref('uom.group_uom')
|
||||
self.env.user.write({'groups_id': [(4, grp_uom.id)]})
|
||||
|
||||
# 10 kit_3 = 10 x compo_f + 20 x compo_g
|
||||
self.env['stock.quant']._update_available_quantity(self.component_f, stock_location, 10)
|
||||
self.env['stock.quant']._update_available_quantity(self.component_g, stock_location, 20)
|
||||
|
||||
so_form = Form(self.env['sale.order'])
|
||||
so_form.partner_id = self.partner_a
|
||||
with so_form.order_line.new() as line:
|
||||
line.product_id = self.kit_3
|
||||
line.product_uom_qty = 2
|
||||
line.product_uom = self.uom_ten
|
||||
so = so_form.save()
|
||||
so.action_confirm()
|
||||
|
||||
delivery = so.picking_ids
|
||||
for m in delivery.move_ids:
|
||||
m.quantity_done = m.product_uom_qty
|
||||
delivery.button_validate()
|
||||
|
||||
self.assertEqual(delivery.state, 'done')
|
||||
self.assertEqual(so.order_line.qty_delivered, 2)
|
||||
|
||||
ctx = {'active_id': delivery.id, 'active_model': 'stock.picking'}
|
||||
return_wizard = Form(self.env['stock.return.picking'].with_context(ctx)).save()
|
||||
return_picking_id, dummy = return_wizard._create_returns()
|
||||
return_picking = self.env['stock.picking'].browse(return_picking_id)
|
||||
for m in return_picking.move_ids:
|
||||
m.quantity_done = m.product_uom_qty
|
||||
return_picking.button_validate()
|
||||
|
||||
self.assertEqual(return_picking.state, 'done')
|
||||
self.assertEqual(so.order_line.qty_delivered, 0)
|
||||
|
||||
with Form(so) as so_form:
|
||||
with so_form.order_line.edit(0) as line:
|
||||
line.product_uom_qty = 0
|
||||
|
||||
self.assertEqual(so.picking_ids, delivery | return_picking)
|
||||
|
||||
Reference in New Issue
Block a user