Merge branch 'develop' into rounding_issue_fix
diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
index dd26c4c..0ebf0eb 100644
--- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
+++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
@@ -43,11 +43,11 @@
if frappe.flags.in_test:
make_dimension_in_accounting_doctypes(doc=self)
else:
- frappe.enqueue(make_dimension_in_accounting_doctypes, doc=self)
+ frappe.enqueue(make_dimension_in_accounting_doctypes, doc=self, queue='long')
def on_trash(self):
if frappe.flags.in_test:
- delete_accounting_dimension(doc=self)
+ delete_accounting_dimension(doc=self, queue='long')
else:
frappe.enqueue(delete_accounting_dimension, doc=self)
@@ -58,8 +58,13 @@
if not self.fieldname:
self.fieldname = scrub(self.label)
-def make_dimension_in_accounting_doctypes(doc):
- doclist = get_doctypes_with_dimensions()
+ def on_update(self):
+ frappe.flags.accounting_dimensions = None
+
+def make_dimension_in_accounting_doctypes(doc, doclist=None):
+ if not doclist:
+ doclist = get_doctypes_with_dimensions()
+
doc_count = len(get_accounting_dimensions())
count = 0
@@ -79,13 +84,13 @@
"owner": "Administrator"
}
- if doctype == "Budget":
- add_dimension_to_budget_doctype(df, doc)
- else:
- meta = frappe.get_meta(doctype, cached=False)
- fieldnames = [d.fieldname for d in meta.get("fields")]
+ meta = frappe.get_meta(doctype, cached=False)
+ fieldnames = [d.fieldname for d in meta.get("fields")]
- if df['fieldname'] not in fieldnames:
+ if df['fieldname'] not in fieldnames:
+ if doctype == "Budget":
+ add_dimension_to_budget_doctype(df.copy(), doc)
+ else:
create_custom_field(doctype, df)
count += 1
@@ -175,23 +180,17 @@
frappe.clear_cache(doctype=doctype)
def get_doctypes_with_dimensions():
- doclist = ["GL Entry", "Sales Invoice", "POS Invoice", "Purchase Invoice", "Payment Entry", "Asset",
- "Expense Claim", "Expense Claim Detail", "Expense Taxes and Charges", "Stock Entry", "Budget", "Payroll Entry", "Delivery Note",
- "Sales Invoice Item", "POS Invoice Item", "Purchase Invoice Item", "Purchase Order Item", "Journal Entry Account", "Material Request Item", "Delivery Note Item",
- "Purchase Receipt Item", "Stock Entry Detail", "Payment Entry Deduction", "Sales Taxes and Charges", "Purchase Taxes and Charges", "Shipping Rule",
- "Landed Cost Item", "Asset Value Adjustment", "Loyalty Program", "Fee Schedule", "Fee Structure", "Stock Reconciliation",
- "Travel Request", "Fees", "POS Profile", "Opening Invoice Creation Tool", "Opening Invoice Creation Tool Item", "Subscription",
- "Subscription Plan"]
-
- return doclist
+ return frappe.get_hooks("accounting_dimension_doctypes")
def get_accounting_dimensions(as_list=True):
- accounting_dimensions = frappe.get_all("Accounting Dimension", fields=["label", "fieldname", "disabled", "document_type"])
+ if frappe.flags.accounting_dimensions is None:
+ frappe.flags.accounting_dimensions = frappe.get_all("Accounting Dimension",
+ fields=["label", "fieldname", "disabled", "document_type"])
if as_list:
- return [d.fieldname for d in accounting_dimensions]
+ return [d.fieldname for d in frappe.flags.accounting_dimensions]
else:
- return accounting_dimensions
+ return frappe.flags.accounting_dimensions
def get_checks_for_pl_and_bs_accounts():
dimensions = frappe.db.sql("""SELECT p.label, p.disabled, p.fieldname, c.default_dimension, c.company, c.mandatory_for_pl, c.mandatory_for_bs
diff --git a/erpnext/accounts/doctype/bank_account/bank_account.json b/erpnext/accounts/doctype/bank_account/bank_account.json
index b42f1f9..de67ab1 100644
--- a/erpnext/accounts/doctype/bank_account/bank_account.json
+++ b/erpnext/accounts/doctype/bank_account/bank_account.json
@@ -86,6 +86,7 @@
},
{
"default": "0",
+ "description": "Setting the account as a Company Account is necessary for Bank Reconciliation",
"fieldname": "is_company_account",
"fieldtype": "Check",
"label": "Is Company Account"
@@ -207,7 +208,7 @@
}
],
"links": [],
- "modified": "2020-07-17 13:59:50.795412",
+ "modified": "2020-10-23 16:48:06.303658",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Bank Account",
diff --git a/erpnext/accounts/doctype/bank_statement_settings/__init__.py b/erpnext/accounts/doctype/bank_reconciliation_tool/__init__.py
similarity index 100%
copy from erpnext/accounts/doctype/bank_statement_settings/__init__.py
copy to erpnext/accounts/doctype/bank_reconciliation_tool/__init__.py
diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js
new file mode 100644
index 0000000..297dd43
--- /dev/null
+++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js
@@ -0,0 +1,162 @@
+// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+frappe.provide("erpnext.accounts.bank_reconciliation");
+
+frappe.ui.form.on("Bank Reconciliation Tool", {
+ setup: function (frm) {
+ frm.set_query("bank_account", function () {
+ return {
+ filters: {
+ company: ["in", frm.doc.company],
+ },
+ };
+ });
+ },
+
+ refresh: function (frm) {
+ frappe.require("assets/js/bank-reconciliation-tool.min.js", () =>
+ frm.trigger("make_reconciliation_tool")
+ );
+ frm.upload_statement_button = frm.page.set_secondary_action(
+ __("Upload Bank Statement"),
+ () =>
+ frappe.call({
+ method:
+ "erpnext.accounts.doctype.bank_statement_import.bank_statement_import.upload_bank_statement",
+ args: {
+ dt: frm.doc.doctype,
+ dn: frm.doc.name,
+ company: frm.doc.company,
+ bank_account: frm.doc.bank_account,
+ },
+ callback: function (r) {
+ if (!r.exc) {
+ var doc = frappe.model.sync(r.message);
+ frappe.set_route(
+ "Form",
+ doc[0].doctype,
+ doc[0].name
+ );
+ }
+ },
+ })
+ );
+ },
+
+ after_save: function (frm) {
+ frm.trigger("make_reconciliation_tool");
+ },
+
+ bank_account: function (frm) {
+ frappe.db.get_value(
+ "Bank Account",
+ frm.bank_account,
+ "account",
+ (r) => {
+ frappe.db.get_value(
+ "Account",
+ r.account,
+ "account_currency",
+ (r) => {
+ frm.currency = r.account_currency;
+ }
+ );
+ }
+ );
+ frm.trigger("get_account_opening_balance");
+ },
+
+ bank_statement_from_date: function (frm) {
+ frm.trigger("get_account_opening_balance");
+ },
+
+ make_reconciliation_tool(frm) {
+ frm.get_field("reconciliation_tool_cards").$wrapper.empty();
+ if (frm.doc.bank_account && frm.doc.bank_statement_to_date) {
+ frm.trigger("get_cleared_balance").then(() => {
+ if (
+ frm.doc.bank_account &&
+ frm.doc.bank_statement_from_date &&
+ frm.doc.bank_statement_to_date &&
+ frm.doc.bank_statement_closing_balance
+ ) {
+ frm.trigger("render_chart");
+ frm.trigger("render");
+ frappe.utils.scroll_to(
+ frm.get_field("reconciliation_tool_cards").$wrapper,
+ true,
+ 30
+ );
+ }
+ });
+ }
+ },
+
+ get_account_opening_balance(frm) {
+ if (frm.doc.bank_account && frm.doc.bank_statement_from_date) {
+ frappe.call({
+ method:
+ "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_account_balance",
+ args: {
+ bank_account: frm.doc.bank_account,
+ till_date: frm.doc.bank_statement_from_date,
+ },
+ callback: (response) => {
+ frm.set_value("account_opening_balance", response.message);
+ },
+ });
+ }
+ },
+
+ get_cleared_balance(frm) {
+ if (frm.doc.bank_account && frm.doc.bank_statement_to_date) {
+ return frappe.call({
+ method:
+ "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_account_balance",
+ args: {
+ bank_account: frm.doc.bank_account,
+ till_date: frm.doc.bank_statement_to_date,
+ },
+ callback: (response) => {
+ frm.cleared_balance = response.message;
+ },
+ });
+ }
+ },
+
+ render_chart(frm) {
+ frm.cards_manager = new erpnext.accounts.bank_reconciliation.NumberCardManager(
+ {
+ $reconciliation_tool_cards: frm.get_field(
+ "reconciliation_tool_cards"
+ ).$wrapper,
+ bank_statement_closing_balance:
+ frm.doc.bank_statement_closing_balance,
+ cleared_balance: frm.cleared_balance,
+ currency: frm.currency,
+ }
+ );
+ },
+
+ render(frm) {
+ if (frm.doc.bank_account) {
+ frm.bank_reconciliation_data_table_manager = new erpnext.accounts.bank_reconciliation.DataTableManager(
+ {
+ company: frm.doc.company,
+ bank_account: frm.doc.bank_account,
+ $reconciliation_tool_dt: frm.get_field(
+ "reconciliation_tool_dt"
+ ).$wrapper,
+ $no_bank_transactions: frm.get_field(
+ "no_bank_transactions"
+ ).$wrapper,
+ bank_statement_from_date: frm.doc.bank_statement_from_date,
+ bank_statement_to_date: frm.doc.bank_statement_to_date,
+ bank_statement_closing_balance:
+ frm.doc.bank_statement_closing_balance,
+ cards_manager: frm.cards_manager,
+ }
+ );
+ }
+ },
+});
diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.json b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.json
new file mode 100644
index 0000000..4837db3
--- /dev/null
+++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.json
@@ -0,0 +1,113 @@
+{
+ "actions": [],
+ "creation": "2020-12-02 10:13:02.148040",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "company",
+ "bank_account",
+ "column_break_1",
+ "bank_statement_from_date",
+ "bank_statement_to_date",
+ "column_break_2",
+ "account_opening_balance",
+ "bank_statement_closing_balance",
+ "section_break_1",
+ "reconciliation_tool_cards",
+ "reconciliation_tool_dt",
+ "no_bank_transactions"
+ ],
+ "fields": [
+ {
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "label": "Company",
+ "options": "Company"
+ },
+ {
+ "fieldname": "bank_account",
+ "fieldtype": "Link",
+ "label": "Bank Account",
+ "options": "Bank Account"
+ },
+ {
+ "fieldname": "column_break_1",
+ "fieldtype": "Column Break"
+ },
+ {
+ "depends_on": "eval: doc.bank_account",
+ "fieldname": "bank_statement_from_date",
+ "fieldtype": "Date",
+ "label": "Bank Statement From Date"
+ },
+ {
+ "depends_on": "eval: doc.bank_statement_from_date",
+ "fieldname": "bank_statement_to_date",
+ "fieldtype": "Date",
+ "label": "Bank Statement To Date"
+ },
+ {
+ "fieldname": "column_break_2",
+ "fieldtype": "Column Break"
+ },
+ {
+ "depends_on": "eval: doc.bank_statement_from_date",
+ "fieldname": "account_opening_balance",
+ "fieldtype": "Currency",
+ "label": "Account Opening Balance",
+ "options": "Currency",
+ "read_only": 1
+ },
+ {
+ "depends_on": "eval: doc.bank_statement_to_date",
+ "fieldname": "bank_statement_closing_balance",
+ "fieldtype": "Currency",
+ "label": "Bank Statement Closing Balance",
+ "options": "Currency"
+ },
+ {
+ "depends_on": "eval: doc.bank_statement_closing_balance",
+ "fieldname": "section_break_1",
+ "fieldtype": "Section Break",
+ "label": "Reconcile"
+ },
+ {
+ "fieldname": "reconciliation_tool_cards",
+ "fieldtype": "HTML"
+ },
+ {
+ "fieldname": "reconciliation_tool_dt",
+ "fieldtype": "HTML"
+ },
+ {
+ "fieldname": "no_bank_transactions",
+ "fieldtype": "HTML",
+ "options": "<div class=\"text-muted text-center\">No Matching Bank Transactions Found</div>"
+ }
+ ],
+ "hide_toolbar": 1,
+ "index_web_pages_for_search": 1,
+ "issingle": 1,
+ "links": [],
+ "modified": "2021-02-02 01:35:53.043578",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Bank Reconciliation Tool",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "print": 1,
+ "read": 1,
+ "role": "System Manager",
+ "share": 1,
+ "write": 1
+ }
+ ],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC"
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py
new file mode 100644
index 0000000..8a17233
--- /dev/null
+++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py
@@ -0,0 +1,452 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import json
+
+import frappe
+from frappe.model.document import Document
+from frappe import _
+from frappe.utils import flt
+
+from erpnext import get_company_currency
+from erpnext.accounts.utils import get_balance_on
+from erpnext.accounts.report.bank_reconciliation_statement.bank_reconciliation_statement import get_entries, get_amounts_not_reflected_in_system
+from erpnext.accounts.doctype.bank_transaction.bank_transaction import get_paid_amount
+
+
+class BankReconciliationTool(Document):
+ pass
+
+@frappe.whitelist()
+def get_bank_transactions(bank_account, from_date = None, to_date = None):
+ # returns bank transactions for a bank account
+ filters = []
+ filters.append(['bank_account', '=', bank_account])
+ filters.append(['docstatus', '=', 1])
+ filters.append(['unallocated_amount', '>', 0])
+ if to_date:
+ filters.append(['date', '<=', to_date])
+ if from_date:
+ filters.append(['date', '>=', from_date])
+ transactions = frappe.get_all(
+ 'Bank Transaction',
+ fields = ['date', 'deposit', 'withdrawal', 'currency',
+ 'description', 'name', 'bank_account', 'company',
+ 'unallocated_amount', 'reference_number', 'party_type', 'party'],
+ filters = filters
+ )
+ return transactions
+
+@frappe.whitelist()
+def get_account_balance(bank_account, till_date):
+ # returns account balance till the specified date
+ account = frappe.db.get_value('Bank Account', bank_account, 'account')
+ filters = frappe._dict({
+ "account": account,
+ "report_date": till_date,
+ "include_pos_transactions": 1
+ })
+ data = get_entries(filters)
+
+ balance_as_per_system = get_balance_on(filters["account"], filters["report_date"])
+
+ total_debit, total_credit = 0,0
+ for d in data:
+ total_debit += flt(d.debit)
+ total_credit += flt(d.credit)
+
+ amounts_not_reflected_in_system = get_amounts_not_reflected_in_system(filters)
+
+ bank_bal = flt(balance_as_per_system) - flt(total_debit) + flt(total_credit) \
+ + amounts_not_reflected_in_system
+
+ return bank_bal
+
+
+@frappe.whitelist()
+def update_bank_transaction(bank_transaction_name, reference_number, party_type=None, party=None):
+ # updates bank transaction based on the new parameters provided by the user from Vouchers
+ bank_transaction = frappe.get_doc("Bank Transaction", bank_transaction_name)
+ bank_transaction.reference_number = reference_number
+ bank_transaction.party_type = party_type
+ bank_transaction.party = party
+ bank_transaction.save()
+ return frappe.db.get_all('Bank Transaction',
+ filters={
+ 'name': bank_transaction_name
+ },
+ fields=['date', 'deposit', 'withdrawal', 'currency',
+ 'description', 'name', 'bank_account', 'company',
+ 'unallocated_amount', 'reference_number',
+ 'party_type', 'party'],
+ )[0]
+
+
+@frappe.whitelist()
+def create_journal_entry_bts( bank_transaction_name, reference_number=None, reference_date=None, posting_date=None, entry_type=None,
+ second_account=None, mode_of_payment=None, party_type=None, party=None, allow_edit=None):
+ # Create a new journal entry based on the bank transaction
+ bank_transaction = frappe.db.get_values(
+ "Bank Transaction", bank_transaction_name,
+ fieldname=["name", "deposit", "withdrawal", "bank_account"] ,
+ as_dict=True
+ )[0]
+ company_account = frappe.get_value("Bank Account", bank_transaction.bank_account, "account")
+ account_type = frappe.db.get_value("Account", second_account, "account_type")
+ if account_type in ["Receivable", "Payable"]:
+ if not (party_type and party):
+ frappe.throw(_("Party Type and Party is required for Receivable / Payable account {0}").format( second_account))
+ accounts = []
+ # Multi Currency?
+ accounts.append({
+ "account": second_account,
+ "credit_in_account_currency": bank_transaction.deposit
+ if bank_transaction.deposit > 0
+ else 0,
+ "debit_in_account_currency":bank_transaction.withdrawal
+ if bank_transaction.withdrawal > 0
+ else 0,
+ "party_type":party_type,
+ "party":party,
+ })
+
+ accounts.append({
+ "account": company_account,
+ "bank_account": bank_transaction.bank_account,
+ "credit_in_account_currency": bank_transaction.withdrawal
+ if bank_transaction.withdrawal > 0
+ else 0,
+ "debit_in_account_currency":bank_transaction.deposit
+ if bank_transaction.deposit > 0
+ else 0,
+ })
+
+ company = frappe.get_value("Account", company_account, "company")
+
+ journal_entry_dict = {
+ "voucher_type" : entry_type,
+ "company" : company,
+ "posting_date" : posting_date,
+ "cheque_date" : reference_date,
+ "cheque_no" : reference_number,
+ "mode_of_payment" : mode_of_payment
+ }
+ journal_entry = frappe.new_doc('Journal Entry')
+ journal_entry.update(journal_entry_dict)
+ journal_entry.set("accounts", accounts)
+
+
+ if allow_edit:
+ return journal_entry
+
+ journal_entry.insert()
+ journal_entry.submit()
+
+ if bank_transaction.deposit > 0:
+ paid_amount = bank_transaction.deposit
+ else:
+ paid_amount = bank_transaction.withdrawal
+
+ vouchers = json.dumps([{
+ "payment_doctype":"Journal Entry",
+ "payment_name":journal_entry.name,
+ "amount":paid_amount}])
+
+ return reconcile_vouchers(bank_transaction.name, vouchers)
+
+@frappe.whitelist()
+def create_payment_entry_bts( bank_transaction_name, reference_number=None, reference_date=None, party_type=None, party=None, posting_date=None,
+ mode_of_payment=None, project=None, cost_center=None, allow_edit=None):
+ # Create a new payment entry based on the bank transaction
+ bank_transaction = frappe.db.get_values(
+ "Bank Transaction", bank_transaction_name,
+ fieldname=["name", "unallocated_amount", "deposit", "bank_account"] ,
+ as_dict=True
+ )[0]
+ paid_amount = bank_transaction.unallocated_amount
+ payment_type = "Receive" if bank_transaction.deposit > 0 else "Pay"
+
+ company_account = frappe.get_value("Bank Account", bank_transaction.bank_account, "account")
+ company = frappe.get_value("Account", company_account, "company")
+ payment_entry_dict = {
+ "company" : company,
+ "payment_type" : payment_type,
+ "reference_no" : reference_number,
+ "reference_date" : reference_date,
+ "party_type" : party_type,
+ "party" : party,
+ "posting_date" : posting_date,
+ "paid_amount": paid_amount,
+ "received_amount": paid_amount
+ }
+ payment_entry = frappe.new_doc("Payment Entry")
+
+
+ payment_entry.update(payment_entry_dict)
+
+ if mode_of_payment:
+ payment_entry.mode_of_payment = mode_of_payment
+ if project:
+ payment_entry.project = project
+ if cost_center:
+ payment_entry.cost_center = cost_center
+ if payment_type == "Receive":
+ payment_entry.paid_to = company_account
+ else:
+ payment_entry.paid_from = company_account
+
+ payment_entry.validate()
+
+ if allow_edit:
+ return payment_entry
+
+ payment_entry.insert()
+
+ payment_entry.submit()
+ vouchers = json.dumps([{
+ "payment_doctype":"Payment Entry",
+ "payment_name":payment_entry.name,
+ "amount":paid_amount}])
+ return reconcile_vouchers(bank_transaction.name, vouchers)
+
+@frappe.whitelist()
+def reconcile_vouchers(bank_transaction_name, vouchers):
+ # updated clear date of all the vouchers based on the bank transaction
+ vouchers = json.loads(vouchers)
+ transaction = frappe.get_doc("Bank Transaction", bank_transaction_name)
+ if transaction.unallocated_amount == 0:
+ frappe.throw(_("This bank transaction is already fully reconciled"))
+ total_amount = 0
+ for voucher in vouchers:
+ voucher['payment_entry'] = frappe.get_doc(voucher['payment_doctype'], voucher['payment_name'])
+ total_amount += get_paid_amount(frappe._dict({
+ 'payment_document': voucher['payment_doctype'],
+ 'payment_entry': voucher['payment_name'],
+ }), transaction.currency)
+
+ if total_amount > transaction.unallocated_amount:
+ frappe.throw(_("The Sum Total of Amounts of All Selected Vouchers Should be Less than the Unallocated Amount of the Bank Transaction"))
+ account = frappe.db.get_value("Bank Account", transaction.bank_account, "account")
+
+ 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_entry'].doctype,
+ "payment_entry": voucher['payment_entry'].name,
+ "allocated_amount": allocated_amount
+ })
+
+ transaction.save()
+ transaction.update_allocations()
+ return frappe.get_doc("Bank Transaction", bank_transaction_name)
+
+@frappe.whitelist()
+def get_linked_payments(bank_transaction_name, document_types = None):
+ # get all matching payments for a bank transaction
+ transaction = frappe.get_doc("Bank Transaction", bank_transaction_name)
+ bank_account = frappe.db.get_values(
+ "Bank Account",
+ transaction.bank_account,
+ ["account", "company"],
+ as_dict=True)[0]
+ (account, company) = (bank_account.account, bank_account.company)
+ matching = check_matching(account, company, transaction, document_types)
+ return matching
+
+def check_matching(bank_account, company, transaction, document_types):
+ # combine all types of vocuhers
+ subquery = get_queries(bank_account, company, transaction, document_types)
+ filters = {
+ "amount": transaction.unallocated_amount,
+ "payment_type" : "Receive" if transaction.deposit > 0 else "Pay",
+ "reference_no": transaction.reference_number,
+ "party_type": transaction.party_type,
+ "party": transaction.party,
+ "bank_account": bank_account
+ }
+
+ matching_vouchers = []
+ for query in subquery:
+ matching_vouchers.extend(
+ frappe.db.sql(query, filters,)
+ )
+
+ return sorted(matching_vouchers, key = lambda x: x[0], reverse=True) if matching_vouchers else []
+
+def get_queries(bank_account, company, transaction, document_types):
+ # get queries to get matching vouchers
+ amount_condition = "=" if "exact_match" in document_types else "<="
+ account_from_to = "paid_to" if transaction.deposit > 0 else "paid_from"
+ queries = []
+
+ if "payment_entry" in document_types:
+ pe_amount_matching = get_pe_matching_query(amount_condition, account_from_to, transaction)
+ queries.extend([pe_amount_matching])
+
+ if "journal_entry" in document_types:
+ je_amount_matching = get_je_matching_query(amount_condition, transaction)
+ queries.extend([je_amount_matching])
+
+ if transaction.deposit > 0 and "sales_invoice" in document_types:
+ si_amount_matching = get_si_matching_query(amount_condition)
+ queries.extend([si_amount_matching])
+
+ if transaction.withdrawal > 0:
+ if "purchase_invoice" in document_types:
+ pi_amount_matching = get_pi_matching_query(amount_condition)
+ queries.extend([pi_amount_matching])
+
+ if "expense_claim" in document_types:
+ ec_amount_matching = get_ec_matching_query(bank_account, company, amount_condition)
+ queries.extend([ec_amount_matching])
+
+ return queries
+
+def get_pe_matching_query(amount_condition, account_from_to, transaction):
+ # get matching payment entries query
+ if transaction.deposit > 0:
+ currency_field = "paid_to_account_currency as currency"
+ else:
+ currency_field = "paid_from_account_currency as currency"
+ 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
+ + 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
+ paid_amount {amount_condition} %(amount)s
+ AND docstatus = 1
+ AND payment_type IN (%(payment_type)s, 'Internal Transfer')
+ AND ifnull(clearance_date, '') = ""
+ AND {account_from_to} = %(bank_account)s
+ """
+
+
+def get_je_matching_query(amount_condition, transaction):
+ # get matching journal entry query
+ cr_or_dr = "credit" if transaction.withdrawal > 0 else "debit"
+ return f"""
+
+ SELECT
+ (CASE WHEN je.cheque_no=%(reference_no)s THEN 1 ELSE 0 END
+ + 1) AS rank ,
+ '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,
+ 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.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_condition} %(amount)s
+ AND je.docstatus = 1
+ """
+
+
+def get_si_matching_query(amount_condition):
+ # get matchin sales invoice query
+ return f"""
+ SELECT
+ ( CASE WHEN si.customer = %(party)s THEN 1 ELSE 0 END
+ + 1 ) AS rank,
+ '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,
+ si.posting_date,
+ si.currency
+
+ FROM
+ `tabSales Invoice Payment` as sip
+ JOIN
+ `tabSales Invoice` as si
+ ON
+ sip.parent = si.name
+ WHERE (sip.clearance_date is null or sip.clearance_date='0000-00-00')
+ AND sip.account = %(bank_account)s
+ AND sip.amount {amount_condition} %(amount)s
+ AND si.docstatus = 1
+ """
+
+def get_pi_matching_query(amount_condition):
+ # get matching purchase invoice query
+ return f"""
+ SELECT
+ ( CASE WHEN supplier = %(party)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
+ paid_amount {amount_condition} %(amount)s
+ AND docstatus = 1
+ AND is_paid = 1
+ AND ifnull(clearance_date, '') = ""
+ AND cash_bank_account = %(bank_account)s
+ """
+
+def get_ec_matching_query(bank_account, company, amount_condition):
+ # get matching Expense Claim query
+ mode_of_payments = [x["parent"] for x in frappe.db.get_list("Mode of Payment Account",
+ filters={"default_account": bank_account}, fields=["parent"])]
+ mode_of_payments = '(\'' + '\', \''.join(mode_of_payments) + '\' )'
+ company_currency = get_company_currency(company)
+ return f"""
+ SELECT
+ ( CASE WHEN employee = %(party)s THEN 1 ELSE 0 END
+ + 1 ) AS rank,
+ 'Expense Claim' as doctype,
+ name,
+ total_sanctioned_amount as paid_amount,
+ '' as reference_no,
+ '' as reference_date,
+ employee as party,
+ 'Employee' as party_type,
+ posting_date,
+ '{company_currency}' as currency
+ FROM
+ `tabExpense Claim`
+ WHERE
+ total_sanctioned_amount {amount_condition} %(amount)s
+ AND docstatus = 1
+ AND is_paid = 1
+ AND ifnull(clearance_date, '') = ""
+ AND mode_of_payment in {mode_of_payments}
+ """
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
new file mode 100644
index 0000000..d96950a
--- /dev/null
+++ b/erpnext/accounts/doctype/bank_reconciliation_tool/test_bank_reconciliation_tool.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+# import frappe
+import unittest
+
+class TestBankReconciliationTool(unittest.TestCase):
+ pass
diff --git a/erpnext/accounts/doctype/bank_statement_settings/__init__.py b/erpnext/accounts/doctype/bank_statement_import/__init__.py
similarity index 100%
rename from erpnext/accounts/doctype/bank_statement_settings/__init__.py
rename to erpnext/accounts/doctype/bank_statement_import/__init__.py
diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.css b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.css
new file mode 100644
index 0000000..5206540
--- /dev/null
+++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.css
@@ -0,0 +1,3 @@
+.warnings .warning {
+ margin-bottom: 40px;
+}
diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js
new file mode 100644
index 0000000..ad4ff9e
--- /dev/null
+++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js
@@ -0,0 +1,574 @@
+// Copyright (c) 2019, Frappe Technologies and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on("Bank Statement Import", {
+ setup(frm) {
+ frappe.realtime.on("data_import_refresh", ({ data_import }) => {
+ frm.import_in_progress = false;
+ if (data_import !== frm.doc.name) return;
+ frappe.model.clear_doc("Bank Statement Import", frm.doc.name);
+ frappe.model
+ .with_doc("Bank Statement Import", frm.doc.name)
+ .then(() => {
+ frm.refresh();
+ });
+ });
+ frappe.realtime.on("data_import_progress", (data) => {
+ frm.import_in_progress = true;
+ if (data.data_import !== frm.doc.name) {
+ return;
+ }
+ let percent = Math.floor((data.current * 100) / data.total);
+ let seconds = Math.floor(data.eta);
+ let minutes = Math.floor(data.eta / 60);
+ let eta_message =
+ // prettier-ignore
+ seconds < 60
+ ? __('About {0} seconds remaining', [seconds])
+ : minutes === 1
+ ? __('About {0} minute remaining', [minutes])
+ : __('About {0} minutes remaining', [minutes]);
+
+ let message;
+ if (data.success) {
+ let message_args = [data.current, data.total, eta_message];
+ message =
+ frm.doc.import_type === "Insert New Records"
+ ? __("Importing {0} of {1}, {2}", message_args)
+ : __("Updating {0} of {1}, {2}", message_args);
+ }
+ if (data.skipping) {
+ message = __(
+ "Skipping {0} of {1}, {2}",
+ [
+ data.current,
+ data.total,
+ eta_message,
+ ]
+ );
+ }
+ frm.dashboard.show_progress(
+ __("Import Progress"),
+ percent,
+ message
+ );
+ frm.page.set_indicator(__("In Progress"), "orange");
+
+ // hide progress when complete
+ if (data.current === data.total) {
+ setTimeout(() => {
+ frm.dashboard.hide();
+ frm.refresh();
+ }, 2000);
+ }
+ });
+
+ frm.set_query("reference_doctype", () => {
+ return {
+ filters: {
+ name: ["in", frappe.boot.user.can_import],
+ },
+ };
+ });
+
+ frm.get_field("import_file").df.options = {
+ restrictions: {
+ allowed_file_types: [".csv", ".xls", ".xlsx"],
+ },
+ };
+
+ frm.has_import_file = () => {
+ return frm.doc.import_file || frm.doc.google_sheets_url;
+ };
+ },
+
+ refresh(frm) {
+ frm.page.hide_icon_group();
+ frm.trigger("update_indicators");
+ frm.trigger("import_file");
+ frm.trigger("show_import_log");
+ frm.trigger("show_import_warnings");
+ frm.trigger("toggle_submit_after_import");
+ frm.trigger("show_import_status");
+ frm.trigger("show_report_error_button");
+
+ if (frm.doc.status === "Partial Success") {
+ frm.add_custom_button(__("Export Errored Rows"), () =>
+ frm.trigger("export_errored_rows")
+ );
+ }
+
+ if (frm.doc.status.includes("Success")) {
+ frm.add_custom_button(
+ __("Go to {0} List", [frm.doc.reference_doctype]),
+ () => frappe.set_route("List", frm.doc.reference_doctype)
+ );
+ }
+ },
+
+ onload_post_render(frm) {
+ frm.trigger("update_primary_action");
+ },
+
+ update_primary_action(frm) {
+ if (frm.is_dirty()) {
+ frm.enable_save();
+ return;
+ }
+ frm.disable_save();
+ if (frm.doc.status !== "Success") {
+ if (!frm.is_new() && frm.has_import_file()) {
+ let label =
+ frm.doc.status === "Pending"
+ ? __("Start Import")
+ : __("Retry");
+ frm.page.set_primary_action(label, () =>
+ frm.events.start_import(frm)
+ );
+ } else {
+ frm.page.set_primary_action(__("Save"), () => frm.save());
+ }
+ }
+ },
+
+ update_indicators(frm) {
+ const indicator = frappe.get_indicator(frm.doc);
+ if (indicator) {
+ frm.page.set_indicator(indicator[0], indicator[1]);
+ } else {
+ frm.page.clear_indicator();
+ }
+ },
+
+ show_import_status(frm) {
+ let import_log = JSON.parse(frm.doc.import_log || "[]");
+ let successful_records = import_log.filter((log) => log.success);
+ let failed_records = import_log.filter((log) => !log.success);
+ if (successful_records.length === 0) return;
+
+ let message;
+ if (failed_records.length === 0) {
+ let message_args = [successful_records.length];
+ if (frm.doc.import_type === "Insert New Records") {
+ message =
+ successful_records.length > 1
+ ? __("Successfully imported {0} records.", message_args)
+ : __("Successfully imported {0} record.", message_args);
+ } else {
+ message =
+ successful_records.length > 1
+ ? __("Successfully updated {0} records.", message_args)
+ : __("Successfully updated {0} record.", message_args);
+ }
+ } else {
+ let message_args = [successful_records.length, import_log.length];
+ if (frm.doc.import_type === "Insert New Records") {
+ message =
+ successful_records.length > 1
+ ? __(
+ "Successfully imported {0} records out of {1}. Click on Export Errored Rows, fix the errors and import again.",
+ message_args
+ )
+ : __(
+ "Successfully imported {0} record out of {1}. Click on Export Errored Rows, fix the errors and import again.",
+ message_args
+ );
+ } else {
+ message =
+ successful_records.length > 1
+ ? __(
+ "Successfully updated {0} records out of {1}. Click on Export Errored Rows, fix the errors and import again.",
+ message_args
+ )
+ : __(
+ "Successfully updated {0} record out of {1}. Click on Export Errored Rows, fix the errors and import again.",
+ message_args
+ );
+ }
+ }
+ frm.dashboard.set_headline(message);
+ },
+
+ show_report_error_button(frm) {
+ if (frm.doc.status === "Error") {
+ frappe.db
+ .get_list("Error Log", {
+ filters: { method: frm.doc.name },
+ fields: ["method", "error"],
+ order_by: "creation desc",
+ limit: 1,
+ })
+ .then((result) => {
+ if (result.length > 0) {
+ frm.add_custom_button("Report Error", () => {
+ let fake_xhr = {
+ responseText: JSON.stringify({
+ exc: result[0].error,
+ }),
+ };
+ frappe.request.report_error(fake_xhr, {});
+ });
+ }
+ });
+ }
+ },
+
+ start_import(frm) {
+ frm.call({
+ method: "form_start_import",
+ args: { data_import: frm.doc.name },
+ btn: frm.page.btn_primary,
+ }).then((r) => {
+ if (r.message === true) {
+ frm.disable_save();
+ }
+ });
+ },
+
+ download_template() {
+ let method =
+ "/api/method/frappe.core.doctype.data_import.data_import.download_template";
+
+ open_url_post(method, {
+ doctype: "Bank Transaction",
+ export_records: "5_records",
+ export_fields: {
+ "Bank Transaction": [
+ "date",
+ "deposit",
+ "withdrawal",
+ "description",
+ "reference_number",
+ ],
+ },
+ });
+ },
+
+ reference_doctype(frm) {
+ frm.trigger("toggle_submit_after_import");
+ },
+
+ toggle_submit_after_import(frm) {
+ frm.toggle_display("submit_after_import", false);
+ let doctype = frm.doc.reference_doctype;
+ if (doctype) {
+ frappe.model.with_doctype(doctype, () => {
+ let meta = frappe.get_meta(doctype);
+ frm.toggle_display("submit_after_import", meta.is_submittable);
+ });
+ }
+ },
+
+ google_sheets_url(frm) {
+ if (!frm.is_dirty()) {
+ frm.trigger("import_file");
+ } else {
+ frm.trigger("update_primary_action");
+ }
+ },
+
+ refresh_google_sheet(frm) {
+ frm.trigger("import_file");
+ },
+
+ import_file(frm) {
+ frm.toggle_display("section_import_preview", frm.has_import_file());
+ if (!frm.has_import_file()) {
+ frm.get_field("import_preview").$wrapper.empty();
+ return;
+ } else {
+ frm.trigger("update_primary_action");
+ }
+
+ // load import preview
+ frm.get_field("import_preview").$wrapper.empty();
+ $('<span class="text-muted">')
+ .html(__("Loading import file..."))
+ .appendTo(frm.get_field("import_preview").$wrapper);
+
+ frm.call({
+ method: "get_preview_from_template",
+ args: {
+ data_import: frm.doc.name,
+ import_file: frm.doc.import_file,
+ google_sheets_url: frm.doc.google_sheets_url,
+ },
+ error_handlers: {
+ TimestampMismatchError() {
+ // ignore this error
+ },
+ },
+ }).then((r) => {
+ let preview_data = r.message;
+ frm.events.show_import_preview(frm, preview_data);
+ frm.events.show_import_warnings(frm, preview_data);
+ });
+ },
+ // method: 'frappe.core.doctype.data_import.data_import.get_preview_from_template',
+
+ show_import_preview(frm, preview_data) {
+ let import_log = JSON.parse(frm.doc.import_log || "[]");
+
+ if (
+ frm.import_preview &&
+ frm.import_preview.doctype === frm.doc.reference_doctype
+ ) {
+ frm.import_preview.preview_data = preview_data;
+ frm.import_preview.import_log = import_log;
+ frm.import_preview.refresh();
+ return;
+ }
+
+ frappe.require("/assets/js/data_import_tools.min.js", () => {
+ frm.import_preview = new frappe.data_import.ImportPreview({
+ wrapper: frm.get_field("import_preview").$wrapper,
+ doctype: frm.doc.reference_doctype,
+ preview_data,
+ import_log,
+ frm,
+ events: {
+ remap_column(changed_map) {
+ let template_options = JSON.parse(
+ frm.doc.template_options || "{}"
+ );
+ template_options.column_to_field_map =
+ template_options.column_to_field_map || {};
+ Object.assign(
+ template_options.column_to_field_map,
+ changed_map
+ );
+ frm.set_value(
+ "template_options",
+ JSON.stringify(template_options)
+ );
+ frm.save().then(() => frm.trigger("import_file"));
+ },
+ },
+ });
+ });
+ },
+
+ export_errored_rows(frm) {
+ open_url_post(
+ "/api/method/frappe.core.doctype.data_import.data_import.download_errored_template",
+ {
+ data_import_name: frm.doc.name,
+ }
+ );
+ },
+
+ show_import_warnings(frm, preview_data) {
+ let columns = preview_data.columns;
+ let warnings = JSON.parse(frm.doc.template_warnings || "[]");
+ warnings = warnings.concat(preview_data.warnings || []);
+
+ frm.toggle_display("import_warnings_section", warnings.length > 0);
+ if (warnings.length === 0) {
+ frm.get_field("import_warnings").$wrapper.html("");
+ return;
+ }
+
+ // group warnings by row
+ let warnings_by_row = {};
+ let other_warnings = [];
+ for (let warning of warnings) {
+ if (warning.row) {
+ warnings_by_row[warning.row] =
+ warnings_by_row[warning.row] || [];
+ warnings_by_row[warning.row].push(warning);
+ } else {
+ other_warnings.push(warning);
+ }
+ }
+
+ let html = "";
+ html += Object.keys(warnings_by_row)
+ .map((row_number) => {
+ let message = warnings_by_row[row_number]
+ .map((w) => {
+ if (w.field) {
+ let label =
+ w.field.label +
+ (w.field.parent !== frm.doc.reference_doctype
+ ? ` (${w.field.parent})`
+ : "");
+ return `<li>${label}: ${w.message}</li>`;
+ }
+ return `<li>${w.message}</li>`;
+ })
+ .join("");
+ return `
+ <div class="warning" data-row="${row_number}">
+ <h5 class="text-uppercase">${__("Row {0}", [row_number])}</h5>
+ <div class="body"><ul>${message}</ul></div>
+ </div>
+ `;
+ })
+ .join("");
+
+ html += other_warnings
+ .map((warning) => {
+ let header = "";
+ if (warning.col) {
+ let column_number = `<span class="text-uppercase">${__(
+ "Column {0}",
+ [warning.col]
+ )}</span>`;
+ let column_header = columns[warning.col].header_title;
+ header = `${column_number} (${column_header})`;
+ }
+ return `
+ <div class="warning" data-col="${warning.col}">
+ <h5>${header}</h5>
+ <div class="body">${warning.message}</div>
+ </div>
+ `;
+ })
+ .join("");
+ frm.get_field("import_warnings").$wrapper.html(`
+ <div class="row">
+ <div class="col-sm-10 warnings">${html}</div>
+ </div>
+ `);
+ },
+
+ show_failed_logs(frm) {
+ frm.trigger("show_import_log");
+ },
+
+ show_import_log(frm) {
+ let import_log = JSON.parse(frm.doc.import_log || "[]");
+ let logs = import_log;
+ frm.toggle_display("import_log", false);
+ frm.toggle_display("import_log_section", logs.length > 0);
+
+ if (logs.length === 0) {
+ frm.get_field("import_log_preview").$wrapper.empty();
+ return;
+ }
+
+ let rows = logs
+ .map((log) => {
+ let html = "";
+ if (log.success) {
+ if (frm.doc.import_type === "Insert New Records") {
+ html = __(
+ "Successfully imported {0}", [
+ `<span class="underline">${frappe.utils.get_form_link(
+ frm.doc.reference_doctype,
+ log.docname,
+ true
+ )}<span>`,
+ ]
+ );
+ } else {
+ html = __(
+ "Successfully updated {0}", [
+ `<span class="underline">${frappe.utils.get_form_link(
+ frm.doc.reference_doctype,
+ log.docname,
+ true
+ )}<span>`,
+ ]
+ );
+ }
+ } else {
+ let messages = log.messages
+ .map(JSON.parse)
+ .map((m) => {
+ let title = m.title
+ ? `<strong>${m.title}</strong>`
+ : "";
+ let message = m.message
+ ? `<div>${m.message}</div>`
+ : "";
+ return title + message;
+ })
+ .join("");
+ let id = frappe.dom.get_unique_id();
+ html = `${messages}
+ <button class="btn btn-default btn-xs" type="button" data-toggle="collapse" data-target="#${id}" aria-expanded="false" aria-controls="${id}" style="margin-top: 15px;">
+ ${__("Show Traceback")}
+ </button>
+ <div class="collapse" id="${id}" style="margin-top: 15px;">
+ <div class="well">
+ <pre>${log.exception}</pre>
+ </div>
+ </div>`;
+ }
+ let indicator_color = log.success ? "green" : "red";
+ let title = log.success ? __("Success") : __("Failure");
+
+ if (frm.doc.show_failed_logs && log.success) {
+ return "";
+ }
+
+ return `<tr>
+ <td>${log.row_indexes.join(", ")}</td>
+ <td>
+ <div class="indicator ${indicator_color}">${title}</div>
+ </td>
+ <td>
+ ${html}
+ </td>
+ </tr>`;
+ })
+ .join("");
+
+ if (!rows && frm.doc.show_failed_logs) {
+ rows = `<tr><td class="text-center text-muted" colspan=3>
+ ${__("No failed logs")}
+ </td></tr>`;
+ }
+
+ frm.get_field("import_log_preview").$wrapper.html(`
+ <table class="table table-bordered">
+ <tr class="text-muted">
+ <th width="10%">${__("Row Number")}</th>
+ <th width="10%">${__("Status")}</th>
+ <th width="80%">${__("Message")}</th>
+ </tr>
+ ${rows}
+ </table>
+ `);
+ },
+
+ show_missing_link_values(frm, missing_link_values) {
+ let can_be_created_automatically = missing_link_values.every(
+ (d) => d.has_one_mandatory_field
+ );
+
+ let html = missing_link_values
+ .map((d) => {
+ let doctype = d.doctype;
+ let values = d.missing_values;
+ return `
+ <h5>${doctype}</h5>
+ <ul>${values.map((v) => `<li>${v}</li>`).join("")}</ul>
+ `;
+ })
+ .join("");
+
+ if (can_be_created_automatically) {
+ // prettier-ignore
+ let message = __('There are some linked records which needs to be created before we can import your file. Do you want to create the following missing records automatically?');
+ frappe.confirm(message + html, () => {
+ frm.call("create_missing_link_values", {
+ missing_link_values,
+ }).then((r) => {
+ let records = r.message;
+ frappe.msgprint(__(
+ "Created {0} records successfully.", [
+ records.length,
+ ]
+ ));
+ });
+ });
+ } else {
+ frappe.msgprint(
+ // prettier-ignore
+ __('The following records needs to be created before we can import your file.') + html
+ );
+ }
+ },
+});
diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.json b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.json
new file mode 100644
index 0000000..5e913cc
--- /dev/null
+++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.json
@@ -0,0 +1,227 @@
+{
+ "actions": [],
+ "autoname": "format:Bank Statement Import on {creation}",
+ "beta": 1,
+ "creation": "2019-08-04 14:16:08.318714",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "company",
+ "bank_account",
+ "bank",
+ "column_break_4",
+ "google_sheets_url",
+ "refresh_google_sheet",
+ "html_5",
+ "import_file",
+ "download_template",
+ "status",
+ "template_options",
+ "import_warnings_section",
+ "template_warnings",
+ "import_warnings",
+ "section_import_preview",
+ "import_preview",
+ "import_log_section",
+ "import_log",
+ "show_failed_logs",
+ "import_log_preview",
+ "reference_doctype",
+ "import_type",
+ "submit_after_import",
+ "mute_emails"
+ ],
+ "fields": [
+ {
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "label": "Company",
+ "options": "Company",
+ "reqd": 1,
+ "set_only_once": 1
+ },
+ {
+ "fieldname": "bank_account",
+ "fieldtype": "Link",
+ "label": "Bank Account",
+ "options": "Bank Account",
+ "reqd": 1,
+ "set_only_once": 1
+ },
+ {
+ "depends_on": "eval:doc.bank_account",
+ "fetch_from": "bank_account.bank",
+ "fieldname": "bank",
+ "fieldtype": "Link",
+ "label": "Bank",
+ "options": "Bank",
+ "read_only": 1,
+ "set_only_once": 1
+ },
+ {
+ "depends_on": "eval:!doc.__islocal",
+ "fieldname": "download_template",
+ "fieldtype": "Button",
+ "label": "Download Template"
+ },
+ {
+ "depends_on": "eval:!doc.__islocal",
+ "fieldname": "import_file",
+ "fieldtype": "Attach",
+ "in_list_view": 1,
+ "label": "Import File"
+ },
+ {
+ "fieldname": "import_preview",
+ "fieldtype": "HTML",
+ "label": "Import Preview"
+ },
+ {
+ "fieldname": "section_import_preview",
+ "fieldtype": "Section Break",
+ "label": "Preview"
+ },
+ {
+ "fieldname": "template_options",
+ "fieldtype": "Code",
+ "hidden": 1,
+ "label": "Template Options",
+ "options": "JSON",
+ "read_only": 1
+ },
+ {
+ "fieldname": "import_log",
+ "fieldtype": "Code",
+ "label": "Import Log",
+ "options": "JSON"
+ },
+ {
+ "fieldname": "import_log_section",
+ "fieldtype": "Section Break",
+ "label": "Import Log"
+ },
+ {
+ "fieldname": "import_log_preview",
+ "fieldtype": "HTML",
+ "label": "Import Log Preview"
+ },
+ {
+ "default": "Pending",
+ "fieldname": "status",
+ "fieldtype": "Select",
+ "hidden": 1,
+ "label": "Status",
+ "options": "Pending\nSuccess\nPartial Success\nError",
+ "read_only": 1
+ },
+ {
+ "fieldname": "template_warnings",
+ "fieldtype": "Code",
+ "hidden": 1,
+ "label": "Template Warnings",
+ "options": "JSON"
+ },
+ {
+ "fieldname": "import_warnings_section",
+ "fieldtype": "Section Break",
+ "label": "Import File Errors and Warnings"
+ },
+ {
+ "fieldname": "import_warnings",
+ "fieldtype": "HTML",
+ "label": "Import Warnings"
+ },
+ {
+ "default": "0",
+ "fieldname": "show_failed_logs",
+ "fieldtype": "Check",
+ "label": "Show Failed Logs"
+ },
+ {
+ "depends_on": "eval:!doc.__islocal && !doc.import_file",
+ "fieldname": "html_5",
+ "fieldtype": "HTML",
+ "options": "<h5 class=\"text-muted uppercase\">Or</h5>"
+ },
+ {
+ "depends_on": "eval:!doc.__islocal && !doc.import_file\n",
+ "description": "Must be a publicly accessible Google Sheets URL",
+ "fieldname": "google_sheets_url",
+ "fieldtype": "Data",
+ "label": "Import from Google Sheets"
+ },
+ {
+ "depends_on": "eval:doc.google_sheets_url && !doc.__unsaved",
+ "fieldname": "refresh_google_sheet",
+ "fieldtype": "Button",
+ "label": "Refresh Google Sheet"
+ },
+ {
+ "default": "Bank Transaction",
+ "fieldname": "reference_doctype",
+ "fieldtype": "Link",
+ "hidden": 1,
+ "in_list_view": 1,
+ "label": "Document Type",
+ "options": "DocType",
+ "reqd": 1,
+ "set_only_once": 1
+ },
+ {
+ "default": "Insert New Records",
+ "fieldname": "import_type",
+ "fieldtype": "Select",
+ "hidden": 1,
+ "in_list_view": 1,
+ "label": "Import Type",
+ "options": "\nInsert New Records\nUpdate Existing Records",
+ "reqd": 1,
+ "set_only_once": 1
+ },
+ {
+ "default": "1",
+ "fieldname": "submit_after_import",
+ "fieldtype": "Check",
+ "hidden": 1,
+ "label": "Submit After Import",
+ "set_only_once": 1
+ },
+ {
+ "default": "1",
+ "fieldname": "mute_emails",
+ "fieldtype": "Check",
+ "hidden": 1,
+ "label": "Don't Send Emails",
+ "set_only_once": 1
+ },
+ {
+ "fieldname": "column_break_4",
+ "fieldtype": "Column Break"
+ }
+ ],
+ "hide_toolbar": 1,
+ "links": [],
+ "modified": "2021-02-10 19:29:59.027325",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Bank Statement Import",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "share": 1,
+ "write": 1
+ }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py
new file mode 100644
index 0000000..9f41b13
--- /dev/null
+++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py
@@ -0,0 +1,205 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019, Frappe Technologies and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import csv
+import json
+import re
+
+import openpyxl
+from openpyxl.styles import Font
+from openpyxl.utils import get_column_letter
+from six import string_types
+
+import frappe
+from frappe.core.doctype.data_import.importer import Importer, ImportFile
+from frappe.utils.background_jobs import enqueue
+from frappe.utils.xlsxutils import handle_html, ILLEGAL_CHARACTERS_RE
+from frappe import _
+
+from frappe.core.doctype.data_import.data_import import DataImport
+
+class BankStatementImport(DataImport):
+ def __init__(self, *args, **kwargs):
+ super(BankStatementImport, self).__init__(*args, **kwargs)
+
+ def validate(self):
+ doc_before_save = self.get_doc_before_save()
+ if (
+ not (self.import_file or self.google_sheets_url)
+ or (doc_before_save and doc_before_save.import_file != self.import_file)
+ or (doc_before_save and doc_before_save.google_sheets_url != self.google_sheets_url)
+ ):
+
+ template_options_dict = {}
+ column_to_field_map = {}
+ bank = frappe.get_doc("Bank", self.bank)
+ for i in bank.bank_transaction_mapping:
+ column_to_field_map[i.file_field] = i.bank_transaction_field
+ template_options_dict["column_to_field_map"] = column_to_field_map
+ self.template_options = json.dumps(template_options_dict)
+
+ self.template_warnings = ""
+
+ self.validate_import_file()
+ self.validate_google_sheets_url()
+
+ def start_import(self):
+
+ from frappe.core.page.background_jobs.background_jobs import get_info
+ from frappe.utils.scheduler import is_scheduler_inactive
+
+ if is_scheduler_inactive() and not frappe.flags.in_test:
+ frappe.throw(
+ _("Scheduler is inactive. Cannot import data."), title=_("Scheduler Inactive")
+ )
+
+ enqueued_jobs = [d.get("job_name") for d in get_info()]
+
+ if self.name not in enqueued_jobs:
+ enqueue(
+ start_import,
+ queue="default",
+ timeout=6000,
+ event="data_import",
+ job_name=self.name,
+ data_import=self.name,
+ bank_account=self.bank_account,
+ import_file_path=self.import_file,
+ bank=self.bank,
+ template_options=self.template_options,
+ now=frappe.conf.developer_mode or frappe.flags.in_test,
+ )
+ return True
+
+ return False
+
+@frappe.whitelist()
+def get_preview_from_template(data_import, import_file=None, google_sheets_url=None):
+ return frappe.get_doc("Bank Statement Import", data_import).get_preview_from_template(
+ import_file, google_sheets_url
+ )
+
+@frappe.whitelist()
+def form_start_import(data_import):
+ return frappe.get_doc("Bank Statement Import", data_import).start_import()
+
+@frappe.whitelist()
+def download_errored_template(data_import_name):
+ data_import = frappe.get_doc("Bank Statement Import", data_import_name)
+ data_import.export_errored_rows()
+
+def start_import(data_import, bank_account, import_file_path, bank, template_options):
+ """This method runs in background job"""
+
+ update_mapping_db(bank, template_options)
+
+ data_import = frappe.get_doc("Bank Statement Import", data_import)
+
+ import_file = ImportFile("Bank Transaction", file = import_file_path, import_type="Insert New Records")
+ data = import_file.raw_data
+
+ add_bank_account(data, bank_account)
+ write_files(import_file, data)
+
+ try:
+ i = Importer(data_import.reference_doctype, data_import=data_import)
+ i.import_data()
+ except Exception:
+ frappe.db.rollback()
+ data_import.db_set("status", "Error")
+ frappe.log_error(title=data_import.name)
+ finally:
+ frappe.flags.in_import = False
+
+ frappe.publish_realtime("data_import_refresh", {"data_import": data_import.name})
+
+def update_mapping_db(bank, template_options):
+ bank = frappe.get_doc("Bank", bank)
+ for d in bank.bank_transaction_mapping:
+ d.delete()
+
+ for d in json.loads(template_options)["column_to_field_map"].items():
+ bank.append("bank_transaction_mapping", {"bank_transaction_field": d[1] ,"file_field": d[0]} )
+
+ bank.save()
+
+def add_bank_account(data, bank_account):
+ bank_account_loc = None
+ if "Bank Account" not in data[0]:
+ data[0].append("Bank Account")
+ else:
+ for loc, header in enumerate(data[0]):
+ if header == "Bank Account":
+ bank_account_loc = loc
+
+ for row in data[1:]:
+ if bank_account_loc:
+ row[bank_account_loc] = bank_account
+ else:
+ row.append(bank_account)
+
+def write_files(import_file, data):
+ full_file_path = import_file.file_doc.get_full_path()
+ parts = import_file.file_doc.get_extension()
+ extension = parts[1]
+ extension = extension.lstrip(".")
+
+ if extension == "csv":
+ with open(full_file_path, 'w', newline='') as file:
+ writer = csv.writer(file)
+ writer.writerows(data)
+ elif extension == "xlsx" or "xls":
+ write_xlsx(data, "trans", file_path = full_file_path)
+
+def write_xlsx(data, sheet_name, wb=None, column_widths=None, file_path=None):
+ # from xlsx utils with changes
+ column_widths = column_widths or []
+ if wb is None:
+ wb = openpyxl.Workbook(write_only=True)
+
+ ws = wb.create_sheet(sheet_name, 0)
+
+ for i, column_width in enumerate(column_widths):
+ if column_width:
+ ws.column_dimensions[get_column_letter(i + 1)].width = column_width
+
+ row1 = ws.row_dimensions[1]
+ row1.font = Font(name='Calibri', bold=True)
+
+ for row in data:
+ clean_row = []
+ for item in row:
+ if isinstance(item, string_types) and (sheet_name not in ['Data Import Template', 'Data Export']):
+ value = handle_html(item)
+ else:
+ value = item
+
+ if isinstance(item, string_types) and next(ILLEGAL_CHARACTERS_RE.finditer(value), None):
+ # Remove illegal characters from the string
+ value = re.sub(ILLEGAL_CHARACTERS_RE, '', value)
+
+ clean_row.append(value)
+
+ ws.append(clean_row)
+
+ wb.save(file_path)
+ return True
+
+@frappe.whitelist()
+def upload_bank_statement(**args):
+ args = frappe._dict(args)
+ bsi = frappe.new_doc("Bank Statement Import")
+
+ if args.company:
+ bsi.update({
+ "company": args.company,
+ })
+
+ if args.bank_account:
+ bsi.update({
+ "bank_account": args.bank_account
+ })
+
+ return bsi
diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import_list.js b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import_list.js
new file mode 100644
index 0000000..6c75402
--- /dev/null
+++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import_list.js
@@ -0,0 +1,36 @@
+let imports_in_progress = [];
+
+frappe.listview_settings['Bank Statement Import'] = {
+ onload(listview) {
+ frappe.realtime.on('data_import_progress', data => {
+ if (!imports_in_progress.includes(data.data_import)) {
+ imports_in_progress.push(data.data_import);
+ }
+ });
+ frappe.realtime.on('data_import_refresh', data => {
+ imports_in_progress = imports_in_progress.filter(
+ d => d !== data.data_import
+ );
+ listview.refresh();
+ });
+ },
+ get_indicator: function(doc) {
+ var colors = {
+ 'Pending': 'orange',
+ 'Not Started': 'orange',
+ 'Partial Success': 'orange',
+ 'Success': 'green',
+ 'In Progress': 'orange',
+ 'Error': 'red'
+ };
+ let status = doc.status;
+ if (imports_in_progress.includes(doc.name)) {
+ status = 'In Progress';
+ }
+ if (status == 'Pending') {
+ status = 'Not Started';
+ }
+ return [__(status), colors[status], 'status,=,' + doc.status];
+ },
+ hide_name_column: true
+};
diff --git a/erpnext/accounts/doctype/bank_statement_import/test_bank_statement_import.py b/erpnext/accounts/doctype/bank_statement_import/test_bank_statement_import.py
new file mode 100644
index 0000000..cd58314
--- /dev/null
+++ b/erpnext/accounts/doctype/bank_statement_import/test_bank_statement_import.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+# import frappe
+import unittest
+
+class TestBankStatementImport(unittest.TestCase):
+ pass
diff --git a/erpnext/accounts/doctype/bank_statement_settings/bank_statement_settings.js b/erpnext/accounts/doctype/bank_statement_settings/bank_statement_settings.js
deleted file mode 100644
index 46aa4f2..0000000
--- a/erpnext/accounts/doctype/bank_statement_settings/bank_statement_settings.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) 2017, sathishpy@gmail.com and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Bank Statement Settings', {
- refresh: function(frm) {
-
- }
-});
diff --git a/erpnext/accounts/doctype/bank_statement_settings/bank_statement_settings.json b/erpnext/accounts/doctype/bank_statement_settings/bank_statement_settings.json
deleted file mode 100644
index 53fbf7d..0000000
--- a/erpnext/accounts/doctype/bank_statement_settings/bank_statement_settings.json
+++ /dev/null
@@ -1,272 +0,0 @@
-{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 1,
- "beta": 0,
- "creation": "2017-11-13 13:38:10.863592",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "",
- "editable_grid": 1,
- "engine": "InnoDB",
- "fields": [
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "bank",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Bank Account",
- "length": 0,
- "no_copy": 0,
- "options": "Bank",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "'%d/%m/%Y'",
- "fieldname": "date_format",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Date Format",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "statement_header_mapping",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Statement Header Mapping",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "header_items",
- "fieldtype": "Table",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Statement Headers",
- "length": 0,
- "no_copy": 0,
- "options": "Bank Statement Settings Item",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "transaction_data_mapping",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Transaction Data Mapping",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "mapped_items",
- "fieldtype": "Table",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Mapped Items",
- "length": 0,
- "no_copy": 0,
- "options": "Bank Statement Transaction Settings Item",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- }
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2018-04-07 18:57:04.048423",
- "modified_by": "Administrator",
- "module": "Accounts",
- "name": "Bank Statement Settings",
- "name_case": "",
- "owner": "Administrator",
- "permissions": [
- {
- "amend": 0,
- "apply_user_permissions": 0,
- "cancel": 0,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "System Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
- "write": 1
- },
- {
- "amend": 0,
- "apply_user_permissions": 0,
- "cancel": 0,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Accounts Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
- "write": 1
- }
- ],
- "quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1,
- "track_seen": 0
-}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/bank_statement_settings/bank_statement_settings.py b/erpnext/accounts/doctype/bank_statement_settings/bank_statement_settings.py
deleted file mode 100644
index 6c4dd1b..0000000
--- a/erpnext/accounts/doctype/bank_statement_settings/bank_statement_settings.py
+++ /dev/null
@@ -1,11 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, sathishpy@gmail.com and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.model.document import Document
-
-class BankStatementSettings(Document):
- def autoname(self):
- self.name = self.bank + "-Statement-Settings"
diff --git a/erpnext/accounts/doctype/bank_statement_settings/test_bank_statement_settings.js b/erpnext/accounts/doctype/bank_statement_settings/test_bank_statement_settings.js
deleted file mode 100644
index f2381c0..0000000
--- a/erpnext/accounts/doctype/bank_statement_settings/test_bank_statement_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Bank Statement Settings", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Bank Statement Settings
- () => frappe.tests.make('Bank Statement Settings', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/accounts/doctype/bank_statement_settings/test_bank_statement_settings.py b/erpnext/accounts/doctype/bank_statement_settings/test_bank_statement_settings.py
deleted file mode 100644
index aa7fe83..0000000
--- a/erpnext/accounts/doctype/bank_statement_settings/test_bank_statement_settings.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, sathishpy@gmail.com and Contributors
-# See license.txt
-from __future__ import unicode_literals
-
-import frappe
-import unittest
-
-class TestBankStatementSettings(unittest.TestCase):
- pass
diff --git a/erpnext/accounts/doctype/bank_statement_settings_item/__init__.py b/erpnext/accounts/doctype/bank_statement_settings_item/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/accounts/doctype/bank_statement_settings_item/__init__.py
+++ /dev/null
diff --git a/erpnext/accounts/doctype/bank_statement_settings_item/bank_statement_settings_item.json b/erpnext/accounts/doctype/bank_statement_settings_item/bank_statement_settings_item.json
deleted file mode 100644
index 7c93f26..0000000
--- a/erpnext/accounts/doctype/bank_statement_settings_item/bank_statement_settings_item.json
+++ /dev/null
@@ -1,101 +0,0 @@
-{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
- "creation": "2018-01-08 00:16:42.762980",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "",
- "editable_grid": 1,
- "engine": "InnoDB",
- "fields": [
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "mapped_header",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Mapped Header",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "stmt_header",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Bank Header",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- }
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 1,
- "max_attachments": 0,
- "modified": "2018-01-08 00:19:14.841134",
- "modified_by": "Administrator",
- "module": "Accounts",
- "name": "Bank Statement Settings Item",
- "name_case": "",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1,
- "track_seen": 0
-}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/bank_statement_settings_item/bank_statement_settings_item.py b/erpnext/accounts/doctype/bank_statement_settings_item/bank_statement_settings_item.py
deleted file mode 100644
index 9438e9a..0000000
--- a/erpnext/accounts/doctype/bank_statement_settings_item/bank_statement_settings_item.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2018, sathishpy@gmail.com and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.model.document import Document
-
-class BankStatementSettingsItem(Document):
- pass
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_entry/__init__.py b/erpnext/accounts/doctype/bank_statement_transaction_entry/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_entry/__init__.py
+++ /dev/null
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.js b/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.js
deleted file mode 100644
index 736ed35..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.js
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright (c) 2017, sathishpy@gmail.com and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Bank Statement Transaction Entry', {
- setup: function(frm) {
- frm.events.account_filters(frm)
- frm.events.invoice_filter(frm)
- },
- refresh: function(frm) {
- frm.set_df_property("bank_account", "read_only", frm.doc.__islocal ? 0 : 1);
- frm.set_df_property("from_date", "read_only", frm.doc.__islocal ? 0 : 1);
- frm.set_df_property("to_date", "read_only", frm.doc.__islocal ? 0 : 1);
- },
- invoke_doc_function(frm, method) {
- frappe.call({
- doc: frm.doc,
- method: method,
- callback: function(r) {
- if(!r.exe) {
- frm.refresh_fields();
- }
- }
- });
- },
- account_filters: function(frm) {
- frm.fields_dict['bank_account'].get_query = function(doc, dt, dn) {
- return {
- filters:[
- ["Account", "account_type", "in", ["Bank"]]
- ]
- }
- };
- frm.fields_dict['receivable_account'].get_query = function(doc, dt, dn) {
- return {
- filters: {"account_type": "Receivable"}
- }
- };
- frm.fields_dict['payable_account'].get_query = function(doc, dt, dn) {
- return {
- filters: {"account_type": "Payable"}
- }
- };
- },
-
- invoice_filter: function(frm) {
- frm.set_query("invoice", "payment_invoice_items", function(doc, cdt, cdn) {
- let row = locals[cdt][cdn]
- if (row.party_type == "Customer") {
- return {
- filters:[[row.invoice_type, "customer", "in", [row.party]],
- [row.invoice_type, "status", "!=", "Cancelled" ],
- [row.invoice_type, "posting_date", "<", row.transaction_date ],
- [row.invoice_type, "outstanding_amount", ">", 0 ]]
- }
- } else if (row.party_type == "Supplier") {
- return {
- filters:[[row.invoice_type, "supplier", "in", [row.party]],
- [row.invoice_type, "status", "!=", "Cancelled" ],
- [row.invoice_type, "posting_date", "<", row.transaction_date ],
- [row.invoice_type, "outstanding_amount", ">", 0 ]]
- }
- }
- });
- },
-
- match_invoices: function(frm) {
- frm.events.invoke_doc_function(frm, "populate_matching_invoices");
- },
- create_payments: function(frm) {
- frm.events.invoke_doc_function(frm, "create_payment_entries");
- },
- submit_payments: function(frm) {
- frm.events.invoke_doc_function(frm, "submit_payment_entries");
- },
-});
-
-
-frappe.ui.form.on('Bank Statement Transaction Invoice Item', {
- party_type: function(frm, cdt, cdn) {
- let row = locals[cdt][cdn];
- if (row.party_type == "Customer") {
- row.invoice_type = "Sales Invoice";
- } else if (row.party_type == "Supplier") {
- row.invoice_type = "Purchase Invoice";
- } else if (row.party_type == "Account") {
- row.invoice_type = "Journal Entry";
- }
- refresh_field("invoice_type", row.name, "payment_invoice_items");
-
- },
- invoice_type: function(frm, cdt, cdn) {
- let row = locals[cdt][cdn];
- if (row.invoice_type == "Purchase Invoice") {
- row.party_type = "Supplier";
- } else if (row.invoice_type == "Sales Invoice") {
- row.party_type = "Customer";
- }
- refresh_field("party_type", row.name, "payment_invoice_items");
- }
-});
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.json b/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.json
deleted file mode 100644
index fb80169..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.json
+++ /dev/null
@@ -1,792 +0,0 @@
-{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 1,
- "beta": 0,
- "creation": "2017-11-07 13:48:13.123185",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "",
- "editable_grid": 1,
- "engine": "InnoDB",
- "fields": [
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "bank_account",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Bank Account",
- "length": 0,
- "no_copy": 0,
- "options": "Account",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "from_date",
- "fieldtype": "Date",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "From Date",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "to_date",
- "fieldtype": "Date",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "To Date",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "bank_settings",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Bank Statement Settings",
- "length": 0,
- "no_copy": 0,
- "options": "Bank Statement Settings",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break_3",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "bank",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Bank",
- "length": 0,
- "no_copy": 0,
- "options": "Bank",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "receivable_account",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Receivable Account",
- "length": 0,
- "no_copy": 0,
- "options": "Account",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "payable_account",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Payable Account",
- "length": 0,
- "no_copy": 0,
- "options": "Account",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "bank_statement",
- "fieldtype": "Attach",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Bank Statement",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "",
- "fieldname": "section_break_6",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Bank Transaction Entries",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "new_transaction_items",
- "fieldtype": "Table",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "New Transactions",
- "length": 0,
- "no_copy": 0,
- "options": "Bank Statement Transaction Payment Item",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "eval:doc.new_transaction_items && doc.new_transaction_items.length",
- "fieldname": "section_break_9",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "",
- "fieldname": "match_invoices",
- "fieldtype": "Button",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Match Transaction to Invoices",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break_14",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "create_payments",
- "fieldtype": "Button",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Create New Payment/Journal Entry",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break_16",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "submit_payments",
- "fieldtype": "Button",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Submit/Reconcile Payments",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "eval:doc.new_transaction_items && doc.new_transaction_items.length",
- "fieldname": "section_break_18",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Matching Invoices",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "payment_invoice_items",
- "fieldtype": "Table",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Payment Invoice Items",
- "length": 0,
- "no_copy": 0,
- "options": "Bank Statement Transaction Invoice Item",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "reconciled_transactions",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Reconciled Transactions",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "reconciled_transaction_items",
- "fieldtype": "Table",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Reconciled Transactions",
- "length": 0,
- "no_copy": 0,
- "options": "Bank Statement Transaction Payment Item",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "amended_from",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Amended From",
- "length": 0,
- "no_copy": 1,
- "options": "Bank Statement Transaction Entry",
- "permlevel": 0,
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- }
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 1,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2018-09-14 18:04:44.170455",
- "modified_by": "Administrator",
- "module": "Accounts",
- "name": "Bank Statement Transaction Entry",
- "name_case": "",
- "owner": "Administrator",
- "permissions": [
- {
- "amend": 1,
- "cancel": 1,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "System Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 1,
- "write": 1
- },
- {
- "amend": 1,
- "cancel": 1,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Accounts Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 1,
- "write": 1
- }
- ],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1,
- "track_seen": 0
-}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.py b/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.py
deleted file mode 100644
index 27dd8e4..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.py
+++ /dev/null
@@ -1,443 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, sathishpy@gmail.com and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe import _
-from frappe.model.document import Document
-from erpnext.accounts.utils import get_outstanding_invoices
-from frappe.utils import nowdate
-from datetime import datetime
-import csv, os, re, io
-import difflib
-import copy
-
-class BankStatementTransactionEntry(Document):
- def autoname(self):
- self.name = self.bank_account + "-" + self.from_date + "-" + self.to_date
- if self.bank:
- mapper_name = self.bank + "-Statement-Settings"
- if not frappe.db.exists("Bank Statement Settings", mapper_name):
- self.create_settings(self.bank)
- self.bank_settings = mapper_name
-
- def create_settings(self, bank):
- mapper = frappe.new_doc("Bank Statement Settings")
- mapper.bank = bank
- mapper.date_format = "%Y-%m-%d"
- mapper.bank_account = self.bank_account
- for header in ["Date", "Particulars", "Withdrawals", "Deposits", "Balance"]:
- header_item = mapper.append("header_items", {})
- header_item.mapped_header = header_item.stmt_header = header
- mapper.save()
-
- def on_update(self):
- if (not self.bank_statement):
- self.reconciled_transaction_items = self.new_transaction_items = []
- return
-
- if len(self.new_transaction_items + self.reconciled_transaction_items) == 0:
- self.populate_payment_entries()
- else:
- self.match_invoice_to_payment()
-
- def validate(self):
- if not self.new_transaction_items:
- self.populate_payment_entries()
-
- def get_statement_headers(self):
- if not self.bank_settings:
- frappe.throw(_("Bank Data mapper doesn't exist"))
- mapper_doc = frappe.get_doc("Bank Statement Settings", self.bank_settings)
- headers = {entry.mapped_header:entry.stmt_header for entry in mapper_doc.header_items}
- return headers
-
- def populate_payment_entries(self):
- if self.bank_statement is None: return
- file_url = self.bank_statement
- if (len(self.new_transaction_items + self.reconciled_transaction_items) > 0):
- frappe.throw(_("Transactions already retreived from the statement"))
-
- date_format = frappe.get_value("Bank Statement Settings", self.bank_settings, "date_format")
- if (date_format is None):
- date_format = '%Y-%m-%d'
- if self.bank_settings:
- mapped_items = frappe.get_doc("Bank Statement Settings", self.bank_settings).mapped_items
- statement_headers = self.get_statement_headers()
- transactions = get_transaction_entries(file_url, statement_headers)
- for entry in transactions:
- date = entry[statement_headers["Date"]].strip()
- #print("Processing entry DESC:{0}-W:{1}-D:{2}-DT:{3}".format(entry["Particulars"], entry["Withdrawals"], entry["Deposits"], entry["Date"]))
- if (not date): continue
- transaction_date = datetime.strptime(date, date_format).date()
- if (self.from_date and transaction_date < datetime.strptime(self.from_date, '%Y-%m-%d').date()): continue
- if (self.to_date and transaction_date > datetime.strptime(self.to_date, '%Y-%m-%d').date()): continue
- bank_entry = self.append('new_transaction_items', {})
- bank_entry.transaction_date = transaction_date
- bank_entry.description = entry[statement_headers["Particulars"]]
-
- mapped_item = next((entry for entry in mapped_items if entry.mapping_type == "Transaction" and frappe.safe_decode(entry.bank_data.lower()) in frappe.safe_decode(bank_entry.description.lower())), None)
- if (mapped_item is not None):
- bank_entry.party_type = mapped_item.mapped_data_type
- bank_entry.party = mapped_item.mapped_data
- else:
- bank_entry.party_type = "Supplier" if not entry[statement_headers["Deposits"]].strip() else "Customer"
- party_list = frappe.get_all(bank_entry.party_type, fields=["name"])
- parties = [party.name for party in party_list]
- matches = difflib.get_close_matches(frappe.safe_decode(bank_entry.description.lower()), parties, 1, 0.4)
- if len(matches) > 0: bank_entry.party = matches[0]
- bank_entry.amount = -float(entry[statement_headers["Withdrawals"]]) if not entry[statement_headers["Deposits"]].strip() else float(entry[statement_headers["Deposits"]])
- self.map_unknown_transactions()
- self.map_transactions_on_journal_entry()
-
- def map_transactions_on_journal_entry(self):
- for entry in self.new_transaction_items:
- vouchers = frappe.db.sql("""select name, posting_date from `tabJournal Entry`
- where posting_date='{0}' and total_credit={1} and cheque_no='{2}' and docstatus != 2
- """.format(entry.transaction_date, abs(entry.amount), frappe.safe_decode(entry.description)), as_dict=True)
- if (len(vouchers) == 1):
- entry.reference_name = vouchers[0].name
-
- def populate_matching_invoices(self):
- self.payment_invoice_items = []
- self.map_unknown_transactions()
- added_invoices = []
- for entry in self.new_transaction_items:
- if (not entry.party or entry.party_type == "Account"): continue
- account = self.receivable_account if entry.party_type == "Customer" else self.payable_account
- invoices = get_outstanding_invoices(entry.party_type, entry.party, account)
- transaction_date = datetime.strptime(entry.transaction_date, "%Y-%m-%d").date()
- outstanding_invoices = [invoice for invoice in invoices if invoice.posting_date <= transaction_date]
- amount = abs(entry.amount)
- matching_invoices = [invoice for invoice in outstanding_invoices if invoice.outstanding_amount == amount]
- sorted(outstanding_invoices, key=lambda k: k['posting_date'])
- for e in (matching_invoices + outstanding_invoices):
- added = next((inv for inv in added_invoices if inv == e.get('voucher_no')), None)
- if (added is not None): continue
- ent = self.append('payment_invoice_items', {})
- ent.transaction_date = entry.transaction_date
- ent.payment_description = frappe.safe_decode(entry.description)
- ent.party_type = entry.party_type
- ent.party = entry.party
- ent.invoice = e.get('voucher_no')
- added_invoices += [ent.invoice]
- ent.invoice_type = "Sales Invoice" if entry.party_type == "Customer" else "Purchase Invoice"
- ent.invoice_date = e.get('posting_date')
- ent.outstanding_amount = e.get('outstanding_amount')
- ent.allocated_amount = min(float(e.get('outstanding_amount')), amount)
- amount -= float(e.get('outstanding_amount'))
- if (amount <= 5): break
- self.match_invoice_to_payment()
- self.populate_matching_vouchers()
- self.map_transactions_on_journal_entry()
-
- def match_invoice_to_payment(self):
- added_payments = []
- for entry in self.new_transaction_items:
- if (not entry.party or entry.party_type == "Account"): continue
- entry.account = self.receivable_account if entry.party_type == "Customer" else self.payable_account
- amount = abs(entry.amount)
- payment, matching_invoices = None, []
- for inv_entry in self.payment_invoice_items:
- if (inv_entry.payment_description != frappe.safe_decode(entry.description) or inv_entry.transaction_date != entry.transaction_date): continue
- if (inv_entry.party != entry.party): continue
- matching_invoices += [inv_entry.invoice_type + "|" + inv_entry.invoice]
- payment = get_payments_matching_invoice(inv_entry.invoice, entry.amount, entry.transaction_date)
- doc = frappe.get_doc(inv_entry.invoice_type, inv_entry.invoice)
- inv_entry.invoice_date = doc.posting_date
- inv_entry.outstanding_amount = doc.outstanding_amount
- inv_entry.allocated_amount = min(float(doc.outstanding_amount), amount)
- amount -= inv_entry.allocated_amount
- if (amount < 0): break
-
- amount = abs(entry.amount)
- if (payment is None):
- order_doctype = "Sales Order" if entry.party_type=="Customer" else "Purchase Order"
- from erpnext.controllers.accounts_controller import get_advance_payment_entries
- payment_entries = get_advance_payment_entries(entry.party_type, entry.party, entry.account, order_doctype, against_all_orders=True)
- payment_entries += self.get_matching_payments(entry.party, amount, entry.transaction_date)
- payment = next((payment for payment in payment_entries if payment.amount == amount and payment not in added_payments), None)
- if (payment is None):
- print("Failed to find payments for {0}:{1}".format(entry.party, amount))
- continue
- added_payments += [payment]
- entry.reference_type = payment.reference_type
- entry.reference_name = payment.reference_name
- entry.mode_of_payment = "Wire Transfer"
- entry.outstanding_amount = min(amount, 0)
- if (entry.payment_reference is None):
- entry.payment_reference = frappe.safe_decode(entry.description)
- entry.invoices = ",".join(matching_invoices)
- #print("Matching payment is {0}:{1}".format(entry.reference_type, entry.reference_name))
-
- def get_matching_payments(self, party, amount, pay_date):
- query = """select 'Payment Entry' as reference_type, name as reference_name, paid_amount as amount
- from `tabPayment Entry` where party='{0}' and paid_amount={1} and posting_date='{2}' and docstatus != 2
- """.format(party, amount, pay_date)
- matching_payments = frappe.db.sql(query, as_dict=True)
- return matching_payments
-
- def map_unknown_transactions(self):
- for entry in self.new_transaction_items:
- if (entry.party): continue
- inv_type = "Sales Invoice" if (entry.amount > 0) else "Purchase Invoice"
- party_type = "customer" if (entry.amount > 0) else "supplier"
-
- query = """select posting_date, name, {0}, outstanding_amount
- from `tab{1}` where ROUND(outstanding_amount)={2} and posting_date < '{3}'
- """.format(party_type, inv_type, round(abs(entry.amount)), entry.transaction_date)
- invoices = frappe.db.sql(query, as_dict = True)
- if(len(invoices) > 0):
- entry.party = invoices[0].get(party_type)
-
- def populate_matching_vouchers(self):
- for entry in self.new_transaction_items:
- if (not entry.party or entry.reference_name): continue
- print("Finding matching voucher for {0}".format(frappe.safe_decode(entry.description)))
- amount = abs(entry.amount)
- invoices = []
- vouchers = get_matching_journal_entries(self.from_date, self.to_date, entry.party, self.bank_account, amount)
- if len(vouchers) == 0: continue
- for voucher in vouchers:
- added = next((entry.invoice for entry in self.payment_invoice_items if entry.invoice == voucher.voucher_no), None)
- if (added):
- print("Found voucher {0}".format(added))
- continue
- print("Adding voucher {0} {1} {2}".format(voucher.voucher_no, voucher.posting_date, voucher.debit))
- ent = self.append('payment_invoice_items', {})
- ent.invoice_date = voucher.posting_date
- ent.invoice_type = "Journal Entry"
- ent.invoice = voucher.voucher_no
- ent.payment_description = frappe.safe_decode(entry.description)
- ent.allocated_amount = max(voucher.debit, voucher.credit)
-
- invoices += [ent.invoice_type + "|" + ent.invoice]
- entry.reference_type = "Journal Entry"
- entry.mode_of_payment = "Wire Transfer"
- entry.reference_name = ent.invoice
- #entry.account = entry.party
- entry.invoices = ",".join(invoices)
- break
-
-
- def create_payment_entries(self):
- for payment_entry in self.new_transaction_items:
- if (not payment_entry.party): continue
- if (payment_entry.reference_name): continue
- print("Creating payment entry for {0}".format(frappe.safe_decode(payment_entry.description)))
- if (payment_entry.party_type == "Account"):
- payment = self.create_journal_entry(payment_entry)
- invoices = [payment.doctype + "|" + payment.name]
- payment_entry.invoices = ",".join(invoices)
- else:
- payment = self.create_payment_entry(payment_entry)
- invoices = [entry.reference_doctype + "|" + entry.reference_name for entry in payment.references if entry is not None]
- payment_entry.invoices = ",".join(invoices)
- payment_entry.mode_of_payment = payment.mode_of_payment
- payment_entry.account = self.receivable_account if payment_entry.party_type == "Customer" else self.payable_account
- payment_entry.reference_name = payment.name
- payment_entry.reference_type = payment.doctype
- frappe.msgprint(_("Successfully created payment entries"))
-
- def create_payment_entry(self, pe):
- payment = frappe.new_doc("Payment Entry")
- payment.posting_date = pe.transaction_date
- payment.payment_type = "Receive" if pe.party_type == "Customer" else "Pay"
- payment.mode_of_payment = "Wire Transfer"
- payment.party_type = pe.party_type
- payment.party = pe.party
- payment.paid_to = self.bank_account if pe.party_type == "Customer" else self.payable_account
- payment.paid_from = self.receivable_account if pe.party_type == "Customer" else self.bank_account
- payment.paid_amount = payment.received_amount = abs(pe.amount)
- payment.reference_no = pe.description
- payment.reference_date = pe.transaction_date
- payment.save()
- for inv_entry in self.payment_invoice_items:
- if (pe.description != inv_entry.payment_description or pe.transaction_date != inv_entry.transaction_date): continue
- if (pe.party != inv_entry.party): continue
- reference = payment.append("references", {})
- reference.reference_doctype = inv_entry.invoice_type
- reference.reference_name = inv_entry.invoice
- reference.allocated_amount = inv_entry.allocated_amount
- print ("Adding invoice {0} {1}".format(reference.reference_name, reference.allocated_amount))
- payment.setup_party_account_field()
- payment.set_missing_values()
- #payment.set_exchange_rate()
- #payment.set_amounts()
- #print("Created payment entry {0}".format(payment.as_dict()))
- payment.save()
- return payment
-
- def create_journal_entry(self, pe):
- je = frappe.new_doc("Journal Entry")
- je.is_opening = "No"
- je.voucher_type = "Bank Entry"
- je.cheque_no = pe.description
- je.cheque_date = pe.transaction_date
- je.remark = pe.description
- je.posting_date = pe.transaction_date
- if (pe.amount < 0):
- je.append("accounts", {"account": pe.party, "debit_in_account_currency": abs(pe.amount)})
- je.append("accounts", {"account": self.bank_account, "credit_in_account_currency": abs(pe.amount)})
- else:
- je.append("accounts", {"account": pe.party, "credit_in_account_currency": pe.amount})
- je.append("accounts", {"account": self.bank_account, "debit_in_account_currency": pe.amount})
- je.save()
- return je
-
- def update_payment_entry(self, payment):
- lst = []
- invoices = payment.invoices.strip().split(',')
- if (len(invoices) == 0): return
- amount = float(abs(payment.amount))
- for invoice_entry in invoices:
- if (not invoice_entry.strip()): continue
- invs = invoice_entry.split('|')
- invoice_type, invoice = invs[0], invs[1]
- outstanding_amount = frappe.get_value(invoice_type, invoice, 'outstanding_amount')
-
- lst.append(frappe._dict({
- 'voucher_type': payment.reference_type,
- 'voucher_no' : payment.reference_name,
- 'against_voucher_type' : invoice_type,
- 'against_voucher' : invoice,
- 'account' : payment.account,
- 'party_type': payment.party_type,
- 'party': frappe.get_value("Payment Entry", payment.reference_name, "party"),
- 'unadjusted_amount' : float(amount),
- 'allocated_amount' : min(outstanding_amount, amount)
- }))
- amount -= outstanding_amount
- if lst:
- from erpnext.accounts.utils import reconcile_against_document
- try:
- reconcile_against_document(lst)
- except:
- frappe.throw(_("Exception occurred while reconciling {0}").format(payment.reference_name))
-
- def submit_payment_entries(self):
- for payment in self.new_transaction_items:
- if payment.reference_name is None: continue
- doc = frappe.get_doc(payment.reference_type, payment.reference_name)
- if doc.docstatus == 1:
- if (payment.reference_type == "Journal Entry"): continue
- if doc.unallocated_amount == 0: continue
- print("Reconciling payment {0}".format(payment.reference_name))
- self.update_payment_entry(payment)
- else:
- print("Submitting payment {0}".format(payment.reference_name))
- if (payment.reference_type == "Payment Entry"):
- if (payment.payment_reference):
- doc.reference_no = payment.payment_reference
- doc.mode_of_payment = payment.mode_of_payment
- doc.save()
- doc.submit()
- self.move_reconciled_entries()
- self.populate_matching_invoices()
-
- def move_reconciled_entries(self):
- idx = 0
- while idx < len(self.new_transaction_items):
- entry = self.new_transaction_items[idx]
- try:
- print("Checking transaction {0}: {2} in {1} entries".format(idx, len(self.new_transaction_items), frappe.safe_decode(entry.description)))
- except UnicodeEncodeError:
- pass
- idx += 1
- if entry.reference_name is None: continue
- doc = frappe.get_doc(entry.reference_type, entry.reference_name)
- if doc.docstatus == 1 and (entry.reference_type == "Journal Entry" or doc.unallocated_amount == 0):
- self.remove(entry)
- rc_entry = self.append('reconciled_transaction_items', {})
- dentry = entry.as_dict()
- dentry.pop('idx', None)
- rc_entry.update(dentry)
- idx -= 1
-
-
-def get_matching_journal_entries(from_date, to_date, account, against, amount):
- query = """select voucher_no, posting_date, account, against, debit_in_account_currency as debit, credit_in_account_currency as credit
- from `tabGL Entry`
- where posting_date between '{0}' and '{1}' and account = '{2}' and against = '{3}' and debit = '{4}'
- """.format(from_date, to_date, account, against, amount)
- jv_entries = frappe.db.sql(query, as_dict=True)
- #print("voucher query:{0}\n Returned {1} entries".format(query, len(jv_entries)))
- return jv_entries
-
-def get_payments_matching_invoice(invoice, amount, pay_date):
- query = """select pe.name as reference_name, per.reference_doctype as reference_type, per.outstanding_amount, per.allocated_amount
- from `tabPayment Entry Reference` as per JOIN `tabPayment Entry` as pe on pe.name = per.parent
- where per.reference_name='{0}' and (posting_date='{1}' or reference_date='{1}') and pe.docstatus != 2
- """.format(invoice, pay_date)
- payments = frappe.db.sql(query, as_dict=True)
- if (len(payments) == 0): return
- payment = next((payment for payment in payments if payment.allocated_amount == amount), payments[0])
- #Hack: Update the reference type which is set to invoice type
- payment.reference_type = "Payment Entry"
- return payment
-
-def is_headers_present(headers, row):
- for header in headers:
- if header not in row:
- return False
- return True
-
-def get_header_index(headers, row):
- header_index = {}
- for header in headers:
- if header in row:
- header_index[header] = row.index(header)
- return header_index
-
-def get_transaction_info(headers, header_index, row):
- transaction = {}
- for header in headers:
- transaction[header] = row[header_index[header]]
- if (transaction[header] == None):
- transaction[header] = ""
- return transaction
-
-def get_transaction_entries(file_url, headers):
- header_index = {}
- rows, transactions = [], []
-
- if (file_url.lower().endswith("xlsx")):
- from frappe.utils.xlsxutils import read_xlsx_file_from_attached_file
- rows = read_xlsx_file_from_attached_file(file_url=file_url)
- elif (file_url.lower().endswith("csv")):
- from frappe.utils.csvutils import read_csv_content
- _file = frappe.get_doc("File", {"file_url": file_url})
- filepath = _file.get_full_path()
- with open(filepath,'rb') as csvfile:
- rows = read_csv_content(csvfile.read())
- elif (file_url.lower().endswith("xls")):
- filename = file_url.split("/")[-1]
- rows = get_rows_from_xls_file(filename)
- else:
- frappe.throw(_("Only .csv and .xlsx files are supported currently"))
-
- stmt_headers = headers.values()
- for row in rows:
- if len(row) == 0 or row[0] == None or not row[0]: continue
- #print("Processing row {0}".format(row))
- if header_index:
- transaction = get_transaction_info(stmt_headers, header_index, row)
- transactions.append(transaction)
- elif is_headers_present(stmt_headers, row):
- header_index = get_header_index(stmt_headers, row)
- return transactions
-
-def get_rows_from_xls_file(filename):
- _file = frappe.get_doc("File", {"file_name": filename})
- filepath = _file.get_full_path()
- import xlrd
- book = xlrd.open_workbook(filepath)
- sheets = book.sheets()
- rows = []
- for row in range(1, sheets[0].nrows):
- row_values = []
- for col in range(1, sheets[0].ncols):
- row_values.append(sheets[0].cell_value(row, col))
- rows.append(row_values)
- return rows
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_entry/test_bank_statement_transaction_entry.js b/erpnext/accounts/doctype/bank_statement_transaction_entry/test_bank_statement_transaction_entry.js
deleted file mode 100644
index 46d570f..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_entry/test_bank_statement_transaction_entry.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Bank Statement Transaction Entry", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Bank Statement Transaction Entry
- () => frappe.tests.make('Bank Statement Transaction Entry', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_entry/test_bank_statement_transaction_entry.py b/erpnext/accounts/doctype/bank_statement_transaction_entry/test_bank_statement_transaction_entry.py
deleted file mode 100644
index 4589483..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_entry/test_bank_statement_transaction_entry.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, sathishpy@gmail.com and Contributors
-# See license.txt
-from __future__ import unicode_literals
-
-import frappe
-import unittest
-
-class TestBankStatementTransactionEntry(unittest.TestCase):
- pass
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_invoice_item/__init__.py b/erpnext/accounts/doctype/bank_statement_transaction_invoice_item/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_invoice_item/__init__.py
+++ /dev/null
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_invoice_item/bank_statement_transaction_invoice_item.json b/erpnext/accounts/doctype/bank_statement_transaction_invoice_item/bank_statement_transaction_invoice_item.json
deleted file mode 100644
index d96c94d..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_invoice_item/bank_statement_transaction_invoice_item.json
+++ /dev/null
@@ -1,365 +0,0 @@
-{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
- "creation": "2017-11-07 13:58:53.827058",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "",
- "editable_grid": 1,
- "engine": "InnoDB",
- "fields": [
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "transaction_date",
- "fieldtype": "Date",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Transaction Date",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 4,
- "fieldname": "payment_description",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Payment Description",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "party_type",
- "fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Party Type",
- "length": 0,
- "no_copy": 0,
- "options": "Customer\nSupplier\nAccount",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "party",
- "fieldtype": "Dynamic Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Party",
- "length": 0,
- "no_copy": 0,
- "options": "party_type",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break_4",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 2,
- "fieldname": "invoice_date",
- "fieldtype": "Date",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Invoice Date",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "invoice_type",
- "fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Invoice Type",
- "length": 0,
- "no_copy": 0,
- "options": "Sales Invoice\nPurchase Invoice\nJournal Entry",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 2,
- "fieldname": "invoice",
- "fieldtype": "Dynamic Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "invoice",
- "length": 0,
- "no_copy": 0,
- "options": "invoice_type",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 1,
- "fieldname": "outstanding_amount",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Outstanding Amount",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 1,
- "fieldname": "allocated_amount",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Allocated Amount",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- }
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 1,
- "max_attachments": 0,
- "modified": "2018-09-14 19:03:30.949831",
- "modified_by": "Administrator",
- "module": "Accounts",
- "name": "Bank Statement Transaction Invoice Item",
- "name_case": "",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1,
- "track_seen": 0,
- "track_views": 0
-}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_invoice_item/bank_statement_transaction_invoice_item.py b/erpnext/accounts/doctype/bank_statement_transaction_invoice_item/bank_statement_transaction_invoice_item.py
deleted file mode 100644
index cb1b158..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_invoice_item/bank_statement_transaction_invoice_item.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, sathishpy@gmail.com and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.model.document import Document
-
-class BankStatementTransactionInvoiceItem(Document):
- pass
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_payment_item/__init__.py b/erpnext/accounts/doctype/bank_statement_transaction_payment_item/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_payment_item/__init__.py
+++ /dev/null
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_payment_item/bank_statement_transaction_payment_item.json b/erpnext/accounts/doctype/bank_statement_transaction_payment_item/bank_statement_transaction_payment_item.json
deleted file mode 100644
index 177dccd..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_payment_item/bank_statement_transaction_payment_item.json
+++ /dev/null
@@ -1,494 +0,0 @@
-{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
- "creation": "2017-11-07 14:03:05.651413",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "",
- "editable_grid": 1,
- "engine": "InnoDB",
- "fields": [
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 1,
- "fieldname": "transaction_date",
- "fieldtype": "Date",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Transaction Date",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 4,
- "fieldname": "description",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Description",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 1,
- "fieldname": "amount",
- "fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Amount",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break_3",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 1,
- "fieldname": "party_type",
- "fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Party Type",
- "length": 0,
- "no_copy": 0,
- "options": "Customer\nSupplier\nAccount",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 2,
- "fieldname": "party",
- "fieldtype": "Dynamic Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Party",
- "length": 0,
- "no_copy": 0,
- "options": "party_type",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "section_break_6",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "reference_type",
- "fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Reference Type",
- "length": 0,
- "no_copy": 0,
- "options": "Payment Entry\nJournal Entry",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "account",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Account",
- "length": 0,
- "no_copy": 0,
- "options": "Account",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "mode_of_payment",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Mode of Payment",
- "length": 0,
- "no_copy": 0,
- "options": "Mode of Payment",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "outstanding_amount",
- "fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "outstanding_amount",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break_10",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 2,
- "fieldname": "reference_name",
- "fieldtype": "Dynamic Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Reference Name",
- "length": 0,
- "no_copy": 0,
- "options": "reference_type",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "payment_reference",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Payment Reference",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "invoices",
- "fieldtype": "Text",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Invoices",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- }
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 1,
- "max_attachments": 0,
- "modified": "2017-11-15 19:18:52.876221",
- "modified_by": "Administrator",
- "module": "Accounts",
- "name": "Bank Statement Transaction Payment Item",
- "name_case": "",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1,
- "track_seen": 0
-}
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_payment_item/bank_statement_transaction_payment_item.py b/erpnext/accounts/doctype/bank_statement_transaction_payment_item/bank_statement_transaction_payment_item.py
deleted file mode 100644
index 9840c0d..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_payment_item/bank_statement_transaction_payment_item.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, sathishpy@gmail.com and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.model.document import Document
-
-class BankStatementTransactionPaymentItem(Document):
- pass
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_settings/__init__.py b/erpnext/accounts/doctype/bank_statement_transaction_settings/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_settings/__init__.py
+++ /dev/null
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_settings/bank_statement_transaction_settings.js b/erpnext/accounts/doctype/bank_statement_transaction_settings/bank_statement_transaction_settings.js
deleted file mode 100644
index 46aa4f2..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_settings/bank_statement_transaction_settings.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) 2017, sathishpy@gmail.com and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Bank Statement Settings', {
- refresh: function(frm) {
-
- }
-});
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_settings/bank_statement_transaction_settings.json b/erpnext/accounts/doctype/bank_statement_transaction_settings/bank_statement_transaction_settings.json
deleted file mode 100644
index 474bb90..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_settings/bank_statement_transaction_settings.json
+++ /dev/null
@@ -1,266 +0,0 @@
-{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 1,
- "beta": 0,
- "creation": "2017-11-13 13:38:10.863592",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "",
- "editable_grid": 1,
- "engine": "InnoDB",
- "fields": [
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "bank_account",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Bank Account",
- "length": 0,
- "no_copy": 0,
- "options": "Account",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "'%d/%m/%Y'",
- "fieldname": "date_format",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Date Format",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "statement_header_mapping",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Statement Header Mapping",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "header_items",
- "fieldtype": "Table",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Statement Headers",
- "length": 0,
- "no_copy": 0,
- "options": "Bank Statement Settings Item",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "transaction_data_mapping",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Transaction Data Mapping",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "mapped_items",
- "fieldtype": "Table",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Mapped Items",
- "length": 0,
- "no_copy": 0,
- "options": "Bank Statement Transaction Settings Item",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- }
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2018-01-12 10:34:32.840487",
- "modified_by": "Administrator",
- "module": "Accounts",
- "name": "Bank Statement Settings",
- "name_case": "",
- "owner": "Administrator",
- "permissions": [
- {
- "amend": 0,
- "apply_user_permissions": 0,
- "cancel": 0,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "System Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
- "write": 1
- },
- {
- "amend": 0,
- "apply_user_permissions": 0,
- "cancel": 0,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Accounts Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
- "write": 1
- }
- ],
- "quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1,
- "track_seen": 0
-}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_settings/bank_statement_transaction_settings.py b/erpnext/accounts/doctype/bank_statement_transaction_settings/bank_statement_transaction_settings.py
deleted file mode 100644
index de9a85f..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_settings/bank_statement_transaction_settings.py
+++ /dev/null
@@ -1,11 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, sathishpy@gmail.com and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.model.document import Document
-
-class BankStatementSettings(Document):
- def autoname(self):
- self.name = self.bank_account + "-Mappings"
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_settings/test_bank_statement_transaction_settings.js b/erpnext/accounts/doctype/bank_statement_transaction_settings/test_bank_statement_transaction_settings.js
deleted file mode 100644
index f2381c0..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_settings/test_bank_statement_transaction_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Bank Statement Settings", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Bank Statement Settings
- () => frappe.tests.make('Bank Statement Settings', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_settings/test_bank_statement_transaction_settings.py b/erpnext/accounts/doctype/bank_statement_transaction_settings/test_bank_statement_transaction_settings.py
deleted file mode 100644
index aa7fe83..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_settings/test_bank_statement_transaction_settings.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, sathishpy@gmail.com and Contributors
-# See license.txt
-from __future__ import unicode_literals
-
-import frappe
-import unittest
-
-class TestBankStatementSettings(unittest.TestCase):
- pass
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_settings_item/__init__.py b/erpnext/accounts/doctype/bank_statement_transaction_settings_item/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_settings_item/__init__.py
+++ /dev/null
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_settings_item/bank_statement_transaction_settings_item.json b/erpnext/accounts/doctype/bank_statement_transaction_settings_item/bank_statement_transaction_settings_item.json
deleted file mode 100644
index 47c3209..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_settings_item/bank_statement_transaction_settings_item.json
+++ /dev/null
@@ -1,166 +0,0 @@
-{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
- "creation": "2017-11-13 13:42:00.335432",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "",
- "editable_grid": 1,
- "engine": "InnoDB",
- "fields": [
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "Transaction",
- "fieldname": "mapping_type",
- "fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Mapping Type",
- "length": 0,
- "no_copy": 0,
- "options": "Transaction",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "bank_data",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Bank Data",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "Account",
- "fieldname": "mapped_data_type",
- "fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Mapped Data Type",
- "length": 0,
- "no_copy": 0,
- "options": "Account\nCustomer\nSupplier\nAccount",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "mapped_data",
- "fieldtype": "Dynamic Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Mapped Data",
- "length": 0,
- "no_copy": 0,
- "options": "mapped_data_type",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- }
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 1,
- "max_attachments": 0,
- "modified": "2018-01-08 00:13:49.973501",
- "modified_by": "Administrator",
- "module": "Accounts",
- "name": "Bank Statement Transaction Settings Item",
- "name_case": "",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1,
- "track_seen": 0
-}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_settings_item/bank_statement_transaction_settings_item.py b/erpnext/accounts/doctype/bank_statement_transaction_settings_item/bank_statement_transaction_settings_item.py
deleted file mode 100644
index bf0a590..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_settings_item/bank_statement_transaction_settings_item.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, sathishpy@gmail.com and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.model.document import Document
-
-class BankStatementTransactionSettingsItem(Document):
- pass
diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.js b/erpnext/accounts/doctype/bank_transaction/bank_transaction.js
index 8b1bab1..3758b52 100644
--- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.js
+++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.js
@@ -1,32 +1,70 @@
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
-frappe.ui.form.on('Bank Transaction', {
+frappe.ui.form.on("Bank Transaction", {
onload(frm) {
- frm.set_query('payment_document', 'payment_entries', function() {
+ frm.set_query("payment_document", "payment_entries", function () {
return {
- "filters": {
- "name": ["in", ["Payment Entry", "Journal Entry", "Sales Invoice", "Purchase Invoice", "Expense Claim"]]
- }
+ filters: {
+ name: [
+ "in",
+ [
+ "Payment Entry",
+ "Journal Entry",
+ "Sales Invoice",
+ "Purchase Invoice",
+ "Expense Claim",
+ ],
+ ],
+ },
};
});
- }
+ },
+ bank_account: function (frm) {
+ set_bank_statement_filter(frm);
+ },
+
+ setup: function (frm) {
+ frm.set_query("party_type", function () {
+ return {
+ filters: {
+ name: ["in", Object.keys(frappe.boot.party_account_types)],
+ },
+ };
+ });
+ },
});
-frappe.ui.form.on('Bank Transaction Payments', {
- payment_entries_remove: function(frm, cdt, cdn) {
+frappe.ui.form.on("Bank Transaction Payments", {
+ payment_entries_remove: function (frm, cdt, cdn) {
update_clearance_date(frm, cdt, cdn);
- }
+ },
});
const update_clearance_date = (frm, cdt, cdn) => {
if (frm.doc.docstatus === 1) {
- frappe.xcall('erpnext.accounts.doctype.bank_transaction.bank_transaction.unclear_reference_payment',
- {doctype: cdt, docname: cdn})
- .then(e => {
+ frappe
+ .xcall(
+ "erpnext.accounts.doctype.bank_transaction.bank_transaction.unclear_reference_payment",
+ { doctype: cdt, docname: cdn }
+ )
+ .then((e) => {
if (e == "success") {
- frappe.show_alert({message:__("Document {0} successfully uncleared", [e]), indicator:'green'});
+ frappe.show_alert({
+ message: __("Document {0} successfully uncleared", [e]),
+ indicator: "green",
+ });
}
});
}
-};
\ No newline at end of file
+};
+
+function set_bank_statement_filter(frm) {
+ frm.set_query("bank_statement", function () {
+ return {
+ filters: {
+ bank_account: frm.doc.bank_account,
+ },
+ };
+ });
+}
diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.json b/erpnext/accounts/doctype/bank_transaction/bank_transaction.json
index 39937bb..69ee497 100644
--- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.json
+++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.json
@@ -1,833 +1,245 @@
{
- "allow_copy": 0,
- "allow_events_in_timeline": 0,
- "allow_guest_to_view": 0,
+ "actions": [],
"allow_import": 1,
- "allow_rename": 0,
"autoname": "naming_series:",
- "beta": 0,
"creation": "2018-10-22 18:19:02.784533",
- "custom": 0,
- "docstatus": 0,
"doctype": "DocType",
- "document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
+ "field_order": [
+ "naming_series",
+ "date",
+ "column_break_2",
+ "status",
+ "bank_account",
+ "company",
+ "section_break_4",
+ "deposit",
+ "withdrawal",
+ "column_break_7",
+ "currency",
+ "section_break_10",
+ "description",
+ "section_break_14",
+ "reference_number",
+ "transaction_id",
+ "payment_entries",
+ "section_break_18",
+ "allocated_amount",
+ "amended_from",
+ "column_break_17",
+ "unallocated_amount",
+ "party_section",
+ "party_type",
+ "party"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"default": "ACC-BTN-.YYYY.-",
- "fetch_if_empty": 0,
"fieldname": "naming_series",
"fieldtype": "Select",
"hidden": 1,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Series",
- "length": 0,
"no_copy": 1,
"options": "ACC-BTN-.YYYY.-",
- "permlevel": 0,
- "precision": "",
"print_hide": 1,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
"reqd": 1,
- "search_index": 0,
- "set_only_once": 1,
- "translatable": 0,
- "unique": 0
+ "set_only_once": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
"fieldname": "date",
"fieldtype": "Date",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Date",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "label": "Date"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
"fieldname": "column_break_2",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "fieldtype": "Column Break"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"default": "Pending",
- "fetch_if_empty": 0,
"fieldname": "status",
"fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
"in_standard_filter": 1,
"label": "Status",
- "length": 0,
- "no_copy": 0,
- "options": "\nPending\nSettled\nUnreconciled\nReconciled",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "options": "\nPending\nSettled\nUnreconciled\nReconciled"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
"fieldname": "bank_account",
"fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
"in_standard_filter": 1,
"label": "Bank Account",
- "length": 0,
- "no_copy": 0,
- "options": "Bank Account",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "options": "Bank Account"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "",
"fetch_from": "bank_account.company",
- "fetch_if_empty": 0,
"fieldname": "company",
"fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
"in_standard_filter": 1,
"label": "Company",
- "length": 0,
- "no_copy": 0,
"options": "Company",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "read_only": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
"fieldname": "section_break_4",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "fieldtype": "Section Break"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "debit",
- "fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Debit",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "credit",
- "fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Credit",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
"fieldname": "column_break_7",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "fieldtype": "Column Break"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
"fieldname": "currency",
"fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Currency",
- "length": 0,
- "no_copy": 0,
- "options": "Currency",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "options": "Currency"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
"fieldname": "section_break_10",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "fieldtype": "Section Break"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
"fieldname": "description",
"fieldtype": "Small Text",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
"in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Description",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "label": "Description"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
"fieldname": "section_break_14",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "fieldtype": "Section Break"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
+ "allow_on_submit": 1,
"fieldname": "reference_number",
"fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Reference Number",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "label": "Reference Number"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
"fieldname": "transaction_id",
"fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Transaction ID",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
"read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
"unique": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
"allow_on_submit": 1,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
"fieldname": "payment_entries",
"fieldtype": "Table",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Payment Entries",
- "length": 0,
- "no_copy": 0,
- "options": "Bank Transaction Payments",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "options": "Bank Transaction Payments"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
"fieldname": "section_break_18",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "fieldtype": "Section Break"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
"fieldname": "allocated_amount",
"fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Allocated Amount",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "label": "Allocated Amount"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
"fieldname": "amended_from",
"fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Amended From",
- "length": 0,
"no_copy": 1,
"options": "Bank Transaction",
- "permlevel": 0,
"print_hide": 1,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "read_only": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
"fieldname": "column_break_17",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "fieldtype": "Column Break"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "",
- "fetch_if_empty": 0,
"fieldname": "unallocated_amount",
"fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Unallocated Amount",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "label": "Unallocated Amount"
+ },
+ {
+ "fieldname": "party_section",
+ "fieldtype": "Section Break",
+ "label": "Payment From / To"
+ },
+ {
+ "allow_on_submit": 1,
+ "fieldname": "party_type",
+ "fieldtype": "Link",
+ "label": "Party Type",
+ "options": "DocType"
+ },
+ {
+ "allow_on_submit": 1,
+ "fieldname": "party",
+ "fieldtype": "Dynamic Link",
+ "label": "Party",
+ "options": "party_type"
+ },
+ {
+ "fieldname": "deposit",
+ "oldfieldname": "debit",
+ "fieldtype": "Currency",
+ "in_list_view": 1,
+ "label": "Deposit"
+ },
+ {
+ "fieldname": "withdrawal",
+ "oldfieldname": "credit",
+ "fieldtype": "Currency",
+ "in_list_view": 1,
+ "label": "Withdrawal"
}
],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
"is_submittable": 1,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2019-05-11 05:27:55.244721",
+ "links": [],
+ "modified": "2020-12-30 19:40:54.221070",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Bank Transaction",
- "name_case": "",
"owner": "Administrator",
"permissions": [
{
- "amend": 0,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
- "set_user_permissions": 0,
"share": 1,
"submit": 1,
"write": 1
},
{
- "amend": 0,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts Manager",
- "set_user_permissions": 0,
"share": 1,
"submit": 1,
"write": 1
},
{
- "amend": 0,
- "cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts User",
- "set_user_permissions": 0,
"share": 1,
"submit": 1,
"write": 1
}
],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
"sort_field": "date",
"sort_order": "DESC",
"title_field": "bank_account",
- "track_changes": 0,
- "track_seen": 0,
- "track_views": 0
+ "track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
index 0e45db3..5246baa 100644
--- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
+++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
@@ -11,7 +11,7 @@
class BankTransaction(StatusUpdater):
def after_insert(self):
- self.unallocated_amount = abs(flt(self.credit) - flt(self.debit))
+ self.unallocated_amount = abs(flt(self.withdrawal) - flt(self.deposit))
def on_submit(self):
self.clear_linked_payment_entries()
@@ -30,13 +30,13 @@
if allocated_amount:
frappe.db.set_value(self.doctype, self.name, "allocated_amount", flt(allocated_amount))
- frappe.db.set_value(self.doctype, self.name, "unallocated_amount", abs(flt(self.credit) - flt(self.debit)) - flt(allocated_amount))
+ frappe.db.set_value(self.doctype, self.name, "unallocated_amount", abs(flt(self.withdrawal) - flt(self.deposit)) - flt(allocated_amount))
else:
frappe.db.set_value(self.doctype, self.name, "allocated_amount", 0)
- frappe.db.set_value(self.doctype, self.name, "unallocated_amount", abs(flt(self.credit) - flt(self.debit)))
+ frappe.db.set_value(self.doctype, self.name, "unallocated_amount", abs(flt(self.withdrawal) - flt(self.deposit)))
- amount = self.debit or self.credit
+ amount = self.deposit or self.withdrawal
if amount == self.allocated_amount:
frappe.db.set_value(self.doctype, self.name, "status", "Reconciled")
@@ -44,18 +44,11 @@
def clear_linked_payment_entries(self):
for payment_entry in self.payment_entries:
- allocated_amount = get_total_allocated_amount(payment_entry)
- paid_amount = get_paid_amount(payment_entry, self.currency)
+ if payment_entry.payment_document in ["Payment Entry", "Journal Entry", "Purchase Invoice", "Expense Claim"]:
+ self.clear_simple_entry(payment_entry)
- if paid_amount and allocated_amount:
- if flt(allocated_amount[0]["allocated_amount"]) > flt(paid_amount):
- frappe.throw(_("The total allocated amount ({0}) is greated than the paid amount ({1}).").format(flt(allocated_amount[0]["allocated_amount"]), flt(paid_amount)))
- else:
- if payment_entry.payment_document in ["Payment Entry", "Journal Entry", "Purchase Invoice", "Expense Claim"]:
- self.clear_simple_entry(payment_entry)
-
- elif payment_entry.payment_document == "Sales Invoice":
- self.clear_sales_invoice(payment_entry)
+ elif payment_entry.payment_document == "Sales Invoice":
+ self.clear_sales_invoice(payment_entry)
def clear_simple_entry(self, payment_entry):
frappe.db.set_value(payment_entry.payment_document, payment_entry.payment_entry, "clearance_date", self.date)
@@ -112,3 +105,4 @@
frappe.db.set_value(doc.payment_document, doc.payment_entry, "clearance_date", None)
return doc.payment_entry
+
diff --git a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py
index e9fc5f0..3b14e4e 100644
--- a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py
+++ b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py
@@ -5,10 +5,11 @@
import frappe
import unittest
+import json
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
-from erpnext.accounts.page.bank_reconciliation.bank_reconciliation import reconcile, get_linked_payments
+from erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool import reconcile_vouchers, get_linked_payments
from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile
test_dependencies = ["Item", "Cost Center"]
@@ -17,7 +18,7 @@
def setUp(self):
make_pos_profile()
add_transactions()
- add_payments()
+ add_vouchers()
def tearDown(self):
for bt in frappe.get_all("Bank Transaction"):
@@ -38,14 +39,18 @@
# This test checks if ERPNext is able to provide a linked payment for a bank transaction based on the amount of the bank transaction.
def test_linked_payments(self):
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="Re 95282925234 FE/000002917 AT171513000281183046 Conrad Electronic"))
- linked_payments = get_linked_payments(bank_transaction.name)
- self.assertTrue(linked_payments[0].party == "Conrad Electronic")
+ linked_payments = get_linked_payments(bank_transaction.name, ['payment_entry', 'exact_match'])
+ 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
def test_reconcile(self):
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G"))
payment = frappe.get_doc("Payment Entry", dict(party="Mr G", paid_amount=1200))
- reconcile(bank_transaction.name, "Payment Entry", payment.name)
+ vouchers = json.dumps([{
+ "payment_doctype":"Payment Entry",
+ "payment_name":payment.name,
+ "amount":bank_transaction.unallocated_amount}])
+ reconcile_vouchers(bank_transaction.name, vouchers)
unallocated_amount = frappe.db.get_value("Bank Transaction", bank_transaction.name, "unallocated_amount")
self.assertTrue(unallocated_amount == 0)
@@ -53,45 +58,40 @@
clearance_date = frappe.db.get_value("Payment Entry", payment.name, "clearance_date")
self.assertTrue(clearance_date is not None)
- # Check if ERPNext can correctly fetch a linked payment based on the party
- def test_linked_payments_based_on_party(self):
- bank_transaction = frappe.get_doc("Bank Transaction", dict(description="1512567 BG/000003025 OPSKATTUZWXXX AT776000000098709849 Herr G"))
- linked_payments = get_linked_payments(bank_transaction.name)
- self.assertTrue(len(linked_payments)==1)
-
# Check if ERPNext can correctly filter a linked payments based on the debit/credit amount
def test_debit_credit_output(self):
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07"))
- linked_payments = get_linked_payments(bank_transaction.name)
- self.assertTrue(linked_payments[0].payment_type == "Pay")
+ linked_payments = get_linked_payments(bank_transaction.name, ['payment_entry', 'exact_match'])
+ print(linked_payments)
+ self.assertTrue(linked_payments[0][3])
# Check error if already reconciled
def test_already_reconciled(self):
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G"))
payment = frappe.get_doc("Payment Entry", dict(party="Mr G", paid_amount=1200))
- reconcile(bank_transaction.name, "Payment Entry", payment.name)
+ vouchers = json.dumps([{
+ "payment_doctype":"Payment Entry",
+ "payment_name":payment.name,
+ "amount":bank_transaction.unallocated_amount}])
+ reconcile_vouchers(bank_transaction.name, vouchers)
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G"))
payment = frappe.get_doc("Payment Entry", dict(party="Mr G", paid_amount=1200))
- self.assertRaises(frappe.ValidationError, reconcile, bank_transaction=bank_transaction.name, payment_doctype="Payment Entry", payment_name=payment.name)
-
- # Raise an error if creditor transaction vs creditor payment
- def test_invalid_creditor_reconcilation(self):
- bank_transaction = frappe.get_doc("Bank Transaction", dict(description="I2015000011 VD/000002514 ATWWXXX AT4701345000003510057 Bio"))
- payment = frappe.get_doc("Payment Entry", dict(party="Conrad Electronic", paid_amount=690))
- self.assertRaises(frappe.ValidationError, reconcile, bank_transaction=bank_transaction.name, payment_doctype="Payment Entry", payment_name=payment.name)
-
- # Raise an error if debitor transaction vs debitor payment
- def test_invalid_debitor_reconcilation(self):
- bank_transaction = frappe.get_doc("Bank Transaction", dict(description="Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07"))
- payment = frappe.get_doc("Payment Entry", dict(party="Fayva", paid_amount=109080))
- self.assertRaises(frappe.ValidationError, reconcile, bank_transaction=bank_transaction.name, payment_doctype="Payment Entry", payment_name=payment.name)
+ vouchers = json.dumps([{
+ "payment_doctype":"Payment Entry",
+ "payment_name":payment.name,
+ "amount":bank_transaction.unallocated_amount}])
+ self.assertRaises(frappe.ValidationError, reconcile_vouchers, bank_transaction_name=bank_transaction.name, vouchers=vouchers)
# Raise an error if debitor transaction vs debitor payment
def test_clear_sales_invoice(self):
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="I2015000011 VD/000002514 ATWWXXX AT4701345000003510057 Bio"))
payment = frappe.get_doc("Sales Invoice", dict(customer="Fayva", status=["=", "Paid"]))
- reconcile(bank_transaction.name, "Sales Invoice", payment.name)
+ vouchers = json.dumps([{
+ "payment_doctype":"Sales Invoice",
+ "payment_name":payment.name,
+ "amount":bank_transaction.unallocated_amount}])
+ reconcile_vouchers(bank_transaction.name, vouchers=vouchers)
self.assertEqual(frappe.db.get_value("Bank Transaction", bank_transaction.name, "unallocated_amount"), 0)
self.assertTrue(frappe.db.get_value("Sales Invoice Payment", dict(parent=payment.name), "clearance_date") is not None)
@@ -126,7 +126,7 @@
"doctype": "Bank Transaction",
"description":"1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G",
"date": "2018-10-23",
- "debit": 1200,
+ "deposit": 1200,
"currency": "INR",
"bank_account": "Checking Account - Citi Bank"
}).insert()
@@ -136,7 +136,7 @@
"doctype": "Bank Transaction",
"description":"1512567 BG/000003025 OPSKATTUZWXXX AT776000000098709849 Herr G",
"date": "2018-10-23",
- "debit": 1700,
+ "deposit": 1700,
"currency": "INR",
"bank_account": "Checking Account - Citi Bank"
}).insert()
@@ -146,7 +146,7 @@
"doctype": "Bank Transaction",
"description":"Re 95282925234 FE/000002917 AT171513000281183046 Conrad Electronic",
"date": "2018-10-26",
- "debit": 690,
+ "withdrawal": 690,
"currency": "INR",
"bank_account": "Checking Account - Citi Bank"
}).insert()
@@ -156,7 +156,7 @@
"doctype": "Bank Transaction",
"description":"Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07",
"date": "2018-10-27",
- "debit": 3900,
+ "deposit": 3900,
"currency": "INR",
"bank_account": "Checking Account - Citi Bank"
}).insert()
@@ -166,7 +166,7 @@
"doctype": "Bank Transaction",
"description":"I2015000011 VD/000002514 ATWWXXX AT4701345000003510057 Bio",
"date": "2018-10-27",
- "credit": 109080,
+ "withdrawal": 109080,
"currency": "INR",
"bank_account": "Checking Account - Citi Bank"
}).insert()
@@ -174,7 +174,7 @@
frappe.flags.test_bank_transactions_created = True
-def add_payments():
+def add_vouchers():
if frappe.flags.test_payments_created:
return
@@ -192,6 +192,7 @@
pass
pi = make_purchase_invoice(supplier="Conrad Electronic", qty=1, rate=690)
+
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
pe.reference_no = "Conrad Oct 18"
pe.reference_date = "2018-10-24"
@@ -242,10 +243,15 @@
except frappe.DuplicateEntryError:
pass
- pi = make_purchase_invoice(supplier="Poore Simon's", qty=1, rate=3900)
+ pi = make_purchase_invoice(supplier="Poore Simon's", qty=1, rate=3900, is_paid=1, do_not_save =1)
+ pi.cash_bank_account = "_Test Bank - _TC"
+ pi.insert()
+ pi.submit()
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
pe.reference_no = "Poore Simon's Oct 18"
pe.reference_date = "2018-10-28"
+ pe.paid_amount = 690
+ pe.received_amount = 690
pe.insert()
pe.submit()
@@ -295,4 +301,4 @@
si.save()
si.submit()
- frappe.flags.test_payments_created = True
\ No newline at end of file
+ frappe.flags.test_payments_created = True
diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py
index b0a864f..ce76d0a 100644
--- a/erpnext/accounts/doctype/gl_entry/gl_entry.py
+++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py
@@ -27,30 +27,30 @@
def validate(self):
self.flags.ignore_submit_comment = True
- self.check_mandatory()
self.validate_and_set_fiscal_year()
self.pl_must_have_cost_center()
- self.validate_cost_center()
if not self.flags.from_repost:
+ self.check_mandatory()
+ self.validate_cost_center()
self.check_pl_account()
self.validate_party()
self.validate_currency()
- def on_update_with_args(self, adv_adj, update_outstanding = 'Yes', from_repost=False):
- if not from_repost:
+ def on_update(self):
+ adv_adj = self.flags.adv_adj
+ if not self.flags.from_repost:
self.validate_account_details(adv_adj)
self.validate_dimensions_for_pl_and_bs()
self.validate_allowed_dimensions()
+ validate_balance_type(self.account, adv_adj)
+ validate_frozen_account(self.account, adv_adj)
- validate_frozen_account(self.account, adv_adj)
- validate_balance_type(self.account, adv_adj)
-
- # Update outstanding amt on against voucher
- if self.against_voucher_type in ['Journal Entry', 'Sales Invoice', 'Purchase Invoice', 'Fees'] \
- and self.against_voucher and update_outstanding == 'Yes' and not from_repost:
- update_outstanding_amt(self.account, self.party_type, self.party, self.against_voucher_type,
- self.against_voucher)
+ # Update outstanding amt on against voucher
+ if (self.against_voucher_type in ['Journal Entry', 'Sales Invoice', 'Purchase Invoice', 'Fees']
+ and self.against_voucher and self.flags.update_outstanding == 'Yes'):
+ update_outstanding_amt(self.account, self.party_type, self.party, self.against_voucher_type,
+ self.against_voucher)
def check_mandatory(self):
mandatory = ['account','voucher_type','voucher_no','company']
@@ -58,7 +58,7 @@
if not self.get(k):
frappe.throw(_("{0} is required").format(_(self.meta.get_label(k))))
- account_type = frappe.db.get_value("Account", self.account, "account_type")
+ account_type = frappe.get_cached_value("Account", self.account, "account_type")
if not (self.party_type and self.party):
if account_type == "Receivable":
frappe.throw(_("{0} {1}: Customer is required against Receivable account {2}")
@@ -73,7 +73,7 @@
.format(self.voucher_type, self.voucher_no, self.account))
def pl_must_have_cost_center(self):
- if frappe.db.get_value("Account", self.account, "report_type") == "Profit and Loss":
+ if frappe.get_cached_value("Account", self.account, "report_type") == "Profit and Loss":
if not self.cost_center and self.voucher_type != 'Period Closing Voucher':
frappe.throw(_("{0} {1}: Cost Center is required for 'Profit and Loss' account {2}. Please set up a default Cost Center for the Company.")
.format(self.voucher_type, self.voucher_no, self.account))
@@ -140,25 +140,16 @@
.format(self.voucher_type, self.voucher_no, self.account, self.company))
def validate_cost_center(self):
- if not hasattr(self, "cost_center_company"):
- self.cost_center_company = {}
+ if not self.cost_center: return
- def _get_cost_center_company():
- if not self.cost_center_company.get(self.cost_center):
- self.cost_center_company[self.cost_center] = frappe.db.get_value(
- "Cost Center", self.cost_center, "company")
+ is_group, company = frappe.get_cached_value('Cost Center',
+ self.cost_center, ['is_group', 'company'])
- return self.cost_center_company[self.cost_center]
-
- def _check_is_group():
- return cint(frappe.get_cached_value('Cost Center', self.cost_center, 'is_group'))
-
- if self.cost_center and _get_cost_center_company() != self.company:
+ if company != self.company:
frappe.throw(_("{0} {1}: Cost Center {2} does not belong to Company {3}")
.format(self.voucher_type, self.voucher_no, self.cost_center, self.company))
- if not self.flags.from_repost and not self.voucher_type == 'Period Closing Voucher' \
- and self.cost_center and _check_is_group():
+ if (self.voucher_type != 'Period Closing Voucher' and is_group):
frappe.throw(_("""{0} {1}: Cost Center {2} is a group cost center and group cost centers cannot be used in transactions""").format(
self.voucher_type, self.voucher_no, frappe.bold(self.cost_center)))
@@ -184,7 +175,6 @@
if not self.fiscal_year:
self.fiscal_year = get_fiscal_year(self.posting_date, company=self.company)[0]
-
def validate_balance_type(account, adv_adj=False):
if not adv_adj and account:
balance_must_be = frappe.db.get_value("Account", account, "balance_must_be")
@@ -250,7 +240,7 @@
def validate_frozen_account(account, adv_adj=None):
- frozen_account = frappe.db.get_value("Account", account, "freeze_account")
+ frozen_account = frappe.get_cached_value("Account", account, "freeze_account")
if frozen_account == 'Yes' and not adv_adj:
frozen_accounts_modifier = frappe.db.get_value( 'Accounts Settings', None,
'frozen_accounts_modifier')
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py
index cb90f80..3419bb6 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py
@@ -102,7 +102,7 @@
if account_currency == previous_account_currency:
if self.total_credit != doc.total_debit or self.total_debit != doc.total_credit:
frappe.throw(_("Total Credit/ Debit Amount should be same as linked Journal Entry"))
-
+
def validate_stock_accounts(self):
stock_accounts = get_stock_accounts(self.company, self.doctype, self.name)
for account in stock_accounts:
@@ -229,11 +229,11 @@
if d.reference_type=="Journal Entry":
account_root_type = frappe.db.get_value("Account", d.account, "root_type")
if account_root_type == "Asset" and flt(d.debit) > 0:
- frappe.throw(_("For {0}, only credit accounts can be linked against another debit entry")
- .format(d.account))
+ frappe.throw(_("Row #{0}: For {1}, you can select reference document only if account gets credited")
+ .format(d.idx, d.account))
elif account_root_type == "Liability" and flt(d.credit) > 0:
- frappe.throw(_("For {0}, only debit accounts can be linked against another credit entry")
- .format(d.account))
+ frappe.throw(_("Row #{0}: For {1}, you can select reference document only if account gets debited")
+ .format(d.idx, d.account))
if d.reference_name == self.name:
frappe.throw(_("You can not enter current voucher in 'Against Journal Entry' column"))
@@ -1077,4 +1077,4 @@
},
}, target_doc)
- return doclist
\ No newline at end of file
+ return doclist
diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
index 0565264..f0b4e29 100644
--- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
@@ -136,7 +136,7 @@
for d in self.items:
max_discount = frappe.get_cached_value("Item", d.item_code, "max_discount")
if max_discount and flt(self.discount_percentage) > flt(max_discount):
- throw(_("Max discount allowed for item: {0} is {1}%").format(self.item_code, max_discount))
+ throw(_("Max discount allowed for item: {0} is {1}%").format(d.item_code, max_discount))
def validate_price_list_with_currency(self):
if self.currency and self.for_price_list:
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index e591ba6..4076be7 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -21,6 +21,7 @@
from erpnext.accounts.doctype.loyalty_program.loyalty_program import \
get_loyalty_program_details_with_points, get_loyalty_details, validate_loyalty_points
from erpnext.accounts.deferred_revenue import validate_service_stop_date
+from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details
from frappe.model.utils import get_fetch_values
from frappe.contacts.doctype.address.address import get_address_display
@@ -75,6 +76,8 @@
if not self.is_pos:
self.so_dn_required()
+
+ self.set_tax_withholding()
self.validate_proj_cust()
self.validate_pos_return()
@@ -153,6 +156,32 @@
if cost_center_company != self.company:
frappe.throw(_("Row #{0}: Cost Center {1} does not belong to company {2}").format(frappe.bold(item.idx), frappe.bold(item.cost_center), frappe.bold(self.company)))
+ def set_tax_withholding(self):
+ tax_withholding_details = get_party_tax_withholding_details(self)
+
+ if not tax_withholding_details:
+ return
+
+ accounts = []
+ tax_withholding_account = tax_withholding_details.get("account_head")
+
+ for d in self.taxes:
+ if d.account_head == tax_withholding_account:
+ d.update(tax_withholding_details)
+ accounts.append(d.account_head)
+
+ if not accounts or tax_withholding_account not in accounts:
+ self.append("taxes", tax_withholding_details)
+
+ to_remove = [d for d in self.taxes
+ if not d.tax_amount and d.charge_type == "Actual" and d.account_head == tax_withholding_account]
+
+ for d in to_remove:
+ self.remove(d)
+
+ # calculate totals again after applying TDS
+ self.calculate_taxes_and_totals()
+
def before_save(self):
set_account_for_mode_of_payment(self)
diff --git a/erpnext/accounts/doctype/subscription/subscription.js b/erpnext/accounts/doctype/subscription/subscription.js
index ba98eb9..1a90664 100644
--- a/erpnext/accounts/doctype/subscription/subscription.js
+++ b/erpnext/accounts/doctype/subscription/subscription.js
@@ -10,6 +10,14 @@
}
}
});
+
+ frm.set_query('cost_center', function() {
+ return {
+ filters: {
+ company: frm.doc.company
+ }
+ };
+ });
},
refresh: function(frm) {
diff --git a/erpnext/accounts/doctype/subscription/subscription.json b/erpnext/accounts/doctype/subscription/subscription.json
index afb94fe..e80df2a 100644
--- a/erpnext/accounts/doctype/subscription/subscription.json
+++ b/erpnext/accounts/doctype/subscription/subscription.json
@@ -7,9 +7,10 @@
"engine": "InnoDB",
"field_order": [
"party_type",
- "status",
- "cb_1",
"party",
+ "cb_1",
+ "company",
+ "status",
"subscription_period",
"start_date",
"end_date",
@@ -44,80 +45,107 @@
{
"allow_on_submit": 1,
"fieldname": "cb_1",
- "fieldtype": "Column Break"
+ "fieldtype": "Column Break",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "status",
"fieldtype": "Select",
"label": "Status",
+ "no_copy": 1,
"options": "\nTrialling\nActive\nPast Due Date\nCancelled\nUnpaid\nCompleted",
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "subscription_period",
"fieldtype": "Section Break",
- "label": "Subscription Period"
+ "label": "Subscription Period",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "cancelation_date",
"fieldtype": "Date",
"label": "Cancelation Date",
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"allow_on_submit": 1,
"fieldname": "trial_period_start",
"fieldtype": "Date",
"label": "Trial Period Start Date",
- "set_only_once": 1
+ "set_only_once": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "eval:doc.trial_period_start",
"fieldname": "trial_period_end",
"fieldtype": "Date",
"label": "Trial Period End Date",
- "set_only_once": 1
+ "set_only_once": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "column_break_11",
- "fieldtype": "Column Break"
+ "fieldtype": "Column Break",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "current_invoice_start",
"fieldtype": "Date",
"label": "Current Invoice Start Date",
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "current_invoice_end",
"fieldtype": "Date",
"label": "Current Invoice End Date",
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "0",
"description": "Number of days that the subscriber has to pay invoices generated by this subscription",
"fieldname": "days_until_due",
"fieldtype": "Int",
- "label": "Days Until Due"
+ "label": "Days Until Due",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "0",
"fieldname": "cancel_at_period_end",
"fieldtype": "Check",
- "label": "Cancel At End Of Period"
+ "label": "Cancel At End Of Period",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "0",
"fieldname": "generate_invoice_at_period_start",
"fieldtype": "Check",
- "label": "Generate Invoice At Beginning Of Period"
+ "label": "Generate Invoice At Beginning Of Period",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"allow_on_submit": 1,
"fieldname": "sb_4",
"fieldtype": "Section Break",
- "label": "Plans"
+ "label": "Plans",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"allow_on_submit": 1,
@@ -125,62 +153,84 @@
"fieldtype": "Table",
"label": "Plans",
"options": "Subscription Plan Detail",
- "reqd": 1
+ "reqd": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "eval:['Customer', 'Supplier'].includes(doc.party_type)",
"fieldname": "sb_1",
"fieldtype": "Section Break",
- "label": "Taxes"
+ "label": "Taxes",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "sb_2",
"fieldtype": "Section Break",
- "label": "Discounts"
+ "label": "Discounts",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "apply_additional_discount",
"fieldtype": "Select",
"label": "Apply Additional Discount On",
- "options": "\nGrand Total\nNet Total"
+ "options": "\nGrand Total\nNet Total",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "cb_2",
- "fieldtype": "Column Break"
+ "fieldtype": "Column Break",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "additional_discount_percentage",
"fieldtype": "Percent",
- "label": "Additional DIscount Percentage"
+ "label": "Additional DIscount Percentage",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"collapsible": 1,
"fieldname": "additional_discount_amount",
"fieldtype": "Currency",
- "label": "Additional DIscount Amount"
+ "label": "Additional DIscount Amount",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "eval:doc.invoices",
"fieldname": "sb_3",
"fieldtype": "Section Break",
- "label": "Invoices"
+ "label": "Invoices",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"collapsible": 1,
"fieldname": "invoices",
"fieldtype": "Table",
"label": "Invoices",
- "options": "Subscription Invoice"
+ "options": "Subscription Invoice",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"collapsible": 1,
"fieldname": "accounting_dimensions_section",
"fieldtype": "Section Break",
- "label": "Accounting Dimensions"
+ "label": "Accounting Dimensions",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "dimension_col_break",
- "fieldtype": "Column Break"
+ "fieldtype": "Column Break",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "party_type",
@@ -188,7 +238,9 @@
"label": "Party Type",
"options": "DocType",
"reqd": 1,
- "set_only_once": 1
+ "set_only_once": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "party",
@@ -197,21 +249,27 @@
"label": "Party",
"options": "party_type",
"reqd": 1,
- "set_only_once": 1
+ "set_only_once": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "eval:doc.party_type === 'Customer'",
"fieldname": "sales_tax_template",
"fieldtype": "Link",
"label": "Sales Taxes and Charges Template",
- "options": "Sales Taxes and Charges Template"
+ "options": "Sales Taxes and Charges Template",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "eval:doc.party_type === 'Supplier'",
"fieldname": "purchase_tax_template",
"fieldtype": "Link",
"label": "Purchase Taxes and Charges Template",
- "options": "Purchase Taxes and Charges Template"
+ "options": "Purchase Taxes and Charges Template",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "0",
@@ -219,36 +277,55 @@
"fieldname": "follow_calendar_months",
"fieldtype": "Check",
"label": "Follow Calendar Months",
- "set_only_once": 1
+ "set_only_once": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "0",
"description": "New invoices will be generated as per schedule even if current invoices are unpaid or past due date",
"fieldname": "generate_new_invoices_past_due_date",
"fieldtype": "Check",
- "label": "Generate New Invoices Past Due Date"
+ "label": "Generate New Invoices Past Due Date",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "end_date",
"fieldtype": "Date",
"label": "Subscription End Date",
- "set_only_once": 1
+ "set_only_once": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "start_date",
"fieldtype": "Date",
"label": "Subscription Start Date",
- "set_only_once": 1
+ "set_only_once": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "cost_center",
"fieldtype": "Link",
"label": "Cost Center",
- "options": "Cost Center"
+ "options": "Cost Center",
+ "show_days": 1,
+ "show_seconds": 1
+ },
+ {
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "label": "Company",
+ "options": "Company",
+ "show_days": 1,
+ "show_seconds": 1
}
],
+ "index_web_pages_for_search": 1,
"links": [],
- "modified": "2020-06-25 10:52:52.265105",
+ "modified": "2021-02-09 15:44:20.024789",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Subscription",
diff --git a/erpnext/accounts/doctype/subscription/subscription.py b/erpnext/accounts/doctype/subscription/subscription.py
index e023b47..826044a 100644
--- a/erpnext/accounts/doctype/subscription/subscription.py
+++ b/erpnext/accounts/doctype/subscription/subscription.py
@@ -1,3 +1,4 @@
+
# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
@@ -5,12 +6,13 @@
from __future__ import unicode_literals
import frappe
+import erpnext
from frappe import _
from frappe.model.document import Document
from frappe.utils.data import nowdate, getdate, cstr, cint, add_days, date_diff, get_last_day, add_to_date, flt
from erpnext.accounts.doctype.subscription_plan.subscription_plan import get_plan_rate
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
-
+from erpnext import get_default_company
class Subscription(Document):
def before_insert(self):
@@ -243,6 +245,7 @@
self.validate_plans_billing_cycle(self.get_billing_cycle_and_interval())
self.validate_end_date()
self.validate_to_follow_calendar_months()
+ self.cost_center = erpnext.get_default_cost_center(self.get('company'))
def validate_trial_period(self):
"""
@@ -304,6 +307,14 @@
doctype = 'Sales Invoice' if self.party_type == 'Customer' else 'Purchase Invoice'
invoice = frappe.new_doc(doctype)
+
+ # For backward compatibility
+ # Earlier subscription didn't had any company field
+ company = self.get('company') or get_default_company()
+ if not company:
+ frappe.throw(_("Company is mandatory was generating invoice. Please set default company in Global Defaults"))
+
+ invoice.company = company
invoice.set_posting_time = 1
invoice.posting_date = self.current_invoice_start if self.generate_invoice_at_period_start \
else self.current_invoice_end
@@ -330,6 +341,7 @@
# for that reason
items_list = self.get_items_from_plans(self.plans, prorate)
for item in items_list:
+ item['cost_center'] = self.cost_center
invoice.append('items', item)
# Taxes
@@ -380,7 +392,8 @@
Returns the `Item`s linked to `Subscription Plan`
"""
if prorate:
- prorate_factor = get_prorata_factor(self.current_invoice_end, self.current_invoice_start)
+ prorate_factor = get_prorata_factor(self.current_invoice_end, self.current_invoice_start,
+ self.generate_invoice_at_period_start)
items = []
party = self.party
@@ -583,10 +596,13 @@
return calendar_months
-def get_prorata_factor(period_end, period_start):
- diff = flt(date_diff(nowdate(), period_start) + 1)
- plan_days = flt(date_diff(period_end, period_start) + 1)
- prorate_factor = diff / plan_days
+def get_prorata_factor(period_end, period_start, is_prepaid):
+ if is_prepaid:
+ prorate_factor = 1
+ else:
+ diff = flt(date_diff(nowdate(), period_start) + 1)
+ plan_days = flt(date_diff(period_end, period_start) + 1)
+ prorate_factor = diff / plan_days
return prorate_factor
diff --git a/erpnext/accounts/doctype/subscription/test_subscription.py b/erpnext/accounts/doctype/subscription/test_subscription.py
index c17fccd..7c58e98 100644
--- a/erpnext/accounts/doctype/subscription/test_subscription.py
+++ b/erpnext/accounts/doctype/subscription/test_subscription.py
@@ -321,7 +321,8 @@
self.assertEqual(
flt(
- get_prorata_factor(subscription.current_invoice_end, subscription.current_invoice_start),
+ get_prorata_factor(subscription.current_invoice_end, subscription.current_invoice_start,
+ subscription.generate_invoice_at_period_start),
2),
flt(prorate_factor, 2)
)
@@ -561,9 +562,7 @@
current_inv = subscription.get_current_invoice()
self.assertEqual(current_inv.status, "Unpaid")
- diff = flt(date_diff(nowdate(), subscription.current_invoice_start) + 1)
- plan_days = flt(date_diff(subscription.current_invoice_end, subscription.current_invoice_start) + 1)
- prorate_factor = flt(diff / plan_days)
+ prorate_factor = 1
self.assertEqual(flt(current_inv.grand_total, 2), flt(prorate_factor * 900, 2))
diff --git a/erpnext/accounts/doctype/subscription_invoice/subscription_invoice.json b/erpnext/accounts/doctype/subscription_invoice/subscription_invoice.json
index f54e887..8a0d1de 100644
--- a/erpnext/accounts/doctype/subscription_invoice/subscription_invoice.json
+++ b/erpnext/accounts/doctype/subscription_invoice/subscription_invoice.json
@@ -13,21 +13,28 @@
"fieldname": "document_type",
"fieldtype": "Link",
"label": "Document Type ",
+ "no_copy": 1,
"options": "DocType",
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "invoice",
"fieldtype": "Dynamic Link",
"in_list_view": 1,
"label": "Invoice",
+ "no_copy": 1,
"options": "document_type",
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
}
],
+ "index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2020-06-01 22:23:54.462718",
+ "modified": "2021-02-09 15:43:32.026233",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Subscription Invoice",
diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
index 32ad4cb..961bdb1 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
@@ -12,37 +12,62 @@
class TaxWithholdingCategory(Document):
pass
-def get_party_tax_withholding_details(ref_doc, tax_withholding_category=None):
+def get_party_details(inv):
+ party_type, party = '', ''
+ if inv.doctype == 'Sales Invoice':
+ party_type = 'Customer'
+ party = inv.customer
+ else:
+ party_type = 'Supplier'
+ party = inv.supplier
+
+ return party_type, party
+
+def get_party_tax_withholding_details(inv, tax_withholding_category=None):
pan_no = ''
- suppliers = []
+ parties = []
+ party_type, party = get_party_details(inv)
if not tax_withholding_category:
- tax_withholding_category, pan_no = frappe.db.get_value('Supplier', ref_doc.supplier, ['tax_withholding_category', 'pan'])
+ tax_withholding_category, pan_no = frappe.db.get_value(party_type, party, ['tax_withholding_category', 'pan'])
if not tax_withholding_category:
return
+ # if tax_withholding_category passed as an argument but not pan_no
if not pan_no:
- pan_no = frappe.db.get_value('Supplier', ref_doc.supplier, 'pan')
+ pan_no = frappe.db.get_value(party_type, party, 'pan')
# Get others suppliers with the same PAN No
if pan_no:
- suppliers = [d.name for d in frappe.get_all('Supplier', fields=['name'], filters={'pan': pan_no})]
+ parties = frappe.get_all(party_type, filters={ 'pan': pan_no }, pluck='name')
- if not suppliers:
- suppliers.append(ref_doc.supplier)
+ if not parties:
+ parties.append(party)
- fy = get_fiscal_year(ref_doc.posting_date, company=ref_doc.company)
- tax_details = get_tax_withholding_details(tax_withholding_category, fy[0], ref_doc.company)
+ fiscal_year = get_fiscal_year(inv.posting_date, company=inv.company)
+ tax_details = get_tax_withholding_details(tax_withholding_category, fiscal_year[0], inv.company)
+
if not tax_details:
frappe.throw(_('Please set associated account in Tax Withholding Category {0} against Company {1}')
- .format(tax_withholding_category, ref_doc.company))
+ .format(tax_withholding_category, inv.company))
- tds_amount = get_tds_amount(suppliers, ref_doc.net_total, ref_doc.company,
- tax_details, fy, ref_doc.posting_date, pan_no)
+ if party_type == 'Customer' and not tax_details.cumulative_threshold:
+ # TCS is only chargeable on sum of invoiced value
+ frappe.throw(_('Tax Withholding Category {} against Company {} for Customer {} should have Cumulative Threshold value.')
+ .format(tax_withholding_category, inv.company, party))
- tax_row = get_tax_row(tax_details, tds_amount)
+ tax_amount, tax_deducted = get_tax_amount(
+ party_type, parties,
+ inv, tax_details,
+ fiscal_year, pan_no
+ )
+
+ if party_type == 'Supplier':
+ tax_row = get_tax_row_for_tds(tax_details, tax_amount)
+ else:
+ tax_row = get_tax_row_for_tcs(inv, tax_details, tax_amount, tax_deducted)
return tax_row
@@ -69,147 +94,254 @@
frappe.throw(_("No Tax Withholding data found for the current Fiscal Year."))
-def get_tax_row(tax_details, tds_amount):
-
- return {
+def get_tax_row_for_tcs(inv, tax_details, tax_amount, tax_deducted):
+ row = {
"category": "Total",
- "add_deduct_tax": "Deduct",
"charge_type": "Actual",
- "account_head": tax_details.account_head,
+ "tax_amount": tax_amount,
"description": tax_details.description,
- "tax_amount": tds_amount
+ "account_head": tax_details.account_head
}
-def get_tds_amount(suppliers, net_total, company, tax_details, fiscal_year_details, posting_date, pan_no=None):
- fiscal_year, year_start_date, year_end_date = fiscal_year_details
- tds_amount = 0
- tds_deducted = 0
+ if tax_deducted:
+ # TCS already deducted on previous invoices
+ # So, TCS will be calculated by 'Previous Row Total'
- def _get_tds(amount, rate):
- if amount <= 0:
- return 0
-
- return amount * rate / 100
-
- ldc_name = frappe.db.get_value('Lower Deduction Certificate',
- {
- 'pan_no': pan_no,
- 'fiscal_year': fiscal_year
- }, 'name')
- ldc = ''
-
- if ldc_name:
- ldc = frappe.get_doc('Lower Deduction Certificate', ldc_name)
-
- entries = frappe.db.sql("""
- select voucher_no, credit
- from `tabGL Entry`
- where company = %s and
- party in %s and fiscal_year=%s and credit > 0
- and is_opening = 'No'
- """, (company, tuple(suppliers), fiscal_year), as_dict=1)
-
- vouchers = [d.voucher_no for d in entries]
- advance_vouchers = get_advance_vouchers(suppliers, fiscal_year=fiscal_year, company=company)
-
- tds_vouchers = vouchers + advance_vouchers
-
- if tds_vouchers:
- tds_deducted = frappe.db.sql("""
- SELECT sum(credit) FROM `tabGL Entry`
- WHERE
- account=%s and fiscal_year=%s and credit > 0
- and voucher_no in ({0})""". format(','.join(['%s'] * len(tds_vouchers))),
- ((tax_details.account_head, fiscal_year) + tuple(tds_vouchers)))
-
- tds_deducted = tds_deducted[0][0] if tds_deducted and tds_deducted[0][0] else 0
-
- if tds_deducted:
- if ldc:
- limit_consumed = frappe.db.get_value('Purchase Invoice',
- {
- 'supplier': ('in', suppliers),
- 'apply_tds': 1,
- 'docstatus': 1
- }, 'sum(net_total)')
-
- if ldc and is_valid_certificate(ldc.valid_from, ldc.valid_upto, posting_date, limit_consumed, net_total,
- ldc.certificate_limit):
-
- tds_amount = get_ltds_amount(net_total, limit_consumed, ldc.certificate_limit, ldc.rate, tax_details)
+ taxes_excluding_tcs = [d for d in inv.taxes if d.account_head != tax_details.account_head]
+ if taxes_excluding_tcs:
+ # chargeable amount is the total amount after other charges are applied
+ row.update({
+ "charge_type": "On Previous Row Total",
+ "row_id": len(taxes_excluding_tcs),
+ "rate": tax_details.rate
+ })
else:
- tds_amount = _get_tds(net_total, tax_details.rate)
- else:
- supplier_credit_amount = frappe.get_all('Purchase Invoice',
- fields = ['sum(net_total)'],
- filters = {'name': ('in', vouchers), 'docstatus': 1, "apply_tds": 1}, as_list=1)
+ # if only TCS is to be charged, then net total is chargeable amount
+ row.update({
+ "charge_type": "On Net Total",
+ "rate": tax_details.rate
+ })
- supplier_credit_amount = (supplier_credit_amount[0][0]
- if supplier_credit_amount and supplier_credit_amount[0][0] else 0)
+ return row
- jv_supplier_credit_amt = frappe.get_all('Journal Entry Account',
- fields = ['sum(credit_in_account_currency)'],
- filters = {
- 'parent': ('in', vouchers), 'docstatus': 1,
- 'party': ('in', suppliers),
- 'reference_type': ('not in', ['Purchase Invoice'])
- }, as_list=1)
+def get_tax_row_for_tds(tax_details, tax_amount):
+ return {
+ "category": "Total",
+ "charge_type": "Actual",
+ "tax_amount": tax_amount,
+ "add_deduct_tax": "Deduct",
+ "description": tax_details.description,
+ "account_head": tax_details.account_head
+ }
- supplier_credit_amount += (jv_supplier_credit_amt[0][0]
- if jv_supplier_credit_amt and jv_supplier_credit_amt[0][0] else 0)
+def get_lower_deduction_certificate(fiscal_year, pan_no):
+ ldc_name = frappe.db.get_value('Lower Deduction Certificate', { 'pan_no': pan_no, 'fiscal_year': fiscal_year }, 'name')
+ if ldc_name:
+ return frappe.get_doc('Lower Deduction Certificate', ldc_name)
- supplier_credit_amount += net_total
+def get_tax_amount(party_type, parties, inv, tax_details, fiscal_year_details, pan_no=None):
+ fiscal_year = fiscal_year_details[0]
- debit_note_amount = get_debit_note_amount(suppliers, year_start_date, year_end_date)
- supplier_credit_amount -= debit_note_amount
+ vouchers = get_invoice_vouchers(parties, fiscal_year, inv.company, party_type=party_type)
+ advance_vouchers = get_advance_vouchers(parties, fiscal_year, inv.company, party_type=party_type)
+ taxable_vouchers = vouchers + advance_vouchers
- if ((tax_details.get('threshold', 0) and supplier_credit_amount >= tax_details.threshold)
- or (tax_details.get('cumulative_threshold', 0) and supplier_credit_amount >= tax_details.cumulative_threshold)):
+ tax_deducted = 0
+ if taxable_vouchers:
+ tax_deducted = get_deducted_tax(taxable_vouchers, fiscal_year, tax_details)
- if ldc and is_valid_certificate(ldc.valid_from, ldc.valid_upto, posting_date, tds_deducted, net_total,
- ldc.certificate_limit):
- tds_amount = get_ltds_amount(supplier_credit_amount, 0, ldc.certificate_limit, ldc.rate,
- tax_details)
+ tax_amount = 0
+ posting_date = inv.posting_date
+ if party_type == 'Supplier':
+ ldc = get_lower_deduction_certificate(fiscal_year, pan_no)
+ if tax_deducted:
+ net_total = inv.net_total
+ if ldc:
+ tax_amount = get_tds_amount_from_ldc(ldc, parties, fiscal_year, pan_no, tax_details, posting_date, net_total)
else:
- tds_amount = _get_tds(supplier_credit_amount, tax_details.rate)
+ tax_amount = net_total * tax_details.rate / 100 if net_total > 0 else 0
+ else:
+ tax_amount = get_tds_amount(
+ ldc, parties, inv, tax_details,
+ fiscal_year_details, tax_deducted, vouchers
+ )
+
+ elif party_type == 'Customer':
+ if tax_deducted:
+ # if already TCS is charged, then amount will be calculated based on 'Previous Row Total'
+ tax_amount = 0
+ else:
+ # if no TCS has been charged in FY,
+ # then chargeable value is "prev invoices + advances" value which cross the threshold
+ tax_amount = get_tcs_amount(
+ parties, inv, tax_details,
+ fiscal_year_details, vouchers, advance_vouchers
+ )
+
+ return tax_amount, tax_deducted
+
+def get_invoice_vouchers(parties, fiscal_year, company, party_type='Supplier'):
+ dr_or_cr = 'credit' if party_type == 'Supplier' else 'debit'
+
+ filters = {
+ dr_or_cr: ['>', 0],
+ 'company': company,
+ 'party_type': party_type,
+ 'party': ['in', parties],
+ 'fiscal_year': fiscal_year,
+ 'is_opening': 'No',
+ 'is_cancelled': 0
+ }
+
+ return frappe.get_all('GL Entry', filters=filters, distinct=1, pluck="voucher_no") or [""]
+
+def get_advance_vouchers(parties, fiscal_year=None, company=None, from_date=None, to_date=None, party_type='Supplier'):
+ # for advance vouchers, debit and credit is reversed
+ dr_or_cr = 'debit' if party_type == 'Supplier' else 'credit'
+
+ filters = {
+ dr_or_cr: ['>', 0],
+ 'is_opening': 'No',
+ 'is_cancelled': 0,
+ 'party_type': party_type,
+ 'party': ['in', parties],
+ 'against_voucher': ['is', 'not set']
+ }
+
+ if fiscal_year:
+ filters['fiscal_year'] = fiscal_year
+ if company:
+ filters['company'] = company
+ if from_date and to_date:
+ filters['posting_date'] = ['between', (from_date, to_date)]
+
+ return frappe.get_all('GL Entry', filters=filters, distinct=1, pluck='voucher_no') or [""]
+
+def get_deducted_tax(taxable_vouchers, fiscal_year, tax_details):
+ # check if TDS / TCS account is already charged on taxable vouchers
+ filters = {
+ 'is_cancelled': 0,
+ 'credit': ['>', 0],
+ 'fiscal_year': fiscal_year,
+ 'account': tax_details.account_head,
+ 'voucher_no': ['in', taxable_vouchers],
+ }
+ field = "sum(credit)"
+
+ return frappe.db.get_value('GL Entry', filters, field) or 0.0
+
+def get_tds_amount(ldc, parties, inv, tax_details, fiscal_year_details, tax_deducted, vouchers):
+ tds_amount = 0
+
+ supp_credit_amt = frappe.db.get_value('Purchase Invoice', {
+ 'name': ('in', vouchers), 'docstatus': 1, 'apply_tds': 1
+ }, 'sum(net_total)') or 0.0
+
+ supp_jv_credit_amt = frappe.db.get_value('Journal Entry Account', {
+ 'parent': ('in', vouchers), 'docstatus': 1,
+ 'party': ('in', parties), 'reference_type': ('!=', 'Purchase Invoice')
+ }, 'sum(credit_in_account_currency)') or 0.0
+
+ supp_credit_amt += supp_jv_credit_amt
+ supp_credit_amt += inv.net_total
+
+ debit_note_amount = get_debit_note_amount(parties, fiscal_year_details, inv.company)
+ supp_credit_amt -= debit_note_amount
+
+ threshold = tax_details.get('threshold', 0)
+ cumulative_threshold = tax_details.get('cumulative_threshold', 0)
+
+ if ((threshold and supp_credit_amt >= threshold) or (cumulative_threshold and supp_credit_amt >= cumulative_threshold)):
+ if ldc and is_valid_certificate(
+ ldc.valid_from, ldc.valid_upto,
+ inv.posting_date, tax_deducted,
+ inv.net_total, ldc.certificate_limit
+ ):
+ tds_amount = get_ltds_amount(supp_credit_amt, 0, ldc.certificate_limit, ldc.rate, tax_details)
+ else:
+ tds_amount = supp_credit_amt * tax_details.rate / 100 if supp_credit_amt > 0 else 0
return tds_amount
-def get_advance_vouchers(suppliers, fiscal_year=None, company=None, from_date=None, to_date=None):
- condition = "fiscal_year=%s" % fiscal_year
+def get_tcs_amount(parties, inv, tax_details, fiscal_year_details, vouchers, adv_vouchers):
+ tcs_amount = 0
+ fiscal_year, _, _ = fiscal_year_details
+
+ # sum of debit entries made from sales invoices
+ invoiced_amt = frappe.db.get_value('GL Entry', {
+ 'is_cancelled': 0,
+ 'party': ['in', parties],
+ 'company': inv.company,
+ 'voucher_no': ['in', vouchers],
+ }, 'sum(debit)') or 0.0
+
+ # sum of credit entries made from PE / JV with unset 'against voucher'
+ advance_amt = frappe.db.get_value('GL Entry', {
+ 'is_cancelled': 0,
+ 'party': ['in', parties],
+ 'company': inv.company,
+ 'voucher_no': ['in', adv_vouchers],
+ }, 'sum(credit)') or 0.0
+
+ # sum of credit entries made from sales invoice
+ credit_note_amt = frappe.db.get_value('GL Entry', {
+ 'is_cancelled': 0,
+ 'credit': ['>', 0],
+ 'party': ['in', parties],
+ 'fiscal_year': fiscal_year,
+ 'company': inv.company,
+ 'voucher_type': 'Sales Invoice',
+ }, 'sum(credit)') or 0.0
+
+ cumulative_threshold = tax_details.get('cumulative_threshold', 0)
+
+ current_invoice_total = get_invoice_total_without_tcs(inv, tax_details)
+ total_invoiced_amt = current_invoice_total + invoiced_amt + advance_amt - credit_note_amt
+
+ if ((cumulative_threshold and total_invoiced_amt >= cumulative_threshold)):
+ chargeable_amt = total_invoiced_amt - cumulative_threshold
+ tcs_amount = chargeable_amt * tax_details.rate / 100 if chargeable_amt > 0 else 0
+
+ return tcs_amount
+
+def get_invoice_total_without_tcs(inv, tax_details):
+ tcs_tax_row = [d for d in inv.taxes if d.account_head == tax_details.account_head]
+ tcs_tax_row_amount = tcs_tax_row[0].base_tax_amount if tcs_tax_row else 0
+
+ return inv.grand_total - tcs_tax_row_amount
+
+def get_tds_amount_from_ldc(ldc, parties, fiscal_year, pan_no, tax_details, posting_date, net_total):
+ tds_amount = 0
+ limit_consumed = frappe.db.get_value('Purchase Invoice', {
+ 'supplier': ('in', parties),
+ 'apply_tds': 1,
+ 'docstatus': 1
+ }, 'sum(net_total)')
+
+ if is_valid_certificate(
+ ldc.valid_from, ldc.valid_upto,
+ posting_date, limit_consumed,
+ net_total, ldc.certificate_limit
+ ):
+ tds_amount = get_ltds_amount(net_total, limit_consumed, ldc.certificate_limit, ldc.rate, tax_details)
+
+ return tds_amount
+
+def get_debit_note_amount(suppliers, fiscal_year_details, company=None):
+ _, year_start_date, year_end_date = fiscal_year_details
+
+ filters = {
+ 'supplier': ['in', suppliers],
+ 'is_return': 1,
+ 'docstatus': 1,
+ 'posting_date': ['between', (year_start_date, year_end_date)]
+ }
+ fields = ['abs(sum(net_total)) as net_total']
if company:
- condition += "and company =%s" % (company)
- if from_date and to_date:
- condition += "and posting_date between %s and %s" % (from_date, to_date)
+ filters['company'] = company
- ## Appending the same supplier again if length of suppliers list is 1
- ## since tuple of single element list contains None, For example ('Test Supplier 1', )
- ## and the below query fails
- if len(suppliers) == 1:
- suppliers.append(suppliers[0])
-
- return frappe.db.sql_list("""
- select distinct voucher_no
- from `tabGL Entry`
- where party in %s and %s and debit > 0
- and is_opening = 'No'
- """, (tuple(suppliers), condition)) or []
-
-def get_debit_note_amount(suppliers, year_start_date, year_end_date, company=None):
- condition = "and 1=1"
- if company:
- condition = " and company=%s " % company
-
- if len(suppliers) == 1:
- suppliers.append(suppliers[0])
-
- return flt(frappe.db.sql("""
- select abs(sum(net_total))
- from `tabPurchase Invoice`
- where supplier in %s and is_return=1 and docstatus=1
- and posting_date between %s and %s %s
- """, (tuple(suppliers), year_start_date, year_end_date, condition)))
+ return frappe.get_all('Purchase Invoice', filters, fields)[0].get('net_total') or 0.0
def get_ltds_amount(current_amount, deducted_amount, certificate_limit, rate, tax_details):
if current_amount < (certificate_limit - deducted_amount):
@@ -227,4 +359,4 @@
certificate_limit > deducted_amount):
valid = True
- return valid
\ No newline at end of file
+ return valid
diff --git a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
index ef77674..9ce8e3f 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
@@ -9,7 +9,7 @@
from erpnext.accounts.utils import get_fiscal_year
from erpnext.buying.doctype.supplier.test_supplier import create_supplier
-test_dependencies = ["Supplier Group"]
+test_dependencies = ["Supplier Group", "Customer Group"]
class TestTaxWithholdingCategory(unittest.TestCase):
@classmethod
@@ -18,6 +18,9 @@
create_records()
create_tax_with_holding_category()
+ def tearDown(self):
+ cancel_invoices()
+
def test_cumulative_threshold_tds(self):
frappe.db.set_value("Supplier", "Test TDS Supplier", "tax_withholding_category", "Cumulative Threshold TDS")
invoices = []
@@ -128,9 +131,59 @@
for d in invoices:
d.cancel()
+ def test_cumulative_threshold_tcs(self):
+ frappe.db.set_value("Customer", "Test TCS Customer", "tax_withholding_category", "Cumulative Threshold TCS")
+ invoices = []
+
+ # create invoices for lower than single threshold tax rate
+ for _ in range(2):
+ si = create_sales_invoice(customer = "Test TCS Customer")
+ si.submit()
+ invoices.append(si)
+
+ # create another invoice whose total when added to previously created invoice,
+ # surpasses cumulative threshhold
+ si = create_sales_invoice(customer = "Test TCS Customer", rate=12000)
+ si.submit()
+
+ # assert tax collection on total invoice amount created until now
+ tcs_charged = sum([d.base_tax_amount for d in si.taxes if d.account_head == 'TCS - _TC'])
+ self.assertEqual(tcs_charged, 200)
+ self.assertEqual(si.grand_total, 12200)
+ invoices.append(si)
+
+ # TCS is already collected once, so going forward system will collect TCS on every invoice
+ si = create_sales_invoice(customer = "Test TCS Customer", rate=5000)
+ si.submit()
+
+ tcs_charged = sum([d.base_tax_amount for d in si.taxes if d.account_head == 'TCS - _TC'])
+ self.assertEqual(tcs_charged, 500)
+ invoices.append(si)
+
+ #delete invoices to avoid clashing
+ for d in invoices:
+ d.cancel()
+
+def cancel_invoices():
+ purchase_invoices = frappe.get_all("Purchase Invoice", {
+ 'supplier': ['in', ['Test TDS Supplier', 'Test TDS Supplier1', 'Test TDS Supplier2']],
+ 'docstatus': 1
+ }, pluck="name")
+
+ sales_invoices = frappe.get_all("Sales Invoice", {
+ 'customer': 'Test TCS Customer',
+ 'docstatus': 1
+ }, pluck="name")
+
+ for d in purchase_invoices:
+ frappe.get_doc('Purchase Invoice', d).cancel()
+
+ for d in sales_invoices:
+ frappe.get_doc('Sales Invoice', d).cancel()
+
def create_purchase_invoice(**args):
# return sales invoice doc object
- item = frappe.get_doc('Item', {'item_name': 'TDS Item'})
+ item = frappe.db.get_value('Item', {'item_name': 'TDS Item'}, "name")
args = frappe._dict(args)
pi = frappe.get_doc({
@@ -145,7 +198,7 @@
"taxes": [],
"items": [{
'doctype': 'Purchase Invoice Item',
- 'item_code': item.name,
+ 'item_code': item,
'qty': args.qty or 1,
'rate': args.rate or 10000,
'cost_center': 'Main - _TC',
@@ -156,6 +209,33 @@
pi.save()
return pi
+def create_sales_invoice(**args):
+ # return sales invoice doc object
+ item = frappe.db.get_value('Item', {'item_name': 'TCS Item'}, "name")
+
+ args = frappe._dict(args)
+ si = frappe.get_doc({
+ "doctype": "Sales Invoice",
+ "posting_date": today(),
+ "customer": args.customer,
+ "company": '_Test Company',
+ "taxes_and_charges": "",
+ "currency": "INR",
+ "debit_to": "Debtors - _TC",
+ "taxes": [],
+ "items": [{
+ 'doctype': 'Sales Invoice Item',
+ 'item_code': item,
+ 'qty': args.qty or 1,
+ 'rate': args.rate or 10000,
+ 'cost_center': 'Main - _TC',
+ 'expense_account': 'Cost of Goods Sold - _TC'
+ }]
+ })
+
+ si.save()
+ return si
+
def create_records():
# create a new suppliers
for name in ['Test TDS Supplier', 'Test TDS Supplier1', 'Test TDS Supplier2']:
@@ -168,7 +248,17 @@
"doctype": "Supplier",
}).insert()
- # create an item
+ for name in ['Test TCS Customer']:
+ if frappe.db.exists('Customer', name):
+ continue
+
+ frappe.get_doc({
+ "customer_group": "_Test Customer Group",
+ "customer_name": name,
+ "doctype": "Customer"
+ }).insert()
+
+ # create item
if not frappe.db.exists('Item', "TDS Item"):
frappe.get_doc({
"doctype": "Item",
@@ -178,7 +268,16 @@
"is_stock_item": 0,
}).insert()
- # create an account
+ if not frappe.db.exists('Item', "TCS Item"):
+ frappe.get_doc({
+ "doctype": "Item",
+ "item_code": "TCS Item",
+ "item_name": "TCS Item",
+ "item_group": "All Item Groups",
+ "is_stock_item": 1
+ }).insert()
+
+ # create tds account
if not frappe.db.exists("Account", "TDS - _TC"):
frappe.get_doc({
'doctype': 'Account',
@@ -189,6 +288,17 @@
'root_type': 'Asset'
}).insert()
+ # create tcs account
+ if not frappe.db.exists("Account", "TCS - _TC"):
+ frappe.get_doc({
+ 'doctype': 'Account',
+ 'company': '_Test Company',
+ 'account_name': 'TCS',
+ 'parent_account': 'Duties and Taxes - _TC',
+ 'report_type': 'Balance Sheet',
+ 'root_type': 'Liability'
+ }).insert()
+
def create_tax_with_holding_category():
fiscal_year = get_fiscal_year(today(), company="_Test Company")[0]
@@ -210,6 +320,23 @@
}]
}).insert()
+ if not frappe.db.exists("Tax Withholding Category", "Cumulative Threshold TCS"):
+ frappe.get_doc({
+ "doctype": "Tax Withholding Category",
+ "name": "Cumulative Threshold TCS",
+ "category_name": "10% TCS",
+ "rates": [{
+ 'fiscal_year': fiscal_year,
+ 'tax_withholding_rate': 10,
+ 'single_threshold': 0,
+ 'cumulative_threshold': 30000.00
+ }],
+ "accounts": [{
+ 'company': '_Test Company',
+ 'account': 'TCS - _TC'
+ }]
+ }).insert()
+
# Single thresold
if not frappe.db.exists("Tax Withholding Category", "Single Threshold TDS"):
frappe.get_doc({
diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py
index 287c79f..dac0c21 100644
--- a/erpnext/accounts/general_ledger.py
+++ b/erpnext/accounts/general_ledger.py
@@ -44,9 +44,9 @@
frappe.throw(_("You cannot create or cancel any accounting entries with in the closed Accounting Period {0}")
.format(frappe.bold(accounting_periods[0].name)), ClosedAccountingPeriod)
-def process_gl_map(gl_map, merge_entries=True):
+def process_gl_map(gl_map, merge_entries=True, precision=None):
if merge_entries:
- gl_map = merge_similar_entries(gl_map)
+ gl_map = merge_similar_entries(gl_map, precision)
for entry in gl_map:
# toggle debit, credit if negative entry
if flt(entry.debit) < 0:
@@ -69,7 +69,7 @@
return gl_map
-def merge_similar_entries(gl_map):
+def merge_similar_entries(gl_map, precision=None):
merged_gl_map = []
accounting_dimensions = get_accounting_dimensions()
for entry in gl_map:
@@ -88,7 +88,9 @@
company = gl_map[0].company if gl_map else erpnext.get_default_company()
company_currency = erpnext.get_company_currency(company)
- precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"), company_currency)
+
+ if not precision:
+ precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"), company_currency)
# filter zero debit and credit entries
merged_gl_map = filter(lambda x: flt(x.debit, precision)!=0 or flt(x.credit, precision)!=0, merged_gl_map)
@@ -132,8 +134,8 @@
gle.update(args)
gle.flags.ignore_permissions = 1
gle.flags.from_repost = from_repost
- gle.insert()
- gle.run_method("on_update_with_args", adv_adj, update_outstanding, from_repost)
+ gle.flags.adv_adj = adv_adj
+ gle.flags.update_outstanding = update_outstanding or 'Yes'
gle.submit()
if not from_repost:
@@ -194,7 +196,7 @@
if not round_off_gle:
for k in ["voucher_type", "voucher_no", "company",
- "posting_date", "remarks", "is_opening"]:
+ "posting_date", "remarks"]:
round_off_gle[k] = gl_map[0][k]
round_off_gle.update({
@@ -206,6 +208,7 @@
"cost_center": round_off_cost_center,
"party_type": None,
"party": None,
+ "is_opening": "No",
"against_voucher_type": None,
"against_voucher": None
})
diff --git a/erpnext/accounts/page/bank_reconciliation/__init__.py b/erpnext/accounts/page/bank_reconciliation/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/accounts/page/bank_reconciliation/__init__.py
+++ /dev/null
diff --git a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js
deleted file mode 100644
index 6ae81d7..0000000
--- a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js
+++ /dev/null
@@ -1,583 +0,0 @@
-frappe.provide("erpnext.accounts");
-
-frappe.pages['bank-reconciliation'].on_page_load = function(wrapper) {
- new erpnext.accounts.bankReconciliation(wrapper);
-}
-
-erpnext.accounts.bankReconciliation = class BankReconciliation {
- constructor(wrapper) {
- this.page = frappe.ui.make_app_page({
- parent: wrapper,
- title: __("Bank Reconciliation"),
- single_column: true
- });
- this.parent = wrapper;
- this.page = this.parent.page;
-
- this.check_plaid_status();
- this.make();
- }
-
- make() {
- const me = this;
-
- me.$main_section = $(`<div class="reconciliation page-main-content"></div>`).appendTo(me.page.main);
- const empty_state = __("Upload a bank statement, link or reconcile a bank account")
- me.$main_section.append(`<div class="flex justify-center align-center text-muted"
- style="height: 50vh; display: flex;"><h5 class="text-muted">${empty_state}</h5></div>`)
-
- me.page.add_field({
- fieldtype: 'Link',
- label: __('Company'),
- fieldname: 'company',
- options: "Company",
- onchange: function() {
- if (this.value) {
- me.company = this.value;
- } else {
- me.company = null;
- me.bank_account = null;
- }
- }
- })
- me.page.add_field({
- fieldtype: 'Link',
- label: __('Bank Account'),
- fieldname: 'bank_account',
- options: "Bank Account",
- get_query: function() {
- if(!me.company) {
- frappe.throw(__("Please select company first"));
- return
- }
-
- return {
- filters: {
- "company": me.company
- }
- }
- },
- onchange: function() {
- if (this.value) {
- me.bank_account = this.value;
- me.add_actions();
- } else {
- me.bank_account = null;
- me.page.hide_actions_menu();
- }
- }
- })
- }
-
- check_plaid_status() {
- const me = this;
- frappe.db.get_value("Plaid Settings", "Plaid Settings", "enabled", (r) => {
- if (r && r.enabled === "1") {
- me.plaid_status = "active"
- } else {
- me.plaid_status = "inactive"
- }
- })
- }
-
- add_actions() {
- const me = this;
-
- me.page.show_menu()
-
- me.page.add_menu_item(__("Upload a statement"), function() {
- me.clear_page_content();
- new erpnext.accounts.bankTransactionUpload(me);
- }, true)
-
- if (me.plaid_status==="active") {
- me.page.add_menu_item(__("Synchronize this account"), function() {
- me.clear_page_content();
- new erpnext.accounts.bankTransactionSync(me);
- }, true)
- }
-
- me.page.add_menu_item(__("Reconcile this account"), function() {
- me.clear_page_content();
- me.make_reconciliation_tool();
- }, true)
- }
-
- clear_page_content() {
- const me = this;
- $(me.page.body).find('.frappe-list').remove();
- me.$main_section.empty();
- }
-
- make_reconciliation_tool() {
- const me = this;
- frappe.model.with_doctype("Bank Transaction", () => {
- erpnext.accounts.ReconciliationList = new erpnext.accounts.ReconciliationTool({
- parent: me.parent,
- doctype: "Bank Transaction"
- });
- })
- }
-}
-
-
-erpnext.accounts.bankTransactionUpload = class bankTransactionUpload {
- constructor(parent) {
- this.parent = parent;
- this.data = [];
-
- const assets = [
- "/assets/frappe/css/frappe-datatable.css",
- "/assets/frappe/js/lib/clusterize.min.js",
- "/assets/frappe/js/lib/Sortable.min.js",
- "/assets/frappe/js/lib/frappe-datatable.js"
- ];
-
- frappe.require(assets, () => {
- this.make();
- });
- }
-
- make() {
- const me = this;
- new frappe.ui.FileUploader({
- method: 'erpnext.accounts.doctype.bank_transaction.bank_transaction_upload.upload_bank_statement',
- allow_multiple: 0,
- on_success: function(attachment, r) {
- if (!r.exc && r.message) {
- me.data = r.message;
- me.setup_transactions_dom();
- me.create_datatable();
- me.add_primary_action();
- }
- }
- })
- }
-
- setup_transactions_dom() {
- const me = this;
- me.parent.$main_section.append('<div class="transactions-table"></div>');
- }
-
- create_datatable() {
- try {
- this.datatable = new DataTable('.transactions-table', {
- columns: this.data.columns,
- data: this.data.data
- })
- }
- catch(err) {
- let msg = __("Your file could not be processed. It should be a standard CSV or XLSX file with headers in the first row.");
- frappe.throw(msg)
- }
-
- }
-
- add_primary_action() {
- const me = this;
- me.parent.page.set_primary_action(__("Submit"), function() {
- me.add_bank_entries()
- }, null, __("Creating bank entries..."))
- }
-
- add_bank_entries() {
- const me = this;
- frappe.xcall('erpnext.accounts.doctype.bank_transaction.bank_transaction_upload.create_bank_entries',
- {columns: this.datatable.datamanager.columns, data: this.datatable.datamanager.data, bank_account: me.parent.bank_account}
- ).then((result) => {
- let result_title = result.errors == 0 ? __("{0} bank transaction(s) created", [result.success]) : __("{0} bank transaction(s) created and {1} errors", [result.success, result.errors])
- let result_msg = `
- <div class="flex justify-center align-center text-muted" style="height: 50vh; display: flex;">
- <h5 class="text-muted">${result_title}</h5>
- </div>`
- me.parent.page.clear_primary_action();
- me.parent.$main_section.empty();
- me.parent.$main_section.append(result_msg);
- if (result.errors == 0) {
- frappe.show_alert({message:__("All bank transactions have been created"), indicator:'green'});
- } else {
- frappe.show_alert({message:__("Please check the error log for details about the import errors"), indicator:'red'});
- }
- })
- }
-}
-
-erpnext.accounts.bankTransactionSync = class bankTransactionSync {
- constructor(parent) {
- this.parent = parent;
- this.data = [];
-
- this.init_config()
- }
-
- init_config() {
- const me = this;
- frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.get_plaid_configuration')
- .then(result => {
- me.plaid_env = result.plaid_env;
- me.client_name = result.client_name;
- me.link_token = result.link_token;
- me.sync_transactions();
- })
- }
-
- sync_transactions() {
- const me = this;
- frappe.db.get_value("Bank Account", me.parent.bank_account, "bank", (r) => {
- frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.sync_transactions', {
- bank: r.bank,
- bank_account: me.parent.bank_account,
- freeze: true
- })
- .then((result) => {
- let result_title = (result && result.length > 0)
- ? __("{0} bank transaction(s) created", [result.length])
- : __("This bank account is already synchronized");
-
- let result_msg = `
- <div class="flex justify-center align-center text-muted" style="height: 50vh; display: flex;">
- <h5 class="text-muted">${result_title}</h5>
- </div>`
-
- this.parent.$main_section.append(result_msg)
- frappe.show_alert({ message: __("Bank account '{0}' has been synchronized", [me.parent.bank_account]), indicator: 'green' });
- })
- })
- }
-}
-
-
-erpnext.accounts.ReconciliationTool = class ReconciliationTool extends frappe.views.BaseList {
- constructor(opts) {
- super(opts);
- this.show();
- }
-
- setup_defaults() {
- super.setup_defaults();
-
- this.page_title = __("Bank Reconciliation");
- this.doctype = 'Bank Transaction';
- this.fields = ['date', 'description', 'debit', 'credit', 'currency']
-
- }
-
- setup_view() {
- this.render_header();
- }
-
- setup_side_bar() {
- //
- }
-
- make_standard_filters() {
- //
- }
-
- freeze() {
- this.$result.find('.list-count').html(`<span>${__('Refreshing')}...</span>`);
- }
-
- get_args() {
- const args = super.get_args();
-
- return Object.assign({}, args, {
- ...args.filters.push(["Bank Transaction", "docstatus", "=", 1],
- ["Bank Transaction", "unallocated_amount", ">", 0])
- });
-
- }
-
- update_data(r) {
- let data = r.message || [];
-
- if (this.start === 0) {
- this.data = data;
- } else {
- this.data = this.data.concat(data);
- }
- }
-
- render() {
- const me = this;
- this.$result.find('.list-row-container').remove();
- $('[data-fieldname="name"]').remove();
- me.data.map((value) => {
- const row = $('<div class="list-row-container">').data("data", value).appendTo(me.$result).get(0);
- new erpnext.accounts.ReconciliationRow(row, value);
- })
- }
-
- render_header() {
- const me = this;
- if ($(this.wrapper).find('.transaction-header').length === 0) {
- me.$result.append(frappe.render_template("bank_transaction_header"));
- }
- }
-}
-
-erpnext.accounts.ReconciliationRow = class ReconciliationRow {
- constructor(row, data) {
- this.data = data;
- this.row = row;
- this.make();
- this.bind_events();
- }
-
- make() {
- $(this.row).append(frappe.render_template("bank_transaction_row", this.data))
- }
-
- bind_events() {
- const me = this;
- $(me.row).on('click', '.clickable-section', function() {
- me.bank_entry = $(this).attr("data-name");
- me.show_dialog($(this).attr("data-name"));
- })
-
- $(me.row).on('click', '.new-reconciliation', function() {
- me.bank_entry = $(this).attr("data-name");
- me.show_dialog($(this).attr("data-name"));
- })
-
- $(me.row).on('click', '.new-payment', function() {
- me.bank_entry = $(this).attr("data-name");
- me.new_payment();
- })
-
- $(me.row).on('click', '.new-invoice', function() {
- me.bank_entry = $(this).attr("data-name");
- me.new_invoice();
- })
-
- $(me.row).on('click', '.new-expense', function() {
- me.bank_entry = $(this).attr("data-name");
- me.new_expense();
- })
- }
-
- new_payment() {
- const me = this;
- const paid_amount = me.data.credit > 0 ? me.data.credit : me.data.debit;
- const payment_type = me.data.credit > 0 ? "Receive": "Pay";
- const party_type = me.data.credit > 0 ? "Customer": "Supplier";
-
- frappe.new_doc("Payment Entry", {"payment_type": payment_type, "paid_amount": paid_amount,
- "party_type": party_type, "paid_from": me.data.bank_account})
- }
-
- new_invoice() {
- const me = this;
- const invoice_type = me.data.credit > 0 ? "Sales Invoice" : "Purchase Invoice";
-
- frappe.new_doc(invoice_type)
- }
-
- new_expense() {
- frappe.new_doc("Expense Claim")
- }
-
-
- show_dialog(data) {
- const me = this;
-
- frappe.db.get_value("Bank Account", me.data.bank_account, "account", (r) => {
- me.gl_account = r.account;
- })
-
- frappe.xcall('erpnext.accounts.page.bank_reconciliation.bank_reconciliation.get_linked_payments',
- { bank_transaction: data, freeze: true, freeze_message: __("Finding linked payments") }
- ).then((result) => {
- me.make_dialog(result)
- })
- }
-
- make_dialog(data) {
- const me = this;
- me.selected_payment = null;
-
- const fields = [
- {
- fieldtype: 'Section Break',
- fieldname: 'section_break_1',
- label: __('Automatic Reconciliation')
- },
- {
- fieldtype: 'HTML',
- fieldname: 'payment_proposals'
- },
- {
- fieldtype: 'Section Break',
- fieldname: 'section_break_2',
- label: __('Search for a payment')
- },
- {
- fieldtype: 'Link',
- fieldname: 'payment_doctype',
- options: 'DocType',
- label: 'Payment DocType',
- get_query: () => {
- return {
- filters : {
- "name": ["in", ["Payment Entry", "Journal Entry", "Sales Invoice", "Purchase Invoice", "Expense Claim"]]
- }
- }
- },
- },
- {
- fieldtype: 'Column Break',
- fieldname: 'column_break_1',
- },
- {
- fieldtype: 'Dynamic Link',
- fieldname: 'payment_entry',
- options: 'payment_doctype',
- label: 'Payment Document',
- get_query: () => {
- let dt = this.dialog.fields_dict.payment_doctype.value;
- if (dt === "Payment Entry") {
- return {
- query: "erpnext.accounts.page.bank_reconciliation.bank_reconciliation.payment_entry_query",
- filters : {
- "bank_account": this.data.bank_account,
- "company": this.data.company
- }
- }
- } else if (dt === "Journal Entry") {
- return {
- query: "erpnext.accounts.page.bank_reconciliation.bank_reconciliation.journal_entry_query",
- filters : {
- "bank_account": this.data.bank_account,
- "company": this.data.company
- }
- }
- } else if (dt === "Sales Invoice") {
- return {
- query: "erpnext.accounts.page.bank_reconciliation.bank_reconciliation.sales_invoices_query"
- }
- } else if (dt === "Purchase Invoice") {
- return {
- filters : [
- ["Purchase Invoice", "ifnull(clearance_date, '')", "=", ""],
- ["Purchase Invoice", "docstatus", "=", 1],
- ["Purchase Invoice", "company", "=", this.data.company]
- ]
- }
- } else if (dt === "Expense Claim") {
- return {
- filters : [
- ["Expense Claim", "ifnull(clearance_date, '')", "=", ""],
- ["Expense Claim", "docstatus", "=", 1],
- ["Expense Claim", "company", "=", this.data.company]
- ]
- }
- }
- },
- onchange: function() {
- if (me.selected_payment !== this.value) {
- me.selected_payment = this.value;
- me.display_payment_details(this);
- }
- }
- },
- {
- fieldtype: 'Section Break',
- fieldname: 'section_break_3'
- },
- {
- fieldtype: 'HTML',
- fieldname: 'payment_details'
- },
- ];
-
- me.dialog = new frappe.ui.Dialog({
- title: __("Choose a corresponding payment"),
- fields: fields,
- size: "large"
- });
-
- const proposals_wrapper = me.dialog.fields_dict.payment_proposals.$wrapper;
- if (data && data.length > 0) {
- proposals_wrapper.append(frappe.render_template("linked_payment_header"));
- data.map(value => {
- proposals_wrapper.append(frappe.render_template("linked_payment_row", value))
- })
- } else {
- const empty_data_msg = __("ERPNext could not find any matching payment entry")
- proposals_wrapper.append(`<div class="text-center"><h5 class="text-muted">${empty_data_msg}</h5></div>`)
- }
-
- $(me.dialog.body).on('click', '.reconciliation-btn', (e) => {
- const payment_entry = $(e.target).attr('data-name');
- const payment_doctype = $(e.target).attr('data-doctype');
- frappe.xcall('erpnext.accounts.page.bank_reconciliation.bank_reconciliation.reconcile',
- {bank_transaction: me.bank_entry, payment_doctype: payment_doctype, payment_name: payment_entry})
- .then((result) => {
- setTimeout(function(){
- erpnext.accounts.ReconciliationList.refresh();
- }, 2000);
- me.dialog.hide();
- })
- })
-
- me.dialog.show();
- }
-
- display_payment_details(event) {
- const me = this;
- if (event.value) {
- let dt = me.dialog.fields_dict.payment_doctype.value;
- me.dialog.fields_dict['payment_details'].$wrapper.empty();
- frappe.db.get_doc(dt, event.value)
- .then(doc => {
- let displayed_docs = []
- let payment = []
- if (dt === "Payment Entry") {
- payment.currency = doc.payment_type == "Receive" ? doc.paid_to_account_currency : doc.paid_from_account_currency;
- payment.doctype = dt
- payment.posting_date = doc.posting_date;
- payment.party = doc.party;
- payment.reference_no = doc.reference_no;
- payment.reference_date = doc.reference_date;
- payment.paid_amount = doc.paid_amount;
- payment.name = doc.name;
- displayed_docs.push(payment);
- } else if (dt === "Journal Entry") {
- doc.accounts.forEach(payment => {
- if (payment.account === me.gl_account) {
- payment.doctype = dt;
- payment.posting_date = doc.posting_date;
- payment.party = doc.pay_to_recd_from;
- payment.reference_no = doc.cheque_no;
- payment.reference_date = doc.cheque_date;
- payment.currency = payment.account_currency;
- payment.paid_amount = payment.credit > 0 ? payment.credit : payment.debit;
- payment.name = doc.name;
- displayed_docs.push(payment);
- }
- })
- } else if (dt === "Sales Invoice") {
- doc.payments.forEach(payment => {
- if (payment.clearance_date === null || payment.clearance_date === "") {
- payment.doctype = dt;
- payment.posting_date = doc.posting_date;
- payment.party = doc.customer;
- payment.reference_no = doc.remarks;
- payment.currency = doc.currency;
- payment.paid_amount = payment.amount;
- payment.name = doc.name;
- displayed_docs.push(payment);
- }
- })
- }
-
- const details_wrapper = me.dialog.fields_dict.payment_details.$wrapper;
- details_wrapper.append(frappe.render_template("linked_payment_header"));
- displayed_docs.forEach(payment => {
- details_wrapper.append(frappe.render_template("linked_payment_row", payment));
- })
- })
- }
-
- }
-}
diff --git a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.json b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.json
deleted file mode 100644
index feea368..0000000
--- a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.json
+++ /dev/null
@@ -1,29 +0,0 @@
-{
- "content": null,
- "creation": "2018-11-24 12:03:14.646669",
- "docstatus": 0,
- "doctype": "Page",
- "idx": 0,
- "modified": "2018-11-24 12:03:14.646669",
- "modified_by": "Administrator",
- "module": "Accounts",
- "name": "bank-reconciliation",
- "owner": "Administrator",
- "page_name": "bank-reconciliation",
- "roles": [
- {
- "role": "System Manager"
- },
- {
- "role": "Accounts Manager"
- },
- {
- "role": "Accounts User"
- }
- ],
- "script": null,
- "standard": "Yes",
- "style": null,
- "system_page": 0,
- "title": "Bank Reconciliation"
-}
\ No newline at end of file
diff --git a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.py b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.py
deleted file mode 100644
index 8abe20c..0000000
--- a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.py
+++ /dev/null
@@ -1,369 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe import _
-import difflib
-from frappe.utils import flt
-from six import iteritems
-from erpnext import get_company_currency
-
-@frappe.whitelist()
-def reconcile(bank_transaction, payment_doctype, payment_name):
- transaction = frappe.get_doc("Bank Transaction", bank_transaction)
- payment_entry = frappe.get_doc(payment_doctype, payment_name)
-
- account = frappe.db.get_value("Bank Account", transaction.bank_account, "account")
- gl_entry = frappe.get_doc("GL Entry", dict(account=account, voucher_type=payment_doctype, voucher_no=payment_name))
-
- if payment_doctype == "Payment Entry" and payment_entry.unallocated_amount > transaction.unallocated_amount:
- frappe.throw(_("The unallocated amount of Payment Entry {0} is greater than the Bank Transaction's unallocated amount").format(payment_name))
-
- if transaction.unallocated_amount == 0:
- frappe.throw(_("This bank transaction is already fully reconciled"))
-
- if transaction.credit > 0 and gl_entry.credit > 0:
- frappe.throw(_("The selected payment entry should be linked with a debtor bank transaction"))
-
- if transaction.debit > 0 and gl_entry.debit > 0:
- frappe.throw(_("The selected payment entry should be linked with a creditor bank transaction"))
-
- add_payment_to_transaction(transaction, payment_entry, gl_entry)
-
- return 'reconciled'
-
-def add_payment_to_transaction(transaction, payment_entry, gl_entry):
- gl_amount, transaction_amount = (gl_entry.credit, transaction.debit) if gl_entry.credit > 0 else (gl_entry.debit, transaction.credit)
- allocated_amount = gl_amount if gl_amount <= transaction_amount else transaction_amount
- transaction.append("payment_entries", {
- "payment_document": payment_entry.doctype,
- "payment_entry": payment_entry.name,
- "allocated_amount": allocated_amount
- })
-
- transaction.save()
- transaction.update_allocations()
-
-@frappe.whitelist()
-def get_linked_payments(bank_transaction):
- transaction = frappe.get_doc("Bank Transaction", bank_transaction)
- bank_account = frappe.db.get_values("Bank Account", transaction.bank_account, ["account", "company"], as_dict=True)
-
- # Get all payment entries with a matching amount
- amount_matching = check_matching_amount(bank_account[0].account, bank_account[0].company, transaction)
-
- # Get some data from payment entries linked to a corresponding bank transaction
- description_matching = get_matching_descriptions_data(bank_account[0].company, transaction)
-
- if amount_matching:
- return check_amount_vs_description(amount_matching, description_matching)
-
- elif description_matching:
- description_matching = filter(lambda x: not x.get('clearance_date'), description_matching)
- if not description_matching:
- return []
-
- return sorted(list(description_matching), key = lambda x: x["posting_date"], reverse=True)
-
- else:
- return []
-
-def check_matching_amount(bank_account, company, transaction):
- payments = []
- amount = transaction.credit if transaction.credit > 0 else transaction.debit
-
- payment_type = "Receive" if transaction.credit > 0 else "Pay"
- account_from_to = "paid_to" if transaction.credit > 0 else "paid_from"
- currency_field = "paid_to_account_currency as currency" if transaction.credit > 0 else "paid_from_account_currency as currency"
-
- payment_entries = frappe.get_all("Payment Entry", fields=["'Payment Entry' as doctype", "name", "paid_amount", "payment_type", "reference_no", "reference_date",
- "party", "party_type", "posting_date", "{0}".format(currency_field)], filters=[["paid_amount", "like", "{0}%".format(amount)],
- ["docstatus", "=", "1"], ["payment_type", "=", [payment_type, "Internal Transfer"]], ["ifnull(clearance_date, '')", "=", ""], ["{0}".format(account_from_to), "=", "{0}".format(bank_account)]])
-
- jea_side = "debit" if transaction.credit > 0 else "credit"
- journal_entries = frappe.db.sql(f"""
- SELECT
- 'Journal Entry' as doctype, je.name, je.posting_date, je.cheque_no as reference_no,
- jea.account_currency as currency, je.pay_to_recd_from as party, je.cheque_date as reference_date,
- jea.{jea_side}_in_account_currency as paid_amount
- FROM
- `tabJournal Entry Account` as jea
- JOIN
- `tabJournal Entry` as je
- ON
- jea.parent = je.name
- WHERE
- (je.clearance_date is null or je.clearance_date='0000-00-00')
- AND
- jea.account = %(bank_account)s
- AND
- jea.{jea_side}_in_account_currency like %(txt)s
- AND
- je.docstatus = 1
- """, {
- 'bank_account': bank_account,
- 'txt': '%%%s%%' % amount
- }, as_dict=True)
-
- if transaction.credit > 0:
- sales_invoices = frappe.db.sql("""
- SELECT
- 'Sales Invoice' as doctype, si.name, si.customer as party,
- si.posting_date, sip.amount as paid_amount
- FROM
- `tabSales Invoice Payment` as sip
- JOIN
- `tabSales Invoice` as si
- ON
- sip.parent = si.name
- WHERE
- (sip.clearance_date is null or sip.clearance_date='0000-00-00')
- AND
- sip.account = %s
- AND
- sip.amount like %s
- AND
- si.docstatus = 1
- """, (bank_account, amount), as_dict=True)
- else:
- sales_invoices = []
-
- if transaction.debit > 0:
- purchase_invoices = frappe.get_all("Purchase Invoice",
- fields = ["'Purchase Invoice' as doctype", "name", "paid_amount", "supplier as party", "posting_date", "currency"],
- filters=[
- ["paid_amount", "like", "{0}%".format(amount)],
- ["docstatus", "=", "1"],
- ["is_paid", "=", "1"],
- ["ifnull(clearance_date, '')", "=", ""],
- ["cash_bank_account", "=", "{0}".format(bank_account)]
- ]
- )
-
- mode_of_payments = [x["parent"] for x in frappe.db.get_list("Mode of Payment Account",
- filters={"default_account": bank_account}, fields=["parent"])]
-
- company_currency = get_company_currency(company)
-
- expense_claims = frappe.get_all("Expense Claim",
- fields=["'Expense Claim' as doctype", "name", "total_sanctioned_amount as paid_amount",
- "employee as party", "posting_date", "'{0}' as currency".format(company_currency)],
- filters=[
- ["total_sanctioned_amount", "like", "{0}%".format(amount)],
- ["docstatus", "=", "1"],
- ["is_paid", "=", "1"],
- ["ifnull(clearance_date, '')", "=", ""],
- ["mode_of_payment", "in", "{0}".format(tuple(mode_of_payments))]
- ]
- )
- else:
- purchase_invoices = expense_claims = []
-
- for data in [payment_entries, journal_entries, sales_invoices, purchase_invoices, expense_claims]:
- if data:
- payments.extend(data)
-
- return payments
-
-def get_matching_descriptions_data(company, transaction):
- if not transaction.description :
- return []
-
- bank_transactions = frappe.db.sql("""
- SELECT
- bt.name, bt.description, bt.date, btp.payment_document, btp.payment_entry
- FROM
- `tabBank Transaction` as bt
- LEFT JOIN
- `tabBank Transaction Payments` as btp
- ON
- bt.name = btp.parent
- WHERE
- bt.allocated_amount > 0
- AND
- bt.docstatus = 1
- """, as_dict=True)
-
- selection = []
- for bank_transaction in bank_transactions:
- if bank_transaction.description:
- seq=difflib.SequenceMatcher(lambda x: x == " ", transaction.description, bank_transaction.description)
-
- if seq.ratio() > 0.6:
- bank_transaction["ratio"] = seq.ratio()
- selection.append(bank_transaction)
-
- document_types = set([x["payment_document"] for x in selection])
-
- links = {}
- for document_type in document_types:
- links[document_type] = [x["payment_entry"] for x in selection if x["payment_document"]==document_type]
-
-
- data = []
- company_currency = get_company_currency(company)
- for key, value in iteritems(links):
- if key == "Payment Entry":
- data.extend(frappe.get_all("Payment Entry", filters=[["name", "in", value]],
- fields=["'Payment Entry' as doctype", "posting_date", "party", "reference_no",
- "reference_date", "paid_amount", "paid_to_account_currency as currency", "clearance_date"]))
- if key == "Journal Entry":
- journal_entries = frappe.get_all("Journal Entry", filters=[["name", "in", value]],
- fields=["name", "'Journal Entry' as doctype", "posting_date",
- "pay_to_recd_from as party", "cheque_no as reference_no", "cheque_date as reference_date",
- "total_credit as paid_amount", "clearance_date"])
- for journal_entry in journal_entries:
- journal_entry_accounts = frappe.get_all("Journal Entry Account", filters={"parenttype": journal_entry["doctype"], "parent": journal_entry["name"]}, fields=["account_currency"])
- journal_entry["currency"] = journal_entry_accounts[0]["account_currency"] if journal_entry_accounts else company_currency
- data.extend(journal_entries)
- if key == "Sales Invoice":
- data.extend(frappe.get_all("Sales Invoice", filters=[["name", "in", value]], fields=["'Sales Invoice' as doctype", "posting_date", "customer_name as party", "paid_amount", "currency"]))
- if key == "Purchase Invoice":
- data.extend(frappe.get_all("Purchase Invoice", filters=[["name", "in", value]], fields=["'Purchase Invoice' as doctype", "posting_date", "supplier_name as party", "paid_amount", "currency"]))
- if key == "Expense Claim":
- expense_claims = frappe.get_all("Expense Claim", filters=[["name", "in", value]], fields=["'Expense Claim' as doctype", "posting_date", "employee_name as party", "total_amount_reimbursed as paid_amount"])
- data.extend([dict(x,**{"currency": company_currency}) for x in expense_claims])
-
- return data
-
-def check_amount_vs_description(amount_matching, description_matching):
- result = []
-
- if description_matching:
- for am_match in amount_matching:
- for des_match in description_matching:
- if des_match.get("clearance_date"):
- continue
-
- if am_match["party"] == des_match["party"]:
- if am_match not in result:
- result.append(am_match)
- continue
-
- if "reference_no" in am_match and "reference_no" in des_match:
- # Sequence Matcher does not handle None as input
- am_reference = am_match["reference_no"] or ""
- des_reference = des_match["reference_no"] or ""
-
- if difflib.SequenceMatcher(lambda x: x == " ", am_reference, des_reference).ratio() > 70:
- if am_match not in result:
- result.append(am_match)
- if result:
- return sorted(result, key = lambda x: x["posting_date"], reverse=True)
- else:
- return sorted(amount_matching, key = lambda x: x["posting_date"], reverse=True)
-
- else:
- return sorted(amount_matching, key = lambda x: x["posting_date"], reverse=True)
-
-def get_matching_transactions_payments(description_matching):
- payments = [x["payment_entry"] for x in description_matching]
-
- payment_by_ratio = {x["payment_entry"]: x["ratio"] for x in description_matching}
-
- if payments:
- reference_payment_list = frappe.get_all("Payment Entry", fields=["name", "paid_amount", "payment_type", "reference_no", "reference_date",
- "party", "party_type", "posting_date", "paid_to_account_currency"], filters=[["name", "in", payments]])
-
- return sorted(reference_payment_list, key=lambda x: payment_by_ratio[x["name"]])
-
- else:
- return []
-
-@frappe.whitelist()
-@frappe.validate_and_sanitize_search_inputs
-def payment_entry_query(doctype, txt, searchfield, start, page_len, filters):
- account = frappe.db.get_value("Bank Account", filters.get("bank_account"), "account")
- if not account:
- return
-
- return frappe.db.sql("""
- SELECT
- name, party, paid_amount, received_amount, reference_no
- FROM
- `tabPayment Entry`
- WHERE
- (clearance_date is null or clearance_date='0000-00-00')
- AND (paid_from = %(account)s or paid_to = %(account)s)
- AND (name like %(txt)s or party like %(txt)s)
- AND docstatus = 1
- ORDER BY
- if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999), name
- LIMIT
- %(start)s, %(page_len)s""",
- {
- 'txt': "%%%s%%" % txt,
- '_txt': txt.replace("%", ""),
- 'start': start,
- 'page_len': page_len,
- 'account': account
- }
- )
-
-@frappe.whitelist()
-@frappe.validate_and_sanitize_search_inputs
-def journal_entry_query(doctype, txt, searchfield, start, page_len, filters):
- account = frappe.db.get_value("Bank Account", filters.get("bank_account"), "account")
-
- return frappe.db.sql("""
- SELECT
- jea.parent, je.pay_to_recd_from,
- if(jea.debit_in_account_currency > 0, jea.debit_in_account_currency, jea.credit_in_account_currency)
- FROM
- `tabJournal Entry Account` as jea
- LEFT JOIN
- `tabJournal Entry` as je
- ON
- jea.parent = je.name
- WHERE
- (je.clearance_date is null or je.clearance_date='0000-00-00')
- AND
- jea.account = %(account)s
- AND
- (jea.parent like %(txt)s or je.pay_to_recd_from like %(txt)s)
- AND
- je.docstatus = 1
- ORDER BY
- if(locate(%(_txt)s, jea.parent), locate(%(_txt)s, jea.parent), 99999),
- jea.parent
- LIMIT
- %(start)s, %(page_len)s""",
- {
- 'txt': "%%%s%%" % txt,
- '_txt': txt.replace("%", ""),
- 'start': start,
- 'page_len': page_len,
- 'account': account
- }
- )
-
-@frappe.whitelist()
-@frappe.validate_and_sanitize_search_inputs
-def sales_invoices_query(doctype, txt, searchfield, start, page_len, filters):
- return frappe.db.sql("""
- SELECT
- sip.parent, si.customer, sip.amount, sip.mode_of_payment
- FROM
- `tabSales Invoice Payment` as sip
- LEFT JOIN
- `tabSales Invoice` as si
- ON
- sip.parent = si.name
- WHERE
- (sip.clearance_date is null or sip.clearance_date='0000-00-00')
- AND
- (sip.parent like %(txt)s or si.customer like %(txt)s)
- ORDER BY
- if(locate(%(_txt)s, sip.parent), locate(%(_txt)s, sip.parent), 99999),
- sip.parent
- LIMIT
- %(start)s, %(page_len)s""",
- {
- 'txt': "%%%s%%" % txt,
- '_txt': txt.replace("%", ""),
- 'start': start,
- 'page_len': page_len
- }
- )
diff --git a/erpnext/accounts/page/bank_reconciliation/bank_transaction_header.html b/erpnext/accounts/page/bank_reconciliation/bank_transaction_header.html
deleted file mode 100644
index 94f183b..0000000
--- a/erpnext/accounts/page/bank_reconciliation/bank_transaction_header.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<div class="transaction-header">
- <div class="level list-row list-row-head text-muted small">
- <div class="col-sm-2 ellipsis hidden-xs">
- {{ __("Date") }}
- </div>
- <div class="col-xs-11 col-sm-4 ellipsis list-subject">
- {{ __("Description") }}
- </div>
- <div class="col-sm-2 ellipsis hidden-xs">
- {{ __("Debit") }}
- </div>
- <div class="col-sm-2 ellipsis hidden-xs">
- {{ __("Credit") }}
- </div>
- <div class="col-sm-1 ellipsis hidden-xs">
- {{ __("Currency") }}
- </div>
- <div class="col-sm-1 ellipsis">
- </div>
- </div>
-</div>
diff --git a/erpnext/accounts/page/bank_reconciliation/bank_transaction_row.html b/erpnext/accounts/page/bank_reconciliation/bank_transaction_row.html
deleted file mode 100644
index 742b84c..0000000
--- a/erpnext/accounts/page/bank_reconciliation/bank_transaction_row.html
+++ /dev/null
@@ -1,36 +0,0 @@
-<div class="list-row transaction-item">
- <div>
- <div class="clickable-section" data-name={{ name }}>
- <div class="col-sm-2 ellipsis hidden-xs">
- {%= frappe.datetime.str_to_user(date) %}
- </div>
- <div class="col-xs-8 col-sm-4 ellipsis list-subject">
- {{ description }}
- </div>
- <div class="col-sm-2 ellipsis hidden-xs">
- {%= format_currency(debit, currency) %}
- </div>
- <div class="col-sm-2 ellipsis hidden-xs">
- {%= format_currency(credit, currency) %}
- </div>
- <div class="col-sm-1 ellipsis hidden-xs">
- {{ currency }}
- </div>
- </div>
- <div class="col-xs-3 col-sm-1">
- <div class="btn-group">
- <a class="dropdown-toggle btn btn-default btn-xs" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
- <span>Actions </span>
- <span class="caret"></span>
- </a>
- <ul class="dropdown-menu reports-dropdown" style="max-height: 300px; overflow-y: auto; right: 0px; left: auto;">
- <li><a class="new-reconciliation" data-name={{ name }}>{{ __("Reconcile") }}</a></li>
- <li class="divider"></li>
- <li><a class="new-payment" data-name={{ name }}>{{ __("New Payment") }}</a></li>
- <li><a class="new-invoice" data-name={{ name }}>{{ __("New Invoice") }}</a></li>
- <li><a class="new-expense" data-name={{ name }}>{{ __("New Expense") }}</a></li>
- </ul>
- </div>
- </div>
- </div>
-</div>
diff --git a/erpnext/accounts/page/bank_reconciliation/linked_payment_header.html b/erpnext/accounts/page/bank_reconciliation/linked_payment_header.html
deleted file mode 100644
index 4542c36..0000000
--- a/erpnext/accounts/page/bank_reconciliation/linked_payment_header.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<div class="transaction-header">
- <div class="level list-row list-row-head text-muted small">
- <div class="col-xs-3 col-sm-2 ellipsis">
- {{ __("Payment Name") }}
- </div>
- <div class="col-xs-3 col-sm-2 ellipsis">
- {{ __("Reference Date") }}
- </div>
- <div class="col-sm-2 ellipsis hidden-xs">
- {{ __("Amount") }}
- </div>
- <div class="col-sm-2 ellipsis hidden-xs">
- {{ __("Party") }}
- </div>
- <div class="col-xs-3 col-sm-2 ellipsis">
- {{ __("Reference Number") }}
- </div>
- <div class="col-xs-2 col-sm-2">
- </div>
- </div>
-</div>
diff --git a/erpnext/accounts/page/bank_reconciliation/linked_payment_row.html b/erpnext/accounts/page/bank_reconciliation/linked_payment_row.html
deleted file mode 100644
index bdbc9fc..0000000
--- a/erpnext/accounts/page/bank_reconciliation/linked_payment_row.html
+++ /dev/null
@@ -1,36 +0,0 @@
-<div class="list-row">
- <div>
- <div class="col-xs-3 col-sm-2 ellipsis">
- {{ name }}
- </div>
- <div class="col-xs-3 col-sm-2 ellipsis">
- {% if (typeof reference_date !== "undefined") %}
- {%= frappe.datetime.str_to_user(reference_date) %}
- {% else %}
- {% if (typeof posting_date !== "undefined") %}
- {%= frappe.datetime.str_to_user(posting_date) %}
- {% endif %}
- {% endif %}
- </div>
- <div class="col-sm-2 ellipsis hidden-xs">
- {{ format_currency(paid_amount, currency) }}
- </div>
- <div class="col-sm-2 ellipsis hidden-xs">
- {% if (typeof party !== "undefined") %}
- {{ party }}
- {% endif %}
- </div>
- <div class="col-xs-3 col-sm-2 ellipsis">
- {% if (typeof reference_no !== "undefined") %}
- {{ reference_no }}
- {% else %}
- {{ "" }}
- {% endif %}
- </div>
- <div class="col-xs-2 col-sm-2">
- <div class="text-right margin-bottom">
- <button class="btn btn-primary btn-xs reconciliation-btn" data-doctype="{{ doctype }}" data-name="{{ name }}">{{ __("Reconcile") }}</button>
- </div>
- </div>
- </div>
-</div>
\ No newline at end of file
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 60d1e20..89a05b1 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -897,18 +897,18 @@
frappe.db.sql("""delete from `tabGL Entry`
where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
-
if not warehouse_account:
warehouse_account = get_warehouse_account_map(company)
- gle = get_voucherwise_gl_entries(stock_vouchers, posting_date)
+ precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit")) or 2
+ gle = get_voucherwise_gl_entries(stock_vouchers, posting_date)
for voucher_type, voucher_no in stock_vouchers:
existing_gle = gle.get((voucher_type, voucher_no), [])
- voucher_obj = frappe.get_doc(voucher_type, voucher_no)
+ voucher_obj = frappe.get_cached_doc(voucher_type, voucher_no)
expected_gle = voucher_obj.get_gl_entries(warehouse_account)
if expected_gle:
- if not existing_gle or not compare_existing_and_expected_gle(existing_gle, expected_gle):
+ if not existing_gle or not compare_existing_and_expected_gle(existing_gle, expected_gle, precision):
_delete_gl_entries(voucher_type, voucher_no)
voucher_obj.make_gl_entries(gl_entries=expected_gle, from_repost=True)
else:
@@ -954,16 +954,17 @@
return gl_entries
-def compare_existing_and_expected_gle(existing_gle, expected_gle):
+def compare_existing_and_expected_gle(existing_gle, expected_gle, precision):
matched = True
for entry in expected_gle:
account_existed = False
for e in existing_gle:
if entry.account == e.account:
account_existed = True
- if entry.account == e.account and entry.against_account == e.against_account \
- and (not entry.cost_center or not e.cost_center or entry.cost_center == e.cost_center) \
- and (entry.debit != e.debit or entry.credit != e.credit):
+ if (entry.account == e.account and entry.against_account == e.against_account
+ and (not entry.cost_center or not e.cost_center or entry.cost_center == e.cost_center)
+ and ( flt(entry.debit, precision) != flt(e.debit, precision) or
+ flt(entry.credit, precision) != flt(e.credit, precision))):
matched = False
break
if not account_existed:
diff --git a/erpnext/assets/doctype/asset_category/asset_category.json b/erpnext/assets/doctype/asset_category/asset_category.json
index b7d1226..a25f546 100644
--- a/erpnext/assets/doctype/asset_category/asset_category.json
+++ b/erpnext/assets/doctype/asset_category/asset_category.json
@@ -19,7 +19,6 @@
],
"fields": [
{
- "depends_on": "eval:!doc.asset_category_name",
"fieldname": "asset_category_name",
"fieldtype": "Data",
"in_list_view": 1,
@@ -67,7 +66,7 @@
}
],
"links": [],
- "modified": "2021-01-22 12:31:14.425319",
+ "modified": "2021-02-24 15:05:38.621803",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset Category",
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index ea9659c..e0031c9 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -74,7 +74,7 @@
gl_list = []
warehouse_with_no_account = []
- precision = frappe.get_precision("GL Entry", "debit_in_account_currency")
+ precision = self.get_debit_field_precision()
for item_row in voucher_details:
sle_list = sle_map.get(item_row.name)
@@ -131,7 +131,13 @@
if frappe.db.get_value("Warehouse", wh, "company"):
frappe.throw(_("Warehouse {0} is not linked to any account, please mention the account in the warehouse record or set default inventory account in company {1}.").format(wh, self.company))
- return process_gl_map(gl_list)
+ return process_gl_map(gl_list, precision=precision)
+
+ def get_debit_field_precision(self):
+ if not frappe.flags.debit_field_precision:
+ frappe.flags.debit_field_precision = frappe.get_precision("GL Entry", "debit_in_account_currency")
+
+ return frappe.flags.debit_field_precision
def update_stock_ledger_entries(self, sle):
sle.valuation_rate = get_valuation_rate(sle.item_code, sle.warehouse,
@@ -244,7 +250,7 @@
.format(item.idx, frappe.bold(item.item_code), msg), title=_("Expense Account Missing"))
else:
- is_expense_account = frappe.db.get_value("Account",
+ is_expense_account = frappe.get_cached_value("Account",
item.get("expense_account"), "report_type")=="Profit and Loss"
if self.doctype not in ("Purchase Receipt", "Purchase Invoice", "Stock Reconciliation", "Stock Entry") and not is_expense_account:
frappe.throw(_("Expense / Difference account ({0}) must be a 'Profit or Loss' account")
@@ -493,7 +499,7 @@
elif not is_reposting_pending():
check_if_stock_and_account_balance_synced(self.posting_date,
self.company, self.doctype, self.name)
-
+
def is_reposting_pending():
return frappe.db.exists("Repost Item Valuation",
{'docstatus': 1, 'status': ['in', ['Queued','In Progress']]})
diff --git a/erpnext/crm/doctype/opportunity/opportunity.js b/erpnext/crm/doctype/opportunity/opportunity.js
index 08958b7..ac374a9 100644
--- a/erpnext/crm/doctype/opportunity/opportunity.js
+++ b/erpnext/crm/doctype/opportunity/opportunity.js
@@ -24,6 +24,12 @@
frm.trigger('set_contact_link');
}
},
+ contact_date: function(frm) {
+ if(frm.doc.contact_date < frappe.datetime.now_datetime()){
+ frm.set_value("contact_date", "");
+ frappe.throw(__("Next follow up date should be greater than now."))
+ }
+ },
onload_post_render: function(frm) {
frm.get_field("items").grid.set_multiple_add("item_code", "qty");
diff --git a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py
index cc75a0a..148c1a6 100644
--- a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py
+++ b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py
@@ -117,7 +117,7 @@
return response
except Exception as e:
delay = math.pow(4, x) * 125
- frappe.log_error(message=e, title=str(mws_method))
+ frappe.log_error(message=e, title=f'Method "{mws_method.__name__}" failed')
time.sleep(delay)
continue
diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
index 70c7f3f..21f6fee 100644
--- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
+++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
@@ -204,8 +204,8 @@
"date": getdate(transaction["date"]),
"status": status,
"bank_account": bank_account,
- "debit": debit,
- "credit": credit,
+ "deposit": debit,
+ "withdrawal": credit,
"currency": transaction["iso_currency_code"],
"transaction_id": transaction["transaction_id"],
"reference_number": transaction["payment_meta"]["reference_number"],
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 347da6b..59639ff 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -272,6 +272,9 @@
'Address': {
'validate': ['erpnext.regional.india.utils.validate_gstin_for_india', 'erpnext.regional.italy.utils.set_state_code', 'erpnext.regional.india.utils.update_gst_category']
},
+ 'Supplier': {
+ 'validate': 'erpnext.regional.india.utils.validate_pan_for_india'
+ },
('Sales Invoice', 'Sales Order', 'Delivery Note', 'Purchase Invoice', 'Purchase Order', 'Purchase Receipt'): {
'validate': ['erpnext.regional.india.utils.set_place_of_supply']
},
@@ -390,6 +393,15 @@
communication_doctypes = ["Customer", "Supplier"]
+accounting_dimension_doctypes = ["GL Entry", "Sales Invoice", "Purchase Invoice", "Payment Entry", "Asset",
+ "Expense Claim", "Expense Claim Detail", "Expense Taxes and Charges", "Stock Entry", "Budget", "Payroll Entry", "Delivery Note",
+ "Sales Invoice Item", "Purchase Invoice Item", "Purchase Order Item", "Journal Entry Account", "Material Request Item", "Delivery Note Item",
+ "Purchase Receipt Item", "Stock Entry Detail", "Payment Entry Deduction", "Sales Taxes and Charges", "Purchase Taxes and Charges", "Shipping Rule",
+ "Landed Cost Item", "Asset Value Adjustment", "Loyalty Program", "Fee Schedule", "Fee Structure", "Stock Reconciliation",
+ "Travel Request", "Fees", "POS Profile", "Opening Invoice Creation Tool", "Opening Invoice Creation Tool Item", "Subscription",
+ "Subscription Plan"
+]
+
regional_overrides = {
'France': {
'erpnext.tests.test_regional.test_method': 'erpnext.regional.france.utils.test_method'
diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.json b/erpnext/hr/doctype/hr_settings/hr_settings.json
index f999635..d8aae66 100644
--- a/erpnext/hr/doctype/hr_settings/hr_settings.json
+++ b/erpnext/hr/doctype/hr_settings/hr_settings.json
@@ -138,7 +138,7 @@
"idx": 1,
"issingle": 1,
"links": [],
- "modified": "2020-08-27 14:30:28.995324",
+ "modified": "2021-02-25 12:31:14.947865",
"modified_by": "Administrator",
"module": "HR",
"name": "HR Settings",
@@ -155,5 +155,6 @@
}
],
"sort_field": "modified",
- "sort_order": "ASC"
-}
+ "sort_order": "ASC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/leave_application/leave_application_dashboard.html b/erpnext/hr/doctype/leave_application/leave_application_dashboard.html
index 6324b04..9f667a6 100644
--- a/erpnext/hr/doctype/leave_application/leave_application_dashboard.html
+++ b/erpnext/hr/doctype/leave_application/leave_application_dashboard.html
@@ -4,11 +4,11 @@
<thead>
<tr>
<th style="width: 16%">{{ __("Leave Type") }}</th>
- <th style="width: 16%" class="text-right">{{ __("Total Allocated Leaves") }}</th>
- <th style="width: 16%" class="text-right">{{ __("Expired Leaves") }}</th>
- <th style="width: 16%" class="text-right">{{ __("Used Leaves") }}</th>
- <th style="width: 16%" class="text-right">{{ __("Pending Leaves") }}</th>
- <th style="width: 16%" class="text-right">{{ __("Available Leaves") }}</th>
+ <th style="width: 16%" class="text-right">{{ __("Total Allocated Leave") }}</th>
+ <th style="width: 16%" class="text-right">{{ __("Expired Leave") }}</th>
+ <th style="width: 16%" class="text-right">{{ __("Used Leave") }}</th>
+ <th style="width: 16%" class="text-right">{{ __("Pending Leave") }}</th>
+ <th style="width: 16%" class="text-right">{{ __("Available Leave") }}</th>
</tr>
</thead>
<tbody>
@@ -25,5 +25,5 @@
</tbody>
</table>
{% else %}
-<p style="margin-top: 30px;"> No Leaves have been allocated. </p>
-{% endif %}
\ No newline at end of file
+<p style="margin-top: 30px;"> No Leave has been allocated. </p>
+{% endif %}
diff --git a/erpnext/hr/doctype/skill/skill.json b/erpnext/hr/doctype/skill/skill.json
index 5182973..a10381f 100644
--- a/erpnext/hr/doctype/skill/skill.json
+++ b/erpnext/hr/doctype/skill/skill.json
@@ -3,7 +3,7 @@
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
- "allow_rename": 0,
+ "allow_rename": 1,
"autoname": "field:skill_name",
"beta": 0,
"creation": "2019-04-16 09:54:39.486915",
@@ -56,7 +56,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2019-04-16 09:55:00.536328",
+ "modified": "2021-02-24 09:55:00.536328",
"modified_by": "Administrator",
"module": "HR",
"name": "Skill",
@@ -110,4 +110,4 @@
"track_changes": 1,
"track_seen": 0,
"track_views": 0
-}
\ No newline at end of file
+}
diff --git a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py
index 1b92358..06f9160 100644
--- a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py
+++ b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py
@@ -40,17 +40,17 @@
'fieldname': 'opening_balance',
'width': 130,
}, {
- 'label': _('Leaves Allocated'),
+ 'label': _('Leave Allocated'),
'fieldtype': 'float',
'fieldname': 'leaves_allocated',
'width': 130,
}, {
- 'label': _('Leaves Taken'),
+ 'label': _('Leave Taken'),
'fieldtype': 'float',
'fieldname': 'leaves_taken',
'width': 130,
}, {
- 'label': _('Leaves Expired'),
+ 'label': _('Leave Expired'),
'fieldtype': 'float',
'fieldname': 'leaves_expired',
'width': 130,
diff --git a/erpnext/loan_management/dashboard_chart/loan_disbursements/loan_disbursements.json b/erpnext/loan_management/dashboard_chart/loan_disbursements/loan_disbursements.json
new file mode 100644
index 0000000..b8abf21
--- /dev/null
+++ b/erpnext/loan_management/dashboard_chart/loan_disbursements/loan_disbursements.json
@@ -0,0 +1,29 @@
+{
+ "based_on": "disbursement_date",
+ "chart_name": "Loan Disbursements",
+ "chart_type": "Sum",
+ "creation": "2021-02-06 18:40:36.148470",
+ "docstatus": 0,
+ "doctype": "Dashboard Chart",
+ "document_type": "Loan Disbursement",
+ "dynamic_filters_json": "[]",
+ "filters_json": "[[\"Loan Disbursement\",\"docstatus\",\"=\",\"1\",false]]",
+ "group_by_type": "Count",
+ "idx": 0,
+ "is_public": 0,
+ "is_standard": 1,
+ "modified": "2021-02-06 18:40:49.308663",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "Loan Disbursements",
+ "number_of_groups": 0,
+ "owner": "Administrator",
+ "source": "",
+ "time_interval": "Daily",
+ "timeseries": 1,
+ "timespan": "Last Month",
+ "type": "Line",
+ "use_report_chart": 0,
+ "value_based_on": "disbursed_amount",
+ "y_axis": []
+}
\ No newline at end of file
diff --git a/erpnext/loan_management/dashboard_chart/loan_interest_accrual/loan_interest_accrual.json b/erpnext/loan_management/dashboard_chart/loan_interest_accrual/loan_interest_accrual.json
new file mode 100644
index 0000000..aa0f78a
--- /dev/null
+++ b/erpnext/loan_management/dashboard_chart/loan_interest_accrual/loan_interest_accrual.json
@@ -0,0 +1,31 @@
+{
+ "based_on": "posting_date",
+ "chart_name": "Loan Interest Accrual",
+ "chart_type": "Sum",
+ "color": "#39E4A5",
+ "creation": "2021-02-18 20:07:04.843876",
+ "docstatus": 0,
+ "doctype": "Dashboard Chart",
+ "document_type": "Loan Interest Accrual",
+ "dynamic_filters_json": "[]",
+ "filters_json": "[[\"Loan Interest Accrual\",\"docstatus\",\"=\",\"1\",false]]",
+ "group_by_type": "Count",
+ "idx": 0,
+ "is_public": 0,
+ "is_standard": 1,
+ "last_synced_on": "2021-02-21 21:01:26.022634",
+ "modified": "2021-02-21 21:01:44.930712",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "Loan Interest Accrual",
+ "number_of_groups": 0,
+ "owner": "Administrator",
+ "source": "",
+ "time_interval": "Monthly",
+ "timeseries": 1,
+ "timespan": "Last Year",
+ "type": "Line",
+ "use_report_chart": 0,
+ "value_based_on": "interest_amount",
+ "y_axis": []
+}
\ No newline at end of file
diff --git a/erpnext/loan_management/dashboard_chart/new_loans/new_loans.json b/erpnext/loan_management/dashboard_chart/new_loans/new_loans.json
new file mode 100644
index 0000000..35bd43b
--- /dev/null
+++ b/erpnext/loan_management/dashboard_chart/new_loans/new_loans.json
@@ -0,0 +1,31 @@
+{
+ "based_on": "creation",
+ "chart_name": "New Loans",
+ "chart_type": "Count",
+ "color": "#449CF0",
+ "creation": "2021-02-06 16:59:27.509170",
+ "docstatus": 0,
+ "doctype": "Dashboard Chart",
+ "document_type": "Loan",
+ "dynamic_filters_json": "[]",
+ "filters_json": "[[\"Loan\",\"docstatus\",\"=\",\"1\",false]]",
+ "group_by_type": "Count",
+ "idx": 0,
+ "is_public": 0,
+ "is_standard": 1,
+ "last_synced_on": "2021-02-21 20:55:33.515025",
+ "modified": "2021-02-21 21:00:33.900821",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "New Loans",
+ "number_of_groups": 0,
+ "owner": "Administrator",
+ "source": "",
+ "time_interval": "Daily",
+ "timeseries": 1,
+ "timespan": "Last Month",
+ "type": "Bar",
+ "use_report_chart": 0,
+ "value_based_on": "",
+ "y_axis": []
+}
\ No newline at end of file
diff --git a/erpnext/loan_management/dashboard_chart/top_10_pledged_loan_securities/top_10_pledged_loan_securities.json b/erpnext/loan_management/dashboard_chart/top_10_pledged_loan_securities/top_10_pledged_loan_securities.json
new file mode 100644
index 0000000..76c27b0
--- /dev/null
+++ b/erpnext/loan_management/dashboard_chart/top_10_pledged_loan_securities/top_10_pledged_loan_securities.json
@@ -0,0 +1,31 @@
+{
+ "based_on": "",
+ "chart_name": "Top 10 Pledged Loan Securities",
+ "chart_type": "Custom",
+ "color": "#EC864B",
+ "creation": "2021-02-06 22:02:46.284479",
+ "docstatus": 0,
+ "doctype": "Dashboard Chart",
+ "document_type": "",
+ "dynamic_filters_json": "[]",
+ "filters_json": "[]",
+ "group_by_type": "Count",
+ "idx": 0,
+ "is_public": 0,
+ "is_standard": 1,
+ "last_synced_on": "2021-02-21 21:00:57.043034",
+ "modified": "2021-02-21 21:01:10.048623",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "Top 10 Pledged Loan Securities",
+ "number_of_groups": 0,
+ "owner": "Administrator",
+ "source": "Top 10 Pledged Loan Securities",
+ "time_interval": "Yearly",
+ "timeseries": 0,
+ "timespan": "Last Year",
+ "type": "Bar",
+ "use_report_chart": 0,
+ "value_based_on": "",
+ "y_axis": []
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/bank_statement_settings/__init__.py b/erpnext/loan_management/dashboard_chart_source/__init__.py
similarity index 100%
copy from erpnext/accounts/doctype/bank_statement_settings/__init__.py
copy to erpnext/loan_management/dashboard_chart_source/__init__.py
diff --git a/erpnext/accounts/doctype/bank_statement_settings/__init__.py b/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/__init__.py
similarity index 100%
copy from erpnext/accounts/doctype/bank_statement_settings/__init__.py
copy to erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/__init__.py
diff --git a/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.js b/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.js
new file mode 100644
index 0000000..cf75cc8
--- /dev/null
+++ b/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.js
@@ -0,0 +1,14 @@
+frappe.provide('frappe.dashboards.chart_sources');
+
+frappe.dashboards.chart_sources["Top 10 Pledged Loan Securities"] = {
+ method: "erpnext.loan_management.dashboard_chart_source.top_10_pledged_loan_securities.top_10_pledged_loan_securities.get_data",
+ filters: [
+ {
+ fieldname: "company",
+ label: __("Company"),
+ fieldtype: "Link",
+ options: "Company",
+ default: frappe.defaults.get_user_default("Company")
+ }
+ ]
+};
\ No newline at end of file
diff --git a/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.json b/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.json
new file mode 100644
index 0000000..42c9b1c
--- /dev/null
+++ b/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.json
@@ -0,0 +1,13 @@
+{
+ "creation": "2021-02-06 22:01:01.332628",
+ "docstatus": 0,
+ "doctype": "Dashboard Chart Source",
+ "idx": 0,
+ "modified": "2021-02-06 22:01:01.332628",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "Top 10 Pledged Loan Securities",
+ "owner": "Administrator",
+ "source_name": "Top 10 Pledged Loan Securities ",
+ "timeseries": 0
+}
\ No newline at end of file
diff --git a/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.py b/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.py
new file mode 100644
index 0000000..6bb0440
--- /dev/null
+++ b/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.py
@@ -0,0 +1,76 @@
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.utils.dashboard import cache_source
+from erpnext.loan_management.report.applicant_wise_loan_security_exposure.applicant_wise_loan_security_exposure \
+ import get_loan_security_details
+from six import iteritems
+
+@frappe.whitelist()
+@cache_source
+def get_data(chart_name = None, chart = None, no_cache = None, filters = None, from_date = None,
+ to_date = None, timespan = None, time_interval = None, heatmap_year = None):
+ if chart_name:
+ chart = frappe.get_doc('Dashboard Chart', chart_name)
+ else:
+ chart = frappe._dict(frappe.parse_json(chart))
+
+ filters = {}
+ current_pledges = {}
+
+ if filters:
+ filters = frappe.parse_json(filters)[0]
+
+ conditions = ""
+ labels = []
+ values = []
+
+ if filters.get('company'):
+ conditions = "AND company = %(company)s"
+
+ loan_security_details = get_loan_security_details()
+
+ unpledges = frappe._dict(frappe.db.sql("""
+ SELECT u.loan_security, sum(u.qty) as qty
+ FROM `tabLoan Security Unpledge` up, `tabUnpledge` u
+ WHERE u.parent = up.name
+ AND up.status = 'Approved'
+ {conditions}
+ GROUP BY u.loan_security
+ """.format(conditions=conditions), filters, as_list=1))
+
+ pledges = frappe._dict(frappe.db.sql("""
+ SELECT p.loan_security, sum(p.qty) as qty
+ FROM `tabLoan Security Pledge` lp, `tabPledge`p
+ WHERE p.parent = lp.name
+ AND lp.status = 'Pledged'
+ {conditions}
+ GROUP BY p.loan_security
+ """.format(conditions=conditions), filters, as_list=1))
+
+ for security, qty in iteritems(pledges):
+ current_pledges.setdefault(security, qty)
+ current_pledges[security] -= unpledges.get(security, 0.0)
+
+ sorted_pledges = dict(sorted(current_pledges.items(), key=lambda item: item[1], reverse=True))
+
+ count = 0
+ for security, qty in iteritems(sorted_pledges):
+ values.append(qty * loan_security_details.get(security, {}).get('latest_price', 0))
+ labels.append(security)
+ count +=1
+
+ ## Just need top 10 securities
+ if count == 10:
+ break
+
+ return {
+ 'labels': labels,
+ 'datasets': [{
+ 'name': 'Top 10 Securities',
+ 'chartType': 'bar',
+ 'values': values
+ }]
+ }
\ No newline at end of file
diff --git a/erpnext/loan_management/doctype/loan/loan.py b/erpnext/loan_management/doctype/loan/loan.py
index e607d4f..83a813f 100644
--- a/erpnext/loan_management/doctype/loan/loan.py
+++ b/erpnext/loan_management/doctype/loan/loan.py
@@ -201,7 +201,9 @@
write_off_limit = frappe.get_value('Loan Type', loan_type, 'write_off_amount')
# checking greater than 0 as there may be some minor precision error
- if pending_amount < write_off_limit:
+ if not pending_amount:
+ frappe.db.set_value('Loan', loan, 'status', 'Loan Closure Requested')
+ elif pending_amount < write_off_limit:
# Auto create loan write off and update status as loan closure requested
write_off = make_loan_write_off(loan)
write_off.submit()
@@ -348,3 +350,13 @@
if employee_currency != company_currency:
frappe.throw(_("Loan cannot be repayed from salary for Employee {0} because salary is processed in currency {1}")
.format(applicant, employee_currency))
+
+@frappe.whitelist()
+def get_shortfall_applicants():
+ loans = frappe.get_all('Loan Security Shortfall', {'status': 'Pending'}, pluck='loan')
+ applicants = set(frappe.get_all('Loan', {'name': ('in', loans)}, pluck='name'))
+
+ return {
+ "value": len(applicants),
+ "fieldtype": "Int"
+ }
\ No newline at end of file
diff --git a/erpnext/loan_management/doctype/loan/test_loan.py b/erpnext/loan_management/doctype/loan/test_loan.py
index f3c9db6..13a2094 100644
--- a/erpnext/loan_management/doctype/loan/test_loan.py
+++ b/erpnext/loan_management/doctype/loan/test_loan.py
@@ -547,7 +547,7 @@
# 30 days - grace period
penalty_days = 30 - 4
- penalty_applicable_amount = flt(amounts['interest_amount']/2, 2)
+ penalty_applicable_amount = flt(amounts['interest_amount']/2)
penalty_amount = flt((((penalty_applicable_amount * 25) / 100) * penalty_days), 2)
process = process_loan_interest_accrual_for_demand_loans(posting_date = '2019-11-30')
diff --git a/erpnext/loan_management/doctype/loan_application/loan_application.py b/erpnext/loan_management/doctype/loan_application/loan_application.py
index e59db4c..9c0147e 100644
--- a/erpnext/loan_management/doctype/loan_application/loan_application.py
+++ b/erpnext/loan_management/doctype/loan_application/loan_application.py
@@ -197,7 +197,7 @@
security.qty = cint(security.amount/security.loan_security_price)
security.amount = security.qty * security.loan_security_price
- security.post_haircut_amount = security.amount - (security.amount * security.haircut/100)
+ security.post_haircut_amount = cint(security.amount - (security.amount * security.haircut/100))
maximum_loan_amount += security.post_haircut_amount
diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py
index 7d7992d..7978350 100644
--- a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py
+++ b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py
@@ -246,7 +246,5 @@
if not posting_date:
posting_date = getdate()
- precision = cint(frappe.db.get_default("currency_precision")) or 2
-
- return flt((principal_amount * rate_of_interest) / (days_in_year(get_datetime(posting_date).year) * 100), precision)
+ return flt((principal_amount * rate_of_interest) / (days_in_year(get_datetime(posting_date).year) * 100))
diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
index ac30c91..bac06c4 100644
--- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
+++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
@@ -81,8 +81,8 @@
last_accrual_date = get_last_accrual_date(self.against_loan)
# get posting date upto which interest has to be accrued
- per_day_interest = flt(get_per_day_interest(self.pending_principal_amount,
- self.rate_of_interest, self.posting_date), 2)
+ per_day_interest = get_per_day_interest(self.pending_principal_amount,
+ self.rate_of_interest, self.posting_date)
no_of_days = flt(flt(self.total_interest_paid - self.interest_payable,
precision)/per_day_interest, 0) - 1
@@ -105,8 +105,6 @@
})
def update_paid_amount(self):
- precision = cint(frappe.db.get_default("currency_precision")) or 2
-
loan = frappe.get_doc("Loan", self.against_loan)
for payment in self.repayment_details:
@@ -114,7 +112,7 @@
SET paid_principal_amount = `paid_principal_amount` + %s,
paid_interest_amount = `paid_interest_amount` + %s
WHERE name = %s""",
- (flt(payment.paid_principal_amount, precision), flt(payment.paid_interest_amount, precision), payment.loan_interest_accrual))
+ (flt(payment.paid_principal_amount), flt(payment.paid_interest_amount), payment.loan_interest_accrual))
frappe.db.sql(""" UPDATE `tabLoan` SET total_amount_paid = %s, total_principal_paid = %s
WHERE name = %s """, (loan.total_amount_paid + self.amount_paid,
@@ -148,8 +146,6 @@
frappe.db.set_value("Loan", self.against_loan, "status", "Disbursed")
def allocate_amounts(self, repayment_details):
- precision = cint(frappe.db.get_default("currency_precision")) or 2
-
self.set('repayment_details', [])
self.principal_amount_paid = 0
total_interest_paid = 0
@@ -185,21 +181,18 @@
# no of days for which to accrue interest
# Interest can only be accrued for an entire day and not partial
if interest_paid > repayment_details['unaccrued_interest']:
- per_day_interest = flt(get_per_day_interest(self.pending_principal_amount,
- self.rate_of_interest, self.posting_date), precision)
interest_paid -= repayment_details['unaccrued_interest']
total_interest_paid += repayment_details['unaccrued_interest']
else:
# get no of days for which interest can be paid
- per_day_interest = flt(get_per_day_interest(self.pending_principal_amount,
- self.rate_of_interest, self.posting_date), precision)
+ per_day_interest = get_per_day_interest(self.pending_principal_amount,
+ self.rate_of_interest, self.posting_date)
no_of_days = cint(interest_paid/per_day_interest)
total_interest_paid += no_of_days * per_day_interest
interest_paid -= no_of_days * per_day_interest
self.total_interest_paid = total_interest_paid
-
if interest_paid:
self.principal_amount_paid += interest_paid
@@ -369,7 +362,7 @@
if pending_days > 0:
principal_amount = flt(pending_principal_amount, precision)
per_day_interest = get_per_day_interest(principal_amount, loan_type_details.rate_of_interest, posting_date)
- unaccrued_interest += (pending_days * flt(per_day_interest, precision))
+ unaccrued_interest += (pending_days * per_day_interest)
amounts["pending_principal_amount"] = flt(pending_principal_amount, precision)
amounts["payable_principal_amount"] = flt(payable_principal_amount, precision)
diff --git a/erpnext/loan_management/loan_management_dashboard/loan_dashboard/loan_dashboard.json b/erpnext/loan_management/loan_management_dashboard/loan_dashboard/loan_dashboard.json
new file mode 100644
index 0000000..e060253
--- /dev/null
+++ b/erpnext/loan_management/loan_management_dashboard/loan_dashboard/loan_dashboard.json
@@ -0,0 +1,70 @@
+{
+ "cards": [
+ {
+ "card": "New Loans"
+ },
+ {
+ "card": "Active Loans"
+ },
+ {
+ "card": "Closed Loans"
+ },
+ {
+ "card": "Total Disbursed"
+ },
+ {
+ "card": "Open Loan Applications"
+ },
+ {
+ "card": "New Loan Applications"
+ },
+ {
+ "card": "Total Sanctioned Amount"
+ },
+ {
+ "card": "Active Securities"
+ },
+ {
+ "card": "Applicants With Unpaid Shortfall"
+ },
+ {
+ "card": "Total Shortfall Amount"
+ },
+ {
+ "card": "Total Repayment"
+ },
+ {
+ "card": "Total Write Off"
+ }
+ ],
+ "charts": [
+ {
+ "chart": "New Loans",
+ "width": "Half"
+ },
+ {
+ "chart": "Loan Disbursements",
+ "width": "Half"
+ },
+ {
+ "chart": "Top 10 Pledged Loan Securities",
+ "width": "Half"
+ },
+ {
+ "chart": "Loan Interest Accrual",
+ "width": "Half"
+ }
+ ],
+ "creation": "2021-02-06 16:52:43.484752",
+ "dashboard_name": "Loan Dashboard",
+ "docstatus": 0,
+ "doctype": "Dashboard",
+ "idx": 0,
+ "is_default": 0,
+ "is_standard": 1,
+ "modified": "2021-02-21 20:53:47.531699",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "Loan Dashboard",
+ "owner": "Administrator"
+}
\ No newline at end of file
diff --git a/erpnext/loan_management/number_card/active_loans/active_loans.json b/erpnext/loan_management/number_card/active_loans/active_loans.json
new file mode 100644
index 0000000..7e0db47
--- /dev/null
+++ b/erpnext/loan_management/number_card/active_loans/active_loans.json
@@ -0,0 +1,23 @@
+{
+ "aggregate_function_based_on": "",
+ "creation": "2021-02-06 17:10:26.132493",
+ "docstatus": 0,
+ "doctype": "Number Card",
+ "document_type": "Loan",
+ "dynamic_filters_json": "[]",
+ "filters_json": "[[\"Loan\",\"docstatus\",\"=\",\"1\",false],[\"Loan\",\"status\",\"in\",[\"Disbursed\",\"Partially Disbursed\",null],false]]",
+ "function": "Count",
+ "idx": 0,
+ "is_public": 0,
+ "is_standard": 1,
+ "label": "Active Loans",
+ "modified": "2021-02-06 17:29:20.304087",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "Active Loans",
+ "owner": "Administrator",
+ "report_function": "Sum",
+ "show_percentage_stats": 1,
+ "stats_time_interval": "Monthly",
+ "type": "Document Type"
+}
\ No newline at end of file
diff --git a/erpnext/loan_management/number_card/active_securities/active_securities.json b/erpnext/loan_management/number_card/active_securities/active_securities.json
new file mode 100644
index 0000000..298e410
--- /dev/null
+++ b/erpnext/loan_management/number_card/active_securities/active_securities.json
@@ -0,0 +1,23 @@
+{
+ "aggregate_function_based_on": "",
+ "creation": "2021-02-06 19:07:21.344199",
+ "docstatus": 0,
+ "doctype": "Number Card",
+ "document_type": "Loan Security",
+ "dynamic_filters_json": "[]",
+ "filters_json": "[[\"Loan Security\",\"disabled\",\"=\",0,false]]",
+ "function": "Count",
+ "idx": 0,
+ "is_public": 0,
+ "is_standard": 1,
+ "label": "Active Securities",
+ "modified": "2021-02-06 19:07:26.671516",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "Active Securities",
+ "owner": "Administrator",
+ "report_function": "Sum",
+ "show_percentage_stats": 1,
+ "stats_time_interval": "Daily",
+ "type": "Document Type"
+}
\ No newline at end of file
diff --git a/erpnext/loan_management/number_card/applicants_with_unpaid_shortfall/applicants_with_unpaid_shortfall.json b/erpnext/loan_management/number_card/applicants_with_unpaid_shortfall/applicants_with_unpaid_shortfall.json
new file mode 100644
index 0000000..3b9eba1
--- /dev/null
+++ b/erpnext/loan_management/number_card/applicants_with_unpaid_shortfall/applicants_with_unpaid_shortfall.json
@@ -0,0 +1,21 @@
+{
+ "creation": "2021-02-07 18:55:12.632616",
+ "docstatus": 0,
+ "doctype": "Number Card",
+ "filters_json": "null",
+ "function": "Count",
+ "idx": 0,
+ "is_public": 0,
+ "is_standard": 1,
+ "label": "Applicants With Unpaid Shortfall",
+ "method": "erpnext.loan_management.doctype.loan.loan.get_shortfall_applicants",
+ "modified": "2021-02-07 21:46:27.369795",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "Applicants With Unpaid Shortfall",
+ "owner": "Administrator",
+ "report_function": "Sum",
+ "show_percentage_stats": 1,
+ "stats_time_interval": "Daily",
+ "type": "Custom"
+}
\ No newline at end of file
diff --git a/erpnext/loan_management/number_card/closed_loans/closed_loans.json b/erpnext/loan_management/number_card/closed_loans/closed_loans.json
new file mode 100644
index 0000000..c2f2244
--- /dev/null
+++ b/erpnext/loan_management/number_card/closed_loans/closed_loans.json
@@ -0,0 +1,23 @@
+{
+ "aggregate_function_based_on": "",
+ "creation": "2021-02-21 19:51:49.261813",
+ "docstatus": 0,
+ "doctype": "Number Card",
+ "document_type": "Loan",
+ "dynamic_filters_json": "[]",
+ "filters_json": "[[\"Loan\",\"docstatus\",\"=\",\"1\",false],[\"Loan\",\"status\",\"=\",\"Closed\",false]]",
+ "function": "Count",
+ "idx": 0,
+ "is_public": 0,
+ "is_standard": 1,
+ "label": "Closed Loans",
+ "modified": "2021-02-21 19:51:54.087903",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "Closed Loans",
+ "owner": "Administrator",
+ "report_function": "Sum",
+ "show_percentage_stats": 1,
+ "stats_time_interval": "Daily",
+ "type": "Document Type"
+}
\ No newline at end of file
diff --git a/erpnext/loan_management/number_card/last_interest_accrual/last_interest_accrual.json b/erpnext/loan_management/number_card/last_interest_accrual/last_interest_accrual.json
new file mode 100644
index 0000000..65c8ce6
--- /dev/null
+++ b/erpnext/loan_management/number_card/last_interest_accrual/last_interest_accrual.json
@@ -0,0 +1,21 @@
+{
+ "creation": "2021-02-07 21:57:14.758007",
+ "docstatus": 0,
+ "doctype": "Number Card",
+ "filters_json": "null",
+ "function": "Count",
+ "idx": 0,
+ "is_public": 0,
+ "is_standard": 1,
+ "label": "Last Interest Accrual",
+ "method": "erpnext.loan_management.doctype.loan.loan.get_last_accrual_date",
+ "modified": "2021-02-07 21:59:47.525197",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "Last Interest Accrual",
+ "owner": "Administrator",
+ "report_function": "Sum",
+ "show_percentage_stats": 1,
+ "stats_time_interval": "Daily",
+ "type": "Custom"
+}
\ No newline at end of file
diff --git a/erpnext/loan_management/number_card/new_loan_applications/new_loan_applications.json b/erpnext/loan_management/number_card/new_loan_applications/new_loan_applications.json
new file mode 100644
index 0000000..7e655ff
--- /dev/null
+++ b/erpnext/loan_management/number_card/new_loan_applications/new_loan_applications.json
@@ -0,0 +1,23 @@
+{
+ "aggregate_function_based_on": "",
+ "creation": "2021-02-06 17:59:10.051269",
+ "docstatus": 0,
+ "doctype": "Number Card",
+ "document_type": "Loan Application",
+ "dynamic_filters_json": "[]",
+ "filters_json": "[[\"Loan Application\",\"docstatus\",\"=\",\"1\",false],[\"Loan Application\",\"creation\",\"Timespan\",\"today\",false]]",
+ "function": "Count",
+ "idx": 0,
+ "is_public": 0,
+ "is_standard": 1,
+ "label": "New Loan Applications",
+ "modified": "2021-02-06 17:59:21.880979",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "New Loan Applications",
+ "owner": "Administrator",
+ "report_function": "Sum",
+ "show_percentage_stats": 1,
+ "stats_time_interval": "Daily",
+ "type": "Document Type"
+}
\ No newline at end of file
diff --git a/erpnext/loan_management/number_card/new_loans/new_loans.json b/erpnext/loan_management/number_card/new_loans/new_loans.json
new file mode 100644
index 0000000..424f0f1
--- /dev/null
+++ b/erpnext/loan_management/number_card/new_loans/new_loans.json
@@ -0,0 +1,23 @@
+{
+ "aggregate_function_based_on": "",
+ "creation": "2021-02-06 17:56:34.624031",
+ "docstatus": 0,
+ "doctype": "Number Card",
+ "document_type": "Loan",
+ "dynamic_filters_json": "[]",
+ "filters_json": "[[\"Loan\",\"docstatus\",\"=\",\"1\",false],[\"Loan\",\"creation\",\"Timespan\",\"today\",false]]",
+ "function": "Count",
+ "idx": 0,
+ "is_public": 0,
+ "is_standard": 1,
+ "label": "New Loans",
+ "modified": "2021-02-06 17:58:20.209166",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "New Loans",
+ "owner": "Administrator",
+ "report_function": "Sum",
+ "show_percentage_stats": 1,
+ "stats_time_interval": "Daily",
+ "type": "Document Type"
+}
\ No newline at end of file
diff --git a/erpnext/loan_management/number_card/open_loan_applications/open_loan_applications.json b/erpnext/loan_management/number_card/open_loan_applications/open_loan_applications.json
new file mode 100644
index 0000000..1d5e84e
--- /dev/null
+++ b/erpnext/loan_management/number_card/open_loan_applications/open_loan_applications.json
@@ -0,0 +1,23 @@
+{
+ "aggregate_function_based_on": "",
+ "creation": "2021-02-06 17:23:32.509899",
+ "docstatus": 0,
+ "doctype": "Number Card",
+ "document_type": "Loan Application",
+ "dynamic_filters_json": "[]",
+ "filters_json": "[[\"Loan Application\",\"docstatus\",\"=\",\"1\",false],[\"Loan Application\",\"status\",\"=\",\"Open\",false]]",
+ "function": "Count",
+ "idx": 0,
+ "is_public": 0,
+ "is_standard": 1,
+ "label": "Open Loan Applications",
+ "modified": "2021-02-06 17:29:09.761011",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "Open Loan Applications",
+ "owner": "Administrator",
+ "report_function": "Sum",
+ "show_percentage_stats": 1,
+ "stats_time_interval": "Monthly",
+ "type": "Document Type"
+}
\ No newline at end of file
diff --git a/erpnext/loan_management/number_card/total_disbursed/total_disbursed.json b/erpnext/loan_management/number_card/total_disbursed/total_disbursed.json
new file mode 100644
index 0000000..4a3f869
--- /dev/null
+++ b/erpnext/loan_management/number_card/total_disbursed/total_disbursed.json
@@ -0,0 +1,23 @@
+{
+ "aggregate_function_based_on": "disbursed_amount",
+ "creation": "2021-02-06 16:52:19.505462",
+ "docstatus": 0,
+ "doctype": "Number Card",
+ "document_type": "Loan Disbursement",
+ "dynamic_filters_json": "[]",
+ "filters_json": "[[\"Loan Disbursement\",\"docstatus\",\"=\",\"1\",false]]",
+ "function": "Sum",
+ "idx": 0,
+ "is_public": 0,
+ "is_standard": 1,
+ "label": "Total Disbursed Amount",
+ "modified": "2021-02-06 17:29:38.453870",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "Total Disbursed",
+ "owner": "Administrator",
+ "report_function": "Sum",
+ "show_percentage_stats": 1,
+ "stats_time_interval": "Monthly",
+ "type": "Document Type"
+}
\ No newline at end of file
diff --git a/erpnext/loan_management/number_card/total_repayment/total_repayment.json b/erpnext/loan_management/number_card/total_repayment/total_repayment.json
new file mode 100644
index 0000000..38de42b
--- /dev/null
+++ b/erpnext/loan_management/number_card/total_repayment/total_repayment.json
@@ -0,0 +1,24 @@
+{
+ "aggregate_function_based_on": "amount_paid",
+ "color": "#29CD42",
+ "creation": "2021-02-21 19:27:45.989222",
+ "docstatus": 0,
+ "doctype": "Number Card",
+ "document_type": "Loan Repayment",
+ "dynamic_filters_json": "[]",
+ "filters_json": "[[\"Loan Repayment\",\"docstatus\",\"=\",\"1\",false]]",
+ "function": "Sum",
+ "idx": 0,
+ "is_public": 0,
+ "is_standard": 1,
+ "label": "Total Repayment",
+ "modified": "2021-02-21 19:34:59.656546",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "Total Repayment",
+ "owner": "Administrator",
+ "report_function": "Sum",
+ "show_percentage_stats": 1,
+ "stats_time_interval": "Daily",
+ "type": "Document Type"
+}
\ No newline at end of file
diff --git a/erpnext/loan_management/number_card/total_sanctioned_amount/total_sanctioned_amount.json b/erpnext/loan_management/number_card/total_sanctioned_amount/total_sanctioned_amount.json
new file mode 100644
index 0000000..dfb9d24
--- /dev/null
+++ b/erpnext/loan_management/number_card/total_sanctioned_amount/total_sanctioned_amount.json
@@ -0,0 +1,23 @@
+{
+ "aggregate_function_based_on": "loan_amount",
+ "creation": "2021-02-06 17:05:04.704162",
+ "docstatus": 0,
+ "doctype": "Number Card",
+ "document_type": "Loan",
+ "dynamic_filters_json": "[]",
+ "filters_json": "[[\"Loan\",\"docstatus\",\"=\",\"1\",false],[\"Loan\",\"status\",\"=\",\"Sanctioned\",false]]",
+ "function": "Sum",
+ "idx": 0,
+ "is_public": 0,
+ "is_standard": 1,
+ "label": "Total Sanctioned Amount",
+ "modified": "2021-02-06 17:29:29.930557",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "Total Sanctioned Amount",
+ "owner": "Administrator",
+ "report_function": "Sum",
+ "show_percentage_stats": 1,
+ "stats_time_interval": "Monthly",
+ "type": "Document Type"
+}
\ No newline at end of file
diff --git a/erpnext/loan_management/number_card/total_shortfall_amount/total_shortfall_amount.json b/erpnext/loan_management/number_card/total_shortfall_amount/total_shortfall_amount.json
new file mode 100644
index 0000000..aa6b093
--- /dev/null
+++ b/erpnext/loan_management/number_card/total_shortfall_amount/total_shortfall_amount.json
@@ -0,0 +1,23 @@
+{
+ "aggregate_function_based_on": "shortfall_amount",
+ "creation": "2021-02-09 08:07:20.096995",
+ "docstatus": 0,
+ "doctype": "Number Card",
+ "document_type": "Loan Security Shortfall",
+ "dynamic_filters_json": "[]",
+ "filters_json": "[]",
+ "function": "Sum",
+ "idx": 0,
+ "is_public": 0,
+ "is_standard": 1,
+ "label": "Total Unpaid Shortfall Amount",
+ "modified": "2021-02-09 08:09:00.355547",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "Total Shortfall Amount",
+ "owner": "Administrator",
+ "report_function": "Sum",
+ "show_percentage_stats": 1,
+ "stats_time_interval": "Daily",
+ "type": "Document Type"
+}
\ No newline at end of file
diff --git a/erpnext/loan_management/number_card/total_write_off/total_write_off.json b/erpnext/loan_management/number_card/total_write_off/total_write_off.json
new file mode 100644
index 0000000..c85169a
--- /dev/null
+++ b/erpnext/loan_management/number_card/total_write_off/total_write_off.json
@@ -0,0 +1,24 @@
+{
+ "aggregate_function_based_on": "write_off_amount",
+ "color": "#CB2929",
+ "creation": "2021-02-21 19:48:29.004429",
+ "docstatus": 0,
+ "doctype": "Number Card",
+ "document_type": "Loan Write Off",
+ "dynamic_filters_json": "[]",
+ "filters_json": "[[\"Loan Write Off\",\"docstatus\",\"=\",\"1\",false]]",
+ "function": "Sum",
+ "idx": 0,
+ "is_public": 0,
+ "is_standard": 1,
+ "label": "Total Write Off",
+ "modified": "2021-02-21 19:48:58.604159",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "Total Write Off",
+ "owner": "Administrator",
+ "report_function": "Sum",
+ "show_percentage_stats": 1,
+ "stats_time_interval": "Daily",
+ "type": "Document Type"
+}
\ No newline at end of file
diff --git a/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.py b/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.py
index ab586bc..0ccd149 100644
--- a/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.py
+++ b/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.py
@@ -36,7 +36,7 @@
def get_data(filters):
data = []
- loan_security_details = get_loan_security_details(filters)
+ loan_security_details = get_loan_security_details()
pledge_values, total_value_map, applicant_type_map = get_applicant_wise_total_loan_security_qty(filters,
loan_security_details)
@@ -64,7 +64,7 @@
return data
-def get_loan_security_details(filters):
+def get_loan_security_details():
security_detail_map = {}
loan_security_price_map = {}
lsp_validity_map = {}
diff --git a/erpnext/loan_management/report/loan_interest_report/loan_interest_report.py b/erpnext/loan_management/report/loan_interest_report/loan_interest_report.py
index a3e69bb..0f72c3c 100644
--- a/erpnext/loan_management/report/loan_interest_report/loan_interest_report.py
+++ b/erpnext/loan_management/report/loan_interest_report/loan_interest_report.py
@@ -171,7 +171,7 @@
return current_pledges
def get_loan_wise_security_value(filters, current_pledges):
- loan_security_details = get_loan_security_details(filters)
+ loan_security_details = get_loan_security_details()
loan_wise_security_value = {}
for key in current_pledges:
diff --git a/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.py b/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.py
index adc8013..887a86a 100644
--- a/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.py
+++ b/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.py
@@ -35,7 +35,7 @@
def get_data(filters):
data = []
- loan_security_details = get_loan_security_details(filters)
+ loan_security_details = get_loan_security_details()
current_pledges, total_portfolio_value = get_company_wise_loan_security_details(filters, loan_security_details)
currency = erpnext.get_company_currency(filters.get('company'))
@@ -76,7 +76,7 @@
if qty:
security_wise_map[key[1]]['applicant_count'] += 1
- total_portfolio_value += flt(qty * loan_security_details.get(key[1])['latest_price'])
+ total_portfolio_value += flt(qty * loan_security_details.get(key[1], {}).get('latest_price', 0))
return security_wise_map, total_portfolio_value
diff --git a/erpnext/loan_management/workspace/loan_management/loan_management.json b/erpnext/loan_management/workspace/loan_management/loan_management.json
index 2e8b5bf..18559dc 100644
--- a/erpnext/loan_management/workspace/loan_management/loan_management.json
+++ b/erpnext/loan_management/workspace/loan_management/loan_management.json
@@ -10,6 +10,7 @@
"hide_custom": 0,
"icon": "loan",
"idx": 0,
+ "is_default": 0,
"is_standard": 1,
"label": "Loan Management",
"links": [
@@ -219,7 +220,7 @@
"type": "Link"
}
],
- "modified": "2021-01-12 11:27:56.079724",
+ "modified": "2021-02-18 17:31:53.586508",
"modified_by": "Administrator",
"module": "Loan Management",
"name": "Loan Management",
@@ -239,6 +240,12 @@
"label": "Loan",
"link_to": "Loan",
"type": "DocType"
+ },
+ {
+ "doc_view": "",
+ "label": "Dashboard",
+ "link_to": "Loan Dashboard",
+ "type": "Dashboard"
}
]
}
\ No newline at end of file
diff --git a/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py b/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py
index f7b407b..ffd9242 100644
--- a/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py
+++ b/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py
@@ -88,11 +88,11 @@
GROUP BY bom_item.item_code""".format(qty_field=qty_field, table=table, conditions=conditions, bom=bom), as_dict=1)
def get_manufacturer_records():
- details = frappe.get_list('Item Manufacturer', fields = ["manufacturer", "manufacturer_part_no, parent"])
+ details = frappe.get_list('Item Manufacturer', fields = ["manufacturer", "manufacturer_part_no", "parent"])
manufacture_details = frappe._dict()
for detail in details:
dic = manufacture_details.setdefault(detail.get('parent'), {})
dic.setdefault('manufacturer', []).append(detail.get('manufacturer'))
dic.setdefault('manufacturer_part', []).append(detail.get('manufacturer_part_no'))
- return manufacture_details
\ No newline at end of file
+ return manufacture_details
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index e5ee551..ba31fee 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -754,3 +754,5 @@
erpnext.patches.v13_0.add_naming_series_to_old_projects # 1-02-2021
erpnext.patches.v12_0.add_state_code_for_ladakh
erpnext.patches.v13_0.item_reposting_for_incorrect_sl_and_gl
+erpnext.patches.v13_0.delete_old_bank_reconciliation_doctypes
+erpnext.patches.v13_0.update_vehicle_no_reqd_condition
diff --git a/erpnext/patches/v11_0/refactor_autoname_naming.py b/erpnext/patches/v11_0/refactor_autoname_naming.py
index 5dc5d3b..b997ba2 100644
--- a/erpnext/patches/v11_0/refactor_autoname_naming.py
+++ b/erpnext/patches/v11_0/refactor_autoname_naming.py
@@ -20,7 +20,7 @@
'Certified Consultant': 'NPO-CONS-.YYYY.-.#####',
'Chat Room': 'CHAT-ROOM-.#####',
'Compensatory Leave Request': 'HR-CMP-.YY.-.MM.-.#####',
- 'Custom Script': 'SYS-SCR-.#####',
+ 'Client Script': 'SYS-SCR-.#####',
'Employee Benefit Application': 'HR-BEN-APP-.YY.-.MM.-.#####',
'Employee Benefit Application Detail': '',
'Employee Benefit Claim': 'HR-BEN-CLM-.YY.-.MM.-.#####',
diff --git a/erpnext/patches/v11_1/update_bank_transaction_status.py b/erpnext/patches/v11_1/update_bank_transaction_status.py
index 1acdfcc..544bc5e 100644
--- a/erpnext/patches/v11_1/update_bank_transaction_status.py
+++ b/erpnext/patches/v11_1/update_bank_transaction_status.py
@@ -7,9 +7,20 @@
def execute():
frappe.reload_doc("accounts", "doctype", "bank_transaction")
- frappe.db.sql(""" UPDATE `tabBank Transaction`
- SET status = 'Reconciled'
- WHERE
- status = 'Settled' and (debit = allocated_amount or credit = allocated_amount)
- and ifnull(allocated_amount, 0) > 0
- """)
\ No newline at end of file
+ bank_transaction_fields = frappe.get_meta("Bank Transaction").get_valid_columns()
+
+ if 'debit' in bank_transaction_fields:
+ frappe.db.sql(""" UPDATE `tabBank Transaction`
+ SET status = 'Reconciled'
+ WHERE
+ status = 'Settled' and (debit = allocated_amount or credit = allocated_amount)
+ and ifnull(allocated_amount, 0) > 0
+ """)
+
+ elif 'deposit' in bank_transaction_fields:
+ frappe.db.sql(""" UPDATE `tabBank Transaction`
+ SET status = 'Reconciled'
+ WHERE
+ status = 'Settled' and (deposit = allocated_amount or withdrawal = allocated_amount)
+ and ifnull(allocated_amount, 0) > 0
+ """)
\ No newline at end of file
diff --git a/erpnext/patches/v13_0/delete_old_bank_reconciliation_doctypes.py b/erpnext/patches/v13_0/delete_old_bank_reconciliation_doctypes.py
new file mode 100644
index 0000000..af1f6e7
--- /dev/null
+++ b/erpnext/patches/v13_0/delete_old_bank_reconciliation_doctypes.py
@@ -0,0 +1,26 @@
+# Copyright (c) 2019, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+
+import frappe
+from frappe.model.utils.rename_field import rename_field
+
+def execute():
+ doctypes = [
+ "Bank Statement Settings",
+ "Bank Statement Settings Item",
+ "Bank Statement Transaction Entry",
+ "Bank Statement Transaction Invoice Item",
+ "Bank Statement Transaction Payment Item",
+ "Bank Statement Transaction Settings Item",
+ "Bank Statement Transaction Settings",
+ ]
+
+ for doctype in doctypes:
+ frappe.delete_doc("DocType", doctype, force=1)
+
+ frappe.delete_doc("Page", "bank-reconciliation", force=1)
+
+ rename_field("Bank Transaction", "debit", "deposit")
+ rename_field("Bank Transaction", "credit", "withdrawal")
diff --git a/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py b/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py
index f60e0d3..ca04e8a 100644
--- a/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py
+++ b/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py
@@ -1,14 +1,30 @@
import frappe
from frappe import _
+from frappe.utils import getdate, get_time
from erpnext.stock.stock_ledger import update_entries_after
from erpnext.accounts.utils import update_gl_entries_after
def execute():
- data = frappe.db.sql(''' SELECT name, item_code, warehouse, voucher_type, voucher_no, posting_date, posting_time
- from `tabStock Ledger Entry` where creation > '2020-12-26 12:58:55.903836' and is_cancelled = 0
- order by timestamp(posting_date, posting_time) asc, creation asc''', as_dict=1)
+ frappe.reload_doc('stock', 'doctype', 'repost_item_valuation')
- for index, d in enumerate(data):
+ reposting_project_deployed_on = frappe.db.get_value("DocType", "Repost Item Valuation", "creation")
+
+ data = frappe.db.sql('''
+ SELECT
+ name, item_code, warehouse, voucher_type, voucher_no, posting_date, posting_time
+ FROM
+ `tabStock Ledger Entry`
+ WHERE
+ creation > %s
+ and is_cancelled = 0
+ ORDER BY timestamp(posting_date, posting_time) asc, creation asc
+ ''', reposting_project_deployed_on, as_dict=1)
+
+ frappe.db.auto_commit_on_many_writes = 1
+ print("Reposting Stock Ledger Entries...")
+ total_sle = len(data)
+ i = 0
+ for d in data:
update_entries_after({
"item_code": d.item_code,
"warehouse": d.warehouse,
@@ -19,9 +35,16 @@
"sle_id": d.name
}, allow_negative_stock=True)
- frappe.db.auto_commit_on_many_writes = 1
+ i += 1
+ if i%100 == 0:
+ print(i, "/", total_sle)
+
+
+ print("Reposting General Ledger Entries...")
+ posting_date = getdate(reposting_project_deployed_on)
+ posting_time = get_time(reposting_project_deployed_on)
for row in frappe.get_all('Company', filters= {'enable_perpetual_inventory': 1}):
- update_gl_entries_after('2020-12-25', '01:58:55', company=row.name)
+ update_gl_entries_after(posting_date, posting_time, company=row.name)
- frappe.db.auto_commit_on_many_writes = 0
\ No newline at end of file
+ frappe.db.auto_commit_on_many_writes = 0
diff --git a/erpnext/patches/v13_0/update_vehicle_no_reqd_condition.py b/erpnext/patches/v13_0/update_vehicle_no_reqd_condition.py
new file mode 100644
index 0000000..c26cddb
--- /dev/null
+++ b/erpnext/patches/v13_0/update_vehicle_no_reqd_condition.py
@@ -0,0 +1,9 @@
+import frappe
+
+def execute():
+ company = frappe.get_all('Company', filters = {'country': 'India'})
+ if not company:
+ return
+
+ if frappe.db.exists('Custom Field', { 'fieldname': 'vehicle_no' }):
+ frappe.db.set_value('Custom Field', { 'fieldname': 'vehicle_no' }, 'mandatory_depends_on', '')
diff --git a/erpnext/patches/v5_0/replace_renamed_fields_in_custom_scripts_and_print_formats.py b/erpnext/patches/v5_0/replace_renamed_fields_in_custom_scripts_and_print_formats.py
index ef3f1d6..c564f8b 100644
--- a/erpnext/patches/v5_0/replace_renamed_fields_in_custom_scripts_and_print_formats.py
+++ b/erpnext/patches/v5_0/replace_renamed_fields_in_custom_scripts_and_print_formats.py
@@ -9,7 +9,7 @@
# NOTE: sequence is important
renamed_fields = get_all_renamed_fields()
- for dt, script_field, ref_dt_field in (("Custom Script", "script", "dt"), ("Print Format", "html", "doc_type")):
+ for dt, script_field, ref_dt_field in (("Client Script", "script", "dt"), ("Print Format", "html", "doc_type")):
cond1 = " or ".join("""{0} like "%%{1}%%" """.format(script_field, d[0].replace("_", "\\_")) for d in renamed_fields)
cond2 = " and standard = 'No'" if dt == "Print Format" else ""
diff --git a/erpnext/patches/v7_0/remove_doctypes_and_reports.py b/erpnext/patches/v7_0/remove_doctypes_and_reports.py
index 746cae0..2356e2f 100644
--- a/erpnext/patches/v7_0/remove_doctypes_and_reports.py
+++ b/erpnext/patches/v7_0/remove_doctypes_and_reports.py
@@ -7,7 +7,7 @@
where name in('Time Log Batch', 'Time Log Batch Detail', 'Time Log')""")
frappe.db.sql("""delete from `tabDocField` where parent in ('Time Log', 'Time Log Batch')""")
- frappe.db.sql("""update `tabCustom Script` set dt = 'Timesheet' where dt = 'Time Log'""")
+ frappe.db.sql("""update `tabClient Script` set dt = 'Timesheet' where dt = 'Time Log'""")
for data in frappe.db.sql(""" select label, fieldname from `tabCustom Field` where dt = 'Time Log'""", as_dict=1):
custom_field = frappe.get_doc({
diff --git a/erpnext/projects/doctype/project/project.js b/erpnext/projects/doctype/project/project.js
index 3570a0f..077011a 100644
--- a/erpnext/projects/doctype/project/project.js
+++ b/erpnext/projects/doctype/project/project.js
@@ -75,24 +75,27 @@
frm.add_custom_button(__('Cancelled'), () => {
frm.events.set_status(frm, 'Cancelled');
}, __('Set Status'));
- }
- if (frappe.model.can_read("Task")) {
- frm.add_custom_button(__("Gantt Chart"), function () {
- frappe.route_options = {
- "project": frm.doc.name
- };
- frappe.set_route("List", "Task", "Gantt");
- });
- frm.add_custom_button(__("Kanban Board"), () => {
- frappe.call('erpnext.projects.doctype.project.project.create_kanban_board_if_not_exists', {
- project: frm.doc.project_name
- }).then(() => {
- frappe.set_route('List', 'Task', 'Kanban', frm.doc.project_name);
+ if (frappe.model.can_read("Task")) {
+ frm.add_custom_button(__("Gantt Chart"), function () {
+ frappe.route_options = {
+ "project": frm.doc.name
+ };
+ frappe.set_route("List", "Task", "Gantt");
});
- });
+
+ frm.add_custom_button(__("Kanban Board"), () => {
+ frappe.call('erpnext.projects.doctype.project.project.create_kanban_board_if_not_exists', {
+ project: frm.doc.project_name
+ }).then(() => {
+ frappe.set_route('List', 'Task', 'Kanban', frm.doc.project_name);
+ });
+ });
+ }
}
+
+
},
create_duplicate: function(frm) {
@@ -135,4 +138,4 @@
frappe.ui.form.make_quick_entry(doctype, null, null, new_doc);
});
-}
\ No newline at end of file
+}
diff --git a/erpnext/projects/doctype/project/test_project.py b/erpnext/projects/doctype/project/test_project.py
index d85c826..6290538 100644
--- a/erpnext/projects/doctype/project/test_project.py
+++ b/erpnext/projects/doctype/project/test_project.py
@@ -37,7 +37,7 @@
task1 = task_exists("Test Template Task Parent")
if not task1:
- task1 = create_task(subject="Test Template Task Parent", is_group=1, is_template=1, begin=1, duration=1)
+ task1 = create_task(subject="Test Template Task Parent", is_group=1, is_template=1, begin=1, duration=4)
task2 = task_exists("Test Template Task Child 1")
if not task2:
@@ -52,7 +52,7 @@
tasks = frappe.get_all('Task', ['subject','exp_end_date','depends_on_tasks', 'name', 'parent_task'], dict(project=project.name), order_by='creation asc')
self.assertEqual(tasks[0].subject, 'Test Template Task Parent')
- self.assertEqual(getdate(tasks[0].exp_end_date), calculate_end_date(project, 1, 1))
+ self.assertEqual(getdate(tasks[0].exp_end_date), calculate_end_date(project, 1, 4))
self.assertEqual(tasks[1].subject, 'Test Template Task Child 1')
self.assertEqual(getdate(tasks[1].exp_end_date), calculate_end_date(project, 1, 3))
diff --git a/erpnext/projects/doctype/project_template_task/project_template_task.json b/erpnext/projects/doctype/project_template_task/project_template_task.json
index 69530b1..16caaa2 100644
--- a/erpnext/projects/doctype/project_template_task/project_template_task.json
+++ b/erpnext/projects/doctype/project_template_task/project_template_task.json
@@ -20,6 +20,7 @@
},
{
"columns": 6,
+ "fetch_from": "task.subject",
"fieldname": "subject",
"fieldtype": "Read Only",
"in_list_view": 1,
@@ -28,7 +29,7 @@
],
"istable": 1,
"links": [],
- "modified": "2021-01-07 15:13:40.995071",
+ "modified": "2021-02-24 15:18:49.095071",
"modified_by": "Administrator",
"module": "Projects",
"name": "Project Template Task",
diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py
index a2095c9..7116348 100755
--- a/erpnext/projects/doctype/task/task.py
+++ b/erpnext/projects/doctype/task/task.py
@@ -30,6 +30,7 @@
def validate(self):
self.validate_dates()
+ self.validate_parent_expected_end_date()
self.validate_parent_project_dates()
self.validate_progress()
self.validate_status()
@@ -45,6 +46,12 @@
frappe.throw(_("{0} can not be greater than {1}").format(frappe.bold("Actual Start Date"), \
frappe.bold("Actual End Date")))
+ def validate_parent_expected_end_date(self):
+ if self.parent_task:
+ parent_exp_end_date = frappe.db.get_value("Task", self.parent_task, "exp_end_date")
+ if parent_exp_end_date and getdate(self.get("exp_end_date")) > getdate(parent_exp_end_date):
+ frappe.throw(_("Expected End Date should be less than or equal to parent task's Expected End Date {0}.").format(getdate(parent_exp_end_date)))
+
def validate_parent_project_dates(self):
if not self.project or frappe.flags.in_test:
return
diff --git a/erpnext/public/build.json b/erpnext/public/build.json
index 7326238..7a3cb83 100644
--- a/erpnext/public/build.json
+++ b/erpnext/public/build.json
@@ -61,5 +61,10 @@
"selling/page/point_of_sale/pos_past_order_list.js",
"selling/page/point_of_sale/pos_past_order_summary.js",
"selling/page/point_of_sale/pos_controller.js"
+ ],
+ "js/bank-reconciliation-tool.min.js": [
+ "public/js/bank_reconciliation_tool/data_table_manager.js",
+ "public/js/bank_reconciliation_tool/number_card.js",
+ "public/js/bank_reconciliation_tool/dialog_manager.js"
]
}
diff --git a/erpnext/public/js/bank_reconciliation_tool/data_table_manager.js b/erpnext/public/js/bank_reconciliation_tool/data_table_manager.js
new file mode 100644
index 0000000..5bb58fa
--- /dev/null
+++ b/erpnext/public/js/bank_reconciliation_tool/data_table_manager.js
@@ -0,0 +1,220 @@
+frappe.provide("erpnext.accounts.bank_reconciliation");
+
+erpnext.accounts.bank_reconciliation.DataTableManager = class DataTableManager {
+ constructor(opts) {
+ Object.assign(this, opts);
+ this.dialog_manager = new erpnext.accounts.bank_reconciliation.DialogManager(
+ this.company,
+ this.bank_account
+ );
+ this.make_dt();
+ }
+
+ make_dt() {
+ var me = this;
+ frappe.call({
+ method:
+ "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_bank_transactions",
+ args: {
+ bank_account: this.bank_account,
+ },
+ callback: function (response) {
+ me.format_data(response.message);
+ me.get_dt_columns();
+ me.get_datatable();
+ me.set_listeners();
+ },
+ });
+ }
+
+ get_dt_columns() {
+ this.columns = [
+ {
+ name: "Date",
+ editable: false,
+ width: 100,
+ },
+
+ {
+ name: "Party Type",
+ editable: false,
+ width: 95,
+ },
+ {
+ name: "Party",
+ editable: false,
+ width: 100,
+ },
+ {
+ name: "Description",
+ editable: false,
+ width: 350,
+ },
+ {
+ name: "Deposit",
+ editable: false,
+ width: 100,
+ format: (value) =>
+ "<span style='color:green;'>" +
+ format_currency(value, this.currency) +
+ "</span>",
+ },
+ {
+ name: "Withdrawal",
+ editable: false,
+ width: 100,
+ format: (value) =>
+ "<span style='color:red;'>" +
+ format_currency(value, this.currency) +
+ "</span>",
+ },
+ {
+ name: "Unallocated Amount",
+ editable: false,
+ width: 100,
+ format: (value) =>
+ "<span style='color:blue;'>" +
+ format_currency(value, this.currency) +
+ "</span>",
+ },
+ {
+ name: "Reference Number",
+ editable: false,
+ width: 140,
+ },
+ {
+ name: "Actions",
+ editable: false,
+ sortable: false,
+ focusable: false,
+ dropdown: false,
+ width: 80,
+ },
+ ];
+ }
+
+ format_data(transactions) {
+ this.transactions = [];
+ if (transactions[0]) {
+ this.currency = transactions[0]["currency"];
+ }
+ this.transaction_dt_map = {};
+ let length;
+ transactions.forEach((row) => {
+ length = this.transactions.push(this.format_row(row));
+ this.transaction_dt_map[row["name"]] = length - 1;
+ });
+ }
+
+ format_row(row) {
+ return [
+ row["date"],
+ row["party_type"],
+ row["party"],
+ row["description"],
+ row["deposit"],
+ row["withdrawal"],
+ row["unallocated_amount"],
+ row["reference_number"],
+ `
+ <Button class="btn btn-primary btn-xs center" data-name = ${row["name"]} >
+ Actions
+ </a>
+ `,
+ ];
+ }
+
+ get_datatable() {
+ const datatable_options = {
+ columns: this.columns,
+ data: this.transactions,
+ dynamicRowHeight: true,
+ checkboxColumn: false,
+ inlineFilters: true,
+ };
+ this.datatable = new frappe.DataTable(
+ this.$reconciliation_tool_dt.get(0),
+ datatable_options
+ );
+ $(`.${this.datatable.style.scopeClass} .dt-scrollable`).css(
+ "max-height",
+ "calc(100vh - 400px)"
+ );
+
+ if (this.transactions.length > 0) {
+ this.$reconciliation_tool_dt.show();
+ this.$no_bank_transactions.hide();
+ } else {
+ this.$reconciliation_tool_dt.hide();
+ this.$no_bank_transactions.show();
+ }
+ }
+
+ set_listeners() {
+ var me = this;
+ $(`.${this.datatable.style.scopeClass} .dt-scrollable`).on(
+ "click",
+ `.btn`,
+ function () {
+ me.dialog_manager.show_dialog(
+ $(this).attr("data-name"),
+ (bank_transaction) => me.update_dt_cards(bank_transaction)
+ );
+ return true;
+ }
+ );
+ }
+
+ update_dt_cards(bank_transaction) {
+ const transaction_index = this.transaction_dt_map[
+ bank_transaction.name
+ ];
+ if (bank_transaction.unallocated_amount > 0) {
+ this.transactions[transaction_index] = this.format_row(
+ bank_transaction
+ );
+ } else {
+ this.transactions.splice(transaction_index, 1);
+ }
+ this.datatable.refresh(this.transactions, this.columns);
+
+ if (this.transactions.length == 0) {
+ this.$reconciliation_tool_dt.hide();
+ this.$no_bank_transactions.show();
+ }
+
+ // this.make_dt();
+ this.get_cleared_balance().then(() => {
+ this.cards_manager.$cards[1].set_value(
+ format_currency(this.cleared_balance),
+ this.currency
+ );
+ this.cards_manager.$cards[2].set_value(
+ format_currency(
+ this.bank_statement_closing_balance - this.cleared_balance
+ ),
+ this.currency
+ );
+ this.cards_manager.$cards[2].set_value_color(
+ this.bank_statement_closing_balance - this.cleared_balance == 0
+ ? "text-success"
+ : "text-danger"
+ );
+ });
+ }
+
+ get_cleared_balance() {
+ if (this.bank_account && this.bank_statement_to_date) {
+ return frappe.call({
+ method:
+ "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_account_balance",
+ args: {
+ bank_account: this.bank_account,
+ till_date: this.bank_statement_to_date,
+ },
+ callback: (response) =>
+ (this.cleared_balance = response.message),
+ });
+ }
+ }
+};
diff --git a/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js b/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js
new file mode 100644
index 0000000..142fe79
--- /dev/null
+++ b/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js
@@ -0,0 +1,594 @@
+frappe.provide("erpnext.accounts.bank_reconciliation");
+
+erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager {
+ constructor(company, bank_account) {
+ this.bank_account = bank_account;
+ this.company = company;
+ this.make_dialog();
+ }
+
+ show_dialog(bank_transaction_name, update_dt_cards) {
+ this.bank_transaction_name = bank_transaction_name;
+ this.update_dt_cards = update_dt_cards;
+ frappe.call({
+ method: "frappe.client.get_value",
+ args: {
+ doctype: "Bank Transaction",
+ filters: { name: this.bank_transaction_name },
+ fieldname: [
+ "date",
+ "deposit",
+ "withdrawal",
+ "currency",
+ "description",
+ "name",
+ "bank_account",
+ "company",
+ "reference_number",
+ "party_type",
+ "party",
+ "unallocated_amount",
+ "allocated_amount",
+ ],
+ },
+ callback: (r) => {
+ if (r.message) {
+ this.bank_transaction = r.message;
+ r.message.payment_entry = 1;
+ this.dialog.set_values(r.message);
+ this.dialog.show();
+ }
+ },
+ });
+ }
+
+ get_linked_vouchers(document_types) {
+ frappe.call({
+ method:
+ "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_linked_payments",
+ args: {
+ bank_transaction_name: this.bank_transaction_name,
+ document_types: document_types,
+ },
+
+ callback: (result) => {
+ const data = result.message;
+
+
+ if (data && data.length > 0) {
+ const proposals_wrapper = this.dialog.fields_dict.payment_proposals.$wrapper;
+ proposals_wrapper.show();
+ this.dialog.fields_dict.no_matching_vouchers.$wrapper.hide();
+ this.data = [];
+ data.forEach((row) => {
+ const reference_date = row[5] ? row[5] : row[8];
+ this.data.push([
+ row[1],
+ row[2],
+ reference_date,
+ format_currency(row[3], row[9]),
+ row[6],
+ row[4],
+ ]);
+ });
+ this.get_dt_columns();
+ this.get_datatable(proposals_wrapper);
+ } else {
+ const proposals_wrapper = this.dialog.fields_dict.payment_proposals.$wrapper;
+ proposals_wrapper.hide();
+ this.dialog.fields_dict.no_matching_vouchers.$wrapper.show();
+
+ }
+ this.dialog.show();
+ },
+ });
+ }
+
+ get_dt_columns() {
+ this.columns = [
+ {
+ name: "Document Type",
+ editable: false,
+ width: 125,
+ },
+ {
+ name: "Document Name",
+ editable: false,
+ width: 150,
+ },
+ {
+ name: "Reference Date",
+ editable: false,
+ width: 120,
+ },
+ {
+ name: "Amount",
+ editable: false,
+ width: 100,
+ },
+ {
+ name: "Party",
+ editable: false,
+ width: 120,
+ },
+
+ {
+ name: "Reference Number",
+ editable: false,
+ width: 140,
+ },
+ ];
+ }
+
+ get_datatable(proposals_wrapper) {
+ if (!this.datatable) {
+ const datatable_options = {
+ columns: this.columns,
+ data: this.data,
+ dynamicRowHeight: true,
+ checkboxColumn: true,
+ inlineFilters: true,
+ };
+ this.datatable = new frappe.DataTable(
+ proposals_wrapper.get(0),
+ datatable_options
+ );
+ } else {
+ this.datatable.refresh(this.data, this.columns);
+ this.datatable.rowmanager.checkMap = [];
+ }
+ }
+
+ make_dialog() {
+ const me = this;
+ me.selected_payment = null;
+
+ const fields = [
+ {
+ label: __("Action"),
+ fieldname: "action",
+ fieldtype: "Select",
+ options: `Match Against Voucher\nCreate Voucher\nUpdate Bank Transaction`,
+ default: "Match Against Voucher",
+ },
+ {
+ fieldname: "column_break_4",
+ fieldtype: "Column Break",
+ },
+ {
+ label: __("Document Type"),
+ fieldname: "document_type",
+ fieldtype: "Select",
+ options: `Payment Entry\nJournal Entry`,
+ default: "Payment Entry",
+ depends_on: "eval:doc.action=='Create Voucher'",
+ },
+ {
+ fieldtype: "Section Break",
+ fieldname: "section_break_1",
+ label: __("Filters"),
+ depends_on: "eval:doc.action=='Match Against Voucher'",
+ },
+ {
+ fieldtype: "Check",
+ label: "Payment Entry",
+ fieldname: "payment_entry",
+ onchange: () => this.update_options(),
+ },
+ {
+ fieldtype: "Check",
+ label: "Journal Entry",
+ fieldname: "journal_entry",
+ onchange: () => this.update_options(),
+ },
+ {
+ fieldname: "column_break_5",
+ fieldtype: "Column Break",
+ },
+ {
+ fieldtype: "Check",
+ label: "Sales Invoice",
+ fieldname: "sales_invoice",
+ onchange: () => this.update_options(),
+ },
+
+ {
+ fieldtype: "Check",
+ label: "Purchase Invoice",
+ fieldname: "purchase_invoice",
+ onchange: () => this.update_options(),
+ },
+ {
+ fieldname: "column_break_5",
+ fieldtype: "Column Break",
+ },
+ {
+ fieldtype: "Check",
+ label: "Expense Claim",
+ fieldname: "expense_claim",
+ onchange: () => this.update_options(),
+ },
+ {
+ fieldtype: "Check",
+ label: "Show Only Exact Amount",
+ fieldname: "exact_match",
+ onchange: () => this.update_options(),
+ },
+ {
+ fieldtype: "Section Break",
+ fieldname: "section_break_1",
+ label: __("Select Vouchers to Match"),
+ depends_on: "eval:doc.action=='Match Against Voucher'",
+ },
+ {
+ fieldtype: "HTML",
+ fieldname: "payment_proposals",
+ },
+ {
+ fieldtype: "HTML",
+ fieldname: "no_matching_vouchers",
+ options: "<div class='text-muted text-center'>No Matching Vouchers Found</div>"
+ },
+ {
+ fieldtype: "Section Break",
+ fieldname: "details",
+ label: "Details",
+ depends_on: "eval:doc.action!='Match Against Voucher'",
+ },
+ {
+ fieldname: "reference_number",
+ fieldtype: "Data",
+ label: "Reference Number",
+ mandatory_depends_on: "eval:doc.action=='Create Voucher'",
+ },
+ {
+ default: "Today",
+ fieldname: "posting_date",
+ fieldtype: "Date",
+ label: "Posting Date",
+ reqd: 1,
+ depends_on: "eval:doc.action=='Create Voucher'",
+ },
+ {
+ fieldname: "reference_date",
+ fieldtype: "Date",
+ label: "Cheque/Reference Date",
+ mandatory_depends_on: "eval:doc.action=='Create Voucher'",
+ depends_on: "eval:doc.action=='Create Voucher'",
+ reqd: 1,
+ },
+ {
+ fieldname: "mode_of_payment",
+ fieldtype: "Link",
+ label: "Mode of Payment",
+ options: "Mode of Payment",
+ depends_on: "eval:doc.action=='Create Voucher'",
+ },
+ {
+ fieldname: "edit_in_full_page",
+ fieldtype: "Button",
+ label: "Edit in Full Page",
+ click: () => {
+ this.edit_in_full_page();
+ },
+ depends_on:
+ "eval:doc.action=='Create Voucher'",
+ },
+ {
+ fieldname: "column_break_7",
+ fieldtype: "Column Break",
+ },
+ {
+ default: "Journal Entry Type",
+ fieldname: "journal_entry_type",
+ fieldtype: "Select",
+ label: "Journal Entry Type",
+ options:
+ "Journal Entry\nInter Company Journal Entry\nBank Entry\nCash Entry\nCredit Card Entry\nDebit Note\nCredit Note\nContra Entry\nExcise Entry\nWrite Off Entry\nOpening Entry\nDepreciation Entry\nExchange Rate Revaluation\nDeferred Revenue\nDeferred Expense",
+ depends_on:
+ "eval:doc.action=='Create Voucher' && doc.document_type=='Journal Entry'",
+ mandatory_depends_on:
+ "eval:doc.action=='Create Voucher' && doc.document_type=='Journal Entry'",
+ },
+ {
+ fieldname: "second_account",
+ fieldtype: "Link",
+ label: "Account",
+ options: "Account",
+ depends_on:
+ "eval:doc.action=='Create Voucher' && doc.document_type=='Journal Entry'",
+ mandatory_depends_on:
+ "eval:doc.action=='Create Voucher' && doc.document_type=='Journal Entry'",
+ get_query: () => {
+ return {
+ filters: {
+ is_group: 0,
+ company: this.company,
+ },
+ };
+ },
+ },
+ {
+ fieldname: "party_type",
+ fieldtype: "Link",
+ label: "Party Type",
+ options: "DocType",
+ mandatory_depends_on:
+ "eval:doc.action=='Create Voucher' && doc.document_type=='Payment Entry'",
+ get_query: function () {
+ return {
+ filters: {
+ name: [
+ "in",
+ Object.keys(frappe.boot.party_account_types),
+ ],
+ },
+ };
+ },
+ },
+ {
+ fieldname: "party",
+ fieldtype: "Dynamic Link",
+ label: "Party",
+ options: "party_type",
+ mandatory_depends_on:
+ "eval:doc.action=='Create Voucher' && doc.document_type=='Payment Entry'",
+ },
+ {
+ fieldname: "project",
+ fieldtype: "Link",
+ label: "Project",
+ options: "Project",
+ depends_on:
+ "eval:doc.action=='Create Voucher' && doc.document_type=='Payment Entry'",
+ },
+ {
+ fieldname: "cost_center",
+ fieldtype: "Link",
+ label: "Cost Center",
+ options: "Cost Center",
+ depends_on:
+ "eval:doc.action=='Create Voucher' && doc.document_type=='Payment Entry'",
+ },
+ {
+ fieldtype: "Section Break",
+ fieldname: "details_section",
+ label: "Transaction Details",
+ collapsible: 1,
+ },
+ {
+ fieldname: "deposit",
+ fieldtype: "Currency",
+ label: "Deposit",
+ read_only: 1,
+ },
+ {
+ fieldname: "withdrawal",
+ fieldtype: "Currency",
+ label: "Withdrawal",
+ read_only: 1,
+ },
+ {
+ fieldname: "description",
+ fieldtype: "Small Text",
+ label: "Description",
+ read_only: 1,
+ },
+ {
+ fieldname: "column_break_17",
+ fieldtype: "Column Break",
+ read_only: 1,
+ },
+ {
+ fieldname: "allocated_amount",
+ fieldtype: "Currency",
+ label: "Allocated Amount",
+ read_only: 1,
+ },
+
+ {
+ fieldname: "unallocated_amount",
+ fieldtype: "Currency",
+ label: "Unallocated Amount",
+ read_only: 1,
+ },
+ ];
+
+ me.dialog = new frappe.ui.Dialog({
+ title: __("Reconcile the Bank Transaction"),
+ fields: fields,
+ size: "large",
+ primary_action: (values) =>
+ this.reconciliation_dialog_primary_action(values),
+ });
+ }
+
+ get_selected_attributes() {
+ let selected_attributes = [];
+ this.dialog.$wrapper.find(".checkbox input").each((i, col) => {
+ if ($(col).is(":checked")) {
+ selected_attributes.push($(col).attr("data-fieldname"));
+ }
+ });
+
+ return selected_attributes;
+ }
+
+ update_options() {
+ let selected_attributes = this.get_selected_attributes();
+ this.get_linked_vouchers(selected_attributes);
+ }
+
+ reconciliation_dialog_primary_action(values) {
+ if (values.action == "Match Against Voucher") this.match(values);
+ if (
+ values.action == "Create Voucher" &&
+ values.document_type == "Payment Entry"
+ )
+ this.add_payment_entry(values);
+ if (
+ values.action == "Create Voucher" &&
+ values.document_type == "Journal Entry"
+ )
+ this.add_journal_entry(values);
+ else if (values.action == "Update Bank Transaction")
+ this.update_transaction(values);
+ }
+
+ match() {
+ var selected_map = this.datatable.rowmanager.checkMap;
+ let rows = [];
+ selected_map.forEach((val, index) => {
+ if (val == 1) rows.push(this.datatable.datamanager.rows[index]);
+ });
+ let vouchers = [];
+ rows.forEach((x) => {
+ vouchers.push({
+ payment_doctype: x[2].content,
+ payment_name: x[3].content,
+ amount: x[5].content,
+ });
+ });
+ frappe.call({
+ method:
+ "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.reconcile_vouchers",
+ args: {
+ bank_transaction_name: this.bank_transaction.name,
+ vouchers: vouchers,
+ },
+ callback: (response) => {
+ const alert_string =
+ "Bank Transaction " +
+ this.bank_transaction.name +
+ " Matched";
+ frappe.show_alert(alert_string);
+ this.update_dt_cards(response.message);
+ this.dialog.hide();
+ },
+ });
+ }
+
+ add_payment_entry(values) {
+ frappe.call({
+ method:
+ "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.create_payment_entry_bts",
+ args: {
+ bank_transaction_name: this.bank_transaction.name,
+ reference_number: values.reference_number,
+ reference_date: values.reference_date,
+ party_type: values.party_type,
+ party: values.party,
+ posting_date: values.posting_date,
+ mode_of_payment: values.mode_of_payment,
+ project: values.project,
+ cost_center: values.cost_center,
+ },
+ callback: (response) => {
+ const alert_string =
+ "Bank Transaction " +
+ this.bank_transaction.name +
+ " added as Payment Entry";
+ frappe.show_alert(alert_string);
+ this.update_dt_cards(response.message);
+ this.dialog.hide();
+ },
+ });
+ }
+
+ add_journal_entry(values) {
+ frappe.call({
+ method:
+ "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.create_journal_entry_bts",
+ args: {
+ bank_transaction_name: this.bank_transaction.name,
+ reference_number: values.reference_number,
+ reference_date: values.reference_date,
+ party_type: values.party_type,
+ party: values.party,
+ posting_date: values.posting_date,
+ mode_of_payment: values.mode_of_payment,
+ entry_type: values.journal_entry_type,
+ second_account: values.second_account,
+ },
+ callback: (response) => {
+ const alert_string =
+ "Bank Transaction " +
+ this.bank_transaction.name +
+ " added as Journal Entry";
+ frappe.show_alert(alert_string);
+ this.update_dt_cards(response.message);
+ this.dialog.hide();
+ },
+ });
+ }
+
+ update_transaction(values) {
+ frappe.call({
+ method:
+ "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.update_bank_transaction",
+ args: {
+ bank_transaction_name: this.bank_transaction.name,
+ reference_number: values.reference_number,
+ party_type: values.party_type,
+ party: values.party,
+ },
+ callback: (response) => {
+ const alert_string =
+ "Bank Transaction " +
+ this.bank_transaction.name +
+ " updated";
+ frappe.show_alert(alert_string);
+ this.update_dt_cards(response.message);
+ this.dialog.hide();
+ },
+ });
+ }
+
+ edit_in_full_page() {
+ const values = this.dialog.get_values(true);
+ if (values.document_type == "Payment Entry") {
+ frappe.call({
+ method:
+ "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.create_payment_entry_bts",
+ args: {
+ bank_transaction_name: this.bank_transaction.name,
+ reference_number: values.reference_number,
+ reference_date: values.reference_date,
+ party_type: values.party_type,
+ party: values.party,
+ posting_date: values.posting_date,
+ mode_of_payment: values.mode_of_payment,
+ project: values.project,
+ cost_center: values.cost_center,
+ allow_edit: true
+ },
+ callback: (r) => {
+ const doc = frappe.model.sync(r.message);
+ frappe.set_route("Form", doc[0].doctype, doc[0].name);
+ },
+ });
+ } else {
+ frappe.call({
+ method:
+ "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.create_journal_entry_bts",
+ args: {
+ bank_transaction_name: this.bank_transaction.name,
+ reference_number: values.reference_number,
+ reference_date: values.reference_date,
+ party_type: values.party_type,
+ party: values.party,
+ posting_date: values.posting_date,
+ mode_of_payment: values.mode_of_payment,
+ entry_type: values.journal_entry_type,
+ second_account: values.second_account,
+ allow_edit: true
+ },
+ callback: (r) => {
+ var doc = frappe.model.sync(r.message);
+ frappe.set_route("Form", doc[0].doctype, doc[0].name);
+ },
+ });
+ }
+ }
+
+};
diff --git a/erpnext/public/js/bank_reconciliation_tool/number_card.js b/erpnext/public/js/bank_reconciliation_tool/number_card.js
new file mode 100644
index 0000000..e10d109
--- /dev/null
+++ b/erpnext/public/js/bank_reconciliation_tool/number_card.js
@@ -0,0 +1,75 @@
+frappe.provide("erpnext.accounts.bank_reconciliation");
+
+erpnext.accounts.bank_reconciliation.NumberCardManager = class NumberCardManager {
+ constructor(opts) {
+ Object.assign(this, opts);
+ this.make_cards();
+ }
+
+ make_cards() {
+ this.$reconciliation_tool_cards.empty();
+ this.$cards = [];
+ this.$summary = $(`<div class="report-summary"></div>`)
+ .hide()
+ .appendTo(this.$reconciliation_tool_cards);
+ var chart_data = [
+ {
+ value: this.bank_statement_closing_balance,
+ label: "Closing Balance as per Bank Statement",
+ datatype: "Currency",
+ currency: this.currency,
+ },
+ {
+ value: this.cleared_balance,
+ label: "Closing Balance as per ERP",
+ datatype: "Currency",
+ currency: this.currency,
+ },
+ {
+ value:
+ this.bank_statement_closing_balance - this.cleared_balance,
+ label: "Difference",
+ datatype: "Currency",
+ currency: this.currency,
+ },
+ ];
+
+ chart_data.forEach((summary) => {
+ let number_card = new erpnext.accounts.NumberCard(summary);
+ this.$cards.push(number_card);
+
+ number_card.$card.appendTo(this.$summary);
+ });
+ this.$cards[2].set_value_color(
+ this.bank_statement_closing_balance - this.cleared_balance == 0
+ ? "text-success"
+ : "text-danger"
+ );
+ this.$summary.css({"border-bottom": "0px", "margin-left": "0px", "margin-right": "0px"});
+ this.$summary.show();
+ }
+};
+
+erpnext.accounts.NumberCard = class NumberCard {
+ constructor(options) {
+ this.$card = frappe.utils.build_summary_item(options);
+ }
+
+ set_value(value) {
+ this.$card.find("div").text(value);
+ }
+
+ set_value_color(color) {
+ this.$card
+ .find("div")
+ .removeClass("text-danger text-success")
+ .addClass(`${color}`);
+ }
+
+ set_indicator(color) {
+ this.$card
+ .find("span")
+ .removeClass("indicator red green")
+ .addClass(`indicator ${color}`);
+ }
+};
diff --git a/erpnext/public/js/telephony.js b/erpnext/public/js/telephony.js
index 6cb1207..b66126c 100644
--- a/erpnext/public/js/telephony.js
+++ b/erpnext/public/js/telephony.js
@@ -1,13 +1,16 @@
frappe.ui.form.ControlData = frappe.ui.form.ControlData.extend( {
make_input() {
- this._super();
+ if (!this.df.read_only) {
+ this._super();
+ }
if (this.df.options == 'Phone') {
this.setup_phone();
}
},
setup_phone() {
if (frappe.phone_call.handler) {
- this.$wrapper.find('.control-input')
+ let control = this.df.read_only ? '.control-value' : '.control-input';
+ this.$wrapper.find(control)
.append(`
<span class="phone-btn">
<a class="btn-open no-decoration" title="${__('Make a call')}">
diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js
index c39609b..e5bd4d7 100755
--- a/erpnext/public/js/utils.js
+++ b/erpnext/public/js/utils.js
@@ -513,6 +513,7 @@
}, {
fieldtype:'Currency',
fieldname:"rate",
+ options: "currency",
default: 0,
read_only: 0,
in_list_view: 1,
diff --git a/erpnext/regional/india/e_invoice/einvoice.js b/erpnext/regional/india/e_invoice/einvoice.js
index 9fa94c4..a756b57 100644
--- a/erpnext/regional/india/e_invoice/einvoice.js
+++ b/erpnext/regional/india/e_invoice/einvoice.js
@@ -188,7 +188,6 @@
'fieldname': 'vehicle_no',
'label': 'Vehicle No',
'fieldtype': 'Data',
- 'depends_on': 'eval:(doc.mode_of_transport === "Road")',
'default': frm.doc.vehicle_no
},
{
diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py
index 2043f49..eea85cd 100644
--- a/erpnext/regional/india/e_invoice/utils.py
+++ b/erpnext/regional/india/e_invoice/utils.py
@@ -10,6 +10,7 @@
import json
import base64
import frappe
+import six
import traceback
import io
from frappe import _, bold
@@ -108,11 +109,13 @@
pincode = 999999
return frappe._dict(dict(
- gstin=d.gstin, legal_name=d.address_title,
- location=d.city, pincode=d.pincode,
+ gstin=d.gstin,
+ legal_name=sanitize_for_json(d.address_title),
+ location=sanitize_for_json(d.city),
+ pincode=d.pincode,
state_code=d.gst_state_number,
- address_line1=d.address_line1,
- address_line2=d.address_line2
+ address_line1=sanitize_for_json(d.address_line1),
+ address_line2=sanitize_for_json(d.address_line2)
))
def get_gstin_details(gstin):
@@ -146,8 +149,11 @@
)
return frappe._dict(dict(
- gstin='URP', legal_name=address_title, location=city,
- address_line1=address_line1, address_line2=address_line2,
+ gstin='URP',
+ legal_name=sanitize_for_json(address_title),
+ location=city,
+ address_line1=sanitize_for_json(address_line1),
+ address_line2=sanitize_for_json(address_line2),
pincode=999999, state_code=96, place_of_supply=96
))
@@ -160,7 +166,7 @@
item.update(d.as_dict())
item.sr_no = d.idx
- item.description = d.item_name.replace('"', '\\"')
+ item.description = sanitize_for_json(d.item_name)
item.qty = abs(item.qty)
item.discount_amount = 0
@@ -326,7 +332,7 @@
buyer_details = get_overseas_address_details(invoice.customer_address)
else:
buyer_details = get_party_details(invoice.customer_address)
- place_of_supply = get_place_of_supply(invoice, invoice.doctype) or invoice.billing_address_gstin
+ place_of_supply = get_place_of_supply(invoice, invoice.doctype) or sanitize_for_json(invoice.billing_address_gstin)
place_of_supply = place_of_supply[:2]
buyer_details.update(dict(place_of_supply=place_of_supply))
@@ -356,7 +362,7 @@
period_details=period_details, prev_doc_details=prev_doc_details,
export_details=export_details, eway_bill_details=eway_bill_details
)
- einvoice = json.loads(einvoice)
+ einvoice = safe_json_load(einvoice)
validations = json.loads(read_json('einv_validation'))
errors = validate_einvoice(validations, einvoice)
@@ -371,6 +377,18 @@
return einvoice
+def safe_json_load(json_string):
+ JSONDecodeError = ValueError if six.PY2 else json.JSONDecodeError
+
+ try:
+ return json.loads(json_string)
+ except JSONDecodeError as e:
+ # print a snippet of 40 characters around the location where error occured
+ pos = e.pos
+ start, end = max(0, pos-20), min(len(json_string)-1, pos+20)
+ snippet = json_string[start:end]
+ frappe.throw(_("Error in input data. Please check for any special characters near following input: <br> {}").format(snippet))
+
def validate_einvoice(validations, einvoice, errors=[]):
for fieldname, field_validation in validations.items():
value = einvoice.get(fieldname, None)
@@ -798,6 +816,13 @@
self.invoice.flags.ignore_validate = True
self.invoice.save()
+
+def sanitize_for_json(string):
+ """Escape JSON specific characters from a string."""
+
+ # json.dumps adds double-quotes to the string. Indexing to remove them.
+ return json.dumps(string)[1:-1]
+
@frappe.whitelist()
def get_einvoice(doctype, docname):
invoice = frappe.get_doc(doctype, docname)
diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py
index 09bafdd..cb30605 100644
--- a/erpnext/regional/india/utils.py
+++ b/erpnext/regional/india/utils.py
@@ -55,6 +55,14 @@
frappe.throw(_("Invalid GSTIN! First 2 digits of GSTIN should match with State number {0}.")
.format(doc.gst_state_number))
+def validate_pan_for_india(doc, method):
+ if doc.get('country') != 'India' or not doc.pan:
+ return
+
+ p = re.compile("[A-Z]{5}[0-9]{4}[A-Z]{1}")
+ if not p.match(doc.pan):
+ frappe.throw(_("Invalid PAN No. The input you've entered doesn't match the format of PAN."))
+
def validate_tax_category(doc, method):
if doc.get('gst_state') and frappe.db.get_value('Tax Category', {'gst_state': doc.gst_state, 'is_inter_state': doc.is_inter_state}):
if doc.is_inter_state:
diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json
index 3c0652eb..a048928 100644
--- a/erpnext/selling/doctype/customer/customer.json
+++ b/erpnext/selling/doctype/customer/customer.json
@@ -16,6 +16,8 @@
"customer_name",
"gender",
"customer_type",
+ "pan",
+ "tax_withholding_category",
"default_bank_account",
"lead_name",
"image",
@@ -210,7 +212,8 @@
"fieldtype": "Link",
"ignore_user_permissions": 1,
"label": "Represents Company",
- "options": "Company"
+ "options": "Company",
+ "unique": 1
},
{
"depends_on": "represents_company",
@@ -479,13 +482,25 @@
"fieldname": "dn_required",
"fieldtype": "Check",
"label": "Allow Sales Invoice Creation Without Delivery Note"
+ },
+ {
+ "fieldname": "pan",
+ "fieldtype": "Data",
+ "label": "PAN"
+ },
+ {
+ "fieldname": "tax_withholding_category",
+ "fieldtype": "Link",
+ "label": "Tax Withholding Category",
+ "options": "Tax Withholding Category"
}
],
"icon": "fa fa-user",
"idx": 363,
"image_field": "image",
+ "index_web_pages_for_search": 1,
"links": [],
- "modified": "2021-01-06 19:35:25.418017",
+ "modified": "2021-01-27 12:54:57.258959",
"modified_by": "Administrator",
"module": "Selling",
"name": "Customer",
diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json
index fcf7c26..6886c1b 100644
--- a/erpnext/stock/doctype/item/item.json
+++ b/erpnext/stock/doctype/item/item.json
@@ -521,8 +521,7 @@
"fieldname": "has_variants",
"fieldtype": "Check",
"in_standard_filter": 1,
- "label": "Has Variants",
- "no_copy": 1
+ "label": "Has Variants"
},
{
"default": "Item Attribute",
@@ -538,7 +537,6 @@
"fieldtype": "Table",
"hidden": 1,
"label": "Attributes",
- "no_copy": 1,
"options": "Item Variant Attribute"
},
{
@@ -1068,7 +1066,7 @@
"index_web_pages_for_search": 1,
"links": [],
"max_attachments": 1,
- "modified": "2021-01-25 20:49:50.222976",
+ "modified": "2021-02-18 14:00:19.668049",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item",
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index b661570..7b7d2da 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -177,7 +177,7 @@
if not self.valuation_rate and self.standard_rate:
self.valuation_rate = self.standard_rate
- if not self.valuation_rate:
+ if not self.valuation_rate and not self.is_customer_provided_item:
frappe.throw(_("Valuation Rate is mandatory if Opening Stock entered"))
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
diff --git a/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json b/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json
index 471e685..9b1a47e 100644
--- a/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json
+++ b/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json
@@ -7,6 +7,7 @@
"engine": "InnoDB",
"field_order": [
"specification",
+ "parameter_group",
"value",
"numeric",
"column_break_3",
@@ -75,12 +76,20 @@
"in_list_view": 1,
"label": "Numeric",
"width": "80px"
+ },
+ {
+ "fetch_from": "specification.parameter_group",
+ "fieldname": "parameter_group",
+ "fieldtype": "Link",
+ "label": "Parameter Group",
+ "options": "Quality Inspection Parameter Group",
+ "read_only": 1
}
],
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2021-02-01 19:18:46.924399",
+ "modified": "2021-02-04 18:50:02.056173",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item Quality Inspection Parameter",
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index 550c849..d721014 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -295,7 +295,8 @@
"against": warehouse_account[d.warehouse]["account"],
"cost_center": d.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
- "credit": flt(amount["base_amount"]),
+ "credit": (flt(amount["base_amount"]) if (amount["base_amount"] or
+ account_currency!=self.company_currency) else flt(amount["amount"])),
"credit_in_account_currency": flt(amount["amount"]),
"project": d.project
}, item=d))
diff --git a/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.json b/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.json
index 0b5a9b5..418b482 100644
--- a/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.json
+++ b/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.json
@@ -7,24 +7,34 @@
"engine": "InnoDB",
"field_order": [
"parameter",
+ "parameter_group",
"description"
],
"fields": [
{
"fieldname": "parameter",
"fieldtype": "Data",
+ "in_list_view": 1,
"label": "Parameter",
+ "reqd": 1,
"unique": 1
},
{
"fieldname": "description",
"fieldtype": "Text Editor",
"label": "Description"
+ },
+ {
+ "fieldname": "parameter_group",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Parameter Group",
+ "options": "Quality Inspection Parameter Group"
}
],
"index_web_pages_for_search": 1,
"links": [],
- "modified": "2020-12-28 18:06:54.897317",
+ "modified": "2021-02-19 20:33:30.657406",
"modified_by": "Administrator",
"module": "Stock",
"name": "Quality Inspection Parameter",
diff --git a/erpnext/accounts/doctype/bank_statement_settings/__init__.py b/erpnext/stock/doctype/quality_inspection_parameter_group/__init__.py
similarity index 100%
copy from erpnext/accounts/doctype/bank_statement_settings/__init__.py
copy to erpnext/stock/doctype/quality_inspection_parameter_group/__init__.py
diff --git a/erpnext/stock/doctype/quality_inspection_parameter_group/quality_inspection_parameter_group.js b/erpnext/stock/doctype/quality_inspection_parameter_group/quality_inspection_parameter_group.js
new file mode 100644
index 0000000..8716a29
--- /dev/null
+++ b/erpnext/stock/doctype/quality_inspection_parameter_group/quality_inspection_parameter_group.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Quality Inspection Parameter Group', {
+ // refresh: function(frm) {
+
+ // }
+});
diff --git a/erpnext/stock/doctype/quality_inspection_parameter_group/quality_inspection_parameter_group.json b/erpnext/stock/doctype/quality_inspection_parameter_group/quality_inspection_parameter_group.json
new file mode 100644
index 0000000..5726474
--- /dev/null
+++ b/erpnext/stock/doctype/quality_inspection_parameter_group/quality_inspection_parameter_group.json
@@ -0,0 +1,82 @@
+{
+ "actions": [],
+ "autoname": "field:group_name",
+ "creation": "2021-02-04 18:44:12.223295",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "group_name"
+ ],
+ "fields": [
+ {
+ "fieldname": "group_name",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Parameter Group Name",
+ "reqd": 1,
+ "unique": 1
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2021-02-04 18:44:12.223295",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Quality Inspection Parameter Group",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "share": 1,
+ "write": 1
+ },
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Stock User",
+ "share": 1,
+ "write": 1
+ },
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Quality Manager",
+ "share": 1,
+ "write": 1
+ },
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Manufacturing User",
+ "share": 1,
+ "write": 1
+ }
+ ],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/quality_inspection_parameter_group/quality_inspection_parameter_group.py b/erpnext/stock/doctype/quality_inspection_parameter_group/quality_inspection_parameter_group.py
new file mode 100644
index 0000000..1a3b1a0
--- /dev/null
+++ b/erpnext/stock/doctype/quality_inspection_parameter_group/quality_inspection_parameter_group.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class QualityInspectionParameterGroup(Document):
+ pass
diff --git a/erpnext/stock/doctype/quality_inspection_parameter_group/test_quality_inspection_parameter_group.py b/erpnext/stock/doctype/quality_inspection_parameter_group/test_quality_inspection_parameter_group.py
new file mode 100644
index 0000000..212d4b8
--- /dev/null
+++ b/erpnext/stock/doctype/quality_inspection_parameter_group/test_quality_inspection_parameter_group.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+# import frappe
+import unittest
+
+class TestQualityInspectionParameterGroup(unittest.TestCase):
+ pass
diff --git a/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json b/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json
index 35d58ef..0eff5a8 100644
--- a/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json
+++ b/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json
@@ -7,6 +7,7 @@
"engine": "InnoDB",
"field_order": [
"specification",
+ "parameter_group",
"status",
"value",
"numeric",
@@ -210,12 +211,20 @@
"fieldtype": "Check",
"in_list_view": 1,
"label": "Numeric"
+ },
+ {
+ "fetch_from": "specification.parameter_group",
+ "fieldname": "parameter_group",
+ "fieldtype": "Link",
+ "label": "Parameter Group",
+ "options": "Quality Inspection Parameter Group",
+ "read_only": 1
}
],
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2021-02-01 19:46:22.138018",
+ "modified": "2021-02-04 19:15:37.991221",
"modified_by": "Administrator",
"module": "Stock",
"name": "Quality Inspection Reading",
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 9cdc3cf..ea1b387 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -276,9 +276,10 @@
item_wise_qty.setdefault(d.item_code, []).append(d.qty)
for item_code, qty_list in iteritems(item_wise_qty):
- if self.fg_completed_qty != sum(qty_list):
+ total = flt(sum(qty_list), frappe.get_precision("Stock Entry Detail", "qty"))
+ if self.fg_completed_qty != total:
frappe.throw(_("The finished product {0} quantity {1} and For Quantity {2} cannot be different")
- .format(frappe.bold(item_code), frappe.bold(sum(qty_list)), frappe.bold(self.fg_completed_qty)))
+ .format(frappe.bold(item_code), frappe.bold(total), frappe.bold(self.fg_completed_qty)))
def validate_difference_account(self):
if not cint(erpnext.is_perpetual_inventory_enabled(self.company)):
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index e4f5725..f54b3c1 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -58,8 +58,9 @@
if repost_entry.status == 'In Progress':
frappe.throw(_("Cannot cancel the transaction. Reposting of item valuation on submission is not completed yet."))
if repost_entry.status == 'Queued':
- frappe.delete_doc("Repost Item Valuation", repost_entry.name)
-
+ doc = frappe.get_doc("Repost Item Valuation", repost_entry.name)
+ doc.cancel()
+ doc.delete()
def set_as_cancel(voucher_type, voucher_no):
frappe.db.sql("""update `tabStock Ledger Entry` set is_cancelled=1,
@@ -133,7 +134,7 @@
self.item_code = args.get("item_code")
if self.args.sle_id:
self.args['name'] = self.args.sle_id
-
+
self.company = frappe.get_cached_value("Warehouse", self.args.warehouse, "company")
self.get_precision()
self.valuation_method = get_valuation_method(self.item_code)
@@ -201,7 +202,7 @@
and timestamp(posting_date, time_format(posting_time, %(time_format)s)) < timestamp(%(posting_date)s, time_format(%(posting_time)s, %(time_format)s))
order by timestamp(posting_date, posting_time) desc, creation desc
limit 1""", args, as_dict=1)
-
+
return sle[0] if sle else frappe._dict()
@@ -224,7 +225,7 @@
if sle.dependant_sle_voucher_detail_no:
entries_to_fix = self.get_dependent_entries_to_fix(entries_to_fix, sle)
-
+
self.update_bin()
if self.exceptions:
@@ -439,7 +440,7 @@
# Recalculate subcontracted item's rate in case of subcontracted purchase receipt/invoice
if frappe.db.get_value(sle.voucher_type, sle.voucher_no, "is_subcontracted"):
- doc = frappe.get_cached_doc(sle.voucher_type, sle.voucher_no)
+ doc = frappe.get_doc(sle.voucher_type, sle.voucher_no)
doc.update_valuation_rate(reset_outgoing_rate=False)
for d in (doc.items + doc.supplied_items):
d.db_update()
diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js
index 167e80f..9fe12f9 100644
--- a/erpnext/support/doctype/issue/issue.js
+++ b/erpnext/support/doctype/issue/issue.js
@@ -49,8 +49,8 @@
},
refresh: function (frm) {
- if (frm.doc.status !== "Closed" && frm.doc.agreement_status === "Ongoing") {
- if (frm.doc.service_level_agreement) {
+ if (frm.doc.status !== "Closed") {
+ if (frm.doc.service_level_agreement && frm.doc.agreement_status === "Ongoing") {
frappe.call({
"method": "frappe.client.get",
args: {