From f2e577bec749fcbba74c224e2869433f24d7a4bb Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 16 Jan 2024 13:38:53 +0530 Subject: [PATCH 1/4] fix: project query controller logic (cherry picked from commit 4eefb445a748100f3c36094188e38c127ad80051) # Conflicts: # erpnext/controllers/queries.py --- erpnext/controllers/queries.py | 59 +++++++++++++++++----------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index b73ebf53ae8..aa06dad4598 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -6,9 +6,15 @@ import json from collections import defaultdict import frappe -from frappe import scrub +from frappe import qb, scrub from frappe.desk.reportview import get_filters_cond, get_match_cond +<<<<<<< HEAD from frappe.utils import nowdate, unique +======= +from frappe.query_builder import Criterion +from frappe.query_builder.functions import Concat, Sum +from frappe.utils import nowdate, today, unique +>>>>>>> 4eefb445a7 (fix: project query controller logic) import erpnext from erpnext.stock.get_item_details import _get_item_tax_template @@ -329,37 +335,32 @@ def bom(doctype, txt, searchfield, start, page_len, filters): @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs def get_project_name(doctype, txt, searchfield, start, page_len, filters): - doctype = "Project" - cond = "" + proj = qb.DocType("Project") + qb_filter_and_conditions = [] + qb_filter_or_conditions = [] if filters and filters.get("customer"): - cond = """(`tabProject`.customer = %s or - ifnull(`tabProject`.customer,"")="") and""" % ( - frappe.db.escape(filters.get("customer")) - ) + qb_filter_and_conditions.append(proj.customer == filters.get("customer")) - fields = get_fields(doctype, ["name", "project_name"]) - searchfields = frappe.get_meta(doctype).get_search_fields() - searchfields = " or ".join(["`tabProject`." + field + " like %(txt)s" for field in searchfields]) + qb_filter_and_conditions.append(proj.status.notin(["Completed", "Cancelled"])) - return frappe.db.sql( - """select {fields} from `tabProject` - where - `tabProject`.status not in ('Completed', 'Cancelled') - and {cond} {scond} {match_cond} - order by - (case when locate(%(_txt)s, `tabProject`.name) > 0 then locate(%(_txt)s, `tabProject`.name) else 99999 end), - `tabProject`.idx desc, - `tabProject`.name asc - limit {page_len} offset {start}""".format( - fields=", ".join(["`tabProject`.{0}".format(f) for f in fields]), - cond=cond, - scond=searchfields, - match_cond=get_match_cond(doctype), - start=start, - page_len=page_len, - ), - {"txt": "%{0}%".format(txt), "_txt": txt.replace("%", "")}, - ) + q = qb.from_(proj) + + fields = get_fields("Project", ["name", "project_name"]) + for x in fields: + q = q.select(proj[x]) + + # ignore 'customer' and 'status' on searchfields as they must be exactly matched + searchfields = [ + x for x in frappe.get_meta(doctype).get_search_fields() if x not in ["customer", "status"] + ] + if txt: + for x in searchfields: + qb_filter_or_conditions.append(proj[x].like(f"%{txt}%")) + + q = q.where(Criterion.all(qb_filter_and_conditions)).where(Criterion.any(qb_filter_or_conditions)) + if page_len: + q = q.limit(page_len) + return q.run() @frappe.whitelist() From 98967ed58487cc63e0ed345c50b6f0fb1be97eea Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 16 Jan 2024 14:28:09 +0530 Subject: [PATCH 2/4] fix(test): test case for project query (cherry picked from commit 3349dde5e2914bd9e2dbe0ce4de94023bfee2e7f) --- erpnext/controllers/queries.py | 2 +- erpnext/controllers/tests/test_queries.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index aa06dad4598..c6285fc7344 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -345,7 +345,7 @@ def get_project_name(doctype, txt, searchfield, start, page_len, filters): q = qb.from_(proj) - fields = get_fields("Project", ["name", "project_name"]) + fields = get_fields(doctype, ["name", "project_name"]) for x in fields: q = q.select(proj[x]) diff --git a/erpnext/controllers/tests/test_queries.py b/erpnext/controllers/tests/test_queries.py index 60d1733021c..3a3bc1cd725 100644 --- a/erpnext/controllers/tests/test_queries.py +++ b/erpnext/controllers/tests/test_queries.py @@ -68,7 +68,7 @@ class TestQueries(unittest.TestCase): self.assertGreaterEqual(len(query(txt="_Test Item Home Desktop Manufactured")), 1) def test_project_query(self): - query = add_default_params(queries.get_project_name, "BOM") + query = add_default_params(queries.get_project_name, "Project") self.assertGreaterEqual(len(query(txt="_Test Project")), 1) From b35a83ee47f0d8d37112a458d79cfe6c2f452e39 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 16 Jan 2024 14:35:06 +0530 Subject: [PATCH 3/4] refactor: better ordering of query result (cherry picked from commit bfe42fdccb13ab797ac7252ada58df49af43ad54) # Conflicts: # erpnext/controllers/queries.py --- erpnext/controllers/queries.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index c6285fc7344..f581fb429bd 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -9,12 +9,19 @@ import frappe from frappe import qb, scrub from frappe.desk.reportview import get_filters_cond, get_match_cond <<<<<<< HEAD +<<<<<<< HEAD from frappe.utils import nowdate, unique ======= from frappe.query_builder import Criterion from frappe.query_builder.functions import Concat, Sum from frappe.utils import nowdate, today, unique >>>>>>> 4eefb445a7 (fix: project query controller logic) +======= +from frappe.query_builder import Criterion, CustomFunction +from frappe.query_builder.functions import Concat, Locate, Sum +from frappe.utils import nowdate, today, unique +from pypika import Order +>>>>>>> bfe42fdccb (refactor: better ordering of query result) import erpnext from erpnext.stock.get_item_details import _get_item_tax_template @@ -338,6 +345,8 @@ def get_project_name(doctype, txt, searchfield, start, page_len, filters): proj = qb.DocType("Project") qb_filter_and_conditions = [] qb_filter_or_conditions = [] + ifelse = CustomFunction("IF", ["condition", "then", "else"]) + if filters and filters.get("customer"): qb_filter_and_conditions.append(proj.customer == filters.get("customer")) @@ -349,17 +358,29 @@ def get_project_name(doctype, txt, searchfield, start, page_len, filters): for x in fields: q = q.select(proj[x]) - # ignore 'customer' and 'status' on searchfields as they must be exactly matched + # don't consider 'customer' and 'status' fields for pattern search, as they must be exactly matched searchfields = [ x for x in frappe.get_meta(doctype).get_search_fields() if x not in ["customer", "status"] ] + + # pattern search if txt: for x in searchfields: qb_filter_or_conditions.append(proj[x].like(f"%{txt}%")) q = q.where(Criterion.all(qb_filter_and_conditions)).where(Criterion.any(qb_filter_or_conditions)) + + # ordering + if txt: + # project_name containing search string 'txt' will be given higher precedence + q = q.orderby(ifelse(Locate(txt, proj.project_name) > 0, Locate(txt, proj.project_name), 99999)) + q = q.orderby(proj.idx, order=Order.desc).orderby(proj.name) + if page_len: q = q.limit(page_len) + + if start: + q = q.offset(start) return q.run() From 42c1de640ce5686b41eb5937e64d6f1351d808c8 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 17 Jan 2024 10:36:29 +0530 Subject: [PATCH 4/4] chore: resolve conflict --- erpnext/controllers/queries.py | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index f581fb429bd..06ea8336bd6 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -8,20 +8,10 @@ from collections import defaultdict import frappe from frappe import qb, scrub from frappe.desk.reportview import get_filters_cond, get_match_cond -<<<<<<< HEAD -<<<<<<< HEAD -from frappe.utils import nowdate, unique -======= -from frappe.query_builder import Criterion -from frappe.query_builder.functions import Concat, Sum -from frappe.utils import nowdate, today, unique ->>>>>>> 4eefb445a7 (fix: project query controller logic) -======= from frappe.query_builder import Criterion, CustomFunction -from frappe.query_builder.functions import Concat, Locate, Sum -from frappe.utils import nowdate, today, unique +from frappe.query_builder.functions import Locate +from frappe.utils import nowdate, unique from pypika import Order ->>>>>>> bfe42fdccb (refactor: better ordering of query result) import erpnext from erpnext.stock.get_item_details import _get_item_tax_template