fix: use Plaid's new API (develop) (#23318)
Co-authored-by: Mangesh-Khairnar <mkhairnar10@gmail.com>
This commit is contained in:
parent
44c692ed40
commit
e42f08085b
@ -72,7 +72,7 @@ erpnext.accounts.bankReconciliation = class BankReconciliation {
|
|||||||
check_plaid_status() {
|
check_plaid_status() {
|
||||||
const me = this;
|
const me = this;
|
||||||
frappe.db.get_value("Plaid Settings", "Plaid Settings", "enabled", (r) => {
|
frappe.db.get_value("Plaid Settings", "Plaid Settings", "enabled", (r) => {
|
||||||
if (r && r.enabled == "1") {
|
if (r && r.enabled === "1") {
|
||||||
me.plaid_status = "active"
|
me.plaid_status = "active"
|
||||||
} else {
|
} else {
|
||||||
me.plaid_status = "inactive"
|
me.plaid_status = "inactive"
|
||||||
@ -139,7 +139,7 @@ erpnext.accounts.bankTransactionUpload = class bankTransactionUpload {
|
|||||||
}
|
}
|
||||||
|
|
||||||
make() {
|
make() {
|
||||||
const me = this;
|
const me = this;
|
||||||
new frappe.ui.FileUploader({
|
new frappe.ui.FileUploader({
|
||||||
method: 'erpnext.accounts.doctype.bank_transaction.bank_transaction_upload.upload_bank_statement',
|
method: 'erpnext.accounts.doctype.bank_transaction.bank_transaction_upload.upload_bank_statement',
|
||||||
allow_multiple: 0,
|
allow_multiple: 0,
|
||||||
@ -214,31 +214,35 @@ erpnext.accounts.bankTransactionSync = class bankTransactionSync {
|
|||||||
|
|
||||||
init_config() {
|
init_config() {
|
||||||
const me = this;
|
const me = this;
|
||||||
frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.plaid_configuration')
|
frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.get_plaid_configuration')
|
||||||
.then(result => {
|
.then(result => {
|
||||||
me.plaid_env = result.plaid_env;
|
me.plaid_env = result.plaid_env;
|
||||||
me.plaid_public_key = result.plaid_public_key;
|
me.client_name = result.client_name;
|
||||||
me.client_name = result.client_name;
|
me.link_token = result.link_token;
|
||||||
me.sync_transactions()
|
me.sync_transactions();
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
sync_transactions() {
|
sync_transactions() {
|
||||||
const me = this;
|
const me = this;
|
||||||
frappe.db.get_value("Bank Account", me.parent.bank_account, "bank", (v) => {
|
frappe.db.get_value("Bank Account", me.parent.bank_account, "bank", (r) => {
|
||||||
frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.sync_transactions', {
|
frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.sync_transactions', {
|
||||||
bank: v['bank'],
|
bank: r.bank,
|
||||||
bank_account: me.parent.bank_account,
|
bank_account: me.parent.bank_account,
|
||||||
freeze: true
|
freeze: true
|
||||||
})
|
})
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
let result_title = (result.length > 0) ? __("{0} bank transaction(s) created", [result.length]) : __("This bank account is already synchronized")
|
let result_title = (result && result.length > 0)
|
||||||
|
? __("{0} bank transaction(s) created", [result.length])
|
||||||
|
: __("This bank account is already synchronized");
|
||||||
|
|
||||||
let result_msg = `
|
let result_msg = `
|
||||||
<div class="flex justify-center align-center text-muted" style="height: 50vh; display: flex;">
|
<div class="flex justify-center align-center text-muted" style="height: 50vh; display: flex;">
|
||||||
<h5 class="text-muted">${result_title}</h5>
|
<h5 class="text-muted">${result_title}</h5>
|
||||||
</div>`
|
</div>`
|
||||||
|
|
||||||
this.parent.$main_section.append(result_msg)
|
this.parent.$main_section.append(result_msg)
|
||||||
frappe.show_alert({message:__("Bank account '{0}' has been synchronized", [me.parent.bank_account]), indicator:'green'});
|
frappe.show_alert({ message: __("Bank account '{0}' has been synchronized", [me.parent.bank_account]), indicator: 'green' });
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -384,7 +388,7 @@ erpnext.accounts.ReconciliationRow = class ReconciliationRow {
|
|||||||
})
|
})
|
||||||
|
|
||||||
frappe.xcall('erpnext.accounts.page.bank_reconciliation.bank_reconciliation.get_linked_payments',
|
frappe.xcall('erpnext.accounts.page.bank_reconciliation.bank_reconciliation.get_linked_payments',
|
||||||
{bank_transaction: data, freeze:true, freeze_message:__("Finding linked payments")}
|
{ bank_transaction: data, freeze: true, freeze_message: __("Finding linked payments") }
|
||||||
).then((result) => {
|
).then((result) => {
|
||||||
me.make_dialog(result)
|
me.make_dialog(result)
|
||||||
})
|
})
|
||||||
|
@ -2,81 +2,84 @@
|
|||||||
# 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
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
import plaid
|
||||||
from frappe import _
|
import requests
|
||||||
from frappe.utils.password import get_decrypted_password
|
from plaid.errors import APIError, ItemError, InvalidRequestError
|
||||||
from plaid import Client
|
|
||||||
from plaid.errors import APIError, ItemError
|
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
import requests
|
from frappe import _
|
||||||
|
|
||||||
|
|
||||||
class PlaidConnector():
|
class PlaidConnector():
|
||||||
def __init__(self, access_token=None):
|
def __init__(self, access_token=None):
|
||||||
|
|
||||||
plaid_settings = frappe.get_single("Plaid Settings")
|
|
||||||
|
|
||||||
self.config = {
|
|
||||||
"plaid_client_id": plaid_settings.plaid_client_id,
|
|
||||||
"plaid_secret": get_decrypted_password("Plaid Settings", "Plaid Settings", 'plaid_secret'),
|
|
||||||
"plaid_public_key": plaid_settings.plaid_public_key,
|
|
||||||
"plaid_env": plaid_settings.plaid_env
|
|
||||||
}
|
|
||||||
|
|
||||||
self.client = Client(client_id=self.config.get("plaid_client_id"),
|
|
||||||
secret=self.config.get("plaid_secret"),
|
|
||||||
public_key=self.config.get("plaid_public_key"),
|
|
||||||
environment=self.config.get("plaid_env")
|
|
||||||
)
|
|
||||||
|
|
||||||
self.access_token = access_token
|
self.access_token = access_token
|
||||||
|
self.settings = frappe.get_single("Plaid Settings")
|
||||||
|
self.products = ["auth", "transactions"]
|
||||||
|
self.client_name = frappe.local.site
|
||||||
|
self.client = plaid.Client(
|
||||||
|
client_id=self.settings.plaid_client_id,
|
||||||
|
secret=self.settings.get_password("plaid_secret"),
|
||||||
|
environment=self.settings.plaid_env,
|
||||||
|
api_version="2019-05-29"
|
||||||
|
)
|
||||||
|
|
||||||
def get_access_token(self, public_token):
|
def get_access_token(self, public_token):
|
||||||
if public_token is None:
|
if public_token is None:
|
||||||
frappe.log_error(_("Public token is missing for this bank"), _("Plaid public token error"))
|
frappe.log_error(_("Public token is missing for this bank"), _("Plaid public token error"))
|
||||||
|
|
||||||
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):
|
||||||
|
token_request = {
|
||||||
|
"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)
|
||||||
|
"language": frappe.local.lang if frappe.local.lang in ["en", "fr", "es", "nl"] else "en",
|
||||||
|
"country_codes": ["US", "CA", "FR", "IE", "NL", "ES", "GB"],
|
||||||
|
"user": {
|
||||||
|
"client_user_id": frappe.generate_hash(frappe.session.user, length=32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = self.client.LinkToken.create(token_request)
|
||||||
|
except InvalidRequestError:
|
||||||
|
frappe.log_error(frappe.get_traceback(), _("Plaid invalid request error"))
|
||||||
|
frappe.msgprint(_("Please check your Plaid client ID and secret values"))
|
||||||
|
except APIError as e:
|
||||||
|
frappe.log_error(frappe.get_traceback(), _("Plaid authentication error"))
|
||||||
|
frappe.throw(_(str(e)), title=_("Authentication Failed"))
|
||||||
|
else:
|
||||||
|
return response["link_token"]
|
||||||
|
|
||||||
def auth(self):
|
def auth(self):
|
||||||
try:
|
try:
|
||||||
self.client.Auth.get(self.access_token)
|
self.client.Auth.get(self.access_token)
|
||||||
print("Authentication successful.....")
|
|
||||||
except ItemError as e:
|
except ItemError as e:
|
||||||
if e.code == 'ITEM_LOGIN_REQUIRED':
|
if e.code == "ITEM_LOGIN_REQUIRED":
|
||||||
pass
|
|
||||||
else:
|
|
||||||
pass
|
pass
|
||||||
except APIError as e:
|
except APIError as e:
|
||||||
if e.code == 'PLANNED_MAINTENANCE':
|
if e.code == "PLANNED_MAINTENANCE":
|
||||||
pass
|
|
||||||
else:
|
|
||||||
pass
|
pass
|
||||||
except requests.Timeout:
|
except requests.Timeout:
|
||||||
pass
|
pass
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
|
||||||
frappe.log_error(frappe.get_traceback(), _("Plaid authentication error"))
|
frappe.log_error(frappe.get_traceback(), _("Plaid authentication error"))
|
||||||
frappe.msgprint({"title": _("Authentication Failed"), "message":e, "raise_exception":1, "indicator":'red'})
|
frappe.throw(_(str(e)), title=_("Authentication Failed"))
|
||||||
|
|
||||||
def get_transactions(self, start_date, end_date, account_id=None):
|
def get_transactions(self, start_date, end_date, account_id=None):
|
||||||
|
self.auth()
|
||||||
|
account_ids = list(account_id) if account_id else None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.auth()
|
response = self.client.Transactions.get(self.access_token, start_date=start_date, end_date=end_date, account_ids=account_ids)
|
||||||
if account_id:
|
transactions = response["transactions"]
|
||||||
account_ids = [account_id]
|
while len(transactions) < response["total_transactions"]:
|
||||||
|
|
||||||
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))
|
response = self.client.Transactions.get(self.access_token, start_date=start_date, end_date=end_date, offset=len(transactions))
|
||||||
transactions.extend(response['transactions'])
|
transactions.extend(response["transactions"])
|
||||||
return transactions
|
return transactions
|
||||||
except Exception:
|
except Exception:
|
||||||
frappe.log_error(frappe.get_traceback(), _("Plaid transactions sync error"))
|
frappe.log_error(frappe.get_traceback(), _("Plaid transactions sync error"))
|
||||||
|
@ -4,14 +4,14 @@
|
|||||||
frappe.provide("erpnext.integrations");
|
frappe.provide("erpnext.integrations");
|
||||||
|
|
||||||
frappe.ui.form.on('Plaid Settings', {
|
frappe.ui.form.on('Plaid Settings', {
|
||||||
enabled: function(frm) {
|
enabled: function (frm) {
|
||||||
frm.toggle_reqd('plaid_client_id', frm.doc.enabled);
|
frm.toggle_reqd('plaid_client_id', frm.doc.enabled);
|
||||||
frm.toggle_reqd('plaid_secret', frm.doc.enabled);
|
frm.toggle_reqd('plaid_secret', frm.doc.enabled);
|
||||||
frm.toggle_reqd('plaid_public_key', frm.doc.enabled);
|
|
||||||
frm.toggle_reqd('plaid_env', frm.doc.enabled);
|
frm.toggle_reqd('plaid_env', frm.doc.enabled);
|
||||||
},
|
},
|
||||||
refresh: function(frm) {
|
|
||||||
if(frm.doc.enabled) {
|
refresh: function (frm) {
|
||||||
|
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);
|
||||||
});
|
});
|
||||||
@ -22,17 +22,16 @@ frappe.ui.form.on('Plaid Settings', {
|
|||||||
erpnext.integrations.plaidLink = class plaidLink {
|
erpnext.integrations.plaidLink = class plaidLink {
|
||||||
constructor(parent) {
|
constructor(parent) {
|
||||||
this.frm = parent;
|
this.frm = parent;
|
||||||
this.product = ["transactions", "auth"];
|
|
||||||
this.plaidUrl = 'https://cdn.plaid.com/link/v2/stable/link-initialize.js';
|
this.plaidUrl = 'https://cdn.plaid.com/link/v2/stable/link-initialize.js';
|
||||||
this.init_config();
|
this.init_config();
|
||||||
}
|
}
|
||||||
|
|
||||||
init_config() {
|
async init_config() {
|
||||||
const me = this;
|
this.product = ["auth", "transactions"];
|
||||||
me.plaid_env = me.frm.doc.plaid_env;
|
this.plaid_env = this.frm.doc.plaid_env;
|
||||||
me.plaid_public_key = me.frm.doc.plaid_public_key;
|
this.client_name = frappe.boot.sitename;
|
||||||
me.client_name = frappe.boot.sitename;
|
this.token = await this.frm.call("get_link_token").then(resp => resp.message);
|
||||||
me.init_plaid();
|
this.init_plaid();
|
||||||
}
|
}
|
||||||
|
|
||||||
init_plaid() {
|
init_plaid() {
|
||||||
@ -69,17 +68,17 @@ erpnext.integrations.plaidLink = class plaidLink {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onScriptLoaded(me) {
|
onScriptLoaded(me) {
|
||||||
me.linkHandler = window.Plaid.create({
|
me.linkHandler = Plaid.create({
|
||||||
clientName: me.client_name,
|
clientName: me.client_name,
|
||||||
|
product: me.product,
|
||||||
env: me.plaid_env,
|
env: me.plaid_env,
|
||||||
key: me.plaid_public_key,
|
token: me.token,
|
||||||
onSuccess: me.plaid_success,
|
onSuccess: me.plaid_success
|
||||||
product: me.product
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onScriptError(error) {
|
onScriptError(error) {
|
||||||
frappe.msgprint('There was an issue loading the link-initialize.js script');
|
frappe.msgprint("There was an issue connecting to Plaid's authentication server");
|
||||||
frappe.msgprint(error);
|
frappe.msgprint(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,21 +86,25 @@ erpnext.integrations.plaidLink = class plaidLink {
|
|||||||
const me = this;
|
const me = this;
|
||||||
|
|
||||||
frappe.prompt({
|
frappe.prompt({
|
||||||
fieldtype:"Link",
|
fieldtype: "Link",
|
||||||
options: "Company",
|
options: "Company",
|
||||||
label:__("Company"),
|
label: __("Company"),
|
||||||
fieldname:"company",
|
fieldname: "company",
|
||||||
reqd:1
|
reqd: 1
|
||||||
}, (data) => {
|
}, (data) => {
|
||||||
me.company = data.company;
|
me.company = data.company;
|
||||||
frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.add_institution', {token: token, response: response})
|
frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.add_institution', {
|
||||||
.then((result) => {
|
token: token,
|
||||||
frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.add_bank_accounts', {response: response,
|
response: response
|
||||||
bank: result, company: me.company});
|
}).then((result) => {
|
||||||
})
|
frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.add_bank_accounts', {
|
||||||
.then(() => {
|
response: response,
|
||||||
frappe.show_alert({message:__("Bank accounts added"), indicator:'green'});
|
bank: result,
|
||||||
|
company: me.company
|
||||||
});
|
});
|
||||||
|
}).then(() => {
|
||||||
|
frappe.show_alert({ message: __("Bank accounts added"), indicator: 'green' });
|
||||||
|
});
|
||||||
}, __("Select a company"), __("Continue"));
|
}, __("Select a company"), __("Continue"));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
{
|
{
|
||||||
"actions": [],
|
|
||||||
"creation": "2018-10-25 10:02:48.656165",
|
"creation": "2018-10-25 10:02:48.656165",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
@ -12,7 +11,6 @@
|
|||||||
"plaid_client_id",
|
"plaid_client_id",
|
||||||
"plaid_secret",
|
"plaid_secret",
|
||||||
"column_break_7",
|
"column_break_7",
|
||||||
"plaid_public_key",
|
|
||||||
"plaid_env"
|
"plaid_env"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
@ -41,12 +39,6 @@
|
|||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Plaid Secret"
|
"label": "Plaid Secret"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fieldname": "plaid_public_key",
|
|
||||||
"fieldtype": "Data",
|
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "Plaid Public Key"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "plaid_env",
|
"fieldname": "plaid_env",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
@ -69,8 +61,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"links": [],
|
"modified": "2020-09-12 02:31:44.542385",
|
||||||
"modified": "2020-02-07 15:21:11.616231",
|
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "ERPNext Integrations",
|
"module": "ERPNext Integrations",
|
||||||
"name": "Plaid Settings",
|
"name": "Plaid Settings",
|
||||||
|
@ -2,30 +2,36 @@
|
|||||||
# 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
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
import frappe
|
|
||||||
import json
|
import json
|
||||||
from frappe import _
|
|
||||||
from frappe.model.document import Document
|
import frappe
|
||||||
from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
|
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 erpnext.erpnext_integrations.doctype.plaid_settings.plaid_connector import PlaidConnector
|
||||||
from frappe.utils import getdate, formatdate, today, add_months
|
from frappe import _
|
||||||
from frappe.desk.doctype.tag.tag import add_tag
|
from frappe.desk.doctype.tag.tag import add_tag
|
||||||
|
from frappe.model.document import Document
|
||||||
|
from frappe.utils import add_months, formatdate, getdate, today
|
||||||
|
|
||||||
|
|
||||||
class PlaidSettings(Document):
|
class PlaidSettings(Document):
|
||||||
pass
|
@staticmethod
|
||||||
|
def get_link_token():
|
||||||
|
plaid = PlaidConnector()
|
||||||
|
return plaid.get_link_token()
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def plaid_configuration():
|
def get_plaid_configuration():
|
||||||
if frappe.db.get_single_value("Plaid Settings", "enabled"):
|
if frappe.db.get_single_value("Plaid Settings", "enabled"):
|
||||||
plaid_settings = frappe.get_single("Plaid Settings")
|
plaid_settings = frappe.get_single("Plaid Settings")
|
||||||
return {
|
return {
|
||||||
"plaid_public_key": plaid_settings.plaid_public_key,
|
|
||||||
"plaid_env": plaid_settings.plaid_env,
|
"plaid_env": plaid_settings.plaid_env,
|
||||||
|
"link_token": plaid_settings.get_link_token(),
|
||||||
"client_name": frappe.local.site
|
"client_name": frappe.local.site
|
||||||
}
|
}
|
||||||
else:
|
|
||||||
return "disabled"
|
return "disabled"
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def add_institution(token, response):
|
def add_institution(token, response):
|
||||||
@ -33,6 +39,7 @@ def add_institution(token, response):
|
|||||||
|
|
||||||
plaid = PlaidConnector()
|
plaid = PlaidConnector()
|
||||||
access_token = plaid.get_access_token(token)
|
access_token = plaid.get_access_token(token)
|
||||||
|
bank = None
|
||||||
|
|
||||||
if not frappe.db.exists("Bank", response["institution"]["name"]):
|
if not frappe.db.exists("Bank", response["institution"]["name"]):
|
||||||
try:
|
try:
|
||||||
@ -44,7 +51,6 @@ def add_institution(token, response):
|
|||||||
bank.insert()
|
bank.insert()
|
||||||
except Exception:
|
except Exception:
|
||||||
frappe.throw(frappe.get_traceback())
|
frappe.throw(frappe.get_traceback())
|
||||||
|
|
||||||
else:
|
else:
|
||||||
bank = frappe.get_doc("Bank", response["institution"]["name"])
|
bank = frappe.get_doc("Bank", response["institution"]["name"])
|
||||||
bank.plaid_access_token = access_token
|
bank.plaid_access_token = access_token
|
||||||
@ -52,6 +58,7 @@ def add_institution(token, response):
|
|||||||
|
|
||||||
return bank
|
return bank
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def add_bank_accounts(response, bank, company):
|
def add_bank_accounts(response, bank, company):
|
||||||
try:
|
try:
|
||||||
@ -92,9 +99,8 @@ def add_bank_accounts(response, bank, company):
|
|||||||
new_account.insert()
|
new_account.insert()
|
||||||
|
|
||||||
result.append(new_account.name)
|
result.append(new_account.name)
|
||||||
|
|
||||||
except frappe.UniqueValidationError:
|
except frappe.UniqueValidationError:
|
||||||
frappe.msgprint(_("Bank account {0} already exists and could not be created again").format(new_account.account_name))
|
frappe.msgprint(_("Bank account {0} already exists and could not be created again").format(account["name"]))
|
||||||
except Exception:
|
except Exception:
|
||||||
frappe.throw(frappe.get_traceback())
|
frappe.throw(frappe.get_traceback())
|
||||||
|
|
||||||
@ -103,6 +109,7 @@ def add_bank_accounts(response, bank, company):
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def add_account_type(account_type):
|
def add_account_type(account_type):
|
||||||
try:
|
try:
|
||||||
frappe.get_doc({
|
frappe.get_doc({
|
||||||
@ -122,10 +129,11 @@ def add_account_subtype(account_subtype):
|
|||||||
except Exception:
|
except Exception:
|
||||||
frappe.throw(frappe.get_traceback())
|
frappe.throw(frappe.get_traceback())
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def sync_transactions(bank, bank_account):
|
def sync_transactions(bank, bank_account):
|
||||||
'''Sync transactions based on the last integration date as the start date, after the sync is completed
|
"""Sync transactions based on the last integration date as the start date, after sync is completed
|
||||||
add the transaction date of the oldest transaction as the last integration date'''
|
add the transaction date of the oldest transaction as the last integration date."""
|
||||||
last_transaction_date = frappe.db.get_value("Bank Account", bank_account, "last_integration_date")
|
last_transaction_date = frappe.db.get_value("Bank Account", bank_account, "last_integration_date")
|
||||||
if last_transaction_date:
|
if last_transaction_date:
|
||||||
start_date = formatdate(last_transaction_date, "YYYY-MM-dd")
|
start_date = formatdate(last_transaction_date, "YYYY-MM-dd")
|
||||||
@ -147,10 +155,10 @@ def sync_transactions(bank, bank_account):
|
|||||||
len(result), bank_account, start_date, end_date))
|
len(result), bank_account, start_date, end_date))
|
||||||
|
|
||||||
frappe.db.set_value("Bank Account", bank_account, "last_integration_date", last_transaction_date)
|
frappe.db.set_value("Bank Account", bank_account, "last_integration_date", last_transaction_date)
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
frappe.log_error(frappe.get_traceback(), _("Plaid transactions sync error"))
|
frappe.log_error(frappe.get_traceback(), _("Plaid transactions sync error"))
|
||||||
|
|
||||||
|
|
||||||
def get_transactions(bank, bank_account=None, start_date=None, end_date=None):
|
def get_transactions(bank, bank_account=None, start_date=None, end_date=None):
|
||||||
access_token = None
|
access_token = None
|
||||||
|
|
||||||
@ -168,6 +176,7 @@ def get_transactions(bank, bank_account=None, start_date=None, end_date=None):
|
|||||||
|
|
||||||
return transactions
|
return transactions
|
||||||
|
|
||||||
|
|
||||||
def new_bank_transaction(transaction):
|
def new_bank_transaction(transaction):
|
||||||
result = []
|
result = []
|
||||||
|
|
||||||
@ -182,8 +191,8 @@ def new_bank_transaction(transaction):
|
|||||||
|
|
||||||
status = "Pending" if transaction["pending"] == "True" else "Settled"
|
status = "Pending" if transaction["pending"] == "True" else "Settled"
|
||||||
|
|
||||||
|
tags = []
|
||||||
try:
|
try:
|
||||||
tags = []
|
|
||||||
tags += transaction["category"]
|
tags += transaction["category"]
|
||||||
tags += ["Plaid Cat. {}".format(transaction["category_id"])]
|
tags += ["Plaid Cat. {}".format(transaction["category_id"])]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@ -216,6 +225,7 @@ def new_bank_transaction(transaction):
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def automatic_synchronization():
|
def automatic_synchronization():
|
||||||
settings = frappe.get_doc("Plaid Settings", "Plaid Settings")
|
settings = frappe.get_doc("Plaid Settings", "Plaid Settings")
|
||||||
|
|
||||||
@ -223,4 +233,8 @@ def automatic_synchronization():
|
|||||||
plaid_accounts = frappe.get_all("Bank Account", filters={"integration_id": ["!=", ""]}, fields=["name", "bank"])
|
plaid_accounts = frappe.get_all("Bank Account", filters={"integration_id": ["!=", ""]}, fields=["name", "bank"])
|
||||||
|
|
||||||
for plaid_account in plaid_accounts:
|
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)
|
frappe.enqueue(
|
||||||
|
"erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.sync_transactions",
|
||||||
|
bank=plaid_account.bank,
|
||||||
|
bank_account=plaid_account.name
|
||||||
|
)
|
||||||
|
@ -1,14 +1,17 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
|
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
# See license.txt
|
# 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
|
import json
|
||||||
from frappe.utils.response import json_handler
|
import unittest
|
||||||
|
|
||||||
|
import frappe
|
||||||
from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
|
from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
|
||||||
|
from erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings import (
|
||||||
|
add_account_subtype, add_account_type, add_bank_accounts,
|
||||||
|
new_bank_transaction, get_plaid_configuration)
|
||||||
|
from frappe.utils.response import json_handler
|
||||||
|
|
||||||
|
|
||||||
class TestPlaidSettings(unittest.TestCase):
|
class TestPlaidSettings(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@ -31,7 +34,7 @@ class TestPlaidSettings(unittest.TestCase):
|
|||||||
|
|
||||||
def test_plaid_disabled(self):
|
def test_plaid_disabled(self):
|
||||||
frappe.db.set_value("Plaid Settings", None, "enabled", 0)
|
frappe.db.set_value("Plaid Settings", None, "enabled", 0)
|
||||||
self.assertTrue(plaid_configuration() == "disabled")
|
self.assertTrue(get_plaid_configuration() == "disabled")
|
||||||
|
|
||||||
def test_add_account_type(self):
|
def test_add_account_type(self):
|
||||||
add_account_type("brokerage")
|
add_account_type("brokerage")
|
||||||
@ -64,7 +67,7 @@ class TestPlaidSettings(unittest.TestCase):
|
|||||||
'mask': '0000',
|
'mask': '0000',
|
||||||
'id': '6GbM6RRQgdfy3lAqGz4JUnpmR948WZFg8DjQK',
|
'id': '6GbM6RRQgdfy3lAqGz4JUnpmR948WZFg8DjQK',
|
||||||
'name': 'Plaid Checking'
|
'name': 'Plaid Checking'
|
||||||
}],
|
}],
|
||||||
'institution': {
|
'institution': {
|
||||||
'institution_id': 'ins_6',
|
'institution_id': 'ins_6',
|
||||||
'name': 'Citi'
|
'name': 'Citi'
|
||||||
@ -100,7 +103,7 @@ class TestPlaidSettings(unittest.TestCase):
|
|||||||
'mask': '0000',
|
'mask': '0000',
|
||||||
'id': '6GbM6RRQgdfy3lAqGz4JUnpmR948WZFg8DjQK',
|
'id': '6GbM6RRQgdfy3lAqGz4JUnpmR948WZFg8DjQK',
|
||||||
'name': 'Plaid Checking'
|
'name': 'Plaid Checking'
|
||||||
}],
|
}],
|
||||||
'institution': {
|
'institution': {
|
||||||
'institution_id': 'ins_6',
|
'institution_id': 'ins_6',
|
||||||
'name': 'Citi'
|
'name': 'Citi'
|
||||||
@ -152,4 +155,4 @@ class TestPlaidSettings(unittest.TestCase):
|
|||||||
|
|
||||||
new_bank_transaction(transactions)
|
new_bank_transaction(transactions)
|
||||||
|
|
||||||
self.assertTrue(len(frappe.get_all("Bank Transaction")) == 1)
|
self.assertTrue(len(frappe.get_all("Bank Transaction")) == 1)
|
||||||
|
@ -4,17 +4,16 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
|
|
||||||
|
|
||||||
def execute():
|
def execute():
|
||||||
frappe.reload_doc("erpnext_integrations", "doctype", "plaid_settings")
|
frappe.reload_doc("erpnext_integrations", "doctype", "plaid_settings")
|
||||||
plaid_settings = frappe.get_single("Plaid Settings")
|
plaid_settings = frappe.get_single("Plaid Settings")
|
||||||
if plaid_settings.enabled:
|
if plaid_settings.enabled:
|
||||||
if not (frappe.conf.plaid_client_id and frappe.conf.plaid_env \
|
if not (frappe.conf.plaid_client_id and frappe.conf.plaid_env and frappe.conf.plaid_secret):
|
||||||
and frappe.conf.plaid_public_key and frappe.conf.plaid_secret):
|
|
||||||
plaid_settings.enabled = 0
|
plaid_settings.enabled = 0
|
||||||
else:
|
else:
|
||||||
plaid_settings.update({
|
plaid_settings.update({
|
||||||
"plaid_client_id": frappe.conf.plaid_client_id,
|
"plaid_client_id": frappe.conf.plaid_client_id,
|
||||||
"plaid_public_key": frappe.conf.plaid_public_key,
|
|
||||||
"plaid_env": frappe.conf.plaid_env,
|
"plaid_env": frappe.conf.plaid_env,
|
||||||
"plaid_secret": frappe.conf.plaid_secret
|
"plaid_secret": frappe.conf.plaid_secret
|
||||||
})
|
})
|
||||||
|
@ -3,7 +3,7 @@ frappe
|
|||||||
gocardless-pro==1.11.0
|
gocardless-pro==1.11.0
|
||||||
googlemaps==3.1.1
|
googlemaps==3.1.1
|
||||||
pandas==1.0.5
|
pandas==1.0.5
|
||||||
plaid-python==3.4.0
|
plaid-python==6.0.0
|
||||||
pycountry==19.8.18
|
pycountry==19.8.18
|
||||||
PyGithub==1.44.1
|
PyGithub==1.44.1
|
||||||
python-stdnum==1.12
|
python-stdnum==1.12
|
||||||
|
Loading…
x
Reference in New Issue
Block a user