184 lines
7.1 KiB
Python
184 lines
7.1 KiB
Python
from odoo import api, fields, models
|
|
import logging
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
class StockPicking(models.Model):
|
|
_inherit = 'stock.picking'
|
|
|
|
subcontract_lot_count = fields.Integer(
|
|
'Subcontract Lots Count',
|
|
compute='_compute_subcontract_lot_count'
|
|
)
|
|
has_subcontract_moves = fields.Boolean(
|
|
'Has Subcontract Moves',
|
|
compute='_compute_has_subcontract_moves'
|
|
)
|
|
|
|
@api.depends('move_ids.is_subcontract')
|
|
def _compute_has_subcontract_moves(self):
|
|
"""Compute if this picking has any subcontract moves."""
|
|
for picking in self:
|
|
picking.has_subcontract_moves = any(move.is_subcontract for move in picking.move_ids)
|
|
|
|
@api.depends('move_ids.move_line_ids.lot_id')
|
|
def _compute_subcontract_lot_count(self):
|
|
"""Compute the number of lots generated for subcontract moves."""
|
|
for picking in self:
|
|
if picking.picking_type_code == 'incoming':
|
|
subcontract_moves = picking.move_ids.filtered('is_subcontract')
|
|
lot_ids = subcontract_moves.move_line_ids.mapped('lot_id')
|
|
picking.subcontract_lot_count = len(lot_ids)
|
|
else:
|
|
picking.subcontract_lot_count = 0
|
|
|
|
def action_view_generated_lots(self):
|
|
"""Action to view all lots generated for subcontract moves in this picking."""
|
|
self.ensure_one()
|
|
subcontract_moves = self.move_ids.filtered('is_subcontract')
|
|
lot_ids = subcontract_moves.move_line_ids.mapped('lot_id').ids
|
|
|
|
if not lot_ids:
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'title': 'No Lots Found',
|
|
'message': 'No lots have been generated for subcontract moves yet.',
|
|
'type': 'info',
|
|
'sticky': False,
|
|
}
|
|
}
|
|
|
|
# Create a simple action to view the lots
|
|
return {
|
|
'name': f'Generated Lots ({len(lot_ids)})',
|
|
'type': 'ir.actions.act_window',
|
|
'res_model': 'stock.lot',
|
|
'view_mode': 'list,form',
|
|
'views': [(False, 'list'), (False, 'form')],
|
|
'domain': [('id', 'in', lot_ids)],
|
|
'context': {
|
|
'create': False,
|
|
'edit': False,
|
|
},
|
|
'target': 'current',
|
|
}
|
|
|
|
def action_auto_generate_lots_subcontract(self):
|
|
"""
|
|
Action to auto-generate lot numbers for subcontracting moves.
|
|
Similar to the "+" icon functionality in MO forms.
|
|
"""
|
|
generated_count = 0
|
|
generated_lots = []
|
|
|
|
for picking in self:
|
|
if picking.picking_type_code == 'incoming':
|
|
subcontract_moves = picking.move_ids.filtered('is_subcontract')
|
|
if not subcontract_moves:
|
|
continue
|
|
for move in subcontract_moves:
|
|
if move.product_id.tracking in ['lot', 'serial']:
|
|
try:
|
|
lots = move._auto_generate_lots_for_subcontract()
|
|
if lots:
|
|
generated_count += len(lots)
|
|
generated_lots.extend([lot.name for lot in lots])
|
|
except Exception as e:
|
|
_logger.error(f"Error generating lots for move {move.id}: {e}")
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'title': 'Generation Error',
|
|
'message': f'Error generating lots: {str(e)}',
|
|
'type': 'danger',
|
|
'sticky': True,
|
|
}
|
|
}
|
|
|
|
if generated_count > 0:
|
|
message = f'Generated {generated_count} lots: {", ".join(generated_lots[:5])}'
|
|
if len(generated_lots) > 5:
|
|
message += f' and {len(generated_lots) - 5} more...'
|
|
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'title': 'Success',
|
|
'message': message,
|
|
'type': 'success',
|
|
'sticky': False,
|
|
}
|
|
}
|
|
else:
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'title': 'No Lots Generated',
|
|
'message': 'No lots were generated. Check that products have lot/serial tracking and custom sequences configured.',
|
|
'type': 'info',
|
|
'sticky': False,
|
|
}
|
|
}
|
|
|
|
def _auto_assign_lots_on_subcontract_receipt(self):
|
|
"""
|
|
Automatically assign lot numbers when validating subcontract receipts.
|
|
This is called during the validation process.
|
|
"""
|
|
for picking in self:
|
|
if picking.picking_type_code == 'incoming':
|
|
subcontract_moves = picking.move_ids.filtered('is_subcontract')
|
|
for move in subcontract_moves:
|
|
if move.product_id.tracking in ['lot', 'serial'] and move.state not in ['done', 'cancel']:
|
|
move._auto_generate_lots_for_subcontract()
|
|
|
|
def action_open_subcontract_lot_wizard(self):
|
|
"""Open the subcontract lot generator wizard."""
|
|
self.ensure_one()
|
|
|
|
# Find the first subcontract move for default values
|
|
subcontract_move = self.move_ids.filtered('is_subcontract')[:1]
|
|
|
|
if not subcontract_move:
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'title': 'No Subcontract Moves',
|
|
'message': 'This picking does not contain any subcontract moves.',
|
|
'type': 'warning',
|
|
'sticky': False,
|
|
}
|
|
}
|
|
|
|
context = {
|
|
'default_picking_id': self.id,
|
|
}
|
|
|
|
if subcontract_move:
|
|
context.update({
|
|
'default_move_id': subcontract_move.id,
|
|
'default_product_id': subcontract_move.product_id.id,
|
|
'default_quantity': subcontract_move.product_uom_qty,
|
|
})
|
|
|
|
return {
|
|
'name': 'Generate Subcontract Lots',
|
|
'type': 'ir.actions.act_window',
|
|
'res_model': 'subcontract.lot.generator',
|
|
'view_mode': 'form',
|
|
'target': 'new',
|
|
'context': context,
|
|
}
|
|
|
|
def button_validate(self):
|
|
"""Override to auto-generate lots for subcontract moves before validation."""
|
|
# Auto-generate lots for subcontract moves if needed
|
|
self._auto_assign_lots_on_subcontract_receipt()
|
|
return super().button_validate() |