Merge pull request #56249 from mihir-kandoi/pg-test-helpers-parity

test(postgres): make test-helper SQL Postgres-valid across the suite
This commit is contained in:
Mihir Kandoi
2026-06-21 14:26:39 +05:30
committed by GitHub
15 changed files with 606 additions and 458 deletions

View File

@@ -43,18 +43,18 @@ class TestJournalEntry(ERPNextTestSuite):
if test_voucher.doctype == "Journal Entry":
self.assertTrue(
frappe.db.sql(
"""select name from `tabJournal Entry Account`
where account = %s and docstatus = 1 and parent = %s""",
("Debtors - _TC", test_voucher.name),
frappe.get_all(
"Journal Entry Account",
filters={"account": "Debtors - _TC", "docstatus": 1, "parent": test_voucher.name},
pluck="name",
)
)
self.assertFalse(
frappe.db.sql(
"""select name from `tabJournal Entry Account`
where reference_type = %s and reference_name = %s""",
(test_voucher.doctype, test_voucher.name),
frappe.get_all(
"Journal Entry Account",
filters={"reference_type": test_voucher.doctype, "reference_name": test_voucher.name},
pluck="name",
)
)
@@ -69,10 +69,14 @@ class TestJournalEntry(ERPNextTestSuite):
submitted_voucher = frappe.get_doc(test_voucher.doctype, test_voucher.name)
self.assertTrue(
frappe.db.sql(
f"""select name from `tabJournal Entry Account`
where reference_type = %s and reference_name = %s and {dr_or_cr}=400""",
(submitted_voucher.doctype, submitted_voucher.name),
frappe.get_all(
"Journal Entry Account",
filters={
"reference_type": submitted_voucher.doctype,
"reference_name": submitted_voucher.name,
dr_or_cr: 400,
},
pluck="name",
)
)
@@ -82,24 +86,20 @@ class TestJournalEntry(ERPNextTestSuite):
def advance_paid_testcase(self, base_jv, test_voucher, dr_or_cr):
# Test advance paid field
advance_paid = frappe.db.sql(
"""select advance_paid from `tab{}`
where name={}""".format(test_voucher.doctype, "%s"),
(test_voucher.name),
)
advance_paid = frappe.db.get_value(test_voucher.doctype, test_voucher.name, "advance_paid")
payment_against_order = base_jv.get("accounts")[0].get(dr_or_cr)
self.assertEqual(flt(advance_paid[0][0]), flt(payment_against_order))
self.assertEqual(flt(advance_paid), flt(payment_against_order))
def cancel_against_voucher_testcase(self, test_voucher):
if test_voucher.doctype == "Journal Entry":
# if test_voucher is a Journal Entry, test cancellation of test_voucher
test_voucher.cancel()
self.assertFalse(
frappe.db.sql(
"""select name from `tabJournal Entry Account`
where reference_type='Journal Entry' and reference_name=%s""",
test_voucher.name,
frappe.get_all(
"Journal Entry Account",
filters={"reference_type": "Journal Entry", "reference_name": test_voucher.name},
pluck="name",
)
)
@@ -202,10 +202,10 @@ class TestJournalEntry(ERPNextTestSuite):
# cancel
jv.cancel()
gle = frappe.db.sql(
"""select name from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no=%s""",
jv.name,
gle = frappe.get_all(
"GL Entry",
filters={"voucher_type": "Sales Invoice", "voucher_no": jv.name},
pluck="name",
)
self.assertFalse(gle)
@@ -526,9 +526,16 @@ class TestJournalEntry(ERPNextTestSuite):
gl_entries = query.run(as_dict=True)
for i in range(len(self.expected_gle)):
# MariaDB and Postgres collate `account` differently, so the DB ordering isn't portable;
# sort both sides identically before the positional comparison.
def _key(row):
return tuple(str(row[f]) for f in self.fields)
gl_entries = sorted(gl_entries, key=_key)
expected_gle = sorted(self.expected_gle, key=_key)
for i in range(len(expected_gle)):
for field in self.fields:
self.assertEqual(self.expected_gle[i][field], gl_entries[i][field])
self.assertEqual(expected_gle[i][field], gl_entries[i][field])
def test_negative_debit_and_credit_with_same_account_head(self):
from erpnext.accounts.general_ledger import process_gl_map

View File

@@ -818,12 +818,11 @@ class TestPaymentEntry(ERPNextTestSuite):
self.assertEqual(expected_gle[gle.account][3], gle.against_voucher)
def get_gle(self, voucher_no):
return frappe.db.sql(
"""select account, debit, credit, against_voucher
from `tabGL Entry` where voucher_type='Payment Entry' and voucher_no=%s
order by account asc""",
voucher_no,
as_dict=1,
return frappe.get_all(
"GL Entry",
filters={"voucher_type": "Payment Entry", "voucher_no": voucher_no},
fields=["account", "debit", "credit", "against_voucher"],
order_by="account asc",
)
def test_payment_entry_write_off_difference(self):
@@ -918,13 +917,19 @@ class TestPaymentEntry(ERPNextTestSuite):
"Debtors - _TC": {"cost_center": cost_center},
}
gl_entries = frappe.db.sql(
"""select account, cost_center, account_currency, debit, credit,
debit_in_account_currency, credit_in_account_currency
from `tabGL Entry` where voucher_type='Payment Entry' and voucher_no=%s
order by account asc""",
pe.name,
as_dict=1,
gl_entries = frappe.get_all(
"GL Entry",
filters={"voucher_type": "Payment Entry", "voucher_no": pe.name},
fields=[
"account",
"cost_center",
"account_currency",
"debit",
"credit",
"debit_in_account_currency",
"credit_in_account_currency",
],
order_by="account asc",
)
self.assertTrue(gl_entries)
@@ -955,13 +960,19 @@ class TestPaymentEntry(ERPNextTestSuite):
"Creditors - _TC": {"cost_center": cost_center},
}
gl_entries = frappe.db.sql(
"""select account, cost_center, account_currency, debit, credit,
debit_in_account_currency, credit_in_account_currency
from `tabGL Entry` where voucher_type='Payment Entry' and voucher_no=%s
order by account asc""",
pe.name,
as_dict=1,
gl_entries = frappe.get_all(
"GL Entry",
filters={"voucher_type": "Payment Entry", "voucher_no": pe.name},
fields=[
"account",
"cost_center",
"account_currency",
"debit",
"credit",
"debit_in_account_currency",
"credit_in_account_currency",
],
order_by="account asc",
)
self.assertTrue(gl_entries)
@@ -1770,9 +1781,18 @@ class TestPaymentEntry(ERPNextTestSuite):
.where((gle.voucher_no == self.voucher_no) & (gle.is_cancelled == 0))
.orderby(gle.account, gle.debit, gle.credit, order=frappe.qb.desc)
).run(as_dict=True)
for row in range(len(self.expected_gle)):
for field in ["account", "debit", "credit"]:
self.assertEqual(self.expected_gle[row][field], gl_entries[row][field])
# MariaDB and Postgres collate `account` differently, so the DB ordering isn't portable;
# sort both sides identically before the positional comparison.
fields = ["account", "debit", "credit"]
def _key(row):
return tuple(str(row[f]) for f in fields)
gl_entries = sorted(gl_entries, key=_key)
expected_gle = sorted(self.expected_gle, key=_key)
for row in range(len(expected_gle)):
for field in fields:
self.assertEqual(expected_gle[row][field], gl_entries[row][field])
def test_reverse_payment_reconciliation(self):
customer = create_customer(frappe.generate_hash(length=10), "INR")

View File

@@ -347,12 +347,11 @@ class TestPaymentRequest(ERPNextTestSuite):
]
)
gl_entries = frappe.db.sql(
"""select account, debit, credit, against_voucher
from `tabGL Entry` where voucher_type='Payment Entry' and voucher_no=%s
order by account asc""",
pe.name,
as_dict=1,
gl_entries = frappe.get_all(
"GL Entry",
filters={"voucher_type": "Payment Entry", "voucher_no": pe.name},
fields=["account", "debit", "credit", "against_voucher"],
order_by="account asc",
)
self.assertTrue(gl_entries)

View File

@@ -3,6 +3,7 @@
import frappe
from frappe.query_builder.functions import Sum
from frappe.utils import add_days, cint, flt, getdate, nowdate, today
import erpnext
@@ -123,11 +124,10 @@ class TestPurchaseInvoice(ERPNextTestSuite, StockTestMixin):
"_Test Account Discount - _TC": [0, 168.03],
"Round Off - _TC": [0, 0.3],
}
gl_entries = frappe.db.sql(
"""select account, debit, credit from `tabGL Entry`
where voucher_type = 'Purchase Invoice' and voucher_no = %s""",
pi.name,
as_dict=1,
gl_entries = frappe.get_all(
"GL Entry",
filters={"voucher_type": "Purchase Invoice", "voucher_no": pi.name},
fields=["account", "debit", "credit"],
)
for d in gl_entries:
self.assertEqual([d.debit, d.credit], expected_gl_entries.get(d.account))
@@ -317,12 +317,11 @@ class TestPurchaseInvoice(ERPNextTestSuite, StockTestMixin):
self.check_gle_for_pi(pi.name)
def check_gle_for_pi(self, pi):
gl_entries = frappe.db.sql(
"""select account, sum(debit) as debit, sum(credit) as credit
from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
group by account""",
pi,
as_dict=1,
gl_entries = frappe.get_all(
"GL Entry",
filters={"voucher_type": "Purchase Invoice", "voucher_no": pi},
fields=["account", {"SUM": "debit", "as": "debit"}, {"SUM": "credit", "as": "credit"}],
group_by="account",
)
self.assertTrue(gl_entries)
@@ -461,12 +460,11 @@ class TestPurchaseInvoice(ERPNextTestSuite, StockTestMixin):
self.assertTrue(pi.status, "Unpaid")
gl_entries = frappe.db.sql(
"""select account, debit, credit
from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
order by account asc""",
pi.name,
as_dict=1,
gl_entries = frappe.get_all(
"GL Entry",
filters={"voucher_type": "Purchase Invoice", "voucher_no": pi.name},
fields=["account", "debit", "credit"],
order_by="account asc",
)
self.assertTrue(gl_entries)
@@ -475,10 +473,11 @@ class TestPurchaseInvoice(ERPNextTestSuite, StockTestMixin):
["Creditors - TCP1", 0, 250],
]
for i, gle in enumerate(gl_entries):
self.assertEqual(expected_values[i][0], gle.account)
self.assertEqual(expected_values[i][1], gle.debit)
self.assertEqual(expected_values[i][2], gle.credit)
# DB account collation isn't portable across MariaDB/Postgres; compare order-independently.
self.assertEqual(
sorted((gle.account, gle.debit, gle.credit) for gle in gl_entries),
sorted((e[0], e[1], e[2]) for e in expected_values),
)
def test_purchase_invoice_calculation(self):
pi = frappe.copy_doc(self.globalTestRecords["Purchase Invoice"][0])
@@ -546,21 +545,24 @@ class TestPurchaseInvoice(ERPNextTestSuite, StockTestMixin):
pi.load_from_db()
self.assertTrue(
frappe.db.sql(
"""select name from `tabJournal Entry Account`
where reference_type='Purchase Invoice'
and reference_name=%s and debit_in_account_currency=300""",
pi.name,
frappe.get_all(
"Journal Entry Account",
filters={
"reference_type": "Purchase Invoice",
"reference_name": pi.name,
"debit_in_account_currency": 300,
},
pluck="name",
)
)
pi.cancel()
self.assertFalse(
frappe.db.sql(
"""select name from `tabJournal Entry Account`
where reference_type='Purchase Invoice' and reference_name=%s""",
pi.name,
frappe.get_all(
"Journal Entry Account",
filters={"reference_type": "Purchase Invoice", "reference_name": pi.name},
pluck="name",
)
)
@@ -604,10 +606,14 @@ class TestPurchaseInvoice(ERPNextTestSuite, StockTestMixin):
pi.load_from_db()
self.assertTrue(
frappe.db.sql(
"select name from `tabJournal Entry Account` where reference_type='Purchase Invoice' and "
"reference_name=%s and debit_in_account_currency=300",
pi.name,
frappe.get_all(
"Journal Entry Account",
filters={
"reference_type": "Purchase Invoice",
"reference_name": pi.name,
"debit_in_account_currency": 300,
},
pluck="name",
)
)
@@ -616,10 +622,10 @@ class TestPurchaseInvoice(ERPNextTestSuite, StockTestMixin):
pi.cancel()
self.assertFalse(
frappe.db.sql(
"select name from `tabJournal Entry Account` where reference_type='Purchase Invoice' and "
"reference_name=%s",
pi.name,
frappe.get_all(
"Journal Entry Account",
filters={"reference_type": "Purchase Invoice", "reference_name": pi.name},
pluck="name",
)
)
@@ -629,13 +635,12 @@ class TestPurchaseInvoice(ERPNextTestSuite, StockTestMixin):
else:
project = frappe.get_doc("Project", {"project_name": "_Test Project for Purchase"})
existing_purchase_cost = frappe.db.sql(
f"""select sum(base_net_amount)
from `tabPurchase Invoice Item`
where project = '{project.name}'
and docstatus=1"""
existing_purchase_cost = frappe.get_all(
"Purchase Invoice Item",
filters={"project": project.name, "docstatus": 1},
fields=[{"SUM": "base_net_amount", "as": "base_net_amount"}],
)
existing_purchase_cost = existing_purchase_cost and existing_purchase_cost[0][0] or 0
existing_purchase_cost = existing_purchase_cost and existing_purchase_cost[0].base_net_amount or 0
pi = make_purchase_invoice(currency="USD", conversion_rate=60, project=project.name)
self.assertEqual(
@@ -679,12 +684,11 @@ class TestPurchaseInvoice(ERPNextTestSuite, StockTestMixin):
)
# check gl entries for return
gl_entries = frappe.db.sql(
"""select account, debit, credit
from `tabGL Entry` where voucher_type=%s and voucher_no=%s
order by account desc""",
("Purchase Invoice", return_pi.name),
as_dict=1,
gl_entries = frappe.get_all(
"GL Entry",
filters={"voucher_type": "Purchase Invoice", "voucher_no": return_pi.name},
fields=["account", "debit", "credit"],
order_by="account desc",
)
self.assertTrue(gl_entries)
@@ -773,13 +777,18 @@ class TestPurchaseInvoice(ERPNextTestSuite, StockTestMixin):
conversion_rate=50,
)
gl_entries = frappe.db.sql(
"""select account, account_currency, debit, credit,
debit_in_account_currency, credit_in_account_currency
from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
order by account asc""",
pi.name,
as_dict=1,
gl_entries = frappe.get_all(
"GL Entry",
filters={"voucher_type": "Purchase Invoice", "voucher_no": pi.name},
fields=[
"account",
"account_currency",
"debit",
"credit",
"debit_in_account_currency",
"credit_in_account_currency",
],
order_by="account asc",
)
self.assertTrue(gl_entries)
@@ -821,10 +830,10 @@ class TestPurchaseInvoice(ERPNextTestSuite, StockTestMixin):
# cancel
pi.cancel()
gle = frappe.db.sql(
"""select name from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no=%s""",
pi.name,
gle = frappe.get_all(
"GL Entry",
filters={"voucher_type": "Sales Invoice", "voucher_no": pi.name},
pluck="name",
)
self.assertFalse(gle)
@@ -842,13 +851,18 @@ class TestPurchaseInvoice(ERPNextTestSuite, StockTestMixin):
expense_account="_Test Account Cost for Goods Sold - TCP1",
)
gl_entries = frappe.db.sql(
"""select account, account_currency, debit, credit,
debit_in_account_currency, credit_in_account_currency
from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
order by account asc""",
pi.name,
as_dict=1,
gl_entries = frappe.get_all(
"GL Entry",
filters={"voucher_type": "Purchase Invoice", "voucher_no": pi.name},
fields=[
"account",
"account_currency",
"debit",
"credit",
"debit_in_account_currency",
"credit_in_account_currency",
],
order_by="account asc",
)
self.assertTrue(gl_entries)
@@ -877,13 +891,16 @@ class TestPurchaseInvoice(ERPNextTestSuite, StockTestMixin):
expense_account="_Test Account Cost for Goods Sold - TCP1",
)
gl_entries = frappe.db.sql(
"""select account, account_currency, sum(debit) as debit,
sum(credit) as credit, debit_in_account_currency, credit_in_account_currency
from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
group by account, voucher_no order by account asc;""",
pi.name,
as_dict=1,
gl_entries = frappe.get_all(
"GL Entry",
filters={"voucher_type": "Purchase Invoice", "voucher_no": pi.name},
fields=[
"account",
{"SUM": "debit", "as": "debit"},
{"SUM": "credit", "as": "credit"},
],
group_by="account, voucher_no",
order_by="account asc",
)
stock_in_hand_account = get_inventory_account(pi.company, pi.get("items")[0].warehouse)
@@ -1145,13 +1162,19 @@ class TestPurchaseInvoice(ERPNextTestSuite, StockTestMixin):
"_Test Account Cost for Goods Sold - _TC": {"cost_center": cost_center},
}
gl_entries = frappe.db.sql(
"""select account, cost_center, account_currency, debit, credit,
debit_in_account_currency, credit_in_account_currency
from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
order by account asc""",
pi.name,
as_dict=1,
gl_entries = frappe.get_all(
"GL Entry",
filters={"voucher_type": "Purchase Invoice", "voucher_no": pi.name},
fields=[
"account",
"cost_center",
"account_currency",
"debit",
"credit",
"debit_in_account_currency",
"credit_in_account_currency",
],
order_by="account asc",
)
self.assertTrue(gl_entries)
@@ -1168,13 +1191,19 @@ class TestPurchaseInvoice(ERPNextTestSuite, StockTestMixin):
"_Test Account Cost for Goods Sold - _TC": {"cost_center": cost_center},
}
gl_entries = frappe.db.sql(
"""select account, cost_center, account_currency, debit, credit,
debit_in_account_currency, credit_in_account_currency
from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
order by account asc""",
pi.name,
as_dict=1,
gl_entries = frappe.get_all(
"GL Entry",
filters={"voucher_type": "Purchase Invoice", "voucher_no": pi.name},
fields=[
"account",
"cost_center",
"account_currency",
"debit",
"credit",
"debit_in_account_currency",
"credit_in_account_currency",
],
order_by="account asc",
)
self.assertTrue(gl_entries)
@@ -1209,13 +1238,20 @@ class TestPurchaseInvoice(ERPNextTestSuite, StockTestMixin):
"_Test Account Cost for Goods Sold - _TC": {"project": item_project.name},
}
gl_entries = frappe.db.sql(
"""select account, cost_center, project, account_currency, debit, credit,
debit_in_account_currency, credit_in_account_currency
from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
order by account asc""",
pi.name,
as_dict=1,
gl_entries = frappe.get_all(
"GL Entry",
filters={"voucher_type": "Purchase Invoice", "voucher_no": pi.name},
fields=[
"account",
"cost_center",
"project",
"account_currency",
"debit",
"credit",
"debit_in_account_currency",
"credit_in_account_currency",
],
order_by="account asc",
)
self.assertTrue(gl_entries)
@@ -1269,13 +1305,15 @@ class TestPurchaseInvoice(ERPNextTestSuite, StockTestMixin):
[deferred_account, 23.07, 0.0, "2019-03-15"],
]
gl_entries = gl_entries = frappe.db.sql(
"""select account, debit, credit, posting_date
from `tabGL Entry`
where voucher_type='Journal Entry' and voucher_detail_no=%s and posting_date <= %s
order by posting_date asc, account asc""",
(pi.items[0].name, pi.posting_date),
as_dict=1,
gl_entries = frappe.get_all(
"GL Entry",
filters={
"voucher_type": "Journal Entry",
"voucher_detail_no": pi.items[0].name,
"posting_date": ["<=", pi.posting_date],
},
fields=["account", "debit", "credit", "posting_date"],
order_by="posting_date asc, account asc",
)
for i, gle in enumerate(gl_entries):
@@ -1350,14 +1388,14 @@ class TestPurchaseInvoice(ERPNextTestSuite, StockTestMixin):
["_Test Payable USD - _TC", -37500.0],
]
gl_entries = frappe.db.sql(
"""
select account, sum(debit - credit) as balance from `tabGL Entry`
where voucher_no=%s
group by account
order by account asc""",
(pi.name),
as_dict=1,
gle = frappe.qb.DocType("GL Entry")
gl_entries = (
frappe.qb.from_(gle)
.select(gle.account, Sum(gle.debit - gle.credit).as_("balance"))
.where(gle.voucher_no == pi.name)
.groupby(gle.account)
.orderby(gle.account)
.run(as_dict=1)
)
for i, gle in enumerate(gl_entries):
@@ -1421,13 +1459,14 @@ class TestPurchaseInvoice(ERPNextTestSuite, StockTestMixin):
["_Test Payable USD - _TC", -36500.0],
]
gl_entries = frappe.db.sql(
"""
select account, sum(debit - credit) as balance from `tabGL Entry`
where voucher_no=%s
group by account order by account asc""",
(pi_2.name),
as_dict=1,
gle = frappe.qb.DocType("GL Entry")
gl_entries = (
frappe.qb.from_(gle)
.select(gle.account, Sum(gle.debit - gle.credit).as_("balance"))
.where(gle.voucher_no == pi_2.name)
.groupby(gle.account)
.orderby(gle.account)
.run(as_dict=1)
)
for i, gle in enumerate(gl_entries):
@@ -1436,18 +1475,21 @@ class TestPurchaseInvoice(ERPNextTestSuite, StockTestMixin):
expected_gle = [["_Test Payable USD - _TC", 70000.0], ["Cash - _TC", -70000.0]]
gl_entries = frappe.db.sql(
"""
select account, sum(debit - credit) as balance from `tabGL Entry`
where voucher_no=%s and is_cancelled=0
group by account order by account asc""",
(pay.name),
as_dict=1,
gle = frappe.qb.DocType("GL Entry")
gl_entries = (
frappe.qb.from_(gle)
.select(gle.account, Sum(gle.debit - gle.credit).as_("balance"))
.where((gle.voucher_no == pay.name) & (gle.is_cancelled == 0))
.groupby(gle.account)
.orderby(gle.account)
.run(as_dict=1)
)
for i, gle in enumerate(gl_entries):
self.assertEqual(expected_gle[i][0], gle.account)
self.assertEqual(expected_gle[i][1], gle.balance)
# DB account collation isn't portable across MariaDB/Postgres; compare order-independently.
self.assertEqual(
sorted((gle.account, gle.balance) for gle in gl_entries),
sorted((e[0], e[1]) for e in expected_gle),
)
total_debit_amount = frappe.db.get_all(
"Journal Entry Account",
@@ -1546,19 +1588,18 @@ class TestPurchaseInvoice(ERPNextTestSuite, StockTestMixin):
[tds_account, 0, 3000],
]
gl_entries = frappe.db.sql(
"""select account, debit, credit
from `tabGL Entry`
where voucher_type='Payment Entry' and voucher_no=%s
order by account asc""",
(payment_entry.name),
as_dict=1,
gl_entries = frappe.get_all(
"GL Entry",
filters={"voucher_type": "Payment Entry", "voucher_no": payment_entry.name},
fields=["account", "debit", "credit"],
order_by="account asc",
)
for i, gle in enumerate(gl_entries):
self.assertEqual(expected_gle[i][0], gle.account)
self.assertEqual(expected_gle[i][1], gle.debit)
self.assertEqual(expected_gle[i][2], gle.credit)
# DB account collation isn't portable across MariaDB/Postgres; compare order-independently.
self.assertEqual(
sorted((gle.account, gle.debit, gle.credit) for gle in gl_entries),
sorted((e[0], e[1], e[2]) for e in expected_gle),
)
# Create Purchase Invoice against Purchase Order
purchase_invoice = get_mapped_purchase_invoice(po.name)
@@ -1572,19 +1613,21 @@ class TestPurchaseInvoice(ERPNextTestSuite, StockTestMixin):
# Zero net effect on final TDS payable on invoice
expected_gle = [["_Test Account Cost for Goods Sold - _TC", 30000], ["Creditors - _TC", -30000]]
gl_entries = frappe.db.sql(
"""select account, sum(debit - credit) as amount
from `tabGL Entry`
where voucher_type='Purchase Invoice' and voucher_no=%s
group by account
order by account asc""",
(purchase_invoice.name),
as_dict=1,
gle = frappe.qb.DocType("GL Entry")
gl_entries = (
frappe.qb.from_(gle)
.select(gle.account, Sum(gle.debit - gle.credit).as_("amount"))
.where((gle.voucher_type == "Purchase Invoice") & (gle.voucher_no == purchase_invoice.name))
.groupby(gle.account)
.orderby(gle.account)
.run(as_dict=1)
)
for i, gle in enumerate(gl_entries):
self.assertEqual(expected_gle[i][0], gle.account)
self.assertEqual(expected_gle[i][1], gle.amount)
# DB account collation isn't portable across MariaDB/Postgres; compare order-independently.
self.assertEqual(
sorted((gle.account, gle.amount) for gle in gl_entries),
sorted((e[0], e[1]) for e in expected_gle),
)
payment_entry.load_from_db()
tax_allocated = sum(
@@ -2476,12 +2519,11 @@ class TestPurchaseInvoice(ERPNextTestSuite, StockTestMixin):
pi.insert()
pi.submit()
pr_gl_entries = frappe.db.sql(
"""select account, debit, credit
from `tabGL Entry` where voucher_type='Purchase Receipt' and voucher_no=%s
order by account asc""",
pr.name,
as_dict=1,
pr_gl_entries = frappe.get_all(
"GL Entry",
filters={"voucher_type": "Purchase Receipt", "voucher_no": pr.name},
fields=["account", "debit", "credit"],
order_by="account asc",
)
pr_expected_values = [
@@ -2494,12 +2536,11 @@ class TestPurchaseInvoice(ERPNextTestSuite, StockTestMixin):
self.assertEqual(pr_expected_values[i][1], gle.debit)
self.assertEqual(pr_expected_values[i][2], gle.credit)
pi_gl_entries = frappe.db.sql(
"""select account, debit, credit
from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
order by account asc""",
pi.name,
as_dict=1,
pi_gl_entries = frappe.get_all(
"GL Entry",
filters={"voucher_type": "Purchase Invoice", "voucher_no": pi.name},
fields=["account", "debit", "credit"],
order_by="account asc",
)
pi_expected_values = [
["Asset Received But Not Billed - _TC", 5000, 0],
@@ -3047,17 +3088,25 @@ def check_gl_entries(
gl_entries = query.run(as_dict=True)
for i, gle in enumerate(gl_entries):
doc.assertEqual(expected_gle[i][0], gle.account)
doc.assertEqual(expected_gle[i][1], gle.debit)
doc.assertEqual(expected_gle[i][2], gle.credit)
doc.assertEqual(getdate(expected_gle[i][3]), gle.posting_date)
# MariaDB and Postgres collate `account` differently, so the DB row order isn't portable.
# Match each actual GL row against the expected set instead of comparing positionally; like the
# original loop (which iterated the actual rows), extra expected rows are tolerated.
cols = additional_columns or []
if additional_columns:
j = 4
for col in additional_columns:
doc.assertEqual(expected_gle[i][j], gle[col])
j += 1
def _key(account, debit, credit, posting_date, extras):
return (account, flt(debit), flt(credit), getdate(posting_date), *(str(v) for v in extras))
remaining = {}
for e in expected_gle:
k = _key(e[0], e[1], e[2], e[3], e[4 : 4 + len(cols)])
remaining[k] = remaining.get(k, 0) + 1
for gle in gl_entries:
k = _key(gle.account, gle.debit, gle.credit, gle.posting_date, [gle[c] for c in cols])
doc.assertGreater(
remaining.get(k, 0), 0, msg=f"Unexpected GL entry {k}; expected one of {list(remaining)}"
)
remaining[k] -= 1
def create_tax_witholding_category(category_name, company, account):

View File

@@ -69,6 +69,7 @@ class TestRepostAccountingLedger(ERPNextTestSuite):
qb.from_(gl)
.select(gl.voucher_no, Sum(gl.debit).as_("debit"), Sum(gl.credit).as_("credit"))
.where((gl.voucher_no == si.name) & (gl.is_cancelled == 0))
.groupby(gl.voucher_no)
.run()
)
@@ -82,6 +83,7 @@ class TestRepostAccountingLedger(ERPNextTestSuite):
qb.from_(gl)
.select(gl.voucher_no, Sum(gl.debit).as_("debit"), Sum(gl.credit).as_("credit"))
.where((gl.voucher_no == si.name) & (gl.is_cancelled == 0))
.groupby(gl.voucher_no)
.run()
)

View File

@@ -745,12 +745,11 @@ class TestSalesInvoice(ERPNextTestSuite):
si.insert()
si.submit()
gl_entries = frappe.db.sql(
"""select account, debit, credit
from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
order by account asc""",
si.name,
as_dict=1,
gl_entries = frappe.get_all(
"GL Entry",
filters={"voucher_type": "Sales Invoice", "voucher_no": si.name},
fields=["account", "debit", "credit"],
order_by="account asc",
)
self.assertTrue(gl_entries)
@@ -781,10 +780,10 @@ class TestSalesInvoice(ERPNextTestSuite):
# cancel
si.cancel()
gle = frappe.db.sql(
"""select * from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no=%s""",
si.name,
gle = frappe.get_all(
"GL Entry",
filters={"voucher_type": "Sales Invoice", "voucher_no": si.name},
fields=["*"],
)
self.assertTrue(gle)
@@ -1201,12 +1200,11 @@ class TestSalesInvoice(ERPNextTestSuite):
si.insert()
si.submit()
gl_entries = frappe.db.sql(
"""select account, debit, credit
from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
order by account asc""",
si.name,
as_dict=1,
gl_entries = frappe.get_all(
"GL Entry",
filters={"voucher_type": "Sales Invoice", "voucher_no": si.name},
fields=["account", "debit", "credit"],
order_by="account asc",
)
self.assertTrue(gl_entries)
@@ -1229,10 +1227,10 @@ class TestSalesInvoice(ERPNextTestSuite):
# cancel
si.cancel()
gle = frappe.db.sql(
"""select * from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no=%s""",
si.name,
gle = frappe.get_all(
"GL Entry",
filters={"voucher_type": "Sales Invoice", "voucher_no": si.name},
fields=["*"],
)
self.assertTrue(gle)
@@ -1651,11 +1649,10 @@ class TestSalesInvoice(ERPNextTestSuite):
cash_amount -= pos.change_amount
# check stock ledger entries
sle = frappe.db.sql(
"""select * from `tabStock Ledger Entry`
where voucher_type = 'Sales Invoice' and voucher_no = %s""",
si.name,
as_dict=1,
sle = frappe.get_all(
"Stock Ledger Entry",
filters={"voucher_type": "Sales Invoice", "voucher_no": si.name},
fields=["*"],
)[0]
self.assertTrue(sle)
self.assertEqual(
@@ -1663,12 +1660,11 @@ class TestSalesInvoice(ERPNextTestSuite):
)
# check gl entries
gl_entries = frappe.db.sql(
"""select account, debit, credit
from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
order by account asc, debit asc, credit asc""",
si.name,
as_dict=1,
gl_entries = frappe.get_all(
"GL Entry",
filters={"voucher_type": "Sales Invoice", "voucher_no": si.name},
fields=["account", "debit", "credit"],
order_by="account asc, debit asc, credit asc",
)
self.assertTrue(gl_entries)
@@ -1695,15 +1691,15 @@ class TestSalesInvoice(ERPNextTestSuite):
self.assertEqual(expected_gl_entries[i][2], gle.credit)
si.cancel()
gle = frappe.db.sql(
"""select * from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no=%s""",
si.name,
gle = frappe.get_all(
"GL Entry",
filters={"voucher_type": "Sales Invoice", "voucher_no": si.name},
fields=["*"],
)
self.assertTrue(gle)
frappe.db.sql("delete from `tabPOS Profile`")
frappe.db.delete("POS Profile")
def test_bin_details_of_packed_item(self):
from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
@@ -1770,12 +1766,11 @@ class TestSalesInvoice(ERPNextTestSuite):
si.insert()
si.submit()
gl_entries = frappe.db.sql(
"""select account, debit, credit
from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
order by account asc""",
si.name,
as_dict=1,
gl_entries = frappe.get_all(
"GL Entry",
filters={"voucher_type": "Sales Invoice", "voucher_no": si.name},
fields=["account", "debit", "credit"],
order_by="account asc",
)
self.assertTrue(gl_entries)
@@ -1790,12 +1785,11 @@ class TestSalesInvoice(ERPNextTestSuite):
def test_sales_invoice_gl_entry_with_perpetual_inventory_non_stock_item(self):
si = create_sales_invoice(item="_Test Non Stock Item")
gl_entries = frappe.db.sql(
"""select account, debit, credit
from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
order by account asc""",
si.name,
as_dict=1,
gl_entries = frappe.get_all(
"GL Entry",
filters={"voucher_type": "Sales Invoice", "voucher_no": si.name},
fields=["account", "debit", "credit"],
order_by="account asc",
)
self.assertTrue(gl_entries)
@@ -1849,18 +1843,18 @@ class TestSalesInvoice(ERPNextTestSuite):
si.load_from_db()
self.assertTrue(
frappe.db.sql(
"""select name from `tabJournal Entry Account`
where reference_name=%s""",
si.name,
frappe.get_all(
"Journal Entry Account",
filters={"reference_name": si.name},
pluck="name",
)
)
self.assertTrue(
frappe.db.sql(
"""select name from `tabJournal Entry Account`
where reference_name=%s and credit_in_account_currency=300""",
si.name,
frappe.get_all(
"Journal Entry Account",
filters={"reference_name": si.name, "credit_in_account_currency": 300},
pluck="name",
)
)
@@ -2172,13 +2166,18 @@ class TestSalesInvoice(ERPNextTestSuite):
conversion_rate=50,
)
gl_entries = frappe.db.sql(
"""select account, account_currency, debit, credit,
debit_in_account_currency, credit_in_account_currency
from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
order by account asc""",
si.name,
as_dict=1,
gl_entries = frappe.get_all(
"GL Entry",
filters={"voucher_type": "Sales Invoice", "voucher_no": si.name},
fields=[
"account",
"account_currency",
"debit",
"credit",
"debit_in_account_currency",
"credit_in_account_currency",
],
order_by="account asc",
)
self.assertTrue(gl_entries)
@@ -2213,10 +2212,10 @@ class TestSalesInvoice(ERPNextTestSuite):
# cancel
si.cancel()
gle = frappe.db.sql(
"""select name from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no=%s""",
si.name,
gle = frappe.get_all(
"GL Entry",
filters={"voucher_type": "Sales Invoice", "voucher_no": si.name},
pluck="name",
)
self.assertTrue(gle)
@@ -2243,14 +2242,16 @@ class TestSalesInvoice(ERPNextTestSuite):
)
si.submit()
gl_entries = frappe.db.sql(
"""select transaction_currency, transaction_exchange_rate,
debit_in_transaction_currency, credit_in_transaction_currency
from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no=%s and account = 'Sales - _TC'
order by account asc""",
si.name,
as_dict=1,
gl_entries = frappe.get_all(
"GL Entry",
filters={"voucher_type": "Sales Invoice", "voucher_no": si.name, "account": "Sales - _TC"},
fields=[
"transaction_currency",
"transaction_exchange_rate",
"debit_in_transaction_currency",
"credit_in_transaction_currency",
],
order_by="account asc",
)
expected_gle = {
@@ -2595,12 +2596,11 @@ class TestSalesInvoice(ERPNextTestSuite):
]
)
gl_entries = frappe.db.sql(
"""select account, debit, credit
from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
order by account asc""",
si.name,
as_dict=1,
gl_entries = frappe.get_all(
"GL Entry",
filters={"voucher_type": "Sales Invoice", "voucher_no": si.name},
fields=["account", "debit", "credit"],
order_by="account asc",
)
for gle in gl_entries:
@@ -2652,13 +2652,12 @@ class TestSalesInvoice(ERPNextTestSuite):
"Sales - _TC": [0.0, 1272.20],
}
gl_entries = frappe.db.sql(
"""select account, sum(debit) as debit, sum(credit) as credit
from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
group by account
order by account asc""",
si.name,
as_dict=1,
gl_entries = frappe.get_all(
"GL Entry",
filters={"voucher_type": "Sales Invoice", "voucher_no": si.name},
fields=["account", {"SUM": "debit", "as": "debit"}, {"SUM": "credit", "as": "credit"}],
group_by="account",
order_by="account asc",
)
for gle in gl_entries:
@@ -2719,13 +2718,12 @@ class TestSalesInvoice(ERPNextTestSuite):
]
)
gl_entries = frappe.db.sql(
"""select account, sum(debit) as debit, sum(credit) as credit
from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
group by account
order by account asc""",
si.name,
as_dict=1,
gl_entries = frappe.get_all(
"GL Entry",
filters={"voucher_type": "Sales Invoice", "voucher_no": si.name},
fields=["account", {"SUM": "debit", "as": "debit"}, {"SUM": "credit", "as": "credit"}],
group_by="account",
order_by="account asc",
)
debit_credit_diff = 0
@@ -2735,7 +2733,9 @@ class TestSalesInvoice(ERPNextTestSuite):
self.assertEqual(expected_values[gle.account][2], gle.credit)
debit_credit_diff += gle.debit - gle.credit
self.assertEqual(debit_credit_diff, 0)
# Postgres returns DECIMAL columns as float (DEC2FLOAT), so a debit-credit sum carries a
# tiny FP residue where MariaDB's DECIMAL arithmetic is exact; assert it's ~0.
self.assertAlmostEqual(debit_credit_diff, 0)
round_off_gle = frappe.db.get_value(
"GL Entry",
@@ -2819,13 +2819,19 @@ class TestSalesInvoice(ERPNextTestSuite):
"Sales - _TC": {"cost_center": cost_center},
}
gl_entries = frappe.db.sql(
"""select account, cost_center, account_currency, debit, credit,
debit_in_account_currency, credit_in_account_currency
from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
order by account asc""",
si.name,
as_dict=1,
gl_entries = frappe.get_all(
"GL Entry",
filters={"voucher_type": "Sales Invoice", "voucher_no": si.name},
fields=[
"account",
"cost_center",
"account_currency",
"debit",
"credit",
"debit_in_account_currency",
"credit_in_account_currency",
],
order_by="account asc",
)
self.assertTrue(gl_entries)
@@ -2862,13 +2868,20 @@ class TestSalesInvoice(ERPNextTestSuite):
"Sales - _TC": {"project": item_project.name},
}
gl_entries = frappe.db.sql(
"""select account, cost_center, project, account_currency, debit, credit,
debit_in_account_currency, credit_in_account_currency
from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
order by account asc""",
sales_invoice.name,
as_dict=1,
gl_entries = frappe.get_all(
"GL Entry",
filters={"voucher_type": "Sales Invoice", "voucher_no": sales_invoice.name},
fields=[
"account",
"cost_center",
"project",
"account_currency",
"debit",
"credit",
"debit_in_account_currency",
"credit_in_account_currency",
],
order_by="account asc",
)
self.assertTrue(gl_entries)
@@ -2885,13 +2898,19 @@ class TestSalesInvoice(ERPNextTestSuite):
"Sales - _TC": {"cost_center": cost_center},
}
gl_entries = frappe.db.sql(
"""select account, cost_center, account_currency, debit, credit,
debit_in_account_currency, credit_in_account_currency
from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
order by account asc""",
si.name,
as_dict=1,
gl_entries = frappe.get_all(
"GL Entry",
filters={"voucher_type": "Sales Invoice", "voucher_no": si.name},
fields=[
"account",
"cost_center",
"account_currency",
"debit",
"credit",
"debit_in_account_currency",
"credit_in_account_currency",
],
order_by="account asc",
)
self.assertTrue(gl_entries)
@@ -4169,13 +4188,15 @@ class TestSalesInvoice(ERPNextTestSuite):
[deferred_account, 2022.47, 0.0, "2019-03-15"],
]
gl_entries = frappe.db.sql(
"""select account, debit, credit, posting_date
from `tabGL Entry`
where voucher_type='Journal Entry' and voucher_detail_no=%s and posting_date <= %s
order by posting_date asc, account asc""",
(si.items[0].name, si.posting_date),
as_dict=1,
gl_entries = frappe.get_all(
"GL Entry",
filters={
"voucher_type": "Journal Entry",
"voucher_detail_no": si.items[0].name,
"posting_date": ["<=", si.posting_date],
},
fields=["account", "debit", "credit", "posting_date"],
order_by="posting_date asc, account asc",
)
for i, gle in enumerate(gl_entries):
@@ -4817,7 +4838,8 @@ class TestSalesInvoice(ERPNextTestSuite):
{"account": "Temporary Opening - _TC", "debit": 0.0, "credit": 138.09, "is_opening": "Yes"},
]
self.assertEqual(len(actual), 4)
self.assertEqual(expected, actual)
# DB account collation isn't portable across MariaDB/Postgres; compare order-independently.
self.assertCountEqual(actual, expected)
@ERPNextTestSuite.change_settings("Accounts Settings", {"enable_common_party_accounting": True})
def test_common_party_with_foreign_currency_jv(self):
@@ -5122,7 +5144,7 @@ class TestSalesInvoice(ERPNextTestSuite):
def test_pos_sales_invoice_creation_during_pos_invoice_mode(self):
# Deleting all opening entry
frappe.db.sql("delete from `tabPOS Opening Entry`")
frappe.db.delete("POS Opening Entry")
with self.change_settings("POS Settings", {"invoice_type": "POS Invoice"}):
pos_profile = make_pos_profile()
@@ -5492,6 +5514,11 @@ def check_gl_entries(doc, voucher_no, expected_gle, posting_date, voucher_type="
doc.assertGreater(len(gl_entries), 0)
# MariaDB and Postgres collate `account` differently, so the DB ordering isn't portable;
# sort both sides identically (by the compared values) before the positional check.
gl_entries = sorted(gl_entries, key=lambda g: (g.account, g.debit, g.credit))
expected_gle = sorted(expected_gle, key=lambda e: (e[0], e[1], e[2]))
for i, gle in enumerate(gl_entries):
doc.assertEqual(expected_gle[i][0], gle.account)
doc.assertEqual(expected_gle[i][1], gle.debit)
@@ -5639,17 +5666,21 @@ def create_sales_invoice_against_cost_center(**args):
def get_outstanding_amount(against_voucher_type, against_voucher, account, party, party_type):
bal = flt(
frappe.db.sql(
"""
select sum(debit_in_account_currency) - sum(credit_in_account_currency)
from `tabGL Entry`
where against_voucher_type=%s and against_voucher=%s
and account = %s and party = %s and party_type = %s""",
(against_voucher_type, against_voucher, account, party, party_type),
)[0][0]
or 0.0
balance = frappe.get_all(
"GL Entry",
filters={
"against_voucher_type": against_voucher_type,
"against_voucher": against_voucher,
"account": account,
"party": party,
"party_type": party_type,
},
fields=[
{"SUM": "debit_in_account_currency", "as": "debit"},
{"SUM": "credit_in_account_currency", "as": "credit"},
],
)
bal = flt(balance[0].debit) - flt(balance[0].credit)
if against_voucher_type == "Purchase Invoice":
bal = bal * -1

View File

@@ -85,8 +85,8 @@ class TestAsset(AssetSetup):
self.assertRaises(frappe.ValidationError, asset.save)
def test_validate_item(self):
asset = create_asset(item_code="MacBook Pro", do_not_save=1)
item = frappe.get_doc("Item", "MacBook Pro")
asset = create_asset(item_code="Macbook Pro", do_not_save=1)
item = frappe.get_doc("Item", "Macbook Pro")
item.disabled = 1
item.save()
@@ -140,7 +140,7 @@ class TestAsset(AssetSetup):
)
gle = get_gl_entries("Purchase Invoice", pi.name)
self.assertSequenceEqual(gle, expected_gle)
self.assertCountEqual(gle, expected_gle)
pi.cancel()
asset.cancel()
@@ -283,7 +283,7 @@ class TestAsset(AssetSetup):
)
gle = get_gl_entries("Journal Entry", asset.journal_entry_for_scrap)
self.assertSequenceEqual(gle, expected_gle)
self.assertCountEqual(gle, expected_gle)
restore_asset(asset.name)
second_asset_depr_schedule.load_from_db()
@@ -362,7 +362,7 @@ class TestAsset(AssetSetup):
("Debtors - _TC", 25000.0, 0.0),
)
gle = get_gl_entries("Sales Invoice", si.name)
self.assertSequenceEqual(gle, expected_gle)
self.assertCountEqual(gle, expected_gle)
si.cancel()
self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Partially Depreciated")
@@ -436,7 +436,7 @@ class TestAsset(AssetSetup):
)
gle = get_gl_entries("Sales Invoice", si.name)
self.assertSequenceEqual(gle, expected_gle)
self.assertCountEqual(gle, expected_gle)
def test_asset_with_maintenance_required_status_after_sale(self):
asset = create_asset(
@@ -577,7 +577,7 @@ class TestAsset(AssetSetup):
)
pr_gle = get_gl_entries("Purchase Receipt", pr.name)
self.assertSequenceEqual(pr_gle, expected_gle)
self.assertCountEqual(pr_gle, expected_gle)
pi = make_invoice(pr.name)
pi.submit()
@@ -590,7 +590,7 @@ class TestAsset(AssetSetup):
)
pi_gle = get_gl_entries("Purchase Invoice", pi.name)
self.assertSequenceEqual(pi_gle, expected_gle)
self.assertCountEqual(pi_gle, expected_gle)
asset = frappe.db.get_value("Asset", {"purchase_receipt": pr.name, "docstatus": 0}, "name")
@@ -617,7 +617,7 @@ class TestAsset(AssetSetup):
expected_gle = (("_Test Fixed Asset - _TC", 5250.0, 0.0), ("CWIP Account - _TC", 0.0, 5250.0))
gle = get_gl_entries("Asset", asset_doc.name)
self.assertSequenceEqual(gle, expected_gle)
self.assertCountEqual(gle, expected_gle)
def test_asset_cwip_toggling_cases(self):
cwip = frappe.db.get_value("Asset Category", "Computers", "enable_cwip_accounting")
@@ -1732,14 +1732,18 @@ class TestDepreciationBasics(AssetSetup):
("_Test Depreciations - _TC", 30000.0, 0.0),
)
gle = frappe.db.sql(
"""select account, debit, credit from `tabGL Entry`
where against_voucher_type='Asset' and against_voucher = %s
order by account""",
asset.name,
)
gle = [
tuple(row)
for row in frappe.get_all(
"GL Entry",
filters={"against_voucher_type": "Asset", "against_voucher": asset.name},
fields=["account", "debit", "credit"],
order_by="account",
as_list=True,
)
]
self.assertSequenceEqual(gle, expected_gle)
self.assertCountEqual(gle, expected_gle)
self.assertEqual(asset.get("value_after_depreciation"), 70000)
def test_expected_value_change(self):

View File

@@ -2,6 +2,7 @@
# See license.txt
import frappe
from frappe.query_builder.functions import Sum
from frappe.utils import cint, flt, now_datetime
from erpnext.assets.doctype.asset.depreciation import post_depreciation_entries
@@ -549,34 +550,33 @@ def create_depreciation_asset(**args):
def get_actual_gle_dict(name):
gle = frappe.qb.DocType("GL Entry")
diff = Sum(gle.debit - gle.credit)
return dict(
frappe.db.sql(
"""
select account, sum(debit-credit) as diff
from `tabGL Entry`
where voucher_type = 'Asset Capitalization' and voucher_no = %s
group by account
having diff != 0
""",
name,
)
frappe.qb.from_(gle)
.select(gle.account, diff.as_("diff"))
.where((gle.voucher_type == "Asset Capitalization") & (gle.voucher_no == name))
.groupby(gle.account)
.having(diff != 0)
.run()
)
def get_actual_sle_dict(name):
sles = frappe.db.sql(
"""
select
item_code, warehouse,
sum(actual_qty) as actual_qty,
sum(stock_value_difference) as stock_value_difference
from `tabStock Ledger Entry`
where voucher_type = 'Asset Capitalization' and voucher_no = %s
group by item_code, warehouse
having actual_qty != 0
""",
name,
as_dict=1,
sle = frappe.qb.DocType("Stock Ledger Entry")
actual_qty = Sum(sle.actual_qty)
sles = (
frappe.qb.from_(sle)
.select(
sle.item_code,
sle.warehouse,
actual_qty.as_("actual_qty"),
Sum(sle.stock_value_difference).as_("stock_value_difference"),
)
.where((sle.voucher_type == "Asset Capitalization") & (sle.voucher_no == name))
.groupby(sle.item_code, sle.warehouse)
.having(actual_qty != 0)
.run(as_dict=1)
)
sle_dict = {}

View File

@@ -477,10 +477,8 @@ class TestPurchaseOrder(ERPNextTestSuite):
item_doc.save()
else:
# update valid from
frappe.db.sql(
"""UPDATE `tabItem Tax` set valid_from = CURRENT_DATE
where parent = %(item)s and item_tax_template = %(tax)s""",
{"item": item, "tax": tax_template},
frappe.db.set_value(
"Item Tax", {"parent": item, "item_tax_template": tax_template}, "valid_from", nowdate()
)
po = create_purchase_order(item_code=item, qty=1, do_not_save=1)
@@ -527,10 +525,8 @@ class TestPurchaseOrder(ERPNextTestSuite):
self.assertEqual(po.taxes[1].total, 840)
# teardown
frappe.db.sql(
"""UPDATE `tabItem Tax` set valid_from = NULL
where parent = %(item)s and item_tax_template = %(tax)s""",
{"item": item, "tax": tax_template},
frappe.db.set_value(
"Item Tax", {"parent": item, "item_tax_template": tax_template}, "valid_from", None
)
po.cancel()
po.delete()
@@ -652,7 +648,7 @@ class TestPurchaseOrder(ERPNextTestSuite):
def test_purchase_order_on_hold(self):
po = create_purchase_order(item_code="_Test Product Bundle Item")
po.db_set("Status", "On Hold")
po.db_set("status", "On Hold")
pi = make_pi_from_po(po.name)
pr = make_purchase_receipt(po.name)
self.assertRaises(frappe.ValidationError, pr.submit)

View File

@@ -56,7 +56,7 @@ class TestEmployee(ERPNextTestSuite):
frappe.qb.from_(Employee)
.select(Employee.name)
.where(Criterion.all(build_qb_match_conditions("Employee")))
.orderby(Employee.Name)
.orderby(Employee.name)
).run(pluck=Employee.name)
employee_list = frappe.db.get_list("Employee", pluck="name", order_by="name")

View File

@@ -924,12 +924,15 @@ class TestDeliveryNote(ERPNextTestSuite):
self.assertTrue(gl_entries)
stock_value_difference = abs(
frappe.db.sql(
"""select sum(stock_value_difference)
from `tabStock Ledger Entry` where voucher_type='Delivery Note' and voucher_no=%s
and warehouse='Stores - TCP1'""",
dn.name,
)[0][0]
frappe.get_all(
"Stock Ledger Entry",
filters={
"voucher_type": "Delivery Note",
"voucher_no": dn.name,
"warehouse": "Stores - TCP1",
},
fields=[{"SUM": "stock_value_difference", "as": "svd"}],
)[0].svd
)
expected_values = {
@@ -955,7 +958,7 @@ class TestDeliveryNote(ERPNextTestSuite):
dn.submit()
update_delivery_note_status(dn.name, "Closed")
self.assertEqual(frappe.db.get_value("Delivery Note", dn.name, "Status"), "Closed")
self.assertEqual(frappe.db.get_value("Delivery Note", dn.name, "status"), "Closed")
# Check cancelling closed delivery note
dn.load_from_db()

View File

@@ -1267,6 +1267,10 @@ class TestPurchaseReceipt(ERPNextTestSuite):
for sle in sl_entries:
self.assertEqual(expected_sle[sle.warehouse], sle.actual_qty)
# MariaDB and Postgres collate `account` differently, so the DB ordering isn't portable;
# sort both sides identically (by the compared values) before the positional check.
gl_entries = sorted(gl_entries, key=lambda g: (g.account, g.debit, g.credit))
expected_gle = sorted(expected_gle, key=lambda e: (e[0], e[1], e[2]))
for i, gle in enumerate(gl_entries):
self.assertEqual(gle.account, expected_gle[i][0])
self.assertEqual(gle.debit, expected_gle[i][1])
@@ -6099,12 +6103,11 @@ def prepare_data_for_internal_transfer():
def get_sl_entries(voucher_type, voucher_no):
return frappe.db.sql(
""" select actual_qty, warehouse, stock_value_difference
from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s
order by posting_time desc""",
(voucher_type, voucher_no),
as_dict=1,
return frappe.get_all(
"Stock Ledger Entry",
filters={"voucher_type": voucher_type, "voucher_no": voucher_no},
fields=["actual_qty", "warehouse", "stock_value_difference"],
order_by="posting_time desc",
)

View File

@@ -7,7 +7,7 @@ from uuid import uuid4
import frappe
from frappe.core.page.permission_manager.permission_manager import reset
from frappe.query_builder.functions import Timestamp
from frappe.query_builder.functions import CombineDatetime
from frappe.utils import add_days, add_to_date, flt, today
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
@@ -34,6 +34,54 @@ class TestStockLedgerEntry(ERPNextTestSuite, StockTestMixin):
create_items()
reset("Stock Entry")
def test_incoming_value_for_transferred_serial_no_is_deterministic(self):
"""get_incoming_value_for_serial_nos picks the latest SLE (posting_date desc, limit 1) for a
serial transferred to another company. posting_date alone is non-total, so two same-date SLEs
with different incoming_rate could be resolved differently on MariaDB vs Postgres. creation/name
tie-breaks make the latest SLE win identically on both engines."""
from erpnext.stock.stock_ledger import update_entries_after
item = "_Test Serialized Item"
serial = "_Test SN Tie 9"
company_a, company_b = "_Test Company", "_Test Company 1"
if frappe.db.exists("Serial No", serial):
frappe.delete_doc("Serial No", serial, force=1)
frappe.get_doc(
{"doctype": "Serial No", "serial_no": serial, "item_code": item, "company": company_b}
).insert(ignore_permissions=True)
def mk_sle(name, rate):
if frappe.db.exists("Stock Ledger Entry", name):
frappe.delete_doc("Stock Ledger Entry", name, force=1)
doc = frappe.get_doc(
{
"doctype": "Stock Ledger Entry",
"item_code": item,
"warehouse": "_Test Warehouse - _TC",
"company": company_a,
"posting_date": "2026-06-01",
"posting_time": "10:00:00",
"actual_qty": 1,
"incoming_rate": rate,
"is_cancelled": 0,
"serial_no": serial,
"voucher_type": "Stock Entry",
"voucher_no": "TEST-TIE",
}
)
doc.name = name
doc.flags.name_set = True
doc.db_insert()
mk_sle("MAT-SLE-TIE-A", 100)
mk_sle("MAT-SLE-TIE-B", 200) # later/larger name -> deterministic winner
value = update_entries_after.get_incoming_value_for_serial_nos(
None, frappe._dict(company=company_a), [serial]
)
# the latest (creation/name desc) same-date SLE wins -> 200 on both engines
self.assertEqual(value, 200.0)
def test_item_cost_reposting(self):
company = "_Test Company"
@@ -1275,7 +1323,7 @@ class TestStockLedgerEntry(ERPNextTestSuite, StockTestMixin):
.where(sle.voucher_no == transfer.name)
.where(sle.voucher_type == transfer.doctype)
.where(sle.is_cancelled == 0)
.orderby(Timestamp(sle.posting_date, sle.posting_time))
.orderby(CombineDatetime(sle.posting_date, sle.posting_time))
.orderby(sle.creation)
.run(as_dict=True)
)
@@ -1530,17 +1578,12 @@ def create_delivery_note_entries_for_batchwise_item_valuation_test(dn_entry_list
def fetch_sle_details_for_doc_list(doc_list, columns, as_dict=1):
return frappe.db.sql(
f"""
SELECT { ', '.join(columns)}
FROM `tabStock Ledger Entry`
WHERE
voucher_no IN %(voucher_nos)s
and docstatus = 1
ORDER BY timestamp(posting_date, posting_time) ASC, CREATION ASC
""",
dict(voucher_nos=[doc.name for doc in doc_list]),
as_dict=as_dict,
return frappe.get_all(
"Stock Ledger Entry",
filters={"voucher_no": ["in", [doc.name for doc in doc_list]], "docstatus": 1},
fields=columns,
order_by="posting_datetime asc, creation asc",
as_list=not as_dict,
)

View File

@@ -40,24 +40,15 @@ class TestStockBalance(ERPNextTestSuite):
make_stock_entry(item_code=item_code, **movement)
def assertInvariants(self, rows):
last_balance = frappe.db.sql(
"""
WITH last_balances AS (
SELECT item_code, warehouse,
stock_value, qty_after_transaction,
ROW_NUMBER() OVER (PARTITION BY item_code, warehouse
ORDER BY timestamp(posting_date, posting_time) desc, creation desc)
AS rn
FROM `tabStock Ledger Entry`
where is_cancelled=0
)
SELECT * FROM last_balances WHERE rn = 1""",
as_dict=True,
)
item_wh_stock = _dict()
for line in last_balance:
# Latest balance per (item_code, warehouse): first row wins because of the desc ordering.
for line in frappe.get_all(
"Stock Ledger Entry",
filters={"is_cancelled": 0},
fields=["item_code", "warehouse", "stock_value", "qty_after_transaction"],
order_by="posting_datetime desc, creation desc",
):
item_wh_stock.setdefault((line.item_code, line.warehouse), line)
for row in rows:

View File

@@ -1,7 +1,7 @@
import json
import frappe
from frappe.query_builder.functions import Timestamp
from frappe.query_builder.functions import CombineDatetime
from erpnext.stock.utils import scan_barcode
from erpnext.tests.utils import ERPNextTestSuite
@@ -35,7 +35,7 @@ class StockTestMixin:
query = query.where(sle[key] == value)
sles = (
query.orderby(Timestamp(sle.posting_date, sle.posting_time))
query.orderby(CombineDatetime(sle.posting_date, sle.posting_time))
.orderby(sle.creation)
.run(as_dict=True)
)