diff --git a/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.py b/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.py index 316e691c89c..5ee78e5bbb3 100644 --- a/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.py +++ b/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.py @@ -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": diff --git a/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py b/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py index 31f2c6b5508..ff17d6bbac2 100644 --- a/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py +++ b/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py @@ -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" ) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index eebf131fd9d..90d898f4751 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -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() ) diff --git a/erpnext/stock/doctype/bin/test_bin.py b/erpnext/stock/doctype/bin/test_bin.py index e66e453aca1..81b60d6ce19 100644 --- a/erpnext/stock/doctype/bin/test_bin.py +++ b/erpnext/stock/doctype/bin/test_bin.py @@ -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) diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index 4b6537edb87..16d8f0f469c 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -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) diff --git a/erpnext/tests/test_perf.py b/erpnext/tests/test_perf.py index cb4ecd2ee13..f37922f645c 100644 --- a/erpnext/tests/test_perf.py +++ b/erpnext/tests/test_perf.py @@ -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}", )