update
Some checks are pending
Setup Native Action / native (3.12.7) (push) Waiting to run
Setup Native Action / docker (3.12.7) (push) Waiting to run

This commit is contained in:
KaySar12 2025-03-20 10:58:08 +07:00
parent e0c0e834fe
commit 4692b037a0

View File

@ -67,6 +67,16 @@ parser.add_argument(
action="store_true",
help="Force delete cascade all records instead of archiving",
)
parser.add_argument(
"--soft",
action="store_true",
help="Just archive the records instead of deleting",
)
parser.add_argument(
"--refresh-cache",
action="store_true",
help="Refersh the related models cache",
)
args = parser.parse_args()
db_name = args.db_name
@ -85,18 +95,24 @@ color_log.Show(INFO, ("Available databases:", odoo.db.list()))
try:
odoo.login(db_name, USERNAME, PASSWORD)
color_log.Show(
0,
f"Connected to Odoo at {HOST}:{PORT}, Database: {db_name}, Model: {base_model}",
OK, f"Connected to {HOST}:{PORT}, DB: {db_name}, Model: {base_model}"
)
except odoorpc.error.RPCError as e:
color_log.Show(FAIL, f"Login failed: {e} (check credentials or database)")
exit(1)
except Exception as e:
color_log.Show(FAIL, f"Fail to Connect to Odoo Server {e}")
color_log.Show(FAIL, f"Connection error: {e} (check host/port)")
exit(1)
# Convert DOMAIN from string to list
try:
domain_filter = ast.literal_eval(domain_str)
except Exception as e:
color_log.Show(FAIL, f"Invalid DOMAIN format: {e}")
if not isinstance(domain_filter, list) or not all(
isinstance(t, (tuple, list)) and len(t) == 3 for t in domain_filter
):
raise ValueError("Domain must be a list of 3-tuples")
except (ValueError, SyntaxError) as e:
color_log.Show(FAIL, f"Invalid domain format: {e}")
exit(1)
@ -136,6 +152,7 @@ def get_related_fields(db_name, base_model):
if os.path.exists(cache_file):
with open(cache_file, "r") as f:
related_models = json.load(f)
color_log.Show(INFO, f"Loaded related models for {base_model} from cache.")
return related_models
color_log.Show(INFO, f"Fetching related models for {base_model} from Odoo...")
@ -145,7 +162,6 @@ def get_related_fields(db_name, base_model):
all_model_ids = odoo.env["ir.model"].search([])
all_models = odoo.env["ir.model"].read(all_model_ids, ["model"])
model_names = [model["model"] for model in all_models]
# Use multiprocessing to fetch related fields
with mp.Pool(processes=process_size) as pool:
results = pool.starmap(
@ -169,6 +185,7 @@ def get_related_fields(db_name, base_model):
return {}
# Experimental function
def delete_records_cascade(
record_ids, model_name, process_count, related_models, db_name
):
@ -211,7 +228,9 @@ def delete_records_cascade(
)
process_count += 1
except odoorpc.error.RPCError as e:
color_log.Show(NOTICE, f"Access denied for model {related_model}: {e}")
color_log.Show(
NOTICE, f"Access denied for model {related_model}: {e}"
)
skipped_models_cache.add(related_model) # Add to cache
break # Skip further processing for this model
except Exception as e:
@ -244,6 +263,33 @@ def delete_records_cascade(
color_log.Show(FAIL, f"Error deleting {model_name} records: {e}")
def unreference_records(record_id, model_name, process_count, related_models):
"""Force delete a record and its related records."""
for related_model, fields in related_models.items():
related_model_obj = odoo.env[related_model]
for field in fields:
try:
# Find records in the related model that reference the record_id
related_ids = related_model_obj.search([(field, "=", record_id)])
if related_ids:
# Unreference the record_id by setting the field to False
related_model_obj.browse(related_ids).write({field: False})
color_log.Show(
OK,
f"Unreferenced {record_id} in {related_model} ({field}) for {len(related_ids)} records.",
)
except odoorpc.error.RPCError as e:
color_log.Show(
NOTICE,
f"Access denied while unreferencing {record_id} in {related_model} ({field}): {e}",
)
except Exception as e:
color_log.Show(
NOTICE,
f"Error while unreferencing {record_id} in {related_model} ({field}): {e}",
)
# Function to delete records in parallel
def delete_record(batch, model_name, process_count, related_models):
"""Process a batch of records - archive or delete based on references."""
@ -266,17 +312,35 @@ def delete_record(batch, model_name, process_count, related_models):
color_log.Show(
INFO, f"{process_count}: Archived {model_name} ID {record_id}"
)
else:
if (not is_referenced and not args.soft) or args.force:
model.unlink([record_id])
deleted_count += 1
color_log.Show(
INFO, f"{process_count}: Deleted {model_name} ID {record_id}"
)
except Exception as e:
skipped_count += 1
color_log.Show(
INFO, f"{process_count}: Skipped {model_name} ID {record_id}: {e}"
)
if args.force:
try:
unreference_records(
record_id, model_name, process_count, related_models
)
model.unlink([record_id])
archived_count += 1
color_log.Show(
INFO,
f"{process_count}: Force deleted {model_name} ID {record_id}",
)
except Exception as e:
skipped_count += 1
color_log.Show(
NOTICE,
f"{process_count}: Skipped {model_name} ID {record_id} after force delete attempt: {e}",
)
else:
skipped_count += 1
color_log.Show(
INFO, f"{process_count}: Skipped {model_name} ID {record_id}: {e}"
)
color_log.Show(
OK,
@ -304,29 +368,16 @@ def main():
for i in range(0, len(record_ids), process_size)
]
processes = []
if args.force:
delete_records_cascade(record_ids, base_model, 0, related_models, db_name)
# for i, batch in enumerate(batch_list, start=1):
# process = mp.Process(
# target=delete_records_cascade,
# args=(batch, base_model, f"Process-{i}", related_models,db_name),
# )
# processes.append(process)
# process.start()
# for process in processes:
# process.join()
else:
for i, batch in enumerate(batch_list, start=1):
process = mp.Process(
target=delete_record,
args=(batch, base_model, f"Process-{i}", related_models),
)
processes.append(process)
process.start()
for process in processes:
process.join()
for i, batch in enumerate(batch_list, start=1):
# delete_record(batch, base_model, f"Process-{i}", related_models)
process = mp.Process(
target=delete_record,
args=(batch, base_model, f"Process-{i}", related_models),
)
processes.append(process)
process.start()
for process in processes:
process.join()
color_log.Show(OK, "Record deletion process completed.")