110 lines
4.5 KiB
Python
110 lines
4.5 KiB
Python
from odoo import api, fields, models, _
|
|
from odoo.exceptions import UserError
|
|
|
|
|
|
class SubcontractLotGenerator(models.TransientModel):
|
|
_name = 'subcontract.lot.generator'
|
|
_description = 'Subcontract Lot Generator Wizard'
|
|
|
|
picking_id = fields.Many2one('stock.picking', string='Picking', required=True)
|
|
move_id = fields.Many2one('stock.move', string='Move', required=True)
|
|
product_id = fields.Many2one('product.product', string='Product', required=True)
|
|
quantity = fields.Float('Quantity', required=True, default=1.0)
|
|
lot_count = fields.Integer('Number of Lots', default=1, help='Number of lots to generate')
|
|
tracking = fields.Selection(related='product_id.tracking')
|
|
use_sequence = fields.Boolean('Use Product Sequence', default=True)
|
|
|
|
@api.onchange('product_id')
|
|
def _onchange_product_id(self):
|
|
if self.product_id:
|
|
if self.product_id.tracking == 'serial':
|
|
self.lot_count = int(self.quantity)
|
|
else:
|
|
self.lot_count = 1
|
|
|
|
@api.onchange('quantity', 'tracking')
|
|
def _onchange_quantity(self):
|
|
if self.tracking == 'serial':
|
|
self.lot_count = int(self.quantity)
|
|
|
|
def action_generate_lots(self):
|
|
"""Generate lots based on wizard configuration."""
|
|
self.ensure_one()
|
|
|
|
if not self.product_id.tracking in ['lot', 'serial']:
|
|
raise UserError(_('Product must have lot or serial tracking enabled.'))
|
|
|
|
if self.lot_count <= 0:
|
|
raise UserError(_('Number of lots must be greater than 0.'))
|
|
|
|
# Get the sequence
|
|
tmpl = self.product_id.product_tmpl_id
|
|
lot_sequence = getattr(tmpl, 'lot_sequence_id', False)
|
|
|
|
if not lot_sequence and self.use_sequence:
|
|
raise UserError(_('No lot sequence configured for product %s') % self.product_id.display_name)
|
|
|
|
# Generate lot names
|
|
if self.use_sequence and lot_sequence:
|
|
# Use standard Odoo sequence generation
|
|
# Removed custom batch allocation and unsafe prefix overrides
|
|
lot_names = [lot_sequence.next_by_id() for _ in range(self.lot_count)]
|
|
else:
|
|
# Generate simple sequential names fallback
|
|
lot_names = [f"LOT-{i+1:04d}" for i in range(self.lot_count)]
|
|
|
|
# Create the lots
|
|
Lot = self.env['stock.lot']
|
|
lot_vals_list = []
|
|
for lot_name in lot_names:
|
|
lot_vals = {
|
|
'name': lot_name,
|
|
'product_id': self.product_id.id,
|
|
'company_id': self.picking_id.company_id.id,
|
|
}
|
|
lot_vals_list.append(lot_vals)
|
|
|
|
lots = Lot.create(lot_vals_list)
|
|
|
|
# Calculate quantity in Product's Base UOM
|
|
# self.quantity is in self.move_id.product_uom (or whatever was passed)
|
|
base_uom = self.product_id.uom_id
|
|
move_uom = self.move_id.product_uom
|
|
|
|
total_base_qty = self.quantity
|
|
if move_uom and base_uom and move_uom != base_uom:
|
|
total_base_qty = move_uom._compute_quantity(self.quantity, base_uom)
|
|
|
|
if self.tracking == 'serial':
|
|
# One move line per lot for serial tracking, usually 1 unit of base UOM per serial
|
|
qty_per_lot = 1.0
|
|
else:
|
|
# Distribute quantity across lots for lot tracking
|
|
qty_per_lot = total_base_qty / self.lot_count
|
|
|
|
for lot in lots:
|
|
vals = {
|
|
'move_id': self.move_id.id,
|
|
'product_id': self.product_id.id,
|
|
'lot_id': lot.id,
|
|
'quantity': qty_per_lot, # Odoo 19 uses 'quantity' instead of product_uom_qty/qty_done in stock.move.line for initial demand?
|
|
# Let's check standard stock.move.line fields. Usually it's quantity (done) or reserved_uom_qty.
|
|
# In Odoo 19 stock.move.line has 'quantity'.
|
|
'product_uom_id': base_uom.id,
|
|
'location_id': self.move_id.location_id.id,
|
|
'location_dest_id': self.move_id.location_dest_id.id,
|
|
'picking_id': self.picking_id.id,
|
|
}
|
|
self.env['stock.move.line'].create(vals)
|
|
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'title': _('Success'),
|
|
'message': _('Generated %d lots for %s') % (len(lots), self.product_id.display_name),
|
|
'type': 'success',
|
|
'sticky': False,
|
|
}
|
|
}
|