chore(plating): de-dash shipped code + intake-neutral customer emails

Replace em-dashes and en-dashes with hyphens across 789 shipped source
files (py/xml/js/scss) so the delivered module reads as human-written;
em-dashes had become a recognizable AI-generated tell. Internal .md dev
notes are excluded. The WO-sticker mojibake strippers keep their dash
search targets (now written — / –). No logic changes: comments
and display strings only; validated with py_compile + lxml parse.

Rewrite the 7 customer notification emails to be intake-neutral
(ship-in / drop-off / pickup) and repair-aware, and fix the Shipped
email documents line (packing slip vs bill of lading; certificate only
when issued). Subjects use a hyphen separator.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-06-05 00:16:19 -04:00
parent c9eb61ee0c
commit 8c76a16366
789 changed files with 4692 additions and 4692 deletions

View File

@@ -4,7 +4,7 @@
# Part of the Fusion Plating product family.
{
'name': 'Fusion Plating Notifications',
'name': 'Fusion Plating - Notifications',
'version': '19.0.7.1.0',
'category': 'Manufacturing/Plating',
'summary': 'Auto-email notifications at workflow milestones with configurable templates, PDF attachments, and audit log.',

View File

@@ -9,7 +9,7 @@
fp.job state transitions through awaiting_cert. Recipients are
resolved by _fp_resolve_cert_authority_users (QM / Manager / Owner
via all_group_ids, transitive). The mail templates are bound to
fp.job the source record passed to _dispatch.
fp.job - the source record passed to _dispatch.
noupdate="1" so admin edits in the UI survive -u (per CLAUDE.md
Rule 22 / mail-template gotcha).
@@ -25,7 +25,7 @@
<!-- Only reference CORE fp.job fields here. The notifications
module loads BEFORE fusion_plating_jobs in dep order, and
mail.template parsing renders subject/body once to validate
fields added by inheriting modules (display_wo_name,
- fields added by inheriting modules (display_wo_name,
part_catalog_id) would AttributeError at parse time. -->
<field name="subject">🏷️ Job {{ object.name }} ready for CoC issuance</field>
<field name="email_from">{{ (object.company_id.email or user.email) }}</field>
@@ -58,7 +58,7 @@
<tr style="border-bottom: 1px solid rgba(128,128,128,0.25);">
<td style="padding: 8px 4px;">Recipe</td>
<td style="padding: 8px 4px; text-align: right;">
<t t-out="(object.recipe_id.name if object.recipe_id else '') or ''"/>
<t t-out="(object.recipe_id.name if object.recipe_id else '') or '-'"/>
</td>
</tr>
</table>
@@ -85,12 +85,12 @@
</record>
<!-- ============================================================= -->
<!-- 2. Cert Voided Please Re-Issue (Urgent, #DC2626) -->
<!-- 2. Cert Voided - Please Re-Issue (Urgent, #DC2626) -->
<!-- ============================================================= -->
<record id="fp_mail_template_cert_voided_re_notify" model="mail.template">
<field name="name">FP: Cert Voided Re-Issue</field>
<field name="name">FP: Cert Voided - Re-Issue</field>
<field name="model_id" ref="fusion_plating.model_fp_job"/>
<field name="subject">⚠️ Job {{ object.name }} CoC voided please re-issue</field>
<field name="subject">⚠️ Job {{ object.name }} CoC voided - please re-issue</field>
<field name="email_from">{{ (object.company_id.email or user.email) }}</field>
<field name="auto_delete" eval="True"/>
<field name="body_html" type="html">
@@ -99,7 +99,7 @@
<div style="font-size: 11px; text-transform: uppercase; letter-spacing: 1px; color: #DC2626; font-weight: 600; margin-bottom: 8px;">
Cert Regression
</div>
<h2 style="margin: 0 0 8px 0; font-size: 22px; font-weight: bold;">CoC Voided Please Re-Issue</h2>
<h2 style="margin: 0 0 8px 0; font-size: 22px; font-weight: bold;">CoC Voided - Please Re-Issue</h2>
<p style="margin: 0 0 20px 0; font-size: 15px; opacity: 0.75;">
A previously-issued CoC for job
<strong t-out="object.name"/>
@@ -117,7 +117,7 @@
</record>
<record id="fp_notif_cert_voided_re_notify" model="fp.notification.template">
<field name="name">Cert Voided Re-Issue Required</field>
<field name="name">Cert Voided - Re-Issue Required</field>
<field name="trigger_event">cert_voided_re_notify</field>
<field name="mail_template_id" ref="fp_mail_template_cert_voided_re_notify"/>
<field name="active" eval="True"/>

View File

@@ -16,7 +16,7 @@
<record id="fp_mail_template_quote_sent" model="mail.template">
<field name="name">FP: Quotation Sent</field>
<field name="model_id" ref="sale.model_sale_order"/>
<field name="subject">Quotation {{ object.name }} Electroless Nickel Technologies Inc. (ENTECH)</field>
<field name="subject">Quotation {{ object.name }} - Electroless Nickel Technologies Inc. (ENTECH)</field>
<field name="email_from">{{ (object.company_id.email or user.email) }}</field>
<field name="email_to">{{ object.partner_id.email }}</field>
<field name="auto_delete" eval="True"/>
@@ -49,7 +49,7 @@
</tr>
</table>
<div style="border-left: 3px solid #2B6CB0; padding: 12px 16px; margin: 20px 0; font-size: 14px;">
<strong>Next steps:</strong> Reply with a PO number or signed copy to accept. Once we receive your parts, we'll confirm receipt and begin production.
<strong>Next steps:</strong> Reply with a PO number or signed copy to accept. Once your parts are with us, we'll confirm receipt and get started.
</div>
<div style="margin-top: 32px; font-size: 14px;">
Best regards,<br/>
@@ -71,7 +71,7 @@
<record id="fp_mail_template_so_confirmed" model="mail.template">
<field name="name">FP: Order Confirmation</field>
<field name="model_id" ref="sale.model_sale_order"/>
<field name="subject">Order Confirmed {{ object.name }}</field>
<field name="subject">Order Confirmed - {{ object.name }}</field>
<field name="email_from">{{ (object.company_id.email or user.email) }}</field>
<field name="email_to">{{ object.partner_id.email }}</field>
<field name="auto_delete" eval="True"/>
@@ -83,7 +83,7 @@
</div>
<h2 style="margin: 0 0 8px 0; font-size: 22px; font-weight: bold;">Order Confirmed</h2>
<p style="margin: 0 0 20px 0; font-size: 15px; opacity: 0.65;">
Thank you, <t t-out="object.partner_id.name or ''"/>. We have your order and will notify you the moment your parts arrive at our facility.
Thank you, <t t-out="object.partner_id.name or ''"/>. Your order is confirmed, and we'll keep you posted at every step.
</p>
<table style="width: 100%; border-collapse: collapse; margin: 20px 0;">
<tr style="border-bottom: 2px solid rgba(128,128,128,0.35);">
@@ -96,11 +96,11 @@
</tr>
<tr style="border-bottom: 1px solid rgba(128,128,128,0.25); background: rgba(128,128,128,0.06);">
<td style="padding: 8px 4px;">Customer PO</td>
<td style="padding: 8px 4px; text-align: right;"><t t-out="object.x_fc_po_number or ''"/></td>
<td style="padding: 8px 4px; text-align: right;"><t t-out="object.x_fc_po_number or '-'"/></td>
</tr>
<tr style="border-bottom: 1px solid rgba(128,128,128,0.25);">
<td style="padding: 8px 4px;">Order Date</td>
<td style="padding: 8px 4px; text-align: right;"><t t-out="format_datetime(object.date_order, tz=(user.tz or object.company_id.x_fc_default_tz or 'UTC'), dt_format='MMM d, y') if object.date_order else ''"/></td>
<td style="padding: 8px 4px; text-align: right;"><t t-out="format_datetime(object.date_order, tz=(user.tz or object.company_id.x_fc_default_tz or 'UTC'), dt_format='MMM d, y') if object.date_order else '-'"/></td>
</tr>
<tr style="border-bottom: 1px solid rgba(128,128,128,0.25); background: rgba(128,128,128,0.06);">
<td style="padding: 8px 4px;"><strong>Total</strong></td>
@@ -108,7 +108,7 @@
</tr>
</table>
<div style="border-left: 3px solid #38a169; padding: 12px 16px; margin: 20px 0; font-size: 14px;">
<strong>What's next:</strong> Ship your parts to our facility. We'll inspect on arrival, run the process, and keep you posted at each milestone.
<strong>What's next:</strong> If your parts aren't already with us, ship or drop them off at your convenience. Once they're on-site, we'll inspect them, complete the work, and update you at each milestone.
</div>
<div style="margin-top: 32px; font-size: 14px;">
Best regards,<br/>
@@ -130,7 +130,7 @@
<record id="fp_mail_template_parts_received" model="mail.template">
<field name="name">FP: Parts Received</field>
<field name="model_id" ref="fusion_plating_receiving.model_fp_receiving"/>
<field name="subject">Parts Received {{ object.sale_order_id.name or object.name }}</field>
<field name="subject">Parts Received - {{ object.sale_order_id.name or object.name }}</field>
<field name="email_from">{{ (object.sale_order_id.company_id.email or user.email) }}</field>
<field name="email_to">{{ object.partner_id.email }}</field>
<field name="auto_delete" eval="True"/>
@@ -142,7 +142,7 @@
</div>
<h2 style="margin: 0 0 8px 0; font-size: 22px; font-weight: bold;">Parts Received</h2>
<p style="margin: 0 0 20px 0; font-size: 15px; opacity: 0.65;">
Good news, <t t-out="object.partner_id.name or ''"/>. Your parts have arrived at our facility and passed incoming inspection.
Good news, <t t-out="object.partner_id.name or ''"/>! Your parts are in and have passed incoming inspection.
</p>
<table style="width: 100%; border-collapse: collapse; margin: 20px 0;">
<tr style="border-bottom: 2px solid rgba(128,128,128,0.35);">
@@ -151,7 +151,7 @@
</tr>
<tr style="border-bottom: 1px solid rgba(128,128,128,0.25);">
<td style="padding: 8px 4px;">Sale Order</td>
<td style="padding: 8px 4px; text-align: right; font-family: monospace;"><t t-out="object.sale_order_id.name or ''"/></td>
<td style="padding: 8px 4px; text-align: right; font-family: monospace;"><t t-out="object.sale_order_id.name or '-'"/></td>
</tr>
<tr style="border-bottom: 1px solid rgba(128,128,128,0.25); background: rgba(128,128,128,0.06);">
<td style="padding: 8px 4px;">Receiving Reference</td>
@@ -163,7 +163,7 @@
</tr>
</table>
<div style="border-left: 3px solid #2B6CB0; padding: 12px 16px; margin: 20px 0; font-size: 14px;">
<strong>Next step:</strong> Your job has entered our production queue. Expect a manufacturing-complete email once the job is through processing.
<strong>Next step:</strong> Your job is now in our queue. We'll keep you posted as it moves through the shop.
</div>
<div style="margin-top: 32px; font-size: 14px;">
Best regards,<br/>
@@ -177,7 +177,7 @@
</field>
</record>
<!-- Phase 5 (Sub 11) fp_mail_template_mo_complete removed.
<!-- Phase 5 (Sub 11) - fp_mail_template_mo_complete removed.
The native equivalent fires from fp.job.button_mark_done via
fp.notification.template's `job_complete` trigger, defined
in fp_notification_template_data.xml. -->
@@ -191,7 +191,7 @@
<record id="fp_mail_template_shipment_labeled" model="mail.template">
<field name="name">FP: Shipping Label Generated</field>
<field name="model_id" ref="fusion_shipping.model_fusion_shipment"/>
<field name="subject">Tracking #{{ object.tracking_number }} your order is being prepared for shipment</field>
<field name="subject">Tracking #{{ object.tracking_number }} - your order is being prepared for shipment</field>
<field name="email_from">{{ (object.company_id.email or user.email) }}</field>
<field name="email_to">{{ (object.sale_order_id and object.sale_order_id.partner_id.email) or '' }}</field>
<field name="auto_delete" eval="True"/>
@@ -212,15 +212,15 @@
</tr>
<tr style="border-bottom: 1px solid rgba(128,128,128,0.25);">
<td style="padding: 8px 4px;">Sale Order</td>
<td style="padding: 8px 4px; text-align: right; font-family: monospace;"><t t-out="object.sale_order_id.name or ''"/></td>
<td style="padding: 8px 4px; text-align: right; font-family: monospace;"><t t-out="object.sale_order_id.name or '-'"/></td>
</tr>
<tr style="border-bottom: 1px solid rgba(128,128,128,0.25); background: rgba(128,128,128,0.06);">
<td style="padding: 8px 4px;">Carrier</td>
<td style="padding: 8px 4px; text-align: right;"><t t-out="object.carrier_id.name or ''"/></td>
<td style="padding: 8px 4px; text-align: right;"><t t-out="object.carrier_id.name or '-'"/></td>
</tr>
<tr style="border-bottom: 1px solid rgba(128,128,128,0.25);">
<td style="padding: 8px 4px;">Tracking Number</td>
<td style="padding: 8px 4px; text-align: right; font-family: monospace; font-weight: bold;"><t t-out="object.tracking_number or ''"/></td>
<td style="padding: 8px 4px; text-align: right; font-family: monospace; font-weight: bold;"><t t-out="object.tracking_number or '-'"/></td>
</tr>
</table>
<div t-if="object.x_fc_tracking_url" style="margin: 24px 0; text-align: center;">
@@ -232,7 +232,7 @@
</a>
</div>
<div style="border-left: 3px solid #2B6CB0; padding: 12px 16px; margin: 20px 0; font-size: 14px;">
<strong>What's next:</strong> Once the carrier collects the package, you'll receive a Shipped confirmation with the Certificate of Conformance attached.
<strong>What's next:</strong> Once the carrier collects the package, you'll receive a Shipped confirmation with your shipping documents attached.
</div>
<div style="margin-top: 32px; font-size: 14px;">
Best regards,<br/>
@@ -252,7 +252,7 @@
<record id="fp_mail_template_shipped" model="mail.template">
<field name="name">FP: Shipped / Delivered</field>
<field name="model_id" ref="fusion_plating_logistics.model_fusion_plating_delivery"/>
<field name="subject">Shipped {{ object.job_ref or object.name }}</field>
<field name="subject">Shipped - {{ object.job_ref or object.name }}</field>
<field name="email_from">{{ (object.company_id.email or user.email) }}</field>
<field name="email_to">{{ object.partner_id.email }}</field>
<field name="auto_delete" eval="True"/>
@@ -264,7 +264,7 @@
</div>
<h2 style="margin: 0 0 8px 0; font-size: 22px; font-weight: bold;">Your Parts Have Shipped</h2>
<p style="margin: 0 0 20px 0; font-size: 15px; opacity: 0.65;">
Hi <t t-out="object.partner_id.name or ''"/>, your order has left our facility. Certificate of Conformance and Bill of Lading are attached for your records.
Hi <t t-out="object.partner_id.name or ''"/>, your order is complete. Your packing slip or bill of lading is attached, along with the Certificate of Conformance where applicable, for your records.
</p>
<table style="width: 100%; border-collapse: collapse; margin: 20px 0;">
<tr style="border-bottom: 2px solid rgba(128,128,128,0.35);">
@@ -277,19 +277,19 @@
</tr>
<tr style="border-bottom: 1px solid rgba(128,128,128,0.25); background: rgba(128,128,128,0.06);">
<td style="padding: 8px 4px;">Job Reference</td>
<td style="padding: 8px 4px; text-align: right; font-family: monospace;"><t t-out="object.job_ref or ''"/></td>
<td style="padding: 8px 4px; text-align: right; font-family: monospace;"><t t-out="object.job_ref or '-'"/></td>
</tr>
<tr style="border-bottom: 1px solid rgba(128,128,128,0.25);">
<td style="padding: 8px 4px;">Delivered</td>
<td style="padding: 8px 4px; text-align: right;"><t t-out="format_datetime(object.delivered_at, tz=(user.tz or object.company_id.x_fc_default_tz or 'UTC'), dt_format='MMM d, y HH:mm') if object.delivered_at else ''"/></td>
<td style="padding: 8px 4px; text-align: right;"><t t-out="format_datetime(object.delivered_at, tz=(user.tz or object.company_id.x_fc_default_tz or 'UTC'), dt_format='MMM d, y HH:mm') if object.delivered_at else '-'"/></td>
</tr>
<tr style="border-bottom: 1px solid rgba(128,128,128,0.25); background: rgba(128,128,128,0.06);">
<td style="padding: 8px 4px;">Driver</td>
<td style="padding: 8px 4px; text-align: right;"><t t-out="object.assigned_driver_id.name or ''"/></td>
<td style="padding: 8px 4px; text-align: right;"><t t-out="object.assigned_driver_id.name or '-'"/></td>
</tr>
</table>
<div style="border-left: 3px solid #38a169; padding: 12px 16px; margin: 20px 0; font-size: 14px;">
<strong>Next:</strong> Your invoice will follow shortly. Please inspect your parts on receipt and contact us with any questions.
<strong>Next:</strong> Your invoice will follow shortly. Please inspect your parts and reach out with any questions.
</div>
<div style="margin-top: 32px; font-size: 14px;">
Best regards,<br/>
@@ -309,7 +309,7 @@
<record id="fp_mail_template_invoice_posted" model="mail.template">
<field name="name">FP: Invoice Notification</field>
<field name="model_id" ref="account.model_account_move"/>
<field name="subject">Invoice {{ object.name }} Electroless Nickel Technologies Inc. (ENTECH)</field>
<field name="subject">Invoice {{ object.name }} - Electroless Nickel Technologies Inc. (ENTECH)</field>
<field name="email_from">{{ (object.company_id.email or user.email) }}</field>
<field name="email_to">{{ object.partner_id.email }}</field>
<field name="auto_delete" eval="True"/>
@@ -334,15 +334,15 @@
</tr>
<tr style="border-bottom: 1px solid rgba(128,128,128,0.25); background: rgba(128,128,128,0.06);">
<td style="padding: 8px 4px;">Source Order</td>
<td style="padding: 8px 4px; text-align: right; font-family: monospace;"><t t-out="object.invoice_origin or ''"/></td>
<td style="padding: 8px 4px; text-align: right; font-family: monospace;"><t t-out="object.invoice_origin or '-'"/></td>
</tr>
<tr style="border-bottom: 1px solid rgba(128,128,128,0.25);">
<td style="padding: 8px 4px;">Invoice Date</td>
<td style="padding: 8px 4px; text-align: right;"><t t-out="format_date(object.invoice_date, date_format='MMM d, y') if object.invoice_date else ''"/></td>
<td style="padding: 8px 4px; text-align: right;"><t t-out="format_date(object.invoice_date, date_format='MMM d, y') if object.invoice_date else '-'"/></td>
</tr>
<tr style="border-bottom: 1px solid rgba(128,128,128,0.25); background: rgba(128,128,128,0.06);">
<td style="padding: 8px 4px;">Due Date</td>
<td style="padding: 8px 4px; text-align: right;"><t t-out="format_date(object.invoice_date_due, date_format='MMM d, y') if object.invoice_date_due else ''"/></td>
<td style="padding: 8px 4px; text-align: right;"><t t-out="format_date(object.invoice_date_due, date_format='MMM d, y') if object.invoice_date_due else '-'"/></td>
</tr>
<tr style="border-bottom: 1px solid rgba(128,128,128,0.25);">
<td style="padding: 8px 4px;"><strong>Amount Due</strong></td>
@@ -372,7 +372,7 @@
<record id="fp_mail_template_payment_received" model="mail.template">
<field name="name">FP: Payment Received</field>
<field name="model_id" ref="account.model_account_payment"/>
<field name="subject">Payment Received {{ object.name }}</field>
<field name="subject">Payment Received - {{ object.name }}</field>
<field name="email_from">{{ (object.company_id.email or user.email) }}</field>
<field name="email_to">{{ object.partner_id.email }}</field>
<field name="auto_delete" eval="True"/>
@@ -382,7 +382,7 @@
<div style="font-size: 11px; text-transform: uppercase; letter-spacing: 1px; color: #38a169; font-weight: 600; margin-bottom: 8px;">
Electroless Nickel Technologies Inc. (ENTECH)
</div>
<h2 style="margin: 0 0 8px 0; font-size: 22px; font-weight: bold;">Payment Received Thank You</h2>
<h2 style="margin: 0 0 8px 0; font-size: 22px; font-weight: bold;">Payment Received - Thank You</h2>
<p style="margin: 0 0 20px 0; font-size: 15px; opacity: 0.65;">
Hi <t t-out="object.partner_id.name or ''"/>, we've received your payment. Your receipt is attached.
</p>
@@ -397,11 +397,11 @@
</tr>
<tr style="border-bottom: 1px solid rgba(128,128,128,0.25); background: rgba(128,128,128,0.06);">
<td style="padding: 8px 4px;">Payment Method</td>
<td style="padding: 8px 4px; text-align: right;"><t t-out="object.payment_method_line_id.name or ''"/></td>
<td style="padding: 8px 4px; text-align: right;"><t t-out="object.payment_method_line_id.name or '-'"/></td>
</tr>
<tr style="border-bottom: 1px solid rgba(128,128,128,0.25);">
<td style="padding: 8px 4px;">Payment Date</td>
<td style="padding: 8px 4px; text-align: right;"><t t-out="format_date(object.date, date_format='MMM d, y') if object.date else ''"/></td>
<td style="padding: 8px 4px; text-align: right;"><t t-out="format_date(object.date, date_format='MMM d, y') if object.date else '-'"/></td>
</tr>
<tr style="border-bottom: 1px solid rgba(128,128,128,0.25); background: rgba(128,128,128,0.06);">
<td style="padding: 8px 4px;"><strong>Amount</strong></td>

View File

@@ -17,7 +17,7 @@ def post_init_hook(env):
1. Point report_template_ids at the current branded reports.
2. Turn off attach_quotation / attach_sale_order / attach_invoice
flags on fp.notification.template those would duplicate the
flags on fp.notification.template - those would duplicate the
PDF that's now auto-attached via the mail template's
report_template_ids.
"""
@@ -65,7 +65,7 @@ def _apply_report_template(env, mail_template_xmlid, report_xmlid):
"""Replace the template's report_template_ids with exactly [report].
We use `set` semantics (replace all) rather than `add` so that old
attachments from previous refactors get cleaned up e.g. when the
attachments from previous refactors get cleaned up - e.g. when the
Acknowledgement report was consolidated into the Sales Order report,
the now-stale Acknowledgement reference gets removed here.
"""

View File

@@ -11,7 +11,7 @@ from . import fp_receiving
from . import account_move
from . import account_move_send
from . import account_payment
# Phase 5 (Sub 11) mrp.production hook retired. The native equivalent
# Phase 5 (Sub 11) - mrp.production hook retired. The native equivalent
# fires from fp.job.button_mark_done -> _fp_fire_notification('job_complete').
# from . import mrp_production
from . import fp_delivery

View File

@@ -28,7 +28,7 @@ class AccountMoveSend(models.AbstractModel):
receives a single, branded invoice.
Partner-level and journal-level ``invoice_template_pdf_report_id``
overrides still win admins can opt out per customer or journal.
overrides still win - admins can opt out per customer or journal.
"""
partner_default = move.commercial_partner_id.with_company(
move.company_id

View File

@@ -24,7 +24,7 @@ class FpDelivery(models.Model):
job = Job.search([('name', '=', rec.job_ref)], limit=1)
if job:
so = job.sale_order_id or False
# Sub 6 pass the delivery address so location-scoped
# Sub 6 - pass the delivery address so location-scoped
# contacts receive the 'shipped' notification.
Dispatch._dispatch(
'shipped', rec, rec.partner_id, sale_order=so,

View File

@@ -11,7 +11,7 @@ from .fp_notification_template import TRIGGER_EVENTS
class FpNotificationLog(models.Model):
"""Audit trail for sent notifications."""
_name = 'fp.notification.log'
_description = 'Fusion Plating Notification Log'
_description = 'Fusion Plating - Notification Log'
_order = 'sent_date desc, id desc'
template_id = fields.Many2one(

View File

@@ -14,25 +14,25 @@ TRIGGER_EVENTS = [
('so_confirmed', 'Order Confirmed'),
('parts_received', 'Parts Received'),
('mo_complete', 'Manufacturing Complete'), # legacy, fired by mrp; kept for back-compat
('job_confirmed', 'Plating Job Confirmed'), # Sub 11 fp.job lifecycle
('job_complete', 'Plating Job Complete'), # Sub 11 fp.job.button_mark_done
('shipment_labeled', 'Shipping Label Generated'), # Phase C fired when tracking_number lands on fusion.shipment
('job_confirmed', 'Plating Job Confirmed'), # Sub 11 - fp.job lifecycle
('job_complete', 'Plating Job Complete'), # Sub 11 - fp.job.button_mark_done
('shipment_labeled', 'Shipping Label Generated'), # Phase C - fired when tracking_number lands on fusion.shipment
('shipped', 'Shipped / Delivered'),
('invoice_posted', 'Invoice Posted'),
('payment_received', 'Payment Received'),
('deposit_created', 'Deposit Required'),
('rma_authorised', 'RMA Authorised'), # Sub 12 RMA lifecycle
('rma_authorised', 'RMA Authorised'), # Sub 12 - RMA lifecycle
('rma_received', 'RMA Parts Received'),
('rma_resolved', 'RMA Resolved'),
# Spec 2026-05-25 post-shop cert + shipping states
# Spec 2026-05-25 - post-shop cert + shipping states
('cert_awaiting_issuance', 'Cert Awaiting Issuance'),
('cert_voided_re_notify', 'Cert Voided Please Re-Issue'),
('cert_voided_re_notify', 'Cert Voided - Please Re-Issue'),
('job_shipped', 'Job Shipped (manual mark)'),
# Spec 2026-05-25 tablet PIN self-service reset
# Spec 2026-05-25 - tablet PIN self-service reset
('tablet_pin_reset_requested', 'Tablet PIN Reset Code Requested'),
]
# Sub 6 map each trigger event to a communication stream. Contacts on
# 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.
@@ -61,7 +61,7 @@ class FpNotificationTemplate(models.Model):
whether the notification fires and what attachments are included.
"""
_name = 'fp.notification.template'
_description = 'Fusion Plating Notification Template'
_description = 'Fusion Plating - Notification Template'
_order = 'trigger_event'
name = fields.Char(string='Template Name', required=True)
@@ -93,7 +93,7 @@ class FpNotificationTemplate(models.Model):
]
# ------------------------------------------------------------------
# Central dispatch helper called from every hook.
# Central dispatch helper - called from every hook.
# ------------------------------------------------------------------
@api.model
def _dispatch(self, trigger_event, record, partner=None, sale_order=None,
@@ -106,7 +106,7 @@ class FpNotificationTemplate(models.Model):
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
set any flags fall back to the company partner's email -
identical to pre-Sub-6 behaviour.
"""
template = self.search(
@@ -125,7 +125,7 @@ class FpNotificationTemplate(models.Model):
if attachment_ids:
attachment_names = self.env['ir.attachment'].browse(attachment_ids).mapped('name')
# Spec 2026-05-25 cert authority events go to INTERNAL users
# Spec 2026-05-25 - cert authority events go to INTERNAL users
# (Manager / QM / Owner), not customer contacts. Replace partner-
# based resolution with the group-membership resolver.
recipient_emails = []
@@ -143,7 +143,7 @@ class FpNotificationTemplate(models.Model):
)
elif partner.email:
recipient_emails = [partner.email]
# Filter out falsy entries sub-contacts may have no email and the
# Filter out falsy entries - sub-contacts may have no email and the
# resolver returns False/None for them. Joining with bool blows up.
recipient_emails = [e for e in (recipient_emails or []) if e]
recipient_str = ', '.join(recipient_emails)
@@ -290,7 +290,7 @@ class FpNotificationTemplate(models.Model):
_logger.warning('Failed to render %s: %s', xmlid, exc)
return None
# Resolve the customer record partner-level flags gate certain
# Resolve the customer record - partner-level flags gate certain
# documents so customer preferences override template defaults.
customer = None
if sale_order:
@@ -311,14 +311,14 @@ class FpNotificationTemplate(models.Model):
return default
# Both attach_quotation and attach_sale_order point at the same
# report today render once to avoid double attachment.
# report today - render once to avoid double attachment.
if (self.attach_quotation or self.attach_sale_order) and sale_order:
att = _render_report(
'fusion_plating_reports.action_report_fp_sale_portrait', sale_order,
)
if att:
ids.append(att)
# CoC gated by customer preference (x_fc_send_coc, default True).
# CoC - gated by customer preference (x_fc_send_coc, default True).
# Prefer the rich PDF that mrp_production.button_mark_done already
# rendered against the fp.certificate (signatures, accreditation
# logos, thickness data). The legacy action_report_coc bound to
@@ -340,7 +340,7 @@ class FpNotificationTemplate(models.Model):
('certificate_type', '=', 'coc'),
], order='id desc', limit=1)
if cert:
# CoC is bilingual now the single EN action renders
# CoC is bilingual now - the single EN action renders
# both languages, so there's no per-lang branch.
att = _render_report(
'fusion_plating_reports.action_report_coc_en', cert,
@@ -351,7 +351,7 @@ class FpNotificationTemplate(models.Model):
)
if att:
ids.append(att)
# Thickness report only attach when the customer opted OUT of
# Thickness report - only attach when the customer opted OUT of
# CoC and ONLY wants thickness. The CoC PDF already embeds
# thickness data so attaching both would be a duplicate.
if (self.attach_thickness_report and portal_job
@@ -374,14 +374,14 @@ class FpNotificationTemplate(models.Model):
)
if att:
ids.append(att)
# Packing slip gated by customer preference (default True)
# Packing slip - gated by customer preference (default True)
if self.attach_packing_list and delivery and _customer_wants('x_fc_send_packing_slip'):
att = _render_report(
'fusion_plating_reports.action_report_fp_packing_slip_delivery_portrait', delivery,
)
if att:
ids.append(att)
# BoL gated by customer preference (default False)
# BoL - gated by customer preference (default False)
if self.attach_bol and delivery and _customer_wants('x_fc_send_bol', default=False):
att = _render_report(
'fusion_plating_reports.action_report_fp_bol_portrait', delivery,

View File

@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
"""Phase C fire 'shipment_labeled' notification when tracking_number
"""Phase C - fire 'shipment_labeled' notification when tracking_number
lands on a fusion.shipment for the first time.
Triggers regardless of how tracking got set: live API call or manual

View File

@@ -3,7 +3,7 @@
# License OPL-1 (Odoo Proprietary License v1.0)
# Part of the Fusion Plating product family.
#
# Sub 6 Recipient resolver.
# Sub 6 - Recipient resolver.
#
# Every outbound-notification path now routes its recipient lookup
# through `_fp_resolve_notification_recipients`. The helper consults
@@ -35,7 +35,7 @@ class ResPartner(models.Model):
Fallback: if no contact at either level carries a matching flag
(or the global flag), the result is the company partner's own
email. This makes the resolver drop-in safe no customer ever
email. This makes the resolver drop-in safe - no customer ever
silently stops receiving notifications after Sub 6 ships.
"""
self.ensure_one()
@@ -62,7 +62,7 @@ class ResPartner(models.Model):
recipients.append(contact.email)
if not recipients:
# Nobody opted in via contacts fall back to the company
# Nobody opted in via contacts - fall back to the company
# email (and the location's email, if distinct).
if delivery_location and delivery_location != self and delivery_location.email:
recipients.append(delivery_location.email)

View File

@@ -45,7 +45,7 @@ class SaleOrder(models.Model):
# NOTE: we intentionally do NOT override action_quotation_send to
# fire the quote_sent trigger. action_quotation_send returns a
# compose-dialog action synchronously if we dispatch here the
# compose-dialog action synchronously - if we dispatch here the
# email is already sent by the time the user sees the dialog and
# decides to Cancel.
#

View File

@@ -2,7 +2,7 @@
<odoo>
<!-- ============================================================ -->
<!-- Notification Log List -->
<!-- Notification Log - List -->
<!-- ============================================================ -->
<record id="view_fp_notification_log_list" model="ir.ui.view">
<field name="name">fp.notification.log.list</field>
@@ -22,7 +22,7 @@
</record>
<!-- ============================================================ -->
<!-- Notification Log Form -->
<!-- Notification Log - Form -->
<!-- ============================================================ -->
<record id="view_fp_notification_log_form" model="ir.ui.view">
<field name="name">fp.notification.log.form</field>
@@ -56,7 +56,7 @@
</record>
<!-- ============================================================ -->
<!-- Notification Log Search -->
<!-- Notification Log - Search -->
<!-- ============================================================ -->
<record id="view_fp_notification_log_search" model="ir.ui.view">
<field name="name">fp.notification.log.search</field>
@@ -89,7 +89,7 @@
</record>
<!-- ============================================================ -->
<!-- Notification Log Action -->
<!-- Notification Log - Action -->
<!-- ============================================================ -->
<record id="action_fp_notification_log" model="ir.actions.act_window">
<field name="name">Notification Log</field>

View File

@@ -2,7 +2,7 @@
<odoo>
<!-- ============================================================ -->
<!-- Notification Template List -->
<!-- Notification Template - List -->
<!-- ============================================================ -->
<record id="view_fp_notification_template_list" model="ir.ui.view">
<field name="name">fp.notification.template.list</field>
@@ -18,7 +18,7 @@
</record>
<!-- ============================================================ -->
<!-- Notification Template Form -->
<!-- Notification Template - Form -->
<!-- ============================================================ -->
<record id="view_fp_notification_template_form" model="ir.ui.view">
<field name="name">fp.notification.template.form</field>
@@ -64,7 +64,7 @@
</record>
<!-- ============================================================ -->
<!-- Notification Template Search -->
<!-- Notification Template - Search -->
<!-- ============================================================ -->
<record id="view_fp_notification_template_search" model="ir.ui.view">
<field name="name">fp.notification.template.search</field>
@@ -80,7 +80,7 @@
</record>
<!-- ============================================================ -->
<!-- Notification Template Action -->
<!-- Notification Template - Action -->
<!-- ============================================================ -->
<record id="action_fp_notification_template" model="ir.actions.act_window">
<field name="name">Notification Templates</field>