# -*- coding: utf-8 -*- """ Ready for Delivery Wizard Wizard to assign delivery technicians and mark order as Ready for Delivery. """ from odoo import models, fields, api, _ from odoo.exceptions import UserError from markupsafe import Markup import logging _logger = logging.getLogger(__name__) class ReadyForDeliveryWizard(models.TransientModel): _name = 'fusion.ready.for.delivery.wizard' _description = 'Ready for Delivery Wizard' sale_order_id = fields.Many2one( 'sale.order', string='Sale Order', required=True, readonly=True, ) partner_id = fields.Many2one( 'res.partner', string='Client', related='sale_order_id.partner_id', readonly=True, ) claim_number = fields.Char( string='Claim Number', related='sale_order_id.x_fc_claim_number', readonly=True, ) is_early_delivery = fields.Boolean( string='Early Delivery', related='sale_order_id.x_fc_early_delivery', readonly=True, help='This delivery is before ADP approval', ) current_status = fields.Selection( related='sale_order_id.x_fc_adp_application_status', string='Current Status', readonly=True, ) # Technician Assignment technician_ids = fields.Many2many( 'res.users', 'ready_delivery_wizard_technician_rel', 'wizard_id', 'user_id', string='Delivery Technicians', required=True, help='Select one or more field technicians for this delivery', ) # Scheduling scheduled_datetime = fields.Datetime( string='Scheduled Delivery Date/Time', help='Optional: When is the delivery scheduled?', ) # Delivery Address delivery_address = fields.Text( string='Delivery Address', compute='_compute_delivery_address', readonly=True, ) # Notes notes = fields.Text( string='Delivery Notes', help='Any special instructions for the delivery technicians', ) @api.depends('sale_order_id') def _compute_delivery_address(self): """Compute delivery address from partner shipping address.""" for wizard in self: order = wizard.sale_order_id if order and order.partner_shipping_id: addr = order.partner_shipping_id parts = [addr.street, addr.street2, addr.city, addr.state_id.name if addr.state_id else '', addr.zip] wizard.delivery_address = ', '.join([p for p in parts if p]) elif order and order.partner_id: addr = order.partner_id parts = [addr.street, addr.street2, addr.city, addr.state_id.name if addr.state_id else '', addr.zip] wizard.delivery_address = ', '.join([p for p in parts if p]) else: wizard.delivery_address = '' def action_confirm(self): """Confirm and mark as Ready for Delivery.""" self.ensure_one() order = self.sale_order_id if not self.technician_ids: raise UserError(_("Please select at least one delivery technician.")) # Get technician names for chatter message technician_names = ', '.join(self.technician_ids.mapped('name')) user_name = self.env.user.name # Update the sale order order.with_context(skip_status_validation=True).write({ 'x_fc_adp_application_status': 'ready_delivery', 'x_fc_delivery_technician_ids': [(6, 0, self.technician_ids.ids)], 'x_fc_ready_for_delivery_date': fields.Datetime.now(), 'x_fc_scheduled_delivery_datetime': self.scheduled_datetime, }) # Build chatter message scheduled_str = '' if self.scheduled_datetime: scheduled_str = f'
  • Scheduled: {self.scheduled_datetime.strftime("%B %d, %Y at %I:%M %p")}
  • ' notes_str = '' if self.notes: notes_str = f'

    Delivery Notes: {self.notes}

    ' early_badge = '' if self.is_early_delivery: early_badge = ' Early Delivery' chatter_body = Markup(f''' ''') order.message_post( body=chatter_body, message_type='notification', subtype_xmlid='mail.mt_note', ) # Send email notifications order._send_ready_for_delivery_email( technicians=self.technician_ids, scheduled_datetime=self.scheduled_datetime, notes=self.notes, ) # Auto-create technician tasks for each assigned technician self._create_technician_tasks(order) return {'type': 'ir.actions.act_window_close'} def _create_technician_tasks(self, order): """Create a single delivery task with lead + additional technicians. The first selected technician becomes the lead. Any remaining technicians are assigned as additional technicians on the same task. The task model's create() method auto-populates address fields from the linked sale order's shipping address when address_street is not explicitly provided, so we intentionally omit address here. """ Task = self.env['fusion.technician.task'] scheduled_date = False time_start = 9.0 if self.scheduled_datetime: scheduled_date = self.scheduled_datetime.date() time_start = self.scheduled_datetime.hour + (self.scheduled_datetime.minute / 60.0) else: scheduled_date = fields.Date.context_today(self) techs = self.technician_ids lead_tech = techs[0] additional_techs = techs[1:] if len(techs) > 1 else self.env['res.users'] vals = { 'technician_id': lead_tech.id, 'additional_technician_ids': [(6, 0, additional_techs.ids)] if additional_techs else False, 'sale_order_id': order.id, 'task_type': 'delivery', 'scheduled_date': scheduled_date, 'time_start': time_start, 'time_end': time_start + 1.0, 'partner_id': order.partner_id.id, 'description': self.notes or '', 'pod_required': True, } task = Task.create(vals) _logger.info( "Created delivery task %s for %s (+%d additional) on order %s", task.name, lead_tech.name, len(additional_techs), order.name, ) task_url = f'/web#id={task.id}&model=fusion.technician.task&view_type=form' time_str = task.time_start_12h or '' all_names = ', '.join(techs.mapped('name')) task_line = ( f'
  • {task.name} - ' f'{all_names} ' f'({task.scheduled_date.strftime("%b %d, %Y") if task.scheduled_date else "TBD"}' f'{" at " + time_str if time_str else ""})
  • ' ) summary = Markup( '
    ' ' Delivery Task Created' '' '
    ' ) % Markup(task_line) order.message_post( body=summary, message_type='notification', subtype_xmlid='mail.mt_note', )