92 lines
3.2 KiB
Python
92 lines
3.2 KiB
Python
# Verify predecessor enforcement
|
|
from odoo import fields
|
|
W = env['fp.direct.order.wizard']
|
|
Line = env['fp.direct.order.line']
|
|
P = env['res.partner']
|
|
Part = env['fp.part.catalog']
|
|
target = P.browse(2529)
|
|
part = Part.search([('x_fc_default_coating_config_id', '!=', False)], limit=1)
|
|
|
|
w = W.create({
|
|
'partner_id': target.id, 'po_pending': True,
|
|
'po_number': 'PO-S14V-' + fields.Datetime.now().strftime('%H%M%S'),
|
|
'invoice_strategy': 'net_terms',
|
|
})
|
|
w._onchange_partner_id()
|
|
Line.create({
|
|
'wizard_id': w.id, 'part_catalog_id': part.id,
|
|
'coating_config_id': part.x_fc_default_coating_config_id.id,
|
|
'quantity': 5, 'unit_price': 20.0,
|
|
})
|
|
r = w.action_create_order()
|
|
so = env['sale.order'].browse(r['res_id'])
|
|
so.action_confirm()
|
|
job = env['fp.job'].search([('sale_order_id', '=', so.id)], limit=1)
|
|
|
|
# Find plating step + flag its recipe node as serial-required
|
|
plating = job.step_ids.filtered(lambda s: 'plating' in (s.name or '').lower())[:1]
|
|
if plating and plating.recipe_node_id:
|
|
plating.recipe_node_id.requires_predecessor_done = True
|
|
print(f' Recipe author flagged "{plating.name}" requires_predecessor_done')
|
|
plating.invalidate_recordset()
|
|
print(f' Step picks it up via related: {plating.requires_predecessor_done}')
|
|
|
|
# Try to start plating with earlier steps still ready
|
|
print()
|
|
print(f' [Operator] Tries to start plating WITHOUT finishing earlier steps:')
|
|
try:
|
|
plating.button_start()
|
|
print(f' ❌ Allowed early start! state={plating.state}')
|
|
except Exception as e:
|
|
print(f' ✓ Blocked: {str(e)[:200]}')
|
|
|
|
# Walk earlier steps to done
|
|
print()
|
|
print(f' [Operator] Walks earlier steps to done:')
|
|
for s in job.step_ids.sorted('sequence'):
|
|
if s == plating:
|
|
break
|
|
if s.state in ('pending', 'ready'):
|
|
s.button_start()
|
|
if s.state == 'in_progress':
|
|
s.button_finish()
|
|
print(f' Earlier steps now: {set(job.step_ids.filtered(lambda x: x.sequence < plating.sequence).mapped("state"))}')
|
|
|
|
# Try plating again
|
|
print()
|
|
print(f' [Operator] Tries plating again after earlier steps done:')
|
|
try:
|
|
plating.button_start()
|
|
print(f' ✓ Allowed: state={plating.state}')
|
|
except Exception as e:
|
|
print(f' ❌ Still blocked: {e}')
|
|
|
|
# Test manager bypass via context
|
|
print()
|
|
print(f' Test manager bypass on a fresh job:')
|
|
w2 = W.create({
|
|
'partner_id': target.id, 'po_pending': True,
|
|
'po_number': 'PO-S14B-' + fields.Datetime.now().strftime('%H%M%S'),
|
|
'invoice_strategy': 'net_terms',
|
|
})
|
|
w2._onchange_partner_id()
|
|
Line.create({
|
|
'wizard_id': w2.id, 'part_catalog_id': part.id,
|
|
'coating_config_id': part.x_fc_default_coating_config_id.id,
|
|
'quantity': 5, 'unit_price': 20.0,
|
|
})
|
|
r2 = w2.action_create_order()
|
|
so2 = env['sale.order'].browse(r2['res_id'])
|
|
so2.action_confirm()
|
|
job2 = env['fp.job'].search([('sale_order_id', '=', so2.id)], limit=1)
|
|
plating2 = job2.step_ids.filtered(lambda s: 'plating' in (s.name or '').lower())[:1]
|
|
# (the recipe_node already has requires_predecessor_done=True from earlier write)
|
|
print(f' Plating step requires_predecessor_done: {plating2.requires_predecessor_done}')
|
|
try:
|
|
plating2.with_context(fp_skip_predecessor_check=True).button_start()
|
|
print(f' ✓ Manager bypass: state={plating2.state}')
|
|
except Exception as e:
|
|
print(f' ❌ Bypass failed: {e}')
|
|
|
|
env.cr.commit()
|