[FIX] stock,sale_mrp: avoid post-process scheduler if no procurement

When the post-production location is a child of the stock location,
running the scheduler may lead to a recursion error.

To reproduce the issue:
(Enable debug mode. Use demo data)
1. In Settings, enable "Multi-Step Routes"
2. Edit the warehouse:
    - Manufacture: 3 steps
3. In the locations, edit WH/Post-Production:
    - Parent Location: WH/Stock
4. Create two products P_finished, P_compo:
    - P_finished:
        - Storable
        - Route Manufacture
5. Create a reordering rule RR for P_finished:
    - Location: WH/Stock
    - Min: 0
    - Max: 0
6. Create a BoM:
    - Product: P_finished
    - Type: Manufacture
    - Components: 1 x P_compo
7. Create a sale order SO with 1 x P_finished
8. Confirm SO

Error: an Odoo Server Error is displayed: "RecursionError"

When confirming the SO, it leads to the creation and the confirmation of
the delivery. When confirming the delivery, we trigger the scheduler:
because of the delivery, there is a need in WH/Stock. Moreover, there is
an reordering rule RR for that product and that location. Therefore, we
create and process a procurement and this will create the manufacturing
order for P_finished. Then, the post-process of the scheduler is called:
1. We find the draft MO related to the RR and confirm it:
https://github.com/odoo/odoo/blob/eabf5cc14b4c731f112e6c78b579ef8e98edcaad/addons/mrp/models/stock_orderpoint.py#L121-L125
2. When confirming the MO, we first try to confirm the associated
pickings (from Pre-Prod to Stock and from Post-Prod to Stock). Note that
we will update the state of the MO after the pickings confirmation:
https://github.com/odoo/odoo/blob/14431b2497dcf4aaf778ef1b06e023101b00a4e2/addons/mrp/models/mrp_production.py#L1204-L1208
3. When confirming a picking, we trigger the scheduler:
https://github.com/odoo/odoo/blob/fcc74186330c8df37cfac08455bd7bba44d4656b/addons/stock/models/stock_picking.py#L790-L791
4. We have two pickings, each one with one stock move: the first one
with 1 x P_compo from Pre-Prod to Stock, and a second one with 1 x
P_finished from Post-Prod to Stock. When triggering the scheduler, we
try to find a RR that fulfill the need of each SM:
https://github.com/odoo/odoo/blob/0183298293192538a801f52262c047ea34a1b76a/addons/stock/models/stock_move.py#L1949-L1954
The location of the created reordering rule is WH/Stock. The source
location of the second stock move is WH/Stock/Post-Production, i.e. a
child of WH/Stock. Therefore, we find the reordering rule we created and
we try to process an associated procurement:
https://github.com/odoo/odoo/blob/0183298293192538a801f52262c047ea34a1b76a/addons/stock/models/stock_move.py#L1961-L1962
5. Because there isn't any quantity to order, we don't create and
process any procurement:
https://github.com/odoo/odoo/blob/029da238a2baaf59bdf9ae828a4739ee4a9f3ede/addons/stock/models/stock_orderpoint.py#L519-L525
However, we still call the post-process of the scheduler:
https://github.com/odoo/odoo/blob/029da238a2baaf59bdf9ae828a4739ee4a9f3ede/addons/stock/models/stock_orderpoint.py#L548-L549
This brings us back to the step 1 => Recursion !

OPW-2926657

closes odoo/odoo#97646

X-original-commit: c06e1ec755206e8dd8aff6b341151848e6d173c7
Signed-off-by: William Henrotin (whe) <whe@odoo.com>
Signed-off-by: Adrien Widart <awt@odoo.com>
This commit is contained in:
Adrien Widart
2022-08-08 23:45:39 +02:00
parent 7d0eb87eea
commit 3e8a45fce0
2 changed files with 60 additions and 1 deletions
@@ -181,3 +181,61 @@ class TestSaleMrpProcurement(TransactionCase):
# ...with two products
self.assertEqual(len(pickings[0].move_ids), 2)
def test_post_prod_location_child_of_stock_location(self):
"""
3-steps manufacturing, the post-prod location is a child of the stock
location. Have a manufactured product with the manufacture route and a
RR min=max=0. Confirm a SO with that product -> It should generate a MO
"""
warehouse = self.env['stock.warehouse'].search([('company_id', '=', self.env.company.id)], limit=1)
manufacture_route = warehouse.manufacture_pull_id.route_id
warehouse.manufacture_steps = 'pbm_sam'
warehouse.sam_loc_id.location_id = warehouse.lot_stock_id
product, component = self.env['product.product'].create([{
'name': 'Finished',
'type': 'product',
'route_ids': [(6, 0, manufacture_route.ids)],
}, {
'name': 'Component',
'type': 'consu',
}])
self.env['mrp.bom'].create({
'product_id': product.id,
'product_tmpl_id': product.product_tmpl_id.id,
'product_uom_id': product.uom_id.id,
'product_qty': 1.0,
'type': 'normal',
'bom_line_ids': [
(0, 0, {'product_id': component.id, 'product_qty': 1}),
],
})
self.env['stock.warehouse.orderpoint'].create({
'name': product.name,
'location_id': warehouse.lot_stock_id.id,
'product_id': product.id,
'product_min_qty': 0,
'product_max_qty': 0,
'trigger': 'auto',
})
so = self.env['sale.order'].create({
'partner_id': self.env['res.partner'].create({'name': 'Super Partner'}).id,
'order_line': [
(0, 0, {
'name': product.name,
'product_id': product.id,
'product_uom_qty': 1.0,
'product_uom': product.uom_id.id,
'price_unit': 1,
})],
})
so.action_confirm()
self.assertEqual(so.state, 'sale')
mo = self.env['mrp.production'].search([('product_id', '=', product.id)], order='id desc', limit=1)
self.assertIn(so.name, mo.origin)
+2 -1
View File
@@ -1977,7 +1977,8 @@ class StockMove(models.Model):
('product_id', '=', move.product_id.id),
('trigger', '=', 'auto'),
('location_id', 'parent_of', move.location_id.id),
('company_id', '=', move.company_id.id)
('company_id', '=', move.company_id.id),
'!', ('location_id', 'parent_of', move.location_dest_id.id),
], limit=1)
if orderpoint:
orderpoints_by_company[orderpoint.company_id] |= orderpoint