From 2e527b4caced7a191a08d352b0d08b3f4d983ac7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20=28avd=29?= Date: Wed, 31 Mar 2021 15:20:49 +0000 Subject: [PATCH] [FIX] mrp: improve perf of explode Batch the bom_search of bom_lines in mrp_bom.explode. If we represent the bom hierarchy as a graph, essentially switch from a depth-first to a breadth-first search, allowing to batch search boms for each depth level, hence improving the overall performance. closes odoo/odoo#71137 X-original-commit: 44ae26e4477e1b76b3dbb0f1acd50e8553b14d71 Signed-off-by: William Henrotin --- addons/mrp/models/mrp_bom.py | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/addons/mrp/models/mrp_bom.py b/addons/mrp/models/mrp_bom.py index 76dbe43bb20..a47848b2a1d 100644 --- a/addons/mrp/models/mrp_bom.py +++ b/addons/mrp/models/mrp_bom.py @@ -234,14 +234,29 @@ class MrpBom(models.Model): recStack[v] = False return False + product_ids = set() + product_boms = {} + def update_product_boms(): + products = self.env['product.product'].browse(product_ids) + product_boms.update(self._bom_find(products, picking_type=picking_type or self.picking_type_id, + company_id=self.company_id.id, bom_type='phantom')) + # Set missing keys to default value + for product in products: + product_boms.setdefault(product, self.env['mrp.bom']) + boms_done = [(self, {'qty': quantity, 'product': product, 'original_qty': quantity, 'parent_line': False})] lines_done = [] V |= set([product.product_tmpl_id.id]) - bom_lines = [(bom_line, product, quantity, False) for bom_line in self.bom_line_ids] + bom_lines = [] for bom_line in self.bom_line_ids: - V |= set([bom_line.product_id.product_tmpl_id.id]) - graph[product.product_tmpl_id.id].append(bom_line.product_id.product_tmpl_id.id) + product_id = bom_line.product_id + V |= set([product_id.product_tmpl_id.id]) + graph[product.product_tmpl_id.id].append(product_id.product_tmpl_id.id) + bom_lines.append((bom_line, product, quantity, False)) + product_ids.add(product_id.id) + update_product_boms() + product_ids.clear() while bom_lines: current_line, current_product, current_qty, parent_line = bom_lines[0] bom_lines = bom_lines[1:] @@ -250,15 +265,20 @@ class MrpBom(models.Model): continue line_quantity = current_qty * current_line.product_qty - bom = self._bom_find(current_line.product_id, picking_type=picking_type or self.picking_type_id, company_id=self.company_id.id, bom_type='phantom')[current_line.product_id] + if not current_line.product_id in product_boms: + update_product_boms() + product_ids.clear() + bom = product_boms.get(current_line.product_id) if bom: converted_line_quantity = current_line.product_uom_id._compute_quantity(line_quantity / bom.product_qty, bom.product_uom_id) - bom_lines = [(line, current_line.product_id, converted_line_quantity, current_line) for line in bom.bom_line_ids] + bom_lines + bom_lines += [(line, current_line.product_id, converted_line_quantity, current_line) for line in bom.bom_line_ids] for bom_line in bom.bom_line_ids: graph[current_line.product_id.product_tmpl_id.id].append(bom_line.product_id.product_tmpl_id.id) if bom_line.product_id.product_tmpl_id.id in V and check_cycle(bom_line.product_id.product_tmpl_id.id, {key: False for key in V}, {key: False for key in V}, graph): raise UserError(_('Recursion error! A product with a Bill of Material should not have itself in its BoM or child BoMs!')) V |= set([bom_line.product_id.product_tmpl_id.id]) + if not bom_line.product_id in product_boms: + product_ids.add(bom_line.product_id.id) boms_done.append((bom, {'qty': converted_line_quantity, 'product': current_product, 'original_qty': quantity, 'parent_line': current_line})) else: # We round up here because the user expects that if he has to consume a little more, the whole UOM unit