[FIX] {sale_,}purchase_stock: handle extra qty on MTO SM

To reproduce the issue:
1. In Settings, enable "Multi Routes"
2. In Routes, unarchive MTO
3. Create a product P:
   - Storable
   - With a vendor
   - Routes: MTO + Buy
4. Create and confirm a SO for 10 x P
5. On the delivery, set the done quantity to 12
6. Validate the delivery

Error: The state of the delivery is set to _done_ but the SM has
changed, its demand is 2 and the done quantity is 0. Moreover, a
backorder has been created

Since [1], extra moves are supposed to be merged with the initial
one. However, there is an issue with the above case. The initial SM
has generated a POL (the product is an MTO-buy one). However, since
the extra move is an MTS one, it does not generate any POL. As a
result, the SMs don't have the same value for the field
`created_purchase_line_ids`. This is an issue because for two SM to
be merged, this field must be the same on both SM:
https://github.com/odoo/odoo/blob/ef3c21255c6f2b1172be3b1f8f0dc83bc276d806/addons/purchase_stock/models/stock_move.py#L20-L24
As a result, the SMs are not merged, the method `_create_extra_move`
only returns the extra move, the initial one is lost and it leads to
an unexpected behaviour.

We should also copy the values of `created_purchase_line_ids` to
ensure the merged of both initial and extra moves.

[1] f9867a5fa5

OPW-3504138

closes odoo/odoo#137869

Related: odoo/enterprise#48547
Signed-off-by: Djamel Touati (otd) <otd@odoo.com>
Signed-off-by: Adrien Widart (awt) <awt@odoo.com>
This commit is contained in:
Adrien Widart (awt)
2023-10-24 12:38:18 +00:00
parent 6bec356e56
commit dab82509fa
2 changed files with 63 additions and 19 deletions
@@ -150,6 +150,7 @@ class StockMove(models.Model):
def _prepare_extra_move_vals(self, qty):
vals = super(StockMove, self)._prepare_extra_move_vals(qty)
vals['purchase_line_id'] = self.purchase_line_id.id
vals['created_purchase_line_ids'] = [Command.set(self.created_purchase_line_ids.ids)]
return vals
def _prepare_move_split_vals(self, uom_qty):
@@ -6,38 +6,81 @@ from odoo.tests.common import TransactionCase, Form
class TestSalePurchaseStockFlow(TransactionCase):
@classmethod
def setUpClass(cls):
super(TestSalePurchaseStockFlow, cls).setUpClass()
cls.mto_route = cls.env.ref('stock.route_warehouse0_mto')
cls.buy_route = cls.env.ref('purchase_stock.route_warehouse0_buy')
cls.mto_route.active = True
cls.customer_location = cls.env.ref('stock.stock_location_customers')
cls.vendor = cls.env['res.partner'].create({'name': 'Super Vendor'})
cls.customer = cls.env['res.partner'].create({'name': 'Super Customer'})
cls.mto_product = cls.env['product.product'].create({
'name': 'SuperProduct',
'type': 'product',
'route_ids': [(6, 0, (cls.mto_route + cls.buy_route).ids)],
'seller_ids': [(0, 0, {
'partner_id': cls.vendor.id,
})],
})
def test_cancel_so_with_draft_po(self):
"""
Sell a MTO+Buy product -> a PO is generated
Cancel the SO -> an activity should be added to the PO
"""
mto_route = self.env.ref('stock.route_warehouse0_mto')
buy_route = self.env.ref('purchase_stock.route_warehouse0_buy')
mto_route.active = True
vendor = self.env['res.partner'].create({
'name': 'Super Vendor'
})
product = self.env['product.product'].create({
'name': 'SuperProduct',
'type': 'product',
'route_ids': [(6, 0, (mto_route + buy_route).ids)],
'seller_ids': [(0, 0, {
'partner_id': vendor.id,
})],
})
so_form = Form(self.env['sale.order'])
so_form.partner_id = self.env.user.partner_id
with so_form.order_line.new() as line:
line.product_id = product
line.product_id = self.mto_product
so = so_form.save()
so.action_confirm()
po = self.env['purchase.order'].search([('partner_id', '=', vendor.id)])
po = self.env['purchase.order'].search([('partner_id', '=', self.vendor.id)])
so._action_cancel()
self.assertTrue(po.activity_ids)
self.assertIn(so.name, po.activity_ids.note)
def test_qty_delivered_with_mto_and_done_quantity_change(self):
"""
MTO product P
Sell 10 x P. On the delivery, set the done quantity to 12, validate and
then set the done quantity to 10: the delivered qty of the SOL should
be 10
"""
so = self.env['sale.order'].create({
'partner_id': self.customer.id,
'order_line': [(0, 0, {
'name': self.mto_product.name,
'product_id': self.mto_product.id,
'product_uom_qty': 10,
'product_uom': self.mto_product.uom_id.id,
'price_unit': 1,
})],
})
so.action_confirm()
delivery = so.picking_ids.filtered(lambda p: p.location_dest_id == self.customer_location)
sm = delivery.move_ids
sm.move_line_ids = [(5, 0, 0), (0, 0, {
'location_id': sm.location_id.id,
'location_dest_id': sm.location_dest_id.id,
'product_id': sm.product_id.id,
'qty_done': 12,
'company_id': sm.company_id.id,
'product_uom_id': sm.product_uom.id,
'picking_id': delivery.id,
})]
delivery.button_validate()
self.assertEqual(delivery.state, 'done')
self.assertEqual(delivery.move_ids.move_line_ids.qty_done, 12)
self.assertEqual(so.order_line.qty_delivered, 12)
sm.move_line_ids.qty_done = 10
self.assertEqual(so.order_line.qty_delivered, 10)