Files
Odoo-Modules/fusion_claims/wizard/case_close_verification_wizard.py
2026-02-22 01:22:18 -05:00

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',
}