Merge pull request #39402 from ruthra-kumar/fix_project_query
fix: project query controller logic
diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py
index 8ebdcc5..e234eec 100644
--- a/erpnext/controllers/queries.py
+++ b/erpnext/controllers/queries.py
@@ -6,10 +6,12 @@
from collections import OrderedDict, defaultdict
import frappe
-from frappe import scrub
+from frappe import qb, scrub
from frappe.desk.reportview import get_filters_cond, get_match_cond
-from frappe.query_builder.functions import Concat, Sum
+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
import erpnext
from erpnext.stock.get_item_details import _get_item_tax_template
@@ -344,37 +346,46 @@
@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 = []
+ ifelse = CustomFunction("IF", ["condition", "then", "else"])
+
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"))
+
+ qb_filter_and_conditions.append(proj.status.notin(["Completed", "Cancelled"]))
+
+ q = qb.from_(proj)
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])
+ for x in fields:
+ q = q.select(proj[x])
- 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("%", "")},
- )
+ # 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()
@frappe.whitelist()
diff --git a/erpnext/controllers/tests/test_queries.py b/erpnext/controllers/tests/test_queries.py
index 60d1733..3a3bc1c 100644
--- a/erpnext/controllers/tests/test_queries.py
+++ b/erpnext/controllers/tests/test_queries.py
@@ -68,7 +68,7 @@
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)