mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-13 10:11:20 +00:00
feat: re-linking bank accounts with plaid (#23913)
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
frappe.provide('erpnext.integrations');
|
||||||
|
|
||||||
frappe.ui.form.on('Bank', {
|
frappe.ui.form.on('Bank', {
|
||||||
onload: function(frm) {
|
onload: function(frm) {
|
||||||
@@ -7,6 +8,12 @@ frappe.ui.form.on('Bank', {
|
|||||||
},
|
},
|
||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
add_fields_to_mapping_table(frm);
|
add_fields_to_mapping_table(frm);
|
||||||
|
|
||||||
|
if (frm.doc.plaid_access_token) {
|
||||||
|
frm.add_custom_button(__('Refresh Plaid Link'), () => {
|
||||||
|
new erpnext.integrations.refreshPlaidLink(frm.doc.plaid_access_token);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -27,4 +34,80 @@ let add_fields_to_mapping_table = function (frm) {
|
|||||||
frm.doc.name).options = options;
|
frm.doc.name).options = options;
|
||||||
|
|
||||||
frm.fields_dict.bank_transaction_mapping.grid.refresh();
|
frm.fields_dict.bank_transaction_mapping.grid.refresh();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
erpnext.integrations.refreshPlaidLink = class refreshPlaidLink {
|
||||||
|
constructor(access_token) {
|
||||||
|
this.access_token = access_token;
|
||||||
|
this.plaidUrl = 'https://cdn.plaid.com/link/v2/stable/link-initialize.js';
|
||||||
|
this.init_config();
|
||||||
|
}
|
||||||
|
|
||||||
|
async init_config() {
|
||||||
|
this.plaid_env = await frappe.db.get_single_value('Plaid Settings', 'plaid_env');
|
||||||
|
this.token = await this.get_link_token_for_update();
|
||||||
|
this.init_plaid();
|
||||||
|
}
|
||||||
|
|
||||||
|
async get_link_token_for_update() {
|
||||||
|
const token = frappe.xcall(
|
||||||
|
'erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.get_link_token_for_update',
|
||||||
|
{ access_token: this.access_token }
|
||||||
|
)
|
||||||
|
if (!token) {
|
||||||
|
frappe.throw(__('Cannot retrieve link token for update. 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({
|
||||||
|
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) {
|
||||||
|
frappe.show_alert({ message: __('Plaid Link Updated'), indicator: 'green' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -29,21 +29,32 @@ class PlaidConnector():
|
|||||||
response = self.client.Item.public_token.exchange(public_token)
|
response = self.client.Item.public_token.exchange(public_token)
|
||||||
access_token = response["access_token"]
|
access_token = response["access_token"]
|
||||||
return access_token
|
return access_token
|
||||||
|
|
||||||
def get_link_token(self):
|
def get_token_request(self, update_mode=False):
|
||||||
token_request = {
|
args = {
|
||||||
"client_name": self.client_name,
|
"client_name": self.client_name,
|
||||||
"client_id": self.settings.plaid_client_id,
|
|
||||||
"secret": self.settings.plaid_secret,
|
|
||||||
"products": self.products,
|
|
||||||
# only allow Plaid-supported languages and countries (LAST: Sep-19-2020)
|
# only allow Plaid-supported languages and countries (LAST: Sep-19-2020)
|
||||||
"language": frappe.local.lang if frappe.local.lang in ["en", "fr", "es", "nl"] else "en",
|
"language": frappe.local.lang if frappe.local.lang in ["en", "fr", "es", "nl"] else "en",
|
||||||
"country_codes": ["US", "CA", "FR", "IE", "NL", "ES", "GB"],
|
"country_codes": ["US", "CA", "ES", "FR", "GB", "IE", "NL"],
|
||||||
"user": {
|
"user": {
|
||||||
"client_user_id": frappe.generate_hash(frappe.session.user, length=32)
|
"client_user_id": frappe.generate_hash(frappe.session.user, length=32)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if update_mode:
|
||||||
|
args["access_token"] = self.access_token
|
||||||
|
else:
|
||||||
|
args.update({
|
||||||
|
"client_id": self.settings.plaid_client_id,
|
||||||
|
"secret": self.settings.plaid_secret,
|
||||||
|
"products": self.products,
|
||||||
|
})
|
||||||
|
|
||||||
|
return args
|
||||||
|
|
||||||
|
def get_link_token(self, update_mode=False):
|
||||||
|
token_request = self.get_token_request(update_mode)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response = self.client.LinkToken.create(token_request)
|
response = self.client.LinkToken.create(token_request)
|
||||||
except InvalidRequestError:
|
except InvalidRequestError:
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ frappe.ui.form.on('Plaid Settings', {
|
|||||||
|
|
||||||
refresh: function (frm) {
|
refresh: function (frm) {
|
||||||
if (frm.doc.enabled) {
|
if (frm.doc.enabled) {
|
||||||
frm.add_custom_button('Link a new bank account', () => {
|
frm.add_custom_button(__('Link a new bank account'), () => {
|
||||||
new erpnext.integrations.plaidLink(frm);
|
new erpnext.integrations.plaidLink(frm);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -30,10 +30,18 @@ erpnext.integrations.plaidLink = class plaidLink {
|
|||||||
this.product = ["auth", "transactions"];
|
this.product = ["auth", "transactions"];
|
||||||
this.plaid_env = this.frm.doc.plaid_env;
|
this.plaid_env = this.frm.doc.plaid_env;
|
||||||
this.client_name = frappe.boot.sitename;
|
this.client_name = frappe.boot.sitename;
|
||||||
this.token = await this.frm.call("get_link_token").then(resp => resp.message);
|
this.token = await this.get_link_token();
|
||||||
this.init_plaid();
|
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() {
|
init_plaid() {
|
||||||
const me = this;
|
const me = this;
|
||||||
me.loadScript(me.plaidUrl)
|
me.loadScript(me.plaidUrl)
|
||||||
@@ -78,8 +86,8 @@ erpnext.integrations.plaidLink = class plaidLink {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onScriptError(error) {
|
onScriptError(error) {
|
||||||
frappe.msgprint("There was an issue connecting to Plaid's authentication server");
|
frappe.msgprint(__("There was an issue connecting to Plaid's authentication server. Check browser console for more information"));
|
||||||
frappe.msgprint(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
plaid_success(token, response) {
|
plaid_success(token, response) {
|
||||||
@@ -107,4 +115,4 @@ erpnext.integrations.plaidLink = class plaidLink {
|
|||||||
});
|
});
|
||||||
}, __("Select a company"), __("Continue"));
|
}, __("Select a company"), __("Continue"));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -239,3 +239,8 @@ def automatic_synchronization():
|
|||||||
bank=plaid_account.bank,
|
bank=plaid_account.bank,
|
||||||
bank_account=plaid_account.name
|
bank_account=plaid_account.name
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def get_link_token_for_update(access_token):
|
||||||
|
plaid = PlaidConnector(access_token)
|
||||||
|
return plaid.get_link_token(update_mode=True)
|
||||||
Reference in New Issue
Block a user