# -*- coding: utf-8 -*- # Copyright 2026 Nexa Systems Inc. # License OPL-1 (Odoo Proprietary License v1.0) # # DESTRUCTIVE: deletes ALL fp.job, fp.job.step, fp.job.step.timelog, # mrp.production, mrp.workorder records and their dependent data # (deliveries, certs, thickness readings, holds, portal jobs, racking # inspections). Preserves masters (partners, parts, recipes, coating # configs, baths, tanks, work centres, users, groups, settings). # # Use only on demo/dev environments. Take a Proxmox snapshot first. def run(env): print('=== Cleanup starting ===') # Walk dependents bottom-up so FK cascades don't bite us. # 1. Time logs (cascades on step delete, but be explicit) n = env['fp.job.step.timelog'].search_count([]) env['fp.job.step.timelog'].sudo().search([]).unlink() print(' Deleted %d fp.job.step.timelog rows' % n) # 2. fp.job.node.override (cascades on job delete) n = env['fp.job.node.override'].search_count([]) env['fp.job.node.override'].sudo().search([]).unlink() print(' Deleted %d fp.job.node.override rows' % n) # 3. Deliveries linked to jobs OR with job_ref set OR linked to a SO that # we will delete. Delete ALL deliveries — they're test data. if 'fusion.plating.delivery' in env: deliveries = env['fusion.plating.delivery'].sudo().search([]) n = len(deliveries) deliveries.unlink() print(' Deleted %d fusion.plating.delivery rows' % n) # 4. Certificates linked to jobs/MOs if 'fp.certificate' in env: certs = env['fp.certificate'].sudo().search([]) n = len(certs) certs.unlink() print(' Deleted %d fp.certificate rows' % n) # 5. Thickness readings if 'fp.thickness.reading' in env: tr = env['fp.thickness.reading'].sudo().search([]) n = len(tr) tr.unlink() print(' Deleted %d fp.thickness.reading rows' % n) # 6. Quality holds linked to jobs/MOs if 'fusion.plating.quality.hold' in env: holds = env['fusion.plating.quality.hold'].sudo().search([]) n = len(holds) holds.unlink() print(' Deleted %d fusion.plating.quality.hold rows' % n) # 7. Portal jobs (linked to jobs OR legacy production) if 'fusion.plating.portal.job' in env: portals = env['fusion.plating.portal.job'].sudo().search([]) n = len(portals) portals.unlink() print(' Deleted %d fusion.plating.portal.job rows' % n) # 8. Racking inspections — required FK to mrp.production, so delete # BEFORE we kill the productions. if 'fp.racking.inspection' in env: insps = env['fp.racking.inspection'].sudo().search([]) n = len(insps) insps.unlink() print(' Deleted %d fp.racking.inspection rows' % n) # 9. Receiving records (required FK to sale.order — delete before SOs) if 'fp.receiving' in env: recs = env['fp.receiving'].sudo().search([]) n = len(recs) recs.unlink() print(' Deleted %d fp.receiving rows' % n) # 10. fp.job.step (cascade-safe via job_id, but be explicit) n = env['fp.job.step'].search_count([]) env['fp.job.step'].sudo().search([]).unlink() print(' Deleted %d fp.job.step rows' % n) # 11. fp.job n = env['fp.job'].search_count([]) env['fp.job'].sudo().search([]).unlink() print(' Deleted %d fp.job rows' % n) # 12. mrp.workorder (legacy) n = env['mrp.workorder'].search_count([]) env['mrp.workorder'].sudo().search([]).unlink() print(' Deleted %d mrp.workorder rows' % n) # 13. mrp.production (legacy) — force state via SQL so unlink() bypasses # Odoo's _unlink_except_done guard (which forbids deleting done MOs) # and the action_cancel guard (which forbids cancelling done MOs). # Demo data only. n = env['mrp.production'].search_count([]) if n: # 'cancel' state is the only state mrp.production._unlink_except_done # explicitly permits. env.cr.execute("UPDATE mrp_production SET state='cancel'") # Also clear stock moves' state so cascaded checks pass env.cr.execute( "UPDATE stock_move SET state='cancel' " "WHERE raw_material_production_id IN (SELECT id FROM mrp_production) " "OR production_id IN (SELECT id FROM mrp_production)" ) env.invalidate_all() env['mrp.production'].sudo().search([]).unlink() print(' Deleted %d mrp.production rows' % n) # 14. Test SOs — delete ALL non-invoiced sale orders. Force state to # 'cancel' via SQL because Odoo's _unlink_except_draft_or_cancel # guard otherwise blocks the unlink. Demo data only. so_ids = env['sale.order'].sudo().search([ ('invoice_ids', '=', False), ]).ids n = len(so_ids) if so_ids: env.cr.execute( "UPDATE sale_order SET state='cancel' WHERE id = ANY(%s)", (so_ids,), ) # Also cancel stock pickings the SOs may have created (forces # Odoo's cascade-aware unlink to pass) env.cr.execute( "UPDATE stock_picking SET state='cancel' " "WHERE sale_id = ANY(%s)", (so_ids,), ) env.invalidate_all() env['sale.order'].sudo().browse(so_ids).unlink() print(' Deleted %d sale.order rows (uninvoiced)' % n) # 15. Reset fp.job sequence so new ones start from JOB/00001 seq = env['ir.sequence'].sudo().search([('code', '=', 'fp.job')], limit=1) if seq: seq.number_next = 1 print(' Reset fp.job sequence to start at 1') env.cr.commit() print('=== Cleanup complete ===') try: run(env) except NameError: print('Run inside `odoo shell`.')