172 lines
6.2 KiB
Python
172 lines
6.2 KiB
Python
# Comprehensive internal-process walk.
|
|
#
|
|
# Phases:
|
|
# A) Pause / resume — multiple intervals merge into duration_actual
|
|
# B) Skip an opt-in step
|
|
# C) Skipped steps don't block job mark-done
|
|
# D) Wet plating step finish auto-spawns bake.window with right window_hours
|
|
# E) Bake-window state evolves (awaiting_bake → bake_in_progress → baked)
|
|
# F) Failure: try to start a step already done
|
|
|
|
import time
|
|
from datetime import timedelta
|
|
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']
|
|
Coating = env['fp.coating.config']
|
|
target = P.browse(2529)
|
|
|
|
# Find or build a coating that requires bake relief.
|
|
coating = Coating.search([('requires_bake_relief', '=', True)], limit=1)
|
|
if not coating:
|
|
coating = Coating.search([], limit=1)
|
|
coating.requires_bake_relief = True
|
|
coating.bake_window_hours = 4.0
|
|
coating.bake_temperature = 375.0
|
|
coating.bake_temperature_uom = 'F'
|
|
coating.bake_duration_hours = 4.0
|
|
print(f'[setup] Configured {coating.name} to require bake relief (4h window @ 375°F for 4h)')
|
|
else:
|
|
print(f'[setup] Using existing bake-required coating: {coating.name} ({coating.bake_window_hours}h window)')
|
|
|
|
# Build a part using this coating as default.
|
|
part = Part.create({
|
|
'partner_id': target.id,
|
|
'part_number': 'INT-' + fields.Datetime.now().strftime('%H%M%S'),
|
|
'revision': 'A',
|
|
'name': 'Internal-process test bracket',
|
|
'substrate_material': 'steel',
|
|
'x_fc_default_coating_config_id': coating.id,
|
|
})
|
|
|
|
w = W.create({
|
|
'partner_id': target.id, 'po_pending': True,
|
|
'po_number': 'PO-INT-' + fields.Datetime.now().strftime('%H%M%S'),
|
|
'invoice_strategy': 'net_terms',
|
|
})
|
|
w._onchange_partner_id()
|
|
ln = Line.new({'wizard_id': w.id})
|
|
ln.part_catalog_id = part
|
|
ln._onchange_part_clears_variant()
|
|
Line.create({
|
|
'wizard_id': w.id, 'part_catalog_id': part.id,
|
|
'coating_config_id': coating.id,
|
|
'quantity': 5, 'unit_price': 22.0,
|
|
})
|
|
result = w.action_create_order()
|
|
so = env['sale.order'].browse(result['res_id'])
|
|
so.action_confirm()
|
|
job = env['fp.job'].search([('sale_order_id', '=', so.id)], limit=1)
|
|
print(f'[setup] Job {job.name} with {len(job.step_ids)} steps')
|
|
|
|
# ====================================================================== A
|
|
print()
|
|
print('='*72)
|
|
print('A — Pause + resume on a step. Multiple intervals must merge.')
|
|
print('='*72)
|
|
masking = job.step_ids.sorted('sequence')[0]
|
|
masking.button_start()
|
|
print(f' start → state={masking.state}, open logs={len(masking.time_log_ids)}')
|
|
time.sleep(2)
|
|
masking.button_pause()
|
|
print(f' pause → state={masking.state}, logs={len(masking.time_log_ids)}, '
|
|
f'log[0]={masking.time_log_ids[0].duration_minutes:.3f} min')
|
|
time.sleep(2)
|
|
masking.button_start() # resume
|
|
print(f' resume → state={masking.state}, logs={len(masking.time_log_ids)}')
|
|
time.sleep(2)
|
|
masking.button_finish()
|
|
print(f' finish → state={masking.state}, logs={len(masking.time_log_ids)}')
|
|
total = sum(masking.time_log_ids.mapped('duration_minutes'))
|
|
print(f' duration_actual={masking.duration_actual:.3f} min (sum of logs={total:.3f} min)')
|
|
if abs(masking.duration_actual - total) < 0.001:
|
|
print(f' ✓ Pause/resume merged correctly')
|
|
else:
|
|
print(f' ❌ Mismatch')
|
|
|
|
# ====================================================================== B
|
|
print()
|
|
print('='*72)
|
|
print('B — Skip an opt-in step')
|
|
print('='*72)
|
|
racking = job.step_ids.sorted('sequence')[1]
|
|
print(f' Step: {racking.name} state={racking.state}')
|
|
racking.button_skip()
|
|
print(f' After Skip: state={racking.state}')
|
|
if racking.state == 'skipped':
|
|
print(f' ✓ Skip works')
|
|
|
|
# ====================================================================== C — walk rest, then mark-done
|
|
print()
|
|
print('='*72)
|
|
print('C — Walk remaining steps (some will spawn bake-window). Mark job done.')
|
|
print('='*72)
|
|
spawn_count_before = env['fusion.plating.bake.window'].search_count([])
|
|
for s in job.step_ids.sorted('sequence'):
|
|
if s.state in ('done', 'skipped', 'cancelled'):
|
|
continue
|
|
if s.state in ('pending', 'ready'):
|
|
s.button_start()
|
|
if s.state == 'in_progress':
|
|
s.button_finish()
|
|
spawn_count_after = env['fusion.plating.bake.window'].search_count([])
|
|
created_bw = spawn_count_after - spawn_count_before
|
|
print(f' Walked all remaining steps to done')
|
|
print(f' Bake windows spawned during walk: {created_bw}')
|
|
|
|
bws = env['fusion.plating.bake.window'].search([('part_ref', '=', job.name)])
|
|
for bw in bws:
|
|
print(f' {bw.name}: state={bw.state}, plate_exit={bw.plate_exit_time}, required_by={bw.bake_required_by}, time_remaining={bw.time_remaining_display}')
|
|
|
|
# ====================================================================== D — try to mark job done
|
|
print()
|
|
print('='*72)
|
|
print('D — Mark job done (skipped+done steps both count as terminal)')
|
|
print('='*72)
|
|
try:
|
|
job.button_mark_done()
|
|
print(f' ✓ Job done — state={job.state}')
|
|
except Exception as e:
|
|
print(f' ❌ {e}')
|
|
|
|
# ====================================================================== E — bake-window lifecycle
|
|
if bws:
|
|
bw = bws[0]
|
|
print()
|
|
print('='*72)
|
|
print('E — Bake-window lifecycle: start → end')
|
|
print('='*72)
|
|
print(f' Before start: state={bw.state}, color={bw.status_color}')
|
|
bw.action_start_bake()
|
|
print(f' After start_bake: state={bw.state}, bake_start={bw.bake_start_time}, color={bw.status_color}')
|
|
time.sleep(1)
|
|
bw.action_end_bake()
|
|
print(f' After end_bake: state={bw.state}, bake_end={bw.bake_end_time}, duration_h={bw.bake_duration_hours:.4f}')
|
|
|
|
# ====================================================================== F — failure: start a done step
|
|
print()
|
|
print('='*72)
|
|
print('F — Failure paths')
|
|
print('='*72)
|
|
done_step = job.step_ids.filtered(lambda s: s.state == 'done')[:1]
|
|
if done_step:
|
|
try:
|
|
done_step.button_start()
|
|
print(f' ❌ Allowed re-start of a done step')
|
|
except Exception as e:
|
|
print(f' ✓ Blocked: {str(e)[:80]}')
|
|
|
|
# Try to skip an already-done step
|
|
try:
|
|
done_step.button_skip()
|
|
print(f' ❌ Allowed skip of done step')
|
|
except Exception as e:
|
|
print(f' ✓ Blocked: {str(e)[:80]}')
|
|
|
|
env.cr.commit()
|
|
print()
|
|
print('== Internal-process walk complete ==')
|