Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: ebics client #103

Merged
merged 46 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
2709014
feat: first draft
barredterra Sep 6, 2024
fa2fb99
Merge branch 'version-15' into ebics
barredterra Oct 14, 2024
b0cd3f5
fix(Ebics User): remove unneeded field License Key
barredterra Oct 16, 2024
f43a6f6
feat(Ebics User): register with backend
barredterra Oct 16, 2024
c9f49b9
feat: add EBICS User to workspace
barredterra Oct 16, 2024
9e46531
fix(Ebics User): list config
barredterra Oct 16, 2024
f5bc915
feat: enable sending keys to bank
barredterra Oct 16, 2024
1eac992
fix: remove users param from fintech.register call
barredterra Oct 17, 2024
9fcab36
refactor: separate method for getting an iniatilized EBICSManager
barredterra Oct 17, 2024
d8c247f
feat: download and validate the bank's keys
barredterra Oct 17, 2024
93e808c
style: format file
barredterra Oct 17, 2024
01c562e
feat: download bank statements
barredterra Oct 17, 2024
8655677
style: bulk format files
barredterra Oct 17, 2024
2cc8196
fix(EBICS USER): prompt title
barredterra Oct 17, 2024
158c781
fix: move storage location of keyring
barredterra Oct 17, 2024
6c901a4
fix: skip UniqueValidationErrors
barredterra Oct 17, 2024
090ddc2
refactor: use objectified XML syntax to get TxId
barredterra Oct 17, 2024
f15c7a5
refactor(EBICS User): verify, not validate (wording)
barredterra Oct 17, 2024
bbabcce
chore(DX): add stubs and type hints for fintech
barredterra Oct 21, 2024
c58008f
fix: error handling for ebics user registration
barredterra Nov 7, 2024
b7f5baf
refactor(Ebics User): store sig_passphrase and keyring
barredterra Nov 7, 2024
a4d7acc
refactor(Banking Settings): rearrange form
barredterra Nov 7, 2024
67f7be0
feat: show used/available Ebics Users in subscription details
barredterra Nov 7, 2024
af45a4f
feat(Banking Settings): show/hide buttons for kosma or ebics
barredterra Nov 7, 2024
30ad608
feat: daily ebics sync via hooks
barredterra Nov 7, 2024
da589b3
fix: add patch to set default provider
barredterra Nov 8, 2024
3e2615d
fix(Ebics User): set no-copy on relevant fields
barredterra Nov 8, 2024
9926047
fix(Ebics User): more granular handling of registration errors
barredterra Nov 8, 2024
ec79caf
fix(Ebics User): store passphrase, not sig_passphrase
barredterra Nov 8, 2024
cd9aa1d
feat(Ebis User): set headline after initialization
barredterra Nov 8, 2024
62d669b
Merge branch 'version-15' into ebics
marination Nov 11, 2024
4fd03d1
fix(Ebics User): hide unnecessary actions when not in developer mode
barredterra Nov 12, 2024
4393a90
fix: allow Kosma and ebics to be active at the same time
barredterra Nov 12, 2024
cc868b2
feat(EBICS Users): add unique constraint
barredterra Nov 12, 2024
e775810
fix: clarify error message
barredterra Nov 12, 2024
b6a3139
feat(EBICS User): make storing the passphrase optional
barredterra Nov 12, 2024
4b850b5
feat(Banking Settings): fetch fintech license via API
barredterra Nov 12, 2024
04d9a99
fix: handle general errors
barredterra Nov 12, 2024
0cc41f3
docs: codesign with python3.10
barredterra Nov 12, 2024
d3852ab
docs: supported countries
barredterra Nov 12, 2024
5743c55
docs: "ready for ebics" logo
barredterra Nov 12, 2024
901e438
style: run pre-commit
barredterra Nov 13, 2024
4f717fc
feat: enqueue ebics sync
barredterra Nov 13, 2024
454abad
feat(EBICS User): allow setting a start date
barredterra Nov 13, 2024
2e1f356
feat: add german translations
barredterra Nov 13, 2024
f4e9c7e
feat: add french translations
barredterra Nov 13, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 13 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,34 +20,14 @@ Check out the [Banking Wiki](https://github.com/alyf-de/banking/wiki) for a step

## Country and Bank Coverage

Currently, we [support more than 15.000 banks from the following countries](https://portal.openbanking.klarna.com/bank-matrix).
<img src="ready_for_ebics.jpg" height="70px">

We use the EBICS protocol which is widely supported by banks in the following countries:

- 🇦🇹 Austria
- 🇧🇪 Belgium
- 🇭🇷 Croatia
- 🇨🇿 Czech Republic
- 🇩🇰 Denmark
- 🇪🇪 Estonia
- 🇫🇮 Finland
- 🇫🇷 France
- 🇩🇪 Germany
- 🇭🇺 Hungary
- 🇮🇪 Ireland
- 🇮🇹 Italy
- 🇱🇻 Latvia
- 🇱🇹 Lithuania
- 🇱🇺 Luxembourg
- 🇲🇹 Malta
- 🇳🇱 Netherlands
- 🇳🇴 Norway
- 🇵🇱 Poland
- 🇵🇹 Portugal
- 🇷🇴 Romania
- 🇸🇰 Slovakia
- 🇪🇸 Spain
- 🇸🇪 Sweden
- 🇨🇭 Switzerland
- 🇬🇧 United Kingdom

## Installation

Expand All @@ -57,3 +37,13 @@ Install [via Frappe Cloud](https://frappecloud.com/marketplace/apps/banking) or
bench get-app https://github.com/alyf-de/banking.git
bench --site <sitename> install-app banking
```

If you want to use ebics on Apple Silicon, the runtime library must be signed manually:

```bash
# python3.11
sudo codesign --force --deep --sign - env/lib/python3.11/site-packages/fintech/runtime/darwin/aarch64/pyarmor_runtime.so

# python3.10
sudo codesign --force --deep --sign - env/lib/python3.10/site-packages/fintech/pytransform/platforms/darwin/aarch64/_pytransform.dylib
```
26 changes: 26 additions & 0 deletions banking/connectors/admin_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,3 +136,29 @@ def fetch_subscription(self):
def get_customer_portal(self):
method = "banking_admin.api.get_customer_portal"
return requests.get(url=self.url + method)

def get_fintech_license(self):
method = "banking_admin.ebics_api.get_fintech_license"
return requests.post(
url=self.url + method, headers=self.headers, json=self.data.copy()
)

def register_ebics_user(self, host_id: str, partner_id: str, user_id: str):
data = self.data
data.update({"host_id": host_id, "partner_id": partner_id, "user_id": user_id})
method = "banking_admin.ebics_api.register_ebics_user"
return requests.post(
url=self.url + method,
headers=self.headers,
json=data,
)

def remove_ebics_user(self, host_id: str, partner_id: str, user_id: str):
data = self.data
data.update({"host_id": host_id, "partner_id": partner_id, "user_id": user_id})
method = "banking_admin.ebics_api.remove_ebics_user"
return requests.post(
url=self.url + method,
headers=self.headers,
json=data,
)
Empty file added banking/ebics/__init__.py
Empty file.
Empty file.
Empty file.
177 changes: 177 additions & 0 deletions banking/ebics/doctype/ebics_user/ebics_user.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
// Copyright (c) 2024, ALYF GmbH and contributors
// For license information, please see license.txt

frappe.ui.form.on("EBICS User", {
refresh(frm) {
if (frm.doc.initialized && !frm.doc.bank_keys_activated) {
frm.dashboard.set_headline(
__("Please print the attached INI letter, send it to your bank and wait for confirmation. Then verify the bank keys.")
);
}

if (!frm.doc.initialized || frappe.boot.developer_mode) {
frm.add_custom_button(
__("Initialize"),
() => {
frappe.prompt(
[
{
fieldname: "passphrase",
label: __("Passphrase"),
fieldtype: "Password",
description: __("Set a new password for downloading bank statements from your bank.")
},
{
fieldname: "store_passphrase",
label: __("Store Passphrase"),
fieldtype: "Check",
default: 1,
description: __("Store the passphrase in the ERPNext database to enable automated, regular download of bank statements.")
},
{
fieldname: "signature_passphrase",
label: __("Signature Passphrase"),
fieldtype: "Password",
description: __("Set a new password for uploading transactions to your bank.")
},
{
fieldname: "info",
fieldtype: "HTML",
options: __(
"Note: When you lose these passwords, you will have to go through the initialization process with your bank again."
)
}
],
(values) => {
frappe.call({
method: "banking.ebics.doctype.ebics_user.ebics_user.initialize",
args: { ebics_user: frm.doc.name, ...values },
freeze: true,
freeze_message: __("Initializing..."),
callback: () => frm.reload_doc(),
});
},
__("Initialize EBICS User")
);
},
frm.doc.initialized ? __("Actions") : null
);
}

if (frm.doc.initialized && (!frm.doc.bank_keys_activated || frappe.boot.developer_mode)) {
frm.add_custom_button(
__("Verify Bank Keys"),
async () => {
bank_keys = await get_bank_keys(frm.doc.name);
if (!bank_keys) {
return;
}

message = __(
"Please confirm that the following keys are identical to the ones mentioned on your bank's letter:"
);
frappe.confirm(
`<p>${message}</p>
<pre>${bank_keys}</pre>`,
async () => {
await confirm_bank_keys(frm.doc.name);
frm.reload_doc();
}
);
},
frm.doc.bank_keys_activated ? __("Actions") : null
);
}

if (frm.doc.initialized && frm.doc.bank_keys_activated) {
frm.add_custom_button(__("Download Bank Statements"), () => {
download_bank_statements(frm.doc.name, !frm.doc.passphrase);
});
}
},
});

async function get_bank_keys(ebics_user) {
try {
return await frappe.xcall(
"banking.ebics.doctype.ebics_user.ebics_user.download_bank_keys",
{ ebics_user: ebics_user }
);
} catch (e) {
frappe.show_alert({
message: e || __("An error occurred"),
indicator: "red",
});
}
}

async function confirm_bank_keys(ebics_user) {
try {
await frappe.xcall(
"banking.ebics.doctype.ebics_user.ebics_user.confirm_bank_keys",
{ ebics_user: ebics_user }
);
frappe.show_alert({
message: __("Bank keys confirmed"),
indicator: "green",
});
} catch (e) {
frappe.show_alert({
message: e || __("An error occurred"),
indicator: "red",
});
}
}

function download_bank_statements(ebics_user, needs_passphrase) {
const fields = [
{
fieldname: "from_date",
label: __("From Date"),
fieldtype: "Date",
default: frappe.datetime.now_date(),
},
{
fieldname: "to_date",
label: __("To Date"),
fieldtype: "Date",
default: frappe.datetime.now_date(),
},
];

if (needs_passphrase) {
fields.push({
fieldname: "passphrase",
label: __("Passphrase"),
fieldtype: "Password",
reqd: true
});
}

frappe.prompt(
fields,
async (values) => {
try {
await frappe.xcall(
"banking.ebics.doctype.ebics_user.ebics_user.download_bank_statements",
{
ebics_user: ebics_user,
from_date: values.from_date,
to_date: values.to_date,
passphrase: values.passphrase,
}
);
frappe.show_alert({
message: __("Bank statements are being downloaded in the background."),
indicator: "blue",
});
} catch (e) {
frappe.show_alert({
message: e || __("An error occurred"),
indicator: "red",
});
}
},
__("Download Bank Statements")
);
}
Loading
Loading