127 lines
4.7 KiB
Python
127 lines
4.7 KiB
Python
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,
|
|
},
|
|
}
|