mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-21 10:19:57 +00:00
fix(website): case-insensitive Item Variant attribute match on Postgres
get_item_codes_by_attributes compared Item Variant Attribute `attribute`/`attribute_value` with raw equality/IN, which is case-sensitive on Postgres -- a differently-cased website filter value missed variants that MariaDB (case-insensitive collation) matches. Lower() both sides: MariaDB result is unchanged (already case-insensitive), Postgres now matches too. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
import frappe
|
||||
from frappe.query_builder.functions import Lower
|
||||
from frappe.utils import cint, cstr, flt, fmt_money
|
||||
|
||||
from erpnext.accounts.doctype.pricing_rule.pricing_rule import get_pricing_rule_for_item
|
||||
@@ -133,9 +134,12 @@ def get_item_codes_by_attributes(attribute_filters, template_item_code=None):
|
||||
frappe.qb.from_(iva)
|
||||
.select(iva.parent)
|
||||
# attribute_value is a varchar column; cast values to str so postgres doesn't choke on
|
||||
# `varchar = numeric` for numeric attributes (stored values are strings on both backends)
|
||||
# `varchar = numeric` for numeric attributes (stored values are strings on both backends).
|
||||
# Lower() both sides so matching is case-insensitive on Postgres too, matching MariaDB's
|
||||
# default collation (MariaDB result is unchanged -- it already matches case-insensitively).
|
||||
.where(
|
||||
(iva.attribute == attribute) & (iva.attribute_value.isin([cstr(v) for v in attribute_values]))
|
||||
(Lower(iva.attribute) == cstr(attribute).lower())
|
||||
& (Lower(iva.attribute_value).isin([cstr(v).lower() for v in attribute_values]))
|
||||
)
|
||||
.where(iva.parent.isin(item_subquery))
|
||||
.groupby(iva.parent)
|
||||
|
||||
25
erpnext/utilities/test_product.py
Normal file
25
erpnext/utilities/test_product.py
Normal file
@@ -0,0 +1,25 @@
|
||||
# Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
import frappe
|
||||
|
||||
from erpnext.controllers.item_variant import create_variant
|
||||
from erpnext.tests.utils import ERPNextTestSuite
|
||||
from erpnext.utilities.product import get_item_codes_by_attributes
|
||||
|
||||
|
||||
class TestProduct(ERPNextTestSuite):
|
||||
def test_get_item_codes_by_attributes_is_case_insensitive(self):
|
||||
# get_item_codes_by_attributes matches Item Variant Attribute values. A raw equality is
|
||||
# case-sensitive on Postgres, so a differently-cased filter value would miss variants that
|
||||
# MariaDB (case-insensitive collation) matches. Lower() both sides keeps MariaDB unchanged and
|
||||
# makes Postgres match too.
|
||||
template = "_Test Variant Item"
|
||||
variant = create_variant(template, {"Test Size": "Small"})
|
||||
if not frappe.db.exists("Item", variant.name):
|
||||
variant.insert()
|
||||
self.addCleanup(frappe.delete_doc, "Item", variant.name, force=True)
|
||||
|
||||
# stored attribute value is "Small"; query with a different case
|
||||
matches = get_item_codes_by_attributes({"Test Size": ["small"]}, template)
|
||||
self.assertIn(variant.name, matches)
|
||||
Reference in New Issue
Block a user