Merge branch 'develop' into fixed_asset_report_asset_value
This commit is contained in:
commit
29088e1777
@ -21,13 +21,22 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
|
|||||||
frm.trigger('bank_account');
|
frm.trigger('bank_account');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
filter_by_reference_date: function (frm) {
|
||||||
|
if (frm.doc.filter_by_reference_date) {
|
||||||
|
frm.set_value("bank_statement_from_date", "");
|
||||||
|
frm.set_value("bank_statement_to_date", "");
|
||||||
|
} else {
|
||||||
|
frm.set_value("from_reference_date", "");
|
||||||
|
frm.set_value("to_reference_date", "");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
refresh: function (frm) {
|
refresh: function (frm) {
|
||||||
frappe.require("bank-reconciliation-tool.bundle.js", () =>
|
frappe.require("bank-reconciliation-tool.bundle.js", () =>
|
||||||
frm.trigger("make_reconciliation_tool")
|
frm.trigger("make_reconciliation_tool")
|
||||||
);
|
);
|
||||||
frm.upload_statement_button = frm.page.set_secondary_action(
|
|
||||||
__("Upload Bank Statement"),
|
frm.add_custom_button(__("Upload Bank Statement"), () =>
|
||||||
() =>
|
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method:
|
method:
|
||||||
"erpnext.accounts.doctype.bank_statement_import.bank_statement_import.upload_bank_statement",
|
"erpnext.accounts.doctype.bank_statement_import.bank_statement_import.upload_bank_statement",
|
||||||
@ -49,6 +58,20 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
frm.add_custom_button(__('Auto Reconcile'), function() {
|
||||||
|
frappe.call({
|
||||||
|
method: "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.auto_reconcile_vouchers",
|
||||||
|
args: {
|
||||||
|
bank_account: frm.doc.bank_account,
|
||||||
|
from_date: frm.doc.bank_statement_from_date,
|
||||||
|
to_date: frm.doc.bank_statement_to_date,
|
||||||
|
filter_by_reference_date: frm.doc.filter_by_reference_date,
|
||||||
|
from_reference_date: frm.doc.from_reference_date,
|
||||||
|
to_reference_date: frm.doc.to_reference_date,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
after_save: function (frm) {
|
after_save: function (frm) {
|
||||||
@ -160,6 +183,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,
|
||||||
|
filter_by_reference_date: frm.doc.filter_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,
|
||||||
|
@ -10,6 +10,9 @@
|
|||||||
"column_break_1",
|
"column_break_1",
|
||||||
"bank_statement_from_date",
|
"bank_statement_from_date",
|
||||||
"bank_statement_to_date",
|
"bank_statement_to_date",
|
||||||
|
"from_reference_date",
|
||||||
|
"to_reference_date",
|
||||||
|
"filter_by_reference_date",
|
||||||
"column_break_2",
|
"column_break_2",
|
||||||
"account_opening_balance",
|
"account_opening_balance",
|
||||||
"bank_statement_closing_balance",
|
"bank_statement_closing_balance",
|
||||||
@ -36,13 +39,13 @@
|
|||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval: doc.bank_account",
|
"depends_on": "eval: doc.bank_account && !doc.filter_by_reference_date",
|
||||||
"fieldname": "bank_statement_from_date",
|
"fieldname": "bank_statement_from_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"label": "From Date"
|
"label": "From Date"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval: doc.bank_statement_from_date",
|
"depends_on": "eval: doc.bank_account && !doc.filter_by_reference_date",
|
||||||
"fieldname": "bank_statement_to_date",
|
"fieldname": "bank_statement_to_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"label": "To Date"
|
"label": "To Date"
|
||||||
@ -81,14 +84,33 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "no_bank_transactions",
|
"fieldname": "no_bank_transactions",
|
||||||
"fieldtype": "HTML"
|
"fieldtype": "HTML",
|
||||||
|
"options": "<div class=\"text-muted text-center\">No Matching Bank Transactions Found</div>"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:doc.filter_by_reference_date",
|
||||||
|
"fieldname": "from_reference_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"label": "From Reference Date"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:doc.filter_by_reference_date",
|
||||||
|
"fieldname": "to_reference_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"label": "To Reference Date"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "filter_by_reference_date",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Filter by Reference Date"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"hide_toolbar": 1,
|
"hide_toolbar": 1,
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-04-21 11:13:49.831769",
|
"modified": "2023-01-13 13:00:02.022919",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Bank Reconciliation Tool",
|
"name": "Bank Reconciliation Tool",
|
||||||
@ -107,5 +129,6 @@
|
|||||||
],
|
],
|
||||||
"quick_entry": 1,
|
"quick_entry": 1,
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC"
|
"sort_order": "DESC",
|
||||||
}
|
"states": []
|
||||||
|
}
|
@ -8,7 +8,7 @@ import frappe
|
|||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.query_builder.custom import ConstantColumn
|
from frappe.query_builder.custom import ConstantColumn
|
||||||
from frappe.utils import flt
|
from frappe.utils import cint, flt
|
||||||
|
|
||||||
from erpnext.accounts.doctype.bank_transaction.bank_transaction import get_paid_amount
|
from erpnext.accounts.doctype.bank_transaction.bank_transaction import get_paid_amount
|
||||||
from erpnext.accounts.report.bank_reconciliation_statement.bank_reconciliation_statement import (
|
from erpnext.accounts.report.bank_reconciliation_statement.bank_reconciliation_statement import (
|
||||||
@ -50,6 +50,7 @@ def get_bank_transactions(bank_account, from_date=None, to_date=None):
|
|||||||
"party",
|
"party",
|
||||||
],
|
],
|
||||||
filters=filters,
|
filters=filters,
|
||||||
|
order_by="date",
|
||||||
)
|
)
|
||||||
return transactions
|
return transactions
|
||||||
|
|
||||||
@ -265,6 +266,80 @@ def create_payment_entry_bts(
|
|||||||
return reconcile_vouchers(bank_transaction.name, vouchers)
|
return reconcile_vouchers(bank_transaction.name, vouchers)
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def auto_reconcile_vouchers(
|
||||||
|
bank_account,
|
||||||
|
from_date=None,
|
||||||
|
to_date=None,
|
||||||
|
filter_by_reference_date=None,
|
||||||
|
from_reference_date=None,
|
||||||
|
to_reference_date=None,
|
||||||
|
):
|
||||||
|
frappe.flags.auto_reconcile_vouchers = True
|
||||||
|
document_types = ["payment_entry", "journal_entry"]
|
||||||
|
bank_transactions = get_bank_transactions(bank_account)
|
||||||
|
matched_transaction = []
|
||||||
|
for transaction in bank_transactions:
|
||||||
|
linked_payments = get_linked_payments(
|
||||||
|
transaction.name,
|
||||||
|
document_types,
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
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))
|
||||||
|
|
||||||
|
frappe.flags.auto_reconcile_vouchers = False
|
||||||
|
|
||||||
|
return frappe.get_doc("Bank Transaction", transaction.name)
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def reconcile_vouchers(bank_transaction_name, vouchers):
|
def reconcile_vouchers(bank_transaction_name, vouchers):
|
||||||
# updated clear date of all the vouchers based on the bank transaction
|
# updated clear date of all the vouchers based on the bank transaction
|
||||||
@ -327,20 +402,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,
|
||||||
|
filter_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,
|
||||||
|
filter_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,
|
||||||
|
filter_by_reference_date,
|
||||||
|
from_reference_date,
|
||||||
|
to_reference_date,
|
||||||
|
):
|
||||||
# combine all types of vouchers
|
# combine all types of vouchers
|
||||||
subquery = get_queries(bank_account, company, transaction, document_types)
|
subquery = get_queries(
|
||||||
|
bank_account,
|
||||||
|
company,
|
||||||
|
transaction,
|
||||||
|
document_types,
|
||||||
|
from_date,
|
||||||
|
to_date,
|
||||||
|
filter_by_reference_date,
|
||||||
|
from_reference_date,
|
||||||
|
to_reference_date,
|
||||||
|
)
|
||||||
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",
|
||||||
@ -361,11 +474,20 @@ def check_matching(bank_account, company, transaction, document_types):
|
|||||||
filters,
|
filters,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return sorted(matching_vouchers, key=lambda x: x[0], reverse=True) if matching_vouchers else []
|
return sorted(matching_vouchers, key=lambda x: x[0], reverse=True) if matching_vouchers else []
|
||||||
|
|
||||||
|
|
||||||
def get_queries(bank_account, company, transaction, document_types):
|
def get_queries(
|
||||||
|
bank_account,
|
||||||
|
company,
|
||||||
|
transaction,
|
||||||
|
document_types,
|
||||||
|
from_date,
|
||||||
|
to_date,
|
||||||
|
filter_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"
|
||||||
@ -381,6 +503,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,
|
||||||
|
filter_by_reference_date,
|
||||||
|
from_reference_date,
|
||||||
|
to_reference_date,
|
||||||
)
|
)
|
||||||
or []
|
or []
|
||||||
)
|
)
|
||||||
@ -389,15 +516,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,
|
||||||
|
filter_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,
|
||||||
|
filter_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,
|
||||||
|
filter_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:
|
||||||
@ -504,47 +658,81 @@ 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,
|
||||||
|
filter_by_reference_date,
|
||||||
|
from_reference_date,
|
||||||
|
to_reference_date,
|
||||||
|
):
|
||||||
# get matching payment entries query
|
# get matching payment entries query
|
||||||
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"
|
||||||
|
filter_by_date = f"AND posting_date between '{from_date}' and '{to_date}'"
|
||||||
|
order_by = " posting_date"
|
||||||
|
filter_by_reference_no = ""
|
||||||
|
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"
|
||||||
|
if frappe.flags.auto_reconcile_vouchers == True:
|
||||||
|
filter_by_reference_no = f"AND reference_no = '{transaction.reference_number}'"
|
||||||
return f"""
|
return 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
|
||||||
+ 1 ) AS rank,
|
+ 1 ) AS rank,
|
||||||
'Payment Entry' as doctype,
|
'Payment Entry' as doctype,
|
||||||
name,
|
name,
|
||||||
paid_amount,
|
paid_amount,
|
||||||
reference_no,
|
reference_no,
|
||||||
reference_date,
|
reference_date,
|
||||||
party,
|
party,
|
||||||
party_type,
|
party_type,
|
||||||
posting_date,
|
posting_date,
|
||||||
{currency_field}
|
{currency_field}
|
||||||
FROM
|
FROM
|
||||||
`tabPayment Entry`
|
`tabPayment Entry`
|
||||||
WHERE
|
WHERE
|
||||||
paid_amount {amount_condition} %(amount)s
|
paid_amount {amount_condition} %(amount)s
|
||||||
AND docstatus = 1
|
AND docstatus = 1
|
||||||
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
|
||||||
|
{filter_by_date}
|
||||||
|
{filter_by_reference_no}
|
||||||
|
order by{order_by}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def get_je_matching_query(amount_condition, transaction):
|
def get_je_matching_query(
|
||||||
|
amount_condition,
|
||||||
|
transaction,
|
||||||
|
from_date,
|
||||||
|
to_date,
|
||||||
|
filter_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
|
||||||
cr_or_dr = "credit" if transaction.withdrawal > 0 else "debit"
|
cr_or_dr = "credit" if transaction.withdrawal > 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 = ""
|
||||||
|
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"""
|
return 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 ,
|
||||||
@ -568,6 +756,9 @@ 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
|
||||||
|
{filter_by_date}
|
||||||
|
{filter_by_reference_no}
|
||||||
|
order by {order_by}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import json
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
|
from frappe import utils
|
||||||
from frappe.tests.utils import FrappeTestCase
|
from frappe.tests.utils import FrappeTestCase
|
||||||
|
|
||||||
from erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool import (
|
from erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool import (
|
||||||
@ -40,7 +41,12 @@ class TestBankTransaction(FrappeTestCase):
|
|||||||
"Bank Transaction",
|
"Bank Transaction",
|
||||||
dict(description="Re 95282925234 FE/000002917 AT171513000281183046 Conrad Electronic"),
|
dict(description="Re 95282925234 FE/000002917 AT171513000281183046 Conrad Electronic"),
|
||||||
)
|
)
|
||||||
linked_payments = get_linked_payments(bank_transaction.name, ["payment_entry", "exact_match"])
|
linked_payments = get_linked_payments(
|
||||||
|
bank_transaction.name,
|
||||||
|
["payment_entry", "exact_match"],
|
||||||
|
from_date=bank_transaction.date,
|
||||||
|
to_date=utils.today(),
|
||||||
|
)
|
||||||
self.assertTrue(linked_payments[0][6] == "Conrad Electronic")
|
self.assertTrue(linked_payments[0][6] == "Conrad Electronic")
|
||||||
|
|
||||||
# This test validates a simple reconciliation leading to the clearance of the bank transaction and the payment
|
# This test validates a simple reconciliation leading to the clearance of the bank transaction and the payment
|
||||||
@ -81,7 +87,12 @@ class TestBankTransaction(FrappeTestCase):
|
|||||||
"Bank Transaction",
|
"Bank Transaction",
|
||||||
dict(description="Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07"),
|
dict(description="Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07"),
|
||||||
)
|
)
|
||||||
linked_payments = get_linked_payments(bank_transaction.name, ["payment_entry", "exact_match"])
|
linked_payments = get_linked_payments(
|
||||||
|
bank_transaction.name,
|
||||||
|
["payment_entry", "exact_match"],
|
||||||
|
from_date=bank_transaction.date,
|
||||||
|
to_date=utils.today(),
|
||||||
|
)
|
||||||
self.assertTrue(linked_payments[0][3])
|
self.assertTrue(linked_payments[0][3])
|
||||||
|
|
||||||
# Check error if already reconciled
|
# Check error if already reconciled
|
||||||
|
@ -219,20 +219,16 @@ class PurchaseOrder(BuyingController):
|
|||||||
else:
|
else:
|
||||||
if not frappe.get_value("Item", item.fg_item, "is_sub_contracted_item"):
|
if not frappe.get_value("Item", item.fg_item, "is_sub_contracted_item"):
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_(
|
_("Row #{0}: Finished Good Item {1} must be a sub-contracted item").format(
|
||||||
"Row #{0}: Finished Good Item {1} must be a sub-contracted item for service item {2}"
|
item.idx, item.fg_item
|
||||||
).format(item.idx, item.fg_item, item.item_code)
|
)
|
||||||
)
|
)
|
||||||
elif not frappe.get_value("Item", item.fg_item, "default_bom"):
|
elif not frappe.get_value("Item", item.fg_item, "default_bom"):
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_("Row #{0}: Default BOM not found for FG Item {1}").format(item.idx, item.fg_item)
|
_("Row #{0}: Default BOM not found for FG Item {1}").format(item.idx, item.fg_item)
|
||||||
)
|
)
|
||||||
if not item.fg_item_qty:
|
if not item.fg_item_qty:
|
||||||
frappe.throw(
|
frappe.throw(_("Row #{0}: Finished Good Item Qty can not be zero").format(item.idx))
|
||||||
_("Row #{0}: Finished Good Item Qty is not specified for service item {0}").format(
|
|
||||||
item.idx, item.item_code
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
for item in self.items:
|
for item in self.items:
|
||||||
item.set("fg_item", None)
|
item.set("fg_item", None)
|
||||||
|
@ -74,24 +74,25 @@ class SubcontractingController(StockController):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if not is_stock_item:
|
if not is_stock_item:
|
||||||
msg = f"Item {item.item_name} must be a stock item."
|
frappe.throw(_("Row {0}: Item {1} must be a stock item.").format(item.idx, item.item_name))
|
||||||
frappe.throw(_(msg))
|
|
||||||
|
|
||||||
if not is_sub_contracted_item:
|
if not is_sub_contracted_item:
|
||||||
msg = f"Item {item.item_name} must be a subcontracted item."
|
frappe.throw(
|
||||||
frappe.throw(_(msg))
|
_("Row {0}: Item {1} must be a subcontracted item.").format(item.idx, item.item_name)
|
||||||
|
)
|
||||||
|
|
||||||
if item.bom:
|
if item.bom:
|
||||||
bom = frappe.get_doc("BOM", item.bom)
|
bom = frappe.get_doc("BOM", item.bom)
|
||||||
if not bom.is_active:
|
if not bom.is_active:
|
||||||
msg = f"Please select an active BOM for Item {item.item_name}."
|
frappe.throw(
|
||||||
frappe.throw(_(msg))
|
_("Row {0}: Please select an active BOM for Item {1}.").format(item.idx, item.item_name)
|
||||||
|
)
|
||||||
if bom.item != item.item_code:
|
if bom.item != item.item_code:
|
||||||
msg = f"Please select an valid BOM for Item {item.item_name}."
|
frappe.throw(
|
||||||
frappe.throw(_(msg))
|
_("Row {0}: Please select an valid BOM for Item {1}.").format(item.idx, item.item_name)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
msg = f"Please select a BOM for Item {item.item_name}."
|
frappe.throw(_("Row {0}: Please select a BOM for Item {1}.").format(item.idx, item.item_name))
|
||||||
frappe.throw(_(msg))
|
|
||||||
|
|
||||||
def __get_data_before_save(self):
|
def __get_data_before_save(self):
|
||||||
item_dict = {}
|
item_dict = {}
|
||||||
|
@ -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.filter_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);
|
||||||
|
@ -5,8 +5,12 @@ 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.filter_by_reference_date = filter_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) {
|
||||||
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;
|
||||||
@ -35,13 +39,13 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager {
|
|||||||
if (r.message) {
|
if (r.message) {
|
||||||
this.bank_transaction = r.message;
|
this.bank_transaction = r.message;
|
||||||
r.message.payment_entry = 1;
|
r.message.payment_entry = 1;
|
||||||
|
r.message.journal_entry = 1;
|
||||||
this.dialog.set_values(r.message);
|
this.dialog.set_values(r.message);
|
||||||
this.dialog.show();
|
this.dialog.show();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get_linked_vouchers(document_types) {
|
get_linked_vouchers(document_types) {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method:
|
method:
|
||||||
@ -49,6 +53,11 @@ 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,
|
||||||
|
filter_by_reference_date: this.filter_by_reference_date,
|
||||||
|
from_reference_date:this.from_reference_date,
|
||||||
|
to_reference_date:this.to_reference_date
|
||||||
},
|
},
|
||||||
|
|
||||||
callback: (result) => {
|
callback: (result) => {
|
||||||
@ -66,6 +75,7 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager {
|
|||||||
row[1],
|
row[1],
|
||||||
row[2],
|
row[2],
|
||||||
reference_date,
|
reference_date,
|
||||||
|
row[8],
|
||||||
format_currency(row[3], row[9]),
|
format_currency(row[3], row[9]),
|
||||||
row[6],
|
row[6],
|
||||||
row[4],
|
row[4],
|
||||||
@ -101,6 +111,11 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager {
|
|||||||
editable: false,
|
editable: false,
|
||||||
width: 120,
|
width: 120,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Posting Date",
|
||||||
|
editable: false,
|
||||||
|
width: 120,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: __("Amount"),
|
name: __("Amount"),
|
||||||
editable: false,
|
editable: false,
|
||||||
@ -578,4 +593,4 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
@ -638,6 +638,7 @@
|
|||||||
"width": "70px"
|
"width": "70px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 1,
|
||||||
"fieldname": "ordered_qty",
|
"fieldname": "ordered_qty",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"label": "Ordered Qty",
|
"label": "Ordered Qty",
|
||||||
@ -864,7 +865,7 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2022-12-25 02:51:10.247569",
|
"modified": "2023-01-12 13:13:28.691585",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Selling",
|
"module": "Selling",
|
||||||
"name": "Sales Order Item",
|
"name": "Sales Order Item",
|
||||||
|
@ -230,7 +230,8 @@ class PickList(Document):
|
|||||||
frappe.throw(_("Qty of Finished Goods Item should be greater than 0."))
|
frappe.throw(_("Qty of Finished Goods Item should be greater than 0."))
|
||||||
|
|
||||||
def before_print(self, settings=None):
|
def before_print(self, settings=None):
|
||||||
self.group_similar_items()
|
if self.group_same_items:
|
||||||
|
self.group_similar_items()
|
||||||
|
|
||||||
def group_similar_items(self):
|
def group_similar_items(self):
|
||||||
group_item_qty = defaultdict(float)
|
group_item_qty = defaultdict(float)
|
||||||
|
@ -445,6 +445,20 @@ class TestPickList(FrappeTestCase):
|
|||||||
pl.before_print()
|
pl.before_print()
|
||||||
self.assertEqual(len(pl.locations), 4)
|
self.assertEqual(len(pl.locations), 4)
|
||||||
|
|
||||||
|
# grouping should not happen if group_same_items is False
|
||||||
|
pl = frappe.get_doc(
|
||||||
|
doctype="Pick List",
|
||||||
|
group_same_items=False,
|
||||||
|
locations=[
|
||||||
|
_dict(item_code="A", warehouse="X", qty=5, picked_qty=1),
|
||||||
|
_dict(item_code="B", warehouse="Y", qty=4, picked_qty=2),
|
||||||
|
_dict(item_code="A", warehouse="X", qty=3, picked_qty=2),
|
||||||
|
_dict(item_code="B", warehouse="Y", qty=2, picked_qty=2),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
pl.before_print()
|
||||||
|
self.assertEqual(len(pl.locations), 4)
|
||||||
|
|
||||||
# grouping should halve the number of items
|
# grouping should halve the number of items
|
||||||
pl = frappe.get_doc(
|
pl = frappe.get_doc(
|
||||||
doctype="Pick List",
|
doctype="Pick List",
|
||||||
|
@ -41,7 +41,7 @@ def get_data(report_filters):
|
|||||||
key = (d.voucher_type, d.voucher_no)
|
key = (d.voucher_type, d.voucher_no)
|
||||||
gl_data = voucher_wise_gl_data.get(key) or {}
|
gl_data = voucher_wise_gl_data.get(key) or {}
|
||||||
d.account_value = gl_data.get("account_value", 0)
|
d.account_value = gl_data.get("account_value", 0)
|
||||||
d.difference_value = abs(d.stock_value - d.account_value)
|
d.difference_value = d.stock_value - d.account_value
|
||||||
if abs(d.difference_value) > 0.1:
|
if abs(d.difference_value) > 0.1:
|
||||||
data.append(d)
|
data.append(d)
|
||||||
|
|
||||||
|
@ -57,6 +57,7 @@ class SubcontractingReceipt(SubcontractingController):
|
|||||||
|
|
||||||
def before_validate(self):
|
def before_validate(self):
|
||||||
super(SubcontractingReceipt, self).before_validate()
|
super(SubcontractingReceipt, self).before_validate()
|
||||||
|
self.validate_items_qty()
|
||||||
self.set_items_bom()
|
self.set_items_bom()
|
||||||
self.set_items_cost_center()
|
self.set_items_cost_center()
|
||||||
self.set_items_expense_account()
|
self.set_items_expense_account()
|
||||||
@ -157,7 +158,7 @@ class SubcontractingReceipt(SubcontractingController):
|
|||||||
|
|
||||||
total_qty = total_amount = 0
|
total_qty = total_amount = 0
|
||||||
for item in self.items:
|
for item in self.items:
|
||||||
if item.name in rm_supp_cost:
|
if item.qty and item.name in rm_supp_cost:
|
||||||
item.rm_supp_cost = rm_supp_cost[item.name]
|
item.rm_supp_cost = rm_supp_cost[item.name]
|
||||||
item.rm_cost_per_qty = item.rm_supp_cost / item.qty
|
item.rm_cost_per_qty = item.rm_supp_cost / item.qty
|
||||||
rm_supp_cost.pop(item.name)
|
rm_supp_cost.pop(item.name)
|
||||||
@ -194,6 +195,13 @@ class SubcontractingReceipt(SubcontractingController):
|
|||||||
).format(item.idx)
|
).format(item.idx)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def validate_items_qty(self):
|
||||||
|
for item in self.items:
|
||||||
|
if not (item.qty or item.rejected_qty):
|
||||||
|
frappe.throw(
|
||||||
|
_("Row {0}: Accepted Qty and Rejected Qty can't be zero at the same time.").format(item.idx)
|
||||||
|
)
|
||||||
|
|
||||||
def set_items_bom(self):
|
def set_items_bom(self):
|
||||||
if self.is_return:
|
if self.is_return:
|
||||||
for item in self.items:
|
for item in self.items:
|
||||||
|
Loading…
Reference in New Issue
Block a user