fixed merge conflict
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Account Subtype', {
|
||||
refresh: function() {
|
||||
|
||||
}
|
||||
});
|
||||
134
erpnext/accounts/doctype/account_subtype/account_subtype.json
Normal file
134
erpnext/accounts/doctype/account_subtype/account_subtype.json
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 1,
|
||||
"allow_rename": 1,
|
||||
"autoname": "field:account_subtype",
|
||||
"beta": 0,
|
||||
"creation": "2018-10-25 15:46:08.054586",
|
||||
"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": "account_subtype",
|
||||
"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": "Account Subtype",
|
||||
"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": 1
|
||||
}
|
||||
],
|
||||
"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-10-25 15:47:03.841390",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Account Subtype",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Accounts Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Accounts User",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 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": 0,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from frappe.model.document import Document
|
||||
|
||||
class AccountSubtype(Document):
|
||||
pass
|
||||
@@ -0,0 +1,23 @@
|
||||
/* eslint-disable */
|
||||
// rename this file from _test_[name] to test_[name] to activate
|
||||
// and remove above this line
|
||||
|
||||
QUnit.test("test: Account Subtype", function (assert) {
|
||||
let done = assert.async();
|
||||
|
||||
// number of asserts
|
||||
assert.expect(1);
|
||||
|
||||
frappe.run_serially([
|
||||
// insert a new Account Subtype
|
||||
() => frappe.tests.make('Account Subtype', [
|
||||
// values to be set
|
||||
{key: 'value'}
|
||||
]),
|
||||
() => {
|
||||
assert.equal(cur_frm.doc.key, 'value');
|
||||
},
|
||||
() => done()
|
||||
]);
|
||||
|
||||
});
|
||||
@@ -0,0 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import unittest
|
||||
|
||||
class TestAccountSubtype(unittest.TestCase):
|
||||
pass
|
||||
0
erpnext/accounts/doctype/account_type/__init__.py
Normal file
0
erpnext/accounts/doctype/account_type/__init__.py
Normal file
8
erpnext/accounts/doctype/account_type/account_type.js
Normal file
8
erpnext/accounts/doctype/account_type/account_type.js
Normal file
@@ -0,0 +1,8 @@
|
||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Account Type', {
|
||||
refresh: function() {
|
||||
|
||||
}
|
||||
});
|
||||
134
erpnext/accounts/doctype/account_type/account_type.json
Normal file
134
erpnext/accounts/doctype/account_type/account_type.json
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 1,
|
||||
"allow_rename": 1,
|
||||
"autoname": "field:account_type",
|
||||
"beta": 0,
|
||||
"creation": "2018-10-25 15:45:45.789963",
|
||||
"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": "account_type",
|
||||
"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": "Account Type",
|
||||
"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": 1
|
||||
}
|
||||
],
|
||||
"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-10-25 15:46:51.042604",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Account Type",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Accounts Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Accounts User",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 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": 0,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
}
|
||||
9
erpnext/accounts/doctype/account_type/account_type.py
Normal file
9
erpnext/accounts/doctype/account_type/account_type.py
Normal file
@@ -0,0 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from frappe.model.document import Document
|
||||
|
||||
class AccountType(Document):
|
||||
pass
|
||||
23
erpnext/accounts/doctype/account_type/test_account_type.js
Normal file
23
erpnext/accounts/doctype/account_type/test_account_type.js
Normal file
@@ -0,0 +1,23 @@
|
||||
/* eslint-disable */
|
||||
// rename this file from _test_[name] to test_[name] to activate
|
||||
// and remove above this line
|
||||
|
||||
QUnit.test("test: Account Type", function (assert) {
|
||||
let done = assert.async();
|
||||
|
||||
// number of asserts
|
||||
assert.expect(1);
|
||||
|
||||
frappe.run_serially([
|
||||
// insert a new Account Type
|
||||
() => frappe.tests.make('Account Type', [
|
||||
// values to be set
|
||||
{key: 'value'}
|
||||
]),
|
||||
() => {
|
||||
assert.equal(cur_frm.doc.key, 'value');
|
||||
},
|
||||
() => done()
|
||||
]);
|
||||
|
||||
});
|
||||
@@ -0,0 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import unittest
|
||||
|
||||
class TestAccountType(unittest.TestCase):
|
||||
pass
|
||||
@@ -2,7 +2,29 @@
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Bank', {
|
||||
onload: function(frm) {
|
||||
add_fields_to_mapping_table(frm);
|
||||
},
|
||||
refresh: function(frm) {
|
||||
|
||||
add_fields_to_mapping_table(frm);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
let add_fields_to_mapping_table = function (frm) {
|
||||
let options = [];
|
||||
|
||||
frappe.model.with_doctype("Bank Transaction", function() {
|
||||
let meta = frappe.get_meta("Bank Transaction");
|
||||
meta.fields.forEach(value => {
|
||||
if (!["Section Break", "Column Break"].includes(value.fieldtype)) {
|
||||
options.push(value.fieldname);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
frappe.meta.get_docfield("Bank Transaction Mapping", "bank_transaction_field",
|
||||
frm.doc.name).options = options;
|
||||
|
||||
frm.fields_dict.bank_transaction_mapping.grid.refresh();
|
||||
};
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
@@ -15,6 +16,7 @@
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@@ -42,6 +44,134 @@
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 1,
|
||||
"columns": 0,
|
||||
"fieldname": "data_import_configuration_section",
|
||||
"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": "Data Import Configuration",
|
||||
"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_transaction_mapping",
|
||||
"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": "Bank Transaction Mapping",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Bank Transaction Mapping",
|
||||
"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": "section_break_4",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "plaid_access_token",
|
||||
"fieldtype": "Data",
|
||||
"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": "Plaid Access Token",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"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
|
||||
}
|
||||
],
|
||||
@@ -55,7 +185,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-04-07 17:00:21.246202",
|
||||
"modified": "2018-11-27 16:12:13.938776",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Bank",
|
||||
@@ -64,7 +194,6 @@
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
@@ -90,5 +219,6 @@
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Bank Account', {
|
||||
@@ -24,5 +24,13 @@ frappe.ui.form.on('Bank Account', {
|
||||
else {
|
||||
frappe.contacts.render_address_and_contact(frm);
|
||||
}
|
||||
|
||||
if (frm.doc.integration_id) {
|
||||
frm.add_custom_button(__("Unlink external integrations"), function() {
|
||||
frappe.confirm(__("This action will unlink this account from any external service integrating ERPNext with your bank accounts. It cannot be undone. Are you certain ?"), function() {
|
||||
frm.set_value("integration_id", "");
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,6 +13,9 @@ class BankAccount(Document):
|
||||
"""Load address and contacts in `__onload`"""
|
||||
load_address_and_contact(self)
|
||||
|
||||
def autoname(self):
|
||||
self.name = self.account_name + " - " + self.bank
|
||||
|
||||
def on_trash(self):
|
||||
delete_contact_and_address('BankAccount', self.name)
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Bank Transaction', {
|
||||
onload: function(frm) {
|
||||
frm.set_query('payment_document', 'payment_entries', function() {
|
||||
return {
|
||||
"filters": {
|
||||
"name": ["in", ["Payment Entry", "Journal Entry", "Sales Invoice", "Purchase Invoice", "Expense Claim"]]
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
});
|
||||
744
erpnext/accounts/doctype/bank_transaction/bank_transaction.json
Normal file
744
erpnext/accounts/doctype/bank_transaction/bank_transaction.json
Normal file
@@ -0,0 +1,744 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 1,
|
||||
"allow_rename": 0,
|
||||
"autoname": "naming_series:",
|
||||
"beta": 0,
|
||||
"creation": "2018-10-22 18:19:02.784533",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "ACC-BTN-.YYYY.-",
|
||||
"fieldname": "naming_series",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Series",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "ACC-BTN-.YYYY.-",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 1,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Date",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_2",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "Settled",
|
||||
"fieldname": "status",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Status",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "\nPending\nSettled",
|
||||
"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_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": "Bank Account",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Bank Account",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "section_break_4",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 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,
|
||||
"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,
|
||||
"fieldname": "column_break_7",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "currency",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Currency",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Currency",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "section_break_10",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Description",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "section_break_14",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "reference_number",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Reference Number",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "transaction_id",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Transaction ID",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"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,
|
||||
"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 Transaction",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"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-11-28 11:05:05.087606",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Bank Transaction",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Accounts Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Accounts User",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "date",
|
||||
"sort_order": "DESC",
|
||||
"title_field": "bank_account",
|
||||
"track_changes": 0,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
# -*- 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
|
||||
from frappe.utils import flt
|
||||
|
||||
class BankTransaction(Document):
|
||||
def after_insert(self):
|
||||
self.unallocated_amount = abs(flt(self.credit) - flt(self.debit))
|
||||
|
||||
def on_update_after_submit(self):
|
||||
allocated_amount = reduce(lambda x, y: flt(x) + flt(y), [x.allocated_amount for x in self.payment_entries])
|
||||
|
||||
if allocated_amount:
|
||||
frappe.db.set_value(self.doctype, self.name, "allocated_amount", flt(allocated_amount))
|
||||
frappe.db.set_value(self.doctype, self.name, "unallocated_amount", abs(flt(self.credit) - flt(self.debit)) - flt(allocated_amount))
|
||||
|
||||
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()
|
||||
@@ -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"];
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,75 @@
|
||||
# -*- 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
|
||||
import json
|
||||
from frappe.utils import getdate
|
||||
from frappe.utils.dateutils import parse_date
|
||||
|
||||
@frappe.whitelist()
|
||||
def upload_bank_statement():
|
||||
if getattr(frappe, "uploaded_file", None):
|
||||
with open(frappe.uploaded_file, "rb") as upfile:
|
||||
fcontent = upfile.read()
|
||||
else:
|
||||
from frappe.utils.file_manager import get_uploaded_content
|
||||
fname, fcontent = get_uploaded_content()
|
||||
|
||||
if frappe.safe_encode(fname).lower().endswith("csv"):
|
||||
from frappe.utils.csvutils import read_csv_content
|
||||
rows = read_csv_content(fcontent, False)
|
||||
|
||||
elif frappe.safe_encode(fname).lower().endswith("xlsx"):
|
||||
from frappe.utils.xlsxutils import read_xlsx_file_from_attached_file
|
||||
rows = read_xlsx_file_from_attached_file(fcontent=fcontent)
|
||||
|
||||
columns = rows[0]
|
||||
rows.pop(0)
|
||||
data = rows
|
||||
return {"columns": columns, "data": data}
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def create_bank_entries(columns, data, bank_account):
|
||||
header_map = get_header_mapping(columns, bank_account)
|
||||
|
||||
count = 0
|
||||
for d in json.loads(data):
|
||||
if all(item is None for item in d) is True:
|
||||
continue
|
||||
fields = {}
|
||||
for key, value in header_map.iteritems():
|
||||
fields.update({key: d[int(value)-1]})
|
||||
|
||||
|
||||
bank_transaction = frappe.get_doc({
|
||||
"doctype": "Bank Transaction"
|
||||
})
|
||||
bank_transaction.update(fields)
|
||||
bank_transaction.date = getdate(parse_date(bank_transaction.date))
|
||||
bank_transaction.bank_account = bank_account
|
||||
bank_transaction.insert()
|
||||
bank_transaction.submit()
|
||||
count = count + 1
|
||||
|
||||
return count
|
||||
|
||||
def get_header_mapping(columns, bank_account):
|
||||
mapping = get_bank_mapping(bank_account)
|
||||
|
||||
header_map = {}
|
||||
for column in json.loads(columns):
|
||||
if column["content"] in mapping:
|
||||
header_map.update({mapping[column["content"]]: column["colIndex"]})
|
||||
|
||||
return header_map
|
||||
|
||||
def get_bank_mapping(bank_account):
|
||||
bank_name = frappe.db.get_value("Bank Account", bank_account, "bank")
|
||||
bank = frappe.get_doc("Bank", bank_name)
|
||||
|
||||
mapping = {row.file_field:row.bank_transaction_field for row in bank.bank_transaction_mapping}
|
||||
|
||||
return mapping
|
||||
@@ -0,0 +1,23 @@
|
||||
/* eslint-disable */
|
||||
// rename this file from _test_[name] to test_[name] to activate
|
||||
// and remove above this line
|
||||
|
||||
QUnit.test("test: Bank Transaction", function (assert) {
|
||||
let done = assert.async();
|
||||
|
||||
// number of asserts
|
||||
assert.expect(1);
|
||||
|
||||
frappe.run_serially([
|
||||
// insert a new Bank Transaction
|
||||
() => frappe.tests.make('Bank Transaction', [
|
||||
// values to be set
|
||||
{key: 'value'}
|
||||
]),
|
||||
() => {
|
||||
assert.equal(cur_frm.doc.key, 'value');
|
||||
},
|
||||
() => done()
|
||||
]);
|
||||
|
||||
});
|
||||
@@ -0,0 +1,286 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import unittest
|
||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
|
||||
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
|
||||
from erpnext.accounts.page.bank_reconciliation.bank_reconciliation import reconcile, get_linked_payments
|
||||
|
||||
test_dependencies = ["Item", "Cost Center"]
|
||||
|
||||
class TestBankTransaction(unittest.TestCase):
|
||||
def setUp(self):
|
||||
add_transactions()
|
||||
add_payments()
|
||||
|
||||
def tearDown(self):
|
||||
for bt in frappe.get_all("Bank Transaction"):
|
||||
doc = frappe.get_doc("Bank Transaction", bt.name)
|
||||
doc.cancel()
|
||||
doc.delete()
|
||||
|
||||
# Delete directly in DB to avoid validation errors for countries not allowing deletion
|
||||
frappe.db.sql("""delete from `tabPayment Entry Reference`""")
|
||||
frappe.db.sql("""delete from `tabPayment Entry`""")
|
||||
|
||||
frappe.flags.test_bank_transactions_created = False
|
||||
frappe.flags.test_payments_created = False
|
||||
|
||||
# This test checks if ERPNext is able to provide a linked payment for a bank transaction based on the amount of the bank transaction.
|
||||
def test_linked_payments(self):
|
||||
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="Re 95282925234 FE/000002917 AT171513000281183046 Conrad Electronic"))
|
||||
linked_payments = get_linked_payments(bank_transaction.name)
|
||||
self.assertTrue(linked_payments[0].party == "Conrad Electronic")
|
||||
|
||||
# This test validates a simple reconciliation leading to the clearance of the bank transaction and the payment
|
||||
def test_reconcile(self):
|
||||
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G"))
|
||||
payment = frappe.get_doc("Payment Entry", dict(party="Mr G", paid_amount=1200))
|
||||
reconcile(bank_transaction.name, "Payment Entry", payment.name)
|
||||
|
||||
unallocated_amount = frappe.db.get_value("Bank Transaction", bank_transaction.name, "unallocated_amount")
|
||||
self.assertTrue(unallocated_amount == 0)
|
||||
|
||||
clearance_date = frappe.db.get_value("Payment Entry", payment.name, "clearance_date")
|
||||
self.assertTrue(clearance_date is not None)
|
||||
|
||||
# Check if ERPNext can correctly fetch a linked payment based on the party
|
||||
def test_linked_payments_based_on_party(self):
|
||||
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="1512567 BG/000003025 OPSKATTUZWXXX AT776000000098709849 Herr G"))
|
||||
linked_payments = get_linked_payments(bank_transaction.name)
|
||||
self.assertTrue(len(linked_payments)==1)
|
||||
|
||||
# Check if ERPNext can correctly filter a linked payments based on the debit/credit amount
|
||||
def test_debit_credit_output(self):
|
||||
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07"))
|
||||
linked_payments = get_linked_payments(bank_transaction.name)
|
||||
self.assertTrue(linked_payments[0].payment_type == "Pay")
|
||||
|
||||
# Check error if already reconciled
|
||||
def test_already_reconciled(self):
|
||||
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G"))
|
||||
payment = frappe.get_doc("Payment Entry", dict(party="Mr G", paid_amount=1200))
|
||||
reconcile(bank_transaction.name, "Payment Entry", payment.name)
|
||||
|
||||
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G"))
|
||||
payment = frappe.get_doc("Payment Entry", dict(party="Mr G", paid_amount=1200))
|
||||
self.assertRaises(frappe.ValidationError, reconcile, bank_transaction=bank_transaction.name, payment_doctype="Payment Entry", payment_name=payment.name)
|
||||
|
||||
# Raise an error if creditor transaction vs creditor payment
|
||||
def test_invalid_creditor_reconcilation(self):
|
||||
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="I2015000011 VD/000002514 ATWWXXX AT4701345000003510057 Bio"))
|
||||
payment = frappe.get_doc("Payment Entry", dict(party="Conrad Electronic", paid_amount=690))
|
||||
self.assertRaises(frappe.ValidationError, reconcile, bank_transaction=bank_transaction.name, payment_doctype="Payment Entry", payment_name=payment.name)
|
||||
|
||||
# Raise an error if debitor transaction vs debitor payment
|
||||
def test_invalid_debitor_reconcilation(self):
|
||||
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07"))
|
||||
payment = frappe.get_doc("Payment Entry", dict(party="Fayva", paid_amount=109080))
|
||||
self.assertRaises(frappe.ValidationError, reconcile, bank_transaction=bank_transaction.name, payment_doctype="Payment Entry", payment_name=payment.name)
|
||||
|
||||
# Raise an error if debitor transaction vs debitor payment
|
||||
def test_clear_sales_invoice(self):
|
||||
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="I2015000011 VD/000002514 ATWWXXX AT4701345000003510057 Bio"))
|
||||
payment = frappe.get_doc("Sales Invoice", dict(customer="Fayva", status=["=", "Paid"]))
|
||||
reconcile(bank_transaction.name, "Sales Invoice", payment.name)
|
||||
|
||||
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)
|
||||
|
||||
def add_transactions():
|
||||
if frappe.flags.test_bank_transactions_created:
|
||||
return
|
||||
|
||||
frappe.set_user("Administrator")
|
||||
try:
|
||||
frappe.get_doc({
|
||||
"doctype": "Bank",
|
||||
"bank_name":"Citi Bank",
|
||||
}).insert()
|
||||
except frappe.DuplicateEntryError:
|
||||
pass
|
||||
|
||||
try:
|
||||
frappe.get_doc({
|
||||
"doctype": "Bank Account",
|
||||
"account_name":"Checking Account",
|
||||
"bank": "Citi Bank",
|
||||
"account": "_Test Bank - _TC"
|
||||
}).insert()
|
||||
except frappe.DuplicateEntryError:
|
||||
pass
|
||||
|
||||
doc = frappe.get_doc({
|
||||
"doctype": "Bank Transaction",
|
||||
"description":"1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G",
|
||||
"date": "2018-10-23",
|
||||
"debit": 1200,
|
||||
"currency": "INR",
|
||||
"bank_account": "Checking Account - Citi Bank"
|
||||
}).insert()
|
||||
doc.submit()
|
||||
|
||||
doc = frappe.get_doc({
|
||||
"doctype": "Bank Transaction",
|
||||
"description":"1512567 BG/000003025 OPSKATTUZWXXX AT776000000098709849 Herr G",
|
||||
"date": "2018-10-23",
|
||||
"debit": 1700,
|
||||
"currency": "INR",
|
||||
"bank_account": "Checking Account - Citi Bank"
|
||||
}).insert()
|
||||
doc.submit()
|
||||
|
||||
doc = frappe.get_doc({
|
||||
"doctype": "Bank Transaction",
|
||||
"description":"Re 95282925234 FE/000002917 AT171513000281183046 Conrad Electronic",
|
||||
"date": "2018-10-26",
|
||||
"debit": 690,
|
||||
"currency": "INR",
|
||||
"bank_account": "Checking Account - Citi Bank"
|
||||
}).insert()
|
||||
doc.submit()
|
||||
|
||||
doc = frappe.get_doc({
|
||||
"doctype": "Bank Transaction",
|
||||
"description":"Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07",
|
||||
"date": "2018-10-27",
|
||||
"debit": 3900,
|
||||
"currency": "INR",
|
||||
"bank_account": "Checking Account - Citi Bank"
|
||||
}).insert()
|
||||
doc.submit()
|
||||
|
||||
doc = frappe.get_doc({
|
||||
"doctype": "Bank Transaction",
|
||||
"description":"I2015000011 VD/000002514 ATWWXXX AT4701345000003510057 Bio",
|
||||
"date": "2018-10-27",
|
||||
"credit": 109080,
|
||||
"currency": "INR",
|
||||
"bank_account": "Checking Account - Citi Bank"
|
||||
}).insert()
|
||||
doc.submit()
|
||||
|
||||
frappe.flags.test_bank_transactions_created = True
|
||||
|
||||
def add_payments():
|
||||
if frappe.flags.test_payments_created:
|
||||
return
|
||||
|
||||
frappe.set_user("Administrator")
|
||||
|
||||
try:
|
||||
frappe.get_doc({
|
||||
"doctype": "Supplier",
|
||||
"supplier_group":"All Supplier Groups",
|
||||
"supplier_type": "Company",
|
||||
"supplier_name": "Conrad Electronic"
|
||||
}).insert()
|
||||
|
||||
except frappe.DuplicateEntryError:
|
||||
pass
|
||||
|
||||
pi = make_purchase_invoice(supplier="Conrad Electronic", qty=1, rate=690)
|
||||
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
|
||||
pe.reference_no = "Conrad Oct 18"
|
||||
pe.reference_date = "2018-10-24"
|
||||
pe.insert()
|
||||
pe.submit()
|
||||
|
||||
try:
|
||||
frappe.get_doc({
|
||||
"doctype": "Supplier",
|
||||
"supplier_group":"All Supplier Groups",
|
||||
"supplier_type": "Company",
|
||||
"supplier_name": "Mr G"
|
||||
}).insert()
|
||||
except frappe.DuplicateEntryError:
|
||||
pass
|
||||
|
||||
pi = make_purchase_invoice(supplier="Mr G", qty=1, rate=1200)
|
||||
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
|
||||
pe.reference_no = "Herr G Oct 18"
|
||||
pe.reference_date = "2018-10-24"
|
||||
pe.insert()
|
||||
pe.submit()
|
||||
|
||||
pi = make_purchase_invoice(supplier="Mr G", qty=1, rate=1700)
|
||||
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
|
||||
pe.reference_no = "Herr G Nov 18"
|
||||
pe.reference_date = "2018-11-01"
|
||||
pe.insert()
|
||||
pe.submit()
|
||||
|
||||
try:
|
||||
frappe.get_doc({
|
||||
"doctype": "Supplier",
|
||||
"supplier_group":"All Supplier Groups",
|
||||
"supplier_type": "Company",
|
||||
"supplier_name": "Poore Simon's"
|
||||
}).insert()
|
||||
except frappe.DuplicateEntryError:
|
||||
pass
|
||||
|
||||
try:
|
||||
frappe.get_doc({
|
||||
"doctype": "Customer",
|
||||
"customer_group":"All Customer Groups",
|
||||
"customer_type": "Company",
|
||||
"customer_name": "Poore Simon's"
|
||||
}).insert()
|
||||
except frappe.DuplicateEntryError:
|
||||
pass
|
||||
|
||||
pi = make_purchase_invoice(supplier="Poore Simon's", qty=1, rate=3900)
|
||||
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
|
||||
pe.reference_no = "Poore Simon's Oct 18"
|
||||
pe.reference_date = "2018-10-28"
|
||||
pe.insert()
|
||||
pe.submit()
|
||||
|
||||
si = create_sales_invoice(customer="Poore Simon's", qty=1, rate=3900)
|
||||
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank - _TC")
|
||||
pe.reference_no = "Poore Simon's Oct 18"
|
||||
pe.reference_date = "2018-10-28"
|
||||
pe.insert()
|
||||
pe.submit()
|
||||
|
||||
try:
|
||||
frappe.get_doc({
|
||||
"doctype": "Customer",
|
||||
"customer_group":"All Customer Groups",
|
||||
"customer_type": "Company",
|
||||
"customer_name": "Fayva"
|
||||
}).insert()
|
||||
except frappe.DuplicateEntryError:
|
||||
pass
|
||||
|
||||
si = create_sales_invoice(customer="Fayva", qty=1, rate=109080)
|
||||
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank - _TC")
|
||||
pe.reference_no = "Fayva Oct 18"
|
||||
pe.reference_date = "2018-10-29"
|
||||
pe.insert()
|
||||
pe.submit()
|
||||
|
||||
company = frappe.db.get_single_value('Global Defaults', 'default_company')
|
||||
frappe.get_doc({
|
||||
"doctype": "Mode of Payment",
|
||||
"name": "Cash"
|
||||
}).append("accounts", {
|
||||
"company": company,
|
||||
"default_account": "_Test Bank - _TC"
|
||||
}).save()
|
||||
si = create_sales_invoice(customer="Fayva", qty=1, rate=109080, do_not_submit=1)
|
||||
si.is_pos = 1
|
||||
si.append("payments", {
|
||||
"mode_of_payment": "Cash",
|
||||
"account": "_Test Bank - _TC",
|
||||
"amount": 109080
|
||||
})
|
||||
si.save()
|
||||
si.submit()
|
||||
|
||||
frappe.flags.test_payments_created = True
|
||||
@@ -0,0 +1,107 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
"creation": "2018-10-24 15:24:56.713277",
|
||||
"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_transaction_field",
|
||||
"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": "Field in Bank Transaction",
|
||||
"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": "file_field",
|
||||
"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": "Column in Bank File",
|
||||
"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
|
||||
}
|
||||
],
|
||||
"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-10-24 15:24:56.713277",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Bank Transaction Mapping",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 0,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from frappe.model.document import Document
|
||||
|
||||
class BankTransactionMapping(Document):
|
||||
pass
|
||||
@@ -0,0 +1,141 @@
|
||||
{
|
||||
"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": 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": "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": 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": "allocated_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": "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": 1,
|
||||
"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-12-06 10:57:02.635141",
|
||||
"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
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from frappe.model.document import Document
|
||||
|
||||
class BankTransactionPayments(Document):
|
||||
pass
|
||||
@@ -3401,6 +3401,38 @@
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "clearance_date",
|
||||
"fieldtype": "Date",
|
||||
"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": "Clearance 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,
|
||||
@@ -4726,7 +4758,7 @@
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"menu_index": 0,
|
||||
"modified": "2019-01-07 16:51:59.800081",
|
||||
"modified": "2019-01-07 18:00:41.508642",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Purchase Invoice",
|
||||
|
||||
540
erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js
Normal file
540
erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js
Normal file
@@ -0,0 +1,540 @@
|
||||
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: __('Bank Account'),
|
||||
fieldname: 'bank_account',
|
||||
options: "Bank Account",
|
||||
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;
|
||||
frappe.upload.make({
|
||||
args: {
|
||||
method: 'erpnext.accounts.doctype.bank_transaction.bank_transaction_upload.upload_bank_statement',
|
||||
allow_multiple: 0
|
||||
},
|
||||
no_socketio: true,
|
||||
sample_url: "e.g. http://example.com/somefile.csv",
|
||||
callback: 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 by ERPNext.
|
||||
<br>It should be a standard CSV or XLSX file.
|
||||
<br>The headers should be 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 = __("{0} bank transaction(s) created", [result])
|
||||
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);
|
||||
frappe.show_alert({message:__("All bank transactions have been created"), indicator:'green'});
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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.plaid_configuration')
|
||||
.then(result => {
|
||||
me.plaid_env = result.plaid_env;
|
||||
me.plaid_public_key = result.plaid_public_key;
|
||||
me.client_name = result.client_name;
|
||||
me.sync_transactions()
|
||||
})
|
||||
}
|
||||
|
||||
sync_transactions() {
|
||||
const me = this;
|
||||
frappe.db.get_value("Bank Account", me.parent.bank_account, "bank", (v) => {
|
||||
frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.sync_transactions', {
|
||||
bank: v['bank'],
|
||||
bank_account: me.parent.bank_account,
|
||||
freeze: true
|
||||
})
|
||||
.then((result) => {
|
||||
let result_title = (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_invoice() {
|
||||
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 {
|
||||
filters : [
|
||||
["Payment Entry", "ifnull(clearance_date, '')", "=", ""],
|
||||
["Payment Entry", "docstatus", "=", 1]
|
||||
]
|
||||
}
|
||||
} else if (dt === "Journal Entry") {
|
||||
return {
|
||||
query: "erpnext.accounts.page.bank_reconciliation.bank_reconciliation.journal_entry_query",
|
||||
filters : {
|
||||
"bank_account": this.data.bank_account
|
||||
}
|
||||
}
|
||||
} 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]
|
||||
]
|
||||
}
|
||||
} else if (dt === "Expense Claim") {
|
||||
return {
|
||||
filters : [
|
||||
["Expense Claim", "ifnull(clearance_date, '')", "=", ""],
|
||||
["Expense Claim", "docstatus", "=", 1]
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
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 = []
|
||||
if (dt === "Payment Entry") {
|
||||
doc.currency = doc.payment_type == "Receive" ? doc.paid_to_account_currency : doc.paid_from_account_currency;
|
||||
displayed_docs.push(doc);
|
||||
} else if (dt === "Journal Entry") {
|
||||
doc.accounts.forEach(payment => {
|
||||
if (payment.account === me.gl_account) {
|
||||
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.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(values => {
|
||||
details_wrapper.append(frappe.render_template("linked_payment_row", values));
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
349
erpnext/accounts/page/bank_reconciliation/bank_reconciliation.py
Normal file
349
erpnext/accounts/page/bank_reconciliation/bank_reconciliation.py
Normal file
@@ -0,0 +1,349 @@
|
||||
# -*- 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 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 debitor 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)
|
||||
clear_payment_entry(transaction, payment_entry, gl_entry)
|
||||
|
||||
return 'reconciled'
|
||||
|
||||
def add_payment_to_transaction(transaction, payment_entry, gl_entry):
|
||||
transaction.append("payment_entries", {
|
||||
"payment_document": payment_entry.doctype,
|
||||
"payment_entry": payment_entry.name,
|
||||
"allocated_amount": gl_entry.credit if gl_entry.credit > 0 else gl_entry.debit
|
||||
})
|
||||
transaction.save()
|
||||
|
||||
def clear_payment_entry(transaction, payment_entry, gl_entry):
|
||||
linked_bank_transactions = frappe.db.sql("""
|
||||
SELECT
|
||||
bt.credit, bt.debit
|
||||
FROM
|
||||
`tabBank Transaction Payments` as btp
|
||||
LEFT JOIN
|
||||
`tabBank Transaction` as bt on btp.parent=bt.name
|
||||
WHERE
|
||||
btp.payment_document = %s
|
||||
AND
|
||||
btp.payment_entry = %s
|
||||
AND
|
||||
bt.docstatus = 1
|
||||
""", (payment_entry.doctype, payment_entry.name), as_dict=True)
|
||||
|
||||
amount_cleared = (flt(linked_bank_transactions[0].credit) - flt(linked_bank_transactions[0].debit))
|
||||
amount_to_be_cleared = (flt(gl_entry.debit) - flt(gl_entry.credit))
|
||||
|
||||
if payment_entry.doctype == "Payment Entry":
|
||||
clear_simple_entry(amount_cleared, amount_to_be_cleared, payment_entry, transaction)
|
||||
|
||||
elif payment_entry.doctype == "Journal Entry":
|
||||
clear_simple_entry(amount_cleared, amount_to_be_cleared, payment_entry, transaction)
|
||||
|
||||
elif payment_entry.doctype == "Sales Invoice":
|
||||
clear_sales_invoice(amount_cleared, amount_to_be_cleared, payment_entry, transaction)
|
||||
|
||||
elif payment_entry.doctype == "Purchase Invoice":
|
||||
clear_simple_entry(amount_cleared, amount_to_be_cleared, payment_entry, transaction)
|
||||
|
||||
elif payment_entry.doctype == "Expense Claim":
|
||||
clear_simple_entry(amount_cleared, amount_to_be_cleared, payment_entry, transaction)
|
||||
|
||||
def clear_simple_entry(amount_cleared, amount_to_be_cleared, payment_entry, transaction):
|
||||
if amount_cleared == amount_to_be_cleared:
|
||||
frappe.db.set_value(payment_entry.doctype, payment_entry.name, "clearance_date", transaction.date)
|
||||
|
||||
def clear_sales_invoice(amount_cleared, amount_to_be_cleared, payment_entry, transaction):
|
||||
if amount_cleared == amount_to_be_cleared:
|
||||
frappe.db.set_value("Sales Invoice Payment", dict(parenttype=payment_entry.doctype,
|
||||
parent=payment_entry.name), "clearance_date", transaction.date)
|
||||
|
||||
@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].account, transaction)
|
||||
|
||||
if amount_matching:
|
||||
return check_amount_vs_description(amount_matching, description_matching)
|
||||
|
||||
elif description_matching:
|
||||
return sorted(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], ["ifnull(clearance_date, '')", "=", ""], ["{0}".format(account_from_to), "=", "{0}".format(bank_account)]])
|
||||
|
||||
if transaction.credit > 0:
|
||||
journal_entries = frappe.db.sql("""
|
||||
SELECT
|
||||
'Journal Entry' as doctype, je.name, je.posting_date, je.cheque_no as reference_no,
|
||||
je.pay_to_recd_from as party, je.cheque_date as reference_date, jea.debit_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 = %s
|
||||
AND
|
||||
jea.debit_in_account_currency like %s
|
||||
AND
|
||||
je.docstatus = 1
|
||||
""", (bank_account, amount), as_dict=True)
|
||||
else:
|
||||
journal_entries = frappe.db.sql("""
|
||||
SELECT
|
||||
'Journal Entry' as doctype, je.name, je.posting_date, je.cheque_no as reference_no,
|
||||
je.pay_to_recd_from as party, je.cheque_date as reference_date, jea.credit_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 = %s
|
||||
AND
|
||||
jea.credit_in_account_currency like %s
|
||||
AND
|
||||
je.docstatus = 1
|
||||
""", (bank_account, 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(bank_account, 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 = []
|
||||
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"]))
|
||||
if key == "Journal Entry":
|
||||
data.extend(frappe.get_all("Journal Entry", filters=[["name", "in", value]], fields=["'Journal Entry' as doctype", "posting_date", "paid_to_recd_from as party", "cheque_no as reference_no", "cheque_date as reference_date"]))
|
||||
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"]))
|
||||
if key == "Purchase Invoice":
|
||||
data.append(frappe.get_all("Purchase Invoice", filters=[["name", "in", value]], fields=["'Purchase Invoice' as doctype", "posting_date", "supplier_name as party"]))
|
||||
if key == "Purchase Invoice":
|
||||
data.append(frappe.get_all("Expense Claim", filters=[["name", "in", value]], fields=["'Expense Claim' as doctype", "posting_date", "employee_name as party"]))
|
||||
|
||||
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 am_match["party"] == des_match["party"]:
|
||||
if am_match not in result:
|
||||
result.append(am_match)
|
||||
continue
|
||||
|
||||
if hasattr(am_match, "reference_no") and hasattr(des_match, "reference_no"):
|
||||
if difflib.SequenceMatcher(lambda x: x == " ", am_match["reference_no"], des_match["reference_no"]) > 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 []
|
||||
|
||||
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
|
||||
}
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,21 @@
|
||||
<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>
|
||||
@@ -0,0 +1,36 @@
|
||||
<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>
|
||||
@@ -0,0 +1,21 @@
|
||||
<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>
|
||||
@@ -0,0 +1,36 @@
|
||||
<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>
|
||||
@@ -76,6 +76,14 @@ def get_data():
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Item",
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Bank",
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Bank Account",
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -135,6 +143,12 @@ def get_data():
|
||||
"name": "Bank Reconciliation",
|
||||
"description": _("Update bank payment dates with journals.")
|
||||
},
|
||||
{
|
||||
"type": "page",
|
||||
"label": _("Reconcile payments and bank transactions"),
|
||||
"name": "bank-reconciliation",
|
||||
"description": _("Link bank transactions with payments.")
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"label": _("Match Payments with Invoices"),
|
||||
|
||||
@@ -35,6 +35,11 @@ def get_data():
|
||||
"type": "doctype",
|
||||
"name": "Amazon MWS Settings",
|
||||
"description": _("Connect Amazon with ERPNext"),
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Plaid Settings",
|
||||
"description": _("Connect your bank accounts to ERPNext"),
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
# -*- 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 requests
|
||||
from plaid import Client
|
||||
from plaid.errors import APIError, ItemError
|
||||
|
||||
class PlaidConnector():
|
||||
def __init__(self, access_token=None):
|
||||
|
||||
if not(frappe.conf.get("plaid_client_id") and frappe.conf.get("plaid_secret") and frappe.conf.get("plaid_public_key")):
|
||||
frappe.throw(_("Please complete your Plaid API configuration before synchronizing your account"))
|
||||
|
||||
self.config = {
|
||||
"plaid_client_id": frappe.conf.get("plaid_client_id"),
|
||||
"plaid_secret": frappe.conf.get("plaid_secret"),
|
||||
"plaid_public_key": frappe.conf.get("plaid_public_key"),
|
||||
"plaid_env": frappe.conf.get("plaid_env")
|
||||
}
|
||||
|
||||
self.client = Client(client_id=self.config["plaid_client_id"],
|
||||
secret=self.config["plaid_secret"],
|
||||
public_key=self.config["plaid_public_key"],
|
||||
environment=self.config["plaid_env"]
|
||||
)
|
||||
|
||||
self.access_token = access_token
|
||||
|
||||
def get_access_token(self, public_token):
|
||||
if public_token is None:
|
||||
frappe.log_error(_("Public token is missing for this bank"), _("Plaid public token error"))
|
||||
|
||||
response = self.client.Item.public_token.exchange(public_token)
|
||||
access_token = response['access_token']
|
||||
|
||||
return access_token
|
||||
|
||||
def auth(self):
|
||||
try:
|
||||
self.client.Auth.get(self.access_token)
|
||||
print("Authentication successful.....")
|
||||
except ItemError as e:
|
||||
if e.code == 'ITEM_LOGIN_REQUIRED':
|
||||
pass
|
||||
else:
|
||||
pass
|
||||
except APIError as e:
|
||||
if e.code == 'PLANNED_MAINTENANCE':
|
||||
pass
|
||||
else:
|
||||
pass
|
||||
except requests.Timeout:
|
||||
pass
|
||||
except Exception as e:
|
||||
print(e)
|
||||
frappe.log_error(frappe.get_traceback(), _("Plaid authentication error"))
|
||||
frappe.msgprint({"title": _("Authentication Failed"), "message":e, "raise_exception":1, "indicator":'red'})
|
||||
|
||||
def get_transactions(self, start_date, end_date, account_id=None):
|
||||
try:
|
||||
self.auth()
|
||||
if account_id:
|
||||
account_ids = [account_id]
|
||||
|
||||
response = self.client.Transactions.get(self.access_token, start_date=start_date, end_date=end_date, account_ids=account_ids)
|
||||
|
||||
else:
|
||||
response = self.client.Transactions.get(self.access_token, start_date=start_date, end_date=end_date)
|
||||
|
||||
transactions = response['transactions']
|
||||
|
||||
while len(transactions) < response['total_transactions']:
|
||||
response = self.client.Transactions.get(self.access_token, start_date=start_date, end_date=end_date, offset=len(transactions))
|
||||
transactions.extend(response['transactions'])
|
||||
return transactions
|
||||
except Exception:
|
||||
frappe.log_error(frappe.get_traceback(), _("Plaid transactions sync error"))
|
||||
@@ -0,0 +1,104 @@
|
||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.provide("erpnext.integrations");
|
||||
|
||||
frappe.ui.form.on('Plaid Settings', {
|
||||
link_new_account: function(frm) {
|
||||
new erpnext.integrations.plaidLink(frm);
|
||||
}
|
||||
});
|
||||
|
||||
erpnext.integrations.plaidLink = class plaidLink {
|
||||
constructor(parent) {
|
||||
this.frm = parent;
|
||||
this.product = ["transactions", "auth"];
|
||||
this.plaidUrl = 'https://cdn.plaid.com/link/v2/stable/link-initialize.js';
|
||||
this.init_config();
|
||||
}
|
||||
|
||||
init_config() {
|
||||
const me = this;
|
||||
frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.plaid_configuration')
|
||||
.then(result => {
|
||||
if (result !== "disabled") {
|
||||
me.plaid_env = result.plaid_env;
|
||||
me.plaid_public_key = result.plaid_public_key;
|
||||
me.client_name = result.client_name;
|
||||
me.init_plaid();
|
||||
} else {
|
||||
frappe.throw(__("Please save your document before adding a new account"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
init_plaid() {
|
||||
const me = this;
|
||||
me.loadScript(me.plaidUrl)
|
||||
.then(() => {
|
||||
me.onScriptLoaded(me);
|
||||
})
|
||||
.then(() => {
|
||||
if (me.linkHandler) {
|
||||
me.linkHandler.open();
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
me.onScriptError(error);
|
||||
});
|
||||
}
|
||||
|
||||
loadScript(src) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
if (document.querySelector('script[src="' + src + '"]')) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
const el = document.createElement('script');
|
||||
el.type = 'text/javascript';
|
||||
el.async = true;
|
||||
el.src = src;
|
||||
el.addEventListener('load', resolve);
|
||||
el.addEventListener('error', reject);
|
||||
el.addEventListener('abort', reject);
|
||||
document.head.appendChild(el);
|
||||
});
|
||||
}
|
||||
|
||||
onScriptLoaded(me) {
|
||||
me.linkHandler = window.Plaid.create({
|
||||
clientName: me.client_name,
|
||||
env: me.plaid_env,
|
||||
key: me.plaid_public_key,
|
||||
onSuccess: me.plaid_success,
|
||||
product: me.product
|
||||
});
|
||||
}
|
||||
|
||||
onScriptError(error) {
|
||||
frappe.msgprint('There was an issue loading the link-initialize.js script');
|
||||
frappe.msgprint(error);
|
||||
}
|
||||
|
||||
plaid_success(token, response) {
|
||||
const me = this;
|
||||
|
||||
frappe.prompt({
|
||||
fieldtype:"Link",
|
||||
options: "Company",
|
||||
label:__("Company"),
|
||||
fieldname:"company",
|
||||
reqd:1
|
||||
}, (data) => {
|
||||
me.company = data.company;
|
||||
frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.add_institution', {token: token, response: response})
|
||||
.then((result) => {
|
||||
frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.add_bank_accounts', {response: response,
|
||||
bank: result, company: me.company});
|
||||
})
|
||||
.then(() => {
|
||||
frappe.show_alert({message:__("Bank accounts added"), indicator:'green'});
|
||||
});
|
||||
}, __("Select a company"), __("Continue"));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,161 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
"creation": "2018-10-25 10:02:48.656165",
|
||||
"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": "enabled",
|
||||
"fieldtype": "Check",
|
||||
"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": "Enabled",
|
||||
"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.enabled==1",
|
||||
"fieldname": "automatic_sync",
|
||||
"fieldtype": "Check",
|
||||
"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": "Synchronize all accounts every hour",
|
||||
"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.enabled==1)&&(!doc.__islocal)",
|
||||
"fieldname": "link_new_account",
|
||||
"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": "Link a new bank account",
|
||||
"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": 1,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-12-14 12:51:12.331395",
|
||||
"modified_by": "Administrator",
|
||||
"module": "ERPNext Integrations",
|
||||
"name": "Plaid Settings",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 0,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"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": 0,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
# -*- 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
|
||||
import json
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
|
||||
from erpnext.erpnext_integrations.doctype.plaid_settings.plaid_connector import PlaidConnector
|
||||
from frappe.utils import getdate, formatdate, today, add_months
|
||||
|
||||
class PlaidSettings(Document):
|
||||
pass
|
||||
|
||||
@frappe.whitelist()
|
||||
def plaid_configuration():
|
||||
if frappe.db.get_value("Plaid Settings", None, "enabled") == "1":
|
||||
return {"plaid_public_key": frappe.conf.get("plaid_public_key") or None, "plaid_env": frappe.conf.get("plaid_env") or None, "client_name": frappe.local.site }
|
||||
else:
|
||||
return "disabled"
|
||||
|
||||
@frappe.whitelist()
|
||||
def add_institution(token, response):
|
||||
response = json.loads(response)
|
||||
|
||||
plaid = PlaidConnector()
|
||||
access_token = plaid.get_access_token(token)
|
||||
|
||||
if not frappe.db.exists("Bank", response["institution"]["name"]):
|
||||
try:
|
||||
bank = frappe.get_doc({
|
||||
"doctype": "Bank",
|
||||
"bank_name": response["institution"]["name"],
|
||||
"plaid_access_token": access_token
|
||||
})
|
||||
bank.insert()
|
||||
except Exception:
|
||||
frappe.throw(frappe.get_traceback())
|
||||
|
||||
else:
|
||||
bank = frappe.get_doc("Bank", response["institution"]["name"])
|
||||
bank.plaid_access_token = access_token
|
||||
bank.save()
|
||||
|
||||
return bank
|
||||
|
||||
@frappe.whitelist()
|
||||
def add_bank_accounts(response, bank, company):
|
||||
response = json.loads(response) if not "accounts" in response else response
|
||||
bank = json.loads(bank)
|
||||
result = []
|
||||
|
||||
default_gl_account = get_default_bank_cash_account(company, "Bank")
|
||||
if not default_gl_account:
|
||||
frappe.throw(_("Please setup a default bank account for company {0}".format(company)))
|
||||
|
||||
for account in response["accounts"]:
|
||||
acc_type = frappe.db.get_value("Account Type", account["type"])
|
||||
if not acc_type:
|
||||
add_account_type(account["type"])
|
||||
|
||||
acc_subtype = frappe.db.get_value("Account Subtype", account["subtype"])
|
||||
if not acc_subtype:
|
||||
add_account_subtype(account["subtype"])
|
||||
|
||||
if not frappe.db.exists("Bank Account", dict(integration_id=account["id"])):
|
||||
try:
|
||||
new_account = frappe.get_doc({
|
||||
"doctype": "Bank Account",
|
||||
"bank": bank["bank_name"],
|
||||
"account": default_gl_account.account,
|
||||
"account_name": account["name"],
|
||||
"account_type": account["type"] or "",
|
||||
"account_subtype": account["subtype"] or "",
|
||||
"mask": account["mask"] or "",
|
||||
"integration_id": account["id"],
|
||||
"is_company_account": 1,
|
||||
"company": company
|
||||
})
|
||||
new_account.insert()
|
||||
|
||||
result.append(new_account.name)
|
||||
|
||||
except frappe.UniqueValidationError:
|
||||
frappe.msgprint(_("Bank account {0} already exists and could not be created again").format(new_account.account_name))
|
||||
except Exception:
|
||||
frappe.throw(frappe.get_traceback())
|
||||
|
||||
else:
|
||||
result.append(frappe.db.get_value("Bank Account", dict(integration_id=account["id"]), "name"))
|
||||
|
||||
return result
|
||||
|
||||
def add_account_type(account_type):
|
||||
try:
|
||||
frappe.get_doc({
|
||||
"doctype": "Account Type",
|
||||
"account_type": account_type
|
||||
}).insert()
|
||||
except Exception:
|
||||
frappe.throw(frappe.get_traceback())
|
||||
|
||||
|
||||
def add_account_subtype(account_subtype):
|
||||
try:
|
||||
frappe.get_doc({
|
||||
"doctype": "Account Subtype",
|
||||
"account_subtype": account_subtype
|
||||
}).insert()
|
||||
except Exception:
|
||||
frappe.throw(frappe.get_traceback())
|
||||
|
||||
@frappe.whitelist()
|
||||
def sync_transactions(bank, bank_account):
|
||||
|
||||
last_sync_date = frappe.db.get_value("Bank Account", bank_account, "last_integration_date")
|
||||
if last_sync_date:
|
||||
start_date = formatdate(last_sync_date, "YYYY-MM-dd")
|
||||
else:
|
||||
start_date = formatdate(add_months(today(), -12), "YYYY-MM-dd")
|
||||
end_date = formatdate(today(), "YYYY-MM-dd")
|
||||
|
||||
try:
|
||||
transactions = get_transactions(bank=bank, bank_account=bank_account, start_date=start_date, end_date=end_date)
|
||||
result = []
|
||||
if transactions:
|
||||
for transaction in transactions:
|
||||
result.append(new_bank_transaction(transaction))
|
||||
|
||||
frappe.db.set_value("Bank Account", bank_account, "last_integration_date", getdate(end_date))
|
||||
|
||||
return result
|
||||
except Exception:
|
||||
frappe.log_error(frappe.get_traceback(), _("Plaid transactions sync error"))
|
||||
|
||||
def get_transactions(bank, bank_account=None, start_date=None, end_date=None):
|
||||
access_token = None
|
||||
|
||||
if bank_account:
|
||||
related_bank = frappe.db.get_values("Bank Account", bank_account, ["bank", "integration_id"], as_dict=True)
|
||||
access_token = frappe.db.get_value("Bank", related_bank[0].bank, "plaid_access_token")
|
||||
account_id = related_bank[0].integration_id
|
||||
|
||||
else:
|
||||
access_token = frappe.db.get_value("Bank", bank, "plaid_access_token")
|
||||
account_id = None
|
||||
|
||||
plaid = PlaidConnector(access_token)
|
||||
transactions = plaid.get_transactions(start_date=start_date, end_date=end_date, account_id=account_id)
|
||||
|
||||
return transactions
|
||||
|
||||
def new_bank_transaction(transaction):
|
||||
result = []
|
||||
|
||||
bank_account = frappe.db.get_value("Bank Account", dict(integration_id=transaction["account_id"]))
|
||||
|
||||
if float(transaction["amount"]) >= 0:
|
||||
debit = float(transaction["amount"])
|
||||
credit = 0
|
||||
else:
|
||||
debit = 0
|
||||
credit = abs(float(transaction["amount"]))
|
||||
|
||||
status = "Pending" if transaction["pending"] == "True" else "Settled"
|
||||
|
||||
if not frappe.db.exists("Bank Transaction", dict(transaction_id=transaction["transaction_id"])):
|
||||
try:
|
||||
new_transaction = frappe.get_doc({
|
||||
"doctype": "Bank Transaction",
|
||||
"date": getdate(transaction["date"]),
|
||||
"status": status,
|
||||
"bank_account": bank_account,
|
||||
"debit": debit,
|
||||
"credit": credit,
|
||||
"currency": transaction["iso_currency_code"],
|
||||
"description": transaction["name"]
|
||||
})
|
||||
new_transaction.insert()
|
||||
new_transaction.submit()
|
||||
|
||||
result.append(new_transaction.name)
|
||||
|
||||
except Exception:
|
||||
frappe.throw(frappe.get_traceback())
|
||||
|
||||
return result
|
||||
|
||||
def automatic_synchronization():
|
||||
settings = frappe.get_doc("Plaid Settings", "Plaid Settings")
|
||||
|
||||
if settings.enabled == 1 and settings.automatic_sync == 1:
|
||||
plaid_accounts = frappe.get_all("Bank Account", filter={"integration_id": ["!=", ""]}, fields=["name", "bank"])
|
||||
|
||||
for plaid_account in plaid_accounts:
|
||||
frappe.enqueue("erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.sync_transactions", bank=plaid_account.bank, bank_account=plaid_account.name)
|
||||
@@ -0,0 +1,23 @@
|
||||
/* eslint-disable */
|
||||
// rename this file from _test_[name] to test_[name] to activate
|
||||
// and remove above this line
|
||||
|
||||
QUnit.test("test: Plaid Settings", function (assert) {
|
||||
let done = assert.async();
|
||||
|
||||
// number of asserts
|
||||
assert.expect(1);
|
||||
|
||||
frappe.run_serially([
|
||||
// insert a new Plaid Settings
|
||||
() => frappe.tests.make('Plaid Settings', [
|
||||
// values to be set
|
||||
{key: 'value'}
|
||||
]),
|
||||
() => {
|
||||
assert.equal(cur_frm.doc.key, 'value');
|
||||
},
|
||||
() => done()
|
||||
]);
|
||||
|
||||
});
|
||||
@@ -0,0 +1,155 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import unittest
|
||||
import frappe
|
||||
from erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings import plaid_configuration, add_account_type, add_account_subtype, new_bank_transaction, add_bank_accounts
|
||||
import json
|
||||
from frappe.utils.response import json_handler
|
||||
from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
|
||||
|
||||
class TestPlaidSettings(unittest.TestCase):
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def tearDown(self):
|
||||
for bt in frappe.get_all("Bank Transaction"):
|
||||
doc = frappe.get_doc("Bank Transaction", bt.name)
|
||||
doc.cancel()
|
||||
doc.delete()
|
||||
|
||||
for ba in frappe.get_all("Bank Account"):
|
||||
frappe.get_doc("Bank Account", ba.name).delete()
|
||||
|
||||
for at in frappe.get_all("Account Type"):
|
||||
frappe.get_doc("Account Type", at.name).delete()
|
||||
|
||||
for ast in frappe.get_all("Account Subtype"):
|
||||
frappe.get_doc("Account Subtype", ast.name).delete()
|
||||
|
||||
def test_plaid_disabled(self):
|
||||
frappe.db.set_value("Plaid Settings", None, "enabled", 0)
|
||||
self.assertTrue(plaid_configuration() == "disabled")
|
||||
|
||||
def test_add_account_type(self):
|
||||
add_account_type("brokerage")
|
||||
self.assertEqual(frappe.get_doc("Account Type", "brokerage").name, "brokerage")
|
||||
|
||||
def test_add_account_subtype(self):
|
||||
add_account_subtype("loan")
|
||||
self.assertEqual(frappe.get_doc("Account Subtype", "loan").name, "loan")
|
||||
|
||||
def test_default_bank_account(self):
|
||||
if not frappe.db.exists("Bank", "Citi"):
|
||||
frappe.get_doc({
|
||||
"doctype": "Bank",
|
||||
"bank_name": "Citi"
|
||||
}).insert()
|
||||
|
||||
bank_accounts = {
|
||||
'account': {
|
||||
'subtype': 'checking',
|
||||
'mask': '0000',
|
||||
'type': 'depository',
|
||||
'id': '6GbM6RRQgdfy3lAqGz4JUnpmR948WZFg8DjQK',
|
||||
'name': 'Plaid Checking'
|
||||
},
|
||||
'account_id': '6GbM6RRQgdfy3lAqGz4JUnpmR948WZFg8DjQK',
|
||||
'link_session_id': 'db673d75-61aa-442a-864f-9b3f174f3725',
|
||||
'accounts': [{
|
||||
'type': 'depository',
|
||||
'subtype': 'checking',
|
||||
'mask': '0000',
|
||||
'id': '6GbM6RRQgdfy3lAqGz4JUnpmR948WZFg8DjQK',
|
||||
'name': 'Plaid Checking'
|
||||
}],
|
||||
'institution': {
|
||||
'institution_id': 'ins_6',
|
||||
'name': 'Citi'
|
||||
}
|
||||
}
|
||||
|
||||
bank = json.dumps(frappe.get_doc("Bank", "Citi").as_dict(), default=json_handler)
|
||||
company = frappe.db.get_single_value('Global Defaults', 'default_company')
|
||||
frappe.db.set_value("Company", company, "default_bank_account", None)
|
||||
|
||||
self.assertRaises(frappe.ValidationError, add_bank_accounts, response=bank_accounts, bank=bank, company=company)
|
||||
|
||||
def test_new_transaction(self):
|
||||
if not frappe.db.exists("Bank", "Citi"):
|
||||
frappe.get_doc({
|
||||
"doctype": "Bank",
|
||||
"bank_name": "Citi"
|
||||
}).insert()
|
||||
|
||||
bank_accounts = {
|
||||
'account': {
|
||||
'subtype': 'checking',
|
||||
'mask': '0000',
|
||||
'type': 'depository',
|
||||
'id': '6GbM6RRQgdfy3lAqGz4JUnpmR948WZFg8DjQK',
|
||||
'name': 'Plaid Checking'
|
||||
},
|
||||
'account_id': '6GbM6RRQgdfy3lAqGz4JUnpmR948WZFg8DjQK',
|
||||
'link_session_id': 'db673d75-61aa-442a-864f-9b3f174f3725',
|
||||
'accounts': [{
|
||||
'type': 'depository',
|
||||
'subtype': 'checking',
|
||||
'mask': '0000',
|
||||
'id': '6GbM6RRQgdfy3lAqGz4JUnpmR948WZFg8DjQK',
|
||||
'name': 'Plaid Checking'
|
||||
}],
|
||||
'institution': {
|
||||
'institution_id': 'ins_6',
|
||||
'name': 'Citi'
|
||||
}
|
||||
}
|
||||
|
||||
bank = json.dumps(frappe.get_doc("Bank", "Citi").as_dict(), default=json_handler)
|
||||
company = frappe.db.get_single_value('Global Defaults', 'default_company')
|
||||
|
||||
if frappe.db.get_value("Company", company, "default_bank_account") is None:
|
||||
frappe.db.set_value("Company", company, "default_bank_account", get_default_bank_cash_account(company, "Cash").get("account"))
|
||||
|
||||
add_bank_accounts(bank_accounts, bank, company)
|
||||
|
||||
transactions = {
|
||||
'account_owner': None,
|
||||
'category': ['Food and Drink', 'Restaurants'],
|
||||
'account_id': 'b4Jkp1LJDZiPgojpr1ansXJrj5Q6w9fVmv6ov',
|
||||
'pending_transaction_id': None,
|
||||
'transaction_id': 'x374xPa7DvUewqlR5mjNIeGK8r8rl3Sn647LM',
|
||||
'unofficial_currency_code': None,
|
||||
'name': 'INTRST PYMNT',
|
||||
'transaction_type': 'place',
|
||||
'amount': -4.22,
|
||||
'location': {
|
||||
'city': None,
|
||||
'zip': None,
|
||||
'store_number': None,
|
||||
'lon': None,
|
||||
'state': None,
|
||||
'address': None,
|
||||
'lat': None
|
||||
},
|
||||
'payment_meta': {
|
||||
'reference_number': None,
|
||||
'payer': None,
|
||||
'payment_method': None,
|
||||
'reason': None,
|
||||
'payee': None,
|
||||
'ppd_id': None,
|
||||
'payment_processor': None,
|
||||
'by_order_of': None
|
||||
},
|
||||
'date': '2017-12-22',
|
||||
'category_id': '13005000',
|
||||
'pending': False,
|
||||
'iso_currency_code': 'USD'
|
||||
}
|
||||
|
||||
new_bank_transaction(transactions)
|
||||
|
||||
self.assertTrue(len(frappe.get_all("Bank Transaction")) == 1)
|
||||
@@ -229,6 +229,7 @@ scheduler_events = {
|
||||
'erpnext.hr.doctype.daily_work_summary_group.daily_work_summary_group.trigger_emails',
|
||||
"erpnext.accounts.doctype.subscription.subscription.process_all",
|
||||
"erpnext.erpnext_integrations.doctype.amazon_mws_settings.amazon_mws_settings.schedule_get_order_details",
|
||||
"erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.automatic_synchronization",
|
||||
"erpnext.projects.doctype.project.project.hourly_reminder",
|
||||
"erpnext.projects.doctype.project.project.collect_project_status"
|
||||
],
|
||||
|
||||
@@ -844,6 +844,38 @@
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "clearance_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": "Clearance 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,
|
||||
|
||||
@@ -1,58 +1,58 @@
|
||||
{
|
||||
"css/erpnext.css": [
|
||||
"public/less/erpnext.less",
|
||||
"public/less/hub.less"
|
||||
],
|
||||
"css/marketplace.css": [
|
||||
"public/less/hub.less"
|
||||
],
|
||||
"js/erpnext-web.min.js": [
|
||||
"public/js/website_utils.js",
|
||||
"public/js/shopping_cart.js"
|
||||
],
|
||||
"css/erpnext.css": [
|
||||
"public/less/erpnext.less",
|
||||
"public/less/hub.less"
|
||||
],
|
||||
"css/marketplace.css": [
|
||||
"public/less/hub.less"
|
||||
],
|
||||
"js/erpnext-web.min.js": [
|
||||
"public/js/website_utils.js",
|
||||
"public/js/shopping_cart.js"
|
||||
],
|
||||
"css/erpnext-web.css": [
|
||||
"public/less/website.less"
|
||||
],
|
||||
"js/marketplace.min.js": [
|
||||
"public/js/hub/marketplace.js"
|
||||
],
|
||||
"js/erpnext.min.js": [
|
||||
"public/js/conf.js",
|
||||
"public/js/utils.js",
|
||||
"public/js/queries.js",
|
||||
"public/js/sms_manager.js",
|
||||
"public/js/utils/party.js",
|
||||
"public/js/templates/address_list.html",
|
||||
"public/js/templates/contact_list.html",
|
||||
"public/js/controllers/stock_controller.js",
|
||||
"public/js/payment/payments.js",
|
||||
"public/js/controllers/taxes_and_totals.js",
|
||||
"public/js/controllers/transaction.js",
|
||||
"public/js/pos/pos.html",
|
||||
"public/js/pos/pos_bill_item.html",
|
||||
"public/js/pos/pos_bill_item_new.html",
|
||||
"public/js/pos/pos_selected_item.html",
|
||||
"public/js/pos/pos_item.html",
|
||||
"public/js/pos/pos_tax_row.html",
|
||||
"public/js/pos/customer_toolbar.html",
|
||||
"public/js/pos/pos_invoice_list.html",
|
||||
"public/js/payment/pos_payment.html",
|
||||
"public/js/payment/payment_details.html",
|
||||
"public/js/templates/item_selector.html",
|
||||
"js/marketplace.min.js": [
|
||||
"public/js/hub/marketplace.js"
|
||||
],
|
||||
"js/erpnext.min.js": [
|
||||
"public/js/conf.js",
|
||||
"public/js/utils.js",
|
||||
"public/js/queries.js",
|
||||
"public/js/sms_manager.js",
|
||||
"public/js/utils/party.js",
|
||||
"public/js/templates/address_list.html",
|
||||
"public/js/templates/contact_list.html",
|
||||
"public/js/controllers/stock_controller.js",
|
||||
"public/js/payment/payments.js",
|
||||
"public/js/controllers/taxes_and_totals.js",
|
||||
"public/js/controllers/transaction.js",
|
||||
"public/js/pos/pos.html",
|
||||
"public/js/pos/pos_bill_item.html",
|
||||
"public/js/pos/pos_bill_item_new.html",
|
||||
"public/js/pos/pos_selected_item.html",
|
||||
"public/js/pos/pos_item.html",
|
||||
"public/js/pos/pos_tax_row.html",
|
||||
"public/js/pos/customer_toolbar.html",
|
||||
"public/js/pos/pos_invoice_list.html",
|
||||
"public/js/payment/pos_payment.html",
|
||||
"public/js/payment/payment_details.html",
|
||||
"public/js/templates/item_selector.html",
|
||||
"public/js/templates/employees_to_mark_attendance.html",
|
||||
"public/js/utils/item_selector.js",
|
||||
"public/js/help_links.js",
|
||||
"public/js/agriculture/ternary_plot.js",
|
||||
"public/js/templates/item_quick_entry.html",
|
||||
"public/js/utils/item_quick_entry.js",
|
||||
"public/js/utils/item_selector.js",
|
||||
"public/js/help_links.js",
|
||||
"public/js/agriculture/ternary_plot.js",
|
||||
"public/js/templates/item_quick_entry.html",
|
||||
"public/js/utils/item_quick_entry.js",
|
||||
"public/js/utils/customer_quick_entry.js",
|
||||
"public/js/education/student_button.html",
|
||||
"public/js/education/assessment_result_tool.html",
|
||||
"public/js/hub/hub_factory.js"
|
||||
],
|
||||
"js/item-dashboard.min.js": [
|
||||
"stock/dashboard/item_dashboard.html",
|
||||
"stock/dashboard/item_dashboard_list.html",
|
||||
"stock/dashboard/item_dashboard.js"
|
||||
]
|
||||
"public/js/education/student_button.html",
|
||||
"public/js/education/assessment_result_tool.html",
|
||||
"public/js/hub/hub_factory.js"
|
||||
],
|
||||
"js/item-dashboard.min.js": [
|
||||
"stock/dashboard/item_dashboard.html",
|
||||
"stock/dashboard/item_dashboard_list.html",
|
||||
"stock/dashboard/item_dashboard.js"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -25,10 +25,10 @@
|
||||
|
||||
.app-icon-svg {
|
||||
display: inline-block;
|
||||
margin: auto;
|
||||
text-align: center;
|
||||
border-radius: 16px;
|
||||
cursor: pointer;
|
||||
margin: auto;
|
||||
text-align: center;
|
||||
border-radius: 16px;
|
||||
cursor: pointer;
|
||||
box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
@@ -458,4 +458,4 @@ body[data-route="pos"] {
|
||||
.list-item_content {
|
||||
padding-right: 45px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,4 +6,5 @@ python-stdnum
|
||||
braintree
|
||||
gocardless_pro
|
||||
woocommerce
|
||||
pandas
|
||||
pandas
|
||||
plaid-python
|
||||
Reference in New Issue
Block a user