product_lot_sequence_per_pr.../models/stock_lot.py

106 lines
4.2 KiB
Python

from odoo import api, models
import logging
_logger = logging.getLogger(__name__)
class StockLot(models.Model):
_inherit = 'stock.lot'
@api.model_create_multi
def create(self, vals_list):
"""
Optimized batch lot creation with efficient sequence allocation.
For large batches, allocates all sequence numbers in a single database query.
"""
# Group lots by product for batch processing
lots_by_product = {}
for vals in vals_list:
if not vals.get('name') and vals.get('product_id'):
product_id = vals['product_id']
if product_id not in lots_by_product:
lots_by_product[product_id] = []
lots_by_product[product_id].append(vals)
# Process each product group
for product_id, product_vals_list in lots_by_product.items():
product = self.env['product.product'].browse(product_id)
seq = getattr(product.product_tmpl_id, 'lot_sequence_id', False)
if seq and len(product_vals_list) > 10:
# Use optimized batch allocation for large quantities
lot_names = self._allocate_sequence_batch(seq, len(product_vals_list))
for vals, name in zip(product_vals_list, lot_names):
vals['name'] = name
_logger.info(f"Batch allocated {len(lot_names)} lot names for product {product.display_name}")
else:
# Standard allocation for small quantities
for vals in product_vals_list:
if seq:
vals['name'] = seq.next_by_id()
else:
# Fallback to global sequence if no product sequence
vals['name'] = self.env['ir.sequence'].next_by_code('stock.lot.serial')
return super().create(vals_list)
def _allocate_sequence_batch(self, sequence, count):
"""
Allocate multiple sequence numbers in a single database operation.
This is significantly faster than calling next_by_id() in a loop for large quantities.
Properly handles date format codes like %(y)s, %(month)s, %(day)s, etc.
"""
if count <= 0:
return []
# Use PostgreSQL's generate_series to allocate multiple sequence values at once
self.env.cr.execute("""
SELECT nextval(%s) FROM generate_series(1, %s)
""", (f"ir_sequence_{sequence.id:03d}", count))
sequence_numbers = [row[0] for row in self.env.cr.fetchall()]
# Get the interpolation context for date formatting (same as ir.sequence)
from datetime import datetime
now = datetime.now()
# Build the interpolation dictionary (same format as Odoo's ir.sequence)
interpolation_dict = {
'year': now.strftime('%Y'),
'y': now.strftime('%y'),
'month': now.strftime('%m'),
'day': now.strftime('%d'),
'doy': now.strftime('%j'),
'woy': now.strftime('%W'),
'weekday': now.strftime('%w'),
'h24': now.strftime('%H'),
'h12': now.strftime('%I'),
'min': now.strftime('%M'),
'sec': now.strftime('%S'),
}
# Format prefix and suffix with date codes
try:
prefix = (sequence.prefix or '') % interpolation_dict if sequence.prefix else ''
except (KeyError, ValueError):
# If formatting fails, use prefix as-is
prefix = sequence.prefix or ''
try:
suffix = (sequence.suffix or '') % interpolation_dict if sequence.suffix else ''
except (KeyError, ValueError):
# If formatting fails, use suffix as-is
suffix = sequence.suffix or ''
# Format the sequence numbers according to the sequence configuration
lot_names = []
for seq_num in sequence_numbers:
lot_name = '{}{:0{}d}{}'.format(
prefix,
seq_num,
sequence.padding,
suffix
)
lot_names.append(lot_name)
return lot_names