[FIX] stock: create orderpoints faster

This commit changes the computation of product having a negative forecasted
quantity to create manual orderpoints. The issue was that each replenish
location needed multiple `_read_group` on `stock.quant` and `stock.move`
on all storable product.

This commit makes only 3 `_read_group`s for all products x locations and
post process the group and quantity sum by location in Python.

This method gives some performance gain in time as well as in memory
consumption

closes odoo/odoo#156507

Task: 3653272
X-original-commit: 1a061791e68a524480b4f230031af25411603606
Signed-off-by: Arnold Moyaux (arm) <arm@odoo.com>
Signed-off-by: William Henrotin (whe) <whe@odoo.com>
This commit is contained in:
William Henrotin
2024-03-05 19:21:37 +00:00
parent ca1b0921d6
commit b718dbd744
+36 -8
View File
@@ -349,15 +349,43 @@ class StockWarehouseOrderpoint(models.Model):
all_replenish_location_ids = self._get_orderpoint_locations()
ploc_per_day = defaultdict(set)
# For each replenish location get products with negative virtual_available aka forecast
Move = self.env['stock.move'].with_context(active_test=False)
Quant = self.env['stock.quant'].with_context(active_test=False)
domain_quant, domain_move_in_loc, domain_move_out_loc = all_product_ids._get_domain_locations_new(all_replenish_location_ids.ids)
domain_state = [('state', 'in', ('waiting', 'confirmed', 'assigned', 'partially_available'))]
domain_product = [['product_id', 'in', all_product_ids.ids]]
domain_quant = expression.AND([domain_product, domain_quant])
domain_move_in = expression.AND([domain_product, domain_state, domain_move_in_loc])
domain_move_out = expression.AND([domain_product, domain_state, domain_move_out_loc])
moves_in = defaultdict(list)
for item in Move._read_group(domain_move_in, ['product_id', 'location_dest_id'], ['product_qty:sum']):
moves_in[item[0]].append((item[1], item[2]))
moves_out = defaultdict(list)
for item in Move._read_group(domain_move_out, ['product_id', 'location_id'], ['product_qty:sum']):
moves_out[item[0]].append((item[1], item[2]))
quants = defaultdict(list)
for item in Quant._read_group(domain_quant, ['product_id', 'location_id'], ['quantity:sum']):
quants[item[0]].append((item[1], item[2]))
rounding = {product.id: product.uom_id.rounding for product in all_product_ids}
path = {loc: loc.parent_path for loc in self.env['stock.location'].search([('id', 'child_of', all_replenish_location_ids.ids)])}
for loc in all_replenish_location_ids:
for product in all_product_ids.with_context(location=loc.id):
if float_compare(product.virtual_available, 0, precision_rounding=product.uom_id.rounding) >= 0:
continue
# group product by lead_days and location in order to read virtual_available
# in batch
rules = product._get_rules_from_location(loc)
lead_days = rules.with_context(bypass_delay_description=True)._get_lead_days(product)[0]['total_delay']
ploc_per_day[(lead_days, loc)].add(product.id)
for product in all_product_ids:
qty_available = sum(q[1] for q in quants.get(product, [(0, 0)]) if q[0] and path[q[0]] in loc.parent_path)
incoming_qty = sum(m[1] for m in moves_in.get(product, [(0, 0)]) if m[0] and path[m[0]] in loc.parent_path)
outgoing_qty = sum(m[1] for m in moves_out.get(product, [(0, 0)]) if m[0] and path[m[0]] in loc.parent_path)
if float_compare(qty_available + incoming_qty - outgoing_qty, 0, precision_rounding=rounding[product.id]) < 0:
# group product by lead_days and location in order to read virtual_available
# in batch
rules = product._get_rules_from_location(loc)
lead_days = rules.with_context(bypass_delay_description=True)._get_lead_days(product)[0]['total_delay']
ploc_per_day[(lead_days, loc)].add(product.id)
# recompute virtual_available with lead days
today = fields.datetime.now().replace(hour=23, minute=59, second=59)