"""Sub 8 smoke test — runs inside odoo-shell on entech.""" env = env Partner = env['res.partner'] SO = env['sale.order'] Receiving = env['fp.receiving'] Insp = env['fp.racking.inspection'] Delivery = env['fusion.plating.delivery'] # ---- Field / model presence ----------------------------------------- assert 'box_count_in' in Receiving._fields assert 'x_fc_box_count_out' in Delivery._fields assert hasattr(Insp, 'action_start') assert hasattr(Insp, 'action_complete') print('[OK] Models + fields present') # ---- Receiving state machine ----------------------------------------- cust = Partner.create({ 'name': 'Sub 8 Smoke Customer', 'is_company': True, 'customer_rank': 1, 'email': 'sub8@test.com', }) Product = env['product.product'] product = Product.search([('sale_ok', '=', True)], limit=1) so = SO.create({ 'partner_id': cust.id, 'x_fc_po_number': 'PO-SUB8', 'x_fc_po_received': True, 'order_line': [(0, 0, { 'product_id': product.id, 'product_uom_qty': 10, 'name': 'Sub 8 smoke', 'x_fc_internal_description': 'smoke', })], }) recv = Receiving.create({'sale_order_id': so.id, 'expected_qty': 10}) assert recv.state == 'draft' # Fail: mark counted without box count try: recv.action_mark_counted() assert False, 'should require box_count_in' except Exception as e: assert 'box' in str(e).lower() or 'Boxes' in str(e) print('[OK] mark_counted blocked without box_count_in') recv.box_count_in = 4 recv.action_mark_counted() assert recv.state == 'counted' print(f'[OK] mark_counted → state={recv.state}, boxes={recv.box_count_in}') recv.action_mark_staged() assert recv.state == 'staged' print(f'[OK] mark_staged → state={recv.state}') recv.action_close() assert recv.state == 'closed' print(f'[OK] close → state={recv.state}') # ---- MO auto-creates racking inspection ------------------------------ so.action_confirm() MO = env['mrp.production'] mos = MO.search([('origin', '=', so.name)]) assert mos, 'MO should be created by SO confirm' mo = mos[0] print(f'[OK] MO created: {mo.name}') inspections = Insp.search([('production_id', '=', mo.id)]) assert len(inspections) == 1, f'expected 1 inspection, got {len(inspections)}' inspection = inspections[0] assert inspection.state == 'draft' assert len(inspection.line_ids) == 1 assert inspection.line_ids[0].qty_expected == 10 print(f'[OK] Racking inspection auto-created: {inspection.name}') # ---- Inspection lifecycle -------------------------------------------- inspection.action_start() assert inspection.state == 'inspecting' assert inspection.inspector_id == env.user print('[OK] Inspection started') # OK case inspection.line_ids[0].write({'qty_found': 10, 'condition': 'ok'}) inspection.action_complete() assert inspection.state == 'done', f'expected done, got {inspection.state}' print(f'[OK] Inspection complete → state={inspection.state}') # ---- Flag a discrepancy on a separate inspection --------------------- # Reopen + set damage inspection.action_reopen() inspection.line_ids[0].write({'qty_found': 8, 'condition': 'major'}) inspection.action_complete() assert inspection.state == 'discrepancy_flagged' print(f'[OK] Discrepancy flagged → state={inspection.state}') # ---- WO soft gate -------------------------------------------------- # Reopen inspection to 'inspecting' to test the gate inspection.action_reopen() assert inspection.state == 'inspecting' first_wo = mo.workorder_ids.sorted('sequence')[:1] if first_wo: try: # Soft gate — manager bypasses. Try with a non-manager user if available. demo_user = env['res.users'].search([('login', '=', 'demo')], limit=1) if demo_user: first_wo.with_user(demo_user)._fp_warn_if_racking_inspection_pending() assert False, 'soft gate should raise for non-manager' else: # No demo user — check manager bypass works (env.user is admin) first_wo._fp_warn_if_racking_inspection_pending() print('[OK] Manager bypass (no demo user to test block)') except Exception as e: if 'Racking inspection' in str(e) or 'Inspecting' in str(e): print('[OK] Soft gate blocks non-manager when inspection pending') else: raise else: print('[SKIP] No WOs on this MO to test gate') # Mark inspection done and verify gate is clear inspection.line_ids[0].qty_found = 10 inspection.line_ids[0].condition = 'ok' inspection.action_complete() assert inspection.state == 'done' if first_wo: first_wo._fp_warn_if_racking_inspection_pending() # should not raise print('[OK] Gate clears when inspection is Done') # ---- Box parity on delivery ------------------------------------------ delivery = Delivery.create({ 'partner_id': cust.id, 'job_ref': mo.name, 'x_fc_box_count_out': 3, }) delivery._fp_check_box_parity() # Check chatter for the warning messages = env['mail.message'].search([ ('model', '=', 'fusion.plating.delivery'), ('res_id', '=', delivery.id), ]) body = ' '.join(m.body for m in messages if m.body) assert 'parity' in body.lower() or 'boxes' in body.lower() or 'shipped' in body.lower(), ( f'expected parity warning; got messages: {body[:200]}' ) print('[OK] Box-parity warning posted on delivery chatter') # Matching counts — no warning delivery2 = Delivery.create({ 'partner_id': cust.id, 'job_ref': mo.name, 'x_fc_box_count_out': 4, # matches receiving.box_count_in }) before = env['mail.message'].search_count([ ('model', '=', 'fusion.plating.delivery'), ('res_id', '=', delivery2.id), ]) delivery2._fp_check_box_parity() after = env['mail.message'].search_count([ ('model', '=', 'fusion.plating.delivery'), ('res_id', '=', delivery2.id), ]) assert after == before, f'expected no new message, before={before} after={after}' print('[OK] No warning when boxes match') env.cr.rollback() print('\n=== SUB 8 SMOKE PASS — all assertions held ===')