212 lines
8.2 KiB
Python
212 lines
8.2 KiB
Python
# -*- 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
|
|
from markupsafe import Markup
|
|
import logging
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
class CaseCloseVerificationWizard(models.TransientModel):
|
|
"""Wizard to verify audit trail documents before closing an ADP case.
|
|
|
|
This wizard checks:
|
|
- Signed Pages 11 & 12 uploaded
|
|
- Final Application uploaded
|
|
- Proof of Delivery uploaded
|
|
- Vendor bills linked
|
|
"""
|
|
_name = 'fusion_claims.case.close.verification.wizard'
|
|
_description = 'Case Close Verification Wizard'
|
|
|
|
# ==========================================================================
|
|
# MAIN FIELDS
|
|
# ==========================================================================
|
|
sale_order_id = fields.Many2one(
|
|
'sale.order',
|
|
string='Sale Order',
|
|
required=True,
|
|
readonly=True,
|
|
)
|
|
|
|
# Document verification fields (computed)
|
|
has_signed_pages = fields.Boolean(
|
|
string='Signed Pages 11 & 12',
|
|
compute='_compute_document_status',
|
|
)
|
|
has_final_application = fields.Boolean(
|
|
string='Final Application',
|
|
compute='_compute_document_status',
|
|
)
|
|
has_proof_of_delivery = fields.Boolean(
|
|
string='Proof of Delivery',
|
|
compute='_compute_document_status',
|
|
)
|
|
has_vendor_bills = fields.Boolean(
|
|
string='Vendor Bills Linked',
|
|
compute='_compute_document_status',
|
|
)
|
|
vendor_bill_count = fields.Integer(
|
|
string='Vendor Bills Count',
|
|
compute='_compute_document_status',
|
|
)
|
|
|
|
# Overall status
|
|
all_verified = fields.Boolean(
|
|
string='All Verified',
|
|
compute='_compute_all_verified',
|
|
)
|
|
missing_items = fields.Text(
|
|
string='Missing Items',
|
|
compute='_compute_all_verified',
|
|
)
|
|
|
|
# User notes (optional)
|
|
closing_notes = fields.Text(
|
|
string='Closing Notes',
|
|
help='Optional notes to record when closing the case.',
|
|
)
|
|
|
|
# ==========================================================================
|
|
# COMPUTED METHODS
|
|
# ==========================================================================
|
|
@api.depends('sale_order_id')
|
|
def _compute_document_status(self):
|
|
for wizard in self:
|
|
order = wizard.sale_order_id
|
|
wizard.has_signed_pages = bool(order.x_fc_signed_pages_11_12)
|
|
wizard.has_final_application = bool(order.x_fc_final_submitted_application)
|
|
wizard.has_proof_of_delivery = bool(order.x_fc_proof_of_delivery)
|
|
wizard.has_vendor_bills = len(order.x_fc_vendor_bill_ids) > 0
|
|
wizard.vendor_bill_count = len(order.x_fc_vendor_bill_ids)
|
|
|
|
@api.depends('has_signed_pages', 'has_final_application', 'has_proof_of_delivery', 'has_vendor_bills')
|
|
def _compute_all_verified(self):
|
|
for wizard in self:
|
|
missing = []
|
|
if not wizard.has_signed_pages:
|
|
missing.append('Signed Pages 11 & 12')
|
|
if not wizard.has_final_application:
|
|
missing.append('Final Application')
|
|
if not wizard.has_proof_of_delivery:
|
|
missing.append('Proof of Delivery')
|
|
if not wizard.has_vendor_bills:
|
|
missing.append('Vendor Bills (at least one)')
|
|
|
|
wizard.all_verified = len(missing) == 0
|
|
wizard.missing_items = '\n'.join(f'• {item}' for item in missing) if missing else ''
|
|
|
|
# ==========================================================================
|
|
# 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
|
|
|
|
res['sale_order_id'] = active_id
|
|
return res
|
|
|
|
# ==========================================================================
|
|
# ACTIONS
|
|
# ==========================================================================
|
|
def action_close_case(self):
|
|
"""Close the case after verification."""
|
|
self.ensure_one()
|
|
|
|
order = self.sale_order_id
|
|
|
|
# Check if everything is verified
|
|
if not self.all_verified:
|
|
raise UserError(
|
|
"Cannot close case - the following items are missing:\n\n"
|
|
f"{self.missing_items}\n\n"
|
|
"Please upload all required documents and link vendor bills before closing."
|
|
)
|
|
|
|
# Close the case
|
|
order.with_context(skip_status_validation=True).write({
|
|
'x_fc_adp_application_status': 'case_closed',
|
|
})
|
|
|
|
# Build summary message with consistent card style
|
|
from datetime import date
|
|
vendor_bills_list = ', '.join(order.x_fc_vendor_bill_ids.mapped('name')) if order.x_fc_vendor_bill_ids else 'N/A'
|
|
|
|
message_body = f"""
|
|
<div style="background: #d4edda; border-left: 4px solid #28a745; padding: 12px; margin: 8px 0; border-radius: 4px;">
|
|
<h4 style="color: #28a745; margin: 0 0 8px 0;">
|
|
<i class="fa fa-check-circle"/> Case Closed
|
|
</h4>
|
|
<p style="margin: 0;"><strong>Date:</strong> {date.today().strftime("%B %d, %Y")}</p>
|
|
<p style="margin: 8px 0 4px 0;"><strong>Audit Trail Verified:</strong></p>
|
|
<ul style="margin: 0; padding-left: 20px;">
|
|
<li><i class="fa fa-check text-success"/> Signed Pages 11 & 12</li>
|
|
<li><i class="fa fa-check text-success"/> Final Application</li>
|
|
<li><i class="fa fa-check text-success"/> Proof of Delivery</li>
|
|
<li><i class="fa fa-check text-success"/> Vendor Bills: {self.vendor_bill_count} ({vendor_bills_list})</li>
|
|
</ul>
|
|
{f'<p style="margin: 8px 0 0 0;"><strong>Notes:</strong> {self.closing_notes}</p>' if self.closing_notes else ''}
|
|
<p style="margin: 8px 0 0 0; color: #666; font-size: 0.9em;">
|
|
Closed by {self.env.user.name}
|
|
</p>
|
|
</div>
|
|
"""
|
|
|
|
order.message_post(
|
|
body=Markup(message_body),
|
|
message_type='notification',
|
|
subtype_xmlid='mail.mt_note',
|
|
)
|
|
|
|
return {
|
|
'type': 'ir.actions.act_window_close',
|
|
}
|
|
|
|
def action_close_anyway(self):
|
|
"""Close the case even if some items are missing (with warning)."""
|
|
self.ensure_one()
|
|
|
|
order = self.sale_order_id
|
|
|
|
# Close the case
|
|
order.with_context(skip_status_validation=True).write({
|
|
'x_fc_adp_application_status': 'case_closed',
|
|
})
|
|
|
|
# Build warning message with consistent card style
|
|
from datetime import date
|
|
missing_items_html = self.missing_items.replace('\n', '<br/>') if self.missing_items else 'None'
|
|
|
|
message_body = f"""
|
|
<div style="background: #fff3cd; border-left: 4px solid #ffc107; padding: 12px; margin: 8px 0; border-radius: 4px;">
|
|
<h4 style="color: #856404; margin: 0 0 8px 0;">
|
|
<i class="fa fa-exclamation-triangle"/> Case Closed (Incomplete)
|
|
</h4>
|
|
<p style="margin: 0;"><strong>Date:</strong> {date.today().strftime("%B %d, %Y")}</p>
|
|
<p style="margin: 8px 0 4px 0; color: #856404;"><strong>Warning:</strong> Case closed without complete audit trail.</p>
|
|
<p style="margin: 4px 0;"><strong>Missing Items:</strong><br/>{missing_items_html}</p>
|
|
{f'<p style="margin: 4px 0 0 0;"><strong>Notes:</strong> {self.closing_notes}</p>' if self.closing_notes else ''}
|
|
<p style="margin: 8px 0 0 0; color: #666; font-size: 0.9em;">
|
|
Closed by {self.env.user.name}
|
|
</p>
|
|
</div>
|
|
"""
|
|
|
|
order.message_post(
|
|
body=Markup(message_body),
|
|
message_type='notification',
|
|
subtype_xmlid='mail.mt_note',
|
|
)
|
|
|
|
return {
|
|
'type': 'ir.actions.act_window_close',
|
|
}
|