fix(plaid): cannot reset plaid link for a bank account
diff --git a/erpnext/accounts/doctype/bank/bank.js b/erpnext/accounts/doctype/bank/bank.js
index 059e1d3..337c93e 100644
--- a/erpnext/accounts/doctype/bank/bank.js
+++ b/erpnext/accounts/doctype/bank/bank.js
@@ -25,6 +25,10 @@
frm.add_custom_button(__('Refresh Plaid Link'), () => {
new erpnext.integrations.refreshPlaidLink(frm.doc.plaid_access_token);
});
+
+ frm.add_custom_button(__('Reset Plaid Link'), () => {
+ new erpnext.integrations.plaidLink(frm);
+ });
}
}
});
@@ -121,3 +125,101 @@
frappe.show_alert({ message: __('Plaid Link Updated'), indicator: 'green' });
}
};
+
+erpnext.integrations.plaidLink = class plaidLink {
+ constructor(parent) {
+ this.frm = parent;
+ this.plaidUrl = 'https://cdn.plaid.com/link/v2/stable/link-initialize.js';
+ this.init_config();
+ }
+
+ async init_config() {
+ this.product = ["auth", "transactions"];
+ this.plaid_env = this.frm.doc.plaid_env;
+ this.client_name = frappe.boot.sitename;
+ this.token = await this.get_link_token();
+ this.init_plaid();
+ }
+
+ async get_link_token() {
+ const token = await this.frm.call("get_link_token").then(resp => resp.message);
+ if (!token) {
+ frappe.throw(__('Cannot retrieve link token. Check Error Log for more information'));
+ }
+ return token;
+ }
+
+ 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 = Plaid.create({
+ clientName: me.client_name,
+ product: me.product,
+ env: me.plaid_env,
+ token: me.token,
+ onSuccess: me.plaid_success
+ });
+ }
+
+ onScriptError(error) {
+ frappe.msgprint(__("There was an issue connecting to Plaid's authentication server. Check browser console for more information"));
+ 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(() => {
+ frappe.show_alert({ message: __("Bank accounts added"), indicator: 'green' });
+ });
+ }, __("Select a company"), __("Continue"));
+ }
+};
diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py
index 5f990cd..42d4b9b 100644
--- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py
+++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py
@@ -99,5 +99,7 @@
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 ItemError as e:
+ raise e
except Exception:
frappe.log_error(frappe.get_traceback(), _("Plaid transactions sync error"))
diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
index ce15e47..3ef069b 100644
--- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
+++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
@@ -12,6 +12,7 @@
from frappe.model.document import Document
from frappe.utils import add_months, formatdate, getdate, today
+from plaid.errors import ItemError
class PlaidSettings(Document):
@staticmethod
@@ -51,7 +52,7 @@
})
bank.insert()
except Exception:
- frappe.throw(frappe.get_traceback())
+ frappe.log_error(frappe.get_traceback(), title=_('Plaid Link Error'))
else:
bank = frappe.get_doc("Bank", response["institution"]["name"])
bank.plaid_access_token = access_token
@@ -83,7 +84,12 @@
if not acc_subtype:
add_account_subtype(account["subtype"])
- if not frappe.db.exists("Bank Account", dict(integration_id=account["id"])):
+ existing_bank_account = frappe.db.exists("Bank Account", {
+ 'account_name': account["name"],
+ 'bank': bank["bank_name"]
+ })
+
+ if not existing_bank_account:
try:
new_account = frappe.get_doc({
"doctype": "Bank Account",
@@ -103,10 +109,27 @@
except frappe.UniqueValidationError:
frappe.msgprint(_("Bank account {0} already exists and could not be created again").format(account["name"]))
except Exception:
- frappe.throw(frappe.get_traceback())
+ frappe.log_error(frappe.get_traceback(), title=_("Plaid Link Error"))
+ frappe.throw(_("There was an error creating Bank Account while linking with Plaid."),
+ title=_("Plaid Link Failed"))
else:
- result.append(frappe.db.get_value("Bank Account", dict(integration_id=account["id"]), "name"))
+ try:
+ existing_account = frappe.get_doc('Bank Account', existing_bank_account)
+ existing_account.update({
+ "bank": bank["bank_name"],
+ "account_name": account["name"],
+ "account_type": account.get("type", ""),
+ "account_subtype": account.get("subtype", ""),
+ "mask": account.get("mask", ""),
+ "integration_id": account["id"]
+ })
+ existing_account.save()
+ result.append(existing_bank_account)
+ except Exception:
+ frappe.log_error(frappe.get_traceback(), title=_("Plaid Link Error"))
+ frappe.throw(_("There was an error updating Bank Account {} while linking with Plaid.").format(
+ existing_bank_account), title=_("Plaid Link Failed"))
return result
@@ -172,9 +195,16 @@
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
+ try:
+ transactions = plaid.get_transactions(start_date=start_date, end_date=end_date, account_id=account_id)
+ except ItemError as e:
+ if e.code == "ITEM_LOGIN_REQUIRED":
+ msg = _("There was an error syncing transactions.") + " "
+ msg += _("Please refresh or reset the Plaid linking of the Bank {}.").format(bank) + " "
+ frappe.log_error(msg, title=_("Plaid Link Refresh Required"))
+
+ return transactions or []
def new_bank_transaction(transaction):