diff --git a/erpnext/accounts/doctype/account_subtype/__init__.py b/erpnext/accounts/doctype/account_subtype/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/accounts/doctype/account_subtype/account_subtype.js b/erpnext/accounts/doctype/account_subtype/account_subtype.js new file mode 100644 index 00000000000..30144adeea6 --- /dev/null +++ b/erpnext/accounts/doctype/account_subtype/account_subtype.js @@ -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() { + + } +}); diff --git a/erpnext/accounts/doctype/account_subtype/account_subtype.json b/erpnext/accounts/doctype/account_subtype/account_subtype.json new file mode 100644 index 00000000000..6b1f2a2526e --- /dev/null +++ b/erpnext/accounts/doctype/account_subtype/account_subtype.json @@ -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 +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/account_subtype/account_subtype.py b/erpnext/accounts/doctype/account_subtype/account_subtype.py new file mode 100644 index 00000000000..46c45cc733e --- /dev/null +++ b/erpnext/accounts/doctype/account_subtype/account_subtype.py @@ -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 diff --git a/erpnext/accounts/doctype/account_subtype/test_account_subtype.js b/erpnext/accounts/doctype/account_subtype/test_account_subtype.js new file mode 100644 index 00000000000..5646763bbd0 --- /dev/null +++ b/erpnext/accounts/doctype/account_subtype/test_account_subtype.js @@ -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() + ]); + +}); diff --git a/erpnext/accounts/doctype/account_subtype/test_account_subtype.py b/erpnext/accounts/doctype/account_subtype/test_account_subtype.py new file mode 100644 index 00000000000..c37b5b9db7d --- /dev/null +++ b/erpnext/accounts/doctype/account_subtype/test_account_subtype.py @@ -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 diff --git a/erpnext/accounts/doctype/account_type/__init__.py b/erpnext/accounts/doctype/account_type/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/accounts/doctype/account_type/account_type.js b/erpnext/accounts/doctype/account_type/account_type.js new file mode 100644 index 00000000000..858b56c077a --- /dev/null +++ b/erpnext/accounts/doctype/account_type/account_type.js @@ -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() { + + } +}); diff --git a/erpnext/accounts/doctype/account_type/account_type.json b/erpnext/accounts/doctype/account_type/account_type.json new file mode 100644 index 00000000000..6b8f724b408 --- /dev/null +++ b/erpnext/accounts/doctype/account_type/account_type.json @@ -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 +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/account_type/account_type.py b/erpnext/accounts/doctype/account_type/account_type.py new file mode 100644 index 00000000000..3e6429318b0 --- /dev/null +++ b/erpnext/accounts/doctype/account_type/account_type.py @@ -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 diff --git a/erpnext/accounts/doctype/account_type/test_account_type.js b/erpnext/accounts/doctype/account_type/test_account_type.js new file mode 100644 index 00000000000..76e434f4ab5 --- /dev/null +++ b/erpnext/accounts/doctype/account_type/test_account_type.js @@ -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() + ]); + +}); diff --git a/erpnext/accounts/doctype/account_type/test_account_type.py b/erpnext/accounts/doctype/account_type/test_account_type.py new file mode 100644 index 00000000000..824c2f66ae3 --- /dev/null +++ b/erpnext/accounts/doctype/account_type/test_account_type.py @@ -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 diff --git a/erpnext/accounts/doctype/bank/bank.js b/erpnext/accounts/doctype/bank/bank.js index 1063a07b820..463d29c9f83 100644 --- a/erpnext/accounts/doctype/bank/bank.js +++ b/erpnext/accounts/doctype/bank/bank.js @@ -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(); +}; \ No newline at end of file diff --git a/erpnext/accounts/doctype/bank/bank.json b/erpnext/accounts/doctype/bank/bank.json index 0a24726f36a..4fa0e4f29ba 100644 --- a/erpnext/accounts/doctype/bank/bank.json +++ b/erpnext/accounts/doctype/bank/bank.json @@ -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 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/bank_account/bank_account.js b/erpnext/accounts/doctype/bank_account/bank_account.js index 5ac41e44520..5d616e5fb7e 100644 --- a/erpnext/accounts/doctype/bank_account/bank_account.js +++ b/erpnext/accounts/doctype/bank_account/bank_account.js @@ -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", ""); + }); + }); + } } }); diff --git a/erpnext/accounts/doctype/bank_account/bank_account.json b/erpnext/accounts/doctype/bank_account/bank_account.json index 84a8e6857c3..75987b84886 100644 --- a/erpnext/accounts/doctype/bank_account/bank_account.json +++ b/erpnext/accounts/doctype/bank_account/bank_account.json @@ -1,729 +1,1019 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 1, - "autoname": "field:account_name", - "beta": 0, - "creation": "2017-05-29 21:35:13.136357", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 0, - "engine": "InnoDB", + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 1, + "autoname": "", + "beta": 0, + "creation": "2017-05-29 21:35:13.136357", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 0, + "engine": "InnoDB", "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "account_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Account Name", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "account_name", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Account Name", + "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": "account", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "GL Account", + "length": 0, + "no_copy": 0, + "options": "Account", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "bank", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Bank", + "length": 0, + "no_copy": 0, + "options": "Bank", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 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": "account_type", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Account Type", + "length": 0, + "no_copy": 0, + "options": "Account Type", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "account_subtype", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Account Subtype", + "length": 0, + "no_copy": 0, + "options": "Account Subtype", + "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, + "label": "", + "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": 1, + "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": "is_default", + "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": "Is the Default 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 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "is_company_account", + "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": "Is Company 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 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "is_company_account", + "fieldname": "company", + "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": 1, + "label": "Company", + "length": 0, + "no_copy": 0, + "options": "Company", + "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.is_company_account", + "fieldname": "section_break_11", + "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": "Party Details", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "party_type", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Party Type", + "length": 0, + "no_copy": 0, + "options": "DocType", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_14", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "party", + "fieldtype": "Dynamic Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Party", + "length": 0, + "no_copy": 0, + "options": "party_type", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 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": "account_details_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": "Account Details", + "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": "iban", + "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": "IBAN", + "length": 30, + "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_12", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "bank_account_no", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Bank Account No", + "length": 30, + "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": "branch_code", + "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": "Branch Code", + "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": "swift_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": "SWIFT 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": "address_and_contact", + "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": "Address and Contact", + "length": 0, + "no_copy": 0, + "options": "fa fa-map-marker", + "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": "address_html", + "fieldtype": "HTML", + "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": "Address HTML", + "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": "website", + "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": "Website", + "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_13", + "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": "contact_html", + "fieldtype": "HTML", + "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": "Contact HTML", + "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": "integration_details_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": "Integration Details", + "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": "integration_id", + "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": "Integration ID", + "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": 1 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "account", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Account", - "length": 0, - "no_copy": 0, - "options": "Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "description": "Change this date manually to setup the next synchronization start date", + "fieldname": "last_integration_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": "Last Integration 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": "bank", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Bank", - "length": 0, - "no_copy": 0, - "options": "Bank", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_27", + "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": "is_company_account", - "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": "Is Company 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 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "is_company_account", - "fieldname": "company", - "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": 1, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "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, - "label": "", - "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": 1, - "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": "is_default", - "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": "Is Default", - "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_account_no", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Bank Account No", - "length": 30, - "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": "iban", - "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": "IBAN", - "length": 30, - "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": "branch_code", - "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": "Branch Code", - "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": "swift_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": "SWIFT 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, - "depends_on": "eval:!doc.is_company_account", - "fieldname": "section_break_11", - "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": "party_type", - "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": "Party Type", - "length": 0, - "no_copy": 0, - "options": "DocType", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_14", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "party", - "fieldtype": "Dynamic Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Party", - "length": 0, - "no_copy": 0, - "options": "party_type", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 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": "address_and_contact", - "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": "Address and Contact", - "length": 0, - "no_copy": 0, - "options": "fa fa-map-marker", - "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": "address_html", - "fieldtype": "HTML", - "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": "Address HTML", - "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": "website", - "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": "Website", - "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_12", - "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": "contact_html", - "fieldtype": "HTML", - "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": "Contact HTML", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "mask", + "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": "Mask", + "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": 0 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-03-05 17:56:05.103238", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Bank Account", - "name_case": "", - "owner": "Administrator", + ], + "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": "2019-03-06 17:56:05.103238", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Bank Account", + "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": "Accounts Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "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, + "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": 0, - "read_only": 0, - "read_only_onload": 0, - "search_fields": "bank,account", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, + ], + "quick_entry": 0, + "read_only": 0, + "read_only_onload": 0, + "search_fields": "bank,account", + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, "track_views": 0 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/bank_account/bank_account.py b/erpnext/accounts/doctype/bank_account/bank_account.py index b13259b55d3..3c679fa215a 100644 --- a/erpnext/accounts/doctype/bank_account/bank_account.py +++ b/erpnext/accounts/doctype/bank_account/bank_account.py @@ -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) diff --git a/erpnext/accounts/doctype/bank_transaction/__init__.py b/erpnext/accounts/doctype/bank_transaction/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.js b/erpnext/accounts/doctype/bank_transaction/bank_transaction.js new file mode 100644 index 00000000000..cf041cd3c77 --- /dev/null +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.js @@ -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"]] + } + }; + }); + } +}); diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.json b/erpnext/accounts/doctype/bank_transaction/bank_transaction.json new file mode 100644 index 00000000000..2a29ed0d957 --- /dev/null +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.json @@ -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 +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py new file mode 100644 index 00000000000..d8b5f00f6ee --- /dev/null +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py @@ -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() \ No newline at end of file diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction_list.js b/erpnext/accounts/doctype/bank_transaction/bank_transaction_list.js new file mode 100644 index 00000000000..44dfaf18766 --- /dev/null +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction_list.js @@ -0,0 +1,13 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +// License: GNU General Public License v3. See license.txt + +frappe.listview_settings['Bank Transaction'] = { + add_fields: ["unallocated_amount"], + get_indicator: function(doc) { + if(flt(doc.unallocated_amount)>0) { + return [__("Unreconciled"), "orange", "unallocated_amount,>,0"]; + } else if(flt(doc.unallocated_amount)===0) { + return [__("Reconciled"), "green", "unallocated_amount,=,0"]; + } + } +}; \ No newline at end of file diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction_upload.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction_upload.py new file mode 100644 index 00000000000..d5d8c0e0465 --- /dev/null +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction_upload.py @@ -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 \ No newline at end of file diff --git a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.js b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.js new file mode 100644 index 00000000000..305119e1370 --- /dev/null +++ b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.js @@ -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() + ]); + +}); diff --git a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py new file mode 100644 index 00000000000..cb05f64d3ee --- /dev/null +++ b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py @@ -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 \ No newline at end of file diff --git a/erpnext/accounts/doctype/bank_transaction_mapping/__init__.py b/erpnext/accounts/doctype/bank_transaction_mapping/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/accounts/doctype/bank_transaction_mapping/bank_transaction_mapping.json b/erpnext/accounts/doctype/bank_transaction_mapping/bank_transaction_mapping.json new file mode 100644 index 00000000000..ace554b2440 --- /dev/null +++ b/erpnext/accounts/doctype/bank_transaction_mapping/bank_transaction_mapping.json @@ -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 +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/bank_transaction_mapping/bank_transaction_mapping.py b/erpnext/accounts/doctype/bank_transaction_mapping/bank_transaction_mapping.py new file mode 100644 index 00000000000..95a5bc33883 --- /dev/null +++ b/erpnext/accounts/doctype/bank_transaction_mapping/bank_transaction_mapping.py @@ -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 diff --git a/erpnext/accounts/doctype/bank_transaction_payments/__init__.py b/erpnext/accounts/doctype/bank_transaction_payments/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/accounts/doctype/bank_transaction_payments/bank_transaction_payments.json b/erpnext/accounts/doctype/bank_transaction_payments/bank_transaction_payments.json new file mode 100644 index 00000000000..a75e866997d --- /dev/null +++ b/erpnext/accounts/doctype/bank_transaction_payments/bank_transaction_payments.json @@ -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 +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/bank_transaction_payments/bank_transaction_payments.py b/erpnext/accounts/doctype/bank_transaction_payments/bank_transaction_payments.py new file mode 100644 index 00000000000..d6d7c109cf5 --- /dev/null +++ b/erpnext/accounts/doctype/bank_transaction_payments/bank_transaction_payments.py @@ -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 diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 41b3c13a3b9..412b2fa9025 100755 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -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", diff --git a/erpnext/accounts/page/bank_reconciliation/__init__.py b/erpnext/accounts/page/bank_reconciliation/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js new file mode 100644 index 00000000000..990d8b10462 --- /dev/null +++ b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js @@ -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 = $(`
`).appendTo(me.page.main); + const empty_state = __("Upload a bank statement, link or reconcile a bank account") + me.$main_section.append(`
${empty_state}
`) + + 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(`
`) + } + + 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. +
It should be a standard CSV or XLSX file. +
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 = ` +
+
${result_title}
+
` + 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 = ` +
+
${result_title}
+
` + 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(`${__('Refreshing')}...`); + } + + 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 = $('
').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(`
${empty_data_msg}
`) + } + + $(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)); + }) + }) + } + + } +} \ No newline at end of file diff --git a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.json b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.json new file mode 100644 index 00000000000..feea36860b1 --- /dev/null +++ b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.json @@ -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" +} \ No newline at end of file diff --git a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.py b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.py new file mode 100644 index 00000000000..6c0e4b8f0b2 --- /dev/null +++ b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.py @@ -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 + } + ) \ No newline at end of file diff --git a/erpnext/accounts/page/bank_reconciliation/bank_transaction_header.html b/erpnext/accounts/page/bank_reconciliation/bank_transaction_header.html new file mode 100644 index 00000000000..94f183b793b --- /dev/null +++ b/erpnext/accounts/page/bank_reconciliation/bank_transaction_header.html @@ -0,0 +1,21 @@ +
+
+ +
+ {{ __("Description") }} +
+ + + +
+
+
+
diff --git a/erpnext/accounts/page/bank_reconciliation/bank_transaction_row.html b/erpnext/accounts/page/bank_reconciliation/bank_transaction_row.html new file mode 100644 index 00000000000..742b84c63f5 --- /dev/null +++ b/erpnext/accounts/page/bank_reconciliation/bank_transaction_row.html @@ -0,0 +1,36 @@ +
+
+
+ +
+ {{ description }} +
+ + + +
+ +
+
diff --git a/erpnext/accounts/page/bank_reconciliation/linked_payment_header.html b/erpnext/accounts/page/bank_reconciliation/linked_payment_header.html new file mode 100644 index 00000000000..4542c36e0dc --- /dev/null +++ b/erpnext/accounts/page/bank_reconciliation/linked_payment_header.html @@ -0,0 +1,21 @@ +
+
+
+ {{ __("Payment Name") }} +
+
+ {{ __("Reference Date") }} +
+ + +
+ {{ __("Reference Number") }} +
+
+
+
+
diff --git a/erpnext/accounts/page/bank_reconciliation/linked_payment_row.html b/erpnext/accounts/page/bank_reconciliation/linked_payment_row.html new file mode 100644 index 00000000000..bdbc9fce033 --- /dev/null +++ b/erpnext/accounts/page/bank_reconciliation/linked_payment_row.html @@ -0,0 +1,36 @@ +
+
+
+ {{ name }} +
+
+ {% 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 %} +
+ + +
+ {% if (typeof reference_no !== "undefined") %} + {{ reference_no }} + {% else %} + {{ "" }} + {% endif %} +
+
+
+ +
+
+
+
\ No newline at end of file diff --git a/erpnext/config/accounts.py b/erpnext/config/accounts.py index 42d0df0a06f..31366b4f1b7 100644 --- a/erpnext/config/accounts.py +++ b/erpnext/config/accounts.py @@ -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"), diff --git a/erpnext/config/integrations.py b/erpnext/config/integrations.py index 01e077fba39..52c9ab8e46c 100644 --- a/erpnext/config/integrations.py +++ b/erpnext/config/integrations.py @@ -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"), } ] } diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/__init__.py b/erpnext/erpnext_integrations/doctype/plaid_settings/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py new file mode 100644 index 00000000000..fbb0ebc2c80 --- /dev/null +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py @@ -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")) \ No newline at end of file diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js new file mode 100644 index 00000000000..76205e6c823 --- /dev/null +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js @@ -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")); + } +}; \ No newline at end of file diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.json b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.json new file mode 100644 index 00000000000..ed51c4e8f80 --- /dev/null +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.json @@ -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 +} \ No newline at end of file diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py new file mode 100644 index 00000000000..8d31e24cd6c --- /dev/null +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py @@ -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) diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.js b/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.js new file mode 100644 index 00000000000..dc91347336d --- /dev/null +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.js @@ -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() + ]); + +}); diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.py new file mode 100644 index 00000000000..29e8fa4fec8 --- /dev/null +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.py @@ -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) \ No newline at end of file diff --git a/erpnext/hooks.py b/erpnext/hooks.py index ccdd412c181..89eb85247b4 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -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" ], diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.json b/erpnext/hr/doctype/expense_claim/expense_claim.json index 44b70c67a13..19b990d82fd 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.json +++ b/erpnext/hr/doctype/expense_claim/expense_claim.json @@ -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, diff --git a/erpnext/public/build.json b/erpnext/public/build.json index c34eef25080..1f702e69cca 100644 --- a/erpnext/public/build.json +++ b/erpnext/public/build.json @@ -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" + ] } diff --git a/erpnext/public/less/erpnext.less b/erpnext/public/less/erpnext.less index b746cc138f6..8ed5f1adb05 100644 --- a/erpnext/public/less/erpnext.less +++ b/erpnext/public/less/erpnext.less @@ -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; } -} +} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 0fee6c10847..28ba9f676ff 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,4 +6,5 @@ python-stdnum braintree gocardless_pro woocommerce -pandas \ No newline at end of file +pandas +plaid-python \ No newline at end of file