Files
Odoo-Modules/fusion_plating/fusion_plating/scripts/bt_e2e_recon3.py
gsinghpal ec0a07fbe9 fix(audit-trail): 3 production bugs found via end-to-end Anodize battle test
Battle-tested complete workflow on entech: ABC Manufacturing + Anodize
recipe (id=136) cloned to part-variant (id=1775) → SO S00276 confirmed →
fp.job 1234 with 17 steps → recorded 56 measurement values exercising all
13 input types (incl. all 4 new types) → CoC chronological report renders
69KB with all values incl. photo thumbnails.

Bugs found and fixed:

1. fp.process.node.input_ids missing copy=True — when a master recipe
   was cloned per-part (the standard variant pattern), the operator
   prompts on each step did NOT get copied to the variant. Result: jobs
   built from variants ran with zero prompts even though the master had
   them. Fixed: input_ids now copy=True so cloning auto-duplicates.

2. CoC chronological template read dest.input_ids where dest is
   fp.job.step. Steps don't carry input_ids — that field lives on the
   recipe node. Result: AttributeError aborted the entire CoC render.
   Fixed: walk via dest.recipe_node_id.input_ids; preserves the existing
   collect=True filter.

3. CoC chronological template used hasattr() in a t-value expression.
   QWeb's expression engine doesn't expose Python builtins, raised
   KeyError: 'hasattr'. Fixed: use 'collect' in i._fields instead.

Also enhanced photo rendering in CoC: was just "[Attachment]" placeholder;
now renders an actual <img> thumbnail (max 80px tall) plus the filename.

Battle-test script saved to fusion_plating/scripts/bt_e2e_anodize_v2.py
for re-runs / regression testing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 22:53:59 -04:00

50 lines
1.8 KiB
Python

# -*- coding: utf-8 -*-
"""Phase 3 recon — SO→job→step trigger pipeline."""
# Find recent confirmed SO with x_fc_process_variant_id set
sol = env['sale.order.line'].search([
('x_fc_process_variant_id', '!=', False),
], limit=5, order='id desc')
print('=== Recent SO lines with process variant ===')
for l in sol:
print(' SO=%s line=%d part=%s recipe=%s' % (
l.order_id.name, l.id, l.x_fc_part_catalog_id.part_number,
l.x_fc_process_variant_id.name,
))
# Pick one and trace its job
if sol:
target = sol[0]
so = target.order_id
print('\nFor SO %s state=%s' % (so.name, so.state))
job = env['fp.job'].search([('origin', '=', so.name)], limit=1)
print('Linked fp.job:', job.id if job else 'NONE')
if job:
steps = env['fp.job.step'].search([('job_id', '=', job.id)], order='sequence')
print(' steps: %d' % len(steps))
for s in steps[:8]:
recipe_node = s.recipe_node_id
input_count = len(recipe_node.input_ids) if recipe_node else 0
print(' [%s] %s -- recipe_node=%d inputs=%d state=%s' % (
s.sequence, s.name, recipe_node.id if recipe_node else 0,
input_count, s.state,
))
# Check whether default_kind exists on every step in the Anodize recipe
Node = env['fusion.plating.process.node']
anodize = Node.browse(136)
def collect_steps(n, out):
if n.node_type in ('step', 'operation'):
out.append(n)
for c in n.child_ids:
collect_steps(c, out)
steps = []
collect_steps(anodize, steps)
print('\n=== Anodize step audit ===')
print('Total step/operation nodes:', len(steps))
no_kind = [s for s in steps if not s.default_kind]
print('Without default_kind:', len(no_kind))
for s in no_kind[:8]:
print(' - %s (type=%s)' % (s.name, s.node_type))