Files
Odoo-Modules/fusion_plating/fusion_plating_certificates/models/res_partner.py
gsinghpal ba6aeaaca9 feat(certificates): multiple Default CoC Contacts per customer (M2o -> M2m)
res.partner.x_fc_default_coc_contact_id (single Many2one) becomes
x_fc_default_coc_contact_ids (self-referential Many2many 'Default CoC
Contacts') so a customer can list several contacts who need the CoC.

- res.partner: M2m field (rel fp_default_coc_contact_rel) + many2many_tags.
- Cert: contact_partner_id (primary addressee printed on the cert) is set to
  the FIRST CoC contact at job creation + lazy-filled at issue.
- Send: action_send_to_customer pre-fills the email composer with ALL the
  customer's CoC contacts (primary + the rest), falling back to the company.
- fp.job cert-default resolution + the action_issue gate wording updated.
- Migration 19.0.10.2.0: copies each partner's old single value into the new
  M2m, then drops the orphaned column.

Deployed + verified on entech: migration copied 2 existing values, old column
dropped, field is M2m, send pre-fills all contacts. entech-only part_line_ids
/ multi-part resolver preserved.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 17:31:17 -04:00

152 lines
7.0 KiB
Python

# -*- coding: utf-8 -*-
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
# Part of the Fusion Plating product family.
from odoo import fields, models
class ResPartner(models.Model):
"""Per-customer preferences for what quality documents are generated
and emailed when a job ships.
Some aerospace customers insist on a full CoC + thickness report;
others just want the CoC; some repeat commercial accounts want
neither (the PO says "no paperwork required"). Rather than hard-code
the shop's policy, each customer controls their own.
"""
_inherit = 'res.partner'
x_fc_send_coc = fields.Boolean(
string='Send Certificate of Conformance',
default=True, tracking=True,
help='When shipping, auto-generate and email a CoC to this customer.',
)
x_fc_send_thickness_report = fields.Boolean(
string='Send Thickness Report',
default=True, tracking=True,
help='When shipping, auto-generate and email a thickness report '
'with the Fischerscope readings for this job.',
)
x_fc_send_packing_slip = fields.Boolean(
string='Send Packing Slip',
default=True, tracking=True,
help='Attach the packing slip PDF to the shipping confirmation email.',
)
x_fc_send_bol = fields.Boolean(
string='Send Bill of Lading',
default=False, tracking=True,
help='Attach the BoL PDF to the shipping confirmation email. '
'Usually only for customers that invoice freight separately.',
)
x_fc_strict_thickness_required = fields.Boolean(
string='Require Thickness Readings on CoC',
default=False, tracking=True,
help='Aerospace / Nadcap customers expect every CoC to carry '
'actual Fischerscope readings (not just "meets spec"). When '
'this is on, action_issue() blocks until at least one '
'thickness reading has been logged for the MO. Leave off '
'for commercial customers.',
)
# Aerospace / Defence cert toggles (2026-05-27 — sub
# docs/superpowers/specs/2026-05-27-recipe-cert-toggles-design.md).
# Default False — opt-in for aerospace/defence customers only.
# Resolver _resolve_required_cert_types reads these alongside the
# existing x_fc_send_coc / x_fc_send_thickness_report. The three
# cert types (nadcap_cert / mill_test / customer_specific) are
# manual-attach: operator uploads the PDF, no QWeb auto-render.
x_fc_send_nadcap_cert = fields.Boolean(
string='Send Nadcap Certificate',
default=False, tracking=True,
help='Auto-spawn a Nadcap-type fp.certificate when a job for '
'this customer reaches awaiting_cert. Operator attaches '
'the supplier/PRI-issued PDF before clicking Issue — '
'there is no QWeb auto-render for this type.',
)
x_fc_send_mill_test = fields.Boolean(
string='Send Mill Test Report (MTR)',
default=False, tracking=True,
help='Auto-spawn a Mill Test Report cert. Operator attaches '
"the steel supplier's MTR PDF before issuing.",
)
x_fc_send_customer_specific = fields.Boolean(
string='Send Customer-Specific Cert',
default=False, tracking=True,
help='Auto-spawn a customer-specific cert. Operator fills the '
'customer-supplied template PDF and attaches before '
'issuing.',
)
# ---- 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.',
)
# ---- Sub 12c+ — Per-customer cert statement override ----------------
x_fc_cert_statement = fields.Text(
string='Cert Statement Override',
help='Override boilerplate text printed in the Certificate of '
'Conformance "Certification Statement" block. When blank, '
'falls back to the company default (res.company.'
'x_fc_default_cert_statement) and finally to a hardcoded '
'AS9100/ISO 9001 boilerplate. Useful for aerospace customers '
'who require specific NIST or DFARS language.',
)
# ---- Default CoC contacts (cert addressees + email recipients) -------
# One or more named contacts who receive this customer's CoC. The first
# is the primary (printed on the cert + pre-fills cert.contact_partner_id
# when a job ships); the rest are CC'd when the CoC is emailed. Sales
# sets the list once per customer. Falls back to manual selection at
# action_issue time if blank. Self-referential M2m (renamed from the old
# single Many2one x_fc_default_coc_contact_id — see migration 19.0.10.2.0).
x_fc_default_coc_contact_ids = fields.Many2many(
'res.partner',
relation='fp_default_coc_contact_rel',
column1='partner_id', column2='contact_id',
string='Default CoC Contacts',
domain="[('parent_id', '=', id), ('is_company', '=', False)]",
tracking=True,
help='Contacts the Certificate of Conformance is addressed to and '
'emailed to. The first contact is the primary (printed on the '
'cert and pre-filled as the cert contact when a job ships); '
'the rest are copied (CC) when the CoC is sent. Leave blank to '
'force the manager to pick at issue time. Child contacts of '
'this company only.',
)