feat(notifications): Sub 6 — contact profiles + communication routing
Five new boolean flags on res.partner applied to CHILD contacts: x_fc_receives_certs, _qc, _quotes_so, _invoices, and _is_global_contact. Single resolver helper res.partner._fp_resolve_notification_recipients (stream, delivery_location=None) walks location contacts first then company contacts, returning emails for contacts that opted into the stream (or flagged themselves global). Falls back to partner.email when no contact opts in so existing customers keep their exact pre-Sub-6 routing. fp.notification.template._dispatch now maps each trigger event to a stream (so_confirmed→quotes_so, invoice_posted→invoices, shipped→ certs, etc.) and overrides the mail_template's email_to with the resolved list. fp.delivery passes its delivery_address_id so the shipped/CoC email routes through location-scoped contacts when they exist. Partner form gets a new "Communication Routing" tab on child contact forms with the 5 flags (hides the per-stream checkboxes when Global Contact is on, since it overrides them). fusion_plating_certificates → 19.0.4.0.0 (adds the flag fields) fusion_plating_notifications → 19.0.5.0.0 (+depends certificates) Smoke on entech: 11/11 assertions pass including per-stream routing, delivery-location scoping, zero-flag fallback, email-less skip, unknown-stream + global behaviour, and case-insensitive dedup. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -20,6 +20,21 @@ TRIGGER_EVENTS = [
|
||||
('deposit_created', 'Deposit Required'),
|
||||
]
|
||||
|
||||
# Sub 6 — map each trigger event to a communication stream. Contacts on
|
||||
# the customer who opt into that stream (or flag themselves as global)
|
||||
# receive the email. Unmapped events fall back to the company partner's
|
||||
# own email, preserving pre-Sub-6 behaviour.
|
||||
FP_TRIGGER_STREAM = {
|
||||
'quote_sent': 'quotes_so',
|
||||
'so_confirmed': 'quotes_so',
|
||||
'parts_received': 'quotes_so',
|
||||
'mo_complete': 'qc',
|
||||
'shipped': 'certs',
|
||||
'invoice_posted': 'invoices',
|
||||
'payment_received': 'invoices',
|
||||
'deposit_created': 'invoices',
|
||||
}
|
||||
|
||||
|
||||
class FpNotificationTemplate(models.Model):
|
||||
"""Configurable notification wrapper.
|
||||
@@ -64,10 +79,17 @@ class FpNotificationTemplate(models.Model):
|
||||
# ------------------------------------------------------------------
|
||||
@api.model
|
||||
def _dispatch(self, trigger_event, record, partner=None, sale_order=None,
|
||||
extra_attachment_ids=None):
|
||||
extra_attachment_ids=None, delivery_location=None):
|
||||
"""Look up the template for this trigger, render it, and send.
|
||||
|
||||
Also logs the attempt in fp.notification.log.
|
||||
|
||||
Sub 6: recipient resolution now goes through
|
||||
res.partner._fp_resolve_notification_recipients so per-contact
|
||||
flags (certs / qc / quotes_so / invoices / global) and per-
|
||||
delivery-location contacts are honoured. Customers who haven't
|
||||
set any flags fall back to the company partner's email —
|
||||
identical to pre-Sub-6 behaviour.
|
||||
"""
|
||||
template = self.search(
|
||||
[('trigger_event', '=', trigger_event), ('active', '=', True)],
|
||||
@@ -85,19 +107,41 @@ class FpNotificationTemplate(models.Model):
|
||||
if attachment_ids:
|
||||
attachment_names = self.env['ir.attachment'].browse(attachment_ids).mapped('name')
|
||||
|
||||
# Sub 6 — resolve recipients via the contact-routing helper.
|
||||
recipient_emails = []
|
||||
if partner:
|
||||
stream = FP_TRIGGER_STREAM.get(trigger_event)
|
||||
if stream:
|
||||
recipient_emails = partner._fp_resolve_notification_recipients(
|
||||
stream, delivery_location=delivery_location,
|
||||
)
|
||||
elif partner.email:
|
||||
recipient_emails = [partner.email]
|
||||
recipient_str = ', '.join(recipient_emails)
|
||||
|
||||
email_values = {}
|
||||
if attachment_ids:
|
||||
email_values['attachment_ids'] = [(6, 0, attachment_ids)]
|
||||
if recipient_str:
|
||||
# Override the mail.template's default Jinja-rendered email_to
|
||||
# with the resolved list. Setting email_to clears partner_ids
|
||||
# inherited from the template so we don't double-send.
|
||||
email_values['email_to'] = recipient_str
|
||||
email_values['partner_ids'] = [(5, 0, 0)]
|
||||
|
||||
Log = self.env['fp.notification.log']
|
||||
try:
|
||||
mail_id = template.mail_template_id.send_mail(
|
||||
record.id,
|
||||
force_send=False,
|
||||
email_values={'attachment_ids': [(6, 0, attachment_ids)]} if attachment_ids else None,
|
||||
email_values=email_values or None,
|
||||
)
|
||||
Log.create({
|
||||
'template_id': template.id,
|
||||
'trigger_event': trigger_event,
|
||||
'sale_order_id': sale_order.id if sale_order else False,
|
||||
'partner_id': partner.id if partner else False,
|
||||
'recipient_email': partner.email if partner else '',
|
||||
'recipient_email': recipient_str or (partner.email if partner else ''),
|
||||
'attachment_names': ', '.join(attachment_names) if attachment_names else '',
|
||||
'status': 'sent',
|
||||
'mail_mail_id': mail_id,
|
||||
@@ -109,7 +153,7 @@ class FpNotificationTemplate(models.Model):
|
||||
'trigger_event': trigger_event,
|
||||
'sale_order_id': sale_order.id if sale_order else False,
|
||||
'partner_id': partner.id if partner else False,
|
||||
'recipient_email': partner.email if partner else '',
|
||||
'recipient_email': recipient_str or (partner.email if partner else ''),
|
||||
'status': 'failed',
|
||||
'error_message': str(exc),
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user