diff --git a/erpnext/accounts/doctype/bank/bank.json b/erpnext/accounts/doctype/bank/bank.json index b7e1c1f4933..4fa0e4f29ba 100644 --- a/erpnext/accounts/doctype/bank/bank.json +++ b/erpnext/accounts/doctype/bank/bank.json @@ -151,7 +151,7 @@ "columns": 0, "fieldname": "plaid_access_token", "fieldtype": "Data", - "hidden": 0, + "hidden": 1, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, @@ -160,7 +160,7 @@ "in_standard_filter": 0, "label": "Plaid Access Token", "length": 0, - "no_copy": 0, + "no_copy": 1, "permlevel": 0, "precision": "", "print_hide": 0, @@ -185,7 +185,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-10-25 15:20:38.837772", + "modified": "2018-11-27 16:12:13.938776", "modified_by": "Administrator", "module": "Accounts", "name": "Bank", diff --git a/erpnext/accounts/doctype/bank_account/bank_account.json b/erpnext/accounts/doctype/bank_account/bank_account.json index f2aa408b3b2..eb911a6260e 100644 --- a/erpnext/accounts/doctype/bank_account/bank_account.json +++ b/erpnext/accounts/doctype/bank_account/bank_account.json @@ -4,7 +4,7 @@ "allow_guest_to_view": 0, "allow_import": 0, "allow_rename": 1, - "autoname": "field:account_name", + "autoname": "", "beta": 0, "creation": "2017-05-29 21:35:13.136357", "custom": 0, @@ -44,7 +44,7 @@ "search_index": 0, "set_only_once": 0, "translatable": 0, - "unique": 1 + "unique": 0 }, { "allow_bulk_edit": 0, @@ -830,7 +830,7 @@ "columns": 0, "fieldname": "integration_id", "fieldtype": "Data", - "hidden": 0, + "hidden": 1, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, @@ -839,7 +839,7 @@ "in_standard_filter": 0, "label": "Integration ID", "length": 0, - "no_copy": 0, + "no_copy": 1, "permlevel": 0, "precision": "", "print_hide": 0, @@ -860,6 +860,7 @@ "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, @@ -959,7 +960,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-11-15 17:37:10.340070", + "modified": "2018-11-27 16:32:17.612257", "modified_by": "Administrator", "module": "Accounts", "name": "Bank Account", diff --git a/erpnext/accounts/doctype/bank_account/bank_account.py b/erpnext/accounts/doctype/bank_account/bank_account.py index 08f8248f3d7..8f672827939 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_reconciliation_dashboard/PlaidLink.vue b/erpnext/accounts/doctype/bank_reconciliation_dashboard/PlaidLink.vue deleted file mode 100644 index 40f486c9d1d..00000000000 --- a/erpnext/accounts/doctype/bank_reconciliation_dashboard/PlaidLink.vue +++ /dev/null @@ -1,136 +0,0 @@ - - - - \ No newline at end of file diff --git a/erpnext/accounts/doctype/bank_reconciliation_dashboard/bank_reconciliation_dashboard.js b/erpnext/accounts/doctype/bank_reconciliation_dashboard/bank_reconciliation_dashboard.js deleted file mode 100644 index 69e91a8ab92..00000000000 --- a/erpnext/accounts/doctype/bank_reconciliation_dashboard/bank_reconciliation_dashboard.js +++ /dev/null @@ -1,217 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.provide("erpnext.accounts"); - - -frappe.ui.form.on('Bank Reconciliation Dashboard', { - refresh: function(frm) { - frm.disable_save(); - toggle_sidebar(frm); - frm.page.add_menu_item(__("Toggle Sidebar"), function() { - toggle_sidebar(frm); - }); - - new erpnext.accounts.newInstitution(frm); - }, - import_data: function(frm) { - new erpnext.accounts.bankTransactionUpload(frm); - }, - sync_data: function(frm) { - new erpnext.accounts.bankTransactionSync(frm); - }, - reconcile_data: function(frm) { - console.log("test") - }, - -}); - -let toggle_sidebar = function(frm) { - frm.sidebar.sidebar.toggle(); - frm.page.current_view.find('.layout-main-section-wrapper').toggleClass('col-md-10 col-md-12'); -} - -erpnext.accounts.bankTransactionUpload = class bankTransactionUpload { - constructor(frm) { - this.frm = frm; - this.data = []; - this.import_wrapper = $(frm.fields_dict['import_html'].wrapper); - this.table_container = $(frm.fields_dict['table_container'].wrapper); - - 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() { - let me = this; - frappe.upload.make({ - parent: me.import_wrapper, - 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.bind_events(); - } - } - }) - } - - setup_transactions_dom() { - this.table_container.append(` -
-
- -
`) - } - - create_datatable() { - this.datatable = new DataTable('.transactions-table', { - columns: this.data.columns, - data: this.data.data - }) - } - - bind_events() { - this.table_container.on('click', '.transactions-btn', function() { - console.log("Test") - }) - } - - add_bank_entries() { - frappe.xcall('erpnext.accounts.doctype.bank_transaction.bank_transaction_upload.create_bank_entries', - {columns: this.data.datamanager.columns, data: this.data.datamanager.data, bank_account: this.frm.doc.bank_account} - ).then((result) => { - console.log(result) - }) - } -} - -erpnext.accounts.bankTransactionSync = class bankTransactionSync { - constructor(frm) { - this.frm = frm; - this.data = []; - this.import_wrapper = $(frm.fields_dict['import_html'].wrapper); - this.table_container = $(frm.fields_dict['table_container'].wrapper); - - - this.init_config() - 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(); - }); - } - - init_config() { - let 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() { - let me = this; - frappe.db.get_value("Bank Account", me.frm.doc.bank_account, "bank", (v) => { - frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.sync_transactions', { - bank: v['bank'], - bank_account: me.frm.doc.bank_account - }) - .then((result) => { - console.log(result) - me.get_transactions(); - }) - }) - } - - get_transactions() { - let me = this; - frappe.db.get_list('Bank Transaction', { - fields: ['name', 'date', 'status', 'debit', 'credit', 'currency', 'description'], - filters: {"docstatus": 1}, - or_filters: [['reference_number', '=', '']] - - }).then((transactions) => { - me.transactions = transactions; - console.log(me) - }) - } - - make() { - - } -} - -erpnext.accounts.newInstitution = class newInstitution { - constructor(frm) { - this.frm = frm; - this.init_config() - } - - init_config() { - let me = this; - frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.plaid_configuration') - .then(result => { - if (result) { - me.plaid_env = result.plaid_env; - me.plaid_public_key = result.plaid_public_key; - me.client_name = result.client_name; - me.new_plaid_link() - } - }) - } - - plaid_success(token, response) { - 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}) - }) - .then((result) => { - this.getBankAccounts(); - }) - } - - new_plaid_link() { - let me = this; - frappe.require('assets/js/frappe-vue.js', () => { - new Vue({ - el: $(frm.fields_dict['new_institution'].wrapper), - render(h) { - return h(PlaidLink, { - props: { - env: me.plaid_env, - publicKey: me.plaid_public_key, - clientName: me.client_name, - product: ["transactions", "auth"], - subtitle: "Test", - plaidSuccess: me.plaid_success - } - }) - } - }); - }) - } -} \ No newline at end of file diff --git a/erpnext/accounts/doctype/bank_reconciliation_dashboard/bank_reconciliation_dashboard.json b/erpnext/accounts/doctype/bank_reconciliation_dashboard/bank_reconciliation_dashboard.json deleted file mode 100644 index 36a7e590636..00000000000 --- a/erpnext/accounts/doctype/bank_reconciliation_dashboard/bank_reconciliation_dashboard.json +++ /dev/null @@ -1,539 +0,0 @@ -{ - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 1, - "creation": "2018-11-14 17:30:33.401641", - "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": "section_break_1", - "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": "Select a 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 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "bank_account", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Bank Account", - "length": 0, - "no_copy": 0, - "options": "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": 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": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "new_institution", - "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": "Add a new institution/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": "eval:doc.bank_account", - "fieldname": "section_break_2", - "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": "Select an action", - "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": "import_data", - "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": "Import Data", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_4", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sync_data", - "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": "Synchronize Data", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 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": "reconcile_data", - "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": "Reconcile Data", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 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": 1, - "columns": 0, - "fieldname": "action_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, - "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": "import_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": "Bank Statement Import", - "length": 0, - "no_copy": 0, - "options": "", - "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": "reconcile_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, - "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_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": "table_container", - "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, - "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-11-15 18:02:39.720945", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Bank Reconciliation Dashboard", - "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/accounts/doctype/bank_reconciliation_dashboard/bank_reconciliation_dashboard.py b/erpnext/accounts/doctype/bank_reconciliation_dashboard/bank_reconciliation_dashboard.py deleted file mode 100644 index 03274408f94..00000000000 --- a/erpnext/accounts/doctype/bank_reconciliation_dashboard/bank_reconciliation_dashboard.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -from frappe.model.document import Document - -class BankReconciliationDashboard(Document): - pass diff --git a/erpnext/accounts/doctype/bank_reconciliation_dashboard/test_bank_reconciliation_dashboard.js b/erpnext/accounts/doctype/bank_reconciliation_dashboard/test_bank_reconciliation_dashboard.js deleted file mode 100644 index 3022a509876..00000000000 --- a/erpnext/accounts/doctype/bank_reconciliation_dashboard/test_bank_reconciliation_dashboard.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Bank Reconciliation Dashboard", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Bank Reconciliation Dashboard - () => frappe.tests.make('Bank Reconciliation Dashboard', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/erpnext/accounts/doctype/bank_reconciliation_dashboard/test_bank_reconciliation_dashboard.py b/erpnext/accounts/doctype/bank_reconciliation_dashboard/test_bank_reconciliation_dashboard.py deleted file mode 100644 index 9b7ede2e607..00000000000 --- a/erpnext/accounts/doctype/bank_reconciliation_dashboard/test_bank_reconciliation_dashboard.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt -from __future__ import unicode_literals - -import frappe -import unittest - -class TestBankReconciliationDashboard(unittest.TestCase): - pass diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.json b/erpnext/accounts/doctype/bank_transaction/bank_transaction.json index 7c39087dfab..6f6e97ae355 100644 --- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.json +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.json @@ -223,7 +223,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_global_search": 0, - "in_list_view": 0, + "in_list_view": 1, "in_standard_filter": 0, "label": "Debit", "length": 0, @@ -255,7 +255,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_global_search": 0, - "in_list_view": 0, + "in_list_view": 1, "in_standard_filter": 0, "label": "Credit", "length": 0, @@ -382,7 +382,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_global_search": 0, - "in_list_view": 0, + "in_list_view": 1, "in_standard_filter": 0, "label": "Description", "length": 0, @@ -526,6 +526,39 @@ "set_only_once": 0, "translatable": 0, "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "payment_entry", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Payment Entry", + "length": 0, + "no_copy": 0, + "options": "Payment Entry", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 } ], "has_web_view": 0, @@ -538,7 +571,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-10-26 15:58:53.400200", + "modified": "2018-11-27 13:26:53.794350", "modified_by": "Administrator", "module": "Accounts", "name": "Bank Transaction", @@ -609,6 +642,7 @@ "show_name_in_global_search": 0, "sort_field": "modified", "sort_order": "DESC", + "title_field": "bank_account", "track_changes": 0, "track_seen": 0, "track_views": 0 diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction_upload.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction_upload.py index 7e591e4a5c3..96529679954 100644 --- a/erpnext/accounts/doctype/bank_transaction/bank_transaction_upload.py +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction_upload.py @@ -32,10 +32,12 @@ def upload_bank_statement(): @frappe.whitelist() def create_bank_entries(columns, data, bank_account): - bank_account = json.loads(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]}) @@ -46,10 +48,12 @@ def create_bank_entries(columns, data, bank_account): }) bank_transaction.update(fields) bank_transaction.date = getdate(bank_transaction.date) - bank_transaction.bank_account = bank_account["name"] + bank_transaction.bank_account = bank_account bank_transaction.insert() + bank_transaction.submit() + count = count + 1 - return 'success' + return count def get_header_mapping(columns, bank_account): mapping = get_bank_mapping(bank_account) @@ -62,7 +66,7 @@ def get_header_mapping(columns, bank_account): return header_map def get_bank_mapping(bank_account): - bank_name = frappe.db.get_value("Bank Account", bank_account["name"], "bank") + 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} diff --git a/erpnext/accounts/doctype/bank_reconciliation_dashboard/__init__.py b/erpnext/accounts/page/bank_reconciliation/__init__.py similarity index 100% rename from erpnext/accounts/doctype/bank_reconciliation_dashboard/__init__.py rename to erpnext/accounts/page/bank_reconciliation/__init__.py 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..58b7c4a6c1d --- /dev/null +++ b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js @@ -0,0 +1,491 @@ +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.make(); + this.add_plaid_btn(); + } + + make() { + const me = this; + + me.$main_section = $(`
`).appendTo(me.page.main); + + 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(); + } + } + }) + } + + add_plaid_btn() { + const me = this; + frappe.db.get_value("Plaid Settings", "Plaid Settings", "enabled", (r) => { + if (r.enabled == "1") { + me.parent.page.add_inner_button(__('Link a new bank account'), function() { + new erpnext.accounts.plaidLink(this) + }) + } + }) + } + + add_actions() { + const me = this; + + me.page.show_actions_menu() + + me.page.add_action_item(__("Upload a statement"), function() { + me.clear_page_content(); + new erpnext.accounts.bankTransactionUpload(me); + }, true) + me.page.add_action_item(__("Synchronize this account"), function() { + me.clear_page_content(); + new erpnext.accounts.bankTransactionSync(me); + }, true) + me.page.add_action_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; + console.log(me) + frappe.model.with_doctype("Bank Transaction", () => { + 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() { + this.datatable = new DataTable('.transactions-table', { + columns: this.data.columns, + data: this.data.data + }) + } + + 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) => { + console.log(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.plaidLink = class plaidLink { + constructor(parent) { + this.parent = 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() + } + }) + } + + 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) { + console.error('There was an issue loading the link-initialize.js script'); + console.log(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((result) => { + console.log(result) + frappe.show_alert({message:__("Bank accounts added"), indicator:'green'}); + }) + }, __("Select a company"), __("Continue")); + } +} + + +erpnext.accounts.ReconciliationTool = class ReconciliationTool extends frappe.views.BaseList { + constructor(opts) { + super(opts); + this.show(); + } + + setup_defaults() { + super.setup_defaults(); + + 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", "payment_entry", "=", ""]) + }); + + } + + 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); + }) + + me.parent.page.hide_menu() + } + + 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-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(); + }) + } + + 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) + } + + show_dialog(data) { + const me = this; + frappe.xcall('erpnext.accounts.page.bank_reconciliation.bank_reconciliation.get_linked_payments', + {bank_transaction: data} + ) + .then((result) => { + me.make_dialog(result) + }) + } + + make_dialog(data) { + const me = this; + 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_entry', + options: 'Payment Entry', + label: 'Payment Entry' + }, + { + fieldtype: 'HTML', + fieldname: 'payment_details' + }, + ]; + + me.dialog = new frappe.ui.Dialog({ + title: __("Choose a corresponding payment"), + fields: fields + }); + + const proposals_wrapper = me.dialog.fields_dict.payment_proposals.$wrapper; + if (data.length > 0) { + 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'); + frappe.xcall('erpnext.accounts.page.bank_reconciliation.bank_reconciliation.reconcile', + {bank_transaction: me.bank_entry, payment_entry: payment_entry}) + .then((result) => console.log(result)) + }) + + $(me.dialog.body).on('blur', '.input-with-feedback', (e) => { + e.preventDefault(); + me.dialog.fields_dict['payment_details'].$wrapper.empty(); + frappe.db.get_doc("Payment Entry", e.target.value) + .then(doc => { + const details_wrapper = me.dialog.fields_dict.payment_details.$wrapper; + details_wrapper.append(frappe.render_template("linked_payment_row", doc)); + }) + + }); + me.dialog.show(); + } +} \ 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..54eda914f8d --- /dev/null +++ b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.py @@ -0,0 +1,109 @@ +# -*- 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 operator import itemgetter + +@frappe.whitelist() +def get_linked_payments(bank_transaction): + + transaction = frappe.get_doc("Bank Transaction", bank_transaction) + + amount_matching = check_matching_amount(transaction) + description_matching = check_matching_descriptions(transaction) + + if amount_matching: + match = check_amount_vs_description(amount_matching, description_matching) + if match: + return match + else: + return merge_matching_lists(amount_matching, description_matching) + + else: + linked_payments = get_matching_transactions_payments(description_matching) + return linked_payments + +@frappe.whitelist() +def reconcile(bank_transaction, payment_entry): + transaction = frappe.get_doc("Bank Transaction", bank_transaction) + payment_entry = frappe.get_doc("Payment Entry", payment_entry) + + if transaction.payment_entry: + frappe.throw(_("This bank transaction is already linked to a payment entry")) + + if transaction.credit > 0 and payment_entry.payment_type == "Pay": + frappe.throw(_("The selected payment entry should be linked with a debitor bank transaction")) + + if transaction.debit > 0 and payment_entry.payment_type == "Receive": + frappe.throw(_("The selected payment entry should be linked with a creditor bank transaction")) + + frappe.db.set_value("Bank Transaction", bank_transaction, "payment_entry", payment_entry) + linked_bank_transactions = frappe.get_all("Bank Transaction", filters={"payment_entry": payment_entry, "docstatus": 1}, + fields=["sum(debit) as debit", "sum(credit) as credit"]) + cleared_amount = (linked_bank_transactions[0].credit - linked_bank_transactions[0].debit) + + if cleared_amount == payment_entry.total_allocated_amount: + frappe.db.set_value("Payment Entry", payment_entry, "clearance_date", transaction.date) + +def check_matching_amount(transaction): + amount = transaction.credit if transaction.credit > 0 else transaction.debit + payment_type = "Receive" if transaction.credit > 0 else "Pay" + + payments = frappe.get_all("Payment Entry", fields=["name", "paid_amount", "payment_type", "reference_no", "reference_date", + "party", "party_type", "posting_date", "paid_to_account_currency"], filters=[["paid_amount", "like", "{0}%".format(amount)], + ["docstatus", "=", "1"], ["payment_type", "=", payment_type], ["clearance_date", "=", ""]]) + + return payments + + +def check_matching_descriptions(transaction): + bank_transactions = frappe.get_all("Bank Transaction", fields=["name", "description", "payment_entry", "date"], + filters=[["docstatus", "=", "1"], ["payment_entry", "!=", ""]]) + + result = [] + for bank_transaction in bank_transactions: + if bank_transaction.description: + seq=difflib.SequenceMatcher(lambda x: x == " ", transaction.description, bank_transaction.description) + + if seq.ratio() > 0.5: + bank_transaction["ratio"] = seq.ratio() + result.append(bank_transaction) + + return result + +def check_amount_vs_description(amount_matching, description_matching): + result = [] + for match in amount_matching: + result.append([match for x in description_matching if match["name"]==x["payment_entry"]]) + + return match + +def merge_matching_lists(amount_matching, description_matching): + + for match in amount_matching: + if match["name"] in map(itemgetter('payment_entry'), description_matching): + index = map(itemgetter('payment_entry'), description_matching).index(match["name"]) + del description_matching[index] + + linked_payments = get_matching_transactions_payments(description_matching) + + result = amount_matching.append(linked_payments) + return sorted(result, 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: + payment_list = frappe.get_all("Payment Entry", fields=["name", "paid_amount", "payment_type", "reference_no", "reference_date", + "party", "party_type", "posting_date", "paid_to_account_currency"], filters=[["name", "in", payments]]) + + return sorted(payment_list, key=lambda x: payment_by_ratio[x["name"]]) + + else: + return [] \ 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..ab83ebec311 --- /dev/null +++ b/erpnext/accounts/page/bank_reconciliation/bank_transaction_row.html @@ -0,0 +1,32 @@ +
+
+
+ +
+ {{ description }} +
+ + + +
+ +
+
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..deeca942412 --- /dev/null +++ b/erpnext/accounts/page/bank_reconciliation/linked_payment_row.html @@ -0,0 +1,19 @@ +
+
+
+ +
+ +
+

{{ __("Amount:") }}

{{ format_currency(paid_amount, paid_to_account_currency) }}
+

{{ __("Party:") }}

{{ party }}
+

{{ __("Reference:") }}

{{ reference_no }}
+
+
+ +
+
+
diff --git a/erpnext/config/accounts.py b/erpnext/config/accounts.py index 9d8e1bf3c34..3a8816ea498 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/erpnext_integrations/doctype/plaid_settings/plaid_connector.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py index 24bb2e9fd9e..f4eb97b32ef 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py @@ -42,7 +42,6 @@ class PlaidConnector(): def auth(self): try: - print(self.access_token) self.client.Auth.get(self.access_token) print("Authentication successful.....") except ItemError as e: diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js index 18519bb9b1a..44a261946cf 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js @@ -7,6 +7,6 @@ frappe.ui.form.on('Plaid Settings', { }, connect_btn: function(frm) { - frappe.set_route('bankreconciliation/synchronization'); + frappe.set_route('bank-reconciliation'); } }); \ 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 index 476e56d5c45..17acb6a6c9d 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py @@ -16,13 +16,14 @@ class PlaidSettings(Document): @frappe.whitelist() def plaid_configuration(): - 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 } - + 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) - frappe.log_error(response) plaid = PlaidConnector() access_token = plaid.get_access_token(token) @@ -46,12 +47,14 @@ def add_institution(token, response): return bank @frappe.whitelist() -def add_bank_accounts(response, bank): +def add_bank_accounts(response, bank, company): response = json.loads(response) bank = json.loads(bank) - company = "Dokos" 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"]) @@ -80,6 +83,8 @@ def add_bank_accounts(response, bank): result.append(new_account.name) + except frappe.UniqueValidationError as e: + 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()) @@ -135,7 +140,7 @@ 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", dict(account_name=bank_account), ["bank", "integration_id"], as_dict=True) + 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 @@ -175,6 +180,7 @@ def new_bank_transaction(transaction): "description": transaction["name"] }) new_transaction.insert() + new_transaction.submit() result.append(new_transaction.name) diff --git a/erpnext/public/js/reconciliation/Home.vue b/erpnext/public/js/reconciliation/Home.vue deleted file mode 100644 index 57e6cefd694..00000000000 --- a/erpnext/public/js/reconciliation/Home.vue +++ /dev/null @@ -1,81 +0,0 @@ - - - diff --git a/erpnext/public/js/reconciliation/Sidebar.vue b/erpnext/public/js/reconciliation/Sidebar.vue deleted file mode 100644 index e99bf978b2a..00000000000 --- a/erpnext/public/js/reconciliation/Sidebar.vue +++ /dev/null @@ -1,49 +0,0 @@ - - \ No newline at end of file diff --git a/erpnext/public/js/reconciliation/components/AccountCard.vue b/erpnext/public/js/reconciliation/components/AccountCard.vue deleted file mode 100644 index 99fda80626c..00000000000 --- a/erpnext/public/js/reconciliation/components/AccountCard.vue +++ /dev/null @@ -1,78 +0,0 @@ - - - - - \ No newline at end of file diff --git a/erpnext/public/js/reconciliation/components/BankAccountsContainer.vue b/erpnext/public/js/reconciliation/components/BankAccountsContainer.vue deleted file mode 100644 index 099a73755cb..00000000000 --- a/erpnext/public/js/reconciliation/components/BankAccountsContainer.vue +++ /dev/null @@ -1,71 +0,0 @@ - - - - - \ No newline at end of file diff --git a/erpnext/public/js/reconciliation/components/EmptyState.vue b/erpnext/public/js/reconciliation/components/EmptyState.vue deleted file mode 100644 index 490a67eb655..00000000000 --- a/erpnext/public/js/reconciliation/components/EmptyState.vue +++ /dev/null @@ -1,45 +0,0 @@ - - - - - \ No newline at end of file diff --git a/erpnext/public/js/reconciliation/components/NewAccountCard.vue b/erpnext/public/js/reconciliation/components/NewAccountCard.vue deleted file mode 100644 index bf311ac44a0..00000000000 --- a/erpnext/public/js/reconciliation/components/NewAccountCard.vue +++ /dev/null @@ -1,71 +0,0 @@ - - - - - \ No newline at end of file diff --git a/erpnext/public/js/reconciliation/components/TransactionCard.vue b/erpnext/public/js/reconciliation/components/TransactionCard.vue deleted file mode 100644 index 96141116947..00000000000 --- a/erpnext/public/js/reconciliation/components/TransactionCard.vue +++ /dev/null @@ -1,41 +0,0 @@ - - - - - \ No newline at end of file diff --git a/erpnext/public/js/reconciliation/components/TransactionsContainer.vue b/erpnext/public/js/reconciliation/components/TransactionsContainer.vue deleted file mode 100644 index 8c25c146071..00000000000 --- a/erpnext/public/js/reconciliation/components/TransactionsContainer.vue +++ /dev/null @@ -1,72 +0,0 @@ - - - - - \ No newline at end of file diff --git a/erpnext/public/js/reconciliation/pages/Dashboard.vue b/erpnext/public/js/reconciliation/pages/Dashboard.vue deleted file mode 100644 index f60a75ece5f..00000000000 --- a/erpnext/public/js/reconciliation/pages/Dashboard.vue +++ /dev/null @@ -1,25 +0,0 @@ - - - \ No newline at end of file diff --git a/erpnext/public/js/reconciliation/pages/Reconciliation.vue b/erpnext/public/js/reconciliation/pages/Reconciliation.vue deleted file mode 100644 index b946e1be580..00000000000 --- a/erpnext/public/js/reconciliation/pages/Reconciliation.vue +++ /dev/null @@ -1,155 +0,0 @@ - - - - \ No newline at end of file diff --git a/erpnext/public/js/reconciliation/pages/Upload.vue b/erpnext/public/js/reconciliation/pages/Upload.vue deleted file mode 100644 index f88b42929e6..00000000000 --- a/erpnext/public/js/reconciliation/pages/Upload.vue +++ /dev/null @@ -1,115 +0,0 @@ - - - - \ No newline at end of file diff --git a/erpnext/public/js/reconciliation/reconciliation_factory.js b/erpnext/public/js/reconciliation/reconciliation_factory.js deleted file mode 100644 index b76722f6ff8..00000000000 --- a/erpnext/public/js/reconciliation/reconciliation_factory.js +++ /dev/null @@ -1,29 +0,0 @@ -frappe.provide('erpnext.bankreconciliation'); - -frappe.views.bankreconciliationFactory = class bankreconciliationFactory extends frappe.views.Factory { - show() { - if (frappe.pages.bankreconciliation) { - frappe.container.change_to('bankreconciliation'); - } else { - this.make('bankreconciliation'); - } - } - make(page_name) { - const assets = [ - '/assets/js/bankreconciliation.min.js' - ]; - frappe.require(assets, () => { - erpnext.bankreconciliation.home = new erpnext.bankreconciliation.Home({ - parent: this.make_page(true, page_name) - }); - }); - } -}; - -$(document).on('toolbar_setup', () => { - $('#toolbar-user .navbar-reload').after(` -
  • - ${__("Bank Reconciliation")} -
  • - `); -}); \ No newline at end of file diff --git a/erpnext/public/js/reconciliation/reconciliation_home.js b/erpnext/public/js/reconciliation/reconciliation_home.js deleted file mode 100644 index fb7fe647149..00000000000 --- a/erpnext/public/js/reconciliation/reconciliation_home.js +++ /dev/null @@ -1,100 +0,0 @@ -import Vue from 'vue/dist/vue.js'; -import './vue-plugins'; - -import Home from './Home.vue'; -import Sidebar from './Sidebar.vue'; - -import EventEmitter from '../hub/event_emitter'; - -frappe.provide('erpnext.bankreconciliation'); -frappe.provide('frappe.route'); -frappe.provide('frappe.upload'); - -$.extend(erpnext.bankreconciliation, EventEmitter.prototype); -$.extend(frappe.route, EventEmitter.prototype); - -erpnext.bankreconciliation.Home = class bankreconciliation { - constructor({ parent }) { - this.$parent = $(parent); - this.page = parent.page; - this.company = frappe.defaults.get_user_default("Company"); - this.setup_header(); - this.make_sidebar(); - this.make_body(); - this.setup_events(); - this.set_secondary_action(); - } - - make_sidebar() { - this.$sidebar = this.$parent.find('.layout-side-section').addClass('hidden-xs'); - - new Vue({ - el: $('
    ').appendTo(this.$sidebar)[0], - render: h => h(Sidebar) - }); - } - - make_body() { - let me = this; - me.$body = me.$parent.find('.layout-main-section'); - me.$page_container = $('
    ').appendTo(this.$body); - - new Vue({ - el: me.$page_container[0], - render(h) { - return h(Home, {props: { initCompany: me.company}}) - } - }); - } - - setup_header() { - this.page.set_title(__('Bank Reconciliation')); - } - - setup_events() { - this.$parent.on('click', '[data-route]', (e) => { - const $target = $(e.currentTarget); - const route = $target.data().route; - frappe.set_route(route); - }); - - this.$parent.on('click', '[data-action]', e => { - const $target = $(e.currentTarget); - const action = $target.data().action; - - if (action && this[action]) { - this[action].apply(this, $target); - } - }) - } - - set_secondary_action() { - let me = this; - this.page.set_secondary_action(this.company, function () { - me.company_selection_dialog(); - }) - } - - company_selection_dialog() { - let me = this; - let dialog = new frappe.ui.Dialog({ - title: __('Select another company'), - fields: [ - { - "label": "Company", - "fieldname": "company", - "fieldtype": "Link", - "options": "Company" - } - ], - primary_action_label: __('Confirm'), - primary_action: function(v) { - me.company = v.company; - erpnext.bankreconciliation.trigger('company_changed', v.company); - me.set_secondary_action(); - dialog.hide(); - }, - }) - dialog.show(); - } -}; \ No newline at end of file diff --git a/erpnext/public/js/reconciliation/vue-plugins.js b/erpnext/public/js/reconciliation/vue-plugins.js deleted file mode 100644 index 708d179177d..00000000000 --- a/erpnext/public/js/reconciliation/vue-plugins.js +++ /dev/null @@ -1,17 +0,0 @@ -import Vue from 'vue/dist/vue.js'; - -Vue.prototype.__ = window.__; -Vue.prototype.frappe = window.frappe; - -Vue.directive('route', { - bind(el, binding) { - const route = binding.value; - if (!route) return; - el.classList.add('cursor-pointer'); - el.dataset.route = route; - el.addEventListener('click', () => frappe.set_route(route)); - }, - unbind(el) { - el.classList.remove('cursor-pointer'); - } -}); \ No newline at end of file diff --git a/erpnext/public/less/bankreconciliation.less b/erpnext/public/less/bankreconciliation.less deleted file mode 100644 index 35c144f48e4..00000000000 --- a/erpnext/public/less/bankreconciliation.less +++ /dev/null @@ -1,71 +0,0 @@ - -@import "variables.less"; -@import (reference) 'common.less'; - -body[data-route*="bankreconciliation"] { - - .layout-side-section { - padding-top: 25px; - padding-left: 5px; - padding-right: 25px; - } - - [data-route], [data-action] { - cursor: pointer; - } - - .layout-main-section { - border: none; - font-size: @text-medium; - padding-top: 25px; - - @media (max-width: @screen-xs) { - padding-left: 20px; - padding-right: 20px; - } - } - - input, textarea { - font-size: @text-medium; - } - - .bankreconciliation-sidebar { - padding-top: 25px; - padding-right: 15px; - } - - .bankreconciliation-sidebar-group { - margin-bottom: 10px; - } - - .bankreconciliation-sidebar-item { - padding: 5px 8px; - margin-bottom: 3px; - border-radius: 4px; - border: 1px solid transparent; - - &.active, &:hover:not(.is-title) { - border-color: @border-color; - } - } - - .form-container { - .frappe-control { - max-width: 100% !important; - } - } - - .upload-btn-container { - margin-top: 20px; - } - - .account-card { - .selected { - background-color: #e2f4d0; - } - } - - .table-container { - margin-top: 20px; - } -} \ No newline at end of file diff --git a/erpnext/public/less/erpnext.less b/erpnext/public/less/erpnext.less index b746cc138f6..0c1751657e4 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); } @@ -459,3 +459,30 @@ body[data-route="pos"] { padding-right: 45px; } } + +// Bank Reconciliation + +.plaid-btn { + margin-top: 24px; + color: #fff; + background-color: #5bc0de; + border-color: #46b8da; +} + +.transactions-btn { + margin: 15px; +} + +[data-fieldname='reconcile_data'], +[data-fieldname='sync_data'], +[data-fieldname='import_data'] { + .btn { + color: #fff; + background-color: #8d99a6; + border-color: #7f8c9b; + } +} + +[data-fieldname='table_container'] { + margin: -15px -30px; +} \ No newline at end of file