"""Sub 5 smoke test — runs inside odoo-shell on entech.""" env = env Partner = env['res.partner'] Part = env['fp.part.catalog'] Coating = env['fp.coating.config'] Thickness = env['fp.coating.thickness'] Serial = env['fp.serial'] SO = env['sale.order'] admin = env.ref('base.user_admin') # ---- Seed customer + part + coating + thickness options --------------- cust = Partner.create({ 'name': 'Sub5 Smoke Customer', 'is_company': True, 'customer_rank': 1, }) part_a = Part.create({ 'partner_id': cust.id, 'part_number': 'SUB5-001', 'revision': 'A', }) # Bump to a newer revision so we have A + B in the chain part_a.action_create_revision() part_b = Part.search([ ('parent_part_id', '=', (part_a.parent_part_id or part_a).id), ('is_latest_revision', '=', True), ], limit=1, order='revision_number desc') assert part_b.revision != part_a.revision, 'revision chain failed' print(f'[OK] Part revisions: A={part_a.revision} / latest={part_b.revision}') coating = Coating.search([], limit=1) or Coating.create({ 'name': 'Sub5 Coating Test', 'process_type_id': env['fusion.plating.process.type'].search([], limit=1).id, }) t1 = Thickness.create({ 'coating_config_id': coating.id, 'value': 0.001, 'uom': 'inches', 'sequence': 10, }) t2 = Thickness.create({ 'coating_config_id': coating.id, 'value': 0.0015, 'uom': 'inches', 'sequence': 20, }) print(f'[OK] Thickness options: {t1.display_name} / {t2.display_name}') # ---- Create SO with latest revision, assign serial via create-on-fly -- product = env['product.product'].search([('sale_ok', '=', True)], limit=1) so = SO.create({ 'partner_id': cust.id, 'x_fc_po_number': 'PO-SUB5-SMOKE', 'x_fc_po_received': True, 'order_line': [(0, 0, { 'product_id': product.id, 'product_uom_qty': 10, 'name': 'Sub5 smoke line', 'x_fc_part_catalog_id': part_b.id, 'x_fc_coating_config_id': coating.id, 'x_fc_thickness_id': t1.id, 'x_fc_internal_description': 'smoke', })], }) line = so.order_line[0] line.invalidate_recordset() assert line.x_fc_revision_snapshot == part_b.revision, ( f'snapshot mismatch: {line.x_fc_revision_snapshot} vs {part_b.revision}' ) print(f'[OK] Revision snapshot captured on create: {line.x_fc_revision_snapshot}') # ---- Generate serial button ----------------------------------------- line.action_generate_serial() line.invalidate_recordset() assert line.x_fc_serial_id, 'serial should be assigned' assert line.x_fc_serial_id.name.startswith('FP-SN-'), ( f'unexpected serial name: {line.x_fc_serial_id.name}' ) print(f'[OK] Generate serial: {line.x_fc_serial_id.name}') # ---- Customer-typed serial -------------------------------------------- so2 = SO.create({ 'partner_id': cust.id, 'x_fc_po_number': 'PO-SUB5-SMOKE-2', 'x_fc_po_received': True, 'order_line': [(0, 0, { 'product_id': product.id, 'product_uom_qty': 5, 'name': 'Sub5 smoke line 2', 'x_fc_part_catalog_id': part_b.id, 'x_fc_coating_config_id': coating.id, 'x_fc_internal_description': 'smoke2', })], }) line2 = so2.order_line[0] typed_serial = Serial.create({'name': 'CUST-999'}) line2.x_fc_serial_id = typed_serial.id line2.invalidate_recordset() assert line2.x_fc_serial_id.name == 'CUST-999' print('[OK] Customer-typed serial attached') # ---- Revision picker: switch to Rev A -------------------------------- line.x_fc_revision_pick_id = part_a.id line.invalidate_recordset() assert line.x_fc_part_catalog_id == part_a, ( f'picker did not re-point part: {line.x_fc_part_catalog_id.revision}' ) assert line.x_fc_revision_snapshot == part_a.revision print(f'[OK] Revision picker: switched to {part_a.revision}') # ---- Confirm SO → job number auto-assigned --------------------------- so.action_confirm() line.invalidate_recordset() assert line.x_fc_job_number, 'job number should be auto-assigned on confirm' assert line.x_fc_job_number.startswith('FP-JOB-'), ( f'unexpected job number: {line.x_fc_job_number}' ) print(f'[OK] SO confirmed → job number: {line.x_fc_job_number}') # ---- MO carry-over (if bridge_mrp auto-created an MO) ----------------- MO = env['mrp.production'] mos = MO.search([('origin', '=', so.name)]) if mos: mo = mos[0] print(f'[OK] MO created ({mo.name}) → serial={mo.x_fc_serial_id.name if mo.x_fc_serial_id else "—"} / job={mo.x_fc_job_number or "—"} / thickness={mo.x_fc_thickness_id.display_name if mo.x_fc_thickness_id else "—"} / rev={mo.x_fc_revision_snapshot or "—"}') if mo.x_fc_serial_id: assert mo.x_fc_serial_id == line.x_fc_serial_id, 'MO serial mismatch' print('[OK] Serial carried MO ← SO line') else: print('[SKIP] No MO auto-created for this SO') # ---- fp.serial smart-button counts reflect reality ------------------- serial = line.x_fc_serial_id serial.invalidate_recordset() prod_cnt = serial.production_count del_cnt = serial.delivery_count print(f'[OK] Serial {serial.name}: MOs={prod_cnt}, Deliveries={del_cnt}, Invoices={serial.invoice_count}') # ---- Thickness dropdown domain ---------------------------------------- another_coating = Coating.create({ 'name': 'Sub5 Other Coating', 'process_type_id': coating.process_type_id.id, }) line.x_fc_coating_config_id = another_coating.id line._onchange_coating_clears_thickness() line.invalidate_recordset() assert not line.x_fc_thickness_id, 'thickness should clear when coating changes' print('[OK] Thickness clears when coating switches') # ---- Cleanup --------------------------------------------------------- env.cr.rollback() print('\n=== SUB 5 SMOKE PASS — all assertions held ===')