import sys import os import time from datetime import datetime, timedelta sys.path.append(os.getcwd()) # Try to import odoo, if not found, assume we are in odoo-bin shell or need path try: import odoo from odoo import api, SUPERUSER_ID from odoo.tools import config except ImportError as e: # Check if due to missing dependency like passlib if "No module named" in str(e): print("!" * 80) print(f"ERROR: Missing dependency: {e}") print("Please run this script using the Python executable that runs Odoo.") print("Example: /path/to/venv/bin/python delete_old_mo_qc.py") print("!" * 80) sys.exit(1) current_dir = os.path.dirname(os.path.abspath(__file__)) # Try adding current_dir/odoo to path if not already covered by os.getcwd() fix above # But usually os.getcwd() should cover it if running from community/ folder. if os.path.exists(os.path.join(current_dir, 'odoo')): sys.path.append(os.path.join(current_dir, 'odoo')) else: sys.path.append(current_dir) import odoo from odoo import api, SUPERUSER_ID from odoo.tools import config def delete_old_records(): print("Starting Delete Old MO/QC Script...") import argparse parser = argparse.ArgumentParser(description='Delete MOs and Quality Checks older than 1 day.') parser.add_argument('db_name', help='Database name') parser.add_argument('-c', '--config', help='Path to odoo.conf', default=None) args = parser.parse_args() db_name = args.db_name # Load config if provided or default exists conf_path = args.config if conf_path: if os.path.exists(conf_path): config.parse_config(['-c', conf_path]) else: print(f"WARNING: Config file '{conf_path}' not found.") elif os.path.exists('odoo.conf'): config.parse_config(['-c', 'odoo.conf']) print(f"Connecting to database: {db_name}") try: registry = odoo.registry(db_name) except AttributeError: from odoo.modules.registry import Registry registry = Registry.new(db_name) with registry.cursor() as cr: env = api.Environment(cr, SUPERUSER_ID, {}) # Calculate Cutoff Date cutoff_date = datetime.now() - timedelta(days=1) print(f"Cutoff Date: {cutoff_date}") # 1. Find Old Quality Checks (NOT passed/failed) print("\nSearching for old Quality Checks...") # quality_state can be 'none', 'pass', 'fail' domain_qc = [('create_date', '<', cutoff_date), ('quality_state', '=', 'none')] qcs = env['quality.check'].search(domain_qc) print(f"Found {len(qcs)} Quality Checks strictly older than 1 day (and not done).") # 2. Find Old Manufacturing Orders (NOT done) print("\nSearching for old Manufacturing Orders...") # state can be 'draft', 'confirmed', 'progress', 'to_close', 'done', 'cancel' # We delete anything NOT done (and maybe not cancel if user wants clean up, but user said 'skip done') # Safer to skip 'cancel' too if we want pure cleanup of abandoned work? User said "skip done". # But 'cancel' is safe to delete usually. Let's stick to != 'done'. start_domain_mo = [('create_date', '<', cutoff_date), ('state', '!=', 'done')] mos = env['mrp.production'].search(start_domain_mo) print(f"Found {len(mos)} Manufacturing Orders strictly older than 1 day (and not done).") if not qcs and not mos: print("\nNothing to delete.") return print("\n---------------------------------------------------") print("Dry run complete. No changes have been committed yet.") print("WARNING: This will PERMANENTLY DELETE the following records:") print(f" - {len(qcs)} Quality Checks") print(f" - {len(mos)} Manufacturing Orders") user_input = input("Do you want to PROCEED with DELETION and COMMIT changes? (yes/no): ") if user_input.lower() == 'yes': try: if qcs: print(f"Deleting {len(qcs)} Quality Checks...") qcs.unlink() print(" Success.") if mos: print(f"Deleting {len(mos)} Manufacturing Orders...") # MO deletions might be blocked if state is 'done'. # We attempt unlink, if it fails, Odoo raises UserError. mos.unlink() print(" Success.") cr.commit() print("Changes COMMITTED.") except Exception as e: cr.rollback() print(f"ERROR during deletion: {e}") print("Transaction ROLLED BACK. No changes persisted.") else: cr.rollback() print("Operation CANCELLED (Rolled back).") print("Done.") if __name__ == '__main__': delete_old_records()