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