From 3df13ca3922c68c49090a37a87ec79d223be3d17 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Mon, 29 May 2023 15:36:38 +0530 Subject: [PATCH] feat: new operator - `descendants of (inclusive)` Co-Authored-By: Faris Ansari --- frappe/database/utils.py | 1 + frappe/model/db_query.py | 24 +++++++++++++++----- frappe/public/js/frappe/ui/filters/filter.js | 2 ++ frappe/utils/data.py | 7 ++---- 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/frappe/database/utils.py b/frappe/database/utils.py index d1030ca6d7..61dd0016c5 100644 --- a/frappe/database/utils.py +++ b/frappe/database/utils.py @@ -23,6 +23,7 @@ NestedSetHierarchy = ( "descendants of", "not ancestors of", "not descendants of", + "descendants of (inclusive)", ) diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py index 340b3a97f4..ca1969abcf 100644 --- a/frappe/model/db_query.py +++ b/frappe/model/db_query.py @@ -730,18 +730,30 @@ class DatabaseQuery: lft, rgt = frappe.db.get_value(ref_doctype, f.value, ["lft", "rgt"]) # Get descendants elements of a DocType with a tree structure - if f.operator.lower() in ("descendants of", "not descendants of"): - result = frappe.get_all( - ref_doctype, filters={"lft": [">", lft], "rgt": ["<", rgt]}, order_by="`lft` ASC" + if f.operator.lower() in ( + "descendants of", + "not descendants of", + "descendants of (inclusive)", + ): + nodes = frappe.get_all( + ref_doctype, + filters={"lft": [">", lft], "rgt": ["<", rgt]}, + order_by="`lft` ASC", + pluck="name", ) + if f.operator.lower() == "descendants of (inclusive)": + nodes += [f.value] else: # Get ancestor elements of a DocType with a tree structure - result = frappe.get_all( - ref_doctype, filters={"lft": ["<", lft], "rgt": [">", rgt]}, order_by="`lft` DESC" + nodes = frappe.get_all( + ref_doctype, + filters={"lft": ["<", lft], "rgt": [">", rgt]}, + order_by="`lft` DESC", + pluck="name", ) fallback = "''" - value = [frappe.db.escape((cstr(v.name) or "").strip(), percent=False) for v in result] + value = [frappe.db.escape((cstr(v)).strip(), percent=False) for v in nodes] if len(value): value = f"({', '.join(value)})" else: diff --git a/frappe/public/js/frappe/ui/filters/filter.js b/frappe/public/js/frappe/ui/filters/filter.js index 5902c136bd..dd8ce7f6dd 100644 --- a/frappe/public/js/frappe/ui/filters/filter.js +++ b/frappe/public/js/frappe/ui/filters/filter.js @@ -30,6 +30,7 @@ frappe.ui.Filter = class { this.nested_set_conditions = [ ["descendants of", __("Descendants Of")], + ["descendants of (inclusive)", __("Descendants Of (inclusive)")], ["not descendants of", __("Not Descendants Of")], ["ancestors of", __("Ancestors Of")], ["not ancestors of", __("Not Ancestors Of")], @@ -524,6 +525,7 @@ frappe.ui.filter_utils = { "=", "!=", "descendants of", + "descendants of (inclusive)", "ancestors of", "not descendants of", "not ancestors of", diff --git a/frappe/utils/data.py b/frappe/utils/data.py index a51cdee04a..3fe854bbf6 100644 --- a/frappe/utils/data.py +++ b/frappe/utils/data.py @@ -1766,6 +1766,7 @@ def get_filter(doctype: str, f: dict | list | tuple, filters_config=None) -> "fr "fieldtype": } """ + from frappe.database.utils import NestedSetHierarchy from frappe.model import child_table_fields, default_fields, optional_fields if isinstance(f, dict): @@ -1805,14 +1806,10 @@ def get_filter(doctype: str, f: dict | list | tuple, filters_config=None) -> "fr "not in", "is", "between", - "descendants of", - "ancestors of", - "not descendants of", - "not ancestors of", "timespan", "previous", "next", - ) + ) + NestedSetHierarchy if filters_config: additional_operators = []