# -*- coding: utf-8 -*- # Copyright 2024-2025 Nexa Systems Inc. # License OPL-1 (Odoo Proprietary License v1.0) from odoo import models, fields, api, _ from odoo.exceptions import UserError from datetime import date import logging _logger = logging.getLogger(__name__) class ReadyToBillWizard(models.TransientModel): """Wizard to collect Proof of Delivery and delivery date before marking as Ready to Bill.""" _name = 'fusion_claims.ready.to.bill.wizard' _description = 'Ready to Bill Wizard' # ========================================================================== # FIELDS # ========================================================================== sale_order_id = fields.Many2one( 'sale.order', string='Sale Order', required=True, readonly=True, ) # Delivery Information delivery_date = fields.Date( string='Delivery Date', required=True, default=fields.Date.today, help='Date the product was delivered to the client', ) # Proof of Delivery proof_of_delivery = fields.Binary( string='Proof of Delivery', required=True, help='Upload the Proof of Delivery document (PDF)', ) proof_of_delivery_filename = fields.Char( string='POD Filename', ) # Optional notes notes = fields.Text( string='Notes', help='Optional notes about the delivery', ) # Info fields has_existing_pod = fields.Boolean( string='Has Existing POD', compute='_compute_existing_data', ) has_existing_date = fields.Boolean( string='Has Existing Date', compute='_compute_existing_data', ) existing_pod_filename = fields.Char( string='Existing POD Filename', compute='_compute_existing_data', ) existing_delivery_date = fields.Date( string='Existing Delivery Date', compute='_compute_existing_data', ) # ========================================================================== # COMPUTED METHODS # ========================================================================== @api.depends('sale_order_id') def _compute_existing_data(self): for wizard in self: order = wizard.sale_order_id wizard.has_existing_pod = bool(order.x_fc_proof_of_delivery) wizard.has_existing_date = bool(order.x_fc_adp_delivery_date) wizard.existing_pod_filename = order.x_fc_proof_of_delivery_filename or '' wizard.existing_delivery_date = order.x_fc_adp_delivery_date # ========================================================================== # DEFAULT GET # ========================================================================== @api.model def default_get(self, fields_list): res = super().default_get(fields_list) active_id = self._context.get('active_id') if not active_id: return res order = self.env['sale.order'].browse(active_id) res['sale_order_id'] = order.id # Pre-fill delivery date if already set if order.x_fc_adp_delivery_date: res['delivery_date'] = order.x_fc_adp_delivery_date # Pre-fill POD if already uploaded if order.x_fc_proof_of_delivery: res['proof_of_delivery'] = order.x_fc_proof_of_delivery res['proof_of_delivery_filename'] = order.x_fc_proof_of_delivery_filename return res # ========================================================================== # ACTION METHODS # ========================================================================== def action_mark_ready_to_bill(self): """Validate and mark the order as Ready to Bill.""" self.ensure_one() order = self.sale_order_id # Validate status if order.x_fc_adp_application_status not in ('approved', 'approved_deduction'): raise UserError( "Order can only be marked as 'Ready to Bill' from 'Approved' status." ) # Validate POD file type if self.proof_of_delivery_filename: if not self.proof_of_delivery_filename.lower().endswith('.pdf'): raise UserError( f"Proof of Delivery must be a PDF file.\n" f"Uploaded: '{self.proof_of_delivery_filename}'" ) # Check device verification if not order.x_fc_device_verification_complete: raise UserError( "Device approval verification must be completed before marking as Ready to Bill.\n\n" "Please verify which devices were approved by ADP using the 'Mark as Approved' button first." ) # Update the order order.with_context(skip_status_validation=True).write({ 'x_fc_adp_application_status': 'ready_bill', 'x_fc_adp_delivery_date': self.delivery_date, 'x_fc_proof_of_delivery': self.proof_of_delivery, 'x_fc_proof_of_delivery_filename': self.proof_of_delivery_filename, }) # Create attachment for POD to post in chatter pod_attachment = self.env['ir.attachment'].create({ 'name': self.proof_of_delivery_filename or 'Proof_of_Delivery.pdf', 'datas': self.proof_of_delivery, 'res_model': 'sale.order', 'res_id': order.id, }) # Post to chatter from markupsafe import Markup notes_html = '' if self.notes: notes_html = f'

Notes: {self.notes}

' order.message_post( body=Markup( '
' '

Ready to Bill

' f'

Delivery Date: {self.delivery_date.strftime("%B %d, %Y")}

' f'

Proof of Delivery: {self.proof_of_delivery_filename}

' f'{notes_html}' f'

Marked by {self.env.user.name}

' '
' ), message_type='notification', subtype_xmlid='mail.mt_note', attachment_ids=[pod_attachment.id], ) return { 'type': 'ir.actions.client', 'tag': 'display_notification', 'params': { 'title': _('Ready to Bill'), 'message': _('Order marked as Ready to Bill. POD attached.'), 'type': 'success', 'sticky': False, 'next': {'type': 'ir.actions.act_window_close'}, }, }