This commit is contained in:
gsinghpal
2026-02-27 14:32:32 -05:00
parent b649246e81
commit b925766966
80 changed files with 7831 additions and 1041 deletions

View File

@@ -11,7 +11,6 @@ _logger = logging.getLogger(__name__)
class AssessmentCompletedWizard(models.TransientModel):
"""Wizard to record assessment completion date."""
_name = 'fusion_claims.assessment.completed.wizard'
_description = 'Assessment Completed Wizard'
@@ -21,18 +20,49 @@ class AssessmentCompletedWizard(models.TransientModel):
required=True,
readonly=True,
)
is_override = fields.Boolean(
string='Scheduling Override',
compute='_compute_is_override',
store=False,
)
assessment_start_date = fields.Date(
string='Assessment Start Date',
required=True,
help='Date the assessment was conducted',
)
completion_date = fields.Date(
string='Assessment Completion Date',
required=True,
default=fields.Date.context_today,
)
notes = fields.Text(
string='Assessment Notes',
help='Any notes from the assessment',
string='Notes',
help='Notes from the assessment',
)
override_reason = fields.Text(
string='Override Reason',
help='Mandatory when skipping the scheduling step. Explain why the assessment was completed without scheduling through the system.',
)
notify_authorizer = fields.Boolean(
string='Notify Authorizer',
default=True,
help='Send email to the authorizer about assessment completion',
)
@api.depends('sale_order_id')
def _compute_is_override(self):
for rec in self:
rec.is_override = (
rec.sale_order_id
and rec.sale_order_id.x_fc_adp_application_status == 'quotation'
)
@api.model
def default_get(self, fields_list):
res = super().default_get(fields_list)
@@ -40,43 +70,174 @@ class AssessmentCompletedWizard(models.TransientModel):
if active_id:
order = self.env['sale.order'].browse(active_id)
res['sale_order_id'] = order.id
if order.x_fc_assessment_start_date:
res['assessment_start_date'] = order.x_fc_assessment_start_date
else:
res['assessment_start_date'] = fields.Date.context_today(self)
return res
def action_complete(self):
"""Mark assessment as completed."""
self.ensure_one()
order = self.sale_order_id
if order.x_fc_adp_application_status != 'assessment_scheduled':
raise UserError("Can only complete assessment from 'Assessment Scheduled' status.")
# Validate completion date is not before start date
if order.x_fc_assessment_start_date and self.completion_date < order.x_fc_assessment_start_date:
current_status = order.x_fc_adp_application_status
is_override = current_status == 'quotation'
if current_status not in ('quotation', 'assessment_scheduled'):
raise UserError(
f"Completion date ({self.completion_date}) cannot be before "
f"assessment start date ({order.x_fc_assessment_start_date})."
_("Can only complete assessment from 'Quotation' or 'Assessment Scheduled' status.")
)
# Update sale order
order.with_context(skip_status_validation=True).write({
if is_override and not (self.override_reason or '').strip():
raise UserError(
_("Override Reason is mandatory when skipping the assessment scheduling step. "
"Please explain why this assessment was completed without being scheduled through the system.")
)
if self.completion_date < self.assessment_start_date:
raise UserError(
_("Completion date (%s) cannot be before assessment start date (%s).")
% (self.completion_date, self.assessment_start_date)
)
write_vals = {
'x_fc_adp_application_status': 'assessment_completed',
'x_fc_assessment_end_date': self.completion_date,
})
# Post to chatter
notes_html = f'<p style="margin: 4px 0 0 0;"><strong>Notes:</strong> {self.notes}</p>' if self.notes else ''
order.message_post(
body=Markup(
'<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-square-o"/> Assessment Completed</h4>'
f'<p style="margin: 0;"><strong>Completion Date:</strong> {self.completion_date.strftime("%B %d, %Y")}</p>'
f'{notes_html}'
}
if is_override or not order.x_fc_assessment_start_date:
write_vals['x_fc_assessment_start_date'] = self.assessment_start_date
order.with_context(skip_status_validation=True).write(write_vals)
if is_override:
override_html = Markup(
'<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"/> Assessment Scheduling Override</h4>'
'<p style="margin:0;"><strong>Override by:</strong> %s</p>'
'<p style="margin:4px 0 0 0;"><strong>Reason:</strong> %s</p>'
'<p style="margin:4px 0 0 0;"><strong>Assessment Date:</strong> %s to %s</p>'
'%s'
'</div>'
),
message_type='notification',
subtype_xmlid='mail.mt_note',
)
) % (
self.env.user.name,
self.override_reason.strip(),
self.assessment_start_date.strftime("%B %d, %Y"),
self.completion_date.strftime("%B %d, %Y"),
Markup('<p style="margin:4px 0 0 0;"><strong>Notes:</strong> %s</p>') % self.notes if self.notes else Markup(''),
)
order.message_post(
body=override_html,
message_type='notification',
subtype_xmlid='mail.mt_note',
)
else:
notes_html = (
Markup('<p style="margin:4px 0 0 0;"><strong>Notes:</strong> %s</p>') % self.notes
) if self.notes else Markup('')
order.message_post(
body=Markup(
'<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-square-o"/> Assessment Completed</h4>'
'<p style="margin:0;"><strong>Completion Date:</strong> %s</p>'
'%s'
'</div>'
) % (self.completion_date.strftime("%B %d, %Y"), notes_html),
message_type='notification',
subtype_xmlid='mail.mt_note',
)
if self.notify_authorizer:
self._send_backend_completion_email(order, is_override)
return {'type': 'ir.actions.act_window_close'}
def _send_backend_completion_email(self, order, is_override):
"""Send assessment completion email when done from backend."""
self.ensure_one()
if not order._email_is_enabled():
return
authorizer = order.x_fc_authorizer_id
if not authorizer or not authorizer.email:
_logger.info("No authorizer email for %s, skipping notification", order.name)
return
to_email = authorizer.email
cc_emails = []
if order.user_id and order.user_id.email:
cc_emails.append(order.user_id.email)
company = self.env.company
office_partners = company.sudo().x_fc_office_notification_ids
cc_emails.extend([p.email for p in office_partners if p.email])
client_name = order.partner_id.name or 'Client'
override_note = ''
if is_override:
override_note = (
'<div style="background:#fff3cd;border-left:3px solid #ffc107;padding:8px 12px;'
'margin:12px 0;border-radius:4px;">'
'<strong>Note:</strong> This assessment was completed without being scheduled '
'through the system. '
f'<strong>Reason:</strong> {self.override_reason.strip()}'
'</div>'
)
sections = [
('Assessment Details', [
('Client', client_name),
('Case', order.name),
('Assessment Date', f"{self.assessment_start_date.strftime('%B %d, %Y')} to {self.completion_date.strftime('%B %d, %Y')}"),
('Completed by', self.env.user.name),
]),
]
if self.notes:
sections.append(('Notes', [('', self.notes)]))
summary = (
f'The assessment for <strong>{client_name}</strong> ({order.name}) '
f'has been completed on {self.completion_date.strftime("%B %d, %Y")}.'
)
if is_override:
summary += f' {override_note}'
email_body = order._email_build(
title='Assessment Completed',
summary=summary,
email_type='success',
sections=sections,
note='<strong>Next step:</strong> Please submit the ADP application '
'(including pages 11-12 signed by the client) so we can proceed.',
button_url=f'{order.get_base_url()}/web#id={order.id}&model=sale.order&view_type=form',
button_text='View Case',
sender_name=order.user_id.name if order.user_id else 'The Team',
)
try:
self.env['mail.mail'].sudo().create({
'subject': f'Assessment Completed - {client_name} - {order.name}',
'body_html': email_body,
'email_to': to_email,
'email_cc': ', '.join(cc_emails) if cc_emails else False,
'model': 'sale.order',
'res_id': order.id,
'auto_delete': True,
}).send()
order.message_post(
body=Markup(
'<div class="alert alert-info" role="alert">'
'<strong>Assessment Completed email sent</strong>'
'<ul class="mb-0 mt-1"><li>To: %s</li>'
'<li>CC: %s</li></ul></div>'
) % (to_email, ', '.join(cc_emails) or 'None'),
message_type='notification',
subtype_xmlid='mail.mt_note',
)
_logger.info("Sent backend assessment completed email for %s", order.name)
except Exception as e:
_logger.error("Failed to send assessment completed email for %s: %s", order.name, e)

View File

@@ -1,18 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Assessment Completed Wizard Form View -->
<record id="view_assessment_completed_wizard_form" model="ir.ui.view">
<field name="name">fusion_claims.assessment.completed.wizard.form</field>
<field name="model">fusion_claims.assessment.completed.wizard</field>
<field name="arch" type="xml">
<form string="Assessment Completed">
<field name="sale_order_id" invisible="1"/>
<field name="is_override" invisible="1"/>
<div invisible="not is_override"
class="alert alert-warning mb-3" role="alert">
<strong>Scheduling Override:</strong>
This assessment was not scheduled through the system.
A reason is required to proceed.
</div>
<group>
<field name="sale_order_id" invisible="1"/>
<field name="completion_date"/>
<field name="notes" placeholder="Enter any notes from the assessment..."/>
<group string="Assessment Dates">
<field name="assessment_start_date"/>
<field name="completion_date"/>
</group>
<group string="Details">
<field name="notes"
placeholder="Enter any notes from the assessment..."/>
<field name="notify_authorizer"/>
</group>
</group>
<group invisible="not is_override">
<field name="override_reason"
required="is_override"
placeholder="e.g., Authorizer completed the assessment independently and sent us the application directly..."
widget="text"/>
</group>
<footer>
<button name="action_complete" type="object"
<button name="action_complete" type="object"
string="Mark Complete" class="btn-primary"
icon="fa-check"/>
<button string="Cancel" class="btn-secondary" special="cancel"/>
@@ -21,7 +44,6 @@
</field>
</record>
<!-- Action for the wizard -->
<record id="action_assessment_completed_wizard" model="ir.actions.act_window">
<field name="name">Assessment Completed</field>
<field name="res_model">fusion_claims.assessment.completed.wizard</field>

View File

@@ -34,11 +34,11 @@ class ReadyForSubmissionWizard(models.TransientModel):
# Client References (may already be filled)
client_ref_1 = fields.Char(
string='Client Reference 1',
help='First client reference number (e.g., PO number)',
help='First two letters of the client\'s first name and last two letters of their last name. Example: John Doe = JODO',
)
client_ref_2 = fields.Char(
string='Client Reference 2',
help='Second client reference number',
help='Last four digits of the client\'s health card number. Example: 1234',
)
# Reason for Application

View File

@@ -59,7 +59,7 @@
<field name="claim_authorization_date"/>
</group>
<group string="Client References">
<field name="client_ref_1" placeholder="e.g., DOJO"/>
<field name="client_ref_1" placeholder="e.g., JODO"/>
<field name="client_ref_2" placeholder="e.g., 1234"/>
</group>
</group>