# Step 5 — Carlos walks the plating job. Test BOTH paths: # A) Try to mark_done with steps still ready → must be blocked # B) Walk every step → mark_done succeeds # Build a fresh SO + job (don't reuse 423 — its job is already done). 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-STEP5-001', 'planned_start_date': fields.Date.today(), 'customer_deadline': fields.Date.add(fields.Date.today(), days=14), 'invoice_strategy': 'net_terms', 'delivery_method': 'shipping_partner', }) 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': ln.coating_config_id.id, 'quantity': 10, 'unit_price': 15.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'[Carlos] Fresh job {job.name} for SO {so.name}') print(f' Steps: {len(job.step_ids)}, all in state: {set(job.step_ids.mapped("state"))}') # Path A: try mark_done without walking steps. print() print('[Carlos] Try Mark Done WITHOUT walking any step (compliance test):') try: job.button_mark_done() print(' ❌ JOB CLOSED WITH ZERO STEPS WALKED — guard failed') except Exception as e: print(f' ✓ blocked: {str(e)[:200]}') # Path B: walk every step then mark_done. print() print('[Carlos] Walk every step, then Mark Done:') for s in job.step_ids.sorted('sequence'): if s.state in ('pending', 'ready'): s.button_start() if s.state == 'in_progress': s.button_finish() done_count = len(job.step_ids.filtered(lambda s: s.state == 'done')) print(f' walked {done_count}/{len(job.step_ids)} to done') try: job.button_mark_done() print(f' ✓ Job marked done — state={job.state}, finished={job.date_finished}') except Exception as e: print(f' ❌ Mark Done failed AFTER walking: {e}') # Verify side effects on this job too. Delivery = env['fusion.plating.delivery'] deliveries = Delivery.search([('x_fc_job_id', '=', job.id)]) Cert = env['fp.certificate'] certs = Cert.search([('x_fc_job_id', '=', job.id)]) print(f' Side effects: {len(deliveries)} delivery, {len(certs)} certificate') # Path C: manager bypass (admin is a manager). print() print('[Mgr] Test manager bypass via context fp_skip_step_gate=True') w2 = W.create({ 'partner_id': target.id, 'po_pending': True, 'po_number': 'PO-STEP5-002', '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': 15.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) print(f' Created fresh job {job2.name} with {len(job2.step_ids)} unworked steps') try: job2.with_context(fp_skip_step_gate=True).button_mark_done() print(f' ✓ Manager bypass worked — job state={job2.state}') except Exception as e: print(f' ❌ Bypass failed: {e}') env.cr.commit() print() print('== Step 5 complete ==')