238 lines
8.9 KiB
Python
238 lines
8.9 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Copyright 2024-2026 Nexa Systems Inc.
|
|
# License OPL-1 (Odoo Proprietary License v1.0)
|
|
# Part of the Fusion Claim Assistant product family.
|
|
|
|
from odoo import api, fields, models
|
|
from markupsafe import Markup
|
|
import logging
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
class FusionSubmissionHistory(models.Model):
|
|
"""Track submission history for ADP applications.
|
|
|
|
Each record represents one submission or resubmission to ADP,
|
|
including the documents submitted, the result, and any rejection reasons.
|
|
"""
|
|
_name = 'fusion.submission.history'
|
|
_description = 'ADP Submission History'
|
|
_order = 'submission_date desc, id desc'
|
|
_rec_name = 'display_name'
|
|
|
|
# ==========================================================================
|
|
# RELATIONSHIPS
|
|
# ==========================================================================
|
|
sale_order_id = fields.Many2one(
|
|
'sale.order',
|
|
string='Sale Order',
|
|
required=True,
|
|
ondelete='cascade',
|
|
index=True,
|
|
)
|
|
|
|
# ==========================================================================
|
|
# SUBMISSION DETAILS
|
|
# ==========================================================================
|
|
display_name = fields.Char(
|
|
string='Display Name',
|
|
compute='_compute_display_name',
|
|
store=True,
|
|
)
|
|
|
|
submission_number = fields.Integer(
|
|
string='Submission #',
|
|
default=1,
|
|
help='Sequence number for this submission (1 = first submission, 2+ = resubmissions)',
|
|
)
|
|
|
|
submission_type = fields.Selection(
|
|
selection=[
|
|
('initial', 'Initial Submission'),
|
|
('resubmission', 'Resubmission'),
|
|
('correction', 'Correction'),
|
|
],
|
|
string='Type',
|
|
default='initial',
|
|
)
|
|
|
|
submission_date = fields.Date(
|
|
string='Submission Date',
|
|
default=fields.Date.today,
|
|
required=True,
|
|
)
|
|
|
|
submitted_by_id = fields.Many2one(
|
|
'res.users',
|
|
string='Submitted By',
|
|
default=lambda self: self.env.user,
|
|
)
|
|
|
|
# ==========================================================================
|
|
# DOCUMENTS SUBMITTED (copies at time of submission)
|
|
# ==========================================================================
|
|
final_application = fields.Binary(
|
|
string='Final Application (PDF)',
|
|
attachment=True,
|
|
help='Copy of the final application PDF at time of submission',
|
|
)
|
|
final_application_filename = fields.Char(
|
|
string='Final Application Filename',
|
|
)
|
|
|
|
xml_file = fields.Binary(
|
|
string='XML File',
|
|
attachment=True,
|
|
help='Copy of the XML file at time of submission',
|
|
)
|
|
xml_filename = fields.Char(
|
|
string='XML Filename',
|
|
)
|
|
|
|
# ==========================================================================
|
|
# RESULT TRACKING
|
|
# ==========================================================================
|
|
result = fields.Selection(
|
|
selection=[
|
|
('pending', 'Pending'),
|
|
('accepted', 'Accepted'),
|
|
('rejected', 'Rejected'),
|
|
('approved', 'Approved'),
|
|
('denied', 'Denied'),
|
|
],
|
|
string='Result',
|
|
default='pending',
|
|
)
|
|
|
|
result_date = fields.Date(
|
|
string='Result Date',
|
|
help='Date when the result was received from ADP',
|
|
)
|
|
|
|
# ==========================================================================
|
|
# REJECTION DETAILS (if rejected)
|
|
# ==========================================================================
|
|
rejection_reason = fields.Selection(
|
|
selection=[
|
|
('name_correction', 'Name Correction Needed'),
|
|
('healthcard_correction', 'Health Card Correction Needed'),
|
|
('duplicate_claim', 'Duplicate Claim Exists'),
|
|
('xml_format_error', 'XML Format/Validation Error'),
|
|
('missing_info', 'Missing Required Information'),
|
|
('other', 'Other'),
|
|
],
|
|
string='Rejection Reason',
|
|
)
|
|
|
|
rejection_details = fields.Text(
|
|
string='Rejection Details',
|
|
help='Additional details about the rejection',
|
|
)
|
|
|
|
# ==========================================================================
|
|
# CORRECTION NOTES (for resubmissions)
|
|
# ==========================================================================
|
|
correction_notes = fields.Text(
|
|
string='Correction Notes',
|
|
help='Notes about what was corrected for this resubmission',
|
|
)
|
|
|
|
# ==========================================================================
|
|
# COMPUTED FIELDS
|
|
# ==========================================================================
|
|
@api.depends('sale_order_id', 'submission_number', 'submission_type')
|
|
def _compute_display_name(self):
|
|
for record in self:
|
|
order_name = record.sale_order_id.name or 'New'
|
|
type_label = dict(record._fields['submission_type'].selection).get(
|
|
record.submission_type, record.submission_type
|
|
)
|
|
record.display_name = f"{order_name} - Submission #{record.submission_number} ({type_label})"
|
|
|
|
# ==========================================================================
|
|
# HELPER METHODS
|
|
# ==========================================================================
|
|
@api.model
|
|
def create_from_submission(self, sale_order, submission_type='initial', correction_notes=None):
|
|
"""Create a submission history record from a sale order submission.
|
|
|
|
Args:
|
|
sale_order: The sale.order record being submitted
|
|
submission_type: 'initial', 'resubmission', or 'correction'
|
|
correction_notes: Optional notes about corrections made
|
|
|
|
Returns:
|
|
The created fusion.submission.history record
|
|
"""
|
|
# Get next submission number
|
|
existing_count = self.search_count([('sale_order_id', '=', sale_order.id)])
|
|
submission_number = existing_count + 1
|
|
|
|
# If submission_number > 1, it's a resubmission
|
|
if submission_number > 1 and submission_type == 'initial':
|
|
submission_type = 'resubmission'
|
|
|
|
vals = {
|
|
'sale_order_id': sale_order.id,
|
|
'submission_number': submission_number,
|
|
'submission_type': submission_type,
|
|
'submission_date': fields.Date.today(),
|
|
'submitted_by_id': self.env.user.id,
|
|
'correction_notes': correction_notes,
|
|
}
|
|
|
|
# Copy current documents
|
|
if sale_order.x_fc_final_submitted_application:
|
|
vals['final_application'] = sale_order.x_fc_final_submitted_application
|
|
vals['final_application_filename'] = sale_order.x_fc_final_application_filename
|
|
|
|
if sale_order.x_fc_xml_file:
|
|
vals['xml_file'] = sale_order.x_fc_xml_file
|
|
vals['xml_filename'] = sale_order.x_fc_xml_filename
|
|
|
|
record = self.create(vals)
|
|
|
|
# Post to chatter
|
|
sale_order.message_post(
|
|
body=Markup(
|
|
'<div style="border-left: 3px solid #3498db; padding-left: 12px; margin: 8px 0;">'
|
|
'<p style="margin: 0 0 8px 0; font-weight: 600; color: #2980b9;">'
|
|
f'<i class="fa fa-paper-plane"></i> Submission #{submission_number} Recorded</p>'
|
|
'<table style="font-size: 13px; color: #555;">'
|
|
f'<tr><td style="padding: 2px 8px 2px 0; font-weight: 500;">Type:</td><td>{dict(self._fields["submission_type"].selection).get(submission_type)}</td></tr>'
|
|
f'<tr><td style="padding: 2px 8px 2px 0; font-weight: 500;">Date:</td><td>{fields.Date.today().strftime("%B %d, %Y")}</td></tr>'
|
|
f'<tr><td style="padding: 2px 8px 2px 0; font-weight: 500;">By:</td><td>{self.env.user.name}</td></tr>'
|
|
'</table>'
|
|
+ (f'<p style="margin: 8px 0 0 0; font-size: 12px;"><strong>Corrections:</strong> {correction_notes}</p>' if correction_notes else '') +
|
|
'</div>'
|
|
),
|
|
message_type='notification',
|
|
subtype_xmlid='mail.mt_note',
|
|
)
|
|
|
|
return record
|
|
|
|
def update_result(self, result, rejection_reason=None, rejection_details=None):
|
|
"""Update the result of a submission.
|
|
|
|
Args:
|
|
result: 'accepted', 'rejected', 'approved', or 'denied'
|
|
rejection_reason: Selection value for rejection reason
|
|
rejection_details: Text details for rejection
|
|
"""
|
|
self.ensure_one()
|
|
|
|
vals = {
|
|
'result': result,
|
|
'result_date': fields.Date.today(),
|
|
}
|
|
|
|
if result == 'rejected':
|
|
vals['rejection_reason'] = rejection_reason
|
|
vals['rejection_details'] = rejection_details
|
|
|
|
self.write(vals)
|
|
|
|
return self
|