336 lines
13 KiB
Python
336 lines
13 KiB
Python
# -*- 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}"))
|