From 98e8d5690ecf8025f9e51545af746a652a606baa Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Mon, 22 Jun 2026 19:00:14 +0530 Subject: [PATCH] fix(controllers): case-insensitive search ranking in employee/lead/bom queries on Postgres MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit employee_query, lead_query and bom() ranked autocomplete results with a bare Locate(txt, col) in ORDER BY. frappe maps Locate -> strpos on Postgres, which is case-sensitive, while MariaDB's LOCATE against a column uses the column's case-insensitive collation. So the search-dropdown ordering diverged between engines for mixed-case matches (row count/membership unchanged — the WHERE .like() is already ILIKE). Wrap both Locate operands in Lower(), matching the sibling item_query/get_project_name handlers in the same file: a no-op on MariaDB, and case-insensitive (MariaDB-faithful) on Postgres. The existing test_queries suite stays green on both engines. --- erpnext/controllers/queries.py | 37 +++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 9d5ebd5be0d..723c60964e4 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -71,14 +71,17 @@ def employee_query( .where(Criterion.any(search_conditions)) .orderby( Case() - .when(Locate(txt_no_percent, Employee.name) > 0, Locate(txt_no_percent, Employee.name)) + .when( + Locate(Lower(txt_no_percent), Lower(Employee.name)) > 0, + Locate(Lower(txt_no_percent), Lower(Employee.name)), + ) .else_(99999) ) .orderby( Case() .when( - Locate(txt_no_percent, Employee.employee_name) > 0, - Locate(txt_no_percent, Employee.employee_name), + Locate(Lower(txt_no_percent), Lower(Employee.employee_name)) > 0, + Locate(Lower(txt_no_percent), Lower(Employee.employee_name)), ) .else_(99999) ) @@ -134,17 +137,28 @@ def lead_query( query.where(Lead.docstatus < 2) .where(Lead.status.isnull() | (Lead.status != "Converted")) .where(Criterion.any(search_conditions)) - .orderby( - Case().when(Locate(txt_no_percent, Lead.name) > 0, Locate(txt_no_percent, Lead.name)).else_(99999) - ) .orderby( Case() - .when(Locate(txt_no_percent, Lead.lead_name) > 0, Locate(txt_no_percent, Lead.lead_name)) + .when( + Locate(Lower(txt_no_percent), Lower(Lead.name)) > 0, + Locate(Lower(txt_no_percent), Lower(Lead.name)), + ) .else_(99999) ) .orderby( Case() - .when(Locate(txt_no_percent, Lead.company_name) > 0, Locate(txt_no_percent, Lead.company_name)) + .when( + Locate(Lower(txt_no_percent), Lower(Lead.lead_name)) > 0, + Locate(Lower(txt_no_percent), Lower(Lead.lead_name)), + ) + .else_(99999) + ) + .orderby( + Case() + .when( + Locate(Lower(txt_no_percent), Lower(Lead.company_name)) > 0, + Locate(Lower(txt_no_percent), Lower(Lead.company_name)), + ) .else_(99999) ) .orderby(Lead.idx, order=Order.desc) @@ -382,7 +396,12 @@ def bom( .where(BOM.is_active == 1) .where(BOM[searchfield].like(f"%{txt}%")) .orderby( - Case().when(Locate(txt_no_percent, BOM.name) > 0, Locate(txt_no_percent, BOM.name)).else_(99999) + Case() + .when( + Locate(Lower(txt_no_percent), Lower(BOM.name)) > 0, + Locate(Lower(txt_no_percent), Lower(BOM.name)), + ) + .else_(99999) ) .orderby(BOM.idx, order=Order.desc) .orderby(BOM.name)