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 3da5ac32a42..9a7a9a31d53 100644 --- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py +++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py @@ -7,7 +7,9 @@ import json import frappe from frappe import _ from frappe.model.document import Document +from frappe.query_builder.custom import ConstantColumn from frappe.utils import cint, flt +from pypika.terms import Parameter from erpnext import get_default_cost_center from erpnext.accounts.doctype.bank_transaction.bank_transaction import get_total_allocated_amount @@ -15,7 +17,7 @@ from erpnext.accounts.report.bank_reconciliation_statement.bank_reconciliation_s get_amounts_not_reflected_in_system, get_entries, ) -from erpnext.accounts.utils import get_balance_on +from erpnext.accounts.utils import get_account_currency, get_balance_on class BankReconciliationTool(Document): @@ -283,68 +285,68 @@ def auto_reconcile_vouchers( to_reference_date=None, ): frappe.flags.auto_reconcile_vouchers = True - document_types = ["payment_entry", "journal_entry"] + reconciled, partially_reconciled = set(), set() + bank_transactions = get_bank_transactions(bank_account) - matched_transaction = [] for transaction in bank_transactions: linked_payments = get_linked_payments( transaction.name, - document_types, + ["payment_entry", "journal_entry"], from_date, to_date, filter_by_reference_date, from_reference_date, to_reference_date, ) - vouchers = [] - for r in linked_payments: - vouchers.append( - { - "payment_doctype": r[1], - "payment_name": r[2], - "amount": r[4], - } - ) - transaction = frappe.get_doc("Bank Transaction", transaction.name) - account = frappe.db.get_value("Bank Account", transaction.bank_account, "account") - matched_trans = 0 - for voucher in vouchers: - gl_entry = frappe.db.get_value( - "GL Entry", - dict( - account=account, voucher_type=voucher["payment_doctype"], voucher_no=voucher["payment_name"] - ), - ["credit", "debit"], - as_dict=1, - ) - gl_amount, transaction_amount = ( - (gl_entry.credit, transaction.deposit) - if gl_entry.credit > 0 - else (gl_entry.debit, transaction.withdrawal) - ) - allocated_amount = gl_amount if gl_amount >= transaction_amount else transaction_amount - transaction.append( - "payment_entries", - { - "payment_document": voucher["payment_doctype"], - "payment_entry": voucher["payment_name"], - "allocated_amount": allocated_amount, + + if not linked_payments: + continue + + vouchers = list( + map( + lambda entry: { + "payment_doctype": entry.get("doctype"), + "payment_name": entry.get("name"), + "amount": entry.get("paid_amount"), }, + linked_payments, ) - matched_transaction.append(str(transaction.name)) - transaction.save() - transaction.update_allocations() - matched_transaction_len = len(set(matched_transaction)) - if matched_transaction_len == 0: - frappe.msgprint(_("No matching references found for auto reconciliation")) - elif matched_transaction_len == 1: - frappe.msgprint(_("{0} transaction is reconcilied").format(matched_transaction_len)) - else: - frappe.msgprint(_("{0} transactions are reconcilied").format(matched_transaction_len)) + ) + + updated_transaction = reconcile_vouchers(transaction.name, json.dumps(vouchers)) + + if updated_transaction.status == "Reconciled": + reconciled.add(updated_transaction.name) + elif flt(transaction.unallocated_amount) != flt(updated_transaction.unallocated_amount): + # Partially reconciled (status = Unreconciled & unallocated amount changed) + partially_reconciled.add(updated_transaction.name) + + alert_message, indicator = get_auto_reconcile_message(partially_reconciled, reconciled) + frappe.msgprint(title=_("Auto Reconciliation"), msg=alert_message, indicator=indicator) frappe.flags.auto_reconcile_vouchers = False + return reconciled, partially_reconciled - return frappe.get_doc("Bank Transaction", transaction.name) + +def get_auto_reconcile_message(partially_reconciled, reconciled): + """Returns alert message and indicator for auto reconciliation depending on result state.""" + alert_message, indicator = "", "blue" + if not partially_reconciled and not reconciled: + alert_message = _("No matches occurred via auto reconciliation") + return alert_message, indicator + + indicator = "green" + if reconciled: + alert_message += _("{0} Transaction(s) Reconciled").format(len(reconciled)) + alert_message += "
" + + if partially_reconciled: + alert_message += _("{0} {1} Partially Reconciled").format( + len(partially_reconciled), + _("Transactions") if len(partially_reconciled) > 1 else _("Transaction"), + ) + + return alert_message, indicator @frappe.whitelist() @@ -390,19 +392,13 @@ def subtract_allocations(gl_account, vouchers): "Look up & subtract any existing Bank Transaction allocations" copied = [] for voucher in vouchers: - rows = get_total_allocated_amount(voucher[1], voucher[2]) - amount = None - for row in rows: - if row["gl_account"] == gl_account: - amount = row["total"] - break + rows = get_total_allocated_amount(voucher.get("doctype"), voucher.get("name")) + filtered_row = list(filter(lambda row: row.get("gl_account") == gl_account, rows)) - if amount: - l = list(voucher) - l[3] -= amount - copied.append(tuple(l)) - else: - copied.append(voucher) + if amount := None if not filtered_row else filtered_row[0]["total"]: + voucher["paid_amount"] -= amount + + copied.append(voucher) return copied @@ -418,6 +414,18 @@ def check_matching( to_reference_date, ): exact_match = True if "exact_match" in document_types else False + queries = get_queries( + bank_account, + company, + transaction, + document_types, + from_date, + to_date, + filter_by_reference_date, + from_reference_date, + to_reference_date, + exact_match, + ) filters = { "amount": transaction.unallocated_amount, @@ -429,30 +437,15 @@ def check_matching( } matching_vouchers = [] + for query in queries: + matching_vouchers.extend(frappe.db.sql(query, filters, as_dict=True)) - # get matching vouchers from all the apps - for method_name in frappe.get_hooks("get_matching_vouchers_for_bank_reconciliation"): - matching_vouchers.extend( - frappe.get_attr(method_name)( - bank_account, - company, - transaction, - document_types, - from_date, - to_date, - filter_by_reference_date, - from_reference_date, - to_reference_date, - exact_match, - filters, - ) - or [] - ) - - return sorted(matching_vouchers, key=lambda x: x[0], reverse=True) if matching_vouchers else [] + return ( + sorted(matching_vouchers, key=lambda x: x["rank"], reverse=True) if matching_vouchers else [] + ) -def get_matching_vouchers_for_bank_reconciliation( +def get_queries( bank_account, company, transaction, @@ -463,7 +456,6 @@ def get_matching_vouchers_for_bank_reconciliation( from_reference_date, to_reference_date, exact_match, - filters, ): # get queries to get matching vouchers account_from_to = "paid_to" if transaction.deposit > 0.0 else "paid_from" @@ -488,17 +480,7 @@ def get_matching_vouchers_for_bank_reconciliation( or [] ) - vouchers = [] - - for query in queries: - vouchers.extend( - frappe.db.sql( - query, - filters, - ) - ) - - return vouchers + return queries def get_matching_queries( @@ -515,6 +497,8 @@ def get_matching_queries( to_reference_date, ): queries = [] + currency = get_account_currency(bank_account) + if "payment_entry" in document_types: query = get_pe_matching_query( exact_match, @@ -541,12 +525,12 @@ def get_matching_queries( queries.append(query) if transaction.deposit > 0.0 and "sales_invoice" in document_types: - query = get_si_matching_query(exact_match) + query = get_si_matching_query(exact_match, currency) queries.append(query) if transaction.withdrawal > 0.0: if "purchase_invoice" in document_types: - query = get_pi_matching_query(exact_match) + query = get_pi_matching_query(exact_match, currency) queries.append(query) if "bank_transaction" in document_types: @@ -560,33 +544,48 @@ def get_bt_matching_query(exact_match, transaction): # get matching bank transaction query # find bank transactions in the same bank account with opposite sign # same bank account must have same company and currency + bt = frappe.qb.DocType("Bank Transaction") + field = "deposit" if transaction.withdrawal > 0.0 else "withdrawal" + amount_equality = getattr(bt, field) == transaction.unallocated_amount + amount_rank = frappe.qb.terms.Case().when(amount_equality, 1).else_(0) + amount_condition = amount_equality if exact_match else getattr(bt, field) > 0.0 - return f""" + ref_rank = ( + frappe.qb.terms.Case().when(bt.reference_number == transaction.reference_number, 1).else_(0) + ) + unallocated_rank = ( + frappe.qb.terms.Case().when(bt.unallocated_amount == transaction.unallocated_amount, 1).else_(0) + ) - SELECT - (CASE WHEN reference_number = %(reference_no)s THEN 1 ELSE 0 END - + CASE WHEN {field} = %(amount)s THEN 1 ELSE 0 END - + CASE WHEN ( party_type = %(party_type)s AND party = %(party)s ) THEN 1 ELSE 0 END - + CASE WHEN unallocated_amount = %(amount)s THEN 1 ELSE 0 END - + 1) AS rank, - 'Bank Transaction' AS doctype, - name, - unallocated_amount AS paid_amount, - reference_number AS reference_no, - date AS reference_date, - party, - party_type, - date AS posting_date, - currency - FROM - `tabBank Transaction` - WHERE - status != 'Reconciled' - AND name != '{transaction.name}' - AND bank_account = '{transaction.bank_account}' - AND {field} {'= %(amount)s' if exact_match else '> 0.0'} - """ + party_condition = ( + (bt.party_type == transaction.party_type) + & (bt.party == transaction.party) + & bt.party.isnotnull() + ) + party_rank = frappe.qb.terms.Case().when(party_condition, 1).else_(0) + + query = ( + frappe.qb.from_(bt) + .select( + (ref_rank + amount_rank + party_rank + unallocated_rank + 1).as_("rank"), + ConstantColumn("Bank Transaction").as_("doctype"), + bt.name, + bt.unallocated_amount.as_("paid_amount"), + bt.reference_number.as_("reference_no"), + bt.date.as_("reference_date"), + bt.party, + bt.party_type, + bt.date.as_("posting_date"), + bt.currency, + ) + .where(bt.status != "Reconciled") + .where(bt.name != transaction.name) + .where(bt.bank_account == transaction.bank_account) + .where(amount_condition) + .where(bt.docstatus == 1) + ) + return str(query) def get_pe_matching_query( @@ -600,45 +599,56 @@ def get_pe_matching_query( to_reference_date, ): # get matching payment entries query - if transaction.deposit > 0.0: - currency_field = "paid_to_account_currency as currency" - else: - currency_field = "paid_from_account_currency as currency" - filter_by_date = f"AND posting_date between '{from_date}' and '{to_date}'" - order_by = " posting_date" - filter_by_reference_no = "" + to_from = "to" if transaction.deposit > 0.0 else "from" + currency_field = f"paid_{to_from}_account_currency" + payment_type = "Receive" if transaction.deposit > 0.0 else "Pay" + pe = frappe.qb.DocType("Payment Entry") + + 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_rank = frappe.qb.terms.Case().when(amount_equality, 1).else_(0) + amount_condition = amount_equality if exact_match else pe.paid_amount > 0.0 + + party_condition = ( + (pe.party_type == transaction.party_type) + & (pe.party == transaction.party) + & pe.party.isnotnull() + ) + party_rank = frappe.qb.terms.Case().when(party_condition, 1).else_(0) + + filter_by_date = pe.posting_date.between(from_date, to_date) if cint(filter_by_reference_date): - filter_by_date = f"AND reference_date between '{from_reference_date}' and '{to_reference_date}'" - order_by = " reference_date" + filter_by_date = pe.reference_date.between(from_reference_date, to_reference_date) + + query = ( + frappe.qb.from_(pe) + .select( + (ref_rank + amount_rank + party_rank + 1).as_("rank"), + ConstantColumn("Payment Entry").as_("doctype"), + pe.name, + pe.paid_amount, + pe.reference_no, + pe.reference_date, + pe.party, + pe.party_type, + pe.posting_date, + getattr(pe, currency_field).as_("currency"), + ) + .where(pe.docstatus == 1) + .where(pe.payment_type.isin([payment_type, "Internal Transfer"])) + .where(pe.clearance_date.isnull()) + .where(getattr(pe, account_from_to) == Parameter("%(bank_account)s")) + .where(amount_condition) + .where(filter_by_date) + .orderby(pe.reference_date if cint(filter_by_reference_date) else pe.posting_date) + ) + if frappe.flags.auto_reconcile_vouchers == True: - filter_by_reference_no = f"AND reference_no = '{transaction.reference_number}'" - return f""" - SELECT - (CASE WHEN reference_no=%(reference_no)s THEN 1 ELSE 0 END - + CASE WHEN (party_type = %(party_type)s AND party = %(party)s ) THEN 1 ELSE 0 END - + CASE WHEN paid_amount = %(amount)s THEN 1 ELSE 0 END - + 1 ) AS rank, - 'Payment Entry' as doctype, - name, - paid_amount, - reference_no, - reference_date, - party, - party_type, - posting_date, - {currency_field} - FROM - `tabPayment Entry` - WHERE - docstatus = 1 - AND payment_type IN (%(payment_type)s, 'Internal Transfer') - AND ifnull(clearance_date, '') = "" - AND {account_from_to} = %(bank_account)s - AND paid_amount {'= %(amount)s' if exact_match else '> 0.0'} - {filter_by_date} - {filter_by_reference_no} - order by{order_by} - """ + query = query.where(ref_condition) + + return str(query) def get_je_matching_query( @@ -655,100 +665,121 @@ def get_je_matching_query( # So one bank could have both types of bank accounts like asset and liability # So cr_or_dr should be judged only on basis of withdrawal and deposit and not account type cr_or_dr = "credit" if transaction.withdrawal > 0.0 else "debit" - filter_by_date = f"AND je.posting_date between '{from_date}' and '{to_date}'" - order_by = " je.posting_date" - filter_by_reference_no = "" + je = frappe.qb.DocType("Journal Entry") + jea = frappe.qb.DocType("Journal Entry Account") + + ref_condition = je.cheque_no == transaction.reference_number + ref_rank = frappe.qb.terms.Case().when(ref_condition, 1).else_(0) + + amount_field = f"{cr_or_dr}_in_account_currency" + amount_equality = getattr(jea, amount_field) == transaction.unallocated_amount + amount_rank = frappe.qb.terms.Case().when(amount_equality, 1).else_(0) + + filter_by_date = je.posting_date.between(from_date, to_date) if cint(filter_by_reference_date): - filter_by_date = f"AND je.cheque_date between '{from_reference_date}' and '{to_reference_date}'" - order_by = " je.cheque_date" - if frappe.flags.auto_reconcile_vouchers == True: - filter_by_reference_no = f"AND je.cheque_no = '{transaction.reference_number}'" - return f""" - SELECT - (CASE WHEN je.cheque_no=%(reference_no)s THEN 1 ELSE 0 END - + CASE WHEN jea.{cr_or_dr}_in_account_currency = %(amount)s THEN 1 ELSE 0 END - + 1) AS rank , - 'Journal Entry' AS doctype, + filter_by_date = je.cheque_date.between(from_reference_date, to_reference_date) + + query = ( + frappe.qb.from_(jea) + .join(je) + .on(jea.parent == je.name) + .select( + (ref_rank + amount_rank + 1).as_("rank"), + ConstantColumn("Journal Entry").as_("doctype"), je.name, - jea.{cr_or_dr}_in_account_currency AS paid_amount, - je.cheque_no AS reference_no, - je.cheque_date AS reference_date, - je.pay_to_recd_from AS party, + getattr(jea, amount_field).as_("paid_amount"), + je.cheque_no.as_("reference_no"), + je.cheque_date.as_("reference_date"), + je.pay_to_recd_from.as_("party"), jea.party_type, je.posting_date, - jea.account_currency AS currency - FROM - `tabJournal Entry Account` AS jea - JOIN - `tabJournal Entry` AS je - ON - jea.parent = je.name - WHERE - je.docstatus = 1 - AND je.voucher_type NOT IN ('Opening Entry') - AND (je.clearance_date IS NULL OR je.clearance_date='0000-00-00') - AND jea.account = %(bank_account)s - AND jea.{cr_or_dr}_in_account_currency {'= %(amount)s' if exact_match else '> 0.0'} - AND je.docstatus = 1 - {filter_by_date} - {filter_by_reference_no} - order by {order_by} - """ + jea.account_currency.as_("currency"), + ) + .where(je.docstatus == 1) + .where(je.voucher_type != "Opening Entry") + .where(je.clearance_date.isnull()) + .where(jea.account == Parameter("%(bank_account)s")) + .where(amount_equality if exact_match else getattr(jea, amount_field) > 0.0) + .where(je.docstatus == 1) + .where(filter_by_date) + .orderby(je.cheque_date if cint(filter_by_reference_date) else je.posting_date) + ) + + if frappe.flags.auto_reconcile_vouchers == True: + query = query.where(ref_condition) + + return str(query) -def get_si_matching_query(exact_match): +def get_si_matching_query(exact_match, currency): # get matching sales invoice query - return f""" - SELECT - ( CASE WHEN si.customer = %(party)s THEN 1 ELSE 0 END - + CASE WHEN sip.amount = %(amount)s THEN 1 ELSE 0 END - + 1 ) AS rank, - 'Sales Invoice' as doctype, + si = frappe.qb.DocType("Sales Invoice") + sip = frappe.qb.DocType("Sales Invoice Payment") + + amount_equality = sip.amount == Parameter("%(amount)s") + amount_rank = frappe.qb.terms.Case().when(amount_equality, 1).else_(0) + amount_condition = amount_equality if exact_match else sip.amount > 0.0 + + party_condition = si.customer == Parameter("%(party)s") + party_rank = frappe.qb.terms.Case().when(party_condition, 1).else_(0) + + query = ( + frappe.qb.from_(sip) + .join(si) + .on(sip.parent == si.name) + .select( + (party_rank + amount_rank + 1).as_("rank"), + ConstantColumn("Sales Invoice").as_("doctype"), si.name, - sip.amount as paid_amount, - '' as reference_no, - '' as reference_date, - si.customer as party, - 'Customer' as party_type, + sip.amount.as_("paid_amount"), + ConstantColumn("").as_("reference_no"), + ConstantColumn("").as_("reference_date"), + si.customer.as_("party"), + ConstantColumn("Customer").as_("party_type"), si.posting_date, - si.currency + si.currency, + ) + .where(si.docstatus == 1) + .where(sip.clearance_date.isnull()) + .where(sip.account == Parameter("%(bank_account)s")) + .where(amount_condition) + .where(si.currency == currency) + ) - FROM - `tabSales Invoice Payment` as sip - JOIN - `tabSales Invoice` as si - ON - sip.parent = si.name - WHERE - si.docstatus = 1 - AND (sip.clearance_date is null or sip.clearance_date='0000-00-00') - AND sip.account = %(bank_account)s - AND sip.amount {'= %(amount)s' if exact_match else '> 0.0'} - """ + return str(query) -def get_pi_matching_query(exact_match): +def get_pi_matching_query(exact_match, currency): # get matching purchase invoice query when they are also used as payment entries (is_paid) - return f""" - SELECT - ( CASE WHEN supplier = %(party)s THEN 1 ELSE 0 END - + CASE WHEN paid_amount = %(amount)s THEN 1 ELSE 0 END - + 1 ) AS rank, - 'Purchase Invoice' as doctype, - name, - paid_amount, - '' as reference_no, - '' as reference_date, - supplier as party, - 'Supplier' as party_type, - posting_date, - currency - FROM - `tabPurchase Invoice` - WHERE - docstatus = 1 - AND is_paid = 1 - AND ifnull(clearance_date, '') = "" - AND cash_bank_account = %(bank_account)s - AND paid_amount {'= %(amount)s' if exact_match else '> 0.0'} - """ + purchase_invoice = frappe.qb.DocType("Purchase Invoice") + + amount_equality = purchase_invoice.paid_amount == Parameter("%(amount)s") + amount_rank = frappe.qb.terms.Case().when(amount_equality, 1).else_(0) + amount_condition = amount_equality if exact_match else purchase_invoice.paid_amount > 0.0 + + party_condition = purchase_invoice.supplier == Parameter("%(party)s") + party_rank = frappe.qb.terms.Case().when(party_condition, 1).else_(0) + + query = ( + frappe.qb.from_(purchase_invoice) + .select( + (party_rank + amount_rank + 1).as_("rank"), + ConstantColumn("Purchase Invoice").as_("doctype"), + purchase_invoice.name, + purchase_invoice.paid_amount, + ConstantColumn("").as_("reference_no"), + ConstantColumn("").as_("reference_date"), + purchase_invoice.supplier.as_("party"), + ConstantColumn("Supplier").as_("party_type"), + purchase_invoice.posting_date, + purchase_invoice.currency, + ) + .where(purchase_invoice.docstatus == 1) + .where(purchase_invoice.is_paid == 1) + .where(purchase_invoice.clearance_date.isnull()) + .where(purchase_invoice.cash_bank_account == Parameter("%(bank_account)s")) + .where(amount_condition) + .where(purchase_invoice.currency == currency) + ) + + return str(query) 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 599ced59192..5a6bb6976f9 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 @@ -1,9 +1,100 @@ # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -# import frappe import unittest +import frappe +from frappe import qb +from frappe.tests.utils import FrappeTestCase, change_settings +from frappe.utils import add_days, flt, getdate, today -class TestBankReconciliationTool(unittest.TestCase): - pass +from erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool import ( + auto_reconcile_vouchers, + get_bank_transactions, +) +from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry +from erpnext.accounts.test.accounts_mixin import AccountsTestMixin + + +class TestBankReconciliationTool(AccountsTestMixin, FrappeTestCase): + def setUp(self): + self.create_company() + self.create_customer() + self.clear_old_entries() + bank_dt = qb.DocType("Bank") + q = qb.from_(bank_dt).delete().where(bank_dt.name == "HDFC").run() + self.create_bank_account() + + def tearDown(self): + frappe.db.rollback() + + def create_bank_account(self): + bank = frappe.get_doc( + { + "doctype": "Bank", + "bank_name": "HDFC", + } + ).save() + + self.bank_account = ( + frappe.get_doc( + { + "doctype": "Bank Account", + "account_name": "HDFC _current_", + "bank": bank, + "is_company_account": True, + "account": self.bank, # account from Chart of Accounts + } + ) + .insert() + .name + ) + + def test_auto_reconcile(self): + # make payment + from_date = add_days(today(), -1) + to_date = today() + payment = create_payment_entry( + company=self.company, + posting_date=from_date, + payment_type="Receive", + party_type="Customer", + party=self.customer, + paid_from=self.debit_to, + paid_to=self.bank, + paid_amount=100, + ).save() + payment.reference_no = "123" + payment = payment.save().submit() + + # make bank transaction + bank_transaction = ( + frappe.get_doc( + { + "doctype": "Bank Transaction", + "date": to_date, + "deposit": 100, + "bank_account": self.bank_account, + "reference_number": "123", + } + ) + .save() + .submit() + ) + + # assert API output pre reconciliation + transactions = get_bank_transactions(self.bank_account, from_date, to_date) + self.assertEqual(len(transactions), 1) + self.assertEqual(transactions[0].name, bank_transaction.name) + + # auto reconcile + auto_reconcile_vouchers( + bank_account=self.bank_account, + from_date=from_date, + to_date=to_date, + filter_by_reference_date=False, + ) + + # assert API output post reconciliation + transactions = get_bank_transactions(self.bank_account, from_date, to_date) + self.assertEqual(len(transactions), 0) diff --git a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py index 59905dad5d8..0c328ff46cd 100644 --- a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py +++ b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py @@ -47,7 +47,7 @@ class TestBankTransaction(FrappeTestCase): from_date=bank_transaction.date, to_date=utils.today(), ) - self.assertTrue(linked_payments[0][6] == "Conrad Electronic") + self.assertTrue(linked_payments[0]["party"] == "Conrad Electronic") # This test validates a simple reconciliation leading to the clearance of the bank transaction and the payment def test_reconcile(self): @@ -93,7 +93,7 @@ class TestBankTransaction(FrappeTestCase): from_date=bank_transaction.date, to_date=utils.today(), ) - self.assertTrue(linked_payments[0][3]) + self.assertTrue(linked_payments[0]["paid_amount"]) # Check error if already reconciled def test_already_reconciled(self): @@ -188,7 +188,7 @@ class TestBankTransaction(FrappeTestCase): repayment_entry = create_loan_and_repayment() linked_payments = get_linked_payments(bank_transaction.name, ["loan_repayment", "exact_match"]) - self.assertEqual(linked_payments[0][2], repayment_entry.name) + self.assertEqual(linked_payments[0]["name"], repayment_entry.name) @if_lending_app_installed diff --git a/erpnext/accounts/test/accounts_mixin.py b/erpnext/accounts/test/accounts_mixin.py index 08688608f4b..d503f7bc4af 100644 --- a/erpnext/accounts/test/accounts_mixin.py +++ b/erpnext/accounts/test/accounts_mixin.py @@ -158,6 +158,8 @@ class AccountsTestMixin: "Journal Entry", "Sales Order", "Exchange Rate Revaluation", + "Bank Account", + "Bank Transaction", ] for doctype in doctype_list: qb.from_(qb.DocType(doctype)).delete().where(qb.DocType(doctype).company == self.company).run() diff --git a/erpnext/hooks.py b/erpnext/hooks.py index c7398cce999..7bf8fb44927 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -555,8 +555,6 @@ get_matching_queries = ( "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_matching_queries" ) -get_matching_vouchers_for_bank_reconciliation = "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_matching_vouchers_for_bank_reconciliation" - get_amounts_not_reflected_in_system_for_bank_reconciliation_statement = "erpnext.accounts.report.bank_reconciliation_statement.bank_reconciliation_statement.get_amounts_not_reflected_in_system_for_bank_reconciliation_statement" get_payment_entries_for_bank_clearance = ( diff --git a/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js b/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js index 52fa8ab0f3c..1f47347d46e 100644 --- a/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js +++ b/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js @@ -134,12 +134,12 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager { format_row(row) { return [ - row[1], // Document Type - row[2], // Document Name - row[5] || row[8], // Reference Date - format_currency(row[3], row[9]), // Remaining - row[4], // Reference Number - row[6], // Party + row["doctype"], + row["name"], + row["reference_date"] || row["posting_date"], + format_currency(row["paid_amount"], row["currency"]), + row["reference_no"], + row["party"], ]; }