changes
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
# 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()
|
||||
Reference in New Issue
Block a user