From 1560fa86104b2c4bfbed7ddf54072cda6dbaff02 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Fri, 27 Oct 2023 14:07:58 +0530 Subject: [PATCH] fix: ignore stale custom fields while checking back-links (#22940) * fix: ignore non-existing back-links * refactor: less indentation, guard clauses --- frappe/model/delete_doc.py | 64 +++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/frappe/model/delete_doc.py b/frappe/model/delete_doc.py index 150be95476..f67585540a 100644 --- a/frappe/model/delete_doc.py +++ b/frappe/model/delete_doc.py @@ -250,37 +250,45 @@ def check_if_doc_is_linked(doc, method="Delete"): for lf in link_fields: link_dt, link_field, issingle = lf["parent"], lf["fieldname"], lf["issingle"] - if not issingle: - fields = ["name", "docstatus"] - if frappe.get_meta(link_dt).istable: - fields.extend(["parent", "parenttype"]) + try: + meta = frappe.get_meta(link_dt) + except frappe.DoesNotExistError: + # This mostly happens when app do not remove their customizations, we shouldn't + # prevent link checks from failing in those cases + continue - for item in frappe.db.get_values(link_dt, {link_field: doc.name}, fields, as_dict=True): - # available only in child table cases - item_parent = getattr(item, "parent", None) - linked_doctype = item.parenttype if item_parent else link_dt - - if linked_doctype in frappe.get_hooks("ignore_links_on_delete") or ( - linked_doctype in ignore_linked_doctypes and method == "Cancel" - ): - # don't check for communication and todo! - continue - - if method != "Delete" and (method != "Cancel" or not DocStatus(item.docstatus).is_submitted()): - # don't raise exception if not - # linked to a non-cancelled doc when deleting or to a submitted doc when cancelling - continue - elif link_dt == doc.doctype and (item_parent or item.name) == doc.name: - # don't raise exception if not - # linked to same item or doc having same name as the item - continue - else: - reference_docname = item_parent or item.name - raise_link_exists_exception(doc, linked_doctype, reference_docname) - - else: + if issingle: if frappe.db.get_value(link_dt, None, link_field) == doc.name: raise_link_exists_exception(doc, link_dt, link_dt) + continue + + fields = ["name", "docstatus"] + + if meta.istable: + fields.extend(["parent", "parenttype"]) + + for item in frappe.db.get_values(link_dt, {link_field: doc.name}, fields, as_dict=True): + # available only in child table cases + item_parent = getattr(item, "parent", None) + linked_doctype = item.parenttype if item_parent else link_dt + + if linked_doctype in frappe.get_hooks("ignore_links_on_delete") or ( + linked_doctype in ignore_linked_doctypes and method == "Cancel" + ): + # don't check for communication and todo! + continue + + if method != "Delete" and (method != "Cancel" or not DocStatus(item.docstatus).is_submitted()): + # don't raise exception if not + # linked to a non-cancelled doc when deleting or to a submitted doc when cancelling + continue + elif link_dt == doc.doctype and (item_parent or item.name) == doc.name: + # don't raise exception if not + # linked to same item or doc having same name as the item + continue + else: + reference_docname = item_parent or item.name + raise_link_exists_exception(doc, linked_doctype, reference_docname) def check_if_doc_is_dynamically_linked(doc, method="Delete"):