Merge pull request #56251 from mihir-kandoi/pg-ci-remaining-failures

fix(postgres): resolve remaining Postgres test failures on develop
This commit is contained in:
Mihir Kandoi
2026-06-21 16:19:28 +05:30
committed by GitHub
6 changed files with 50 additions and 11 deletions

View File

@@ -479,7 +479,7 @@ def reconcile(doc: None | str = None) -> None:
finally:
if reconciled_entries == total_allocations:
frappe.db.set_value("Process Payment Reconciliation Log", log, "status", "Reconciled")
frappe.db.set_value("Process Payment Reconciliation Log", log, "reconciled", True)
frappe.db.set_value("Process Payment Reconciliation Log", log, "reconciled", 1)
frappe.db.set_value("Process Payment Reconciliation", doc, "status", "Completed")
else:
if frappe.db.get_value("Process Payment Reconciliation", doc, "status") != "Paused":

View File

@@ -424,7 +424,7 @@ class TestUnreconcilePayment(ERPNextTestSuite, AccountsTestMixin):
self.disable_advance_as_liability()
def test_07_adv_from_so_to_invoice(self):
frappe.db.set_value("Company", self.company, "book_advance_payments_in_separate_party_account", True)
frappe.db.set_value("Company", self.company, "book_advance_payments_in_separate_party_account", 1)
frappe.db.set_value(
"Company", self.company, "default_advance_received_account", "Advance Received - _TC"
)

View File

@@ -1402,16 +1402,18 @@ def validate_bom_no(item, bom_no):
def _bom_contains_item(bom, item):
item = item.lower()
item_lower = item.lower()
for d in bom.items:
if d.item_code.lower() == item:
if d.item_code.lower() == item_lower:
return True
for d in bom.secondary_items:
if d.item_code.lower() == item:
if d.item_code.lower() == item_lower:
return True
# Use the original-cased `item` for the Item lookup: names are case-sensitive on Postgres,
# so a lowercased name would miss the record and drop the variant->template BOM match.
return (
bom.item.lower() == item
bom.item.lower() == item_lower
or bom.item.lower() == cstr(frappe.db.get_value("Item", item, "variant_of")).lower()
)

View File

@@ -19,8 +19,10 @@ class TestBin(ERPNextTestSuite):
bin1.insert()
bin2 = frappe.get_doc(doctype="Bin", item_code=item_code, warehouse=warehouse)
frappe.db.savepoint("dup_bin")
with self.assertRaises(frappe.UniqueValidationError):
bin2.insert()
frappe.db.rollback(save_point="dup_bin") # preserve transaction in postgres
# util method should handle it
bin = _create_bin(item_code, warehouse)

View File

@@ -751,7 +751,9 @@ class TestItem(ERPNextTestSuite):
item_doc = frappe.get_doc("Item", item_code)
new_barcode = item_doc.append("barcodes")
new_barcode.update(barcode_properties_list[0])
frappe.db.savepoint("dup_barcode")
self.assertRaises(frappe.UniqueValidationError, item_doc.save)
frappe.db.rollback(save_point="dup_barcode") # preserve transaction in postgres
# Add invalid barcode - should cause InvalidBarcode
item_doc = frappe.get_doc("Item", item_code)
@@ -768,10 +770,14 @@ class TestItem(ERPNextTestSuite):
now = time.time()
one_year_ago = now - 366 * 24 * 60 * 60
# posting_date is a calendar date; its midnight unix timestamp (taken in the database
# session timezone) can sit up to a day ahead of the precise current instant when the app
# timezone is ahead of UTC, so allow a day of slack on the upper bound.
one_day = 24 * 60 * 60
for timestamp, count in data.items():
self.assertIsInstance(timestamp, int)
self.assertTrue(one_year_ago <= timestamp <= now)
self.assertTrue(one_year_ago <= timestamp <= now + one_day)
self.assertIsInstance(count, int)
self.assertGreaterEqual(count, 0)

View File

@@ -9,6 +9,37 @@ INDEXED_FIELDS = {
}
def _is_leading_index_column(doctype: str, field: str) -> bool:
"""Whether `field` is the first column of some index on the doctype's table.
`SHOW INDEX` is MySQL-only; on Postgres read the leading key column (indkey[0])
from the pg_index catalog. Both check the same thing across engines.
"""
table = f"tab{doctype}"
if frappe.db.db_type == "postgres":
return bool(
frappe.db.sql(
"""
SELECT 1
FROM pg_index i
JOIN pg_class t ON t.oid = i.indrelid
JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = i.indkey[0]
WHERE t.relname = %s AND a.attname = %s
LIMIT 1
""",
(table, field),
)
)
# `table` is a trusted constant (from INDEXED_FIELDS); a table identifier can't be a %s
# placeholder in SHOW INDEX, so the f-string is unavoidable and safe here.
return bool(
frappe.db.sql(
f"""SHOW INDEX FROM `{table}` WHERE Column_name = %s AND Seq_in_index = 1""",
(field,),
)
)
class TestPerformance(ERPNextTestSuite):
def test_ensure_indexes(self):
# These fields are not explicitly indexed BUT they are prefix in some
@@ -17,8 +48,6 @@ class TestPerformance(ERPNextTestSuite):
for doctype, fields in INDEXED_FIELDS.items():
for field in fields:
self.assertTrue(
frappe.db.sql(
f"""SHOW INDEX FROM `tab{doctype}`
WHERE Column_name = "{field}" AND Seq_in_index = 1"""
)
_is_leading_index_column(doctype, field),
msg=f"{field} is not the leading column of any index on tab{doctype}",
)