mirror of
https://github.com/OCA/stock-logistics-warehouse.git
synced 2025-01-21 14:27:28 +02:00
Improve performance of stock available of BoMs
Use an optimized method to explode the BoM. The explode methods include data and a cycle check that we don't need here. Besides, it calls '_bom_find' on every bom line of the graph, which generates thousands of SELECT queries on large BoMs.
This commit is contained in:
committed by
Cédric Pigeon
parent
a8c547d22c
commit
5dc3fc08a1
@@ -5,6 +5,7 @@ from collections import Counter
|
|||||||
|
|
||||||
from odoo import api, fields, models
|
from odoo import api, fields, models
|
||||||
from odoo.fields import first
|
from odoo.fields import first
|
||||||
|
from odoo.tools import float_round
|
||||||
|
|
||||||
|
|
||||||
class ProductProduct(models.Model):
|
class ProductProduct(models.Model):
|
||||||
@@ -82,7 +83,8 @@ class ProductProduct(models.Model):
|
|||||||
for product in product_with_bom:
|
for product in product_with_bom:
|
||||||
# Need by product (same product can be in many BOM lines/levels)
|
# Need by product (same product can be in many BOM lines/levels)
|
||||||
exploded_components = exploded_boms[product.id]
|
exploded_components = exploded_boms[product.id]
|
||||||
component_needs = product._get_components_needs(exploded_components)
|
component_needs = product._get_components_needs(
|
||||||
|
exploded_components)
|
||||||
if not component_needs:
|
if not component_needs:
|
||||||
# The BoM has no line we can use
|
# The BoM has no line we can use
|
||||||
potential_qty = immediately_usable_qty = 0.0
|
potential_qty = immediately_usable_qty = 0.0
|
||||||
@@ -119,10 +121,7 @@ class ProductProduct(models.Model):
|
|||||||
return a dict by product_id of exploded bom lines
|
return a dict by product_id of exploded bom lines
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
exploded_boms = {}
|
return self.explode_bom_quantities()
|
||||||
for rec in self:
|
|
||||||
exploded_boms[rec.id] = rec.bom_id.explode(rec, 1.0)[1]
|
|
||||||
return exploded_boms
|
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def _get_components_needs(self, exploded_components):
|
def _get_components_needs(self, exploded_components):
|
||||||
@@ -132,8 +131,79 @@ class ProductProduct(models.Model):
|
|||||||
:rtype: collections.Counter
|
:rtype: collections.Counter
|
||||||
"""
|
"""
|
||||||
needs = Counter()
|
needs = Counter()
|
||||||
for bom_component in exploded_components:
|
for bom_line, bom_qty in exploded_components:
|
||||||
component = bom_component[0].product_id
|
component = bom_line.product_id
|
||||||
needs += Counter({component: bom_component[1]["qty"]})
|
needs += Counter({component: bom_qty})
|
||||||
|
|
||||||
return needs
|
return needs
|
||||||
|
|
||||||
|
def explode_bom_quantities(self):
|
||||||
|
"""Explode a bill of material with quantities to consume
|
||||||
|
|
||||||
|
It returns a dict with the exploded bom lines and
|
||||||
|
the quantity they consume. Example::
|
||||||
|
|
||||||
|
{
|
||||||
|
<product-id>: [
|
||||||
|
(<bom-line-id>, <quantity>)
|
||||||
|
(<bom-line-id>, <quantity>)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
The 'MrpBom.explode()' method includes the same information, with other
|
||||||
|
things, but is under-optimized to be used for the purpose of this
|
||||||
|
module. The killer is particularly the call to `_bom_find()` which can
|
||||||
|
generate thousands of SELECT for searches.
|
||||||
|
"""
|
||||||
|
result = {}
|
||||||
|
|
||||||
|
for product in self:
|
||||||
|
lines_done = []
|
||||||
|
bom_lines = [
|
||||||
|
(product.bom_id, bom_line, product, 1.0)
|
||||||
|
for bom_line in product.bom_id.bom_line_ids
|
||||||
|
]
|
||||||
|
|
||||||
|
while bom_lines:
|
||||||
|
(current_bom,
|
||||||
|
current_line,
|
||||||
|
current_product,
|
||||||
|
current_qty) = bom_lines[0]
|
||||||
|
bom_lines = bom_lines[1:]
|
||||||
|
|
||||||
|
if current_line._skip_bom_line(current_product):
|
||||||
|
continue
|
||||||
|
|
||||||
|
line_quantity = current_qty * current_line.product_qty
|
||||||
|
|
||||||
|
sub_bom = current_line.product_id.bom_id
|
||||||
|
if sub_bom.type == 'phantom':
|
||||||
|
product_uom = current_line.product_uom_id
|
||||||
|
converted_line_quantity = product_uom._compute_quantity(
|
||||||
|
line_quantity / sub_bom.product_qty,
|
||||||
|
sub_bom.product_uom_id,
|
||||||
|
)
|
||||||
|
bom_lines = [
|
||||||
|
(
|
||||||
|
sub_bom,
|
||||||
|
line,
|
||||||
|
current_line.product_id,
|
||||||
|
converted_line_quantity,
|
||||||
|
)
|
||||||
|
for line in sub_bom.bom_line_ids
|
||||||
|
] + bom_lines
|
||||||
|
else:
|
||||||
|
# We round up here because the user expects that if he has
|
||||||
|
# to consume a little more, the whole UOM unit should be
|
||||||
|
# consumed.
|
||||||
|
rounding = current_line.product_uom_id.rounding
|
||||||
|
line_quantity = float_round(
|
||||||
|
line_quantity,
|
||||||
|
precision_rounding=rounding,
|
||||||
|
rounding_method='UP',
|
||||||
|
)
|
||||||
|
lines_done.append((current_line, line_quantity))
|
||||||
|
|
||||||
|
result[product.id] = lines_done
|
||||||
|
|
||||||
|
return result
|
||||||
|
|||||||
Reference in New Issue
Block a user