diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py index 0fd89e48d50..7d0675b23e2 100644 --- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py +++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py @@ -1340,9 +1340,11 @@ def get_pe_matching_query( ref_condition = pe.reference_no == transaction.reference_number ref_rank = frappe.qb.terms.Case().when(ref_condition, 1).else_(0) - amount_equality = pe.paid_amount == transaction.unallocated_amount + amount_field = pe.received_amount_after_tax if account_from_to == "paid_to" else pe.paid_amount_after_tax + + amount_equality = amount_field == transaction.unallocated_amount amount_rank = frappe.qb.terms.Case().when(amount_equality, 1).else_(0) - amount_condition = amount_equality if exact_match else pe.paid_amount > 0.0 + amount_condition = amount_equality if exact_match else amount_field > 0.0 party_condition = ( (pe.party_type == transaction.party_type) & (pe.party == transaction.party) & pe.party.isnotnull() @@ -1359,7 +1361,7 @@ def get_pe_matching_query( (ref_rank + amount_rank + party_rank + 1).as_("rank"), ConstantColumn("Payment Entry").as_("doctype"), pe.name, - pe.base_paid_amount_after_tax.as_("paid_amount"), + amount_field.as_("paid_amount"), pe.reference_no, pe.reference_date, pe.party, diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/test_bank_reconciliation_tool.py b/erpnext/accounts/doctype/bank_reconciliation_tool/test_bank_reconciliation_tool.py index 1be8c5177c6..5bad7582dde 100644 --- a/erpnext/accounts/doctype/bank_reconciliation_tool/test_bank_reconciliation_tool.py +++ b/erpnext/accounts/doctype/bank_reconciliation_tool/test_bank_reconciliation_tool.py @@ -8,7 +8,9 @@ from frappe.utils import add_days, today from erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool import ( auto_reconcile_vouchers, + get_auto_reconcile_message, get_bank_transactions, + get_linked_payments, ) from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry from erpnext.accounts.test.accounts_mixin import AccountsTestMixin @@ -97,3 +99,103 @@ class TestBankReconciliationTool(ERPNextTestSuite, AccountsTestMixin): # assert API output post reconciliation transactions = get_bank_transactions(self.bank_account, from_date, to_date) self.assertEqual(len(transactions), 0) + + def make_bank_transaction(self, date, deposit=100, withdrawal=0): + return ( + frappe.get_doc( + { + "doctype": "Bank Transaction", + "date": date, + "deposit": deposit, + "withdrawal": withdrawal, + "bank_account": self.bank_account, + "currency": "INR", + } + ) + .save() + .submit() + ) + + def get_matching_payment_entries(self, bank_transaction, exact_match=False): + document_types = ["payment_entry", "exact_match"] if exact_match else ["payment_entry"] + vouchers = get_linked_payments( + bank_transaction, + document_types, + from_date=add_days(today(), -1), + to_date=today(), + ) + return [v for v in vouchers if v.get("doctype") == "Payment Entry"] + + def test_get_bank_transactions_excludes_dates_after_to_date(self): + self.make_bank_transaction(date=today()) + names = [t.name for t in get_bank_transactions(self.bank_account, to_date=add_days(today(), -1))] + self.assertEqual(names, []) + + def test_deposit_matches_amount_received_in_bank_account(self): + # money leaves another bank account and lands here minus a charge, so the two sides differ + payment = frappe.get_doc( + { + "doctype": "Payment Entry", + "payment_type": "Internal Transfer", + "company": self.company, + "posting_date": today(), + "paid_from": "_Test Bank - _TC", + "paid_to": self.bank, + "paid_amount": 3537.64, + "received_amount": 3460.52, + "reference_no": "TRF-001", + "reference_date": today(), + } + ) + payment.set_missing_values() + payment.set_exchange_rate() + payment.set_amounts() + payment.deductions[-1].account = "_Test Exchange Gain/Loss - _TC" + payment.deductions[-1].cost_center = "_Test Cost Center - _TC" + payment = payment.save().submit() + + transaction = self.make_bank_transaction(date=today(), deposit=3460.52) + + # the received side is what reached this bank account, so that is what is shown + matches = self.get_matching_payment_entries(transaction.name) + self.assertEqual([m["name"] for m in matches], [payment.name]) + self.assertEqual(matches[0]["paid_amount"], 3460.52) + + # and what the exact match compares against + exact_matches = self.get_matching_payment_entries(transaction.name, exact_match=True) + self.assertEqual([m["name"] for m in exact_matches], [payment.name]) + + def test_withdrawal_matches_amount_paid_from_bank_account(self): + payment = create_payment_entry( + company=self.company, + payment_type="Pay", + party_type="Supplier", + party="_Test Supplier", + paid_from=self.bank, + paid_to="Creditors - _TC", + paid_amount=1250, + ) + payment = payment.save().submit() + + transaction = self.make_bank_transaction(date=today(), deposit=0, withdrawal=1250) + + exact_matches = self.get_matching_payment_entries(transaction.name, exact_match=True) + self.assertEqual([m["name"] for m in exact_matches], [payment.name]) + self.assertEqual(exact_matches[0]["paid_amount"], 1250) + + def test_auto_reconcile_message_for_no_matches(self): + message, indicator = get_auto_reconcile_message([], []) + self.assertEqual(indicator, "blue") + self.assertIn("No matches", message) + + def test_auto_reconcile_message_counts_and_pluralizes(self): + # reconciled count is reported and the indicator turns green + message, indicator = get_auto_reconcile_message([], ["t1", "t2"]) + self.assertEqual(indicator, "green") + self.assertIn("2 Transaction(s) Reconciled", message) + + # partially-reconciled label is singular for one, plural for many + singular, _ = get_auto_reconcile_message(["p1"], []) + self.assertIn("1 Transaction Partially Reconciled", singular) + plural, _ = get_auto_reconcile_message(["p1", "p2"], []) + self.assertIn("2 Transactions Partially Reconciled", plural)