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:
gsinghpal
2026-04-23 00:01:15 -04:00
parent f9e1b62409
commit 0342535b9f
10 changed files with 351 additions and 7 deletions

View File

@@ -5,7 +5,7 @@
{
'name': 'Fusion Plating — Certificates',
'version': '19.0.3.2.0',
'version': '19.0.4.0.0',
'category': 'Manufacturing/Plating',
'summary': 'Certificate registry for CoC, thickness reports, and quality documents.',
'description': """

View File

@@ -48,3 +48,42 @@ class ResPartner(models.Model):
'thickness reading has been logged for the MO. Leave off '
'for commercial customers.',
)
# ---- Sub 6 — Per-contact communication routing -----------------------
# These five flags live on CHILD contacts under a company partner.
# When every contact under a company leaves them blank, the resolver
# falls back to the company's own `email` — matching the pre-Sub-6
# behaviour exactly. Admins opt in to per-contact routing by ticking
# the relevant flag on each contact row.
x_fc_receives_certs = fields.Boolean(
string='Receives Certificates',
default=False, tracking=True,
help='This contact receives CoC PDFs, thickness reports, and '
'other quality documents when a job ships.',
)
x_fc_receives_qc = fields.Boolean(
string='Receives QC Alerts',
default=False, tracking=True,
help='This contact receives NCR, CAPA, quality-hold, and contract-'
'review notifications.',
)
x_fc_receives_quotes_so = fields.Boolean(
string='Receives Quotes & SOs',
default=False, tracking=True,
help='This contact receives quote sends, sale order acknowledgements, '
'and order confirmations.',
)
x_fc_receives_invoices = fields.Boolean(
string='Receives Invoices',
default=False, tracking=True,
help='This contact receives invoices, payment receipts, and '
'dunning communications.',
)
x_fc_is_global_contact = fields.Boolean(
string='Global Contact',
default=False, tracking=True,
help='Firehose. When set, this contact receives every outbound '
'stream regardless of the per-stream flags above. Typical '
'use: a primary account-manager contact who wants full '
'visibility into everything the shop sends out.',
)

View File

@@ -37,4 +37,52 @@
</field>
</record>
<!-- Sub 6 — Per-contact routing flags on the contact's own form
(opens when editing a child contact row). Applied to every
res.partner form so delivery-location partners and their child
contacts both surface the same flags. -->
<record id="view_partner_form_fp_contact_routing_flags"
model="ir.ui.view">
<field name="name">res.partner.form.fp.contact.routing.flags</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="priority">30</field>
<field name="arch" type="xml">
<xpath expr="//notebook" position="inside">
<page string="Communication Routing"
name="fp_contact_routing"
invisible="is_company == True">
<p class="text-muted">
Tick the streams this contact should receive. Leave all
blank to let the customer's company-level email handle
everything (default behaviour). Set
<b>Global Contact</b> for a primary account-manager
contact who wants visibility into every outbound stream.
</p>
<group>
<group>
<field name="x_fc_is_global_contact"
widget="boolean_toggle"/>
</group>
</group>
<group string="Per-stream subscriptions"
invisible="x_fc_is_global_contact">
<group>
<field name="x_fc_receives_certs"
widget="boolean_toggle"/>
<field name="x_fc_receives_qc"
widget="boolean_toggle"/>
</group>
<group>
<field name="x_fc_receives_quotes_so"
widget="boolean_toggle"/>
<field name="x_fc_receives_invoices"
widget="boolean_toggle"/>
</group>
</group>
</page>
</xpath>
</field>
</record>
</odoo>