From 64eb34cdff99150b8282974453ab159ef12b5b62 Mon Sep 17 00:00:00 2001 From: gsinghpal Date: Thu, 28 May 2026 23:20:38 -0400 Subject: [PATCH] fix(plating): CoC signer follows Settings "Certificate Owner" (no stale freeze) Changing Settings -> Certificate Owner didn't move existing certs: the signer was snapshotted from the company owner at cert-creation time, and the CoC prefers that snapshot over the live owner. - _fp_create_certificates no longer freezes the company owner into certified_by_id; it snapshots ONLY a deliberate per-spec signer. Empty certified_by_id then resolves the LIVE company owner in the CoC report. - action_issue lazy-fill made robust: resolves the company via the SO / env.company (fp.certificate has no company_id) so it fills the CURRENT owner at issue and the "Certified By" gate still passes. - Settings help text corrected: signature comes from the user's Plating Signature (Preferences -> My Profile), not "HR Employee". - Data fix on entech: cleared certified_by_id on 5 stale draft CoCs with no per-spec signer so they follow the current owner. Bump certificates 19.0.9.3.0, jobs 19.0.11.4.0. Verified: CoC-30058 resolves signer = Garry Singh (has Plating Signature), renders clean. Co-Authored-By: Claude Opus 4.7 --- .../__manifest__.py | 2 +- .../models/fp_certificate.py | 33 ++++++++++++------- .../views/res_config_settings_views.xml | 2 +- .../fusion_plating_jobs/__manifest__.py | 2 +- .../fusion_plating_jobs/models/fp_job.py | 17 +++++++--- 5 files changed, 37 insertions(+), 19 deletions(-) diff --git a/fusion_plating/fusion_plating_certificates/__manifest__.py b/fusion_plating/fusion_plating_certificates/__manifest__.py index 19257427..55376831 100644 --- a/fusion_plating/fusion_plating_certificates/__manifest__.py +++ b/fusion_plating/fusion_plating_certificates/__manifest__.py @@ -5,7 +5,7 @@ { 'name': 'Fusion Plating — Certificates', - 'version': '19.0.9.2.0', + 'version': '19.0.9.3.0', 'category': 'Manufacturing/Plating', 'summary': 'Certificate registry for CoC, thickness reports, and quality documents.', 'description': """ diff --git a/fusion_plating/fusion_plating_certificates/models/fp_certificate.py b/fusion_plating/fusion_plating_certificates/models/fp_certificate.py index 9d7e2447..4da83ead 100644 --- a/fusion_plating/fusion_plating_certificates/models/fp_certificate.py +++ b/fusion_plating/fusion_plating_certificates/models/fp_certificate.py @@ -485,17 +485,28 @@ class FpCertificate(models.Model): rec.contact_partner_id = ( rec.partner_id.x_fc_default_coc_contact_id ) - # Guard with field-existence check — fp.certificate doesn't - # declare company_id directly; production picks it up from - # auto-creation context but tests can build a cert without - # one. Without the guard, AttributeError on the .company_id - # access bubbles up as a test error. - if (not rec.certified_by_id - and 'company_id' in rec._fields - and rec.company_id - and 'x_fc_owner_user_id' in rec.company_id._fields - and rec.company_id.x_fc_owner_user_id): - rec.certified_by_id = rec.company_id.x_fc_owner_user_id + # Lazy-fill the signer from the LIVE company owner (Settings + # "Certificate Owner") when no per-cert / per-spec signer was + # chosen. As of 2026-05-28 the cert no longer freezes the + # company owner at creation, so certified_by_id is empty in + # the common case and this fill is what locks the signer at + # issue. fp.certificate doesn't declare company_id directly, + # so resolve the company from the SO (then env.company) the + # same way the CoC report does — otherwise this fill is + # skipped and the "Certified By is not set" gate below would + # block issuance. + if not rec.certified_by_id: + company = ( + (rec.company_id + if 'company_id' in rec._fields and rec.company_id + else False) + or (rec.sale_order_id.company_id + if rec.sale_order_id else False) + or rec.env.company + ) + if (company and 'x_fc_owner_user_id' in company._fields + and company.x_fc_owner_user_id): + rec.certified_by_id = company.x_fc_owner_user_id # Spec Reference is OPTIONAL (client request 2026-05-28). # The customer-facing description now serves as the cert's # spec / certificate information (see diff --git a/fusion_plating/fusion_plating_certificates/views/res_config_settings_views.xml b/fusion_plating/fusion_plating_certificates/views/res_config_settings_views.xml index ea57248c..1eb7c0c5 100644 --- a/fusion_plating/fusion_plating_certificates/views/res_config_settings_views.xml +++ b/fusion_plating/fusion_plating_certificates/views/res_config_settings_views.xml @@ -18,7 +18,7 @@ help="Branding, accreditation logos, and default signer for Certificates of Conformance."> + help="Signs every CoC that has no per-spec or per-cert signer. Their Plating Signature (Preferences → My Profile) is printed. Changing this flows through to draft certificates."> diff --git a/fusion_plating/fusion_plating_jobs/__manifest__.py b/fusion_plating/fusion_plating_jobs/__manifest__.py index 2ad80667..6216518b 100644 --- a/fusion_plating/fusion_plating_jobs/__manifest__.py +++ b/fusion_plating/fusion_plating_jobs/__manifest__.py @@ -3,7 +3,7 @@ # License OPL-1 (Odoo Proprietary License v1.0) { 'name': 'Fusion Plating — Native Jobs', - 'version': '19.0.11.3.0', + 'version': '19.0.11.4.0', 'category': 'Manufacturing/Plating', 'summary': 'Native plating job model — replaces mrp.production / mrp.workorder bridge.', 'author': 'Nexa Systems Inc.', diff --git a/fusion_plating/fusion_plating_jobs/models/fp_job.py b/fusion_plating/fusion_plating_jobs/models/fp_job.py index b8534ad5..71e834a3 100644 --- a/fusion_plating/fusion_plating_jobs/models/fp_job.py +++ b/fusion_plating/fusion_plating_jobs/models/fp_job.py @@ -2572,8 +2572,10 @@ class FpJob(models.Model): Resolution sources for the new prefill fields: - process_description ← recipe.name (the job's process root) - - certified_by_id ← customer_spec.signer_user_id, falling - back to company.x_fc_owner_user_id + - certified_by_id ← customer_spec.signer_user_id ONLY + (a per-spec override). Left empty + otherwise so the LIVE company owner + resolves at render / issue time. - contact_partner_id ← partner.x_fc_default_coc_contact_id - nc_quantity ← qty_scrapped + qty_visual_insp_rejects @@ -2596,12 +2598,17 @@ class FpJob(models.Model): # sourced from sale_order.x_fc_coating_config_id (since retired); # recipe.name is the human-readable replacement. recipe = self.recipe_id - # Signer resolution: per-spec override wins, company default fills. + # Signer resolution (2026-05-28): snapshot ONLY a deliberate + # per-spec signer here. Do NOT freeze the company owner into + # certified_by_id — leaving it empty lets the CoC report and + # action_issue resolve the LIVE company owner (res.company + # .x_fc_owner_user_id / Settings "Certificate Owner") at render / + # issue time. That way changing the Settings signer flows through + # to existing draft certs instead of being frozen to whoever was + # the owner when the cert was created. signer = False if spec and 'signer_user_id' in spec._fields: signer = spec.signer_user_id - if not signer and 'x_fc_owner_user_id' in self.company_id._fields: - signer = self.company_id.x_fc_owner_user_id # Contact: per-customer default; blank means manager picks at issue. contact = False if 'x_fc_default_coc_contact_id' in self.partner_id._fields: