feat:filters on bank reconciliation

Added date filters on bank transactions, payment entries and journal entries and sorted list as per date in ascending order.
This commit is contained in:
sonali 2022-12-17 17:15:28 +05:30
parent 408c89df03
commit 05b6fce03d
5 changed files with 179 additions and 126 deletions

View File

@ -160,6 +160,9 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
).$wrapper, ).$wrapper,
bank_statement_from_date: frm.doc.bank_statement_from_date, bank_statement_from_date: frm.doc.bank_statement_from_date,
bank_statement_to_date: frm.doc.bank_statement_to_date, bank_statement_to_date: frm.doc.bank_statement_to_date,
filtered_by_reference_date:frm.doc.filtered_by_reference_date,
from_reference_date:frm.doc.from_reference_date,
to_reference_date:frm.doc.to_reference_date,
bank_statement_closing_balance: bank_statement_closing_balance:
frm.doc.bank_statement_closing_balance, frm.doc.bank_statement_closing_balance,
cards_manager: frm.cards_manager, cards_manager: frm.cards_manager,

View File

@ -25,8 +25,6 @@ class BankReconciliationTool(Document):
@frappe.whitelist() @frappe.whitelist()
def get_bank_transactions(bank_account, from_date=None, to_date=None): def get_bank_transactions(bank_account, from_date=None, to_date=None):
# returns bank transactions for a bank account # returns bank transactions for a bank account
from_date = frappe.db.get_single_value("Bank Reconciliation Tool", "bank_statement_from_date")
to_date = frappe.db.get_single_value("Bank Reconciliation Tool", "bank_statement_to_date")
filters = [] filters = []
filters.append(["bank_account", "=", bank_account]) filters.append(["bank_account", "=", bank_account])
filters.append(["docstatus", "=", 1]) filters.append(["docstatus", "=", 1])
@ -52,8 +50,8 @@ def get_bank_transactions(bank_account, from_date=None, to_date=None):
"party", "party",
], ],
filters=filters, filters=filters,
order_by="date",
) )
transactions = sorted(transactions, key=lambda x: x["date"]) if transactions else []
return transactions return transactions
@ -330,23 +328,58 @@ def reconcile_vouchers(bank_transaction_name, vouchers):
@frappe.whitelist() @frappe.whitelist()
def get_linked_payments(bank_transaction_name, document_types=None): def get_linked_payments(
bank_transaction_name,
document_types=None,
from_date=None,
to_date=None,
filtered_by_reference_date=None,
from_reference_date=None,
to_reference_date=None,
):
# get all matching payments for a bank transaction # get all matching payments for a bank transaction
transaction = frappe.get_doc("Bank Transaction", bank_transaction_name) transaction = frappe.get_doc("Bank Transaction", bank_transaction_name)
bank_account = frappe.db.get_values( bank_account = frappe.db.get_values(
"Bank Account", transaction.bank_account, ["account", "company"], as_dict=True "Bank Account", transaction.bank_account, ["account", "company"], as_dict=True
)[0] )[0]
(account, company) = (bank_account.account, bank_account.company) (account, company) = (bank_account.account, bank_account.company)
matching = check_matching(account, company, transaction, document_types) matching = check_matching(
account,
company,
transaction,
document_types,
from_date,
to_date,
filtered_by_reference_date,
from_reference_date,
to_reference_date,
)
return matching return matching
def check_matching(bank_account, company, transaction, document_types): def check_matching(
bank_account,
company,
transaction,
document_types,
from_date,
to_date,
filtered_by_reference_date,
from_reference_date,
to_reference_date,
):
# combine all types of vouchers # combine all types of vouchers
filtered_by_reference_date = frappe.db.get_single_value( subquery = get_queries(
"Bank Reconciliation Tool", "filtered_by_reference_date" bank_account,
company,
transaction,
document_types,
from_date,
to_date,
filtered_by_reference_date,
from_reference_date,
to_reference_date,
) )
subquery = get_queries(bank_account, company, transaction, document_types)
filters = { filters = {
"amount": transaction.unallocated_amount, "amount": transaction.unallocated_amount,
"payment_type": "Receive" if transaction.deposit > 0 else "Pay", "payment_type": "Receive" if transaction.deposit > 0 else "Pay",
@ -367,19 +400,20 @@ def check_matching(bank_account, company, transaction, document_types):
filters, filters,
) )
) )
matching_vouchers_with_ref_no = tuple( return sorted(matching_vouchers, key=lambda x: x[0], reverse=True) if matching_vouchers else []
ele for ele in matching_vouchers if frappe.as_json(ele[5]) != "null"
)
if filtered_by_reference_date:
matching_vouchers = (
sorted(matching_vouchers_with_ref_no, key=lambda x: x[5]) if matching_vouchers else []
)
else:
matching_vouchers = sorted(matching_vouchers, key=lambda x: x[8]) if matching_vouchers else []
return matching_vouchers
def get_queries(bank_account, company, transaction, document_types): def get_queries(
bank_account,
company,
transaction,
document_types,
from_date,
to_date,
filtered_by_reference_date,
from_reference_date,
to_reference_date,
):
# get queries to get matching vouchers # get queries to get matching vouchers
amount_condition = "=" if "exact_match" in document_types else "<=" amount_condition = "=" if "exact_match" in document_types else "<="
account_from_to = "paid_to" if transaction.deposit > 0 else "paid_from" account_from_to = "paid_to" if transaction.deposit > 0 else "paid_from"
@ -395,6 +429,11 @@ def get_queries(bank_account, company, transaction, document_types):
document_types, document_types,
amount_condition, amount_condition,
account_from_to, account_from_to,
from_date,
to_date,
filtered_by_reference_date,
from_reference_date,
to_reference_date,
) )
or [] or []
) )
@ -403,15 +442,42 @@ def get_queries(bank_account, company, transaction, document_types):
def get_matching_queries( def get_matching_queries(
bank_account, company, transaction, document_types, amount_condition, account_from_to bank_account,
company,
transaction,
document_types,
amount_condition,
account_from_to,
from_date,
to_date,
filtered_by_reference_date,
from_reference_date,
to_reference_date,
): ):
queries = [] queries = []
if "payment_entry" in document_types: if "payment_entry" in document_types:
pe_amount_matching = get_pe_matching_query(amount_condition, account_from_to, transaction) pe_amount_matching = get_pe_matching_query(
amount_condition,
account_from_to,
transaction,
from_date,
to_date,
filtered_by_reference_date,
from_reference_date,
to_reference_date,
)
queries.extend([pe_amount_matching]) queries.extend([pe_amount_matching])
if "journal_entry" in document_types: if "journal_entry" in document_types:
je_amount_matching = get_je_matching_query(amount_condition, transaction) je_amount_matching = get_je_matching_query(
amount_condition,
transaction,
from_date,
to_date,
filtered_by_reference_date,
from_reference_date,
to_reference_date,
)
queries.extend([je_amount_matching]) queries.extend([je_amount_matching])
if transaction.deposit > 0 and "sales_invoice" in document_types: if transaction.deposit > 0 and "sales_invoice" in document_types:
@ -518,41 +584,27 @@ def get_lr_matching_query(bank_account, amount_condition, filters):
return vouchers return vouchers
def get_pe_matching_query(amount_condition, account_from_to, transaction): def get_pe_matching_query(
amount_condition,
account_from_to,
transaction,
from_date,
to_date,
filtered_by_reference_date,
from_reference_date,
to_reference_date,
):
# get matching payment entries query # get matching payment entries query
from_date = frappe.db.get_single_value("Bank Reconciliation Tool", "bank_statement_from_date")
to_date = frappe.db.get_single_value("Bank Reconciliation Tool", "bank_statement_to_date")
from_reference_date = frappe.db.get_single_value(
"Bank Reconciliation Tool", "from_reference_date"
)
to_reference_date = frappe.db.get_single_value("Bank Reconciliation Tool", "to_reference_date")
filtered_by_reference_date = frappe.db.get_single_value(
"Bank Reconciliation Tool", "filtered_by_reference_date"
)
if transaction.deposit > 0: if transaction.deposit > 0:
currency_field = "paid_to_account_currency as currency" currency_field = "paid_to_account_currency as currency"
else: else:
currency_field = "paid_from_account_currency as currency" currency_field = "paid_from_account_currency as currency"
cond_filtered_from_ref_date = "" filter_by_date = f"AND posting_date between '{from_date}' and '{to_date}'"
cond_filtered_to_ref_date = "" order_by = " posting_date"
cond_filtered_from_posting_date = "" if filtered_by_reference_date == "1":
cond_filtered_to_posting_date = "" filter_by_date = f"AND reference_date between '{from_reference_date}' and '{to_reference_date}'"
from_ref_date ="" order_by = " reference_date"
to_ref_date ="" return f"""
from_post_date = ""
to_post_date = ""
if(filtered_by_reference_date):
cond_filtered_from_ref_date = " AND reference_date >="
cond_filtered_to_ref_date = " AND reference_date <="
from_ref_date = from_reference_date
to_ref_date = to_reference_date
elif(not filtered_by_reference_date):
cond_filtered_from_posting_date = " AND posting_date >="
cond_filtered_to_posting_date = " AND posting_date <="
from_post_date = from_date
to_post_date = to_date
pe_data= f"""
SELECT SELECT
(CASE WHEN reference_no=%(reference_no)s THEN 1 ELSE 0 END (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 (party_type = %(party_type)s AND party = %(party)s ) THEN 1 ELSE 0 END
@ -574,49 +626,33 @@ def get_pe_matching_query(amount_condition, account_from_to, transaction):
AND payment_type IN (%(payment_type)s, 'Internal Transfer') AND payment_type IN (%(payment_type)s, 'Internal Transfer')
AND ifnull(clearance_date, '') = "" AND ifnull(clearance_date, '') = ""
AND {account_from_to} = %(bank_account)s AND {account_from_to} = %(bank_account)s
AND reference_no = '{transaction.reference_number}' {filter_by_date}
{cond_filtered_from_ref_date} "{from_ref_date}" order by{order_by}
{cond_filtered_to_ref_date} "{to_ref_date}"
{cond_filtered_from_posting_date} "{from_post_date}" """
{cond_filtered_to_posting_date} "{to_post_date}"
"""
return pe_data
def get_je_matching_query(amount_condition, transaction): def get_je_matching_query(
amount_condition,
transaction,
from_date,
to_date,
filtered_by_reference_date,
from_reference_date,
to_reference_date,
):
# get matching journal entry query # get matching journal entry query
# We have mapping at the bank level # We have mapping at the bank level
# So one bank could have both types of bank accounts like asset and liability # 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 # So cr_or_dr should be judged only on basis of withdrawal and deposit and not account type
from_date = frappe.db.get_single_value("Bank Reconciliation Tool", "bank_statement_from_date")
to_date = frappe.db.get_single_value("Bank Reconciliation Tool", "bank_statement_to_date")
from_reference_date = frappe.db.get_single_value(
"Bank Reconciliation Tool", "from_reference_date"
)
to_reference_date = frappe.db.get_single_value("Bank Reconciliation Tool", "to_reference_date")
filtered_by_reference_date = frappe.db.get_single_value(
"Bank Reconciliation Tool", "filtered_by_reference_date"
)
cr_or_dr = "credit" if transaction.withdrawal > 0 else "debit" cr_or_dr = "credit" if transaction.withdrawal > 0 else "debit"
cond_filtered_from_ref_date = "" # filter_by_date = f"AND je.posting_date between '{from_date}' and '{to_date}'"
cond_filtered_to_ref_date = "" order_by = " je.posting_date"
cond_filtered_from_posting_date = "" if filtered_by_reference_date == "1":
cond_filtered_to_posting_date = "" filter_by_date = f"AND je.cheque_date between '{from_reference_date}' and '{to_reference_date}'"
from_ref_date ="" order_by = " je.cheque_date"
to_ref_date =""
from_post_date = "" return f"""
to_post_date = ""
if(filtered_by_reference_date):
cond_filtered_from_ref_date = " AND je.cheque_date >="
cond_filtered_to_ref_date = " AND je.cheque_date <="
from_ref_date = from_reference_date
to_ref_date = to_reference_date
elif(not filtered_by_reference_date):
cond_filtered_from_posting_date = " AND je.posting_date>="
cond_filtered_to_posting_date = " AND je.posting_date <="
from_post_date = from_date
to_post_date = to_date
je_data = f"""
SELECT SELECT
(CASE WHEN je.cheque_no=%(reference_no)s THEN 1 ELSE 0 END (CASE WHEN je.cheque_no=%(reference_no)s THEN 1 ELSE 0 END
+ 1) AS rank , + 1) AS rank ,
@ -640,13 +676,10 @@ def get_je_matching_query(amount_condition, transaction):
AND jea.account = %(bank_account)s AND jea.account = %(bank_account)s
AND jea.{cr_or_dr}_in_account_currency {amount_condition} %(amount)s AND jea.{cr_or_dr}_in_account_currency {amount_condition} %(amount)s
AND je.docstatus = 1 AND je.docstatus = 1
AND je.cheque_no = '{transaction.reference_number}' {filter_by_date}
{cond_filtered_from_ref_date} "{from_ref_date}" order by {order_by}
{cond_filtered_to_ref_date} "{to_ref_date}" """
{cond_filtered_from_posting_date} "{from_post_date}"
{cond_filtered_to_posting_date} "{to_post_date}"
"""
return je_data
def get_si_matching_query(amount_condition): def get_si_matching_query(amount_condition):
# get matchin sales invoice query # get matchin sales invoice query

View File

@ -469,8 +469,8 @@ period_closing_doctypes = [
bank_reconciliation_doctypes = [ bank_reconciliation_doctypes = [
"Payment Entry", "Payment Entry",
"Journal Entry", "Journal Entry",
# "Purchase Invoice", "Purchase Invoice",
# "Sales Invoice", "Sales Invoice",
"Loan Repayment", "Loan Repayment",
"Loan Disbursement", "Loan Disbursement",
] ]

View File

@ -5,7 +5,12 @@ erpnext.accounts.bank_reconciliation.DataTableManager = class DataTableManager {
Object.assign(this, opts); Object.assign(this, opts);
this.dialog_manager = new erpnext.accounts.bank_reconciliation.DialogManager( this.dialog_manager = new erpnext.accounts.bank_reconciliation.DialogManager(
this.company, this.company,
this.bank_account this.bank_account,
this.bank_statement_from_date,
this.bank_statement_to_date,
this.filtered_by_reference_date,
this.from_reference_date,
this.to_reference_date
); );
this.make_dt(); this.make_dt();
} }
@ -17,6 +22,8 @@ erpnext.accounts.bank_reconciliation.DataTableManager = class DataTableManager {
"erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_bank_transactions", "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_bank_transactions",
args: { args: {
bank_account: this.bank_account, bank_account: this.bank_account,
from_date:this.bank_statement_from_date,
to_date:this.bank_statement_to_date
}, },
callback: function (response) { callback: function (response) {
me.format_data(response.message); me.format_data(response.message);

View File

@ -5,6 +5,11 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager {
this.bank_account = bank_account; this.bank_account = bank_account;
this.company = company; this.company = company;
this.make_dialog(); this.make_dialog();
this.bank_statement_from_date = bank_statement_from_date;
this.bank_statement_to_date = bank_statement_to_date;
this.filtered_by_reference_date = filtered_by_reference_date;
this.from_reference_date = from_reference_date;
this.to_reference_date = to_reference_date;
} }
show_dialog(bank_transaction_name, update_dt_cards) { show_dialog(bank_transaction_name, update_dt_cards) {
@ -50,7 +55,7 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager {
this.company = company; this.company = company;
this.make_dialog(); this.make_dialog();
} }
show_dialog(bank_transaction_name, update_dt_cards) { show_dialog(bank_transaction_name, update_dt_cards) {
this.bank_transaction_name = bank_transaction_name; this.bank_transaction_name = bank_transaction_name;
this.update_dt_cards = update_dt_cards; this.update_dt_cards = update_dt_cards;
@ -86,7 +91,7 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager {
}, },
}); });
} }
get_linked_vouchers(document_types) { get_linked_vouchers(document_types) {
frappe.call({ frappe.call({
method: method:
@ -94,12 +99,17 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager {
args: { args: {
bank_transaction_name: this.bank_transaction_name, bank_transaction_name: this.bank_transaction_name,
document_types: document_types, document_types: document_types,
from_date: this.bank_statement_from_date,
to_date: this.bank_statement_to_date,
filtered_by_reference_date: this.filtered_by_reference_date,
from_reference_date:this.from_reference_date,
to_reference_date:this.to_reference_date
}, },
callback: (result) => { callback: (result) => {
const data = result.message; const data = result.message;
if (data && data.length > 0) { if (data && data.length > 0) {
const proposals_wrapper = this.dialog.fields_dict.payment_proposals.$wrapper; const proposals_wrapper = this.dialog.fields_dict.payment_proposals.$wrapper;
proposals_wrapper.show(); proposals_wrapper.show();
@ -123,13 +133,13 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager {
const proposals_wrapper = this.dialog.fields_dict.payment_proposals.$wrapper; const proposals_wrapper = this.dialog.fields_dict.payment_proposals.$wrapper;
proposals_wrapper.hide(); proposals_wrapper.hide();
this.dialog.fields_dict.no_matching_vouchers.$wrapper.show(); this.dialog.fields_dict.no_matching_vouchers.$wrapper.show();
} }
this.dialog.show(); this.dialog.show();
}, },
}); });
} }
get_dt_columns() { get_dt_columns() {
this.columns = [ this.columns = [
{ {
@ -162,7 +172,7 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager {
editable: false, editable: false,
width: 120, width: 120,
}, },
{ {
name: __("Reference Number"), name: __("Reference Number"),
editable: false, editable: false,
@ -170,7 +180,7 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager {
}, },
]; ];
} }
get_datatable(proposals_wrapper) { get_datatable(proposals_wrapper) {
if (!this.datatable) { if (!this.datatable) {
const datatable_options = { const datatable_options = {
@ -189,11 +199,11 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager {
this.datatable.rowmanager.checkMap = []; this.datatable.rowmanager.checkMap = [];
} }
} }
make_dialog() { make_dialog() {
const me = this; const me = this;
me.selected_payment = null; me.selected_payment = null;
const fields = [ const fields = [
{ {
label: __("Action"), label: __("Action"),
@ -221,7 +231,7 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager {
depends_on: "eval:doc.action=='Match Against Voucher'", depends_on: "eval:doc.action=='Match Against Voucher'",
}, },
]; ];
frappe.call({ frappe.call({
method: "erpnext.accounts.doctype.bank_transaction.bank_transaction.get_doctypes_for_bank_reconciliation", method: "erpnext.accounts.doctype.bank_transaction.bank_transaction.get_doctypes_for_bank_reconciliation",
callback: (r) => { callback: (r) => {
@ -238,9 +248,9 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager {
onchange: () => this.update_options(), onchange: () => this.update_options(),
}); });
}); });
fields.push(...this.get_voucher_fields()); fields.push(...this.get_voucher_fields());
me.dialog = new frappe.ui.Dialog({ me.dialog = new frappe.ui.Dialog({
title: __("Reconcile the Bank Transaction"), title: __("Reconcile the Bank Transaction"),
fields: fields, fields: fields,
@ -251,7 +261,7 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager {
} }
}); });
} }
get_voucher_fields() { get_voucher_fields() {
return [ return [
{ {
@ -431,7 +441,7 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager {
label: "Allocated Amount", label: "Allocated Amount",
read_only: 1, read_only: 1,
}, },
{ {
fieldname: "unallocated_amount", fieldname: "unallocated_amount",
fieldtype: "Currency", fieldtype: "Currency",
@ -440,7 +450,7 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager {
}, },
]; ];
} }
get_selected_attributes() { get_selected_attributes() {
let selected_attributes = []; let selected_attributes = [];
this.dialog.$wrapper.find(".checkbox input").each((i, col) => { this.dialog.$wrapper.find(".checkbox input").each((i, col) => {
@ -448,15 +458,15 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager {
selected_attributes.push($(col).attr("data-fieldname")); selected_attributes.push($(col).attr("data-fieldname"));
} }
}); });
return selected_attributes; return selected_attributes;
} }
update_options() { update_options() {
let selected_attributes = this.get_selected_attributes(); let selected_attributes = this.get_selected_attributes();
this.get_linked_vouchers(selected_attributes); this.get_linked_vouchers(selected_attributes);
} }
reconciliation_dialog_primary_action(values) { reconciliation_dialog_primary_action(values) {
if (values.action == "Match Against Voucher") this.match(values); if (values.action == "Match Against Voucher") this.match(values);
if ( if (
@ -472,7 +482,7 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager {
else if (values.action == "Update Bank Transaction") else if (values.action == "Update Bank Transaction")
this.update_transaction(values); this.update_transaction(values);
} }
match() { match() {
var selected_map = this.datatable.rowmanager.checkMap; var selected_map = this.datatable.rowmanager.checkMap;
let rows = []; let rows = [];
@ -502,7 +512,7 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager {
}, },
}); });
} }
add_payment_entry(values) { add_payment_entry(values) {
frappe.call({ frappe.call({
method: method:
@ -526,7 +536,7 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager {
}, },
}); });
} }
add_journal_entry(values) { add_journal_entry(values) {
frappe.call({ frappe.call({
method: method:
@ -550,7 +560,7 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager {
}, },
}); });
} }
update_transaction(values) { update_transaction(values) {
frappe.call({ frappe.call({
method: method:
@ -569,7 +579,7 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager {
}, },
}); });
} }
edit_in_full_page() { edit_in_full_page() {
const values = this.dialog.get_values(true); const values = this.dialog.get_values(true);
if (values.document_type == "Payment Entry") { if (values.document_type == "Payment Entry") {
@ -616,9 +626,9 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager {
}); });
} }
} }
}; };
get_linked_vouchers(document_types) { get_linked_vouchers(document_types) {
frappe.call({ frappe.call({
method: method: