From 07c98146c5cb2fe25363435109c97a0923a6b273 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sat, 3 Feb 2024 11:58:11 +0530 Subject: [PATCH 01/30] refactor: more options for 'status' and move it to top (cherry picked from commit 0d65d878deb717b80a22cae4d152d07289acae58) # Conflicts: # erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py --- .../transaction_deletion_record.json | 22 ++++++++++++++----- .../transaction_deletion_record.py | 22 +++++++++++++++++++ 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json index 23e59472a6d7..8f3a5d056662 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json @@ -7,10 +7,12 @@ "engine": "InnoDB", "field_order": [ "company", + "column_break_txbg", + "status", + "section_break_tbej", "doctypes", "doctypes_to_be_ignored", - "amended_from", - "status" + "amended_from" ], "fields": [ { @@ -46,18 +48,27 @@ { "fieldname": "status", "fieldtype": "Select", - "hidden": 1, "label": "Status", - "options": "Draft\nCompleted" + "options": "Queued\nRunning\nFailed\nCompleted\nCancelled", + "read_only": 1 + }, + { + "fieldname": "column_break_txbg", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_tbej", + "fieldtype": "Section Break" } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-08-04 20:15:59.071493", + "modified": "2024-02-03 12:42:21.628177", "modified_by": "Administrator", "module": "Setup", "name": "Transaction Deletion Record", + "naming_rule": "Expression (old style)", "owner": "Administrator", "permissions": [ { @@ -76,5 +87,6 @@ ], "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py index 649b43b5e914..7f6e72db725f 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py @@ -10,6 +10,28 @@ class TransactionDeletionRecord(Document): +<<<<<<< HEAD +======= + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + from erpnext.setup.doctype.transaction_deletion_record_item.transaction_deletion_record_item import ( + TransactionDeletionRecordItem, + ) + + amended_from: DF.Link | None + company: DF.Link + doctypes: DF.Table[TransactionDeletionRecordItem] + doctypes_to_be_ignored: DF.Table[TransactionDeletionRecordItem] + status: DF.Literal["Queued", "Running", "Completed"] + # end: auto-generated types + +>>>>>>> 0d65d878de (refactor: more options for 'status' and move it to top) def __init__(self, *args, **kwargs): super(TransactionDeletionRecord, self).__init__(*args, **kwargs) self.batch_size = 5000 From 7280a76f73f28c494522596783b409b3d721b69b Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sat, 3 Feb 2024 12:46:12 +0530 Subject: [PATCH 02/30] refactor: set status and trigger job on submit (cherry picked from commit 6fbb67b1d2da1fe9aa1879ce354f5702150584c8) --- .../transaction_deletion_record.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py index 7f6e72db725f..cd7cd3ab3387 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py @@ -28,7 +28,7 @@ class TransactionDeletionRecord(Document): company: DF.Link doctypes: DF.Table[TransactionDeletionRecordItem] doctypes_to_be_ignored: DF.Table[TransactionDeletionRecordItem] - status: DF.Literal["Queued", "Running", "Completed"] + status: DF.Literal["Queued", "Running", "Failed", "Completed", "Cancelled"] # end: auto-generated types >>>>>>> 0d65d878de (refactor: more options for 'status' and move it to top) @@ -55,6 +55,16 @@ def before_submit(self): if not self.doctypes_to_be_ignored: self.populate_doctypes_to_be_ignored_table() + def before_save(self): + self.status = "" + + def on_submit(self): + self.db_set("status", "Queued") + + def on_cancel(self): + self.db_set("status", "Cancelled") + + def start_deletion_process(self): self.delete_bins() self.delete_lead_addresses() self.reset_company_values() From 52d22d8b557024b43f49b24ce299d0aa68aac7df Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sat, 3 Feb 2024 14:29:04 +0530 Subject: [PATCH 03/30] refactor: tasks section and UI niceties (cherry picked from commit d0dc2c6e77c4e04e8d74a36a632ad6f2189dfedc) --- .../transaction_deletion_record.json | 53 ++++++++++++++++++- .../transaction_deletion_record.py | 19 +++++++ 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json index 8f3a5d056662..a9e04d3892e6 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json @@ -9,6 +9,12 @@ "company", "column_break_txbg", "status", + "tasks_section", + "delete_bin_data", + "delete_leads_and_addresses", + "reset_company_default_values", + "clear_notifications", + "delete_transactions", "section_break_tbej", "doctypes", "doctypes_to_be_ignored", @@ -59,12 +65,57 @@ { "fieldname": "section_break_tbej", "fieldtype": "Section Break" + }, + { + "fieldname": "tasks_section", + "fieldtype": "Section Break", + "label": "Tasks" + }, + { + "default": "0", + "fieldname": "delete_bin_data", + "fieldtype": "Check", + "label": "Delete Bins", + "no_copy": 1, + "read_only": 1 + }, + { + "default": "0", + "fieldname": "delete_leads_and_addresses", + "fieldtype": "Check", + "label": "Delete Leads and Addresses", + "no_copy": 1, + "read_only": 1 + }, + { + "default": "0", + "fieldname": "clear_notifications", + "fieldtype": "Check", + "label": "Clear Notifications", + "no_copy": 1, + "read_only": 1 + }, + { + "default": "0", + "fieldname": "reset_company_default_values", + "fieldtype": "Check", + "label": "Reset Company Default Values", + "no_copy": 1, + "read_only": 1 + }, + { + "default": "0", + "fieldname": "delete_transactions", + "fieldtype": "Check", + "label": "Delete Transactions", + "no_copy": 1, + "read_only": 1 } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2024-02-03 12:42:21.628177", + "modified": "2024-02-03 14:40:40.207482", "modified_by": "Administrator", "module": "Setup", "name": "Transaction Deletion Record", diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py index cd7cd3ab3387..c4015f262f51 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py @@ -25,9 +25,14 @@ class TransactionDeletionRecord(Document): ) amended_from: DF.Link | None + clear_notifications: DF.Check company: DF.Link + delete_bin_data: DF.Check + delete_leads_and_addresses: DF.Check + delete_transactions: DF.Check doctypes: DF.Table[TransactionDeletionRecordItem] doctypes_to_be_ignored: DF.Table[TransactionDeletionRecordItem] + reset_company_default_values: DF.Check status: DF.Literal["Queued", "Running", "Failed", "Completed", "Cancelled"] # end: auto-generated types @@ -55,8 +60,16 @@ def before_submit(self): if not self.doctypes_to_be_ignored: self.populate_doctypes_to_be_ignored_table() + def reset_task_flags(self): + self.clear_notifications = 0 + self.delete_bin_data = 0 + self.delete_leads_and_addresses = 0 + self.delete_transactions = 0 + self.reset_company_default_values = 0 + def before_save(self): self.status = "" + self.reset_task_flags() def on_submit(self): self.db_set("status", "Queued") @@ -64,11 +77,13 @@ def on_submit(self): def on_cancel(self): self.db_set("status", "Cancelled") + @frappe.whitelist() def start_deletion_process(self): self.delete_bins() self.delete_lead_addresses() self.reset_company_values() clear_notifications() + self.db_set("clear_notifications", 1) self.delete_company_transactions() def populate_doctypes_to_be_ignored_table(self): @@ -82,6 +97,7 @@ def delete_bins(self): (select name from tabWarehouse where company=%s)""", self.company, ) + self.db_set("delete_bin_data", 1) def delete_lead_addresses(self): """Delete addresses to which leads are linked""" @@ -120,12 +136,14 @@ def delete_lead_addresses(self): leads=",".join(leads) ) ) + self.db_set("delete_leads_and_addresses", 1) def reset_company_values(self): company_obj = frappe.get_doc("Company", self.company) company_obj.total_monthly_sales = 0 company_obj.sales_monthly_history = None company_obj.save() + self.db_set("reset_company_default_values", 1) def delete_company_transactions(self): doctypes_to_be_ignored_list = self.get_doctypes_to_be_ignored_list() @@ -159,6 +177,7 @@ def delete_company_transactions(self): if naming_series: if "#" in naming_series: self.update_naming_series(naming_series, docfield["parent"]) + self.db_set("delete_transactions", 1) def get_doctypes_to_be_ignored_list(self): singles = frappe.get_all("DocType", filters={"issingle": 1}, pluck="name") From 5542985e251e56e0a746bbe8dd850acb07fb66d4 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sat, 3 Feb 2024 14:49:18 +0530 Subject: [PATCH 04/30] refactor: UI trigger (cherry picked from commit 8944ab8b6ad1da2acdd5a852a17fc72e11d84695) --- .../transaction_deletion_record.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js index 527c753d6a97..671f927106d6 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js @@ -24,6 +24,17 @@ frappe.ui.form.on("Transaction Deletion Record", { refresh: function (frm) { frm.fields_dict["doctypes_to_be_ignored"].grid.set_column_disp("no_of_docs", false); frm.refresh_field("doctypes_to_be_ignored"); + + if (frm.doc.docstatus==1 && ['Queued', 'Failed'].find(x => x == frm.doc.status)) { + let execute_btn = __("Start / Resume") + + frm.add_custom_button(execute_btn, () => { + frm.call({ + method: 'start_deletion_process', + doc: frm.doc + }); + }); + } }, }); From c9d77044b0c0a13379b3b38c298287fca377b57e Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sat, 3 Feb 2024 20:21:30 +0530 Subject: [PATCH 05/30] refactor: use flags to decide on current stage (cherry picked from commit 6a77d86a53580b670937b92bcbde69ea920dbb9e) # Conflicts: # erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.py --- .../transaction_deletion_record.js | 12 +- .../transaction_deletion_record.json | 10 +- .../transaction_deletion_record.py | 196 ++++++++++-------- .../transaction_deletion_record_item.json | 19 +- .../transaction_deletion_record_item.py | 20 ++ 5 files changed, 159 insertions(+), 98 deletions(-) diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js index 671f927106d6..1a8b52f46bd5 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js @@ -16,9 +16,15 @@ frappe.ui.form.on("Transaction Deletion Record", { }); } - frm.get_field("doctypes_to_be_ignored").grid.cannot_add_rows = true; - frm.fields_dict["doctypes_to_be_ignored"].grid.set_column_disp("no_of_docs", false); - frm.refresh_field("doctypes_to_be_ignored"); + + frm.get_field('doctypes_to_be_ignored').grid.cannot_add_rows = true; + frm.fields_dict['doctypes_to_be_ignored'].grid.set_column_disp('no_of_docs', false); + frm.fields_dict['doctypes_to_be_ignored'].grid.set_column_disp('done', false); + frm.refresh_field('doctypes_to_be_ignored'); + + frm.get_field('doctypes').grid.cannot_add_rows = true; + frm.fields_dict['doctypes'].grid.set_column_disp('no_of_docs', true); + frm.refresh_field('doctypes'); }, refresh: function (frm) { diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json index a9e04d3892e6..6a848413ffc6 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json @@ -15,6 +15,7 @@ "reset_company_default_values", "clear_notifications", "delete_transactions", + "initialize_doctypes_table", "section_break_tbej", "doctypes", "doctypes_to_be_ignored", @@ -110,12 +111,19 @@ "label": "Delete Transactions", "no_copy": 1, "read_only": 1 + }, + { + "default": "0", + "fieldname": "initialize_doctypes_table", + "fieldtype": "Check", + "label": "Initialize Summary Table", + "read_only": 1 } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2024-02-03 14:40:40.207482", + "modified": "2024-02-03 20:48:34.107577", "modified_by": "Administrator", "module": "Setup", "name": "Transaction Deletion Record", diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py index c4015f262f51..55837ac385c3 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py @@ -32,6 +32,7 @@ class TransactionDeletionRecord(Document): delete_transactions: DF.Check doctypes: DF.Table[TransactionDeletionRecordItem] doctypes_to_be_ignored: DF.Table[TransactionDeletionRecordItem] + initialize_doctypes_table: DF.Check reset_company_default_values: DF.Check status: DF.Literal["Queued", "Running", "Failed", "Completed", "Cancelled"] # end: auto-generated types @@ -82,8 +83,10 @@ def start_deletion_process(self): self.delete_bins() self.delete_lead_addresses() self.reset_company_values() - clear_notifications() - self.db_set("clear_notifications", 1) + if not self.clear_notifications: + clear_notifications() + self.db_set("clear_notifications", 1) + self.initialize_doctypes_to_be_deleted_table() self.delete_company_transactions() def populate_doctypes_to_be_ignored_table(self): @@ -92,92 +95,108 @@ def populate_doctypes_to_be_ignored_table(self): self.append("doctypes_to_be_ignored", {"doctype_name": doctype}) def delete_bins(self): - frappe.db.sql( - """delete from `tabBin` where warehouse in - (select name from tabWarehouse where company=%s)""", - self.company, - ) - self.db_set("delete_bin_data", 1) + if not self.delete_bin_data: + frappe.db.sql( + """delete from `tabBin` where warehouse in + (select name from tabWarehouse where company=%s)""", + self.company, + ) + self.db_set("delete_bin_data", 1) def delete_lead_addresses(self): """Delete addresses to which leads are linked""" - leads = frappe.get_all("Lead", filters={"company": self.company}) - leads = ["'%s'" % row.get("name") for row in leads] - addresses = [] - if leads: - addresses = frappe.db.sql_list( - """select parent from `tabDynamic Link` where link_name - in ({leads})""".format( - leads=",".join(leads) + if not self.delete_leads_and_addresses: + leads = frappe.get_all("Lead", filters={"company": self.company}) + leads = ["'%s'" % row.get("name") for row in leads] + addresses = [] + if leads: + addresses = frappe.db.sql_list( + """select parent from `tabDynamic Link` where link_name + in ({leads})""".format( + leads=",".join(leads) + ) ) - ) - if addresses: - addresses = ["%s" % frappe.db.escape(addr) for addr in addresses] + if addresses: + addresses = ["%s" % frappe.db.escape(addr) for addr in addresses] - frappe.db.sql( - """delete from `tabAddress` where name in ({addresses}) and - name not in (select distinct dl1.parent from `tabDynamic Link` dl1 - inner join `tabDynamic Link` dl2 on dl1.parent=dl2.parent - and dl1.link_doctype<>dl2.link_doctype)""".format( - addresses=",".join(addresses) + frappe.db.sql( + """delete from `tabAddress` where name in ({addresses}) and + name not in (select distinct dl1.parent from `tabDynamic Link` dl1 + inner join `tabDynamic Link` dl2 on dl1.parent=dl2.parent + and dl1.link_doctype<>dl2.link_doctype)""".format( + addresses=",".join(addresses) + ) + ) + + frappe.db.sql( + """delete from `tabDynamic Link` where link_doctype='Lead' + and parenttype='Address' and link_name in ({leads})""".format( + leads=",".join(leads) + ) ) - ) frappe.db.sql( - """delete from `tabDynamic Link` where link_doctype='Lead' - and parenttype='Address' and link_name in ({leads})""".format( + """update `tabCustomer` set lead_name=NULL where lead_name in ({leads})""".format( leads=",".join(leads) ) ) - - frappe.db.sql( - """update `tabCustomer` set lead_name=NULL where lead_name in ({leads})""".format( - leads=",".join(leads) - ) - ) - self.db_set("delete_leads_and_addresses", 1) + self.db_set("delete_leads_and_addresses", 1) def reset_company_values(self): - company_obj = frappe.get_doc("Company", self.company) - company_obj.total_monthly_sales = 0 - company_obj.sales_monthly_history = None - company_obj.save() - self.db_set("reset_company_default_values", 1) + if not self.reset_company_default_values: + company_obj = frappe.get_doc("Company", self.company) + company_obj.total_monthly_sales = 0 + company_obj.sales_monthly_history = None + company_obj.save() + self.db_set("reset_company_default_values", 1) + + def initialize_doctypes_to_be_deleted_table(self): + if not self.initialize_doctypes_table: + doctypes_to_be_ignored_list = self.get_doctypes_to_be_ignored_list() + docfields = self.get_doctypes_with_company_field(doctypes_to_be_ignored_list) + tables = self.get_all_child_doctypes() + for docfield in docfields: + if docfield["parent"] != self.doctype: + no_of_docs = self.get_number_of_docs_linked_with_specified_company( + docfield["parent"], docfield["fieldname"] + ) + if no_of_docs > 0: + # Initialize + self.populate_doctypes_table(tables, docfield["parent"], docfield["fieldname"], 0) + self.db_set("initialize_doctypes_table", 1) def delete_company_transactions(self): - doctypes_to_be_ignored_list = self.get_doctypes_to_be_ignored_list() - docfields = self.get_doctypes_with_company_field(doctypes_to_be_ignored_list) - - tables = self.get_all_child_doctypes() - for docfield in docfields: - if docfield["parent"] != self.doctype: - no_of_docs = self.get_number_of_docs_linked_with_specified_company( - docfield["parent"], docfield["fieldname"] - ) - - if no_of_docs > 0: - self.delete_version_log(docfield["parent"], docfield["fieldname"]) - - reference_docs = frappe.get_all( - docfield["parent"], filters={docfield["fieldname"]: self.company} + if not self.delete_transactions: + doctypes_to_be_ignored_list = self.get_doctypes_to_be_ignored_list() + docfields = self.get_doctypes_with_company_field(doctypes_to_be_ignored_list) + + tables = self.get_all_child_doctypes() + for docfield in self.doctypes: + if docfield.doctype_name != self.doctype: + no_of_docs = self.get_number_of_docs_linked_with_specified_company( + docfield.doctype_name, docfield.docfield_name ) - reference_doc_names = [r.name for r in reference_docs] - - self.delete_communications(docfield["parent"], reference_doc_names) - self.delete_comments(docfield["parent"], reference_doc_names) - self.unlink_attachments(docfield["parent"], reference_doc_names) - - self.populate_doctypes_table(tables, docfield["parent"], no_of_docs) - - self.delete_child_tables(docfield["parent"], docfield["fieldname"]) - self.delete_docs_linked_with_specified_company(docfield["parent"], docfield["fieldname"]) - - naming_series = frappe.db.get_value("DocType", docfield["parent"], "autoname") - if naming_series: - if "#" in naming_series: - self.update_naming_series(naming_series, docfield["parent"]) - self.db_set("delete_transactions", 1) + if no_of_docs > 0: + reference_docs = frappe.get_all( + docfield.doctype_name, filters={docfield.docfield_name: self.company}, limit=self.batch_size + ) + reference_doc_names = [r.name for r in reference_docs] + + self.delete_version_log(docfield.doctype_name, reference_doc_names) + self.delete_communications(docfield.doctype_name, reference_doc_names) + self.delete_comments(docfield.doctype_name, reference_doc_names) + self.unlink_attachments(docfield.doctype_name, reference_doc_names) + + self.delete_child_tables(docfield.doctype_name, reference_doc_names) + self.delete_docs_linked_with_specified_company(docfield.doctype_name, docfield.docfield_name) + + naming_series = frappe.db.get_value("DocType", docfield.doctype_name, "autoname") + # TODO: do this at the end of each doctype + if naming_series: + if "#" in naming_series: + self.update_naming_series(naming_series, docfield.doctype_name) + self.db_set("delete_transactions", 1) def get_doctypes_to_be_ignored_list(self): singles = frappe.get_all("DocType", filters={"issingle": 1}, pluck="name") @@ -206,22 +225,21 @@ def get_all_child_doctypes(self): def get_number_of_docs_linked_with_specified_company(self, doctype, company_fieldname): return frappe.db.count(doctype, {company_fieldname: self.company}) - def populate_doctypes_table(self, tables, doctype, no_of_docs): + def populate_doctypes_table(self, tables, doctype, fieldname, no_of_docs): + self.flags.ignore_validate_update_after_submit = True if doctype not in tables: - self.append("doctypes", {"doctype_name": doctype, "no_of_docs": no_of_docs}) - - def delete_child_tables(self, doctype, company_fieldname): - parent_docs_to_be_deleted = frappe.get_all( - doctype, {company_fieldname: self.company}, pluck="name" - ) + self.append( + "doctypes", {"doctype_name": doctype, "docfield_name": fieldname, "no_of_docs": no_of_docs} + ) + self.save(ignore_permissions=True) + def delete_child_tables(self, doctype, reference_doc_names): child_tables = frappe.get_all( "DocField", filters={"fieldtype": "Table", "parent": doctype}, pluck="options" ) - for batch in create_batch(parent_docs_to_be_deleted, self.batch_size): - for table in child_tables: - frappe.db.delete(table, {"parent": ["in", batch]}) + for table in child_tables: + frappe.db.delete(table, {"parent": ["in", reference_doc_names]}) def delete_docs_linked_with_specified_company(self, doctype, company_fieldname): frappe.db.delete(doctype, {company_fieldname: self.company}) @@ -245,17 +263,11 @@ def update_naming_series(self, naming_series, doctype_name): frappe.db.sql("""update `tabSeries` set current = %s where name=%s""", (last, prefix)) - def delete_version_log(self, doctype, company_fieldname): - dt = qb.DocType(doctype) - names = qb.from_(dt).select(dt.name).where(dt[company_fieldname] == self.company).run(as_list=1) - names = [x[0] for x in names] - - if names: - versions = qb.DocType("Version") - for batch in create_batch(names, self.batch_size): - qb.from_(versions).delete().where( - (versions.ref_doctype == doctype) & (versions.docname.isin(batch)) - ).run() + def delete_version_log(self, doctype, docnames): + versions = qb.DocType("Version") + qb.from_(versions).delete().where( + (versions.ref_doctype == doctype) & (versions.docname.isin(docnames)) + ).run() def delete_communications(self, doctype, reference_doc_names): communications = frappe.get_all( diff --git a/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.json b/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.json index be0be945c4e6..4e5e18469997 100644 --- a/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.json +++ b/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.json @@ -6,7 +6,9 @@ "engine": "InnoDB", "field_order": [ "doctype_name", - "no_of_docs" + "docfield_name", + "no_of_docs", + "done" ], "fields": [ { @@ -22,12 +24,24 @@ "fieldtype": "Data", "in_list_view": 1, "label": "Number of Docs" + }, + { + "default": "0", + "fieldname": "done", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Done" + }, + { + "fieldname": "docfield_name", + "fieldtype": "Data", + "label": "DocField Name" } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-05-08 23:10:46.166744", + "modified": "2024-02-03 21:06:32.274445", "modified_by": "Administrator", "module": "Setup", "name": "Transaction Deletion Record Item", @@ -35,5 +49,6 @@ "permissions": [], "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.py b/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.py index 92ca8a2ac730..c004ed7a09d8 100644 --- a/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.py +++ b/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.py @@ -7,4 +7,24 @@ class TransactionDeletionRecordItem(Document): +<<<<<<< HEAD +======= + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + docfield_name: DF.Data | None + doctype_name: DF.Link + done: DF.Check + no_of_docs: DF.Data | None + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + # end: auto-generated types + +>>>>>>> 6a77d86a53 (refactor: use flags to decide on current stage) pass From e56138ddb00f11e0b3708c172977a7320c5c2a42 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sun, 4 Feb 2024 08:09:53 +0530 Subject: [PATCH 06/30] refactor: reorder flags in Tasks section (cherry picked from commit cccb2d5141e30234dd9d3f7ff877c9aaaa44879e) --- .../transaction_deletion_record.json | 4 ++-- .../transaction_deletion_record.py | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json index 6a848413ffc6..dc35fe595535 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json @@ -14,8 +14,8 @@ "delete_leads_and_addresses", "reset_company_default_values", "clear_notifications", - "delete_transactions", "initialize_doctypes_table", + "delete_transactions", "section_break_tbej", "doctypes", "doctypes_to_be_ignored", @@ -123,7 +123,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2024-02-03 20:48:34.107577", + "modified": "2024-02-04 08:09:26.784109", "modified_by": "Administrator", "module": "Setup", "name": "Transaction Deletion Record", diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py index 55837ac385c3..2bbb515498d8 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py @@ -173,7 +173,7 @@ def delete_company_transactions(self): tables = self.get_all_child_doctypes() for docfield in self.doctypes: - if docfield.doctype_name != self.doctype: + if docfield.doctype_name != self.doctype and not docfield.done: no_of_docs = self.get_number_of_docs_linked_with_specified_company( docfield.doctype_name, docfield.docfield_name ) @@ -196,6 +196,9 @@ def delete_company_transactions(self): if naming_series: if "#" in naming_series: self.update_naming_series(naming_series, docfield.doctype_name) + + else: + frappe.db.set_value(docfield.doctype, docfield.name, "done", 1) self.db_set("delete_transactions", 1) def get_doctypes_to_be_ignored_list(self): From b1367e839ccf98164bdc7bb591dba2a150729df7 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sun, 4 Feb 2024 08:25:18 +0530 Subject: [PATCH 07/30] refactor: chained callback (cherry picked from commit b12ca65fcc76c85c9a26fb9d6d2e252ede4a3e70) --- .../transaction_deletion_record.py | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py index 2bbb515498d8..b897c7bf879f 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py @@ -78,22 +78,37 @@ def on_submit(self): def on_cancel(self): self.db_set("status", "Cancelled") + def chain_callback(self, method): + frappe.enqueue( + "frappe.utils.background_jobs.run_doc_method", + doctype=self.doctype, + name=self.name, + doc_method=method, + queue="long", + enqueue_after_commit=True, + ) + @frappe.whitelist() def start_deletion_process(self): self.delete_bins() self.delete_lead_addresses() self.reset_company_values() + self.delete_notifications() + self.initialize_doctypes_to_be_deleted_table() + self.delete_company_transactions() + + def delete_notifications(self): if not self.clear_notifications: clear_notifications() self.db_set("clear_notifications", 1) - self.initialize_doctypes_to_be_deleted_table() - self.delete_company_transactions() + self.chain_callback("initialize_doctypes_to_be_deleted_table") def populate_doctypes_to_be_ignored_table(self): doctypes_to_be_ignored_list = get_doctypes_to_be_ignored() for doctype in doctypes_to_be_ignored_list: self.append("doctypes_to_be_ignored", {"doctype_name": doctype}) + @frappe.whitelist() def delete_bins(self): if not self.delete_bin_data: frappe.db.sql( @@ -102,6 +117,7 @@ def delete_bins(self): self.company, ) self.db_set("delete_bin_data", 1) + self.chain_callback(method="delete_lead_addresses") def delete_lead_addresses(self): """Delete addresses to which leads are linked""" @@ -142,6 +158,7 @@ def delete_lead_addresses(self): ) ) self.db_set("delete_leads_and_addresses", 1) + self.chain_callback(method="reset_company_values") def reset_company_values(self): if not self.reset_company_default_values: @@ -150,6 +167,7 @@ def reset_company_values(self): company_obj.sales_monthly_history = None company_obj.save() self.db_set("reset_company_default_values", 1) + self.chain_callback(method="delete_notifications") def initialize_doctypes_to_be_deleted_table(self): if not self.initialize_doctypes_table: @@ -165,6 +183,7 @@ def initialize_doctypes_to_be_deleted_table(self): # Initialize self.populate_doctypes_table(tables, docfield["parent"], docfield["fieldname"], 0) self.db_set("initialize_doctypes_table", 1) + self.chain_callback(method="delete_company_transactions") def delete_company_transactions(self): if not self.delete_transactions: @@ -197,6 +216,7 @@ def delete_company_transactions(self): if "#" in naming_series: self.update_naming_series(naming_series, docfield.doctype_name) + self.chain_callback(method="delete_company_transactions") else: frappe.db.set_value(docfield.doctype, docfield.name, "done", 1) self.db_set("delete_transactions", 1) From 0d791f594f91e7884d7ef368f4954f847a455342 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sun, 4 Feb 2024 10:57:34 +0530 Subject: [PATCH 08/30] refactor: use separate child table for summary (cherry picked from commit 49d3bcbc8df8fe9457cce55793896146705b50e5) --- .../__init__.py | 0 .../transaction_deletion_record_details.json | 59 +++++++++++++++++++ .../transaction_deletion_record_details.py | 26 ++++++++ .../transaction_deletion_record.js | 12 ++++ .../transaction_deletion_record.json | 4 +- .../transaction_deletion_record.py | 43 ++++++++------ .../transaction_deletion_record_item.json | 25 +------- .../transaction_deletion_record_item.py | 3 - 8 files changed, 126 insertions(+), 46 deletions(-) create mode 100644 erpnext/accounts/doctype/transaction_deletion_record_details/__init__.py create mode 100644 erpnext/accounts/doctype/transaction_deletion_record_details/transaction_deletion_record_details.json create mode 100644 erpnext/accounts/doctype/transaction_deletion_record_details/transaction_deletion_record_details.py diff --git a/erpnext/accounts/doctype/transaction_deletion_record_details/__init__.py b/erpnext/accounts/doctype/transaction_deletion_record_details/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/erpnext/accounts/doctype/transaction_deletion_record_details/transaction_deletion_record_details.json b/erpnext/accounts/doctype/transaction_deletion_record_details/transaction_deletion_record_details.json new file mode 100644 index 000000000000..e8a5eb6c4321 --- /dev/null +++ b/erpnext/accounts/doctype/transaction_deletion_record_details/transaction_deletion_record_details.json @@ -0,0 +1,59 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2024-02-04 10:53:32.307930", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "doctype_name", + "docfield_name", + "no_of_docs", + "done" + ], + "fields": [ + { + "fieldname": "doctype_name", + "fieldtype": "Link", + "in_list_view": 1, + "label": "DocType", + "options": "DocType", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "docfield_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "DocField", + "read_only": 1 + }, + { + "fieldname": "no_of_docs", + "fieldtype": "Int", + "in_list_view": 1, + "label": "No of Docs", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "done", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Done", + "read_only": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2024-02-04 10:55:52.060417", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Transaction Deletion Record Details", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/transaction_deletion_record_details/transaction_deletion_record_details.py b/erpnext/accounts/doctype/transaction_deletion_record_details/transaction_deletion_record_details.py new file mode 100644 index 000000000000..bc5b5c41fddb --- /dev/null +++ b/erpnext/accounts/doctype/transaction_deletion_record_details/transaction_deletion_record_details.py @@ -0,0 +1,26 @@ +# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class TransactionDeletionRecordDetails(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + docfield_name: DF.Data | None + doctype_name: DF.Link + done: DF.Check + no_of_docs: DF.Int + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + # end: auto-generated types + + pass diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js index 1a8b52f46bd5..c6bb3781bfcf 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js @@ -41,6 +41,18 @@ frappe.ui.form.on("Transaction Deletion Record", { }); }); } + + if (frm.doc.docstatus==1 && ['Queued', 'Failed'].find(x => x == frm.doc.status)) { + let execute_btn = __("Start Chain of Events") + + frm.add_custom_button(execute_btn, () => { + frm.call({ + method: 'delete_bins', + doc: frm.doc + }); + }); + } + }, }); diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json index dc35fe595535..bbc571a0816a 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json @@ -34,7 +34,7 @@ "fieldname": "doctypes", "fieldtype": "Table", "label": "Summary", - "options": "Transaction Deletion Record Item", + "options": "Transaction Deletion Record Details", "read_only": 1 }, { @@ -123,7 +123,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2024-02-04 08:09:26.784109", + "modified": "2024-02-04 10:55:09.430373", "modified_by": "Administrator", "module": "Setup", "name": "Transaction Deletion Record", diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py index b897c7bf879f..c7efeaab146b 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py @@ -20,6 +20,9 @@ class TransactionDeletionRecord(Document): if TYPE_CHECKING: from frappe.types import DF + from erpnext.accounts.doctype.transaction_deletion_record_details.transaction_deletion_record_details import ( + TransactionDeletionRecordDetails, + ) from erpnext.setup.doctype.transaction_deletion_record_item.transaction_deletion_record_item import ( TransactionDeletionRecordItem, ) @@ -30,7 +33,7 @@ class TransactionDeletionRecord(Document): delete_bin_data: DF.Check delete_leads_and_addresses: DF.Check delete_transactions: DF.Check - doctypes: DF.Table[TransactionDeletionRecordItem] + doctypes: DF.Table[TransactionDeletionRecordDetails] doctypes_to_be_ignored: DF.Table[TransactionDeletionRecordItem] initialize_doctypes_table: DF.Check reset_company_default_values: DF.Check @@ -40,7 +43,7 @@ class TransactionDeletionRecord(Document): >>>>>>> 0d65d878de (refactor: more options for 'status' and move it to top) def __init__(self, *args, **kwargs): super(TransactionDeletionRecord, self).__init__(*args, **kwargs) - self.batch_size = 5000 + self.batch_size = 5 def validate(self): frappe.only_for("System Manager") @@ -78,7 +81,7 @@ def on_submit(self): def on_cancel(self): self.db_set("status", "Cancelled") - def chain_callback(self, method): + def chain_call(self, method): frappe.enqueue( "frappe.utils.background_jobs.run_doc_method", doctype=self.doctype, @@ -101,7 +104,7 @@ def delete_notifications(self): if not self.clear_notifications: clear_notifications() self.db_set("clear_notifications", 1) - self.chain_callback("initialize_doctypes_to_be_deleted_table") + self.chain_call("initialize_doctypes_to_be_deleted_table") def populate_doctypes_to_be_ignored_table(self): doctypes_to_be_ignored_list = get_doctypes_to_be_ignored() @@ -117,7 +120,7 @@ def delete_bins(self): self.company, ) self.db_set("delete_bin_data", 1) - self.chain_callback(method="delete_lead_addresses") + self.chain_call(method="delete_lead_addresses") def delete_lead_addresses(self): """Delete addresses to which leads are linked""" @@ -158,7 +161,7 @@ def delete_lead_addresses(self): ) ) self.db_set("delete_leads_and_addresses", 1) - self.chain_callback(method="reset_company_values") + self.chain_call(method="reset_company_values") def reset_company_values(self): if not self.reset_company_default_values: @@ -167,7 +170,7 @@ def reset_company_values(self): company_obj.sales_monthly_history = None company_obj.save() self.db_set("reset_company_default_values", 1) - self.chain_callback(method="delete_notifications") + self.chain_call(method="delete_notifications") def initialize_doctypes_to_be_deleted_table(self): if not self.initialize_doctypes_table: @@ -183,7 +186,7 @@ def initialize_doctypes_to_be_deleted_table(self): # Initialize self.populate_doctypes_table(tables, docfield["parent"], docfield["fieldname"], 0) self.db_set("initialize_doctypes_table", 1) - self.chain_callback(method="delete_company_transactions") + self.chain_call(method="delete_company_transactions") def delete_company_transactions(self): if not self.delete_transactions: @@ -206,20 +209,24 @@ def delete_company_transactions(self): self.delete_communications(docfield.doctype_name, reference_doc_names) self.delete_comments(docfield.doctype_name, reference_doc_names) self.unlink_attachments(docfield.doctype_name, reference_doc_names) - self.delete_child_tables(docfield.doctype_name, reference_doc_names) - self.delete_docs_linked_with_specified_company(docfield.doctype_name, docfield.docfield_name) - + self.delete_docs_linked_with_specified_company(docfield.doctype_name, reference_doc_names) + processed = int(docfield.no_of_docs) + len(reference_doc_names) + frappe.db.set_value(docfield.doctype, docfield.name, "no_of_docs", processed) + else: naming_series = frappe.db.get_value("DocType", docfield.doctype_name, "autoname") - # TODO: do this at the end of each doctype if naming_series: if "#" in naming_series: self.update_naming_series(naming_series, docfield.doctype_name) - - self.chain_callback(method="delete_company_transactions") - else: frappe.db.set_value(docfield.doctype, docfield.name, "done", 1) - self.db_set("delete_transactions", 1) + + pending_doctypes = frappe.db.get_all( + docfield.doctype, filters={"parent": self.name, "done": 0}, pluck="doctype_name" + ) + if pending_doctypes: + self.chain_call(method="delete_company_transactions") + else: + self.db_set("delete_transactions", 1) def get_doctypes_to_be_ignored_list(self): singles = frappe.get_all("DocType", filters={"issingle": 1}, pluck="name") @@ -264,8 +271,8 @@ def delete_child_tables(self, doctype, reference_doc_names): for table in child_tables: frappe.db.delete(table, {"parent": ["in", reference_doc_names]}) - def delete_docs_linked_with_specified_company(self, doctype, company_fieldname): - frappe.db.delete(doctype, {company_fieldname: self.company}) + def delete_docs_linked_with_specified_company(self, doctype, reference_doc_names): + frappe.db.delete(doctype, {"name": ("in", reference_doc_names)}) def update_naming_series(self, naming_series, doctype_name): if "." in naming_series: diff --git a/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.json b/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.json index 4e5e18469997..89db63694c20 100644 --- a/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.json +++ b/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.json @@ -5,10 +5,7 @@ "editable_grid": 1, "engine": "InnoDB", "field_order": [ - "doctype_name", - "docfield_name", - "no_of_docs", - "done" + "doctype_name" ], "fields": [ { @@ -18,30 +15,12 @@ "label": "DocType", "options": "DocType", "reqd": 1 - }, - { - "fieldname": "no_of_docs", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Number of Docs" - }, - { - "default": "0", - "fieldname": "done", - "fieldtype": "Check", - "in_list_view": 1, - "label": "Done" - }, - { - "fieldname": "docfield_name", - "fieldtype": "Data", - "label": "DocField Name" } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2024-02-03 21:06:32.274445", + "modified": "2024-02-04 10:56:27.413691", "modified_by": "Administrator", "module": "Setup", "name": "Transaction Deletion Record Item", diff --git a/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.py b/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.py index c004ed7a09d8..ed01afe70f94 100644 --- a/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.py +++ b/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.py @@ -17,10 +17,7 @@ class TransactionDeletionRecordItem(Document): if TYPE_CHECKING: from frappe.types import DF - docfield_name: DF.Data | None doctype_name: DF.Link - done: DF.Check - no_of_docs: DF.Data | None parent: DF.Data parentfield: DF.Data parenttype: DF.Data From a303788c715120019cdd387928d2595928529907 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sun, 4 Feb 2024 15:26:33 +0530 Subject: [PATCH 09/30] chore: remove unwanted UI code (cherry picked from commit b98a5e4edcd896b05e4e7fd5874c56ceb9d95bf0) --- .../transaction_deletion_record.js | 29 +++---------------- 1 file changed, 4 insertions(+), 25 deletions(-) diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js index c6bb3781bfcf..027bbcb4b208 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js @@ -10,40 +10,19 @@ frappe.ui.form.on("Transaction Deletion Record", { callback: function (r) { doctypes_to_be_ignored_array = r.message; populate_doctypes_to_be_ignored(doctypes_to_be_ignored_array, frm); - frm.fields_dict["doctypes_to_be_ignored"].grid.set_column_disp("no_of_docs", false); - frm.refresh_field("doctypes_to_be_ignored"); - }, + frm.refresh_field('doctypes_to_be_ignored'); + } }); } frm.get_field('doctypes_to_be_ignored').grid.cannot_add_rows = true; - frm.fields_dict['doctypes_to_be_ignored'].grid.set_column_disp('no_of_docs', false); - frm.fields_dict['doctypes_to_be_ignored'].grid.set_column_disp('done', false); - frm.refresh_field('doctypes_to_be_ignored'); - frm.get_field('doctypes').grid.cannot_add_rows = true; - frm.fields_dict['doctypes'].grid.set_column_disp('no_of_docs', true); - frm.refresh_field('doctypes'); }, - refresh: function (frm) { - frm.fields_dict["doctypes_to_be_ignored"].grid.set_column_disp("no_of_docs", false); - frm.refresh_field("doctypes_to_be_ignored"); - - if (frm.doc.docstatus==1 && ['Queued', 'Failed'].find(x => x == frm.doc.status)) { - let execute_btn = __("Start / Resume") - - frm.add_custom_button(execute_btn, () => { - frm.call({ - method: 'start_deletion_process', - doc: frm.doc - }); - }); - } - + refresh: function(frm) { if (frm.doc.docstatus==1 && ['Queued', 'Failed'].find(x => x == frm.doc.status)) { - let execute_btn = __("Start Chain of Events") + let execute_btn = __("Start Deletion") frm.add_custom_button(execute_btn, () => { frm.call({ From 833df2c76b6e41aa52a05d6a463ec95272d1f8aa Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sun, 4 Feb 2024 15:29:39 +0530 Subject: [PATCH 10/30] refactor: make Excluded doctype table read only (cherry picked from commit 7c4cff2649daa52d6cabf8a046b3867ab60e0da6) --- .../transaction_deletion_record.js | 4 +--- .../transaction_deletion_record.json | 5 +++-- .../transaction_deletion_record.py | 1 + 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js index 027bbcb4b208..ed70ebb5f700 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js @@ -15,9 +15,6 @@ frappe.ui.form.on("Transaction Deletion Record", { }); } - - frm.get_field('doctypes_to_be_ignored').grid.cannot_add_rows = true; - }, refresh: function(frm) { @@ -25,6 +22,7 @@ frappe.ui.form.on("Transaction Deletion Record", { let execute_btn = __("Start Deletion") frm.add_custom_button(execute_btn, () => { + // Entry point for chain of events frm.call({ method: 'delete_bins', doc: frm.doc diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json index bbc571a0816a..bd45b1c109df 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json @@ -41,7 +41,8 @@ "fieldname": "doctypes_to_be_ignored", "fieldtype": "Table", "label": "Excluded DocTypes", - "options": "Transaction Deletion Record Item" + "options": "Transaction Deletion Record Item", + "read_only": 1 }, { "fieldname": "amended_from", @@ -123,7 +124,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2024-02-04 10:55:09.430373", + "modified": "2024-02-04 15:28:29.532826", "modified_by": "Administrator", "module": "Setup", "name": "Transaction Deletion Record", diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py index c7efeaab146b..89471071e799 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py @@ -113,6 +113,7 @@ def populate_doctypes_to_be_ignored_table(self): @frappe.whitelist() def delete_bins(self): + # This methid is the entry point for the chain of events that follow if not self.delete_bin_data: frappe.db.sql( """delete from `tabBin` where warehouse in From 341e467056c86e5b8c70c7b8dac9a66931bc64f3 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sun, 4 Feb 2024 15:37:21 +0530 Subject: [PATCH 11/30] refactor: validate status before running events (cherry picked from commit 86b5e2e2779eb498a8525ca07c6e25335e6f2f5d) --- .../transaction_deletion_record.py | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py index 89471071e799..15a9ca9d2cfb 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py @@ -6,7 +6,7 @@ from frappe import _, qb from frappe.desk.notifications import clear_notifications from frappe.model.document import Document -from frappe.utils import cint, create_batch +from frappe.utils import cint, create_batch, get_link_to_form class TransactionDeletionRecord(Document): @@ -43,7 +43,7 @@ class TransactionDeletionRecord(Document): >>>>>>> 0d65d878de (refactor: more options for 'status' and move it to top) def __init__(self, *args, **kwargs): super(TransactionDeletionRecord, self).__init__(*args, **kwargs) - self.batch_size = 5 + self.batch_size = 5000 def validate(self): frappe.only_for("System Manager") @@ -101,6 +101,7 @@ def start_deletion_process(self): self.delete_company_transactions() def delete_notifications(self): + self.validate_doc_status() if not self.clear_notifications: clear_notifications() self.db_set("clear_notifications", 1) @@ -111,9 +112,19 @@ def populate_doctypes_to_be_ignored_table(self): for doctype in doctypes_to_be_ignored_list: self.append("doctypes_to_be_ignored", {"doctype_name": doctype}) + def validate_doc_status(self): + if self.status != "Running": + frappe.throw( + _("{0} is not running. Cannot trigger events for this Document").format( + get_link_to_form("Transaction Deletion Record", self.name) + ) + ) + @frappe.whitelist() def delete_bins(self): # This methid is the entry point for the chain of events that follow + self.db_set("status", "Running") + if not self.delete_bin_data: frappe.db.sql( """delete from `tabBin` where warehouse in @@ -125,6 +136,7 @@ def delete_bins(self): def delete_lead_addresses(self): """Delete addresses to which leads are linked""" + self.validate_doc_status() if not self.delete_leads_and_addresses: leads = frappe.get_all("Lead", filters={"company": self.company}) leads = ["'%s'" % row.get("name") for row in leads] @@ -165,6 +177,7 @@ def delete_lead_addresses(self): self.chain_call(method="reset_company_values") def reset_company_values(self): + self.validate_doc_status() if not self.reset_company_default_values: company_obj = frappe.get_doc("Company", self.company) company_obj.total_monthly_sales = 0 @@ -174,6 +187,7 @@ def reset_company_values(self): self.chain_call(method="delete_notifications") def initialize_doctypes_to_be_deleted_table(self): + self.validate_doc_status() if not self.initialize_doctypes_table: doctypes_to_be_ignored_list = self.get_doctypes_to_be_ignored_list() docfields = self.get_doctypes_with_company_field(doctypes_to_be_ignored_list) @@ -190,6 +204,7 @@ def initialize_doctypes_to_be_deleted_table(self): self.chain_call(method="delete_company_transactions") def delete_company_transactions(self): + self.validate_doc_status() if not self.delete_transactions: doctypes_to_be_ignored_list = self.get_doctypes_to_be_ignored_list() docfields = self.get_doctypes_with_company_field(doctypes_to_be_ignored_list) @@ -215,6 +230,7 @@ def delete_company_transactions(self): processed = int(docfield.no_of_docs) + len(reference_doc_names) frappe.db.set_value(docfield.doctype, docfield.name, "no_of_docs", processed) else: + # reset naming series naming_series = frappe.db.get_value("DocType", docfield.doctype_name, "autoname") if naming_series: if "#" in naming_series: @@ -227,6 +243,7 @@ def delete_company_transactions(self): if pending_doctypes: self.chain_call(method="delete_company_transactions") else: + self.db_set("status", "Completed") self.db_set("delete_transactions", 1) def get_doctypes_to_be_ignored_list(self): From c38cfd14f3d3ba16e358ee364ef2772c5fb4bd9a Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sun, 4 Feb 2024 16:11:42 +0530 Subject: [PATCH 12/30] chore: show correct status in list view (cherry picked from commit 1014940953ed54f31a170e0e99d03c1c5f1cd022) --- .../transaction_deletion_record_list.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record_list.js b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record_list.js index 08a35df2c174..7c7b8ff25a72 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record_list.js +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record_list.js @@ -1,12 +1,16 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -frappe.listview_settings["Transaction Deletion Record"] = { - get_indicator: function (doc) { - if (doc.docstatus == 0) { - return [__("Draft"), "red"]; - } else { - return [__("Completed"), "green"]; - } +frappe.listview_settings['Transaction Deletion Record'] = { + add_fields: ["status"], + get_indicator: function(doc) { + let colors = { + 'Queued': 'orange', + 'Completed': 'green', + 'Running': 'blue', + 'Failed': 'red', + }; + let status = doc.status; + return [__(status), colors[status], 'status,=,'+status]; }, }; From 97ed9056274957b055025267fbb040ae09d7974b Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 5 Feb 2024 10:21:12 +0530 Subject: [PATCH 13/30] refactor: reset all flags and remove unwanted code (cherry picked from commit 2dbe68a09d0bf9a13ef0d3bb8f071a7634291766) --- .../transaction_deletion_record.json | 4 +++- .../transaction_deletion_record.py | 19 ++++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json index bd45b1c109df..aa06d14b170d 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json @@ -57,6 +57,7 @@ "fieldname": "status", "fieldtype": "Select", "label": "Status", + "no_copy": 1, "options": "Queued\nRunning\nFailed\nCompleted\nCancelled", "read_only": 1 }, @@ -118,13 +119,14 @@ "fieldname": "initialize_doctypes_table", "fieldtype": "Check", "label": "Initialize Summary Table", + "no_copy": 1, "read_only": 1 } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2024-02-04 15:28:29.532826", + "modified": "2024-02-05 10:25:28.462255", "modified_by": "Administrator", "module": "Setup", "name": "Transaction Deletion Record", diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py index 15a9ca9d2cfb..fef5d7f20850 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py @@ -61,6 +61,15 @@ def validate_doctypes_to_be_ignored(self): ) def before_submit(self): + if queued_docs := frappe.db.get_all( + "Transaction Deletion Record", filters={"company": self.company, "status": "Queued"} + ): + frappe.throw( + _("There is another document: {0} Queued. Cannot queue multi docs for one company.").format( + self.queued_docs + ) + ) + if not self.doctypes_to_be_ignored: self.populate_doctypes_to_be_ignored_table() @@ -69,6 +78,7 @@ def reset_task_flags(self): self.delete_bin_data = 0 self.delete_leads_and_addresses = 0 self.delete_transactions = 0 + self.initialize_doctypes_table = 0 self.reset_company_default_values = 0 def before_save(self): @@ -91,15 +101,6 @@ def chain_call(self, method): enqueue_after_commit=True, ) - @frappe.whitelist() - def start_deletion_process(self): - self.delete_bins() - self.delete_lead_addresses() - self.reset_company_values() - self.delete_notifications() - self.initialize_doctypes_to_be_deleted_table() - self.delete_company_transactions() - def delete_notifications(self): self.validate_doc_status() if not self.clear_notifications: From e600109872652d3571b3265eb26f35a54498bd28 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 5 Feb 2024 10:42:59 +0530 Subject: [PATCH 14/30] refactor: no copy on summary table and more validations (cherry picked from commit 55e93b3fe14b492e2f648b6ee860e902d87013e6) --- .../transaction_deletion_record.json | 3 ++- .../transaction_deletion_record.py | 14 ++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json index aa06d14b170d..6e057ace4a6e 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json @@ -34,6 +34,7 @@ "fieldname": "doctypes", "fieldtype": "Table", "label": "Summary", + "no_copy": 1, "options": "Transaction Deletion Record Details", "read_only": 1 }, @@ -126,7 +127,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2024-02-05 10:25:28.462255", + "modified": "2024-02-05 10:36:34.229864", "modified_by": "Administrator", "module": "Setup", "name": "Transaction Deletion Record", diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py index fef5d7f20850..87ca9fab5674 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py @@ -6,7 +6,7 @@ from frappe import _, qb from frappe.desk.notifications import clear_notifications from frappe.model.document import Document -from frappe.utils import cint, create_batch, get_link_to_form +from frappe.utils import cint, comma_and, create_batch, get_link_to_form class TransactionDeletionRecord(Document): @@ -62,11 +62,16 @@ def validate_doctypes_to_be_ignored(self): def before_submit(self): if queued_docs := frappe.db.get_all( - "Transaction Deletion Record", filters={"company": self.company, "status": "Queued"} + "Transaction Deletion Record", + filters={"company": self.company, "status": ("in", ["Running", "Queued"]), "docstatus": 1}, + pluck="name", ): frappe.throw( - _("There is another document: {0} Queued. Cannot queue multi docs for one company.").format( - self.queued_docs + _( + "Cannot queue multi docs for one company. {0} is already queued/running for company: {1}" + ).format( + comma_and([get_link_to_form("Transaction Deletion Record", x) for x in queued_docs]), + frappe.bold(self.company), ) ) @@ -83,6 +88,7 @@ def reset_task_flags(self): def before_save(self): self.status = "" + self.doctypes.clear() self.reset_task_flags() def on_submit(self): From 962105bc8744fcf24bb1c15ef69a723dc040c417 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 5 Feb 2024 11:52:00 +0530 Subject: [PATCH 15/30] refactor: validations to prevent duplicate jobs (cherry picked from commit 31a2da552b0773f8b7caf510fa06e54cdc1f2e9c) --- .../transaction_deletion_record.py | 75 +++++++++++++++---- 1 file changed, 59 insertions(+), 16 deletions(-) diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py index 87ca9fab5674..d57f3f609371 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py @@ -7,6 +7,7 @@ from frappe.desk.notifications import clear_notifications from frappe.model.document import Document from frappe.utils import cint, comma_and, create_batch, get_link_to_form +from frappe.utils.background_jobs import get_job, is_job_enqueued class TransactionDeletionRecord(Document): @@ -44,6 +45,15 @@ class TransactionDeletionRecord(Document): def __init__(self, *args, **kwargs): super(TransactionDeletionRecord, self).__init__(*args, **kwargs) self.batch_size = 5000 + # Tasks are listged by their execution order + self.task_to_internal_method_map = { + "Delete Bins": "delete_bins", + "Delete Leads and Addresses": "delete_lead_addresses", + "Reset Company Values": "reset_company_values", + "Clear Notifications": "delete_notifications", + "Initialize Summary Table": "initialize_doctypes_to_be_deleted_table", + "Delete Transactions": "delete_company_transactions", + } def validate(self): frappe.only_for("System Manager") @@ -60,6 +70,16 @@ def validate_doctypes_to_be_ignored(self): title=_("Not Allowed"), ) + def generate_job_name_for_task(self, task=None): + method = self.task_to_internal_method_map[task] + return f"{self.name}_{method}" + + def generate_job_name_for_all_tasks(self): + job_names = [] + for method in self.task_to_internal_method_map.values(): + job_names.append(self.generate_job_name_for_task) + return job_names + def before_submit(self): if queued_docs := frappe.db.get_all( "Transaction Deletion Record", @@ -68,7 +88,7 @@ def before_submit(self): ): frappe.throw( _( - "Cannot queue multi docs for one company. {0} is already queued/running for company: {1}" + "Cannot enqueue multi docs for one company. {0} is already queued/running for company: {1}" ).format( comma_and([get_link_to_form("Transaction Deletion Record", x) for x in queued_docs]), frappe.bold(self.company), @@ -97,28 +117,47 @@ def on_submit(self): def on_cancel(self): self.db_set("status", "Cancelled") - def chain_call(self, method): - frappe.enqueue( - "frappe.utils.background_jobs.run_doc_method", - doctype=self.doctype, - name=self.name, - doc_method=method, - queue="long", - enqueue_after_commit=True, - ) + def chain_call(self, task=None): + if task and task in self.task_to_internal_method_map: + method = self.task_to_internal_method_map[task] + job_id = self.generate_job_name_for_task(task) + + frappe.enqueue( + "frappe.utils.background_jobs.run_doc_method", + doctype=self.doctype, + name=self.name, + doc_method=method, + job_id=job_id, + queue="long", + enqueue_after_commit=True, + ) def delete_notifications(self): self.validate_doc_status() if not self.clear_notifications: clear_notifications() self.db_set("clear_notifications", 1) - self.chain_call("initialize_doctypes_to_be_deleted_table") + self.chain_call(task="Initialize Summary Table") def populate_doctypes_to_be_ignored_table(self): doctypes_to_be_ignored_list = get_doctypes_to_be_ignored() for doctype in doctypes_to_be_ignored_list: self.append("doctypes_to_be_ignored", {"doctype_name": doctype}) + def validate_running_task_for_doc(self, job_names: list = None): + # at most only one task should be runnning + running_tasks = [] + for x in job_names: + if is_job_enqueued(x): + running_tasks.append(get_job(x).get_id()) + + if running_tasks: + frappe.throw( + _("{0} is already running for {1}").format( + comma_and([get_link_to_form("RQ Job", x) for x in running_tasks]), self.name + ) + ) + def validate_doc_status(self): if self.status != "Running": frappe.throw( @@ -126,6 +165,9 @@ def validate_doc_status(self): get_link_to_form("Transaction Deletion Record", self.name) ) ) + # make sure that job none of tasks are already running + job_names = self.generate_job_name_for_all_tasks() + self.validate_running_task_for_doc(job_names=job_names) @frappe.whitelist() def delete_bins(self): @@ -139,7 +181,7 @@ def delete_bins(self): self.company, ) self.db_set("delete_bin_data", 1) - self.chain_call(method="delete_lead_addresses") + self.chain_call(task="Delete Leads and Addresses") def delete_lead_addresses(self): """Delete addresses to which leads are linked""" @@ -181,7 +223,7 @@ def delete_lead_addresses(self): ) ) self.db_set("delete_leads_and_addresses", 1) - self.chain_call(method="reset_company_values") + self.chain_call(task="Reset Company Values") def reset_company_values(self): self.validate_doc_status() @@ -191,7 +233,7 @@ def reset_company_values(self): company_obj.sales_monthly_history = None company_obj.save() self.db_set("reset_company_default_values", 1) - self.chain_call(method="delete_notifications") + self.chain_call(task="Clear Notifications") def initialize_doctypes_to_be_deleted_table(self): self.validate_doc_status() @@ -208,7 +250,7 @@ def initialize_doctypes_to_be_deleted_table(self): # Initialize self.populate_doctypes_table(tables, docfield["parent"], docfield["fieldname"], 0) self.db_set("initialize_doctypes_table", 1) - self.chain_call(method="delete_company_transactions") + self.chain_call(task="Delete Transactions") def delete_company_transactions(self): self.validate_doc_status() @@ -248,7 +290,8 @@ def delete_company_transactions(self): docfield.doctype, filters={"parent": self.name, "done": 0}, pluck="doctype_name" ) if pending_doctypes: - self.chain_call(method="delete_company_transactions") + # as method is enqueued after commit, calling itself will not make validate_doc_status to throw + self.chain_call(task="Delete Transactions") else: self.db_set("status", "Completed") self.db_set("delete_transactions", 1) From e142daca7fcd7b5904ea144ffa21d593f5da337f Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 5 Feb 2024 17:35:14 +0530 Subject: [PATCH 16/30] chore: hide docfield in list view (cherry picked from commit 98afb4d468145ac2270362841468f2ccb05b501f) --- .../transaction_deletion_record_details.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/transaction_deletion_record_details/transaction_deletion_record_details.json b/erpnext/accounts/doctype/transaction_deletion_record_details/transaction_deletion_record_details.json index e8a5eb6c4321..fe4b0852ac1b 100644 --- a/erpnext/accounts/doctype/transaction_deletion_record_details/transaction_deletion_record_details.json +++ b/erpnext/accounts/doctype/transaction_deletion_record_details/transaction_deletion_record_details.json @@ -24,7 +24,6 @@ { "fieldname": "docfield_name", "fieldtype": "Data", - "in_list_view": 1, "label": "DocField", "read_only": 1 }, @@ -47,7 +46,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2024-02-04 10:55:52.060417", + "modified": "2024-02-05 17:35:09.556054", "modified_by": "Administrator", "module": "Accounts", "name": "Transaction Deletion Record Details", From e9080033585003d07a70362c1ce71b1bb0a8882a Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 5 Feb 2024 20:35:29 +0530 Subject: [PATCH 17/30] refactor: make sure only one task is running for doc (cherry picked from commit 78c9cc63b1011761dd3fed84edb5c0b41ff9f3f5) --- .../transaction_deletion_record.js | 2 +- .../transaction_deletion_record.py | 48 ++++++++++++------- 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js index ed70ebb5f700..ccf09a6c38b0 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js @@ -24,7 +24,7 @@ frappe.ui.form.on("Transaction Deletion Record", { frm.add_custom_button(execute_btn, () => { // Entry point for chain of events frm.call({ - method: 'delete_bins', + method: 'process_tasks', doc: frm.doc }); }); diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py index d57f3f609371..0ae587e1f868 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py @@ -1,6 +1,7 @@ # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt +from collections import OrderedDict import frappe from frappe import _, qb @@ -45,15 +46,17 @@ class TransactionDeletionRecord(Document): def __init__(self, *args, **kwargs): super(TransactionDeletionRecord, self).__init__(*args, **kwargs) self.batch_size = 5000 - # Tasks are listged by their execution order - self.task_to_internal_method_map = { - "Delete Bins": "delete_bins", - "Delete Leads and Addresses": "delete_lead_addresses", - "Reset Company Values": "reset_company_values", - "Clear Notifications": "delete_notifications", - "Initialize Summary Table": "initialize_doctypes_to_be_deleted_table", - "Delete Transactions": "delete_company_transactions", - } + # Tasks are listed by their execution order + self.task_to_internal_method_map = OrderedDict( + { + "Delete Bins": "delete_bins", + "Delete Leads and Addresses": "delete_lead_addresses", + "Reset Company Values": "reset_company_values", + "Clear Notifications": "delete_notifications", + "Initialize Summary Table": "initialize_doctypes_to_be_deleted_table", + "Delete Transactions": "delete_company_transactions", + } + ) def validate(self): frappe.only_for("System Manager") @@ -74,10 +77,19 @@ def generate_job_name_for_task(self, task=None): method = self.task_to_internal_method_map[task] return f"{self.name}_{method}" + def generate_job_name_for_next_tasks(self, task=None): + job_names = [] + current_task_idx = list(self.task_to_internal_method_map).index(task) + for idx, task in enumerate(self.task_to_internal_method_map.keys(), 0): + # generate job_name for next tasks + if idx > current_task_idx: + job_names.append(self.generate_job_name_for_task(task)) + return job_names + def generate_job_name_for_all_tasks(self): job_names = [] - for method in self.task_to_internal_method_map.values(): - job_names.append(self.generate_job_name_for_task) + for task in self.task_to_internal_method_map.keys(): + job_names.append(self.generate_job_name_for_task(task)) return job_names def before_submit(self): @@ -119,6 +131,10 @@ def on_cancel(self): def chain_call(self, task=None): if task and task in self.task_to_internal_method_map: + # make sure that none of next tasks are already running + job_names = self.generate_job_name_for_next_tasks(task=task) + self.validate_running_task_for_doc(job_names=job_names) + method = self.task_to_internal_method_map[task] job_id = self.generate_job_name_for_task(task) @@ -165,15 +181,15 @@ def validate_doc_status(self): get_link_to_form("Transaction Deletion Record", self.name) ) ) - # make sure that job none of tasks are already running - job_names = self.generate_job_name_for_all_tasks() - self.validate_running_task_for_doc(job_names=job_names) @frappe.whitelist() - def delete_bins(self): - # This methid is the entry point for the chain of events that follow + def process_tasks(self): + # This method is the entry point for the chain of events that follow self.db_set("status", "Running") + self.chain_call(task="Delete Bins") + def delete_bins(self): + self.validate_doc_status() if not self.delete_bin_data: frappe.db.sql( """delete from `tabBin` where warehouse in From cf6cb80d180a9fbd12e422503b759302b1ad8be8 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 7 Feb 2024 20:54:29 +0530 Subject: [PATCH 18/30] refactor: barebones hook on all doctypes with 'company' field (cherry picked from commit ec194ef076037b16edea68728069b3308a9a4216) --- erpnext/hooks.py | 5 ++++- .../transaction_deletion_record.py | 21 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 5a4e8539bf6b..e328c686f5dc 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -299,7 +299,10 @@ doc_events = { "*": { - "validate": "erpnext.support.doctype.service_level_agreement.service_level_agreement.apply", + "validate": [ + "erpnext.support.doctype.service_level_agreement.service_level_agreement.apply", + "erpnext.setup.doctype.transaction_deletion_record.transaction_deletion_record.check_for_running_deletion_job", + ], }, tuple(period_closing_doctypes): { "validate": "erpnext.accounts.doctype.accounting_period.accounting_period.validate_accounting_period_on_doc_save", diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py index 0ae587e1f868..4d6463ca5e53 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py @@ -453,3 +453,24 @@ def get_doctypes_to_be_ignored(): doctypes_to_be_ignored.extend(frappe.get_hooks("company_data_to_be_ignored") or []) return doctypes_to_be_ignored + + +def check_for_running_deletion_job(doc, method=None): + df = qb.DocType("DocField") + if ( + not_allowed := qb.from_(df) + .select(df.parent) + .where((df.fieldname == "company") & (df.parent == doc.doctype)) + .run() + ): + if running_deletion_jobs := frappe.db.get_all( + "Transaction Deletion Record", + filters={"docstatus": 1, "company": doc.company, "status": "Running"}, + ): + frappe.throw( + _( + "Transaction Deletion job {0} is running for this Company. Cannot make any transactions until the deletion job is completed" + ).format( + get_link_to_form("Transaction Deletion Record", running_deletion_jobs[0].name) + ) + ) From 0ea9ce7a5aa1716533218662c1f7d62d14169ec0 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 8 Feb 2024 16:08:59 +0530 Subject: [PATCH 19/30] refactor: better method naming (cherry picked from commit 30463657bf7f44321b690a79c94e24d6286d486e) --- .../transaction_deletion_record.py | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py index 4d6463ca5e53..bc30c3f505c2 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py @@ -129,31 +129,40 @@ def on_submit(self): def on_cancel(self): self.db_set("status", "Cancelled") - def chain_call(self, task=None): + def enqueue_task(self, task: str | None = None): if task and task in self.task_to_internal_method_map: # make sure that none of next tasks are already running job_names = self.generate_job_name_for_next_tasks(task=task) self.validate_running_task_for_doc(job_names=job_names) - method = self.task_to_internal_method_map[task] + # method = self.task_to_internal_method_map[task] + # Generate Job Id to uniquely identify each task for this document job_id = self.generate_job_name_for_task(task) frappe.enqueue( "frappe.utils.background_jobs.run_doc_method", doctype=self.doctype, name=self.name, - doc_method=method, + doc_method="execute_task", job_id=job_id, queue="long", enqueue_after_commit=True, + task_to_execute=task, ) + def execute_task(self, task_to_execute: str | None = None): + if task_to_execute: + pass + method = self.task_to_internal_method_map[task_to_execute] + if task := getattr(self, method, None): + task() + def delete_notifications(self): self.validate_doc_status() if not self.clear_notifications: clear_notifications() self.db_set("clear_notifications", 1) - self.chain_call(task="Initialize Summary Table") + self.enqueue_task(task="Initialize Summary Table") def populate_doctypes_to_be_ignored_table(self): doctypes_to_be_ignored_list = get_doctypes_to_be_ignored() @@ -186,7 +195,7 @@ def validate_doc_status(self): def process_tasks(self): # This method is the entry point for the chain of events that follow self.db_set("status", "Running") - self.chain_call(task="Delete Bins") + self.enqueue_task(task="Delete Bins") def delete_bins(self): self.validate_doc_status() @@ -197,7 +206,7 @@ def delete_bins(self): self.company, ) self.db_set("delete_bin_data", 1) - self.chain_call(task="Delete Leads and Addresses") + self.enqueue_task(task="Delete Leads and Addresses") def delete_lead_addresses(self): """Delete addresses to which leads are linked""" @@ -239,7 +248,7 @@ def delete_lead_addresses(self): ) ) self.db_set("delete_leads_and_addresses", 1) - self.chain_call(task="Reset Company Values") + self.enqueue_task(task="Reset Company Values") def reset_company_values(self): self.validate_doc_status() @@ -249,7 +258,7 @@ def reset_company_values(self): company_obj.sales_monthly_history = None company_obj.save() self.db_set("reset_company_default_values", 1) - self.chain_call(task="Clear Notifications") + self.enqueue_task(task="Clear Notifications") def initialize_doctypes_to_be_deleted_table(self): self.validate_doc_status() @@ -266,7 +275,7 @@ def initialize_doctypes_to_be_deleted_table(self): # Initialize self.populate_doctypes_table(tables, docfield["parent"], docfield["fieldname"], 0) self.db_set("initialize_doctypes_table", 1) - self.chain_call(task="Delete Transactions") + self.enqueue_task(task="Delete Transactions") def delete_company_transactions(self): self.validate_doc_status() @@ -307,7 +316,8 @@ def delete_company_transactions(self): ) if pending_doctypes: # as method is enqueued after commit, calling itself will not make validate_doc_status to throw - self.chain_call(task="Delete Transactions") + # recursively call this task to delete all transactions + self.enqueue_task(task="Delete Transactions") else: self.db_set("status", "Completed") self.db_set("delete_transactions", 1) From 1110dd93ff06cffc0f5e537bb552356aa1dd3bb4 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 19 Mar 2024 11:18:11 +0530 Subject: [PATCH 20/30] chore: code cleanup (cherry picked from commit eea260b9f9c4a4a3a5a716b1d74a45c7569d4f98) --- .../transaction_deletion_record.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py index bc30c3f505c2..d66158e9e50b 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py @@ -135,7 +135,6 @@ def enqueue_task(self, task: str | None = None): job_names = self.generate_job_name_for_next_tasks(task=task) self.validate_running_task_for_doc(job_names=job_names) - # method = self.task_to_internal_method_map[task] # Generate Job Id to uniquely identify each task for this document job_id = self.generate_job_name_for_task(task) @@ -152,7 +151,6 @@ def enqueue_task(self, task: str | None = None): def execute_task(self, task_to_execute: str | None = None): if task_to_execute: - pass method = self.task_to_internal_method_map[task_to_execute] if task := getattr(self, method, None): task() @@ -312,7 +310,9 @@ def delete_company_transactions(self): frappe.db.set_value(docfield.doctype, docfield.name, "done", 1) pending_doctypes = frappe.db.get_all( - docfield.doctype, filters={"parent": self.name, "done": 0}, pluck="doctype_name" + "Transaction Deletion Record Details", + filters={"parent": self.name, "done": 0}, + pluck="doctype_name", ) if pending_doctypes: # as method is enqueued after commit, calling itself will not make validate_doc_status to throw From 5e590389f37f34df5b7811661acc30cb4304b788 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 19 Mar 2024 17:39:20 +0530 Subject: [PATCH 21/30] refactor: exception propogation (cherry picked from commit 4a55240e630fedf1590a2c324528fd5934066d37) --- .../transaction_deletion_record.json | 9 ++++++++- .../transaction_deletion_record.py | 13 ++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json index 6e057ace4a6e..8291a4ffc246 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json @@ -10,6 +10,7 @@ "column_break_txbg", "status", "tasks_section", + "error_log", "delete_bin_data", "delete_leads_and_addresses", "reset_company_default_values", @@ -122,12 +123,18 @@ "label": "Initialize Summary Table", "no_copy": 1, "read_only": 1 + }, + { + "depends_on": "eval: doc.error_log", + "fieldname": "error_log", + "fieldtype": "Long Text", + "label": "Error Log" } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2024-02-05 10:36:34.229864", + "modified": "2024-03-19 17:04:49.369734", "modified_by": "Administrator", "module": "Setup", "name": "Transaction Deletion Record", diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py index d66158e9e50b..af95ce69a082 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py @@ -37,6 +37,7 @@ class TransactionDeletionRecord(Document): delete_transactions: DF.Check doctypes: DF.Table[TransactionDeletionRecordDetails] doctypes_to_be_ignored: DF.Table[TransactionDeletionRecordItem] + error_log: DF.LongText | None initialize_doctypes_table: DF.Check reset_company_default_values: DF.Check status: DF.Literal["Queued", "Running", "Failed", "Completed", "Cancelled"] @@ -149,11 +150,21 @@ def enqueue_task(self, task: str | None = None): task_to_execute=task, ) + # todo: add a non-background job based approach as well + def execute_task(self, task_to_execute: str | None = None): if task_to_execute: method = self.task_to_internal_method_map[task_to_execute] if task := getattr(self, method, None): - task() + try: + task() + except Exception as err: + frappe.db.rollback() + traceback = frappe.get_traceback(with_context=True) + if traceback: + message = "Traceback:
" + traceback + frappe.db.set_value(self.doctype, self.name, "error_log", message) + frappe.db.set_value(self.doctype, self.name, "status", "Failed") def delete_notifications(self): self.validate_doc_status() From 6ae9eb250f5a5578507c4e0a6ddd50d109b752cd Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 19 Mar 2024 17:41:37 +0530 Subject: [PATCH 22/30] refactor: minor UI tweaks (cherry picked from commit 0455d0c46c8a171c0bbbd6f1730f671263cfedc4) --- .../transaction_deletion_record.js | 16 +++++++--------- .../transaction_deletion_record.py | 1 + 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js index ccf09a6c38b0..d543baa3cf60 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js @@ -10,26 +10,24 @@ frappe.ui.form.on("Transaction Deletion Record", { callback: function (r) { doctypes_to_be_ignored_array = r.message; populate_doctypes_to_be_ignored(doctypes_to_be_ignored_array, frm); - frm.refresh_field('doctypes_to_be_ignored'); - } + frm.refresh_field("doctypes_to_be_ignored"); + }, }); } - }, - refresh: function(frm) { - if (frm.doc.docstatus==1 && ['Queued', 'Failed'].find(x => x == frm.doc.status)) { - let execute_btn = __("Start Deletion") + refresh: function (frm) { + if (frm.doc.docstatus == 1 && ["Queued", "Failed"].find((x) => x == frm.doc.status)) { + let execute_btn = frm.doc.status == "Queued" ? __("Start Deletion") : __("Retry"); frm.add_custom_button(execute_btn, () => { // Entry point for chain of events frm.call({ - method: 'process_tasks', - doc: frm.doc + method: "process_tasks", + doc: frm.doc, }); }); } - }, }); diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py index af95ce69a082..c2ea56a96ceb 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py @@ -332,6 +332,7 @@ def delete_company_transactions(self): else: self.db_set("status", "Completed") self.db_set("delete_transactions", 1) + self.db_set("error_log", None) def get_doctypes_to_be_ignored_list(self): singles = frappe.get_all("DocType", filters={"issingle": 1}, pluck="name") From d496a1e58e17edf6b0c6b18bb9e3b06b94e024d2 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 19 Mar 2024 17:49:05 +0530 Subject: [PATCH 23/30] chore: move status and error log to their own section (cherry picked from commit 3cec62d4f872a66557e1b9e56a2207f194e5439f) --- .../transaction_deletion_record.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json index 8291a4ffc246..688b14a808ab 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json @@ -7,10 +7,10 @@ "engine": "InnoDB", "field_order": [ "company", - "column_break_txbg", + "section_break_qpwb", "status", - "tasks_section", "error_log", + "tasks_section", "delete_bin_data", "delete_leads_and_addresses", "reset_company_default_values", @@ -63,10 +63,6 @@ "options": "Queued\nRunning\nFailed\nCompleted\nCancelled", "read_only": 1 }, - { - "fieldname": "column_break_txbg", - "fieldtype": "Column Break" - }, { "fieldname": "section_break_tbej", "fieldtype": "Section Break" @@ -129,12 +125,16 @@ "fieldname": "error_log", "fieldtype": "Long Text", "label": "Error Log" + }, + { + "fieldname": "section_break_qpwb", + "fieldtype": "Section Break" } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2024-03-19 17:04:49.369734", + "modified": "2024-03-19 17:47:04.490196", "modified_by": "Administrator", "module": "Setup", "name": "Transaction Deletion Record", From fab5c1170d4383d7267bf2fbee0f740c616df5e5 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 20 Mar 2024 14:10:08 +0530 Subject: [PATCH 24/30] chore: rename entry point (cherry picked from commit 5fe0b20be108ce55a9519af5c953f7aebeaca93b) --- .../transaction_deletion_record/transaction_deletion_record.js | 2 +- .../transaction_deletion_record/transaction_deletion_record.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js index d543baa3cf60..9aa027841659 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js @@ -23,7 +23,7 @@ frappe.ui.form.on("Transaction Deletion Record", { frm.add_custom_button(execute_btn, () => { // Entry point for chain of events frm.call({ - method: "process_tasks", + method: "start_deletion_tasks", doc: frm.doc, }); }); diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py index c2ea56a96ceb..5d4f95f5b611 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py @@ -201,7 +201,7 @@ def validate_doc_status(self): ) @frappe.whitelist() - def process_tasks(self): + def start_deletion_tasks(self): # This method is the entry point for the chain of events that follow self.db_set("status", "Running") self.enqueue_task(task="Delete Bins") From b429f75d65ca001a6db29b95ff70cf8926d97452 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 20 Mar 2024 14:32:27 +0530 Subject: [PATCH 25/30] refactor: link running doc validation to company master (cherry picked from commit 5a3afea8c772b7167839593ba88f3582a381259c) # Conflicts: # erpnext/setup/doctype/company/company.js # erpnext/setup/doctype/company/company.py --- erpnext/setup/doctype/company/company.js | 43 +++++++++++++++++++ erpnext/setup/doctype/company/company.py | 23 ++++++++++ .../transaction_deletion_record.py | 32 +++++++++----- 3 files changed, 87 insertions(+), 11 deletions(-) diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index 23a55487adce..39aca3506509 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -169,6 +169,7 @@ frappe.ui.form.on("Company", { }, delete_company_transactions: function (frm) { +<<<<<<< HEAD frappe.verify_password(function () { var d = frappe.prompt( { @@ -200,6 +201,48 @@ frappe.ui.form.on("Company", { onerror: function () { frappe.msgprint(__("Wrong Password")); }, +======= + frappe.call({ + method: "erpnext.setup.doctype.transaction_deletion_record.transaction_deletion_record.is_deletion_doc_running", + args: { + company: frm.doc.name, + }, + freeze: true, + callback: function (r) { + if (!r.exc) { + frappe.verify_password(function () { + var d = frappe.prompt( + { + fieldtype: "Data", + fieldname: "company_name", + label: __("Please enter the company name to confirm"), + reqd: 1, + description: __( + "Please make sure you really want to delete all the transactions for this company. Your master data will remain as it is. This action cannot be undone." + ), + }, + function (data) { + if (data.company_name !== frm.doc.name) { + frappe.msgprint(__("Company name not same")); + return; + } + frappe.call({ + method: "erpnext.setup.doctype.company.company.create_transaction_deletion_request", + args: { + company: data.company_name, + }, + freeze: true, + callback: function (r, rt) {}, + onerror: function () { + frappe.msgprint(__("Wrong Password")); + }, + }); + }, + __("Delete all the Transactions for this Company"), + __("Delete") + ); + d.get_primary_btn().addClass("btn-danger"); +>>>>>>> 5a3afea8c7 (refactor: link running doc validation to company master) }); }, __("Delete all the Transactions for this Company"), diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index bea76f19412b..7d861979daa8 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -11,7 +11,11 @@ from frappe.contacts.address_and_contact import load_address_and_contact from frappe.custom.doctype.property_setter.property_setter import make_property_setter from frappe.desk.page.setup_wizard.setup_wizard import make_records +<<<<<<< HEAD from frappe.utils import cint, formatdate, get_timestamp, today +======= +from frappe.utils import cint, formatdate, get_link_to_form, get_timestamp, today +>>>>>>> 5a3afea8c7 (refactor: link running doc validation to company master) from frappe.utils.nestedset import NestedSet, rebuild_tree from erpnext.accounts.doctype.account.account import get_account_currency @@ -812,6 +816,25 @@ def get_default_company_address(name, sort_key="is_primary_address", existing_ad @frappe.whitelist() def create_transaction_deletion_request(company): +<<<<<<< HEAD tdr = frappe.get_doc({"doctype": "Transaction Deletion Record", "company": company}) tdr.insert() tdr.submit() +======= + from erpnext.setup.doctype.transaction_deletion_record.transaction_deletion_record import ( + is_deletion_doc_running, + ) + + is_deletion_doc_running(company) + + tdr = frappe.get_doc({"doctype": "Transaction Deletion Record", "company": company}) + tdr.submit() + tdr.start_deletion_tasks() + + frappe.msgprint( + _("A Transaction Deletion Document: {0} is triggered for {0}").format( + get_link_to_form("Transaction Deletion Record", tdr.name) + ), + frappe.bold(company), + ) +>>>>>>> 5a3afea8c7 (refactor: link running doc validation to company master) diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py index 5d4f95f5b611..2140b519f4cb 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py @@ -477,7 +477,25 @@ def get_doctypes_to_be_ignored(): return doctypes_to_be_ignored +@frappe.whitelist() +def is_deletion_doc_running(company: str | None = None, err_msg: str | None = None): + if company: + if running_deletion_jobs := frappe.db.get_all( + "Transaction Deletion Record", + filters={"docstatus": 1, "company": company, "status": "Running"}, + ): + if not err_msg: + err_msg = "" + frappe.throw( + title=_("Deletion in Progress!"), + msg=_("Transaction Deletion Document: {0} is running for this Company. {1}").format( + get_link_to_form("Transaction Deletion Record", running_deletion_jobs[0].name), err_msg + ), + ) + + def check_for_running_deletion_job(doc, method=None): + # Check if DocType has 'company' field df = qb.DocType("DocField") if ( not_allowed := qb.from_(df) @@ -485,14 +503,6 @@ def check_for_running_deletion_job(doc, method=None): .where((df.fieldname == "company") & (df.parent == doc.doctype)) .run() ): - if running_deletion_jobs := frappe.db.get_all( - "Transaction Deletion Record", - filters={"docstatus": 1, "company": doc.company, "status": "Running"}, - ): - frappe.throw( - _( - "Transaction Deletion job {0} is running for this Company. Cannot make any transactions until the deletion job is completed" - ).format( - get_link_to_form("Transaction Deletion Record", running_deletion_jobs[0].name) - ) - ) + is_deletion_doc_running( + doc.company, _("Cannot make any transactions until the deletion job is completed") + ) From 1fe14334f33465cc2e2e4eb188e6dad1aa434f2e Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 20 Mar 2024 14:59:19 +0530 Subject: [PATCH 26/30] refactor: ability to process in single transaction (cherry picked from commit a158b825d7eb359a66743b3e6972aa1b81389df0) --- .../transaction_deletion_record.json | 11 ++++++-- .../transaction_deletion_record.py | 26 ++++++++++--------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json index 688b14a808ab..e03e1695e0ef 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json @@ -20,7 +20,8 @@ "section_break_tbej", "doctypes", "doctypes_to_be_ignored", - "amended_from" + "amended_from", + "process_in_single_transaction" ], "fields": [ { @@ -129,12 +130,18 @@ { "fieldname": "section_break_qpwb", "fieldtype": "Section Break" + }, + { + "default": "0", + "fieldname": "process_in_single_transaction", + "fieldtype": "Check", + "label": "Process in Single Transaction" } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2024-03-19 17:47:04.490196", + "modified": "2024-03-20 14:58:15.086360", "modified_by": "Administrator", "module": "Setup", "name": "Transaction Deletion Record", diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py index 2140b519f4cb..a144525fe13a 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py @@ -39,6 +39,7 @@ class TransactionDeletionRecord(Document): doctypes_to_be_ignored: DF.Table[TransactionDeletionRecordItem] error_log: DF.LongText | None initialize_doctypes_table: DF.Check + process_in_single_transaction: DF.Check reset_company_default_values: DF.Check status: DF.Literal["Queued", "Running", "Failed", "Completed", "Cancelled"] # end: auto-generated types @@ -139,18 +140,19 @@ def enqueue_task(self, task: str | None = None): # Generate Job Id to uniquely identify each task for this document job_id = self.generate_job_name_for_task(task) - frappe.enqueue( - "frappe.utils.background_jobs.run_doc_method", - doctype=self.doctype, - name=self.name, - doc_method="execute_task", - job_id=job_id, - queue="long", - enqueue_after_commit=True, - task_to_execute=task, - ) - - # todo: add a non-background job based approach as well + if self.process_in_single_transaction: + self.execute_task(task_to_execute=task) + else: + frappe.enqueue( + "frappe.utils.background_jobs.run_doc_method", + doctype=self.doctype, + name=self.name, + doc_method="execute_task", + job_id=job_id, + queue="long", + enqueue_after_commit=True, + task_to_execute=task, + ) def execute_task(self, task_to_execute: str | None = None): if task_to_execute: From 35fcd032ad334e5efb0fce41373e6c59db9a54ed Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 20 Mar 2024 15:16:33 +0530 Subject: [PATCH 27/30] refactor(test): test cases modified to handle new approach (cherry picked from commit 81309576b0cb41ac2c91cf1abbf79b4655c7697d) # Conflicts: # erpnext/setup/demo.py # erpnext/setup/doctype/transaction_deletion_record/test_transaction_deletion_record.py --- erpnext/setup/demo.py | 228 ++++++++++++++++++ .../test_transaction_deletion_record.py | 7 + 2 files changed, 235 insertions(+) create mode 100644 erpnext/setup/demo.py diff --git a/erpnext/setup/demo.py b/erpnext/setup/demo.py new file mode 100644 index 000000000000..f48402e175b4 --- /dev/null +++ b/erpnext/setup/demo.py @@ -0,0 +1,228 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import json +import os +from random import randint + +import frappe +from frappe import _ +from frappe.utils import add_days, getdate + +from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry +from erpnext.accounts.utils import get_fiscal_year +from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_invoice +from erpnext.selling.doctype.sales_order.sales_order import make_sales_invoice +from erpnext.setup.setup_wizard.operations.install_fixtures import create_bank_account + + +def setup_demo_data(): + from frappe.utils.telemetry import capture + + capture("demo_data_creation_started", "erpnext") + try: + company = create_demo_company() + process_masters() + make_transactions(company) + frappe.cache.delete_keys("bootinfo") + frappe.publish_realtime("demo_data_complete") + except Exception: + frappe.log_error("Failed to create demo data") + capture("demo_data_creation_failed", "erpnext", properties={"exception": frappe.get_traceback()}) + raise + capture("demo_data_creation_completed", "erpnext") + + +@frappe.whitelist() +def clear_demo_data(): + from frappe.utils.telemetry import capture + + frappe.only_for("System Manager") + + capture("demo_data_erased", "erpnext") + try: + company = frappe.db.get_single_value("Global Defaults", "demo_company") + create_transaction_deletion_record(company) + clear_masters() + delete_company(company) + default_company = frappe.db.get_single_value("Global Defaults", "default_company") + frappe.db.set_default("company", default_company) + except Exception: + frappe.db.rollback() + frappe.log_error("Failed to erase demo data") + frappe.throw( + _("Failed to erase demo data, please delete the demo company manually."), + title=_("Could Not Delete Demo Data"), + ) + + +def create_demo_company(): + company = frappe.db.get_all("Company")[0].name + company_doc = frappe.get_doc("Company", company) + + # Make a dummy company + new_company = frappe.new_doc("Company") + new_company.company_name = company_doc.company_name + " (Demo)" + new_company.abbr = company_doc.abbr + "D" + new_company.enable_perpetual_inventory = 1 + new_company.default_currency = company_doc.default_currency + new_company.country = company_doc.country + new_company.chart_of_accounts_based_on = "Standard Template" + new_company.chart_of_accounts = company_doc.chart_of_accounts + new_company.insert() + + # Set Demo Company as default to + frappe.db.set_single_value("Global Defaults", "demo_company", new_company.name) + frappe.db.set_default("company", new_company.name) + + bank_account = create_bank_account({"company_name": new_company.name}) + frappe.db.set_value("Company", new_company.name, "default_bank_account", bank_account.name) + + return new_company.name + + +def process_masters(): + for doctype in frappe.get_hooks("demo_master_doctypes"): + data = read_data_file_using_hooks(doctype) + if data: + for item in json.loads(data): + create_demo_record(item) + + +def create_demo_record(doctype): + frappe.get_doc(doctype).insert(ignore_permissions=True) + + +def make_transactions(company): + frappe.db.set_single_value("Stock Settings", "allow_negative_stock", 1) + from erpnext.accounts.utils import FiscalYearError + + try: + start_date = get_fiscal_year(date=getdate())[1] + except FiscalYearError: + # User might have setup fiscal year for previous or upcoming years + active_fiscal_years = frappe.db.get_all("Fiscal Year", filters={"disabled": 0}, as_list=1) + if active_fiscal_years: + start_date = frappe.db.get_value("Fiscal Year", active_fiscal_years[0][0], "year_start_date") + else: + frappe.throw(_("There are no active Fiscal Years for which Demo Data can be generated.")) + + for doctype in frappe.get_hooks("demo_transaction_doctypes"): + data = read_data_file_using_hooks(doctype) + if data: + for item in json.loads(data): + create_transaction(item, company, start_date) + + convert_order_to_invoices() + frappe.db.set_single_value("Stock Settings", "allow_negative_stock", 0) + + +def create_transaction(doctype, company, start_date): + document_type = doctype.get("doctype") + warehouse = get_warehouse(company) + + if document_type == "Purchase Order": + posting_date = get_random_date(start_date, 1, 25) + else: + posting_date = get_random_date(start_date, 31, 350) + + doctype.update( + { + "company": company, + "set_posting_time": 1, + "transaction_date": posting_date, + "schedule_date": posting_date, + "delivery_date": posting_date, + "set_warehouse": warehouse, + } + ) + + doc = frappe.get_doc(doctype) + doc.save(ignore_permissions=True) + doc.submit() + + +def convert_order_to_invoices(): + for document in ["Purchase Order", "Sales Order"]: + # Keep some orders intentionally unbilled/unpaid + for i, order in enumerate( + frappe.db.get_all( + document, filters={"docstatus": 1}, fields=["name", "transaction_date"], limit=6 + ) + ): + + if document == "Purchase Order": + invoice = make_purchase_invoice(order.name) + elif document == "Sales Order": + invoice = make_sales_invoice(order.name) + + invoice.set_posting_time = 1 + invoice.posting_date = order.transaction_date + invoice.due_date = order.transaction_date + invoice.bill_date = order.transaction_date + + if invoice.get("payment_schedule"): + invoice.payment_schedule[0].due_date = order.transaction_date + + invoice.update_stock = 1 + invoice.submit() + + if i % 2 != 0: + payment = get_payment_entry(invoice.doctype, invoice.name) + payment.posting_date = order.transaction_date + payment.reference_no = invoice.name + payment.submit() + + +def get_random_date(start_date, start_range, end_range): + return add_days(start_date, randint(start_range, end_range)) + + +def create_transaction_deletion_record(company): + transaction_deletion_record = frappe.new_doc("Transaction Deletion Record") + transaction_deletion_record.company = company + transaction_deletion_record.process_in_single_transaction = True + transaction_deletion_record.save(ignore_permissions=True) + transaction_deletion_record.submit() + transaction_deletion_record.start_deletion_tasks() + + +def clear_masters(): + for doctype in frappe.get_hooks("demo_master_doctypes")[::-1]: + data = read_data_file_using_hooks(doctype) + if data: + for item in json.loads(data): + clear_demo_record(item) + + +def clear_demo_record(document): + document_type = document.get("doctype") + del document["doctype"] + + valid_columns = frappe.get_meta(document_type).get_valid_columns() + + filters = document + for key in list(filters): + if key not in valid_columns: + filters.pop(key, None) + + doc = frappe.get_doc(document_type, filters) + doc.delete(ignore_permissions=True) + + +def delete_company(company): + frappe.db.set_single_value("Global Defaults", "demo_company", "") + frappe.delete_doc("Company", company, ignore_permissions=True) + + +def read_data_file_using_hooks(doctype): + path = os.path.join(os.path.dirname(__file__), "demo_data") + with open(os.path.join(path, doctype + ".json"), "r") as f: + data = f.read() + + return data + + +def get_warehouse(company): + warehouses = frappe.db.get_all("Warehouse", {"company": company, "is_group": 0}) + return warehouses[randint(0, 3)].name diff --git a/erpnext/setup/doctype/transaction_deletion_record/test_transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/test_transaction_deletion_record.py index 319d435ca69f..78d6ea67bff5 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/test_transaction_deletion_record.py +++ b/erpnext/setup/doctype/transaction_deletion_record/test_transaction_deletion_record.py @@ -27,7 +27,12 @@ def test_doctypes_contain_company_field(self): def test_no_of_docs_is_correct(self): for i in range(5): create_task("Dunder Mifflin Paper Co") +<<<<<<< HEAD tdr = create_transaction_deletion_request("Dunder Mifflin Paper Co") +======= + tdr = create_transaction_deletion_doc("Dunder Mifflin Paper Co") + tdr.reload() +>>>>>>> 81309576b0 (refactor(test): test cases modified to handle new approach) for doctype in tdr.doctypes: if doctype.doctype_name == "Task": self.assertEqual(doctype.no_of_docs, 5) @@ -49,7 +54,9 @@ def create_company(company_name): def create_transaction_deletion_request(company): tdr = frappe.get_doc({"doctype": "Transaction Deletion Record", "company": company}) tdr.insert() + tdr.process_in_single_transaction = True tdr.submit() + tdr.start_deletion_tasks() return tdr From e5722a772a7cbdb17bc9023555a574c85637ebe6 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 20 Mar 2024 16:01:09 +0530 Subject: [PATCH 28/30] chore: fix linting issue in JS (cherry picked from commit 02c522b7cddf0b1ae4bcc1d05e156a5b7aa09f2f) --- .../transaction_deletion_record_list.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record_list.js b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record_list.js index 7c7b8ff25a72..285cb6dd2211 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record_list.js +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record_list.js @@ -1,16 +1,16 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -frappe.listview_settings['Transaction Deletion Record'] = { +frappe.listview_settings["Transaction Deletion Record"] = { add_fields: ["status"], - get_indicator: function(doc) { + get_indicator: function (doc) { let colors = { - 'Queued': 'orange', - 'Completed': 'green', - 'Running': 'blue', - 'Failed': 'red', + Queued: "orange", + Completed: "green", + Running: "blue", + Failed: "red", }; let status = doc.status; - return [__(status), colors[status], 'status,=,'+status]; + return [__(status), colors[status], "status,=," + status]; }, }; From 87fdb4e7209d34dfa844c0a59cdaf39b594b6996 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 20 Mar 2024 19:48:45 +0530 Subject: [PATCH 29/30] chore: resolve conflicts --- erpnext/setup/doctype/company/company.js | 41 +------------------ erpnext/setup/doctype/company/company.py | 10 ----- .../test_transaction_deletion_record.py | 4 -- .../transaction_deletion_record.py | 33 --------------- .../transaction_deletion_record_item.py | 17 -------- 5 files changed, 2 insertions(+), 103 deletions(-) diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index 39aca3506509..6c237d787bb0 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -169,39 +169,6 @@ frappe.ui.form.on("Company", { }, delete_company_transactions: function (frm) { -<<<<<<< HEAD - frappe.verify_password(function () { - var d = frappe.prompt( - { - fieldtype: "Data", - fieldname: "company_name", - label: __("Please enter the company name to confirm"), - reqd: 1, - description: __( - "Please make sure you really want to delete all the transactions for this company. Your master data will remain as it is. This action cannot be undone." - ), - }, - function (data) { - if (data.company_name !== frm.doc.name) { - frappe.msgprint(__("Company name not same")); - return; - } - frappe.call({ - method: "erpnext.setup.doctype.company.company.create_transaction_deletion_request", - args: { - company: data.company_name, - }, - freeze: true, - callback: function (r, rt) { - if (!r.exc) - frappe.msgprint( - __("Successfully deleted all transactions related to this company!") - ); - }, - onerror: function () { - frappe.msgprint(__("Wrong Password")); - }, -======= frappe.call({ method: "erpnext.setup.doctype.transaction_deletion_record.transaction_deletion_record.is_deletion_doc_running", args: { @@ -242,13 +209,9 @@ frappe.ui.form.on("Company", { __("Delete") ); d.get_primary_btn().addClass("btn-danger"); ->>>>>>> 5a3afea8c7 (refactor: link running doc validation to company master) }); - }, - __("Delete all the Transactions for this Company"), - __("Delete") - ); - d.get_primary_btn().addClass("btn-danger"); + } + }, }); }, }); diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index 7d861979daa8..5ee7dbb4b47e 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -11,11 +11,7 @@ from frappe.contacts.address_and_contact import load_address_and_contact from frappe.custom.doctype.property_setter.property_setter import make_property_setter from frappe.desk.page.setup_wizard.setup_wizard import make_records -<<<<<<< HEAD -from frappe.utils import cint, formatdate, get_timestamp, today -======= from frappe.utils import cint, formatdate, get_link_to_form, get_timestamp, today ->>>>>>> 5a3afea8c7 (refactor: link running doc validation to company master) from frappe.utils.nestedset import NestedSet, rebuild_tree from erpnext.accounts.doctype.account.account import get_account_currency @@ -816,11 +812,6 @@ def get_default_company_address(name, sort_key="is_primary_address", existing_ad @frappe.whitelist() def create_transaction_deletion_request(company): -<<<<<<< HEAD - tdr = frappe.get_doc({"doctype": "Transaction Deletion Record", "company": company}) - tdr.insert() - tdr.submit() -======= from erpnext.setup.doctype.transaction_deletion_record.transaction_deletion_record import ( is_deletion_doc_running, ) @@ -837,4 +828,3 @@ def create_transaction_deletion_request(company): ), frappe.bold(company), ) ->>>>>>> 5a3afea8c7 (refactor: link running doc validation to company master) diff --git a/erpnext/setup/doctype/transaction_deletion_record/test_transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/test_transaction_deletion_record.py index 78d6ea67bff5..24a12bac9fef 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/test_transaction_deletion_record.py +++ b/erpnext/setup/doctype/transaction_deletion_record/test_transaction_deletion_record.py @@ -27,12 +27,8 @@ def test_doctypes_contain_company_field(self): def test_no_of_docs_is_correct(self): for i in range(5): create_task("Dunder Mifflin Paper Co") -<<<<<<< HEAD tdr = create_transaction_deletion_request("Dunder Mifflin Paper Co") -======= - tdr = create_transaction_deletion_doc("Dunder Mifflin Paper Co") tdr.reload() ->>>>>>> 81309576b0 (refactor(test): test cases modified to handle new approach) for doctype in tdr.doctypes: if doctype.doctype_name == "Task": self.assertEqual(doctype.no_of_docs, 5) diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py index a144525fe13a..f1eb4e6a9b93 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py @@ -12,39 +12,6 @@ class TransactionDeletionRecord(Document): -<<<<<<< HEAD -======= - # begin: auto-generated types - # This code is auto-generated. Do not modify anything in this block. - - from typing import TYPE_CHECKING - - if TYPE_CHECKING: - from frappe.types import DF - - from erpnext.accounts.doctype.transaction_deletion_record_details.transaction_deletion_record_details import ( - TransactionDeletionRecordDetails, - ) - from erpnext.setup.doctype.transaction_deletion_record_item.transaction_deletion_record_item import ( - TransactionDeletionRecordItem, - ) - - amended_from: DF.Link | None - clear_notifications: DF.Check - company: DF.Link - delete_bin_data: DF.Check - delete_leads_and_addresses: DF.Check - delete_transactions: DF.Check - doctypes: DF.Table[TransactionDeletionRecordDetails] - doctypes_to_be_ignored: DF.Table[TransactionDeletionRecordItem] - error_log: DF.LongText | None - initialize_doctypes_table: DF.Check - process_in_single_transaction: DF.Check - reset_company_default_values: DF.Check - status: DF.Literal["Queued", "Running", "Failed", "Completed", "Cancelled"] - # end: auto-generated types - ->>>>>>> 0d65d878de (refactor: more options for 'status' and move it to top) def __init__(self, *args, **kwargs): super(TransactionDeletionRecord, self).__init__(*args, **kwargs) self.batch_size = 5000 diff --git a/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.py b/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.py index ed01afe70f94..92ca8a2ac730 100644 --- a/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.py +++ b/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.py @@ -7,21 +7,4 @@ class TransactionDeletionRecordItem(Document): -<<<<<<< HEAD -======= - # begin: auto-generated types - # This code is auto-generated. Do not modify anything in this block. - - from typing import TYPE_CHECKING - - if TYPE_CHECKING: - from frappe.types import DF - - doctype_name: DF.Link - parent: DF.Data - parentfield: DF.Data - parenttype: DF.Data - # end: auto-generated types - ->>>>>>> 6a77d86a53 (refactor: use flags to decide on current stage) pass From 1515bb7f0b90d1a011e3705e5de83928351be5cf Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 21 Mar 2024 09:53:55 +0530 Subject: [PATCH 30/30] refactor: replace get_job with create_job_id utility method --- .../transaction_deletion_record.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py index f1eb4e6a9b93..db5024bbc199 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py @@ -8,7 +8,7 @@ from frappe.desk.notifications import clear_notifications from frappe.model.document import Document from frappe.utils import cint, comma_and, create_batch, get_link_to_form -from frappe.utils.background_jobs import get_job, is_job_enqueued +from frappe.utils.background_jobs import create_job_id, is_job_enqueued class TransactionDeletionRecord(Document): @@ -152,7 +152,7 @@ def validate_running_task_for_doc(self, job_names: list = None): running_tasks = [] for x in job_names: if is_job_enqueued(x): - running_tasks.append(get_job(x).get_id()) + running_tasks.append(create_job_id(x)) if running_tasks: frappe.throw(