feat: separate fusion field service and LTC into standalone modules, update core modules
- fusion_claims: separated field service logic, updated controllers/views - fusion_tasks: updated task views and map integration - fusion_authorizer_portal: added page 11 signing, schedule booking, migrations - fusion_shipping: new standalone shipping module (Canada Post, FedEx, DHL, Purolator) - fusion_ltc_management: new standalone LTC management module
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user