From 51823d1b8806709786d6fb9f9f85639968c65a94 Mon Sep 17 00:00:00 2001 From: naaa760 Date: Fri, 8 Aug 2025 14:12:36 +0530 Subject: [PATCH 1/4] Added input validation Added document existence checks Added reference field validation Changed None to empty strings for reference fields --- crm/api/doc.py | 134 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 98 insertions(+), 36 deletions(-) diff --git a/crm/api/doc.py b/crm/api/doc.py index afa2f2a6..50fabefe 100644 --- a/crm/api/doc.py +++ b/crm/api/doc.py @@ -751,7 +751,12 @@ def getCounts(d, doctype): @frappe.whitelist() def get_linked_docs_of_document(doctype, docname): - doc = frappe.get_doc(doctype, docname) + try: + doc = frappe.get_doc(doctype, docname) + except frappe.DoesNotExistError: + + return [] + linked_docs = get_linked_docs(doc) dynamic_linked_docs = get_dynamic_linked_docs(doc) @@ -760,7 +765,16 @@ def get_linked_docs_of_document(doctype, docname): docs_data = [] for doc in linked_docs: - data = frappe.get_doc(doc["reference_doctype"], doc["reference_docname"]) + + if not doc.get("reference_doctype") or not doc.get("reference_docname"): + continue + + try: + data = frappe.get_doc(doc["reference_doctype"], doc["reference_docname"]) + except (frappe.DoesNotExistError, frappe.ValidationError): + + continue + title = data.get("title") if data.doctype == "CRM Call Log": title = f"Call from {data.get('from')} to {data.get('to')}" @@ -780,25 +794,42 @@ def get_linked_docs_of_document(doctype, docname): def remove_doc_link(doctype, docname): - linked_doc_data = frappe.get_doc(doctype, docname) - linked_doc_data.update( - { - "reference_doctype": None, - "reference_docname": None, - } - ) - linked_doc_data.save(ignore_permissions=True) + + if not doctype or not docname: + return + + try: + linked_doc_data = frappe.get_doc(doctype, docname) + + linked_doc_data.update( + { + "reference_doctype": "", + "reference_docname": "", + } + ) + linked_doc_data.save(ignore_permissions=True) + except (frappe.DoesNotExistError, frappe.ValidationError): + + pass def remove_contact_link(doctype, docname): - linked_doc_data = frappe.get_doc(doctype, docname) - linked_doc_data.update( - { - "contact": None, - "contacts": [], - } - ) - linked_doc_data.save(ignore_permissions=True) + + if not doctype or not docname: + return + + try: + linked_doc_data = frappe.get_doc(doctype, docname) + linked_doc_data.update( + { + "contact": None, + "contacts": [], + } + ) + linked_doc_data.save(ignore_permissions=True) + except (frappe.DoesNotExistError, frappe.ValidationError): + + pass @frappe.whitelist() @@ -807,13 +838,21 @@ def remove_linked_doc_reference(items, remove_contact=None, delete=False): items = frappe.parse_json(items) for item in items: - if remove_contact: - remove_contact_link(item["doctype"], item["docname"]) - else: - remove_doc_link(item["doctype"], item["docname"]) + + if not item.get("doctype") or not item.get("docname"): + continue + + try: + if remove_contact: + remove_contact_link(item["doctype"], item["docname"]) + else: + remove_doc_link(item["doctype"], item["docname"]) - if delete: - frappe.delete_doc(item["doctype"], item["docname"]) + if delete: + frappe.delete_doc(item["doctype"], item["docname"]) + except (frappe.DoesNotExistError, frappe.ValidationError): + # Skip if document doesn't exist or has validation errors + continue return "success" @@ -822,20 +861,43 @@ def remove_linked_doc_reference(items, remove_contact=None, delete=False): def delete_bulk_docs(doctype, items, delete_linked=False): from frappe.desk.reportview import delete_bulk + + if not doctype: + frappe.throw("Doctype is required") + + if not items: + frappe.throw("Items are required") + items = frappe.parse_json(items) + if not isinstance(items, list): + frappe.throw("Items must be a list") + for doc in items: - linked_docs = get_linked_docs_of_document(doctype, doc) - for linked_doc in linked_docs: - remove_linked_doc_reference( - [ - { - "doctype": linked_doc["reference_doctype"], - "docname": linked_doc["reference_docname"], - } - ], - remove_contact=doctype == "Contact", - delete=delete_linked, - ) + try: + + if not frappe.db.exists(doctype, doc): + frappe.log_error(f"Document {doctype} {doc} does not exist", "Bulk Delete Error") + continue + + linked_docs = get_linked_docs_of_document(doctype, doc) + for linked_doc in linked_docs: + + if not linked_doc.get("reference_doctype") or not linked_doc.get("reference_docname"): + continue + + remove_linked_doc_reference( + [ + { + "doctype": linked_doc["reference_doctype"], + "docname": linked_doc["reference_docname"], + } + ], + remove_contact=doctype == "Contact", + delete=delete_linked, + ) + except Exception as e: + + frappe.log_error(f"Error processing linked docs for {doctype} {doc}: {str(e)}", "Bulk Delete Error") if len(items) > 10: frappe.enqueue("frappe.desk.reportview.delete_bulk", doctype=doctype, items=items) From 3d6111627b314f2d99420985cfcdabe9ad5fe643 Mon Sep 17 00:00:00 2001 From: Pratik Badhe Date: Wed, 3 Sep 2025 05:24:45 +0000 Subject: [PATCH 2/4] fix: handle notification deletion --- crm/api/doc.py | 67 +- .../components/BulkDeleteLinkedDocModal.vue | 4 +- .../src/components/DeleteLinkedDocModal.vue | 5 +- .../ListViews/LinkedDocsListView.vue | 17 +- yarn.lock | 2277 ++++++++++++++++- 5 files changed, 2252 insertions(+), 118 deletions(-) diff --git a/crm/api/doc.py b/crm/api/doc.py index 50fabefe..8ab52b0b 100644 --- a/crm/api/doc.py +++ b/crm/api/doc.py @@ -754,9 +754,8 @@ def get_linked_docs_of_document(doctype, docname): try: doc = frappe.get_doc(doctype, docname) except frappe.DoesNotExistError: - return [] - + linked_docs = get_linked_docs(doc) dynamic_linked_docs = get_dynamic_linked_docs(doc) @@ -765,16 +764,14 @@ def get_linked_docs_of_document(doctype, docname): docs_data = [] for doc in linked_docs: - if not doc.get("reference_doctype") or not doc.get("reference_docname"): continue - + try: data = frappe.get_doc(doc["reference_doctype"], doc["reference_docname"]) except (frappe.DoesNotExistError, frappe.ValidationError): - continue - + title = data.get("title") if data.doctype == "CRM Call Log": title = f"Call from {data.get('from')} to {data.get('to')}" @@ -782,6 +779,9 @@ def get_linked_docs_of_document(doctype, docname): if data.doctype == "CRM Deal": title = data.get("organization") + if data.doctype == "CRM Notification": + title = data.get("message") + docs_data.append( { "doc": data.doctype, @@ -794,30 +794,41 @@ def get_linked_docs_of_document(doctype, docname): def remove_doc_link(doctype, docname): - if not doctype or not docname: return - + try: linked_doc_data = frappe.get_doc(doctype, docname) - - linked_doc_data.update( - { - "reference_doctype": "", - "reference_docname": "", + if doctype == "CRM Notification": + delete_notification_type = { + "notification_type_doctype": "", + "notification_type_doc": "", } - ) + delete_references = { + "reference_doctype": "", + "reference_name": "", + } + + if linked_doc_data.get("notification_type_doctype") == linked_doc_data.get("reference_doctype"): + delete_references.update(delete_notification_type) + + linked_doc_data.update(delete_references) + else: + linked_doc_data.update( + { + "reference_doctype": "", + "reference_docname": "", + } + ) linked_doc_data.save(ignore_permissions=True) except (frappe.DoesNotExistError, frappe.ValidationError): - pass def remove_contact_link(doctype, docname): - if not doctype or not docname: return - + try: linked_doc_data = frappe.get_doc(doctype, docname) linked_doc_data.update( @@ -828,7 +839,6 @@ def remove_contact_link(doctype, docname): ) linked_doc_data.save(ignore_permissions=True) except (frappe.DoesNotExistError, frappe.ValidationError): - pass @@ -838,10 +848,9 @@ def remove_linked_doc_reference(items, remove_contact=None, delete=False): items = frappe.parse_json(items) for item in items: - if not item.get("doctype") or not item.get("docname"): continue - + try: if remove_contact: remove_contact_link(item["doctype"], item["docname"]) @@ -861,30 +870,27 @@ def remove_linked_doc_reference(items, remove_contact=None, delete=False): def delete_bulk_docs(doctype, items, delete_linked=False): from frappe.desk.reportview import delete_bulk - if not doctype: frappe.throw("Doctype is required") - + if not items: frappe.throw("Items are required") - + items = frappe.parse_json(items) if not isinstance(items, list): frappe.throw("Items must be a list") - + for doc in items: try: - if not frappe.db.exists(doctype, doc): frappe.log_error(f"Document {doctype} {doc} does not exist", "Bulk Delete Error") continue - + linked_docs = get_linked_docs_of_document(doctype, doc) for linked_doc in linked_docs: - if not linked_doc.get("reference_doctype") or not linked_doc.get("reference_docname"): continue - + remove_linked_doc_reference( [ { @@ -896,8 +902,9 @@ def delete_bulk_docs(doctype, items, delete_linked=False): delete=delete_linked, ) except Exception as e: - - frappe.log_error(f"Error processing linked docs for {doctype} {doc}: {str(e)}", "Bulk Delete Error") + frappe.log_error( + f"Error processing linked docs for {doctype} {doc}: {str(e)}", "Bulk Delete Error" + ) if len(items) > 10: frappe.enqueue("frappe.desk.reportview.delete_bulk", doctype=doctype, items=items) diff --git a/frontend/src/components/BulkDeleteLinkedDocModal.vue b/frontend/src/components/BulkDeleteLinkedDocModal.vue index b27273b5..26b19323 100644 --- a/frontend/src/components/BulkDeleteLinkedDocModal.vue +++ b/frontend/src/components/BulkDeleteLinkedDocModal.vue @@ -13,7 +13,7 @@
-
+
{{ __('Are you sure you want to delete {0} items?', [ props.items?.length, @@ -53,7 +53,7 @@
-
+
{{ confirmDeleteInfo.delete ? __( diff --git a/frontend/src/components/DeleteLinkedDocModal.vue b/frontend/src/components/DeleteLinkedDocModal.vue index 64a24128..1ca3606f 100644 --- a/frontend/src/components/DeleteLinkedDocModal.vue +++ b/frontend/src/components/DeleteLinkedDocModal.vue @@ -2,7 +2,7 @@