Merge branch 'develop' into opening_round_gl
This commit is contained in:
commit
325a461c34
@ -58,6 +58,9 @@ class AccountingDimension(Document):
|
|||||||
if not self.fieldname:
|
if not self.fieldname:
|
||||||
self.fieldname = scrub(self.label)
|
self.fieldname = scrub(self.label)
|
||||||
|
|
||||||
|
def on_update(self):
|
||||||
|
frappe.flags.accounting_dimensions = None
|
||||||
|
|
||||||
def make_dimension_in_accounting_doctypes(doc):
|
def make_dimension_in_accounting_doctypes(doc):
|
||||||
doclist = get_doctypes_with_dimensions()
|
doclist = get_doctypes_with_dimensions()
|
||||||
doc_count = len(get_accounting_dimensions())
|
doc_count = len(get_accounting_dimensions())
|
||||||
@ -186,12 +189,14 @@ def get_doctypes_with_dimensions():
|
|||||||
return doclist
|
return doclist
|
||||||
|
|
||||||
def get_accounting_dimensions(as_list=True):
|
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:
|
if as_list:
|
||||||
return [d.fieldname for d in accounting_dimensions]
|
return [d.fieldname for d in frappe.flags.accounting_dimensions]
|
||||||
else:
|
else:
|
||||||
return accounting_dimensions
|
return frappe.flags.accounting_dimensions
|
||||||
|
|
||||||
def get_checks_for_pl_and_bs_accounts():
|
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
|
dimensions = frappe.db.sql("""SELECT p.label, p.disabled, p.fieldname, c.default_dimension, c.company, c.mandatory_for_pl, c.mandatory_for_bs
|
||||||
|
@ -86,6 +86,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
|
"description": "Setting the account as a Company Account is necessary for Bank Reconciliation",
|
||||||
"fieldname": "is_company_account",
|
"fieldname": "is_company_account",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Is Company Account"
|
"label": "Is Company Account"
|
||||||
@ -207,7 +208,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-07-17 13:59:50.795412",
|
"modified": "2020-10-23 16:48:06.303658",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Bank Account",
|
"name": "Bank Account",
|
||||||
|
@ -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,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
@ -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"
|
||||||
|
}
|
@ -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}
|
||||||
|
"""
|
@ -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
|
3
erpnext/accounts/doctype/bank_statement_import/bank_statement_import.css
vendored
Normal file
3
erpnext/accounts/doctype/bank_statement_import/bank_statement_import.css
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.warnings .warning {
|
||||||
|
margin-bottom: 40px;
|
||||||
|
}
|
@ -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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
@ -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
|
||||||
|
}
|
@ -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
|
@ -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
|
||||||
|
};
|
@ -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
|
@ -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) {
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
@ -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
|
|
||||||
}
|
|
@ -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"
|
|
@ -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()
|
|
||||||
]);
|
|
||||||
|
|
||||||
});
|
|
@ -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
|
|
@ -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
|
|
||||||
}
|
|
@ -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
|
|
@ -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");
|
|
||||||
}
|
|
||||||
});
|
|
@ -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
|
|
||||||
}
|
|
@ -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
|
|
@ -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()
|
|
||||||
]);
|
|
||||||
|
|
||||||
});
|
|
@ -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
|
|
@ -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
|
|
||||||
}
|
|
@ -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
|
|
@ -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
|
|
||||||
}
|
|
@ -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
|
|
@ -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) {
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
@ -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
|
|
||||||
}
|
|
@ -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"
|
|
@ -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()
|
|
||||||
]);
|
|
||||||
|
|
||||||
});
|
|
@ -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
|
|
@ -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
|
|
||||||
}
|
|
@ -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
|
|
@ -1,32 +1,70 @@
|
|||||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Bank Transaction', {
|
frappe.ui.form.on("Bank Transaction", {
|
||||||
onload(frm) {
|
onload(frm) {
|
||||||
frm.set_query('payment_document', 'payment_entries', function() {
|
frm.set_query("payment_document", "payment_entries", function () {
|
||||||
return {
|
return {
|
||||||
"filters": {
|
filters: {
|
||||||
"name": ["in", ["Payment Entry", "Journal Entry", "Sales Invoice", "Purchase Invoice", "Expense Claim"]]
|
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', {
|
frappe.ui.form.on("Bank Transaction Payments", {
|
||||||
payment_entries_remove: function(frm, cdt, cdn) {
|
payment_entries_remove: function (frm, cdt, cdn) {
|
||||||
update_clearance_date(frm, cdt, cdn);
|
update_clearance_date(frm, cdt, cdn);
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const update_clearance_date = (frm, cdt, cdn) => {
|
const update_clearance_date = (frm, cdt, cdn) => {
|
||||||
if (frm.doc.docstatus === 1) {
|
if (frm.doc.docstatus === 1) {
|
||||||
frappe.xcall('erpnext.accounts.doctype.bank_transaction.bank_transaction.unclear_reference_payment',
|
frappe
|
||||||
{doctype: cdt, docname: cdn})
|
.xcall(
|
||||||
.then(e => {
|
"erpnext.accounts.doctype.bank_transaction.bank_transaction.unclear_reference_payment",
|
||||||
|
{ doctype: cdt, docname: cdn }
|
||||||
|
)
|
||||||
|
.then((e) => {
|
||||||
if (e == "success") {
|
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",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function set_bank_statement_filter(frm) {
|
||||||
|
frm.set_query("bank_statement", function () {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
bank_account: frm.doc.bank_account,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@ -1,833 +1,245 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_events_in_timeline": 0,
|
|
||||||
"allow_guest_to_view": 0,
|
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"allow_rename": 0,
|
|
||||||
"autoname": "naming_series:",
|
"autoname": "naming_series:",
|
||||||
"beta": 0,
|
|
||||||
"creation": "2018-10-22 18:19:02.784533",
|
"creation": "2018-10-22 18:19:02.784533",
|
||||||
"custom": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "",
|
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
"engine": "InnoDB",
|
"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": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"default": "ACC-BTN-.YYYY.-",
|
"default": "ACC-BTN-.YYYY.-",
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "naming_series",
|
"fieldname": "naming_series",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"hidden": 1,
|
"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",
|
"label": "Series",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "ACC-BTN-.YYYY.-",
|
"options": "ACC-BTN-.YYYY.-",
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 0,
|
"set_only_once": 1
|
||||||
"set_only_once": 1,
|
|
||||||
"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": "date",
|
"fieldname": "date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"hidden": 0,
|
"label": "Date"
|
||||||
"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
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"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",
|
"fieldname": "column_break_2",
|
||||||
"fieldtype": "Column Break",
|
"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,
|
|
||||||
"default": "Pending",
|
"default": "Pending",
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "status",
|
"fieldname": "status",
|
||||||
"fieldtype": "Select",
|
"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,
|
"in_standard_filter": 1,
|
||||||
"label": "Status",
|
"label": "Status",
|
||||||
"length": 0,
|
"options": "\nPending\nSettled\nUnreconciled\nReconciled"
|
||||||
"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
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"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",
|
"fieldname": "bank_account",
|
||||||
"fieldtype": "Link",
|
"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,
|
"in_standard_filter": 1,
|
||||||
"label": "Bank Account",
|
"label": "Bank Account",
|
||||||
"length": 0,
|
"options": "Bank Account"
|
||||||
"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
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"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_from": "bank_account.company",
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "company",
|
"fieldname": "company",
|
||||||
"fieldtype": "Link",
|
"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,
|
"in_standard_filter": 1,
|
||||||
"label": "Company",
|
"label": "Company",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Company",
|
"options": "Company",
|
||||||
"permlevel": 0,
|
"read_only": 1
|
||||||
"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,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "section_break_4",
|
"fieldname": "section_break_4",
|
||||||
"fieldtype": "Section Break",
|
"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,
|
|
||||||
"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",
|
"fieldname": "column_break_7",
|
||||||
"fieldtype": "Column Break",
|
"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,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "currency",
|
"fieldname": "currency",
|
||||||
"fieldtype": "Link",
|
"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",
|
"label": "Currency",
|
||||||
"length": 0,
|
"options": "Currency"
|
||||||
"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
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"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",
|
"fieldname": "section_break_10",
|
||||||
"fieldtype": "Section Break",
|
"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,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "description",
|
"fieldname": "description",
|
||||||
"fieldtype": "Small Text",
|
"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_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
"label": "Description"
|
||||||
"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
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"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",
|
"fieldname": "section_break_14",
|
||||||
"fieldtype": "Section Break",
|
"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_on_submit": 1,
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "reference_number",
|
"fieldname": "reference_number",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 0,
|
"label": "Reference Number"
|
||||||
"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
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"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",
|
"fieldname": "transaction_id",
|
||||||
"fieldtype": "Data",
|
"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",
|
"label": "Transaction ID",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 1
|
"unique": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 1,
|
"allow_on_submit": 1,
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "payment_entries",
|
"fieldname": "payment_entries",
|
||||||
"fieldtype": "Table",
|
"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",
|
"label": "Payment Entries",
|
||||||
"length": 0,
|
"options": "Bank Transaction Payments"
|
||||||
"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
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"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",
|
"fieldname": "section_break_18",
|
||||||
"fieldtype": "Section Break",
|
"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,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "allocated_amount",
|
"fieldname": "allocated_amount",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"hidden": 0,
|
"label": "Allocated Amount"
|
||||||
"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
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"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",
|
"fieldname": "amended_from",
|
||||||
"fieldtype": "Link",
|
"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",
|
"label": "Amended From",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "Bank Transaction",
|
"options": "Bank Transaction",
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"print_hide_if_no_value": 0,
|
"read_only": 1
|
||||||
"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,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "column_break_17",
|
"fieldname": "column_break_17",
|
||||||
"fieldtype": "Column Break",
|
"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,
|
|
||||||
"depends_on": "",
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "unallocated_amount",
|
"fieldname": "unallocated_amount",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"hidden": 0,
|
"label": "Unallocated Amount"
|
||||||
"ignore_user_permissions": 0,
|
},
|
||||||
"ignore_xss_filter": 0,
|
{
|
||||||
"in_filter": 0,
|
"fieldname": "party_section",
|
||||||
"in_global_search": 0,
|
"fieldtype": "Section Break",
|
||||||
"in_list_view": 0,
|
"label": "Payment From / To"
|
||||||
"in_standard_filter": 0,
|
},
|
||||||
"label": "Unallocated Amount",
|
{
|
||||||
"length": 0,
|
"allow_on_submit": 1,
|
||||||
"no_copy": 0,
|
"fieldname": "party_type",
|
||||||
"permlevel": 0,
|
"fieldtype": "Link",
|
||||||
"precision": "",
|
"label": "Party Type",
|
||||||
"print_hide": 0,
|
"options": "DocType"
|
||||||
"print_hide_if_no_value": 0,
|
},
|
||||||
"read_only": 0,
|
{
|
||||||
"remember_last_selected_value": 0,
|
"allow_on_submit": 1,
|
||||||
"report_hide": 0,
|
"fieldname": "party",
|
||||||
"reqd": 0,
|
"fieldtype": "Dynamic Link",
|
||||||
"search_index": 0,
|
"label": "Party",
|
||||||
"set_only_once": 0,
|
"options": "party_type"
|
||||||
"translatable": 0,
|
},
|
||||||
"unique": 0
|
{
|
||||||
|
"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,
|
"is_submittable": 1,
|
||||||
"issingle": 0,
|
"links": [],
|
||||||
"istable": 0,
|
"modified": "2020-12-30 19:40:54.221070",
|
||||||
"max_attachments": 0,
|
|
||||||
"modified": "2019-05-11 05:27:55.244721",
|
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Bank Transaction",
|
"name": "Bank Transaction",
|
||||||
"name_case": "",
|
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"amend": 0,
|
|
||||||
"cancel": 1,
|
"cancel": 1,
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "System Manager",
|
"role": "System Manager",
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"submit": 1,
|
"submit": 1,
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
|
||||||
"cancel": 1,
|
"cancel": 1,
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Accounts Manager",
|
"role": "Accounts Manager",
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"submit": 1,
|
"submit": 1,
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
|
||||||
"cancel": 0,
|
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Accounts User",
|
"role": "Accounts User",
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"submit": 1,
|
"submit": 1,
|
||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"read_only_onload": 0,
|
|
||||||
"show_name_in_global_search": 0,
|
|
||||||
"sort_field": "date",
|
"sort_field": "date",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"title_field": "bank_account",
|
"title_field": "bank_account",
|
||||||
"track_changes": 0,
|
"track_changes": 1
|
||||||
"track_seen": 0,
|
|
||||||
"track_views": 0
|
|
||||||
}
|
}
|
@ -11,7 +11,7 @@ from frappe import _
|
|||||||
|
|
||||||
class BankTransaction(StatusUpdater):
|
class BankTransaction(StatusUpdater):
|
||||||
def after_insert(self):
|
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):
|
def on_submit(self):
|
||||||
self.clear_linked_payment_entries()
|
self.clear_linked_payment_entries()
|
||||||
@ -30,13 +30,13 @@ class BankTransaction(StatusUpdater):
|
|||||||
|
|
||||||
if allocated_amount:
|
if allocated_amount:
|
||||||
frappe.db.set_value(self.doctype, self.name, "allocated_amount", flt(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:
|
else:
|
||||||
frappe.db.set_value(self.doctype, self.name, "allocated_amount", 0)
|
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:
|
if amount == self.allocated_amount:
|
||||||
frappe.db.set_value(self.doctype, self.name, "status", "Reconciled")
|
frappe.db.set_value(self.doctype, self.name, "status", "Reconciled")
|
||||||
|
|
||||||
@ -44,18 +44,11 @@ class BankTransaction(StatusUpdater):
|
|||||||
|
|
||||||
def clear_linked_payment_entries(self):
|
def clear_linked_payment_entries(self):
|
||||||
for payment_entry in self.payment_entries:
|
for payment_entry in self.payment_entries:
|
||||||
allocated_amount = get_total_allocated_amount(payment_entry)
|
if payment_entry.payment_document in ["Payment Entry", "Journal Entry", "Purchase Invoice", "Expense Claim"]:
|
||||||
paid_amount = get_paid_amount(payment_entry, self.currency)
|
self.clear_simple_entry(payment_entry)
|
||||||
|
|
||||||
if paid_amount and allocated_amount:
|
elif payment_entry.payment_document == "Sales Invoice":
|
||||||
if flt(allocated_amount[0]["allocated_amount"]) > flt(paid_amount):
|
self.clear_sales_invoice(payment_entry)
|
||||||
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)
|
|
||||||
|
|
||||||
def clear_simple_entry(self, 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)
|
frappe.db.set_value(payment_entry.payment_document, payment_entry.payment_entry, "clearance_date", self.date)
|
||||||
@ -112,3 +105,4 @@ def unclear_reference_payment(doctype, docname):
|
|||||||
frappe.db.set_value(doc.payment_document, doc.payment_entry, "clearance_date", None)
|
frappe.db.set_value(doc.payment_document, doc.payment_entry, "clearance_date", None)
|
||||||
|
|
||||||
return doc.payment_entry
|
return doc.payment_entry
|
||||||
|
|
||||||
|
@ -5,10 +5,11 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
import unittest
|
import unittest
|
||||||
|
import json
|
||||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
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.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.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
|
from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile
|
||||||
|
|
||||||
test_dependencies = ["Item", "Cost Center"]
|
test_dependencies = ["Item", "Cost Center"]
|
||||||
@ -17,7 +18,7 @@ class TestBankTransaction(unittest.TestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
make_pos_profile()
|
make_pos_profile()
|
||||||
add_transactions()
|
add_transactions()
|
||||||
add_payments()
|
add_vouchers()
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
for bt in frappe.get_all("Bank Transaction"):
|
for bt in frappe.get_all("Bank Transaction"):
|
||||||
@ -38,14 +39,18 @@ class TestBankTransaction(unittest.TestCase):
|
|||||||
# This test checks if ERPNext is able to provide a linked payment for a bank transaction based on the amount of the bank transaction.
|
# 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):
|
def test_linked_payments(self):
|
||||||
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="Re 95282925234 FE/000002917 AT171513000281183046 Conrad Electronic"))
|
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="Re 95282925234 FE/000002917 AT171513000281183046 Conrad Electronic"))
|
||||||
linked_payments = get_linked_payments(bank_transaction.name)
|
linked_payments = get_linked_payments(bank_transaction.name, ['payment_entry', 'exact_match'])
|
||||||
self.assertTrue(linked_payments[0].party == "Conrad Electronic")
|
self.assertTrue(linked_payments[0][6] == "Conrad Electronic")
|
||||||
|
|
||||||
# This test validates a simple reconciliation leading to the clearance of the bank transaction and the payment
|
# This test validates a simple reconciliation leading to the clearance of the bank transaction and the payment
|
||||||
def test_reconcile(self):
|
def test_reconcile(self):
|
||||||
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G"))
|
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))
|
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")
|
unallocated_amount = frappe.db.get_value("Bank Transaction", bank_transaction.name, "unallocated_amount")
|
||||||
self.assertTrue(unallocated_amount == 0)
|
self.assertTrue(unallocated_amount == 0)
|
||||||
@ -53,45 +58,40 @@ class TestBankTransaction(unittest.TestCase):
|
|||||||
clearance_date = frappe.db.get_value("Payment Entry", payment.name, "clearance_date")
|
clearance_date = frappe.db.get_value("Payment Entry", payment.name, "clearance_date")
|
||||||
self.assertTrue(clearance_date is not None)
|
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
|
# Check if ERPNext can correctly filter a linked payments based on the debit/credit amount
|
||||||
def test_debit_credit_output(self):
|
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"))
|
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)
|
linked_payments = get_linked_payments(bank_transaction.name, ['payment_entry', 'exact_match'])
|
||||||
self.assertTrue(linked_payments[0].payment_type == "Pay")
|
print(linked_payments)
|
||||||
|
self.assertTrue(linked_payments[0][3])
|
||||||
|
|
||||||
# Check error if already reconciled
|
# Check error if already reconciled
|
||||||
def test_already_reconciled(self):
|
def test_already_reconciled(self):
|
||||||
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G"))
|
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))
|
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"))
|
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))
|
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)
|
vouchers = json.dumps([{
|
||||||
|
"payment_doctype":"Payment Entry",
|
||||||
# Raise an error if creditor transaction vs creditor payment
|
"payment_name":payment.name,
|
||||||
def test_invalid_creditor_reconcilation(self):
|
"amount":bank_transaction.unallocated_amount}])
|
||||||
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="I2015000011 VD/000002514 ATWWXXX AT4701345000003510057 Bio"))
|
self.assertRaises(frappe.ValidationError, reconcile_vouchers, bank_transaction_name=bank_transaction.name, vouchers=vouchers)
|
||||||
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)
|
|
||||||
|
|
||||||
# Raise an error if debitor transaction vs debitor payment
|
# Raise an error if debitor transaction vs debitor payment
|
||||||
def test_clear_sales_invoice(self):
|
def test_clear_sales_invoice(self):
|
||||||
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="I2015000011 VD/000002514 ATWWXXX AT4701345000003510057 Bio"))
|
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"]))
|
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.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)
|
self.assertTrue(frappe.db.get_value("Sales Invoice Payment", dict(parent=payment.name), "clearance_date") is not None)
|
||||||
@ -126,7 +126,7 @@ def add_transactions():
|
|||||||
"doctype": "Bank Transaction",
|
"doctype": "Bank Transaction",
|
||||||
"description":"1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G",
|
"description":"1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G",
|
||||||
"date": "2018-10-23",
|
"date": "2018-10-23",
|
||||||
"debit": 1200,
|
"deposit": 1200,
|
||||||
"currency": "INR",
|
"currency": "INR",
|
||||||
"bank_account": "Checking Account - Citi Bank"
|
"bank_account": "Checking Account - Citi Bank"
|
||||||
}).insert()
|
}).insert()
|
||||||
@ -136,7 +136,7 @@ def add_transactions():
|
|||||||
"doctype": "Bank Transaction",
|
"doctype": "Bank Transaction",
|
||||||
"description":"1512567 BG/000003025 OPSKATTUZWXXX AT776000000098709849 Herr G",
|
"description":"1512567 BG/000003025 OPSKATTUZWXXX AT776000000098709849 Herr G",
|
||||||
"date": "2018-10-23",
|
"date": "2018-10-23",
|
||||||
"debit": 1700,
|
"deposit": 1700,
|
||||||
"currency": "INR",
|
"currency": "INR",
|
||||||
"bank_account": "Checking Account - Citi Bank"
|
"bank_account": "Checking Account - Citi Bank"
|
||||||
}).insert()
|
}).insert()
|
||||||
@ -146,7 +146,7 @@ def add_transactions():
|
|||||||
"doctype": "Bank Transaction",
|
"doctype": "Bank Transaction",
|
||||||
"description":"Re 95282925234 FE/000002917 AT171513000281183046 Conrad Electronic",
|
"description":"Re 95282925234 FE/000002917 AT171513000281183046 Conrad Electronic",
|
||||||
"date": "2018-10-26",
|
"date": "2018-10-26",
|
||||||
"debit": 690,
|
"withdrawal": 690,
|
||||||
"currency": "INR",
|
"currency": "INR",
|
||||||
"bank_account": "Checking Account - Citi Bank"
|
"bank_account": "Checking Account - Citi Bank"
|
||||||
}).insert()
|
}).insert()
|
||||||
@ -156,7 +156,7 @@ def add_transactions():
|
|||||||
"doctype": "Bank Transaction",
|
"doctype": "Bank Transaction",
|
||||||
"description":"Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07",
|
"description":"Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07",
|
||||||
"date": "2018-10-27",
|
"date": "2018-10-27",
|
||||||
"debit": 3900,
|
"deposit": 3900,
|
||||||
"currency": "INR",
|
"currency": "INR",
|
||||||
"bank_account": "Checking Account - Citi Bank"
|
"bank_account": "Checking Account - Citi Bank"
|
||||||
}).insert()
|
}).insert()
|
||||||
@ -166,7 +166,7 @@ def add_transactions():
|
|||||||
"doctype": "Bank Transaction",
|
"doctype": "Bank Transaction",
|
||||||
"description":"I2015000011 VD/000002514 ATWWXXX AT4701345000003510057 Bio",
|
"description":"I2015000011 VD/000002514 ATWWXXX AT4701345000003510057 Bio",
|
||||||
"date": "2018-10-27",
|
"date": "2018-10-27",
|
||||||
"credit": 109080,
|
"withdrawal": 109080,
|
||||||
"currency": "INR",
|
"currency": "INR",
|
||||||
"bank_account": "Checking Account - Citi Bank"
|
"bank_account": "Checking Account - Citi Bank"
|
||||||
}).insert()
|
}).insert()
|
||||||
@ -174,7 +174,7 @@ def add_transactions():
|
|||||||
|
|
||||||
frappe.flags.test_bank_transactions_created = True
|
frappe.flags.test_bank_transactions_created = True
|
||||||
|
|
||||||
def add_payments():
|
def add_vouchers():
|
||||||
if frappe.flags.test_payments_created:
|
if frappe.flags.test_payments_created:
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -192,6 +192,7 @@ def add_payments():
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
pi = make_purchase_invoice(supplier="Conrad Electronic", qty=1, rate=690)
|
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 = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
|
||||||
pe.reference_no = "Conrad Oct 18"
|
pe.reference_no = "Conrad Oct 18"
|
||||||
pe.reference_date = "2018-10-24"
|
pe.reference_date = "2018-10-24"
|
||||||
@ -242,10 +243,15 @@ def add_payments():
|
|||||||
except frappe.DuplicateEntryError:
|
except frappe.DuplicateEntryError:
|
||||||
pass
|
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 = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
|
||||||
pe.reference_no = "Poore Simon's Oct 18"
|
pe.reference_no = "Poore Simon's Oct 18"
|
||||||
pe.reference_date = "2018-10-28"
|
pe.reference_date = "2018-10-28"
|
||||||
|
pe.paid_amount = 690
|
||||||
|
pe.received_amount = 690
|
||||||
pe.insert()
|
pe.insert()
|
||||||
pe.submit()
|
pe.submit()
|
||||||
|
|
||||||
|
@ -27,30 +27,30 @@ class GLEntry(Document):
|
|||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.flags.ignore_submit_comment = True
|
self.flags.ignore_submit_comment = True
|
||||||
self.check_mandatory()
|
|
||||||
self.validate_and_set_fiscal_year()
|
self.validate_and_set_fiscal_year()
|
||||||
self.pl_must_have_cost_center()
|
self.pl_must_have_cost_center()
|
||||||
self.validate_cost_center()
|
|
||||||
|
|
||||||
if not self.flags.from_repost:
|
if not self.flags.from_repost:
|
||||||
|
self.check_mandatory()
|
||||||
|
self.validate_cost_center()
|
||||||
self.check_pl_account()
|
self.check_pl_account()
|
||||||
self.validate_party()
|
self.validate_party()
|
||||||
self.validate_currency()
|
self.validate_currency()
|
||||||
|
|
||||||
def on_update_with_args(self, adv_adj, update_outstanding = 'Yes', from_repost=False):
|
def on_update(self):
|
||||||
if not from_repost:
|
adv_adj = self.flags.adv_adj
|
||||||
|
if not self.flags.from_repost:
|
||||||
self.validate_account_details(adv_adj)
|
self.validate_account_details(adv_adj)
|
||||||
self.validate_dimensions_for_pl_and_bs()
|
self.validate_dimensions_for_pl_and_bs()
|
||||||
self.validate_allowed_dimensions()
|
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)
|
# Update outstanding amt on against voucher
|
||||||
validate_balance_type(self.account, adv_adj)
|
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 on against voucher
|
update_outstanding_amt(self.account, self.party_type, self.party, self.against_voucher_type,
|
||||||
if self.against_voucher_type in ['Journal Entry', 'Sales Invoice', 'Purchase Invoice', 'Fees'] \
|
self.against_voucher)
|
||||||
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)
|
|
||||||
|
|
||||||
def check_mandatory(self):
|
def check_mandatory(self):
|
||||||
mandatory = ['account','voucher_type','voucher_no','company']
|
mandatory = ['account','voucher_type','voucher_no','company']
|
||||||
@ -58,7 +58,7 @@ class GLEntry(Document):
|
|||||||
if not self.get(k):
|
if not self.get(k):
|
||||||
frappe.throw(_("{0} is required").format(_(self.meta.get_label(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 not (self.party_type and self.party):
|
||||||
if account_type == "Receivable":
|
if account_type == "Receivable":
|
||||||
frappe.throw(_("{0} {1}: Customer is required against Receivable account {2}")
|
frappe.throw(_("{0} {1}: Customer is required against Receivable account {2}")
|
||||||
@ -73,7 +73,7 @@ class GLEntry(Document):
|
|||||||
.format(self.voucher_type, self.voucher_no, self.account))
|
.format(self.voucher_type, self.voucher_no, self.account))
|
||||||
|
|
||||||
def pl_must_have_cost_center(self):
|
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':
|
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.")
|
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))
|
.format(self.voucher_type, self.voucher_no, self.account))
|
||||||
@ -140,25 +140,16 @@ class GLEntry(Document):
|
|||||||
.format(self.voucher_type, self.voucher_no, self.account, self.company))
|
.format(self.voucher_type, self.voucher_no, self.account, self.company))
|
||||||
|
|
||||||
def validate_cost_center(self):
|
def validate_cost_center(self):
|
||||||
if not hasattr(self, "cost_center_company"):
|
if not self.cost_center: return
|
||||||
self.cost_center_company = {}
|
|
||||||
|
|
||||||
def _get_cost_center_company():
|
is_group, company = frappe.get_cached_value('Cost Center',
|
||||||
if not self.cost_center_company.get(self.cost_center):
|
self.cost_center, ['is_group', 'company'])
|
||||||
self.cost_center_company[self.cost_center] = frappe.db.get_value(
|
|
||||||
"Cost Center", self.cost_center, "company")
|
|
||||||
|
|
||||||
return self.cost_center_company[self.cost_center]
|
if company != self.company:
|
||||||
|
|
||||||
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:
|
|
||||||
frappe.throw(_("{0} {1}: Cost Center {2} does not belong to Company {3}")
|
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))
|
.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' \
|
if (self.voucher_type != 'Period Closing Voucher' and is_group):
|
||||||
and self.cost_center and _check_is_group():
|
|
||||||
frappe.throw(_("""{0} {1}: Cost Center {2} is a group cost center and group cost centers cannot be used in transactions""").format(
|
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)))
|
self.voucher_type, self.voucher_no, frappe.bold(self.cost_center)))
|
||||||
|
|
||||||
@ -184,7 +175,6 @@ class GLEntry(Document):
|
|||||||
if not self.fiscal_year:
|
if not self.fiscal_year:
|
||||||
self.fiscal_year = get_fiscal_year(self.posting_date, company=self.company)[0]
|
self.fiscal_year = get_fiscal_year(self.posting_date, company=self.company)[0]
|
||||||
|
|
||||||
|
|
||||||
def validate_balance_type(account, adv_adj=False):
|
def validate_balance_type(account, adv_adj=False):
|
||||||
if not adv_adj and account:
|
if not adv_adj and account:
|
||||||
balance_must_be = frappe.db.get_value("Account", account, "balance_must_be")
|
balance_must_be = frappe.db.get_value("Account", account, "balance_must_be")
|
||||||
@ -250,7 +240,7 @@ def update_outstanding_amt(account, party_type, party, against_voucher_type, aga
|
|||||||
|
|
||||||
|
|
||||||
def validate_frozen_account(account, adv_adj=None):
|
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:
|
if frozen_account == 'Yes' and not adv_adj:
|
||||||
frozen_accounts_modifier = frappe.db.get_value( 'Accounts Settings', None,
|
frozen_accounts_modifier = frappe.db.get_value( 'Accounts Settings', None,
|
||||||
'frozen_accounts_modifier')
|
'frozen_accounts_modifier')
|
||||||
|
@ -21,6 +21,7 @@ from erpnext.accounts.general_ledger import get_round_off_account_and_cost_cente
|
|||||||
from erpnext.accounts.doctype.loyalty_program.loyalty_program import \
|
from erpnext.accounts.doctype.loyalty_program.loyalty_program import \
|
||||||
get_loyalty_program_details_with_points, get_loyalty_details, validate_loyalty_points
|
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.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.model.utils import get_fetch_values
|
||||||
from frappe.contacts.doctype.address.address import get_address_display
|
from frappe.contacts.doctype.address.address import get_address_display
|
||||||
|
|
||||||
@ -76,6 +77,8 @@ class SalesInvoice(SellingController):
|
|||||||
if not self.is_pos:
|
if not self.is_pos:
|
||||||
self.so_dn_required()
|
self.so_dn_required()
|
||||||
|
|
||||||
|
self.set_tax_withholding()
|
||||||
|
|
||||||
self.validate_proj_cust()
|
self.validate_proj_cust()
|
||||||
self.validate_pos_return()
|
self.validate_pos_return()
|
||||||
self.validate_with_previous_doc()
|
self.validate_with_previous_doc()
|
||||||
@ -153,6 +156,32 @@ class SalesInvoice(SellingController):
|
|||||||
if cost_center_company != self.company:
|
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)))
|
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):
|
def before_save(self):
|
||||||
set_account_for_mode_of_payment(self)
|
set_account_for_mode_of_payment(self)
|
||||||
|
|
||||||
|
@ -12,37 +12,62 @@ from erpnext.accounts.utils import get_fiscal_year
|
|||||||
class TaxWithholdingCategory(Document):
|
class TaxWithholdingCategory(Document):
|
||||||
pass
|
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 = ''
|
pan_no = ''
|
||||||
suppliers = []
|
parties = []
|
||||||
|
party_type, party = get_party_details(inv)
|
||||||
|
|
||||||
if not tax_withholding_category:
|
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:
|
if not tax_withholding_category:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# if tax_withholding_category passed as an argument but not pan_no
|
||||||
if 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
|
# Get others suppliers with the same PAN No
|
||||||
if 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:
|
if not parties:
|
||||||
suppliers.append(ref_doc.supplier)
|
parties.append(party)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
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)
|
|
||||||
if not tax_details:
|
if not tax_details:
|
||||||
frappe.throw(_('Please set associated account in Tax Withholding Category {0} against Company {1}')
|
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,
|
if party_type == 'Customer' and not tax_details.cumulative_threshold:
|
||||||
tax_details, fy, ref_doc.posting_date, pan_no)
|
# 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
|
return tax_row
|
||||||
|
|
||||||
@ -69,147 +94,254 @@ def get_tax_withholding_rates(tax_withholding, fiscal_year):
|
|||||||
|
|
||||||
frappe.throw(_("No Tax Withholding data found for the current Fiscal Year."))
|
frappe.throw(_("No Tax Withholding data found for the current Fiscal Year."))
|
||||||
|
|
||||||
def get_tax_row(tax_details, tds_amount):
|
def get_tax_row_for_tcs(inv, tax_details, tax_amount, tax_deducted):
|
||||||
|
row = {
|
||||||
return {
|
|
||||||
"category": "Total",
|
"category": "Total",
|
||||||
"add_deduct_tax": "Deduct",
|
|
||||||
"charge_type": "Actual",
|
"charge_type": "Actual",
|
||||||
"account_head": tax_details.account_head,
|
"tax_amount": tax_amount,
|
||||||
"description": tax_details.description,
|
"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):
|
if tax_deducted:
|
||||||
fiscal_year, year_start_date, year_end_date = fiscal_year_details
|
# TCS already deducted on previous invoices
|
||||||
tds_amount = 0
|
# So, TCS will be calculated by 'Previous Row Total'
|
||||||
tds_deducted = 0
|
|
||||||
|
|
||||||
def _get_tds(amount, rate):
|
taxes_excluding_tcs = [d for d in inv.taxes if d.account_head != tax_details.account_head]
|
||||||
if amount <= 0:
|
if taxes_excluding_tcs:
|
||||||
return 0
|
# chargeable amount is the total amount after other charges are applied
|
||||||
|
row.update({
|
||||||
return amount * rate / 100
|
"charge_type": "On Previous Row Total",
|
||||||
|
"row_id": len(taxes_excluding_tcs),
|
||||||
ldc_name = frappe.db.get_value('Lower Deduction Certificate',
|
"rate": tax_details.rate
|
||||||
{
|
})
|
||||||
'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)
|
|
||||||
else:
|
else:
|
||||||
tds_amount = _get_tds(net_total, tax_details.rate)
|
# if only TCS is to be charged, then net total is chargeable amount
|
||||||
else:
|
row.update({
|
||||||
supplier_credit_amount = frappe.get_all('Purchase Invoice',
|
"charge_type": "On Net Total",
|
||||||
fields = ['sum(net_total)'],
|
"rate": tax_details.rate
|
||||||
filters = {'name': ('in', vouchers), 'docstatus': 1, "apply_tds": 1}, as_list=1)
|
})
|
||||||
|
|
||||||
supplier_credit_amount = (supplier_credit_amount[0][0]
|
return row
|
||||||
if supplier_credit_amount and supplier_credit_amount[0][0] else 0)
|
|
||||||
|
|
||||||
jv_supplier_credit_amt = frappe.get_all('Journal Entry Account',
|
def get_tax_row_for_tds(tax_details, tax_amount):
|
||||||
fields = ['sum(credit_in_account_currency)'],
|
return {
|
||||||
filters = {
|
"category": "Total",
|
||||||
'parent': ('in', vouchers), 'docstatus': 1,
|
"charge_type": "Actual",
|
||||||
'party': ('in', suppliers),
|
"tax_amount": tax_amount,
|
||||||
'reference_type': ('not in', ['Purchase Invoice'])
|
"add_deduct_tax": "Deduct",
|
||||||
}, as_list=1)
|
"description": tax_details.description,
|
||||||
|
"account_head": tax_details.account_head
|
||||||
|
}
|
||||||
|
|
||||||
supplier_credit_amount += (jv_supplier_credit_amt[0][0]
|
def get_lower_deduction_certificate(fiscal_year, pan_no):
|
||||||
if jv_supplier_credit_amt and jv_supplier_credit_amt[0][0] else 0)
|
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)
|
vouchers = get_invoice_vouchers(parties, fiscal_year, inv.company, party_type=party_type)
|
||||||
supplier_credit_amount -= debit_note_amount
|
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)
|
tax_deducted = 0
|
||||||
or (tax_details.get('cumulative_threshold', 0) and supplier_credit_amount >= tax_details.cumulative_threshold)):
|
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,
|
tax_amount = 0
|
||||||
ldc.certificate_limit):
|
posting_date = inv.posting_date
|
||||||
tds_amount = get_ltds_amount(supplier_credit_amount, 0, ldc.certificate_limit, ldc.rate,
|
if party_type == 'Supplier':
|
||||||
tax_details)
|
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:
|
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
|
return tds_amount
|
||||||
|
|
||||||
def get_advance_vouchers(suppliers, fiscal_year=None, company=None, from_date=None, to_date=None):
|
def get_tcs_amount(parties, inv, tax_details, fiscal_year_details, vouchers, adv_vouchers):
|
||||||
condition = "fiscal_year=%s" % fiscal_year
|
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:
|
if company:
|
||||||
condition += "and company =%s" % (company)
|
filters['company'] = company
|
||||||
if from_date and to_date:
|
|
||||||
condition += "and posting_date between %s and %s" % (from_date, to_date)
|
|
||||||
|
|
||||||
## Appending the same supplier again if length of suppliers list is 1
|
return frappe.get_all('Purchase Invoice', filters, fields)[0].get('net_total') or 0.0
|
||||||
## 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)))
|
|
||||||
|
|
||||||
def get_ltds_amount(current_amount, deducted_amount, certificate_limit, rate, tax_details):
|
def get_ltds_amount(current_amount, deducted_amount, certificate_limit, rate, tax_details):
|
||||||
if current_amount < (certificate_limit - deducted_amount):
|
if current_amount < (certificate_limit - deducted_amount):
|
||||||
|
@ -9,7 +9,7 @@ from frappe.utils import today
|
|||||||
from erpnext.accounts.utils import get_fiscal_year
|
from erpnext.accounts.utils import get_fiscal_year
|
||||||
from erpnext.buying.doctype.supplier.test_supplier import create_supplier
|
from erpnext.buying.doctype.supplier.test_supplier import create_supplier
|
||||||
|
|
||||||
test_dependencies = ["Supplier Group"]
|
test_dependencies = ["Supplier Group", "Customer Group"]
|
||||||
|
|
||||||
class TestTaxWithholdingCategory(unittest.TestCase):
|
class TestTaxWithholdingCategory(unittest.TestCase):
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -18,6 +18,9 @@ class TestTaxWithholdingCategory(unittest.TestCase):
|
|||||||
create_records()
|
create_records()
|
||||||
create_tax_with_holding_category()
|
create_tax_with_holding_category()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
cancel_invoices()
|
||||||
|
|
||||||
def test_cumulative_threshold_tds(self):
|
def test_cumulative_threshold_tds(self):
|
||||||
frappe.db.set_value("Supplier", "Test TDS Supplier", "tax_withholding_category", "Cumulative Threshold TDS")
|
frappe.db.set_value("Supplier", "Test TDS Supplier", "tax_withholding_category", "Cumulative Threshold TDS")
|
||||||
invoices = []
|
invoices = []
|
||||||
@ -128,9 +131,59 @@ class TestTaxWithholdingCategory(unittest.TestCase):
|
|||||||
for d in invoices:
|
for d in invoices:
|
||||||
d.cancel()
|
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):
|
def create_purchase_invoice(**args):
|
||||||
# return sales invoice doc object
|
# 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)
|
args = frappe._dict(args)
|
||||||
pi = frappe.get_doc({
|
pi = frappe.get_doc({
|
||||||
@ -145,7 +198,7 @@ def create_purchase_invoice(**args):
|
|||||||
"taxes": [],
|
"taxes": [],
|
||||||
"items": [{
|
"items": [{
|
||||||
'doctype': 'Purchase Invoice Item',
|
'doctype': 'Purchase Invoice Item',
|
||||||
'item_code': item.name,
|
'item_code': item,
|
||||||
'qty': args.qty or 1,
|
'qty': args.qty or 1,
|
||||||
'rate': args.rate or 10000,
|
'rate': args.rate or 10000,
|
||||||
'cost_center': 'Main - _TC',
|
'cost_center': 'Main - _TC',
|
||||||
@ -156,6 +209,33 @@ def create_purchase_invoice(**args):
|
|||||||
pi.save()
|
pi.save()
|
||||||
return pi
|
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():
|
def create_records():
|
||||||
# create a new suppliers
|
# create a new suppliers
|
||||||
for name in ['Test TDS Supplier', 'Test TDS Supplier1', 'Test TDS Supplier2']:
|
for name in ['Test TDS Supplier', 'Test TDS Supplier1', 'Test TDS Supplier2']:
|
||||||
@ -168,7 +248,17 @@ def create_records():
|
|||||||
"doctype": "Supplier",
|
"doctype": "Supplier",
|
||||||
}).insert()
|
}).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"):
|
if not frappe.db.exists('Item', "TDS Item"):
|
||||||
frappe.get_doc({
|
frappe.get_doc({
|
||||||
"doctype": "Item",
|
"doctype": "Item",
|
||||||
@ -178,7 +268,16 @@ def create_records():
|
|||||||
"is_stock_item": 0,
|
"is_stock_item": 0,
|
||||||
}).insert()
|
}).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"):
|
if not frappe.db.exists("Account", "TDS - _TC"):
|
||||||
frappe.get_doc({
|
frappe.get_doc({
|
||||||
'doctype': 'Account',
|
'doctype': 'Account',
|
||||||
@ -189,6 +288,17 @@ def create_records():
|
|||||||
'root_type': 'Asset'
|
'root_type': 'Asset'
|
||||||
}).insert()
|
}).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():
|
def create_tax_with_holding_category():
|
||||||
fiscal_year = get_fiscal_year(today(), company="_Test Company")[0]
|
fiscal_year = get_fiscal_year(today(), company="_Test Company")[0]
|
||||||
|
|
||||||
@ -210,6 +320,23 @@ def create_tax_with_holding_category():
|
|||||||
}]
|
}]
|
||||||
}).insert()
|
}).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
|
# Single thresold
|
||||||
if not frappe.db.exists("Tax Withholding Category", "Single Threshold TDS"):
|
if not frappe.db.exists("Tax Withholding Category", "Single Threshold TDS"):
|
||||||
frappe.get_doc({
|
frappe.get_doc({
|
||||||
|
@ -44,9 +44,9 @@ def validate_accounting_period(gl_map):
|
|||||||
frappe.throw(_("You cannot create or cancel any accounting entries with in the closed Accounting Period {0}")
|
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)
|
.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:
|
if merge_entries:
|
||||||
gl_map = merge_similar_entries(gl_map)
|
gl_map = merge_similar_entries(gl_map, precision)
|
||||||
for entry in gl_map:
|
for entry in gl_map:
|
||||||
# toggle debit, credit if negative entry
|
# toggle debit, credit if negative entry
|
||||||
if flt(entry.debit) < 0:
|
if flt(entry.debit) < 0:
|
||||||
@ -69,7 +69,7 @@ def process_gl_map(gl_map, merge_entries=True):
|
|||||||
|
|
||||||
return gl_map
|
return gl_map
|
||||||
|
|
||||||
def merge_similar_entries(gl_map):
|
def merge_similar_entries(gl_map, precision=None):
|
||||||
merged_gl_map = []
|
merged_gl_map = []
|
||||||
accounting_dimensions = get_accounting_dimensions()
|
accounting_dimensions = get_accounting_dimensions()
|
||||||
for entry in gl_map:
|
for entry in gl_map:
|
||||||
@ -88,7 +88,9 @@ def merge_similar_entries(gl_map):
|
|||||||
|
|
||||||
company = gl_map[0].company if gl_map else erpnext.get_default_company()
|
company = gl_map[0].company if gl_map else erpnext.get_default_company()
|
||||||
company_currency = erpnext.get_company_currency(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
|
# 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)
|
merged_gl_map = filter(lambda x: flt(x.debit, precision)!=0 or flt(x.credit, precision)!=0, merged_gl_map)
|
||||||
@ -132,8 +134,8 @@ def make_entry(args, adv_adj, update_outstanding, from_repost=False):
|
|||||||
gle.update(args)
|
gle.update(args)
|
||||||
gle.flags.ignore_permissions = 1
|
gle.flags.ignore_permissions = 1
|
||||||
gle.flags.from_repost = from_repost
|
gle.flags.from_repost = from_repost
|
||||||
gle.insert()
|
gle.flags.adv_adj = adv_adj
|
||||||
gle.run_method("on_update_with_args", adv_adj, update_outstanding, from_repost)
|
gle.flags.update_outstanding = update_outstanding or 'Yes'
|
||||||
gle.submit()
|
gle.submit()
|
||||||
|
|
||||||
if not from_repost:
|
if not from_repost:
|
||||||
|
@ -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));
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -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"
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
||||||
)
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -897,18 +897,18 @@ def repost_gle_for_stock_vouchers(stock_vouchers, posting_date, company=None, wa
|
|||||||
frappe.db.sql("""delete from `tabGL Entry`
|
frappe.db.sql("""delete from `tabGL Entry`
|
||||||
where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
|
where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
|
||||||
|
|
||||||
|
|
||||||
if not warehouse_account:
|
if not warehouse_account:
|
||||||
warehouse_account = get_warehouse_account_map(company)
|
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:
|
for voucher_type, voucher_no in stock_vouchers:
|
||||||
existing_gle = gle.get((voucher_type, voucher_no), [])
|
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)
|
expected_gle = voucher_obj.get_gl_entries(warehouse_account)
|
||||||
if expected_gle:
|
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)
|
_delete_gl_entries(voucher_type, voucher_no)
|
||||||
voucher_obj.make_gl_entries(gl_entries=expected_gle, from_repost=True)
|
voucher_obj.make_gl_entries(gl_entries=expected_gle, from_repost=True)
|
||||||
else:
|
else:
|
||||||
@ -954,16 +954,17 @@ def get_voucherwise_gl_entries(future_stock_vouchers, posting_date):
|
|||||||
|
|
||||||
return gl_entries
|
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
|
matched = True
|
||||||
for entry in expected_gle:
|
for entry in expected_gle:
|
||||||
account_existed = False
|
account_existed = False
|
||||||
for e in existing_gle:
|
for e in existing_gle:
|
||||||
if entry.account == e.account:
|
if entry.account == e.account:
|
||||||
account_existed = True
|
account_existed = True
|
||||||
if entry.account == e.account and entry.against_account == e.against_account \
|
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 (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):
|
and ( flt(entry.debit, precision) != flt(e.debit, precision) or
|
||||||
|
flt(entry.credit, precision) != flt(e.credit, precision))):
|
||||||
matched = False
|
matched = False
|
||||||
break
|
break
|
||||||
if not account_existed:
|
if not account_existed:
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"depends_on": "eval:!doc.asset_category_name",
|
|
||||||
"fieldname": "asset_category_name",
|
"fieldname": "asset_category_name",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
@ -67,7 +66,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-01-22 12:31:14.425319",
|
"modified": "2021-02-24 15:05:38.621803",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Assets",
|
"module": "Assets",
|
||||||
"name": "Asset Category",
|
"name": "Asset Category",
|
||||||
|
@ -74,7 +74,7 @@ class StockController(AccountsController):
|
|||||||
|
|
||||||
gl_list = []
|
gl_list = []
|
||||||
warehouse_with_no_account = []
|
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:
|
for item_row in voucher_details:
|
||||||
|
|
||||||
sle_list = sle_map.get(item_row.name)
|
sle_list = sle_map.get(item_row.name)
|
||||||
@ -131,7 +131,13 @@ class StockController(AccountsController):
|
|||||||
if frappe.db.get_value("Warehouse", wh, "company"):
|
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))
|
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):
|
def update_stock_ledger_entries(self, sle):
|
||||||
sle.valuation_rate = get_valuation_rate(sle.item_code, sle.warehouse,
|
sle.valuation_rate = get_valuation_rate(sle.item_code, sle.warehouse,
|
||||||
@ -244,7 +250,7 @@ class StockController(AccountsController):
|
|||||||
.format(item.idx, frappe.bold(item.item_code), msg), title=_("Expense Account Missing"))
|
.format(item.idx, frappe.bold(item.item_code), msg), title=_("Expense Account Missing"))
|
||||||
|
|
||||||
else:
|
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"
|
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:
|
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")
|
frappe.throw(_("Expense / Difference account ({0}) must be a 'Profit or Loss' account")
|
||||||
|
@ -204,8 +204,8 @@ def new_bank_transaction(transaction):
|
|||||||
"date": getdate(transaction["date"]),
|
"date": getdate(transaction["date"]),
|
||||||
"status": status,
|
"status": status,
|
||||||
"bank_account": bank_account,
|
"bank_account": bank_account,
|
||||||
"debit": debit,
|
"deposit": debit,
|
||||||
"credit": credit,
|
"withdrawal": credit,
|
||||||
"currency": transaction["iso_currency_code"],
|
"currency": transaction["iso_currency_code"],
|
||||||
"transaction_id": transaction["transaction_id"],
|
"transaction_id": transaction["transaction_id"],
|
||||||
"reference_number": transaction["payment_meta"]["reference_number"],
|
"reference_number": transaction["payment_meta"]["reference_number"],
|
||||||
|
@ -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": []
|
||||||
|
}
|
@ -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": []
|
||||||
|
}
|
@ -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": []
|
||||||
|
}
|
@ -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": []
|
||||||
|
}
|
@ -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")
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
@ -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
|
||||||
|
}
|
@ -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
|
||||||
|
}]
|
||||||
|
}
|
@ -201,7 +201,9 @@ def request_loan_closure(loan, posting_date=None):
|
|||||||
write_off_limit = frappe.get_value('Loan Type', loan_type, 'write_off_amount')
|
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
|
# 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
|
# Auto create loan write off and update status as loan closure requested
|
||||||
write_off = make_loan_write_off(loan)
|
write_off = make_loan_write_off(loan)
|
||||||
write_off.submit()
|
write_off.submit()
|
||||||
@ -348,3 +350,13 @@ def validate_employee_currency_with_company_currency(applicant, company):
|
|||||||
if employee_currency != company_currency:
|
if employee_currency != company_currency:
|
||||||
frappe.throw(_("Loan cannot be repayed from salary for Employee {0} because salary is processed in currency {1}")
|
frappe.throw(_("Loan cannot be repayed from salary for Employee {0} because salary is processed in currency {1}")
|
||||||
.format(applicant, employee_currency))
|
.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"
|
||||||
|
}
|
@ -547,7 +547,7 @@ class TestLoan(unittest.TestCase):
|
|||||||
|
|
||||||
# 30 days - grace period
|
# 30 days - grace period
|
||||||
penalty_days = 30 - 4
|
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)
|
penalty_amount = flt((((penalty_applicable_amount * 25) / 100) * penalty_days), 2)
|
||||||
process = process_loan_interest_accrual_for_demand_loans(posting_date = '2019-11-30')
|
process = process_loan_interest_accrual_for_demand_loans(posting_date = '2019-11-30')
|
||||||
|
|
||||||
|
@ -197,7 +197,7 @@ def get_proposed_pledge(securities):
|
|||||||
security.qty = cint(security.amount/security.loan_security_price)
|
security.qty = cint(security.amount/security.loan_security_price)
|
||||||
|
|
||||||
security.amount = security.qty * 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
|
maximum_loan_amount += security.post_haircut_amount
|
||||||
|
|
||||||
|
@ -246,7 +246,5 @@ def get_per_day_interest(principal_amount, rate_of_interest, posting_date=None):
|
|||||||
if not posting_date:
|
if not posting_date:
|
||||||
posting_date = getdate()
|
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))
|
||||||
|
|
||||||
return flt((principal_amount * rate_of_interest) / (days_in_year(get_datetime(posting_date).year) * 100), precision)
|
|
||||||
|
|
||||||
|
@ -81,8 +81,8 @@ class LoanRepayment(AccountsController):
|
|||||||
last_accrual_date = get_last_accrual_date(self.against_loan)
|
last_accrual_date = get_last_accrual_date(self.against_loan)
|
||||||
|
|
||||||
# get posting date upto which interest has to be accrued
|
# get posting date upto which interest has to be accrued
|
||||||
per_day_interest = flt(get_per_day_interest(self.pending_principal_amount,
|
per_day_interest = get_per_day_interest(self.pending_principal_amount,
|
||||||
self.rate_of_interest, self.posting_date), 2)
|
self.rate_of_interest, self.posting_date)
|
||||||
|
|
||||||
no_of_days = flt(flt(self.total_interest_paid - self.interest_payable,
|
no_of_days = flt(flt(self.total_interest_paid - self.interest_payable,
|
||||||
precision)/per_day_interest, 0) - 1
|
precision)/per_day_interest, 0) - 1
|
||||||
@ -105,8 +105,6 @@ class LoanRepayment(AccountsController):
|
|||||||
})
|
})
|
||||||
|
|
||||||
def update_paid_amount(self):
|
def update_paid_amount(self):
|
||||||
precision = cint(frappe.db.get_default("currency_precision")) or 2
|
|
||||||
|
|
||||||
loan = frappe.get_doc("Loan", self.against_loan)
|
loan = frappe.get_doc("Loan", self.against_loan)
|
||||||
|
|
||||||
for payment in self.repayment_details:
|
for payment in self.repayment_details:
|
||||||
@ -114,7 +112,7 @@ class LoanRepayment(AccountsController):
|
|||||||
SET paid_principal_amount = `paid_principal_amount` + %s,
|
SET paid_principal_amount = `paid_principal_amount` + %s,
|
||||||
paid_interest_amount = `paid_interest_amount` + %s
|
paid_interest_amount = `paid_interest_amount` + %s
|
||||||
WHERE name = %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
|
frappe.db.sql(""" UPDATE `tabLoan` SET total_amount_paid = %s, total_principal_paid = %s
|
||||||
WHERE name = %s """, (loan.total_amount_paid + self.amount_paid,
|
WHERE name = %s """, (loan.total_amount_paid + self.amount_paid,
|
||||||
@ -148,8 +146,6 @@ class LoanRepayment(AccountsController):
|
|||||||
frappe.db.set_value("Loan", self.against_loan, "status", "Disbursed")
|
frappe.db.set_value("Loan", self.against_loan, "status", "Disbursed")
|
||||||
|
|
||||||
def allocate_amounts(self, repayment_details):
|
def allocate_amounts(self, repayment_details):
|
||||||
precision = cint(frappe.db.get_default("currency_precision")) or 2
|
|
||||||
|
|
||||||
self.set('repayment_details', [])
|
self.set('repayment_details', [])
|
||||||
self.principal_amount_paid = 0
|
self.principal_amount_paid = 0
|
||||||
total_interest_paid = 0
|
total_interest_paid = 0
|
||||||
@ -185,21 +181,18 @@ class LoanRepayment(AccountsController):
|
|||||||
# no of days for which to accrue interest
|
# no of days for which to accrue interest
|
||||||
# Interest can only be accrued for an entire day and not partial
|
# Interest can only be accrued for an entire day and not partial
|
||||||
if interest_paid > repayment_details['unaccrued_interest']:
|
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']
|
interest_paid -= repayment_details['unaccrued_interest']
|
||||||
total_interest_paid += repayment_details['unaccrued_interest']
|
total_interest_paid += repayment_details['unaccrued_interest']
|
||||||
else:
|
else:
|
||||||
# get no of days for which interest can be paid
|
# get no of days for which interest can be paid
|
||||||
per_day_interest = flt(get_per_day_interest(self.pending_principal_amount,
|
per_day_interest = get_per_day_interest(self.pending_principal_amount,
|
||||||
self.rate_of_interest, self.posting_date), precision)
|
self.rate_of_interest, self.posting_date)
|
||||||
|
|
||||||
no_of_days = cint(interest_paid/per_day_interest)
|
no_of_days = cint(interest_paid/per_day_interest)
|
||||||
total_interest_paid += no_of_days * per_day_interest
|
total_interest_paid += no_of_days * per_day_interest
|
||||||
interest_paid -= no_of_days * per_day_interest
|
interest_paid -= no_of_days * per_day_interest
|
||||||
|
|
||||||
self.total_interest_paid = total_interest_paid
|
self.total_interest_paid = total_interest_paid
|
||||||
|
|
||||||
if interest_paid:
|
if interest_paid:
|
||||||
self.principal_amount_paid += interest_paid
|
self.principal_amount_paid += interest_paid
|
||||||
|
|
||||||
@ -369,7 +362,7 @@ def get_amounts(amounts, against_loan, posting_date):
|
|||||||
if pending_days > 0:
|
if pending_days > 0:
|
||||||
principal_amount = flt(pending_principal_amount, precision)
|
principal_amount = flt(pending_principal_amount, precision)
|
||||||
per_day_interest = get_per_day_interest(principal_amount, loan_type_details.rate_of_interest, posting_date)
|
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["pending_principal_amount"] = flt(pending_principal_amount, precision)
|
||||||
amounts["payable_principal_amount"] = flt(payable_principal_amount, precision)
|
amounts["payable_principal_amount"] = flt(payable_principal_amount, precision)
|
||||||
|
@ -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"
|
||||||
|
}
|
@ -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"
|
||||||
|
}
|
@ -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"
|
||||||
|
}
|
@ -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"
|
||||||
|
}
|
@ -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"
|
||||||
|
}
|
@ -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"
|
||||||
|
}
|
@ -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"
|
||||||
|
}
|
23
erpnext/loan_management/number_card/new_loans/new_loans.json
Normal file
23
erpnext/loan_management/number_card/new_loans/new_loans.json
Normal file
@ -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"
|
||||||
|
}
|
@ -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"
|
||||||
|
}
|
@ -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"
|
||||||
|
}
|
@ -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"
|
||||||
|
}
|
@ -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"
|
||||||
|
}
|
@ -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"
|
||||||
|
}
|
@ -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"
|
||||||
|
}
|
@ -36,7 +36,7 @@ def get_columns(filters):
|
|||||||
|
|
||||||
def get_data(filters):
|
def get_data(filters):
|
||||||
data = []
|
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,
|
pledge_values, total_value_map, applicant_type_map = get_applicant_wise_total_loan_security_qty(filters,
|
||||||
loan_security_details)
|
loan_security_details)
|
||||||
|
|
||||||
@ -64,7 +64,7 @@ def get_data(filters):
|
|||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def get_loan_security_details(filters):
|
def get_loan_security_details():
|
||||||
security_detail_map = {}
|
security_detail_map = {}
|
||||||
loan_security_price_map = {}
|
loan_security_price_map = {}
|
||||||
lsp_validity_map = {}
|
lsp_validity_map = {}
|
||||||
|
@ -171,7 +171,7 @@ def get_loan_wise_pledges(filters):
|
|||||||
return current_pledges
|
return current_pledges
|
||||||
|
|
||||||
def get_loan_wise_security_value(filters, 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 = {}
|
loan_wise_security_value = {}
|
||||||
|
|
||||||
for key in current_pledges:
|
for key in current_pledges:
|
||||||
|
@ -35,7 +35,7 @@ def get_columns(filters):
|
|||||||
|
|
||||||
def get_data(filters):
|
def get_data(filters):
|
||||||
data = []
|
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)
|
current_pledges, total_portfolio_value = get_company_wise_loan_security_details(filters, loan_security_details)
|
||||||
currency = erpnext.get_company_currency(filters.get('company'))
|
currency = erpnext.get_company_currency(filters.get('company'))
|
||||||
|
|
||||||
@ -76,7 +76,7 @@ def get_company_wise_loan_security_details(filters, loan_security_details):
|
|||||||
if qty:
|
if qty:
|
||||||
security_wise_map[key[1]]['applicant_count'] += 1
|
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
|
return security_wise_map, total_portfolio_value
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
"hide_custom": 0,
|
"hide_custom": 0,
|
||||||
"icon": "loan",
|
"icon": "loan",
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
|
"is_default": 0,
|
||||||
"is_standard": 1,
|
"is_standard": 1,
|
||||||
"label": "Loan Management",
|
"label": "Loan Management",
|
||||||
"links": [
|
"links": [
|
||||||
@ -219,7 +220,7 @@
|
|||||||
"type": "Link"
|
"type": "Link"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2021-01-12 11:27:56.079724",
|
"modified": "2021-02-18 17:31:53.586508",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Loan Management",
|
"module": "Loan Management",
|
||||||
"name": "Loan Management",
|
"name": "Loan Management",
|
||||||
@ -239,6 +240,12 @@
|
|||||||
"label": "Loan",
|
"label": "Loan",
|
||||||
"link_to": "Loan",
|
"link_to": "Loan",
|
||||||
"type": "DocType"
|
"type": "DocType"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"doc_view": "",
|
||||||
|
"label": "Dashboard",
|
||||||
|
"link_to": "Loan Dashboard",
|
||||||
|
"type": "Dashboard"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -88,7 +88,7 @@ def get_bom_stock(filters):
|
|||||||
GROUP BY bom_item.item_code""".format(qty_field=qty_field, table=table, conditions=conditions, bom=bom), as_dict=1)
|
GROUP BY bom_item.item_code""".format(qty_field=qty_field, table=table, conditions=conditions, bom=bom), as_dict=1)
|
||||||
|
|
||||||
def get_manufacturer_records():
|
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()
|
manufacture_details = frappe._dict()
|
||||||
for detail in details:
|
for detail in details:
|
||||||
dic = manufacture_details.setdefault(detail.get('parent'), {})
|
dic = manufacture_details.setdefault(detail.get('parent'), {})
|
||||||
|
@ -754,4 +754,5 @@ erpnext.patches.v13_0.setup_patient_history_settings_for_standard_doctypes
|
|||||||
erpnext.patches.v13_0.add_naming_series_to_old_projects # 1-02-2021
|
erpnext.patches.v13_0.add_naming_series_to_old_projects # 1-02-2021
|
||||||
erpnext.patches.v12_0.add_state_code_for_ladakh
|
erpnext.patches.v12_0.add_state_code_for_ladakh
|
||||||
erpnext.patches.v13_0.item_reposting_for_incorrect_sl_and_gl
|
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
|
erpnext.patches.v13_0.update_vehicle_no_reqd_condition
|
@ -7,9 +7,20 @@ import frappe
|
|||||||
def execute():
|
def execute():
|
||||||
frappe.reload_doc("accounts", "doctype", "bank_transaction")
|
frappe.reload_doc("accounts", "doctype", "bank_transaction")
|
||||||
|
|
||||||
frappe.db.sql(""" UPDATE `tabBank Transaction`
|
bank_transaction_fields = frappe.get_meta("Bank Transaction").get_valid_columns()
|
||||||
SET status = 'Reconciled'
|
|
||||||
WHERE
|
if 'debit' in bank_transaction_fields:
|
||||||
status = 'Settled' and (debit = allocated_amount or credit = allocated_amount)
|
frappe.db.sql(""" UPDATE `tabBank Transaction`
|
||||||
and ifnull(allocated_amount, 0) > 0
|
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
|
||||||
|
""")
|
@ -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")
|
@ -1,14 +1,30 @@
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
|
from frappe.utils import getdate, get_time
|
||||||
from erpnext.stock.stock_ledger import update_entries_after
|
from erpnext.stock.stock_ledger import update_entries_after
|
||||||
from erpnext.accounts.utils import update_gl_entries_after
|
from erpnext.accounts.utils import update_gl_entries_after
|
||||||
|
|
||||||
def execute():
|
def execute():
|
||||||
data = frappe.db.sql(''' SELECT name, item_code, warehouse, voucher_type, voucher_no, posting_date, posting_time
|
frappe.reload_doc('stock', 'doctype', 'repost_item_valuation')
|
||||||
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)
|
|
||||||
|
|
||||||
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({
|
update_entries_after({
|
||||||
"item_code": d.item_code,
|
"item_code": d.item_code,
|
||||||
"warehouse": d.warehouse,
|
"warehouse": d.warehouse,
|
||||||
@ -19,9 +35,16 @@ def execute():
|
|||||||
"sle_id": d.name
|
"sle_id": d.name
|
||||||
}, allow_negative_stock=True)
|
}, 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}):
|
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
|
frappe.db.auto_commit_on_many_writes = 0
|
@ -75,24 +75,27 @@ frappe.ui.form.on("Project", {
|
|||||||
frm.add_custom_button(__('Cancelled'), () => {
|
frm.add_custom_button(__('Cancelled'), () => {
|
||||||
frm.events.set_status(frm, 'Cancelled');
|
frm.events.set_status(frm, 'Cancelled');
|
||||||
}, __('Set Status'));
|
}, __('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"), () => {
|
if (frappe.model.can_read("Task")) {
|
||||||
frappe.call('erpnext.projects.doctype.project.project.create_kanban_board_if_not_exists', {
|
frm.add_custom_button(__("Gantt Chart"), function () {
|
||||||
project: frm.doc.project_name
|
frappe.route_options = {
|
||||||
}).then(() => {
|
"project": frm.doc.name
|
||||||
frappe.set_route('List', 'Task', 'Kanban', frm.doc.project_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) {
|
create_duplicate: function(frm) {
|
||||||
|
@ -37,7 +37,7 @@ class TestProject(unittest.TestCase):
|
|||||||
|
|
||||||
task1 = task_exists("Test Template Task Parent")
|
task1 = task_exists("Test Template Task Parent")
|
||||||
if not task1:
|
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")
|
task2 = task_exists("Test Template Task Child 1")
|
||||||
if not task2:
|
if not task2:
|
||||||
@ -52,7 +52,7 @@ class TestProject(unittest.TestCase):
|
|||||||
tasks = frappe.get_all('Task', ['subject','exp_end_date','depends_on_tasks', 'name', 'parent_task'], dict(project=project.name), order_by='creation asc')
|
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(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(tasks[1].subject, 'Test Template Task Child 1')
|
||||||
self.assertEqual(getdate(tasks[1].exp_end_date), calculate_end_date(project, 1, 3))
|
self.assertEqual(getdate(tasks[1].exp_end_date), calculate_end_date(project, 1, 3))
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user