From c856c743b4f849d04d99175c2e1dee076df21885 Mon Sep 17 00:00:00 2001 From: Hussain Nagaria Date: Sat, 8 Nov 2025 10:59:34 +0530 Subject: [PATCH 01/13] chore: add communication date field to Communication Link --- .../communication_link/communication_link.json | 14 +++++++++++--- .../communication_link/communication_link.py | 1 + 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/frappe/core/doctype/communication_link/communication_link.json b/frappe/core/doctype/communication_link/communication_link.json index 9a6b5dcff3..5a61a8e05e 100644 --- a/frappe/core/doctype/communication_link/communication_link.json +++ b/frappe/core/doctype/communication_link/communication_link.json @@ -7,7 +7,8 @@ "field_order": [ "link_doctype", "link_name", - "link_title" + "link_title", + "communication_date" ], "fields": [ { @@ -32,19 +33,26 @@ "in_list_view": 1, "label": "Link Title", "read_only": 1 + }, + { + "fieldname": "communication_date", + "fieldtype": "Date", + "label": "Communication Date", + "read_only": 1 } ], "istable": 1, "links": [], - "modified": "2024-03-23 16:01:30.438791", + "modified": "2025-11-08 10:59:03.629772", "modified_by": "Administrator", "module": "Core", "name": "Communication Link", "owner": "Administrator", "permissions": [], "quick_entry": 1, + "row_format": "Dynamic", "sort_field": "creation", "sort_order": "DESC", "states": [], "track_changes": 1 -} \ No newline at end of file +} diff --git a/frappe/core/doctype/communication_link/communication_link.py b/frappe/core/doctype/communication_link/communication_link.py index f6e2a52659..841418ff75 100644 --- a/frappe/core/doctype/communication_link/communication_link.py +++ b/frappe/core/doctype/communication_link/communication_link.py @@ -14,6 +14,7 @@ class CommunicationLink(Document): if TYPE_CHECKING: from frappe.types import DF + communication_date: DF.Date | None link_doctype: DF.Link link_name: DF.DynamicLink link_title: DF.ReadOnly | None From 7ba90374db0af216e36dd45fd4bf56ebe515b46b Mon Sep 17 00:00:00 2001 From: Hussain Nagaria Date: Sat, 8 Nov 2025 11:22:35 +0530 Subject: [PATCH 02/13] fix: it should be datetime --- .../core/doctype/communication_link/communication_link.json | 4 ++-- frappe/core/doctype/communication_link/communication_link.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frappe/core/doctype/communication_link/communication_link.json b/frappe/core/doctype/communication_link/communication_link.json index 5a61a8e05e..c912910c29 100644 --- a/frappe/core/doctype/communication_link/communication_link.json +++ b/frappe/core/doctype/communication_link/communication_link.json @@ -36,14 +36,14 @@ }, { "fieldname": "communication_date", - "fieldtype": "Date", + "fieldtype": "Datetime", "label": "Communication Date", "read_only": 1 } ], "istable": 1, "links": [], - "modified": "2025-11-08 10:59:03.629772", + "modified": "2025-11-08 11:07:13.960236", "modified_by": "Administrator", "module": "Core", "name": "Communication Link", diff --git a/frappe/core/doctype/communication_link/communication_link.py b/frappe/core/doctype/communication_link/communication_link.py index 841418ff75..cbabde3716 100644 --- a/frappe/core/doctype/communication_link/communication_link.py +++ b/frappe/core/doctype/communication_link/communication_link.py @@ -14,7 +14,7 @@ class CommunicationLink(Document): if TYPE_CHECKING: from frappe.types import DF - communication_date: DF.Date | None + communication_date: DF.Datetime | None link_doctype: DF.Link link_name: DF.DynamicLink link_title: DF.ReadOnly | None From 295a7008b9f862eaf1ff986dc68dfa0150b483ac Mon Sep 17 00:00:00 2001 From: Hussain Nagaria Date: Sat, 8 Nov 2025 11:23:29 +0530 Subject: [PATCH 03/13] chore: patch to copy communication date to link --- .../doctype/communication_link/patches/__init__.py | 0 .../patches/copy_communication_date_to_link.py | 13 +++++++++++++ frappe/patches.txt | 1 + 3 files changed, 14 insertions(+) create mode 100644 frappe/core/doctype/communication_link/patches/__init__.py create mode 100644 frappe/core/doctype/communication_link/patches/copy_communication_date_to_link.py diff --git a/frappe/core/doctype/communication_link/patches/__init__.py b/frappe/core/doctype/communication_link/patches/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/core/doctype/communication_link/patches/copy_communication_date_to_link.py b/frappe/core/doctype/communication_link/patches/copy_communication_date_to_link.py new file mode 100644 index 0000000000..29e20fd78c --- /dev/null +++ b/frappe/core/doctype/communication_link/patches/copy_communication_date_to_link.py @@ -0,0 +1,13 @@ +import frappe + + +# copy communication_date from Communication to Communication Link +def execute(): + frappe.db.sql( + """ + update `tabCommunication Link` cl + inner join `tabCommunication` c on cl.parent = c.name + set cl.communication_date = c.communication_date + where c.communication_date is not null + """ + ) diff --git a/frappe/patches.txt b/frappe/patches.txt index b6374edd7b..6efb4cb759 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -247,3 +247,4 @@ frappe.patches.v14_0.fix_user_settings_collation execute:frappe.call("frappe.core.doctype.system_settings.system_settings.sync_system_settings") frappe.patches.v15_0.migrate_to_utm frappe.patches.v16_0.add_module_deprecation_warning +frappe.core.doctype.communication_link.patches.copy_communication_date_to_link From 3c5affd3591efdc986eddb9ac67578748750afec Mon Sep 17 00:00:00 2001 From: Hussain Nagaria Date: Wed, 12 Nov 2025 12:44:21 +0530 Subject: [PATCH 04/13] refactor: use CTEs to get merged results --- frappe/desk/form/load.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/frappe/desk/form/load.py b/frappe/desk/form/load.py index d798b42d45..f70b6f1785 100644 --- a/frappe/desk/form/load.py +++ b/frappe/desk/form/load.py @@ -290,6 +290,8 @@ def get_communication_data( WHERE C.communication_type IN ('Communication', 'Automated Message') AND (C.reference_doctype = %(doctype)s AND C.reference_name = %(name)s) {conditions} + ORDER BY C.communication_date DESC + LIMIT %(limit)s """ # communications linked in Timeline Links @@ -300,6 +302,8 @@ def get_communication_data( WHERE C.communication_type IN ('Communication', 'Automated Message') AND `tabCommunication Link`.link_doctype = %(doctype)s AND `tabCommunication Link`.link_name = %(name)s {conditions} + ORDER BY `tabCommunication Link`.communication_date DESC + LIMIT %(limit)s """ sqlite_query = f""" @@ -314,8 +318,13 @@ def get_communication_data( OFFSET %(start)s""" query = f""" + WITH part1 AS ({part1}), part2 AS ({part2}) SELECT * - FROM (({part1}) UNION ({part2})) AS combined + FROM ( + SELECT * FROM part1 + UNION + SELECT * FROM part2 + ) AS combined {group_by or ""} ORDER BY communication_date DESC LIMIT %(limit)s From f284ec6dadc84f0cceb24dcdce84ba868f5805d5 Mon Sep 17 00:00:00 2001 From: Hussain Nagaria Date: Wed, 12 Nov 2025 12:47:04 +0530 Subject: [PATCH 05/13] perf: better index --- frappe/core/doctype/communication/communication.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frappe/core/doctype/communication/communication.py b/frappe/core/doctype/communication/communication.py index 88d1e5c32f..4e68bd8307 100644 --- a/frappe/core/doctype/communication/communication.py +++ b/frappe/core/doctype/communication/communication.py @@ -466,9 +466,13 @@ class Communication(Document, CommunicationEmailMixin): def on_doctype_update(): """Add indexes in `tabCommunication`""" - frappe.db.add_index("Communication", ["reference_doctype", "reference_name"]) frappe.db.add_index("Communication", ["status", "communication_type"]) frappe.db.add_index("Communication", ["message_id(140)"]) + frappe.db.add_index( + "Communication", + ["reference_doctype", "reference_name", "communication_date DESC", "communication_type"], + index_name="comm_ref_type_date_idx", + ) def has_permission(doc, ptype, user=None, debug=False): From 60f7579904c6fb654574e7ffe86716f1ff1b3663 Mon Sep 17 00:00:00 2001 From: Hussain Nagaria Date: Wed, 12 Nov 2025 12:47:44 +0530 Subject: [PATCH 06/13] perf: add index to communication link --- frappe/core/doctype/communication_link/communication_link.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frappe/core/doctype/communication_link/communication_link.py b/frappe/core/doctype/communication_link/communication_link.py index cbabde3716..0e4379c771 100644 --- a/frappe/core/doctype/communication_link/communication_link.py +++ b/frappe/core/doctype/communication_link/communication_link.py @@ -28,3 +28,6 @@ class CommunicationLink(Document): def on_doctype_update(): frappe.db.add_index("Communication Link", ["link_doctype", "link_name"]) + frappe.db.add_index( + "Communication Link", ["link_doctype", "link_name", "communication_date DESC", "parent"] + ) From e1866620fa2c383d8cae8b70e51fecb395c038e2 Mon Sep 17 00:00:00 2001 From: Hussain Nagaria Date: Wed, 12 Nov 2025 13:16:12 +0530 Subject: [PATCH 07/13] fix: set communication date while adding communication link --- frappe/core/doctype/communication/communication.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/frappe/core/doctype/communication/communication.py b/frappe/core/doctype/communication/communication.py index 4e68bd8307..517dd6d861 100644 --- a/frappe/core/doctype/communication/communication.py +++ b/frappe/core/doctype/communication/communication.py @@ -447,7 +447,14 @@ class Communication(Document, CommunicationEmailMixin): self.add_link(doctype, name) def add_link(self, link_doctype, link_name, autosave=False): - self.append("timeline_links", {"link_doctype": link_doctype, "link_name": link_name}) + self.append( + "timeline_links", + { + "link_doctype": link_doctype, + "link_name": link_name, + "communication_date": self.communication_date, + }, + ) if autosave: self.save(ignore_permissions=True) From 637ee313c22aba89855fafb8ed840ac83a5e9aa4 Mon Sep 17 00:00:00 2001 From: Hussain Nagaria Date: Thu, 27 Nov 2025 22:29:51 +0530 Subject: [PATCH 08/13] refactor: desc is not needed --- frappe/core/doctype/communication/communication.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/core/doctype/communication/communication.py b/frappe/core/doctype/communication/communication.py index 517dd6d861..dc242bcff6 100644 --- a/frappe/core/doctype/communication/communication.py +++ b/frappe/core/doctype/communication/communication.py @@ -477,7 +477,7 @@ def on_doctype_update(): frappe.db.add_index("Communication", ["message_id(140)"]) frappe.db.add_index( "Communication", - ["reference_doctype", "reference_name", "communication_date DESC", "communication_type"], + ["reference_doctype", "reference_name", "communication_date", "communication_type"], index_name="comm_ref_type_date_idx", ) From ee94e604b6d5b0e6675451a06c48cb1aad2a0f30 Mon Sep 17 00:00:00 2001 From: Hussain Nagaria Date: Thu, 27 Nov 2025 22:32:44 +0530 Subject: [PATCH 09/13] refactor: remove DESC from Communication Link index too --- frappe/core/doctype/communication_link/communication_link.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/frappe/core/doctype/communication_link/communication_link.py b/frappe/core/doctype/communication_link/communication_link.py index 0e4379c771..38d8733b4d 100644 --- a/frappe/core/doctype/communication_link/communication_link.py +++ b/frappe/core/doctype/communication_link/communication_link.py @@ -28,6 +28,4 @@ class CommunicationLink(Document): def on_doctype_update(): frappe.db.add_index("Communication Link", ["link_doctype", "link_name"]) - frappe.db.add_index( - "Communication Link", ["link_doctype", "link_name", "communication_date DESC", "parent"] - ) + frappe.db.add_index("Communication Link", ["link_doctype", "link_name", "communication_date", "parent"]) From 2b54da021911b5ac33ccd037cb23c02c9f5a52b5 Mon Sep 17 00:00:00 2001 From: Hussain Nagaria Date: Thu, 27 Nov 2025 22:36:57 +0530 Subject: [PATCH 10/13] fix: select correct number of rows in CTEs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thanks @ankush for helping with this competetive coding task 😂 --- frappe/desk/form/load.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/desk/form/load.py b/frappe/desk/form/load.py index 094dc5250a..1776c42203 100644 --- a/frappe/desk/form/load.py +++ b/frappe/desk/form/load.py @@ -293,7 +293,7 @@ def get_communication_data( AND (C.reference_doctype = %(doctype)s AND C.reference_name = %(name)s) {conditions} ORDER BY C.communication_date DESC - LIMIT %(limit)s + LIMIT %({start + limit})s """ # communications linked in Timeline Links @@ -305,7 +305,7 @@ def get_communication_data( AND `tabCommunication Link`.link_doctype = %(doctype)s AND `tabCommunication Link`.link_name = %(name)s {conditions} ORDER BY `tabCommunication Link`.communication_date DESC - LIMIT %(limit)s + LIMIT %({start + limit})s """ sqlite_query = f""" From 5fd66915489ef25b90feed464b48b2d30b1c29f7 Mon Sep 17 00:00:00 2001 From: Hussain Nagaria Date: Fri, 28 Nov 2025 11:40:05 +0530 Subject: [PATCH 11/13] fix: query placeholders --- frappe/desk/form/load.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frappe/desk/form/load.py b/frappe/desk/form/load.py index 1776c42203..49d05a6e73 100644 --- a/frappe/desk/form/load.py +++ b/frappe/desk/form/load.py @@ -293,7 +293,7 @@ def get_communication_data( AND (C.reference_doctype = %(doctype)s AND C.reference_name = %(name)s) {conditions} ORDER BY C.communication_date DESC - LIMIT %({start + limit})s + LIMIT %(cte_limit)s """ # communications linked in Timeline Links @@ -305,7 +305,7 @@ def get_communication_data( AND `tabCommunication Link`.link_doctype = %(doctype)s AND `tabCommunication Link`.link_name = %(name)s {conditions} ORDER BY `tabCommunication Link`.communication_date DESC - LIMIT %({start + limit})s + LIMIT %(cte_limit)s """ sqlite_query = f""" @@ -344,6 +344,7 @@ def get_communication_data( name=str(name), start=frappe.utils.cint(start), limit=limit, + cte_limit=limit + start, ), as_dict=as_dict, ) From 01d2e24f4a3e37c0d6a5d541432025c97e73003c Mon Sep 17 00:00:00 2001 From: Hussain Nagaria Date: Fri, 28 Nov 2025 11:48:55 +0530 Subject: [PATCH 12/13] chore: patch to drop old index * there is a better covering index now --- frappe/core/doctype/communication/patches/__init__.py | 0 .../doctype/communication/patches/drop_ref_dt_dn_index.py | 8 ++++++++ frappe/patches.txt | 1 + 3 files changed, 9 insertions(+) create mode 100644 frappe/core/doctype/communication/patches/__init__.py create mode 100644 frappe/core/doctype/communication/patches/drop_ref_dt_dn_index.py diff --git a/frappe/core/doctype/communication/patches/__init__.py b/frappe/core/doctype/communication/patches/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/core/doctype/communication/patches/drop_ref_dt_dn_index.py b/frappe/core/doctype/communication/patches/drop_ref_dt_dn_index.py new file mode 100644 index 0000000000..c1c3fa995b --- /dev/null +++ b/frappe/core/doctype/communication/patches/drop_ref_dt_dn_index.py @@ -0,0 +1,8 @@ +import frappe +from frappe.patches.v14_0.drop_unused_indexes import drop_index_if_exists + + +def execute(): + index_fields = ["reference_doctype", "reference_name"] + index_name = frappe.db.get_index_name(index_fields) + drop_index_if_exists("tabCommunication", index_name) diff --git a/frappe/patches.txt b/frappe/patches.txt index 8113d8db1a..5b076fab59 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -251,3 +251,4 @@ frappe.patches.v16_0.add_module_deprecation_warning frappe.patches.v16_0.auto_generate_desktop_icon_and_sidebar frappe.patches.v16_0.add_private_workspaces_to_sidebar frappe.core.doctype.communication_link.patches.copy_communication_date_to_link +frappe.core.doctype.communication.patches.drop_ref_dt_dn_index From de7d1abcf44766bb4a17c99d4d907b55caaa6d7a Mon Sep 17 00:00:00 2001 From: Hussain Nagaria Date: Fri, 28 Nov 2025 12:09:13 +0530 Subject: [PATCH 13/13] refactor: move drop index util to db utils file --- .../patches/drop_ref_dt_dn_index.py | 2 +- frappe/database/utils.py | 17 +++++++++++++++++ frappe/patches/v14_0/drop_unused_indexes.py | 18 +----------------- frappe/patches/v15_0/drop_modified_index.py | 2 +- 4 files changed, 20 insertions(+), 19 deletions(-) diff --git a/frappe/core/doctype/communication/patches/drop_ref_dt_dn_index.py b/frappe/core/doctype/communication/patches/drop_ref_dt_dn_index.py index c1c3fa995b..2afe5e9f7c 100644 --- a/frappe/core/doctype/communication/patches/drop_ref_dt_dn_index.py +++ b/frappe/core/doctype/communication/patches/drop_ref_dt_dn_index.py @@ -1,5 +1,5 @@ import frappe -from frappe.patches.v14_0.drop_unused_indexes import drop_index_if_exists +from frappe.database.utils import drop_index_if_exists def execute(): diff --git a/frappe/database/utils.py b/frappe/database/utils.py index 0ceb9bd440..3165046faf 100644 --- a/frappe/database/utils.py +++ b/frappe/database/utils.py @@ -183,3 +183,20 @@ def commit_after_response(func): request.after_response.add(callback_manager.run) callback_manager.add(func) + + +def drop_index_if_exists(table: str, index: str): + import click + + if not frappe.db.has_index(table, index): + click.echo(f"- Skipped {index} index for {table} because it doesn't exist") + return + + try: + frappe.db.sql_ddl(f"ALTER TABLE `{table}` DROP INDEX `{index}`") + except Exception as e: + frappe.log_error("Failed to drop index") + click.secho(f"x Failed to drop index {index} from {table}\n {e!s}", fg="red") + return + + click.echo(f"✓ dropped {index} index from {table}") diff --git a/frappe/patches/v14_0/drop_unused_indexes.py b/frappe/patches/v14_0/drop_unused_indexes.py index a2b3f468c2..711ba39f15 100644 --- a/frappe/patches/v14_0/drop_unused_indexes.py +++ b/frappe/patches/v14_0/drop_unused_indexes.py @@ -2,9 +2,8 @@ This patch just drops some known indexes which aren't being used anymore or never were used. """ -import click - import frappe +from frappe.database.utils import drop_index_if_exists UNUSED_INDEXES = [ ("Comment", ["link_doctype", "link_name"]), @@ -39,18 +38,3 @@ def execute(): if table not in db_tables: continue drop_index_if_exists(table, index_name) - - -def drop_index_if_exists(table: str, index: str): - if not frappe.db.has_index(table, index): - click.echo(f"- Skipped {index} index for {table} because it doesn't exist") - return - - try: - frappe.db.sql_ddl(f"ALTER TABLE `{table}` DROP INDEX `{index}`") - except Exception as e: - frappe.log_error("Failed to drop index") - click.secho(f"x Failed to drop index {index} from {table}\n {e!s}", fg="red") - return - - click.echo(f"✓ dropped {index} index from {table}") diff --git a/frappe/patches/v15_0/drop_modified_index.py b/frappe/patches/v15_0/drop_modified_index.py index 8cdcf12ece..e8e3590896 100644 --- a/frappe/patches/v15_0/drop_modified_index.py +++ b/frappe/patches/v15_0/drop_modified_index.py @@ -1,5 +1,5 @@ import frappe -from frappe.patches.v14_0.drop_unused_indexes import drop_index_if_exists +from frappe.database.utils import drop_index_if_exists def execute():