from odoo import api, models, _ import logging _logger = logging.getLogger(__name__) class StockMove(models.Model): _inherit = 'stock.move' def _auto_generate_lots_for_subcontract(self): """ Auto-generate lot numbers for subcontracting moves. This method handles the automatic lot generation for subcontracted products. """ self.ensure_one() if not self.product_id.tracking in ['lot', 'serial']: return [] product = self.product_id tmpl = product.product_tmpl_id # Use standard Odoo 19 lot_sequence_id field lot_sequence = getattr(tmpl, 'lot_sequence_id', False) if not lot_sequence: _logger.warning(f"No lot sequence configured for product {product.display_name}") return [] # Check if we already have move lines with lots existing_lots = self.move_line_ids.filtered(lambda ml: ml.lot_id) if existing_lots: _logger.info(f"Move already has lots assigned: {[lot.lot_id.name for lot in existing_lots]}") return existing_lots.mapped('lot_id') # Calculate how many lots we need to generate remaining_qty = self.product_uom_qty # For serial tracking, create one lot per unit if product.tracking == 'serial': lots_needed = int(remaining_qty) else: # For lot tracking, create one lot for the entire quantity lots_needed = 1 if lots_needed <= 0: return [] # Generate lot names using standard sequence lot_names = [lot_sequence.next_by_id() for _ in range(lots_needed)] # Create the lots Lot = self.env['stock.lot'] lot_vals_list = [] for lot_name in lot_names: lot_vals = { 'name': lot_name, 'product_id': product.id, 'company_id': self.company_id.id, } lot_vals_list.append(lot_vals) lots = Lot.create(lot_vals_list) # Assign lots to existing move lines or create new ones existing_move_lines = self.move_line_ids.filtered(lambda ml: not ml.lot_id) if existing_move_lines and product.tracking == 'lot': # For lot tracking, assign the first lot to the first available move line if lots: existing_move_lines[0].lot_id = lots[0].id _logger.info(f"Assigned lot {lots[0].name} to existing move line") elif product.tracking == 'serial': # For serial tracking, we need one move line per lot # If we don't have enough move lines, let Odoo handle the creation # by using the standard lot assignment mechanism for i, lot in enumerate(lots): if i < len(existing_move_lines): existing_move_lines[i].lot_id = lot.id else: # Let Odoo create additional move lines as needed break _logger.info(f"Auto-generated {len(lots)} lots for subcontract move of product {product.display_name}") return lots def action_generate_lots_for_move(self): """Open the lot generator wizard for this specific move.""" self.ensure_one() if not getattr(self, 'is_subcontract', False): return { 'type': 'ir.actions.client', 'tag': 'display_notification', 'params': { 'title': 'Not a Subcontract Move', 'message': 'This action is only available for subcontract moves.', 'type': 'warning', 'sticky': False, } } if self.product_id.tracking == 'none': return { 'type': 'ir.actions.client', 'tag': 'display_notification', 'params': { 'title': 'No Tracking Required', 'message': 'This product does not require lot/serial tracking.', 'type': 'info', 'sticky': False, } } return { 'name': 'Generate Lots for Move', 'type': 'ir.actions.act_window', 'res_model': 'subcontract.lot.generator', 'view_mode': 'form', 'target': 'new', 'context': { 'default_move_id': self.id, 'default_picking_id': self.picking_id.id, 'default_product_id': self.product_id.id, 'default_quantity': self.product_uom_qty, }, }