mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-30 07:08:24 +00:00
fix(stock): case-insensitive serial-no match in get_stock_ledgers_for_serial_nos
The serial-no filter used serial_batch_entry.serial_no.isin(serial_nos), which is case-sensitive on Postgres -- a differently-cased serial no missed Serial and Batch Entry rows that MariaDB (case-insensitive collation) matches (the OR'd regexp branch only covers the legacy Stock Ledger Entry.serial_no text, empty for bundle-tracked serials). Lower() both sides: MariaDB result unchanged, Postgres now matches too. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -11,7 +11,7 @@ import frappe.query_builder
|
|||||||
from frappe import _, _dict, bold
|
from frappe import _, _dict, bold
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.model.naming import make_autoname
|
from frappe.model.naming import make_autoname
|
||||||
from frappe.query_builder.functions import Concat_ws, Max, Sum
|
from frappe.query_builder.functions import Concat_ws, Lower, Max, Sum
|
||||||
from frappe.utils import (
|
from frappe.utils import (
|
||||||
cint,
|
cint,
|
||||||
cstr,
|
cstr,
|
||||||
@@ -3388,7 +3388,8 @@ def get_stock_ledgers_for_serial_nos(kwargs):
|
|||||||
query.left_join(serial_batch_entry)
|
query.left_join(serial_batch_entry)
|
||||||
.on(stock_ledger_entry.serial_and_batch_bundle == serial_batch_entry.parent)
|
.on(stock_ledger_entry.serial_and_batch_bundle == serial_batch_entry.parent)
|
||||||
.where(
|
.where(
|
||||||
serial_batch_entry.serial_no.isin(serial_nos)
|
# Lower() both sides so serial-no matching is case-insensitive on Postgres as on MariaDB
|
||||||
|
Lower(serial_batch_entry.serial_no).isin([sn.lower() for sn in serial_nos])
|
||||||
| Concat_ws("", "\n", stock_ledger_entry.serial_no, "\n").regexp(regex_pattern)
|
| Concat_ws("", "\n", stock_ledger_entry.serial_no, "\n").regexp(regex_pattern)
|
||||||
)
|
)
|
||||||
.distinct()
|
.distinct()
|
||||||
|
|||||||
@@ -80,6 +80,31 @@ class TestSerialandBatchBundle(ERPNextTestSuite):
|
|||||||
|
|
||||||
self.assertFalse(bundle_doc.name.startswith("SABB-"))
|
self.assertFalse(bundle_doc.name.startswith("SABB-"))
|
||||||
|
|
||||||
|
def test_get_stock_ledgers_for_serial_nos_is_case_insensitive(self):
|
||||||
|
# get_stock_ledgers_for_serial_nos matches Serial and Batch Entry.serial_no with isin(), which is
|
||||||
|
# case-sensitive on Postgres -- a differently-cased serial no would miss entries that MariaDB
|
||||||
|
# (case-insensitive collation) matches. Lower() both sides keeps MariaDB unchanged and makes
|
||||||
|
# Postgres match too.
|
||||||
|
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
|
||||||
|
from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import (
|
||||||
|
get_stock_ledgers_for_serial_nos,
|
||||||
|
)
|
||||||
|
|
||||||
|
item_code = "Test SBB Case-insensitive Serial Item"
|
||||||
|
make_item(
|
||||||
|
item_code,
|
||||||
|
{"has_serial_no": 1, "serial_no_series": "TESTCISER-.#####", "is_stock_item": 1},
|
||||||
|
)
|
||||||
|
pr = make_purchase_receipt(item_code=item_code, warehouse="_Test Warehouse - _TC", qty=1, rate=100)
|
||||||
|
bundle = pr.items[0].serial_and_batch_bundle
|
||||||
|
serial_no = get_serial_nos_from_bundle(bundle)[0]
|
||||||
|
|
||||||
|
# query with a lowercased serial no; the stored Serial and Batch Entry value is uppercase
|
||||||
|
rows = get_stock_ledgers_for_serial_nos(
|
||||||
|
frappe._dict({"item_code": item_code, "serial_nos": [serial_no.lower()]})
|
||||||
|
)
|
||||||
|
self.assertTrue(any(row.serial_and_batch_bundle == bundle for row in rows))
|
||||||
|
|
||||||
def test_inward_outward_serial_valuation(self):
|
def test_inward_outward_serial_valuation(self):
|
||||||
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
|
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
|
||||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
|
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
|
||||||
|
|||||||
Reference in New Issue
Block a user