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:
@@ -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.',
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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.
|
||||
"""
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
#
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user