# -*- coding: utf-8 -*- # Copyright 2024-2025 Nexa Systems Inc. # License OPL-1 (Odoo Proprietary License v1.0) # Part of the Fusion Claim Assistant product family. from odoo import models, fields, api, _ from odoo.exceptions import UserError import json import logging _logger = logging.getLogger(__name__) class SubmissionVerificationWizard(models.TransientModel): """Wizard to verify which device types are being submitted in the ADP application. This is Stage 1 of the two-stage verification system: - Stage 1 (Submission): Verify what device types are being applied for - Stage 2 (Approval): Verify what device types were approved by ADP """ _name = 'fusion_claims.submission.verification.wizard' _description = 'ADP Submission Verification Wizard' # ========================================================================== # MAIN FIELDS # ========================================================================== sale_order_id = fields.Many2one( 'sale.order', string='Sale Order', required=True, readonly=True, ) line_ids = fields.One2many( 'fusion_claims.submission.verification.wizard.line', 'wizard_id', string='Device Type Lines', ) # Store device type mapping as JSON - needed because readonly fields aren't sent back # Format: {"line_index": "device_type_name", ...} device_type_mapping = fields.Text( string='Device Type Mapping (JSON)', help='Internal field to store device type names by line index', ) # Summary fields total_device_types = fields.Integer( string='Total Device Types', compute='_compute_summary', ) selected_device_types = fields.Integer( string='Selected Device Types', compute='_compute_summary', ) # ========================================================================== # DOCUMENT UPLOAD FIELDS (for Submit Application mode) # ========================================================================== is_submit_mode = fields.Boolean( string='Submit Mode', default=False, help='True if this wizard is being used to submit the application (not just verify)', ) final_application = fields.Binary( string='Final Submitted Application', help='Upload the final application PDF being submitted to ADP', ) final_application_filename = fields.Char( string='Final Application Filename', ) xml_file = fields.Binary( string='XML Data File', help='Upload the XML data file for ADP submission', ) xml_filename = fields.Char( string='XML Filename', ) # ========================================================================== # COMPUTED METHODS # ========================================================================== @api.depends('line_ids', 'line_ids.selected') def _compute_summary(self): for wizard in self: wizard.total_device_types = len(wizard.line_ids) wizard.selected_device_types = len(wizard.line_ids.filtered(lambda l: l.selected)) # ========================================================================== # DEFAULT GET - Populate with device types from order # ========================================================================== @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 # Set submit mode based on context res['is_submit_mode'] = self._context.get('submit_application', False) # Group order lines by device type ADPDevice = self.env['fusion.adp.device.code'].sudo() device_type_data = {} # {device_type: {'products': [], 'total_amount': 0}} for so_line in order.order_line: # Skip non-product lines if so_line.display_type in ('line_section', 'line_note'): continue if not so_line.product_id or so_line.product_uom_qty <= 0: continue # Get device code and look up device type device_code = so_line._get_adp_device_code() if not device_code: continue adp_device = ADPDevice.search([ ('device_code', '=', device_code), ('active', '=', True) ], limit=1) if not adp_device: continue device_type = adp_device.device_type or 'Unknown' if device_type not in device_type_data: device_type_data[device_type] = { 'products': [], 'total_amount': 0, 'adp_portion': 0, } device_type_data[device_type]['products'].append({ 'name': so_line.product_id.display_name, 'code': device_code, 'qty': so_line.product_uom_qty, 'price': so_line.price_subtotal, }) device_type_data[device_type]['total_amount'] += so_line.price_subtotal device_type_data[device_type]['adp_portion'] += so_line.x_fc_adp_portion # Check if there are previously submitted device types previous_selection = {} if order.x_fc_submitted_device_types: try: previous_selection = json.loads(order.x_fc_submitted_device_types) _logger.info(f"Loaded previous selection from order {order.id}: {previous_selection}") except (json.JSONDecodeError, TypeError) as e: _logger.warning(f"Failed to parse submitted_device_types for order {order.id}: {e}") else: _logger.info(f"No previous selection found for order {order.id}") # Build wizard lines and device type mapping lines_data = [] device_type_mapping = {} # {line_index: device_type_name} for idx, (device_type, data) in enumerate(sorted(device_type_data.items())): product_list = ', '.join([ f"{p['name']} ({p['code']}) x{p['qty']}" for p in data['products'] ]) # Check if previously selected was_selected = previous_selection.get(device_type, False) _logger.info(f"Device type '{device_type}' - was_selected: {was_selected} (previous_selection keys: {list(previous_selection.keys())})") # Store mapping by index device_type_mapping[str(idx)] = device_type lines_data.append((0, 0, { 'device_type': device_type, 'product_details': product_list, 'product_count': len(data['products']), 'total_amount': data['total_amount'], 'adp_portion': data['adp_portion'], 'selected': was_selected, # Default to previously selected state })) res['line_ids'] = lines_data res['device_type_mapping'] = json.dumps(device_type_mapping) _logger.info(f"Created device_type_mapping: {device_type_mapping}") return res # ========================================================================== # ACTION METHODS # ========================================================================== def action_confirm_submission(self): """Confirm the selected device types and store them for Stage 2 comparison.""" self.ensure_one() # Load the device type mapping (stored during default_get) # This is needed because readonly fields aren't sent back from the form device_type_mapping = {} if self.device_type_mapping: try: device_type_mapping = json.loads(self.device_type_mapping) except (json.JSONDecodeError, TypeError): pass _logger.info(f"Loaded device_type_mapping: {device_type_mapping}") # Get selected lines and map them to device types using the stored mapping selected_device_types = [] for idx, line in enumerate(self.line_ids): if line.selected: # Get device type from mapping (use index as key) device_type = device_type_mapping.get(str(idx)) if device_type: selected_device_types.append(device_type) _logger.info(f"Line {idx} selected - device_type from mapping: {device_type}") else: # Fallback to line.device_type (might be False due to readonly issue) _logger.warning(f"Line {idx} selected but no mapping found, line.device_type={line.device_type}") if line.device_type: selected_device_types.append(line.device_type) if not selected_device_types: raise UserError( "Please select at least one device type that is being submitted.\n\n" "If none of these device types are being applied for, " "please verify the order lines have the correct ADP device codes." ) # Store the selection as JSON selection_data = { device_type: True for device_type in selected_device_types } _logger.info(f"Saving selection for order {self.sale_order_id.id}: {selection_data}") # Update the sale order self.sale_order_id.write({ 'x_fc_submission_verified': True, 'x_fc_submitted_device_types': json.dumps(selection_data), }) _logger.info(f"Saved x_fc_submitted_device_types: {json.dumps(selection_data)}") # Post to chatter with nice card style from markupsafe import Markup from datetime import date # Build HTML list from selected device types device_list_html = ''.join([ f'
Date: {date.today().strftime("%B %d, %Y")}
' 'Device Types:
' f'Verified by {self.env.user.name}
' 'Date: {date.today().strftime("%B %d, %Y")}
' 'Device Types Submitted:
' f'Submitted by {self.env.user.name}
' '