[FIX] stock, purchase: performance in Replenishement report
Forward port of #70351 closes odoo/odoo#70557 Related: odoo/enterprise#18215 Signed-off-by: Arnold Moyaux <amoyaux@users.noreply.github.com> Signed-off-by: William Henrotin <Whenrow@users.noreply.github.com>
This commit is contained in:
@@ -119,16 +119,19 @@ class StockRule(models.Model):
|
||||
and cumulative description.
|
||||
"""
|
||||
delay, delay_description = super()._get_lead_days(product)
|
||||
bypass_delay_description = self.env.context.get('bypass_delay_description')
|
||||
manufacture_rule = self.filtered(lambda r: r.action == 'manufacture')
|
||||
if not manufacture_rule:
|
||||
return delay, delay_description
|
||||
manufacture_rule.ensure_one()
|
||||
manufacture_delay = product.produce_delay
|
||||
delay += manufacture_delay
|
||||
delay_description += '<tr><td>%s</td><td class="text-right">+ %d %s</td></tr>' % (_('Manufacturing Lead Time'), manufacture_delay, _('day(s)'))
|
||||
if not bypass_delay_description:
|
||||
delay_description += '<tr><td>%s</td><td class="text-right">+ %d %s</td></tr>' % (_('Manufacturing Lead Time'), manufacture_delay, _('day(s)'))
|
||||
security_delay = manufacture_rule.picking_type_id.company_id.manufacturing_lead
|
||||
delay += security_delay
|
||||
delay_description += '<tr><td>%s</td><td class="text-right">+ %d %s</td></tr>' % (_('Manufacture Security Lead Time'), security_delay, _('day(s)'))
|
||||
if not bypass_delay_description:
|
||||
delay_description += '<tr><td>%s</td><td class="text-right">+ %d %s</td></tr>' % (_('Manufacture Security Lead Time'), security_delay, _('day(s)'))
|
||||
return delay, delay_description
|
||||
|
||||
def _push_prepare_move_copy_values(self, move_to_copy, new_date):
|
||||
|
||||
@@ -598,9 +598,7 @@ class ProductProduct(models.Model):
|
||||
'target': 'new'}
|
||||
|
||||
def _prepare_sellers(self, params=False):
|
||||
# This search is made to avoid retrieving seller_ids from the cache.
|
||||
return self.env['product.supplierinfo'].search([('product_tmpl_id', '=', self.product_tmpl_id.id),
|
||||
('name.active', '=', True)]).sorted(lambda s: (s.sequence, -s.min_qty, s.price, s.id))
|
||||
return self.seller_ids.filtered(lambda s: s.name.active).sorted(lambda s: (s.sequence, -s.min_qty, s.price, s.id))
|
||||
|
||||
def _select_seller(self, partner_id=False, quantity=0.0, date=None, uom_id=False, params=False):
|
||||
self.ensure_one()
|
||||
@@ -776,7 +774,7 @@ class ProductPackaging(models.Model):
|
||||
class SupplierInfo(models.Model):
|
||||
_name = "product.supplierinfo"
|
||||
_description = "Supplier Pricelist"
|
||||
_order = 'sequence, min_qty desc, price'
|
||||
_order = 'sequence, min_qty DESC, price, id'
|
||||
|
||||
name = fields.Many2one(
|
||||
'res.partner', 'Vendor',
|
||||
|
||||
@@ -110,7 +110,7 @@ class ProductTemplate(models.Model):
|
||||
packaging_ids = fields.One2many(
|
||||
'product.packaging', string="Product Packages", compute="_compute_packaging_ids", inverse="_set_packaging_ids",
|
||||
help="Gives the different ways to package the same product.")
|
||||
seller_ids = fields.One2many('product.supplierinfo', 'product_tmpl_id', 'Vendors', help="Define vendor pricelists.")
|
||||
seller_ids = fields.One2many('product.supplierinfo', 'product_tmpl_id', 'Vendors', depends_context=('company',), help="Define vendor pricelists.")
|
||||
variant_seller_ids = fields.One2many('product.supplierinfo', 'product_tmpl_id')
|
||||
|
||||
active = fields.Boolean('Active', default=True, help="If unchecked, it will allow you to hide the product without removing it.")
|
||||
|
||||
@@ -148,17 +148,21 @@ class StockRule(models.Model):
|
||||
purpose in order to indicate that those options are available.
|
||||
"""
|
||||
delay, delay_description = super()._get_lead_days(product)
|
||||
bypass_delay_description = self.env.context.get('bypass_delay_description')
|
||||
buy_rule = self.filtered(lambda r: r.action == 'buy')
|
||||
if not buy_rule or not product._prepare_sellers():
|
||||
seller = product._prepare_sellers()
|
||||
if not buy_rule or not seller:
|
||||
return delay, delay_description
|
||||
buy_rule.ensure_one()
|
||||
supplier_delay = product._prepare_sellers()[0].delay
|
||||
if supplier_delay:
|
||||
supplier_delay = seller[0].delay
|
||||
if supplier_delay and not bypass_delay_description:
|
||||
delay_description += '<tr><td>%s</td><td class="text-right">+ %d %s</td></tr>' % (_('Vendor Lead Time'), supplier_delay, _('day(s)'))
|
||||
security_delay = buy_rule.picking_type_id.company_id.po_lead
|
||||
delay_description += '<tr><td>%s</td><td class="text-right">+ %d %s</td></tr>' % (_('Purchase Security Lead Time'), security_delay, _('day(s)'))
|
||||
if not bypass_delay_description:
|
||||
delay_description += '<tr><td>%s</td><td class="text-right">+ %d %s</td></tr>' % (_('Purchase Security Lead Time'), security_delay, _('day(s)'))
|
||||
days_to_purchase = buy_rule.company_id.days_to_purchase
|
||||
delay_description += '<tr><td>%s</td><td class="text-right">+ %d %s</td></tr>' % (_('Days to Purchase'), days_to_purchase, _('day(s)'))
|
||||
if not bypass_delay_description:
|
||||
delay_description += '<tr><td>%s</td><td class="text-right">+ %d %s</td></tr>' % (_('Days to Purchase'), days_to_purchase, _('day(s)'))
|
||||
return delay + supplier_delay + security_delay + days_to_purchase, delay_description
|
||||
|
||||
@api.model
|
||||
|
||||
@@ -318,6 +318,8 @@ class StockWarehouseOrderpoint(models.Model):
|
||||
to_remove.unlink()
|
||||
orderpoints = orderpoints - to_remove
|
||||
to_refill = defaultdict(float)
|
||||
all_product_ids = []
|
||||
all_warehouse_ids = []
|
||||
qty_by_product_warehouse = self.env['report.stock.quantity'].read_group(
|
||||
[('date', '=', fields.date.today()), ('state', '=', 'forecast')],
|
||||
['product_id', 'product_qty', 'warehouse_id'],
|
||||
@@ -326,6 +328,8 @@ class StockWarehouseOrderpoint(models.Model):
|
||||
warehouse_id = group.get('warehouse_id') and group['warehouse_id'][0]
|
||||
if group['product_qty'] >= 0.0 or not warehouse_id:
|
||||
continue
|
||||
all_product_ids.append(group['product_id'][0])
|
||||
all_warehouse_ids.append(warehouse_id)
|
||||
to_refill[(group['product_id'][0], warehouse_id)] = group['product_qty']
|
||||
if not to_refill:
|
||||
return action
|
||||
@@ -333,19 +337,27 @@ class StockWarehouseOrderpoint(models.Model):
|
||||
# Recompute the forecasted quantity for missing product today but at this time
|
||||
# with their real lead days.
|
||||
key_to_remove = []
|
||||
pwh_per_day = defaultdict(list)
|
||||
for (product, warehouse) in to_refill.keys():
|
||||
product = self.env['product.product'].browse(product)
|
||||
warehouse = self.env['stock.warehouse'].browse(warehouse)
|
||||
product = self.env['product.product'].browse(product).with_prefetch(all_product_ids)
|
||||
warehouse = self.env['stock.warehouse'].browse(warehouse).with_prefetch(all_warehouse_ids)
|
||||
rules = product._get_rules_from_location(warehouse.lot_stock_id)
|
||||
lead_days = rules._get_lead_days(product)[0]
|
||||
virtual_available = product.with_context(
|
||||
lead_days = rules.with_context(bypass_delay_description=True)._get_lead_days(product)[0]
|
||||
pwh_per_day[(lead_days, warehouse)].append(product.id)
|
||||
# group product by lead_days and warehouse in order to read virtual_available
|
||||
# in batch
|
||||
for (days, warehouse), p_ids in pwh_per_day.items():
|
||||
products = self.env['product.product'].browse(p_ids)
|
||||
qties = products.with_context(
|
||||
warehouse=warehouse.id,
|
||||
to_date=fields.datetime.now() + relativedelta.relativedelta(days=lead_days)
|
||||
).virtual_available
|
||||
if float_compare(virtual_available, 0, precision_rounding=product.uom_id.rounding) >= 0:
|
||||
key_to_remove.append((product.id, warehouse.id))
|
||||
else:
|
||||
to_refill[(product.id, warehouse.id)] = virtual_available
|
||||
to_date=fields.datetime.now() + relativedelta.relativedelta(days=days)
|
||||
).read(['virtual_available'])
|
||||
for qty in qties:
|
||||
if float_compare(qty['virtual_available'], 0, precision_rounding=product.uom_id.rounding) >= 0:
|
||||
key_to_remove.append((qty['id'], warehouse.id))
|
||||
else:
|
||||
to_refill[(qty['id'], warehouse.id)] = qty['virtual_available']
|
||||
|
||||
for key in key_to_remove:
|
||||
del to_refill[key]
|
||||
if not to_refill:
|
||||
|
||||
@@ -330,7 +330,10 @@ class StockRule(models.Model):
|
||||
:rtype: tuple
|
||||
"""
|
||||
delay = sum(self.filtered(lambda r: r.action in ['pull', 'pull_push']).mapped('delay'))
|
||||
delay_description = ''.join(['<tr><td>%s %s</td><td class="text-right">+ %d %s</td></tr>' % (_('Delay on'), html_escape(rule.name), rule.delay, _('day(s)')) for rule in self if rule.action in ['pull', 'pull_push'] and rule.delay])
|
||||
if self.env.context.get('bypass_delay_description'):
|
||||
delay_description = ""
|
||||
else:
|
||||
delay_description = ''.join(['<tr><td>%s %s</td><td class="text-right">+ %d %s</td></tr>' % (_('Delay on'), html_escape(rule.name), rule.delay, _('day(s)')) for rule in self if rule.action in ['pull', 'pull_push'] and rule.delay])
|
||||
return delay, delay_description
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user