feat(fusion_plating): extend resolver + auto-classify hook on process node
Resolver (fp_resolve_step_kind) extensions: - New aliases: blasting/bead blast/media blast variants, adhesion testing, corrosion testing, lab testing, strip process, chemical conversion, trivalent chromate, plug the threaded holes, air dry, desmut, soak clean, cleaner, nickel strike/strip - Parenthetical suffix stripping - "Masking (If Required)" resolves through "masking", "Incoming Inspection (Standard)" through "incoming inspection", "Trivalent Chromate Conversion (A-14 / A)" through "trivalent chromate conversion" - New RESOLVER_KIND_TO_ACTIVE_KIND map translates the resolver's vocabulary (cleaning/electroclean/etch/rinse/strike/dry/wbf_test -> wet_process) so the resolver output lands on active kinds only Auto-classify hook on fusion.plating.process.node: - _fp_autoclassify_kind() upgrades kind_id when current is 'other' AND name resolves via the resolver. Idempotent - never overrides a non-'other' kind. Skip via context flag fp_skip_kind_autoclassify - Wired into create() and write() (only fires when name or kind_id changed on write) - Side-effects: recipe duplication via copy() auto-corrects newly copied nodes; Simple/Tree editor authoring auto-classifies as soon as the name is saved Spec: docs/superpowers/specs/2026-05-24-recipe-cleanup-design.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -784,6 +784,62 @@ class FpProcessNode(models.Model):
|
||||
return self.action_open_simple_editor()
|
||||
return self.action_open_tree_editor()
|
||||
|
||||
# ---- Auto-classify kind from name (2026-05-24, spec
|
||||
# docs/superpowers/specs/2026-05-24-recipe-cleanup-design.md) -------
|
||||
# Safety net: when a node's kind is the catch-all 'other' AND its
|
||||
# name resolves via fp_resolve_step_kind(), upgrade kind_id to the
|
||||
# resolved active kind. Runs on create() and on write() when name
|
||||
# or kind_id changes. Prevents recipe authoring + recipe
|
||||
# duplication from silently leaving nodes as 'other' (which then
|
||||
# routes them to the wrong Shop Floor column).
|
||||
#
|
||||
# Skip with context flag fp_skip_kind_autoclassify=True for admin
|
||||
# workflows that need to keep kind=other despite a known name.
|
||||
|
||||
def _fp_autoclassify_kind(self):
|
||||
"""Upgrade kind_id when current is 'other' and name resolves."""
|
||||
if self.env.context.get('fp_skip_kind_autoclassify'):
|
||||
return
|
||||
from odoo.addons.fusion_plating import (
|
||||
fp_resolve_step_kind,
|
||||
RESOLVER_KIND_TO_ACTIVE_KIND,
|
||||
)
|
||||
Kind = self.env['fp.step.kind']
|
||||
other = Kind.search([('code', '=', 'other')], limit=1)
|
||||
if not other:
|
||||
return
|
||||
# Cache active-kind ids by code so we don't re-search per row.
|
||||
kind_by_code = {}
|
||||
for node in self:
|
||||
if not node.name or node.kind_id != other:
|
||||
continue
|
||||
resolver_code = fp_resolve_step_kind(node.name)
|
||||
if not resolver_code:
|
||||
continue
|
||||
target_code = RESOLVER_KIND_TO_ACTIVE_KIND.get(resolver_code)
|
||||
if not target_code:
|
||||
continue
|
||||
if target_code not in kind_by_code:
|
||||
tgt = Kind.search([('code', '=', target_code)], limit=1)
|
||||
kind_by_code[target_code] = tgt.id if tgt else False
|
||||
target_id = kind_by_code[target_code]
|
||||
if target_id:
|
||||
node.with_context(
|
||||
fp_skip_kind_autoclassify=True,
|
||||
).write({'kind_id': target_id})
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
nodes = super().create(vals_list)
|
||||
nodes._fp_autoclassify_kind()
|
||||
return nodes
|
||||
|
||||
def write(self, vals):
|
||||
res = super().write(vals)
|
||||
if 'name' in vals or 'kind_id' in vals:
|
||||
self._fp_autoclassify_kind()
|
||||
return res
|
||||
|
||||
# ---- Copy (deep-duplicate) -----------------------------------------------
|
||||
|
||||
def copy(self, default=None):
|
||||
|
||||
Reference in New Issue
Block a user