191 lines
7.6 KiB
Python
191 lines
7.6 KiB
Python
# Walk part creation + 4 process variants step by step.
|
|
# Personas:
|
|
# Bob (Estimator) — owns the part catalog, designs process variants
|
|
# Sarah (CSR) — picks a variant on order entry
|
|
#
|
|
# Goal: prove that
|
|
# 1) Bob can create a part
|
|
# 2) Bob can attach 4 distinct process variants via the Composer flow
|
|
# 3) One is flagged default; switching default works
|
|
# 4) Sarah opens a Direct Order, picks the part — variant dropdown lists ALL FOUR
|
|
# 5) Sarah picks a non-default variant; the SO + job actually use it
|
|
|
|
from odoo import fields
|
|
from odoo.addons.fusion_plating_configurator.controllers.fp_part_composer_controller \
|
|
import _list_variants, _clone_subtree
|
|
|
|
P = env['res.partner']
|
|
Part = env['fp.part.catalog']
|
|
Coating = env['fp.coating.config']
|
|
Treat = env['fp.treatment']
|
|
Node = env['fusion.plating.process.node']
|
|
Tpl = Node # template recipes are also fp.process.node records
|
|
|
|
# ====================================================================== STEP 2
|
|
print('='*72)
|
|
print('STEP 2 — Bob creates a brand-new part')
|
|
print('='*72)
|
|
target_partner = P.browse(2529) # 2CM INNOVATIVE
|
|
default_coating = Coating.search([], limit=1)
|
|
default_treats = Treat.search([], limit=2)
|
|
part = Part.create({
|
|
'partner_id': target_partner.id,
|
|
'part_number': 'E2E-VAR-' + fields.Datetime.now().strftime('%H%M%S'),
|
|
'revision': 'A',
|
|
'name': 'E2E variant test bracket',
|
|
'substrate_material': 'aluminium',
|
|
'surface_area': 12.5,
|
|
'surface_area_uom': 'sq_in',
|
|
'weight': 0.45,
|
|
'complexity': 'simple',
|
|
'masking_zones': 1,
|
|
'x_fc_default_coating_config_id': default_coating.id,
|
|
'x_fc_default_treatment_ids': [(6, 0, default_treats.ids)],
|
|
})
|
|
print(f'[Bob] Created part: {part.display_name} (id={part.id})')
|
|
print(f' default coating: {part.x_fc_default_coating_config_id.name}')
|
|
print(f' default treatments: {default_treats.mapped("name")}')
|
|
print(f' process_variant_count (BEFORE adding any): {part.process_variant_count}')
|
|
|
|
# Find a shared template recipe to clone from. Templates = fp.process.node
|
|
# records with node_type='recipe', parent_id=False, part_catalog_id=False.
|
|
template = Node.search([
|
|
('node_type', '=', 'recipe'),
|
|
('parent_id', '=', False),
|
|
('part_catalog_id', '=', False),
|
|
], limit=1)
|
|
if not template:
|
|
print(' ❌ No shared template recipes available — cannot continue!')
|
|
raise SystemExit
|
|
print(f'[Bob] Will clone from shared template: {template.name} ({len(template.child_ids)} root children)')
|
|
|
|
# ====================================================================== STEP 3
|
|
print()
|
|
print('='*72)
|
|
print('STEP 3 — Bob adds variant #1: Standard Production')
|
|
print('='*72)
|
|
v1 = _clone_subtree(env, template, part, parent=False)
|
|
v1.variant_label = 'Standard Production'
|
|
v1.is_default_variant = True
|
|
part.default_process_id = v1.id
|
|
print(f'[Bob] Created variant: {v1.variant_label} (root node id={v1.id}, name="{v1.name}")')
|
|
print(f' is_default: {v1.is_default_variant}')
|
|
print(f' child nodes cloned: {len(v1.child_ids)}')
|
|
|
|
# ====================================================================== STEP 4
|
|
print()
|
|
print('='*72)
|
|
print('STEP 4 — Bob adds variant #2: Aerospace Cert (AS9100)')
|
|
print('='*72)
|
|
v2 = _clone_subtree(env, template, part, parent=False)
|
|
v2.variant_label = 'Aerospace Cert (AS9100)'
|
|
print(f'[Bob] Created variant: {v2.variant_label} (root id={v2.id})')
|
|
print(f' is_default: {v2.is_default_variant} (correct — first one stays default)')
|
|
|
|
# ====================================================================== STEP 5
|
|
print()
|
|
print('='*72)
|
|
print('STEP 5 — Bob adds variant #3: Quick-turn (no bake)')
|
|
print('='*72)
|
|
v3 = _clone_subtree(env, template, part, parent=False)
|
|
v3.variant_label = 'Quick-turn (no bake)'
|
|
print(f'[Bob] Created variant: {v3.variant_label} (root id={v3.id})')
|
|
|
|
# ====================================================================== STEP 6
|
|
print()
|
|
print('='*72)
|
|
print('STEP 6 — Bob adds variant #4: Heavy build (wear)')
|
|
print('='*72)
|
|
v4 = _clone_subtree(env, template, part, parent=False)
|
|
v4.variant_label = 'Heavy build (wear)'
|
|
print(f'[Bob] Created variant: {v4.variant_label} (root id={v4.id})')
|
|
|
|
# Refresh the part and inspect what the form would show.
|
|
part.invalidate_recordset()
|
|
print()
|
|
print(f'[Bob] After 4 adds — part {part.display_name}:')
|
|
print(f' process_variant_count: {part.process_variant_count}')
|
|
print(f' default_process_id: {part.default_process_id.name if part.default_process_id else None}')
|
|
print(f' Variants list (per Composer endpoint /fp/part/composer/state):')
|
|
for entry in _list_variants(part):
|
|
flag = '★ default' if entry['is_default'] else ' '
|
|
print(f' {flag} id={entry["id"]:>5} "{entry["label"]}" — {entry["node_count"]} nodes')
|
|
|
|
# ====================================================================== STEP 7
|
|
print()
|
|
print('='*72)
|
|
print('STEP 7 — Sarah enters a Direct Order, picks the part, picks a variant')
|
|
print('='*72)
|
|
W = env['fp.direct.order.wizard']
|
|
Line = env['fp.direct.order.line']
|
|
w = W.create({
|
|
'partner_id': target_partner.id, 'po_pending': True,
|
|
'po_number': 'PO-VARTEST-001',
|
|
'invoice_strategy': 'net_terms',
|
|
})
|
|
w._onchange_partner_id()
|
|
|
|
# Sarah adds a line, picks the part. Onchange should pre-fill default coating.
|
|
ln = Line.new({'wizard_id': w.id})
|
|
ln.part_catalog_id = part
|
|
ln._onchange_part_clears_variant()
|
|
print(f'[Sarah] Picked part {part.part_number}.')
|
|
print(f' Pre-filled coating: {ln.coating_config_id.name if ln.coating_config_id else "(none)"}')
|
|
print(f' Pre-filled treatments: {ln.treatment_ids.mapped("name") if ln.treatment_ids else "(none)"}')
|
|
|
|
# What variants would the dropdown show? Inspect process_variant_id field domain.
|
|
print()
|
|
print(f'[Sarah] Looking at the Variant dropdown on the line:')
|
|
# Domain on x_fc_process_variant_id (defined on sale.order.line) is part-scoped.
|
|
# For the wizard line it's process_variant_id with the same domain.
|
|
visible_variants = part.process_variant_ids
|
|
print(f' Domain: part_scoped (id, child_of, ...). Visible variants: {len(visible_variants)}')
|
|
for v in visible_variants:
|
|
flag = '★' if v.is_default_variant else ' '
|
|
print(f' {flag} {v.variant_label or v.name} (id={v.id})')
|
|
|
|
# Sarah picks variant #3 (Quick-turn).
|
|
ln.process_variant_id = v3
|
|
print()
|
|
print(f'[Sarah] Picked variant: {ln.process_variant_id.variant_label}')
|
|
|
|
# Persist via Line.create with the chosen variant.
|
|
new_line = Line.create({
|
|
'wizard_id': w.id,
|
|
'part_catalog_id': part.id,
|
|
'coating_config_id': default_coating.id,
|
|
'process_variant_id': v3.id,
|
|
'quantity': 5,
|
|
'unit_price': 25.0,
|
|
})
|
|
print(f' Saved line: process_variant_id={new_line.process_variant_id.variant_label}')
|
|
|
|
# ====================================================================== STEP 8
|
|
print()
|
|
print('='*72)
|
|
print('STEP 8 — Confirm SO; verify the JOB uses variant #3, not the default')
|
|
print('='*72)
|
|
result = w.action_create_order()
|
|
so = env['sale.order'].browse(result['res_id'])
|
|
print(f'[Sarah] SO created: {so.name}')
|
|
|
|
# Inspect the SO line's variant.
|
|
sol = so.order_line[:1]
|
|
print(f' SO line process_variant_id: {sol.x_fc_process_variant_id.variant_label if sol.x_fc_process_variant_id else "(none)"}')
|
|
|
|
# Confirm the SO.
|
|
so.action_confirm()
|
|
job = env['fp.job'].search([('sale_order_id', '=', so.id)], limit=1)
|
|
print(f' Job created: {job.name}')
|
|
print(f' Job recipe_id: {job.recipe_id.name if job.recipe_id else "(none)"}')
|
|
print(f' EXPECTED: recipe_id should match variant #3 root (id={v3.id}, name="{v3.name}")')
|
|
print(f' ACTUAL: recipe_id={job.recipe_id.id} (name="{job.recipe_id.name}")')
|
|
if job.recipe_id.id == v3.id:
|
|
print(f' ✓ Job correctly inherited the picked variant')
|
|
else:
|
|
print(f' ❌ Job did NOT use the picked variant! Recipe is {job.recipe_id.name}, expected {v3.name}')
|
|
|
|
env.cr.commit()
|
|
print()
|
|
print('== Walk complete ==')
|