mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-22 18:59:58 +00:00
fix(postgres): other-class parity fixes (case-folding & null-ordering) for merged queries
Parity sweep findings in queries already merged in develop: - pos_invoice.py: serial_no return-match matched case-sensitively on Postgres (case-insensitive on MariaDB). Replaced the get_all or_filters lookup with a qb query that case-folds both sides via Lower() (no-op on MariaDB). - controllers/queries.py + pick_list.py: the Locate()-based relevance ranking in link-query ORDER BY is case-sensitive on Postgres (Strpos) vs case-insensitive on MariaDB (Locate), so autocomplete order differed. Lower() both arguments so the ranking matches on both engines. - crm/lead_conversion_time.py: "first contact" used ORDER BY communication_date LIMIT 1 read by index; a NULL date sorts first on MariaDB but last on Postgres, changing the result. Added `communication_date IS NOT NULL` so both engines return the earliest real contact date. Verified on MariaDB and Postgres: test_pick_list 40/40, test_pos_invoice 26/26 on both engines. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,7 @@ import frappe
|
||||
from frappe import _, bold
|
||||
from frappe.model.document import Document
|
||||
from frappe.model.mapper import map_child_doc, map_doc
|
||||
from frappe.query_builder.functions import IfNull, Sum
|
||||
from frappe.query_builder.functions import IfNull, Lower, Sum
|
||||
from frappe.utils import cint, flt, get_link_to_form, getdate, nowdate
|
||||
from frappe.utils.nestedset import get_descendants_of
|
||||
|
||||
@@ -505,16 +505,20 @@ class POSInvoice(SalesInvoice):
|
||||
if d.get("serial_no"):
|
||||
serial_nos = get_serial_nos(d.serial_no)
|
||||
for sr in serial_nos:
|
||||
serial_no_exists = frappe.get_all(
|
||||
"POS Invoice Item",
|
||||
filters={"parent": self.return_against},
|
||||
or_filters=[
|
||||
["serial_no", "=", sr],
|
||||
["serial_no", "like", f"{sr}\n%"],
|
||||
["serial_no", "like", f"%\n{sr}"],
|
||||
["serial_no", "like", f"%\n{sr}\n%"],
|
||||
],
|
||||
limit=1,
|
||||
POI = frappe.qb.DocType("POS Invoice Item")
|
||||
s = sr.lower()
|
||||
serial_no_exists = (
|
||||
frappe.qb.from_(POI)
|
||||
.select(POI.name)
|
||||
.where(POI.parent == self.return_against)
|
||||
.where(
|
||||
(Lower(POI.serial_no) == s)
|
||||
| Lower(POI.serial_no).like(f"{s}\n%")
|
||||
| Lower(POI.serial_no).like(f"%\n{s}")
|
||||
| Lower(POI.serial_no).like(f"%\n{s}\n%")
|
||||
)
|
||||
.limit(1)
|
||||
.run()
|
||||
)
|
||||
|
||||
if not serial_no_exists:
|
||||
|
||||
@@ -10,7 +10,7 @@ from frappe import qb, scrub
|
||||
from frappe.desk.reportview import get_filters_cond, get_match_cond
|
||||
from frappe.permissions import has_permission
|
||||
from frappe.query_builder import Case, Criterion, DocType
|
||||
from frappe.query_builder.functions import Concat, CustomFunction, Length, Locate, Substring, Sum
|
||||
from frappe.query_builder.functions import Concat, CustomFunction, Length, Locate, Lower, Substring, Sum
|
||||
from frappe.utils import nowdate, today, unique
|
||||
from pypika import Order
|
||||
|
||||
@@ -313,11 +313,19 @@ def item_query(
|
||||
.where(date_condition)
|
||||
.where(Criterion.any(search_conditions))
|
||||
.orderby(
|
||||
Case().when(Locate(txt_no_percent, item.name) > 0, Locate(txt_no_percent, item.name)).else_(99999)
|
||||
Case()
|
||||
.when(
|
||||
Locate(Lower(txt_no_percent), Lower(item.name)) > 0,
|
||||
Locate(Lower(txt_no_percent), Lower(item.name)),
|
||||
)
|
||||
.else_(99999)
|
||||
)
|
||||
.orderby(
|
||||
Case()
|
||||
.when(Locate(txt_no_percent, item.item_name) > 0, Locate(txt_no_percent, item.item_name))
|
||||
.when(
|
||||
Locate(Lower(txt_no_percent), Lower(item.item_name)) > 0,
|
||||
Locate(Lower(txt_no_percent), Lower(item.item_name)),
|
||||
)
|
||||
.else_(99999)
|
||||
)
|
||||
.orderby(item.idx, order=Order.desc)
|
||||
@@ -406,7 +414,13 @@ def get_project_name(
|
||||
# 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(
|
||||
ifelse(
|
||||
Locate(Lower(txt), Lower(proj.project_name)) > 0,
|
||||
Locate(Lower(txt), Lower(proj.project_name)),
|
||||
99999,
|
||||
)
|
||||
)
|
||||
q = q.orderby(proj.idx, order=Order.desc).orderby(proj.name)
|
||||
|
||||
if page_len:
|
||||
|
||||
@@ -124,7 +124,7 @@ def get_communication_details(filters):
|
||||
FROM
|
||||
`tabCommunication`
|
||||
WHERE
|
||||
recipients = %s
|
||||
recipients = %s AND communication_date IS NOT NULL
|
||||
ORDER BY
|
||||
communication_date
|
||||
LIMIT 1
|
||||
|
||||
@@ -9,7 +9,7 @@ import frappe
|
||||
from frappe import _, bold
|
||||
from frappe.model.document import Document
|
||||
from frappe.query_builder import Case
|
||||
from frappe.query_builder.functions import Coalesce, GroupConcat, Locate, Max, Replace, Sum
|
||||
from frappe.query_builder.functions import Coalesce, GroupConcat, Locate, Lower, Max, Replace, Sum
|
||||
from frappe.utils import cint, floor, flt, get_link_to_form
|
||||
from frappe.utils.nestedset import get_descendants_of
|
||||
|
||||
@@ -1301,7 +1301,11 @@ def get_pending_work_orders(
|
||||
& (wo.company == filters.get("company"))
|
||||
& (wo.name.like(f"%{txt}%"))
|
||||
)
|
||||
.orderby(Case().when(Locate(txt, wo.name) > 0, Locate(txt, wo.name)).else_(99999))
|
||||
.orderby(
|
||||
Case()
|
||||
.when(Locate(Lower(txt), Lower(wo.name)) > 0, Locate(Lower(txt), Lower(wo.name)))
|
||||
.else_(99999)
|
||||
)
|
||||
.orderby(wo.name)
|
||||
.limit(cint(page_length))
|
||||
.offset(start)
|
||||
|
||||
Reference in New Issue
Block a user