diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.json b/erpnext/accounts/doctype/bank_transaction/bank_transaction.json
index 6f6e97ae355..2a29ed0d957 100644
--- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.json
+++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.json
@@ -495,6 +495,134 @@
"translatable": 0,
"unique": 1
},
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 1,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "payment_entries",
+ "fieldtype": "Table",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Payment Entries",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Bank Transaction Payments",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "translatable": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_17",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "translatable": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "allocated_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Allocated Amount",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "translatable": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "unallocated_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Unallocated Amount",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "translatable": 0,
+ "unique": 0
+ },
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
@@ -526,39 +654,6 @@
"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_entry",
- "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": "Payment Entry",
- "length": 0,
- "no_copy": 0,
- "options": "Payment 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,
- "translatable": 0,
- "unique": 0
}
],
"has_web_view": 0,
@@ -571,7 +666,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2018-11-27 13:26:53.794350",
+ "modified": "2018-11-28 11:05:05.087606",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Bank Transaction",
@@ -640,7 +735,7 @@
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
- "sort_field": "modified",
+ "sort_field": "date",
"sort_order": "DESC",
"title_field": "bank_account",
"track_changes": 0,
diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
index efa9093339b..195816689f0 100644
--- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
+++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
@@ -4,7 +4,25 @@
from __future__ import unicode_literals
import frappe
+from frappe import _
from frappe.model.document import Document
+from frappe.utils import flt
class BankTransaction(Document):
- pass
+ def after_insert(self):
+ self.unallocated_amount = abs(flt(self.credit) - flt(self.debit))
+
+ def on_update_after_submit(self):
+ linked_payments = [x.payment_entry for x in self.payment_entries]
+
+ if linked_payments:
+ allocated_amount = frappe.get_all("Payment Entry", filters=[["name", "in", linked_payments]], fields=["sum(paid_amount) as total"])
+
+ frappe.db.set_value(self.doctype, self.name, "allocated_amount", flt(allocated_amount[0].total))
+ frappe.db.set_value(self.doctype, self.name, "unallocated_amount", abs(flt(self.credit) - flt(self.debit)) - flt(allocated_amount[0].total))
+
+ else:
+ frappe.db.set_value(self.doctype, self.name, "allocated_amount", 0)
+ frappe.db.set_value(self.doctype, self.name, "unallocated_amount", abs(flt(self.credit) - flt(self.debit)))
+
+ self.reload()
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction_list.js b/erpnext/accounts/doctype/bank_transaction/bank_transaction_list.js
new file mode 100644
index 00000000000..ece3cd7e435
--- /dev/null
+++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction_list.js
@@ -0,0 +1,13 @@
+// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
+// License: GNU General Public License v3. See license.txt
+
+frappe.listview_settings['Bank Transaction'] = {
+ add_fields: ["unallocated_amount"],
+ get_indicator: function(doc) {
+ if(flt(doc.unallocated_amount)>0) {
+ return [__("Unreconciled"), "orange", "unallocated_amount,>,0"]
+ } else if(flt(doc.unallocated_amount)==0) {
+ return [__("Reconciled"), "green", "unallocated_amount,=,0"];
+ }
+ }
+};
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/bank_transaction_payments/__init__.py b/erpnext/accounts/doctype/bank_transaction_payments/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/accounts/doctype/bank_transaction_payments/bank_transaction_payments.json b/erpnext/accounts/doctype/bank_transaction_payments/bank_transaction_payments.json
new file mode 100644
index 00000000000..7b1ad0fe2ea
--- /dev/null
+++ b/erpnext/accounts/doctype/bank_transaction_payments/bank_transaction_payments.json
@@ -0,0 +1,109 @@
+{
+ "allow_copy": 0,
+ "allow_events_in_timeline": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "beta": 0,
+ "creation": "2018-11-28 08:55:40.815355",
+ "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": "payment_document",
+ "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": "Payment Document",
+ "length": 0,
+ "no_copy": 0,
+ "options": "DocType",
+ "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_entry",
+ "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": "Payment Entry",
+ "length": 0,
+ "no_copy": 0,
+ "options": "payment_document",
+ "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-11-28 12:34:41.685571",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Bank Transaction Payments",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "show_name_in_global_search": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1,
+ "track_seen": 0,
+ "track_views": 0
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/bank_transaction_payments/bank_transaction_payments.py b/erpnext/accounts/doctype/bank_transaction_payments/bank_transaction_payments.py
new file mode 100644
index 00000000000..88ac38fde53
--- /dev/null
+++ b/erpnext/accounts/doctype/bank_transaction_payments/bank_transaction_payments.py
@@ -0,0 +1,10 @@
+# -*- 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.model.document import Document
+
+class BankTransactionPayments(Document):
+ pass
diff --git a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js
index 58b7c4a6c1d..0aaf48891b8 100644
--- a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js
+++ b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js
@@ -43,7 +43,7 @@ erpnext.accounts.bankReconciliation = class BankReconciliation {
add_plaid_btn() {
const me = this;
frappe.db.get_value("Plaid Settings", "Plaid Settings", "enabled", (r) => {
- if (r.enabled == "1") {
+ if (r && r.enabled == "1") {
me.parent.page.add_inner_button(__('Link a new bank account'), function() {
new erpnext.accounts.plaidLink(this)
})
@@ -116,6 +116,7 @@ erpnext.accounts.bankTransactionUpload = class bankTransactionUpload {
no_socketio: true,
sample_url: "e.g. http://example.com/somefile.csv",
callback: function(attachment, r) {
+ console.log(r)
if (!r.exc && r.message) {
me.data = r.message;
me.setup_transactions_dom();
@@ -132,10 +133,19 @@ erpnext.accounts.bankTransactionUpload = class bankTransactionUpload {
}
create_datatable() {
- this.datatable = new DataTable('.transactions-table', {
- columns: this.data.columns,
- data: this.data.data
- })
+ 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 by ERPNext.
+
It should be a standard CSV or XLSX file.
+
The headers should be in the first row.`)
+ frappe.throw(msg)
+ }
+
}
add_primary_action() {
@@ -333,7 +343,7 @@ erpnext.accounts.ReconciliationTool = class ReconciliationTool extends frappe.vi
return Object.assign({}, args, {
...args.filters.push(["Bank Transaction", "docstatus", "=", 1],
- ["Bank Transaction", "payment_entry", "=", ""])
+ ["Bank Transaction", "unallocated_amount", ">", 0])
});
}
@@ -366,6 +376,11 @@ erpnext.accounts.ReconciliationTool = class ReconciliationTool extends frappe.vi
me.$result.append(frappe.render_template("bank_transaction_header"));
}
}
+
+ static trigger_list_update() {
+ const reconciliation_list = erpnext.accounts.ReconciliationTool;
+ reconciliation_list && reconciliation_list.on_update();
+ }
}
erpnext.accounts.ReconciliationRow = class ReconciliationRow {
@@ -446,7 +461,15 @@ erpnext.accounts.ReconciliationRow = class ReconciliationRow {
fieldtype: 'Link',
fieldname: 'payment_entry',
options: 'Payment Entry',
- label: 'Payment Entry'
+ label: 'Payment Entry',
+ get_query: () => {
+ return {
+ filters : [
+ ["Payment Entry", "ifnull(clearance_date, '')", "=", ""],
+ ["Payment Entry", "docstatus", "=", 1]
+ ]
+ }
+ }
},
{
fieldtype: 'HTML',
@@ -473,18 +496,23 @@ erpnext.accounts.ReconciliationRow = class ReconciliationRow {
const payment_entry = $(e.target).attr('data-name');
frappe.xcall('erpnext.accounts.page.bank_reconciliation.bank_reconciliation.reconcile',
{bank_transaction: me.bank_entry, payment_entry: payment_entry})
- .then((result) => console.log(result))
+ .then((result) => {
+ erpnext.accounts.ReconciliationTool.trigger_list_update();
+ me.dialog.hide();
+ })
})
$(me.dialog.body).on('blur', '.input-with-feedback', (e) => {
- e.preventDefault();
- me.dialog.fields_dict['payment_details'].$wrapper.empty();
- frappe.db.get_doc("Payment Entry", e.target.value)
- .then(doc => {
- const details_wrapper = me.dialog.fields_dict.payment_details.$wrapper;
- details_wrapper.append(frappe.render_template("linked_payment_row", doc));
- })
-
+ if (e.target.value) {
+ e.preventDefault();
+ me.dialog.fields_dict['payment_details'].$wrapper.empty();
+ frappe.db.get_doc("Payment Entry", e.target.value)
+ .then(doc => {
+ const details_wrapper = me.dialog.fields_dict.payment_details.$wrapper;
+ details_wrapper.append(frappe.render_template("linked_payment_row", doc));
+ })
+ }
+
});
me.dialog.show();
}
diff --git a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.py b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.py
index 54eda914f8d..caccc9911b4 100644
--- a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.py
+++ b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.py
@@ -7,25 +7,7 @@ import frappe
from frappe import _
import difflib
from operator import itemgetter
-
-@frappe.whitelist()
-def get_linked_payments(bank_transaction):
-
- transaction = frappe.get_doc("Bank Transaction", bank_transaction)
-
- amount_matching = check_matching_amount(transaction)
- description_matching = check_matching_descriptions(transaction)
-
- if amount_matching:
- match = check_amount_vs_description(amount_matching, description_matching)
- if match:
- return match
- else:
- return merge_matching_lists(amount_matching, description_matching)
-
- else:
- linked_payments = get_matching_transactions_payments(description_matching)
- return linked_payments
+from frappe.utils import flt
@frappe.whitelist()
def reconcile(bank_transaction, payment_entry):
@@ -41,13 +23,49 @@ def reconcile(bank_transaction, payment_entry):
if transaction.debit > 0 and payment_entry.payment_type == "Receive":
frappe.throw(_("The selected payment entry should be linked with a creditor bank transaction"))
- frappe.db.set_value("Bank Transaction", bank_transaction, "payment_entry", payment_entry)
+ add_payment_to_transaction(transaction, payment_entry)
+ clear_payment_entry(transaction, payment_entry)
+
+ return 'reconciled'
+
+def add_payment_to_transaction(transaction, payment_entry):
+ transaction.append("payment_entries", {"payment_entry": payment_entry.name})
+ transaction.save()
+
+def clear_payment_entry(transaction, payment_entry):
linked_bank_transactions = frappe.get_all("Bank Transaction", filters={"payment_entry": payment_entry, "docstatus": 1},
fields=["sum(debit) as debit", "sum(credit) as credit"])
- cleared_amount = (linked_bank_transactions[0].credit - linked_bank_transactions[0].debit)
- if cleared_amount == payment_entry.total_allocated_amount:
- frappe.db.set_value("Payment Entry", payment_entry, "clearance_date", transaction.date)
+ cleared_amount = (flt(linked_bank_transactions[0].credit) - flt(linked_bank_transactions[0].debit))
+
+ if cleared_amount == payment_entry.paid_amount:
+ frappe.db.set_value("Payment Entry", payment_entry.name, "clearance_date", transaction.date)
+
+@frappe.whitelist()
+def get_linked_payments(bank_transaction):
+
+ transaction = frappe.get_doc("Bank Transaction", bank_transaction)
+
+ # Get all payment entries with a matching amount
+ amount_matching = check_matching_amount(transaction)
+ print(amount_matching)
+
+ # Get some data from payment entries linked to a corresponding bank transaction
+ description_matching = check_matching_descriptions(transaction)
+ print(description_matching)
+
+ """
+ if amount_matching:
+ match = check_amount_vs_description(amount_matching, description_matching)
+ if match:
+ return match
+ else:
+ return merge_matching_lists(amount_matching, description_matching)
+
+ else:
+ linked_payments = get_matching_transactions_payments(description_matching)
+ return linked_payments
+ """
def check_matching_amount(transaction):
amount = transaction.credit if transaction.credit > 0 else transaction.debit
@@ -55,32 +73,52 @@ def check_matching_amount(transaction):
payments = 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=[["paid_amount", "like", "{0}%".format(amount)],
- ["docstatus", "=", "1"], ["payment_type", "=", payment_type], ["clearance_date", "=", ""]])
+ ["docstatus", "=", "1"], ["payment_type", "=", payment_type], ["ifnull(clearance_date, '')", "=", ""]])
return payments
-
def check_matching_descriptions(transaction):
- bank_transactions = frappe.get_all("Bank Transaction", fields=["name", "description", "payment_entry", "date"],
- filters=[["docstatus", "=", "1"], ["payment_entry", "!=", ""]])
+ 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)
- result = []
+ 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.5:
bank_transaction["ratio"] = seq.ratio()
- result.append(bank_transaction)
+ selection.append(bank_transaction)
- return result
+ document_types = set([x["payment_document"] for x in selection])
+
+ for document_type in document_types:
+ print(document_type)
+
+
+ return selection
def check_amount_vs_description(amount_matching, description_matching):
result = []
+ print(description_matching)
+ print(amount_matching)
for match in amount_matching:
- result.append([match for x in description_matching if match["name"]==x["payment_entry"]])
-
- return match
+ m = [match for x in description_matching.payment_entries if match["name"]==x["payment_entry"]]
+ result.append(m)
+ print(result)
+ return result
def merge_matching_lists(amount_matching, description_matching):
@@ -100,10 +138,10 @@ def get_matching_transactions_payments(description_matching):
payment_by_ratio = {x["payment_entry"]: x["ratio"] for x in description_matching}
if payments:
- payment_list = frappe.get_all("Payment Entry", fields=["name", "paid_amount", "payment_type", "reference_no", "reference_date",
+ 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(payment_list, key=lambda x: payment_by_ratio[x["name"]])
+ return sorted(reference_payment_list, key=lambda x: payment_by_ratio[x["name"]])
else:
return []
\ No newline at end of file