243 lines
8.9 KiB
Python
243 lines
8.9 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Fusion Claims - Professional Email Builder Mixin
|
|
# Provides consistent, dark/light mode safe email templates across all modules.
|
|
|
|
from odoo import models
|
|
|
|
|
|
class FusionEmailBuilderMixin(models.AbstractModel):
|
|
_name = 'fusion.email.builder.mixin'
|
|
_description = 'Fusion Email Builder Mixin'
|
|
|
|
# ------------------------------------------------------------------
|
|
# Color constants
|
|
# ------------------------------------------------------------------
|
|
_EMAIL_COLORS = {
|
|
'info': '#2B6CB0',
|
|
'success': '#38a169',
|
|
'attention': '#d69e2e',
|
|
'urgent': '#c53030',
|
|
}
|
|
|
|
# ------------------------------------------------------------------
|
|
# Public API
|
|
# ------------------------------------------------------------------
|
|
|
|
def _email_build(
|
|
self,
|
|
title,
|
|
summary,
|
|
sections=None,
|
|
note=None,
|
|
note_color=None,
|
|
email_type='info',
|
|
attachments_note=None,
|
|
button_url=None,
|
|
button_text='View Case Details',
|
|
sender_name=None,
|
|
extra_html='',
|
|
):
|
|
"""Build a complete professional email HTML string.
|
|
|
|
Args:
|
|
title: Email heading (e.g. "Application Approved")
|
|
summary: One-sentence summary HTML (may contain <strong> tags)
|
|
sections: list of (heading, rows) where rows is list of (label, value)
|
|
e.g. [('Case Details', [('Client', 'John'), ('Case', 'S30073')])]
|
|
note: Optional note/next-steps text (plain or HTML)
|
|
note_color: Override left-border color for note (default uses email_type)
|
|
email_type: 'info' | 'success' | 'attention' | 'urgent'
|
|
attachments_note: Optional string listing attached files
|
|
button_url: Optional CTA button URL
|
|
button_text: CTA button label
|
|
sender_name: Name for sign-off (defaults to current user)
|
|
extra_html: Any additional HTML to insert before sign-off
|
|
"""
|
|
accent = self._EMAIL_COLORS.get(email_type, self._EMAIL_COLORS['info'])
|
|
company = self._get_company_info()
|
|
|
|
parts = []
|
|
# -- Wrapper open + accent bar
|
|
parts.append(
|
|
f'<div style="font-family:-apple-system,BlinkMacSystemFont,\'Segoe UI\',Roboto,Arial,sans-serif;'
|
|
f'max-width:600px;margin:0 auto;color:#2d3748;">'
|
|
f'<div style="height:4px;background-color:{accent};"></div>'
|
|
f'<div style="background:#ffffff;padding:32px 28px;border:1px solid #e2e8f0;border-top:none;">'
|
|
)
|
|
|
|
# -- Company name
|
|
parts.append(
|
|
f'<p style="color:{accent};font-size:13px;font-weight:600;letter-spacing:0.5px;'
|
|
f'text-transform:uppercase;margin:0 0 24px 0;">{company["name"]}</p>'
|
|
)
|
|
|
|
# -- Title
|
|
parts.append(
|
|
f'<h2 style="color:#1a202c;font-size:22px;font-weight:700;'
|
|
f'margin:0 0 6px 0;line-height:1.3;">{title}</h2>'
|
|
)
|
|
|
|
# -- Summary
|
|
parts.append(
|
|
f'<p style="color:#718096;font-size:15px;line-height:1.5;'
|
|
f'margin:0 0 24px 0;">{summary}</p>'
|
|
)
|
|
|
|
# -- Sections (details tables)
|
|
if sections:
|
|
for heading, rows in sections:
|
|
parts.append(self._email_section(heading, rows))
|
|
|
|
# -- Note / Next Steps
|
|
if note:
|
|
nc = note_color or accent
|
|
parts.append(self._email_note(note, nc))
|
|
|
|
# -- Extra HTML
|
|
if extra_html:
|
|
parts.append(extra_html)
|
|
|
|
# -- Attachment note
|
|
if attachments_note:
|
|
parts.append(self._email_attachment_note(attachments_note))
|
|
|
|
# -- CTA Button
|
|
if button_url:
|
|
parts.append(self._email_button(button_url, button_text, accent))
|
|
|
|
# -- Sign-off
|
|
signer = sender_name or (self.env.user.name if self.env.user else '')
|
|
parts.append(
|
|
f'<p style="color:#2d3748;font-size:14px;line-height:1.6;margin:24px 0 0 0;">'
|
|
f'Best regards,<br/>'
|
|
f'<strong>{signer}</strong><br/>'
|
|
f'<span style="color:#718096;">{company["name"]}</span></p>'
|
|
)
|
|
|
|
# -- Close content card
|
|
parts.append('</div>')
|
|
|
|
# -- Footer
|
|
footer_parts = [company['name']]
|
|
if company['phone']:
|
|
footer_parts.append(company['phone'])
|
|
if company['email']:
|
|
footer_parts.append(company['email'])
|
|
footer_text = ' · '.join(footer_parts)
|
|
|
|
parts.append(
|
|
f'<div style="padding:16px 28px;text-align:center;">'
|
|
f'<p style="color:#a0aec0;font-size:11px;line-height:1.5;margin:0;">'
|
|
f'{footer_text}<br/>'
|
|
f'This is an automated notification from the ADP Claims Management System.</p>'
|
|
f'</div>'
|
|
)
|
|
|
|
# -- Close wrapper
|
|
parts.append('</div>')
|
|
|
|
return ''.join(parts)
|
|
|
|
# ------------------------------------------------------------------
|
|
# Building blocks
|
|
# ------------------------------------------------------------------
|
|
|
|
def _email_section(self, heading, rows):
|
|
"""Build a labeled details table section.
|
|
|
|
Args:
|
|
heading: Section title (e.g. "Case Details")
|
|
rows: list of (label, value) tuples. Value can be plain text or HTML.
|
|
"""
|
|
if not rows:
|
|
return ''
|
|
|
|
html = (
|
|
'<table style="width:100%;border-collapse:collapse;margin:0 0 24px 0;">'
|
|
f'<tr><td colspan="2" style="padding:10px 14px;font-size:12px;font-weight:600;'
|
|
f'color:#718096;text-transform:uppercase;letter-spacing:0.5px;'
|
|
f'border-bottom:2px solid #e2e8f0;">{heading}</td></tr>'
|
|
)
|
|
|
|
for label, value in rows:
|
|
if value is None or value == '' or value is False:
|
|
continue
|
|
html += (
|
|
f'<tr>'
|
|
f'<td style="padding:10px 14px;color:#718096;font-size:14px;'
|
|
f'border-bottom:1px solid #f0f0f0;width:35%;">{label}</td>'
|
|
f'<td style="padding:10px 14px;color:#2d3748;font-size:14px;'
|
|
f'border-bottom:1px solid #f0f0f0;">{value}</td>'
|
|
f'</tr>'
|
|
)
|
|
|
|
html += '</table>'
|
|
return html
|
|
|
|
def _email_note(self, text, color='#2B6CB0'):
|
|
"""Build a left-border accent note block."""
|
|
return (
|
|
f'<div style="border-left:3px solid {color};padding:12px 16px;'
|
|
f'margin:0 0 24px 0;background:#f7fafc;">'
|
|
f'<p style="margin:0;font-size:14px;line-height:1.5;color:#2d3748;">{text}</p>'
|
|
f'</div>'
|
|
)
|
|
|
|
def _email_button(self, url, text='View Case Details', color='#2B6CB0'):
|
|
"""Build a centered CTA button."""
|
|
return (
|
|
f'<p style="text-align:center;margin:28px 0;">'
|
|
f'<a href="{url}" style="display:inline-block;background:{color};color:#ffffff;'
|
|
f'padding:12px 28px;text-decoration:none;border-radius:6px;'
|
|
f'font-size:14px;font-weight:600;">{text}</a></p>'
|
|
)
|
|
|
|
def _email_attachment_note(self, description):
|
|
"""Build a dashed-border attachment callout.
|
|
|
|
Args:
|
|
description: e.g. "ADP Application (PDF), XML Data File"
|
|
"""
|
|
return (
|
|
f'<div style="padding:10px 14px;border:1px dashed #e2e8f0;border-radius:6px;'
|
|
f'margin:0 0 24px 0;">'
|
|
f'<p style="margin:0;font-size:13px;color:#718096;">'
|
|
f'<strong style="color:#2d3748;">Attached:</strong> {description}</p>'
|
|
f'</div>'
|
|
)
|
|
|
|
def _email_status_badge(self, label, color='#2B6CB0'):
|
|
"""Return an inline status badge/pill HTML snippet."""
|
|
# Pick a light background tint for the badge
|
|
bg_map = {
|
|
'#38a169': '#f0fff4',
|
|
'#2B6CB0': '#ebf4ff',
|
|
'#d69e2e': '#fefcbf',
|
|
'#c53030': '#fff5f5',
|
|
}
|
|
bg = bg_map.get(color, '#ebf4ff')
|
|
return (
|
|
f'<span style="display:inline-block;background:{bg};color:{color};'
|
|
f'padding:2px 10px;border-radius:12px;font-size:12px;font-weight:600;">'
|
|
f'{label}</span>'
|
|
)
|
|
|
|
# ------------------------------------------------------------------
|
|
# Helpers
|
|
# ------------------------------------------------------------------
|
|
|
|
def _get_company_info(self):
|
|
"""Return company name, phone, email for email templates."""
|
|
company = getattr(self, 'company_id', None) or self.env.company
|
|
return {
|
|
'name': company.name or 'Our Company',
|
|
'phone': company.phone or '',
|
|
'email': company.email or '',
|
|
}
|
|
|
|
def _email_is_enabled(self):
|
|
"""Check if email notifications are enabled in settings."""
|
|
ICP = self.env['ir.config_parameter'].sudo()
|
|
val = ICP.get_param('fusion_claims.enable_email_notifications', 'True')
|
|
return val.lower() in ('true', '1', 'yes')
|