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>
57 lines
2.1 KiB
Python
57 lines
2.1 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""End-to-end battle test — Phase 1: reconnaissance."""
|
|
|
|
# Find ABC Manufacturing
|
|
abc = env['res.partner'].search([('name', 'ilike', 'ABC Manufactor')], limit=1)
|
|
if not abc:
|
|
abc = env['res.partner'].search([('name', 'ilike', 'ABC Manuf')], limit=1)
|
|
print('ABC partner:', abc.id, abc.name if abc else 'NOT FOUND')
|
|
|
|
# List recipes (process nodes that are recipe roots)
|
|
recipes = env['fusion.plating.process.node'].search([
|
|
('node_type', '=', 'recipe'),
|
|
('parent_id', '=', False),
|
|
('is_template', '=', False),
|
|
])
|
|
print('\nRecipes (non-template):')
|
|
for r in recipes[:10]:
|
|
step_count = env['fusion.plating.process.node'].search_count([
|
|
('parent_id', '=', r.id), ('node_type', '=', 'step'),
|
|
])
|
|
print(' id=%d name=%s steps=%d code=%s' % (r.id, r.name, step_count, r.code or ''))
|
|
|
|
# Anodize-specific recipes
|
|
anodize = env['fusion.plating.process.node'].search([
|
|
('node_type', '=', 'recipe'), ('name', 'ilike', 'anodize'),
|
|
])
|
|
print('\nAnodize-related:')
|
|
for r in anodize[:5]:
|
|
print(' id=%d name=%s' % (r.id, r.name))
|
|
|
|
# Find parts in the catalog for ABC (or any)
|
|
parts = env['fp.part.catalog'].search([], limit=5)
|
|
print('\nSample parts in catalog:')
|
|
for p in parts:
|
|
print(' id=%d num=%s partner=%s' % (p.id, p.part_number, p.partner_id.name or ''))
|
|
|
|
# Existing fp.job records
|
|
jobs = env['fp.job'].search([], limit=5, order='id desc')
|
|
print('\nRecent fp.job rows:')
|
|
for j in jobs:
|
|
print(' id=%d origin=%s state=%s recipe=%s' % (
|
|
j.id, j.origin or '', j.state or '', j.recipe_id.name if j.recipe_id else 'no_recipe'
|
|
))
|
|
|
|
# Check what kinds of steps exist on a sample recipe
|
|
if recipes:
|
|
big_recipe = max(recipes, key=lambda r: env['fusion.plating.process.node'].search_count([
|
|
('parent_id', '=', r.id),
|
|
]))
|
|
print('\nBiggest recipe: id=%d name=%s' % (big_recipe.id, big_recipe.name))
|
|
steps = env['fusion.plating.process.node'].search([
|
|
('parent_id', '=', big_recipe.id),
|
|
('node_type', '=', 'step'),
|
|
], order='sequence')
|
|
for s in steps[:20]:
|
|
print(' step %d: %s (kind=%s)' % (s.sequence, s.name, s.default_kind or '-'))
|