[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:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user