[FIX] delivery: read_group fest speeding up weights compute

Currently _compute_bulk_weight and _compute_weight are
going through each picking in self and each stock_move_line
in picking.move_line_ids to compute the pickings' weight.

This can be slow when there are lots of move_lines by pickings
as the field cache will be filled by the move_lines records
and uom._compute_quantity will be called once by move_line.

This is especially true for pickings with SN-tracked products.
For SN tracked products there will be one move_line by product_qty
(so a picking with 1 SN tracked product with a qty of 100 will have
 100 move_lines). In this case doing a read_group yields the highest
speedup.

Following the same reasoning a search_count is done in _compute_packages
before retrieving package.move_line_ids.
When package.move_line_ids.result_package_id is empty doing a count
is much faster as it avoids calling _in_cache_without for the package
move_line_ids. The search_count overhead is negligeable in the other
case so adding it leads to an overall speedup on average.

opw-3017013

closes odoo/odoo#107994

X-original-commit: c95abbe8fa09decca2f94a035934e8d6ae7903e1
Signed-off-by: William Henrotin (whe) <whe@odoo.com>
Signed-off-by: Van Delft Aurélien (avd) <avd@odoo.com>
This commit is contained in:
Aurelien van Delft (avd)
2022-12-14 22:04:20 +01:00
parent 4d719a4ef8
commit 81773cd70f
+41 -18
View File
@@ -2,6 +2,7 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import json
from collections import defaultdict
from odoo import models, fields, api, _
from odoo.exceptions import UserError
@@ -13,22 +14,31 @@ class StockQuantPackage(models.Model):
@api.depends('quant_ids', 'package_type_id')
def _compute_weight(self):
if self.env.context.get('picking_id'):
package_weights = defaultdict(float)
# Ordering by qty_done prevents the default ordering by groupby fields that can inject multiple Left Joins in the resulting query.
res_groups = self.env['stock.move.line'].read_group(
[('result_package_id', 'in', self.ids), ('product_id', '!=', False), ('picking_id', '=', self.env.context['picking_id'])],
['id:count'],
['result_package_id', 'product_id', 'product_uom_id', 'qty_done'],
lazy=False, orderby='qty_done asc'
)
for res_group in res_groups:
product_id = self.env['product.product'].browse(res_group['product_id'][0])
product_uom_id = self.env['uom.uom'].browse(res_group['product_uom_id'][0])
package_weights[res_group['result_package_id'][0]] += (
res_group['__count']
* product_uom_id._compute_quantity(res_group['qty_done'], product_id.uom_id)
* product_id.weight
)
for package in self:
# set initial weight to package type base weight
weight = package.package_type_id.base_weight or 0.0
if self.env.context.get('picking_id'):
# TODO: potential bottleneck: N packages = N queries, use groupby ?
current_picking_move_line_ids = self.env['stock.move.line'].search([
('result_package_id', '=', package.id),
('picking_id', '=', self.env.context['picking_id'])
])
for ml in current_picking_move_line_ids:
weight += ml.product_uom_id._compute_quantity(
ml.qty_done, ml.product_id.uom_id) * ml.product_id.weight
package.weight = weight + package_weights[package.id]
else:
for quant in package.quant_ids:
weight += quant.quantity * quant.product_id.weight
package.weight = weight
package.weight = weight
def _get_default_weight_uom(self):
return self.env['product.template']._get_weight_uom_name_from_ir_config_parameter()
@@ -59,19 +69,32 @@ class StockPicking(models.Model):
def _compute_packages(self):
for package in self:
packs = set()
for move_line in package.move_line_ids:
if move_line.result_package_id:
packs.add(move_line.result_package_id.id)
if self.env['stock.move.line'].search_count([('picking_id', '=', package.id), ('result_package_id', '!=', False)]):
for move_line in package.move_line_ids:
if move_line.result_package_id:
packs.add(move_line.result_package_id.id)
package.package_ids = list(packs)
@api.depends('move_line_ids', 'move_line_ids.result_package_id', 'move_line_ids.product_uom_id', 'move_line_ids.qty_done')
def _compute_bulk_weight(self):
picking_weights = defaultdict(float)
# Ordering by qty_done prevents the default ordering by groupby fields that can inject multiple Left Joins in the resulting query.
res_groups = self.env['stock.move.line'].read_group(
[('picking_id', 'in', self.ids), ('product_id', '!=', False), ('result_package_id', '=', False)],
['id:count'],
['picking_id', 'product_id', 'product_uom_id', 'qty_done'],
lazy=False, orderby='qty_done asc'
)
for res_group in res_groups:
product_id = self.env['product.product'].browse(res_group['product_id'][0])
product_uom_id = self.env['uom.uom'].browse(res_group['product_uom_id'][0])
picking_weights[res_group['picking_id'][0]] += (
res_group['__count']
* product_uom_id._compute_quantity(res_group['qty_done'], product_id.uom_id)
* product_id.weight
)
for picking in self:
weight = 0.0
for move_line in picking.move_line_ids:
if move_line.product_id and not move_line.result_package_id:
weight += move_line.product_uom_id._compute_quantity(move_line.qty_done, move_line.product_id.uom_id) * move_line.product_id.weight
picking.weight_bulk = weight
picking.weight_bulk = picking_weights[picking.id]
@api.depends('move_line_ids.result_package_id', 'move_line_ids.result_package_id.shipping_weight', 'weight_bulk')
def _compute_shipping_weight(self):