Initial commit
This commit is contained in:
335
fusion_claims/wizard/send_to_mod_wizard.py
Normal file
335
fusion_claims/wizard/send_to_mod_wizard.py
Normal file
@@ -0,0 +1,335 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2024-2026 Nexa Systems Inc.
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
|
||||
from odoo import models, fields, api, _
|
||||
from odoo.exceptions import UserError
|
||||
import base64
|
||||
import logging
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
_DOC_NAMES = {
|
||||
'x_fc_mod_drawing': 'Drawing',
|
||||
'x_fc_mod_initial_photos': 'Assessment Photos',
|
||||
'x_fc_mod_pca_document': 'Payment Commitment Agreement',
|
||||
'x_fc_mod_proof_of_delivery': 'Proof of Delivery',
|
||||
'x_fc_mod_completion_photos': 'Completion Photos',
|
||||
}
|
||||
|
||||
|
||||
class SendToModWizard(models.TransientModel):
|
||||
_name = 'fusion_claims.send.to.mod.wizard'
|
||||
_description = 'Send to March of Dimes Wizard'
|
||||
|
||||
sale_order_id = fields.Many2one('sale.order', required=True, readonly=True)
|
||||
|
||||
send_mode = fields.Selection([
|
||||
('drawing', 'Submit Drawing and Quotation'),
|
||||
('quotation', 'Re-send Quotation'),
|
||||
('completion', 'Submit POD'),
|
||||
], required=True, readonly=True)
|
||||
|
||||
# --- Recipients as contacts ---
|
||||
recipient_ids = fields.Many2many(
|
||||
'res.partner', 'send_mod_wizard_recipient_rel',
|
||||
'wizard_id', 'partner_id', string='Send To',
|
||||
)
|
||||
cc_ids = fields.Many2many(
|
||||
'res.partner', 'send_mod_wizard_cc_rel',
|
||||
'wizard_id', 'partner_id', string='CC',
|
||||
)
|
||||
|
||||
# --- Drawing mode uploads ---
|
||||
drawing_file = fields.Binary(string='Drawing')
|
||||
drawing_filename = fields.Char()
|
||||
initial_photos_file = fields.Binary(string='Initial Photos')
|
||||
initial_photos_filename = fields.Char()
|
||||
|
||||
# --- Quotation mode toggles ---
|
||||
include_quotation = fields.Boolean(string='Quotation PDF', default=True)
|
||||
include_drawing = fields.Boolean(string='Drawing', default=True)
|
||||
include_initial_photos = fields.Boolean(string='Initial Photos', default=True)
|
||||
|
||||
# --- Completion mode uploads ---
|
||||
completion_photos_file = fields.Binary(string='Completion Photos')
|
||||
completion_photos_filename = fields.Char()
|
||||
pod_file = fields.Binary(string='Proof of Delivery')
|
||||
pod_filename = fields.Char()
|
||||
|
||||
additional_notes = fields.Text(string='Notes')
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields_list):
|
||||
res = super().default_get(fields_list)
|
||||
if not self.env.context.get('active_id'):
|
||||
return res
|
||||
|
||||
order = self.env['sale.order'].browse(self.env.context['active_id'])
|
||||
res['sale_order_id'] = order.id
|
||||
|
||||
ICP = self.env['ir.config_parameter'].sudo()
|
||||
mod_default_email = ICP.get_param('fusion_claims.mod_default_email', 'hvmp@marchofdimes.ca')
|
||||
mode = self.env.context.get('mod_wizard_mode', 'quotation')
|
||||
|
||||
client = order.partner_id
|
||||
authorizer = order.x_fc_authorizer_id
|
||||
case_worker = order.x_fc_case_worker
|
||||
|
||||
# Find or create a MOD partner for the default email
|
||||
mod_partner = self._get_mod_partner(mod_default_email)
|
||||
|
||||
if mode == 'drawing':
|
||||
res['send_mode'] = 'drawing'
|
||||
# To: Client. CC: MOD + Authorizer
|
||||
res['recipient_ids'] = [(6, 0, [client.id])] if client else [(6, 0, [])]
|
||||
cc_ids = []
|
||||
if mod_partner:
|
||||
cc_ids.append(mod_partner.id)
|
||||
if authorizer:
|
||||
cc_ids.append(authorizer.id)
|
||||
res['cc_ids'] = [(6, 0, cc_ids)]
|
||||
# Pre-load files
|
||||
if order.x_fc_mod_drawing:
|
||||
res['drawing_file'] = order.x_fc_mod_drawing
|
||||
res['drawing_filename'] = order.x_fc_mod_drawing_filename
|
||||
if order.x_fc_mod_initial_photos:
|
||||
res['initial_photos_file'] = order.x_fc_mod_initial_photos
|
||||
res['initial_photos_filename'] = order.x_fc_mod_initial_photos_filename
|
||||
|
||||
elif mode == 'completion':
|
||||
res['send_mode'] = 'completion'
|
||||
# To: Case worker. CC: Authorizer
|
||||
to_ids = []
|
||||
if case_worker:
|
||||
to_ids.append(case_worker.id)
|
||||
elif mod_partner:
|
||||
to_ids.append(mod_partner.id)
|
||||
res['recipient_ids'] = [(6, 0, to_ids)]
|
||||
cc_ids = []
|
||||
if authorizer:
|
||||
cc_ids.append(authorizer.id)
|
||||
res['cc_ids'] = [(6, 0, cc_ids)]
|
||||
if order.x_fc_mod_completion_photos:
|
||||
res['completion_photos_file'] = order.x_fc_mod_completion_photos
|
||||
res['completion_photos_filename'] = order.x_fc_mod_completion_photos_filename
|
||||
if order.x_fc_mod_proof_of_delivery:
|
||||
res['pod_file'] = order.x_fc_mod_proof_of_delivery
|
||||
res['pod_filename'] = order.x_fc_mod_pod_filename
|
||||
|
||||
else:
|
||||
res['send_mode'] = 'quotation'
|
||||
res['recipient_ids'] = [(6, 0, [client.id])] if client else [(6, 0, [])]
|
||||
cc_ids = []
|
||||
if mod_partner:
|
||||
cc_ids.append(mod_partner.id)
|
||||
if authorizer:
|
||||
cc_ids.append(authorizer.id)
|
||||
res['cc_ids'] = [(6, 0, cc_ids)]
|
||||
|
||||
return res
|
||||
|
||||
def _get_mod_partner(self, email):
|
||||
"""Find or create a partner for the MOD default email."""
|
||||
if not email:
|
||||
return None
|
||||
partner = self.env['res.partner'].sudo().search([('email', '=', email)], limit=1)
|
||||
if not partner:
|
||||
partner = self.env['res.partner'].sudo().create({
|
||||
'name': 'March of Dimes Canada (HVMP)',
|
||||
'email': email,
|
||||
'is_company': True,
|
||||
'company_type': 'company',
|
||||
})
|
||||
return partner
|
||||
|
||||
def action_preview_quotation(self):
|
||||
self.ensure_one()
|
||||
return {
|
||||
'type': 'ir.actions.act_url',
|
||||
'url': f'/report/pdf/fusion_claims.report_mod_quotation/{self.sale_order_id.id}',
|
||||
'target': 'new',
|
||||
}
|
||||
|
||||
def _pro_name(self, field_name, order, orig_filename=None):
|
||||
"""Professional attachment name."""
|
||||
client = order.partner_id.name or 'Client'
|
||||
client_clean = client.replace(' ', '_').replace(',', '')
|
||||
base = _DOC_NAMES.get(field_name, field_name)
|
||||
ext = 'pdf'
|
||||
if orig_filename and '.' in orig_filename:
|
||||
ext = orig_filename.rsplit('.', 1)[-1].lower()
|
||||
return f'{base} - {client_clean} - {order.name}.{ext}'
|
||||
|
||||
def _get_field_att(self, order, field_name):
|
||||
att = self.env['ir.attachment'].sudo().search([
|
||||
('res_model', '=', 'sale.order'),
|
||||
('res_id', '=', order.id),
|
||||
('res_field', '=', field_name),
|
||||
], order='id desc', limit=1)
|
||||
if att:
|
||||
att.sudo().write({'name': self._pro_name(field_name, order, att.name)})
|
||||
return att
|
||||
|
||||
def action_send(self):
|
||||
self.ensure_one()
|
||||
order = self.sale_order_id
|
||||
client_name = order.partner_id.name or 'Client'
|
||||
client_clean = client_name.replace(' ', '_').replace(',', '')
|
||||
|
||||
to_emails = [p.email for p in self.recipient_ids if p.email]
|
||||
cc_emails = [p.email for p in self.cc_ids if p.email]
|
||||
if order.user_id and order.user_id.email:
|
||||
sr_email = order.user_id.email
|
||||
if sr_email not in to_emails and sr_email not in cc_emails:
|
||||
cc_emails.append(sr_email)
|
||||
|
||||
if not to_emails:
|
||||
raise UserError(_("Please add at least one recipient."))
|
||||
|
||||
# --- Save files and change status ---
|
||||
if self.send_mode == 'drawing':
|
||||
if not self.drawing_file:
|
||||
raise UserError(_("Drawing is required."))
|
||||
save = {
|
||||
'x_fc_mod_drawing': self.drawing_file,
|
||||
'x_fc_mod_drawing_filename': self.drawing_filename or f'Drawing - {client_name}.pdf',
|
||||
}
|
||||
if self.initial_photos_file:
|
||||
save['x_fc_mod_initial_photos'] = self.initial_photos_file
|
||||
save['x_fc_mod_initial_photos_filename'] = (
|
||||
self.initial_photos_filename or f'Assessment Photos - {client_name}.jpg')
|
||||
order.with_context(skip_all_validations=True).write(save)
|
||||
order.write({
|
||||
'x_fc_mod_status': 'quote_submitted',
|
||||
'x_fc_mod_drawing_submitted_date': fields.Date.today(),
|
||||
})
|
||||
|
||||
elif self.send_mode == 'completion':
|
||||
if not self.completion_photos_file:
|
||||
raise UserError(_("Completion Photos are required."))
|
||||
if not self.pod_file:
|
||||
raise UserError(_("Proof of Delivery is required."))
|
||||
order.with_context(skip_all_validations=True).write({
|
||||
'x_fc_mod_completion_photos': self.completion_photos_file,
|
||||
'x_fc_mod_completion_photos_filename': (
|
||||
self.completion_photos_filename or f'Completion Photos - {client_name}.jpg'),
|
||||
'x_fc_mod_proof_of_delivery': self.pod_file,
|
||||
'x_fc_mod_pod_filename': (
|
||||
self.pod_filename or f'Proof of Delivery - {client_name}.pdf'),
|
||||
})
|
||||
order.write({
|
||||
'x_fc_mod_status': 'pod_submitted',
|
||||
'x_fc_mod_pod_submitted_date': fields.Date.today(),
|
||||
})
|
||||
|
||||
# --- Collect attachments ---
|
||||
att_ids = []
|
||||
att_names = []
|
||||
Att = self.env['ir.attachment'].sudo()
|
||||
|
||||
if self.send_mode in ('drawing', 'quotation'):
|
||||
try:
|
||||
report = self.env.ref('fusion_claims.action_report_mod_quotation')
|
||||
pdf, _ = report._render_qweb_pdf(report.id, [order.id])
|
||||
a = Att.create({
|
||||
'name': f'Quotation - {client_clean} - {order.name}.pdf',
|
||||
'type': 'binary', 'datas': base64.b64encode(pdf),
|
||||
'res_model': 'sale.order', 'res_id': order.id,
|
||||
'mimetype': 'application/pdf',
|
||||
})
|
||||
att_ids.append(a.id)
|
||||
att_names.append(a.name)
|
||||
except Exception as e:
|
||||
_logger.error(f"Quotation PDF failed: {e}")
|
||||
|
||||
if self.send_mode == 'drawing' or self.include_drawing:
|
||||
a = self._get_field_att(order, 'x_fc_mod_drawing')
|
||||
if a:
|
||||
att_ids.append(a.id)
|
||||
att_names.append(a.name)
|
||||
if self.send_mode == 'drawing' or self.include_initial_photos:
|
||||
a = self._get_field_att(order, 'x_fc_mod_initial_photos')
|
||||
if a:
|
||||
att_ids.append(a.id)
|
||||
att_names.append(a.name)
|
||||
|
||||
elif self.send_mode == 'completion':
|
||||
a = self._get_field_att(order, 'x_fc_mod_completion_photos')
|
||||
if a:
|
||||
att_ids.append(a.id)
|
||||
att_names.append(a.name)
|
||||
a = self._get_field_att(order, 'x_fc_mod_proof_of_delivery')
|
||||
if a:
|
||||
att_ids.append(a.id)
|
||||
att_names.append(a.name)
|
||||
|
||||
if not att_ids:
|
||||
raise UserError(_("No documents to send."))
|
||||
|
||||
# --- Build email ---
|
||||
sender = (order.user_id or self.env.user).name
|
||||
if self.send_mode in ('drawing', 'quotation'):
|
||||
title = 'Accessibility Modification Proposal'
|
||||
summary = (
|
||||
f'Dear <strong>{client_name}</strong>,<br/><br/>'
|
||||
f'Please find attached the quotation, drawing and assessment photos for your '
|
||||
f'accessibility modification project. Please review and let us know if you '
|
||||
f'have any questions.')
|
||||
else:
|
||||
title = 'Completion Photos and Proof of Delivery'
|
||||
summary = (
|
||||
f'Please find attached the completion photos and signed proof of delivery '
|
||||
f'for <strong>{client_name}</strong>.')
|
||||
|
||||
body = order._mod_email_build(
|
||||
title=title, summary=summary,
|
||||
email_type='info' if self.send_mode != 'completion' else 'success',
|
||||
sections=[('Project Details', order._build_mod_case_detail_rows(
|
||||
include_amounts=self.send_mode in ('drawing', 'quotation')))],
|
||||
note=self.additional_notes or None,
|
||||
attachments_note=', '.join(att_names),
|
||||
sender_name=sender,
|
||||
)
|
||||
|
||||
prefixes = {
|
||||
'drawing': 'Quotation and Drawing',
|
||||
'quotation': 'Quotation and Documents',
|
||||
'completion': 'Completion Photos and Proof of Delivery',
|
||||
}
|
||||
ref = order.x_fc_case_reference
|
||||
subj = f'{prefixes[self.send_mode]} - {client_name} - {order.name}'
|
||||
if ref:
|
||||
subj = f'{prefixes[self.send_mode]} - {ref} - {client_name}'
|
||||
|
||||
try:
|
||||
self.env['mail.mail'].sudo().create({
|
||||
'subject': subj,
|
||||
'body_html': body,
|
||||
'email_to': ', '.join(to_emails),
|
||||
'email_cc': ', '.join(cc_emails) if cc_emails else '',
|
||||
'model': 'sale.order',
|
||||
'res_id': order.id,
|
||||
'attachment_ids': [(6, 0, att_ids)],
|
||||
}).send()
|
||||
|
||||
to_str = ', '.join(to_emails)
|
||||
cc_str = ', '.join(cc_emails) if cc_emails else None
|
||||
order._email_chatter_log(
|
||||
f'{prefixes[self.send_mode]} sent', to_str, cc_str,
|
||||
[f'Documents: {", ".join(att_names)}'],
|
||||
)
|
||||
|
||||
return {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'display_notification',
|
||||
'params': {
|
||||
'title': 'Sent',
|
||||
'message': f'Documents sent to {to_str}',
|
||||
'type': 'success', 'sticky': False,
|
||||
'next': {'type': 'ir.actions.act_window_close'},
|
||||
},
|
||||
}
|
||||
except Exception as e:
|
||||
_logger.error(f"Send failed for {order.name}: {e}")
|
||||
raise UserError(_(f"Failed to send: {e}"))
|
||||
Reference in New Issue
Block a user