feat(notifications): cert authority events + QM activity (Tasks 17-20)

TRIGGER_EVENTS extended with three new events:
  - cert_awaiting_issuance — fires on in_progress → awaiting_cert
  - cert_voided_re_notify  — fires on awaiting_ship → awaiting_cert
                             regress (cert voided post-issue)
  - job_shipped            — fires on button_mark_shipped

_dispatch routes cert events through new internal-recipient resolver
(QM/Manager/Owner via all_group_ids, transitive per Rule 13l)
instead of the partner-based stream lookup. Other events unchanged.

Mail templates (fp_cert_authority_templates.xml): two new
mail.template records bound to fp.job. Amber accent bar for awaiting,
red accent bar for void-re-issue. Deep-link to
/odoo/action-...?tab=certificates so QM lands on the right tab.

Activity type (fp_activity_types_data.xml): mail.activity.type
activity_type_issue_coc — bound to fp.job, 1-day delay, certificate
icon.

fp.job helpers:
  _fp_schedule_cert_activity: round-robin by oldest login_date,
    idempotent on existing open activity, soft-fails if helpers
    are missing.
  _fp_resolve_cert_activities: auto-resolves on awaiting_ship,
    soft-fails on per-activity exceptions.

Manifest bumps:
  fusion_plating_notifications 19.0.6.6.1 → 19.0.7.0.0
  fusion_plating_jobs: data list gains fp_activity_types_data.xml

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-05-25 09:53:09 -04:00
parent c00831a72a
commit 26a1086623
6 changed files with 272 additions and 3 deletions

View File

@@ -0,0 +1,127 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2026 Nexa Systems Inc.
License OPL-1 (Odoo Proprietary License v1.0)
Spec: docs/superpowers/specs/2026-05-25-post-shop-cert-shipping-job-states-design.md
Templates for the two new internal-recipient events that fire on
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.
noupdate="1" so admin edits in the UI survive -u (per CLAUDE.md
Rule 22 / mail-template gotcha).
-->
<odoo noupdate="1">
<!-- ============================================================= -->
<!-- 1. Cert Awaiting Issuance (Warn, #F59E0B) -->
<!-- ============================================================= -->
<record id="fp_mail_template_cert_awaiting_issuance" model="mail.template">
<field name="name">FP: Cert Awaiting Issuance</field>
<field name="model_id" ref="fusion_plating.model_fp_job"/>
<field name="subject">🏷️ Job {{ object.display_wo_name or object.name }} ready for CoC issuance</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">
<div style="font-family: -apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Arial,sans-serif; max-width: 600px; margin: 0 auto; padding: 32px 24px;">
<div style="height: 4px; background-color: #F59E0B; margin-bottom: 28px;"></div>
<div style="font-size: 11px; text-transform: uppercase; letter-spacing: 1px; color: #F59E0B; font-weight: 600; margin-bottom: 8px;">
Quality Action Required
</div>
<h2 style="margin: 0 0 8px 0; font-size: 22px; font-weight: bold;">CoC Awaiting Issuance</h2>
<p style="margin: 0 0 20px 0; font-size: 15px; opacity: 0.75;">
Job <strong t-out="object.display_wo_name or object.name"/>
(<t t-out="object.partner_id.name"/>) has finished the shop floor
and is awaiting CoC issuance.
</p>
<table style="width: 100%; border-collapse: collapse; margin: 20px 0;">
<tr style="border-bottom: 2px solid rgba(128,128,128,0.35);">
<th style="text-align: left; padding: 8px 4px; font-size: 12px; text-transform: uppercase; opacity: 0.55; font-weight: 600;">Detail</th>
<th style="text-align: right; padding: 8px 4px; font-size: 12px; text-transform: uppercase; opacity: 0.55; font-weight: 600;">Value</th>
</tr>
<tr style="border-bottom: 1px solid rgba(128,128,128,0.25);">
<td style="padding: 8px 4px;">Customer</td>
<td style="padding: 8px 4px; text-align: right;"><t t-out="object.partner_id.name or ''"/></td>
</tr>
<tr style="border-bottom: 1px solid rgba(128,128,128,0.25);">
<td style="padding: 8px 4px;">Part</td>
<td style="padding: 8px 4px; text-align: right; font-family: monospace;">
<t t-out="(object.part_catalog_id.part_number if object.part_catalog_id else '') or '—'"/>
</td>
</tr>
<tr style="border-bottom: 1px solid rgba(128,128,128,0.25);">
<td style="padding: 8px 4px;">Quantity</td>
<td style="padding: 8px 4px; text-align: right;"><t t-out="object.qty_done or 0"/></td>
</tr>
<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 '—'"/>
</td>
</tr>
</table>
<p style="margin: 16px 0; font-size: 14px; opacity: 0.75;">
Review the inspection prompts captured by the operator on the
Final Inspection step, then issue the CoC from the Quality
Dashboard.
</p>
<p style="margin: 20px 0;">
<a t-attf-href="/odoo/action-fusion_plating_quality.action_fp_quality_dashboard?tab=certificates"
style="display: inline-block; padding: 10px 20px; background-color: #F59E0B; color: white; text-decoration: none; font-weight: 600; border-radius: 4px;">
Open Quality Dashboard →
</a>
</p>
</div>
</field>
</record>
<record id="fp_notif_cert_awaiting_issuance" model="fp.notification.template">
<field name="name">Cert Awaiting Issuance</field>
<field name="trigger_event">cert_awaiting_issuance</field>
<field name="mail_template_id" ref="fp_mail_template_cert_awaiting_issuance"/>
<field name="active" eval="True"/>
</record>
<!-- ============================================================= -->
<!-- 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="model_id" ref="fusion_plating.model_fp_job"/>
<field name="subject">⚠️ Job {{ object.display_wo_name or 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">
<div style="font-family: -apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Arial,sans-serif; max-width: 600px; margin: 0 auto; padding: 32px 24px;">
<div style="height: 4px; background-color: #DC2626; margin-bottom: 28px;"></div>
<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>
<p style="margin: 0 0 20px 0; font-size: 15px; opacity: 0.75;">
A previously-issued CoC for job
<strong t-out="object.display_wo_name or object.name"/>
(<t t-out="object.partner_id.name"/>) was voided. The job has
slid back to <em>Awaiting Cert</em> and is waiting for re-issuance.
</p>
<p style="margin: 20px 0;">
<a t-attf-href="/odoo/action-fusion_plating_quality.action_fp_quality_dashboard?tab=certificates"
style="display: inline-block; padding: 10px 20px; background-color: #DC2626; color: white; text-decoration: none; font-weight: 600; border-radius: 4px;">
Open Quality Dashboard →
</a>
</p>
</div>
</field>
</record>
<record id="fp_notif_cert_voided_re_notify" model="fp.notification.template">
<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"/>
</record>
</odoo>