From a5063cc8160632a246da5da1785b43019777dda8 Mon Sep 17 00:00:00 2001 From: gsinghpal Date: Wed, 27 May 2026 01:57:32 -0400 Subject: [PATCH] feat(plating): recipe-level cert suppression Booleans Adds five requires_* Booleans on fusion.plating.process.node (requires_coc, requires_thickness_report, requires_nadcap_cert, requires_mill_test, requires_customer_specific), default True. Recipe is SUPPRESS-ONLY: when False, the recipe never produces that cert type even if the customer/part requested it. Default True = existing recipes keep producing the same cert set they produce today. Surfaced on recipe-level form (node_type == 'recipe'); resolver reads from job.recipe_id which is always a top-level recipe node. Post-migrate backfills NULL -> TRUE on existing nodes. Sub: docs/superpowers/specs/2026-05-27-recipe-cert-toggles-design.md Task: T2. Co-Authored-By: Claude Opus 4.7 (1M context) --- fusion_plating/fusion_plating/__manifest__.py | 2 +- .../migrations/19.0.22.0.0/post-migrate.py | 41 +++++++++++++++ .../fusion_plating/models/fp_process_node.py | 50 +++++++++++++++++++ 3 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 fusion_plating/fusion_plating/migrations/19.0.22.0.0/post-migrate.py diff --git a/fusion_plating/fusion_plating/__manifest__.py b/fusion_plating/fusion_plating/__manifest__.py index 30bef953..7141ce42 100644 --- a/fusion_plating/fusion_plating/__manifest__.py +++ b/fusion_plating/fusion_plating/__manifest__.py @@ -5,7 +5,7 @@ { 'name': 'Fusion Plating', - 'version': '19.0.21.4.0', + 'version': '19.0.22.0.0', 'category': 'Manufacturing/Plating', 'summary': 'Core plating / metal finishing ERP: facilities, processes, tanks, baths, jobs, operators.', 'description': """ diff --git a/fusion_plating/fusion_plating/migrations/19.0.22.0.0/post-migrate.py b/fusion_plating/fusion_plating/migrations/19.0.22.0.0/post-migrate.py new file mode 100644 index 00000000..de7bf275 --- /dev/null +++ b/fusion_plating/fusion_plating/migrations/19.0.22.0.0/post-migrate.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +# Copyright 2026 Nexa Systems Inc. +# License OPL-1 +"""Post-migrate for 19.0.22.0.0 — Recipe-level cert suppression Booleans. + +Backfills NULL -> TRUE on the five new requires_* columns on +fusion.plating.process.node (requires_coc, requires_thickness_report, +requires_nadcap_cert, requires_mill_test, requires_customer_specific). + +Default TRUE = inherit current behaviour for every existing recipe +(zero migration surprises — every existing recipe keeps producing +the same cert set it produces today). + +Idempotent: safe to re-run. + +Spec: docs/superpowers/specs/2026-05-27-recipe-cert-toggles-design.md +""" +import logging + +_logger = logging.getLogger(__name__) + + +def migrate(cr, version): + if not version: + return + _logger.info( + '19.0.22.0.0 post-migrate: backfilling recipe cert-suppression ' + 'requires_* Booleans to TRUE on fusion.plating.process.node rows' + ) + for col in ( + 'requires_coc', + 'requires_thickness_report', + 'requires_nadcap_cert', + 'requires_mill_test', + 'requires_customer_specific', + ): + cr.execute( + "UPDATE fusion_plating_process_node SET %s = TRUE " + "WHERE %s IS NULL" % (col, col) + ) + _logger.info(' %s: %d rows updated', col, cr.rowcount) diff --git a/fusion_plating/fusion_plating/models/fp_process_node.py b/fusion_plating/fusion_plating/models/fp_process_node.py index 99307645..64bdb7b2 100644 --- a/fusion_plating/fusion_plating/models/fp_process_node.py +++ b/fusion_plating/fusion_plating/models/fp_process_node.py @@ -502,6 +502,56 @@ class FpProcessNode(models.Model): string='Requires Transition Form', help='Sub 12b — opens the transition form before Mark Done.', ) + + # Certificate Output — recipe-level cert suppression (2026-05-27 sub + # docs/superpowers/specs/2026-05-27-recipe-cert-toggles-design.md). + # Default True for all five so existing recipes keep producing the + # same cert set they produce today. A recipe author flips OFF only + # the types the recipe physically never produces (passivation = no + # thickness; commodity ENP = no nadcap). + # + # Precedence (Q1 locked decision): recipe SUPPRESSES ONLY. Customer + # / part flags decide what is requested; recipe can remove from that + # set but never add. See fp.job._resolve_required_cert_types. + # + # Surfaced on the recipe form only when node_type == 'recipe'; the + # fields exist on every node row but the UX hides them deeper in + # the tree to avoid confusing authors. Resolver reads from + # job.recipe_id which is always a top-level recipe node. + requires_coc = fields.Boolean( + string='Requires CoC', + default=True, + help='When False, this recipe never produces a Certificate of ' + 'Conformance even if the customer/part requested one.', + ) + requires_thickness_report = fields.Boolean( + string='Requires Thickness Report', + default=True, + help='When False, this recipe never produces a thickness report. ' + 'Use for passivation, chemical conversion, anodize seal-only, ' + 'etc. — processes that physically have no plating thickness ' + 'to measure.', + ) + requires_nadcap_cert = fields.Boolean( + string='Requires Nadcap Certificate', + default=True, + help='When False, this recipe never auto-spawns a Nadcap cert. ' + 'Use for commodity recipes that the shop does not run under ' + 'Nadcap accreditation.', + ) + requires_mill_test = fields.Boolean( + string='Requires Mill Test Report', + default=True, + help='When False, this recipe never auto-spawns a Mill Test Report ' + 'cert.', + ) + requires_customer_specific = fields.Boolean( + string='Requires Customer-Specific Cert', + default=True, + help='When False, this recipe never auto-spawns a Customer-Specific ' + 'cert.', + ) + # Sub 14b — User-extensible Step Kinds (was Selection of 24). # 2026-05-20: required + ondelete='restrict' — kind drives gates, # workflow milestones, and operator routing. Optional was a foot-gun