#!/usr/bin/env python import odoorpc import json import os import multiprocessing as mp import ast # Odoo connection setup odoo = odoorpc.ODOO("workspace.local", port=8069) print("Available databases:", odoo.db.list()) db_name = "enterprise-ambio" base_model = "res.partner" # Change this to any model (e.g., "res.partner") domain = "[('employee', '=', False), ('customer_rank', '>', 0)]" process_size = 30 odoo.login(db_name, "admin", "admin") def get_related_fields(db_name, base_model): """Fetch related fields for a given model, using a cached file to reduce API calls.""" cache_file = f"{db_name}-{base_model}.cache.json" # Load from cache if exists if os.path.exists(cache_file): with open(cache_file, "r") as f: related_models = json.load(f) print(f"Loaded related models from cache ({cache_file}).") return related_models print(f"Fetching related models for {base_model} from Odoo...") related_models = {} # Get all models all_model_ids = odoo.env["ir.model"].search([]) all_models = odoo.env["ir.model"].read(all_model_ids, ["model"]) for model in all_models: model_name = model["model"] try: fields = odoo.env[model_name].fields_get() # Get field metadata for field_name, field_info in fields.items(): if ( field_info.get("type") in ["many2one", "many2many", "one2many"] and field_info.get("relation") == base_model ): related_models.setdefault(model_name, []).append(field_name) except Exception as e: print(f"Skipping model {model_name}: {e}") # Save to cache with open(cache_file, "w") as f: json.dump(related_models, f, indent=4) print(f"Saved related models to cache ({cache_file}).") return related_models def split_array(array, size): """Split a list into chunks of specified size.""" return [array[i : i + size] for i in range(0, len(array), size)] def process_batch(batch, model_name, process_count, related_models): """Process a batch of records - archive or delete based on references.""" model = odoo.env[model_name] archived_count = 0 deleted_count = 0 skipped_count = 0 for record_id in batch: is_referenced = False # Check references in related models for related_model, fields in related_models.items(): for field in fields: if odoo.env[related_model].search_count([(field, "=", record_id)]) > 0: is_referenced = True break if is_referenced: break # Exit loop early if reference is found try: if is_referenced: model.browse(record_id).write({"active": False}) # Archive archived_count += 1 print(f"{process_count}: Archived {model_name} ID {record_id}") else: model.unlink([record_id]) # Delete if no references deleted_count += 1 print(f"{process_count}: Deleted {model_name} ID {record_id}") except Exception as e: skipped_count += 1 print(f"{process_count}: Skipped {model_name} ID {record_id}: {e}") print( f"{process_count}: {model_name} - Deleted: {deleted_count}, Archived: {archived_count}, Skipped: {skipped_count}." ) def main(): """Main function to fetch records and process them in parallel.""" model_name = base_model model = odoo.env[model_name] domain_filter = ast.literal_eval(domain) if domain else [] record_ids = model.search(domain_filter) related_models = get_related_fields(db_name, model_name) # Split records into batches batch_list = split_array(record_ids, process_size) processes = [] process_count = 0 for batch in batch_list: process_count += 1 process = mp.Process( name=f"Process-{process_count}", target=process_batch, args=(batch, model_name, process_count, related_models), ) print(f"Process-{process_count} Created") process.start() print(f"Process-{process_count} Started") processes.append(process) # Ensure all processes complete for process in processes: process.join() if __name__ == "__main__": main()