From 9851d00f4e370282db7ae5c77b687b0415187fe6 Mon Sep 17 00:00:00 2001 From: Fabian Frey Date: Mon, 11 Nov 2024 10:20:25 +0100 Subject: [PATCH 1/8] feat: adds boilerplate code for dkb bank support --- src/app-gocardless/banks/dkb_byladem1.js | 22 +++++++++++++++++++ .../banks/tests/dkb_byladem1.js | 15 +++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 src/app-gocardless/banks/dkb_byladem1.js create mode 100644 src/app-gocardless/banks/tests/dkb_byladem1.js diff --git a/src/app-gocardless/banks/dkb_byladem1.js b/src/app-gocardless/banks/dkb_byladem1.js new file mode 100644 index 000000000..c261ffc8d --- /dev/null +++ b/src/app-gocardless/banks/dkb_byladem1.js @@ -0,0 +1,22 @@ +import Fallback from './integration-bank.js'; + +/** @type {import('./bank.interface.js').IBank} */ +export default { + ...Fallback, + + institutionIds: ['DKB_BYLADEM1'], + + accessValidForDays: 180, + + sortTransactions: (transactions = []) => { + + }, + + normalizeTransaction: (transaction, _booked) => { + + }, + + normalizeAccount: (account) => { + + } +} diff --git a/src/app-gocardless/banks/tests/dkb_byladem1.js b/src/app-gocardless/banks/tests/dkb_byladem1.js new file mode 100644 index 000000000..fa3926fcd --- /dev/null +++ b/src/app-gocardless/banks/tests/dkb_byladem1.js @@ -0,0 +1,15 @@ +describe('dkb_byladem1', function() { + describe('#normalizeAccount', function() { + it('should normalize the account information', function() { + + }) + }) + + describe('#normalizeTransaction', function() { + + }) + + describe('#sortTransactions', function() { + + }) +}) From 6f29b3d00c9cbf0f843faca598323934cb619584 Mon Sep 17 00:00:00 2001 From: Fabian Frey Date: Mon, 11 Nov 2024 10:23:34 +0100 Subject: [PATCH 2/8] feat: adds dkb bank to factory --- src/app-gocardless/bank-factory.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/app-gocardless/bank-factory.js b/src/app-gocardless/bank-factory.js index a38afa3bc..0ea3752d8 100644 --- a/src/app-gocardless/bank-factory.js +++ b/src/app-gocardless/bank-factory.js @@ -28,6 +28,7 @@ import SpkMarburgBiedenkopfHeladef1mar from './banks/spk-marburg-biedenkopf-hela import SpkWormsAlzeyRiedMalade51wor from './banks/spk-worms-alzey-ried-malade51wor.js'; import SwedbankHabaLV22 from './banks/swedbank-habalv22.js'; import VirginNrnbgb22 from './banks/virgin_nrnbgb22.js'; +import Dkb_byladem1 from './banks/dkb_byladem1.js'; export const banks = [ AbancaCaglesmm, @@ -59,6 +60,7 @@ export const banks = [ SpkWormsAlzeyRiedMalade51wor, SwedbankHabaLV22, VirginNrnbgb22, + Dkb_byladem1 ]; export default (institutionId) => From ee79a9c42e1234cbf16d397fec595a3d312621f8 Mon Sep 17 00:00:00 2001 From: Fabian Frey Date: Mon, 11 Nov 2024 10:28:58 +0100 Subject: [PATCH 3/8] feat: adds release note --- upcoming-release-notes/500.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 upcoming-release-notes/500.md diff --git a/upcoming-release-notes/500.md b/upcoming-release-notes/500.md new file mode 100644 index 000000000..72869fac8 --- /dev/null +++ b/upcoming-release-notes/500.md @@ -0,0 +1,6 @@ +--- +category: Features +authors: [mrsteakhouse] +--- + +Implements support for sync with DKB bank accounts. From 6bbbab6d36d1bb993076366bac7e4fe1b7ca9a3a Mon Sep 17 00:00:00 2001 From: Fabian Frey Date: Mon, 11 Nov 2024 17:55:48 +0100 Subject: [PATCH 4/8] feat: implement necessary functions --- src/app-gocardless/banks/dkb_byladem1.js | 42 +++++-- .../banks/tests/dkb_byladem1.js | 15 --- .../banks/tests/dkb_byladem1.spec.js | 116 ++++++++++++++++++ 3 files changed, 149 insertions(+), 24 deletions(-) delete mode 100644 src/app-gocardless/banks/tests/dkb_byladem1.js create mode 100644 src/app-gocardless/banks/tests/dkb_byladem1.spec.js diff --git a/src/app-gocardless/banks/dkb_byladem1.js b/src/app-gocardless/banks/dkb_byladem1.js index c261ffc8d..528fe7e70 100644 --- a/src/app-gocardless/banks/dkb_byladem1.js +++ b/src/app-gocardless/banks/dkb_byladem1.js @@ -1,22 +1,46 @@ import Fallback from './integration-bank.js'; +import { printIban } from '../utils.js'; /** @type {import('./bank.interface.js').IBank} */ export default { ...Fallback, - institutionIds: ['DKB_BYLADEM1'], - accessValidForDays: 180, - sortTransactions: (transactions = []) => { + institutionIds: ['DKB_BYLADEM1'], + normalizeAccount(account) { + return { + account_id: account.id, + institution: account.institution, + mask: account.iban.slice(-4), + iban: account.iban, + name: [account.product, printIban(account)].join(' '), + official_name: account.product, + type: 'checking', + }; }, - normalizeTransaction: (transaction, _booked) => { - + sortTransactions(transactions = []) { + return transactions.sort((a, b) => { + const dateA = transactionIdToDate(a.transactionId); + const dateB = transactionIdToDate(b.transactionId); + return +dateB - +dateA; + }); }, - - normalizeAccount: (account) => { - - } +}; + +/** + * Takes a transactionId string and converts it into a `Date`. + * + * @param {string} transactionId The transactionId in the format `YYYY-MM-DD-HH.MM.SS.NNNNNN`. + * @returns {Date} The `Date` representation of the given `transactionId` + */ +function transactionIdToDate(transactionId) { + const datePart = /\d{4}-\d{2}-\d{2}/.exec(transactionId)[0]; + const timePart = /\d{2}\.\d{2}\.\d{2}/ + .exec(transactionId)[0] + .replaceAll('.', ':'); + const dateTimeString = [datePart, timePart].join(' '); + return new Date(dateTimeString); } diff --git a/src/app-gocardless/banks/tests/dkb_byladem1.js b/src/app-gocardless/banks/tests/dkb_byladem1.js deleted file mode 100644 index fa3926fcd..000000000 --- a/src/app-gocardless/banks/tests/dkb_byladem1.js +++ /dev/null @@ -1,15 +0,0 @@ -describe('dkb_byladem1', function() { - describe('#normalizeAccount', function() { - it('should normalize the account information', function() { - - }) - }) - - describe('#normalizeTransaction', function() { - - }) - - describe('#sortTransactions', function() { - - }) -}) diff --git a/src/app-gocardless/banks/tests/dkb_byladem1.spec.js b/src/app-gocardless/banks/tests/dkb_byladem1.spec.js new file mode 100644 index 000000000..e66f72831 --- /dev/null +++ b/src/app-gocardless/banks/tests/dkb_byladem1.spec.js @@ -0,0 +1,116 @@ +import Dkb_byladem1 from '../dkb_byladem1.js'; +import { mockTransactionAmount, mockTransactions } from '../../services/tests/fixtures.js'; + +describe('dkb_byladem1', function () { + describe('#normalizeAccount', function () { + /** @type {import('../../gocardless.types.js').DetailedAccountWithInstitution} */ + const accountRaw = { + resourceId: 'd1c61254-242e-4a88-aaff-8490846e2491', + iban: 'DE02500105170137075030', + currency: 'EUR', + ownerName: 'Jane Doe', + product: 'Girokonto', + id: 'a787ba27-02ee-4fd6-be86-73831adc5498', + created: '2023-09-04T14:38:58.841381Z', + last_accessed: '2024-11-08T08:48:39.667722Z', + institution_id: 'DKB_BYLADEM1', + status: 'READY', + owner_name: 'Jane Doe', + cashAccountType: 'CACC', + usage: 'PRIV', + institution: { + id: 'DKB_BYLADEM1', + name: 'Deutsche Kreditbank AG (DKB)', + bic: 'BALLADE1001', + transaction_total_days: '365', + countries: ['DE'], + logo: 'https://storage.googleapis.com/gc-prd-institution_icons-production/DE/PNG/deutschekreditbank.png', + supported_payments: { + 'single-payment': ['SCT', 'ISCSI'], + }, + supported_features: [ + 'business_accounts', + 'corporate_accounts', + 'payments', + 'pending_transactions', + 'private_accounts', + ], + }, + }; + + it('should normalize the account information', function () { + const actual = Dkb_byladem1.normalizeAccount(accountRaw); + expect(actual).toEqual({ + account_id: 'a787ba27-02ee-4fd6-be86-73831adc5498', + iban: 'DE02500105170137075030', + institution: { + id: 'DKB_BYLADEM1', + name: 'Deutsche Kreditbank AG (DKB)', + bic: 'BALLADE1001', + transaction_total_days: '365', + countries: ['DE'], + logo: 'https://storage.googleapis.com/gc-prd-institution_icons-production/DE/PNG/deutschekreditbank.png', + supported_payments: { + 'single-payment': ['SCT', 'ISCSI'], + }, + supported_features: [ + 'business_accounts', + 'corporate_accounts', + 'payments', + 'pending_transactions', + 'private_accounts', + ], + }, + mask: '5030', + name: 'Girokonto (XXX 5030)', + official_name: 'Girokonto', + type: 'checking', + }); + }); + }); + + describe('#sortTransactions', function () { + const transactionsRaw = [ + { + transactionId: '2024-11-05-00.25.38.982769', + transactionAmount: mockTransactionAmount, + }, + { + transactionId: '2024-11-05-01.25.38.982769', + transactionAmount: mockTransactionAmount, + }, + { + transactionId: '2024-11-04-01.26.38.982769', + transactionAmount: mockTransactionAmount, + }, + { + transactionId: '2024-11-05-01.26.38.982769', + transactionAmount: mockTransactionAmount, + }, + ]; + + const expected = [ + { + transactionId: '2024-11-05-01.26.38.982769', + transactionAmount: mockTransactionAmount, + }, + { + transactionId: '2024-11-05-01.25.38.982769', + transactionAmount: mockTransactionAmount, + }, + { + transactionId: '2024-11-05-00.25.38.982769', + transactionAmount: mockTransactionAmount, + }, + { + transactionId: '2024-11-04-01.26.38.982769', + transactionAmount: mockTransactionAmount, + }, + ]; + + it('should sort by transaction id', function () { + const actual = Dkb_byladem1.sortTransactions(transactionsRaw); + expect(actual).toEqual(expected); + }); + }); +}); From a4b3f0a156411130e3fc1f8df6f8d9242d1d2570 Mon Sep 17 00:00:00 2001 From: Fabian Frey Date: Mon, 11 Nov 2024 17:56:17 +0100 Subject: [PATCH 5/8] fix: remove unused import --- src/app-gocardless/banks/tests/dkb_byladem1.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app-gocardless/banks/tests/dkb_byladem1.spec.js b/src/app-gocardless/banks/tests/dkb_byladem1.spec.js index e66f72831..110cd8942 100644 --- a/src/app-gocardless/banks/tests/dkb_byladem1.spec.js +++ b/src/app-gocardless/banks/tests/dkb_byladem1.spec.js @@ -1,5 +1,5 @@ import Dkb_byladem1 from '../dkb_byladem1.js'; -import { mockTransactionAmount, mockTransactions } from '../../services/tests/fixtures.js'; +import { mockTransactionAmount } from '../../services/tests/fixtures.js'; describe('dkb_byladem1', function () { describe('#normalizeAccount', function () { From 6e6f942c8fc113fa74d506524d915dc99385b050 Mon Sep 17 00:00:00 2001 From: Fabian Frey Date: Mon, 11 Nov 2024 17:57:33 +0100 Subject: [PATCH 6/8] fix: adds comma to end of line --- src/app-gocardless/bank-factory.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app-gocardless/bank-factory.js b/src/app-gocardless/bank-factory.js index 0ea3752d8..fa09f76fd 100644 --- a/src/app-gocardless/bank-factory.js +++ b/src/app-gocardless/bank-factory.js @@ -60,7 +60,7 @@ export const banks = [ SpkWormsAlzeyRiedMalade51wor, SwedbankHabaLV22, VirginNrnbgb22, - Dkb_byladem1 + Dkb_byladem1, ]; export default (institutionId) => From 4de2b0fa09ccd5a835b50c01413dc494228e9f4d Mon Sep 17 00:00:00 2001 From: Fabian Frey Date: Tue, 12 Nov 2024 15:26:21 +0100 Subject: [PATCH 7/8] feat: refactor tests --- .../banks/tests/dkb_byladem1.spec.js | 153 +++++++++--------- 1 file changed, 79 insertions(+), 74 deletions(-) diff --git a/src/app-gocardless/banks/tests/dkb_byladem1.spec.js b/src/app-gocardless/banks/tests/dkb_byladem1.spec.js index 110cd8942..73449a311 100644 --- a/src/app-gocardless/banks/tests/dkb_byladem1.spec.js +++ b/src/app-gocardless/banks/tests/dkb_byladem1.spec.js @@ -3,44 +3,44 @@ import { mockTransactionAmount } from '../../services/tests/fixtures.js'; describe('dkb_byladem1', function () { describe('#normalizeAccount', function () { - /** @type {import('../../gocardless.types.js').DetailedAccountWithInstitution} */ - const accountRaw = { - resourceId: 'd1c61254-242e-4a88-aaff-8490846e2491', - iban: 'DE02500105170137075030', - currency: 'EUR', - ownerName: 'Jane Doe', - product: 'Girokonto', - id: 'a787ba27-02ee-4fd6-be86-73831adc5498', - created: '2023-09-04T14:38:58.841381Z', - last_accessed: '2024-11-08T08:48:39.667722Z', - institution_id: 'DKB_BYLADEM1', - status: 'READY', - owner_name: 'Jane Doe', - cashAccountType: 'CACC', - usage: 'PRIV', - institution: { - id: 'DKB_BYLADEM1', - name: 'Deutsche Kreditbank AG (DKB)', - bic: 'BALLADE1001', - transaction_total_days: '365', - countries: ['DE'], - logo: 'https://storage.googleapis.com/gc-prd-institution_icons-production/DE/PNG/deutschekreditbank.png', - supported_payments: { - 'single-payment': ['SCT', 'ISCSI'], + it('should normalize the account information', function () { + /** @type {import('../../gocardless.types.js').DetailedAccountWithInstitution} */ + const accountRaw = { + resourceId: 'd1c61254-242e-4a88-aaff-8490846e2491', + iban: 'DE02500105170137075030', + currency: 'EUR', + ownerName: 'Jane Doe', + product: 'Girokonto', + id: 'a787ba27-02ee-4fd6-be86-73831adc5498', + created: '2023-09-04T14:38:58.841381Z', + last_accessed: '2024-11-08T08:48:39.667722Z', + institution_id: 'DKB_BYLADEM1', + status: 'READY', + owner_name: 'Jane Doe', + cashAccountType: 'CACC', + usage: 'PRIV', + institution: { + id: 'DKB_BYLADEM1', + name: 'Deutsche Kreditbank AG (DKB)', + bic: 'BALLADE1001', + transaction_total_days: '365', + countries: ['DE'], + logo: 'https://storage.googleapis.com/gc-prd-institution_icons-production/DE/PNG/deutschekreditbank.png', + supported_payments: { + 'single-payment': ['SCT', 'ISCSI'], + }, + supported_features: [ + 'business_accounts', + 'corporate_accounts', + 'payments', + 'pending_transactions', + 'private_accounts', + ], }, - supported_features: [ - 'business_accounts', - 'corporate_accounts', - 'payments', - 'pending_transactions', - 'private_accounts', - ], - }, - }; + }; - it('should normalize the account information', function () { - const actual = Dkb_byladem1.normalizeAccount(accountRaw); - expect(actual).toEqual({ + /** @type {import('../../gocardless.types.js').NormalizedAccountDetails} */ + const expected = { account_id: 'a787ba27-02ee-4fd6-be86-73831adc5498', iban: 'DE02500105170137075030', institution: { @@ -65,50 +65,55 @@ describe('dkb_byladem1', function () { name: 'Girokonto (XXX 5030)', official_name: 'Girokonto', type: 'checking', - }); + }; + + const actual = Dkb_byladem1.normalizeAccount(accountRaw); + expect(actual).toEqual(expected); }); }); describe('#sortTransactions', function () { - const transactionsRaw = [ - { - transactionId: '2024-11-05-00.25.38.982769', - transactionAmount: mockTransactionAmount, - }, - { - transactionId: '2024-11-05-01.25.38.982769', - transactionAmount: mockTransactionAmount, - }, - { - transactionId: '2024-11-04-01.26.38.982769', - transactionAmount: mockTransactionAmount, - }, - { - transactionId: '2024-11-05-01.26.38.982769', - transactionAmount: mockTransactionAmount, - }, - ]; + it('should sort by transaction id', function () { + /** @type {import('../../gocardless-node.types.js').Transaction[]} */ + const transactionsRaw = [ + { + transactionId: '2024-11-05-00.25.38.982769Z', + transactionAmount: mockTransactionAmount, + }, + { + transactionId: '2024-11-05-01.25.38.982769Z', + transactionAmount: mockTransactionAmount, + }, + { + transactionId: '2024-11-04-01.26.38.982769Z', + transactionAmount: mockTransactionAmount, + }, + { + transactionId: '2024-11-05-01.26.38.982769Z', + transactionAmount: mockTransactionAmount, + }, + ]; - const expected = [ - { - transactionId: '2024-11-05-01.26.38.982769', - transactionAmount: mockTransactionAmount, - }, - { - transactionId: '2024-11-05-01.25.38.982769', - transactionAmount: mockTransactionAmount, - }, - { - transactionId: '2024-11-05-00.25.38.982769', - transactionAmount: mockTransactionAmount, - }, - { - transactionId: '2024-11-04-01.26.38.982769', - transactionAmount: mockTransactionAmount, - }, - ]; + /** @type {import('../../gocardless-node.types.js').Transaction[]} */ + const expected = [ + { + transactionId: '2024-11-04-01.26.38.982769Z', + transactionAmount: mockTransactionAmount, + }, + { + transactionId: '2024-11-05-00.25.38.982769Z', + transactionAmount: mockTransactionAmount, + }, + { + transactionId: '2024-11-05-01.25.38.982769Z', + transactionAmount: mockTransactionAmount, + }, + { + transactionId: '2024-11-05-01.26.38.982769Z', + transactionAmount: mockTransactionAmount, + }, + ]; - it('should sort by transaction id', function () { const actual = Dkb_byladem1.sortTransactions(transactionsRaw); expect(actual).toEqual(expected); }); From 7d1811ebf6c2c2a9e651641eebfe98897cb77913 Mon Sep 17 00:00:00 2001 From: Fabian Frey Date: Tue, 12 Nov 2024 15:26:40 +0100 Subject: [PATCH 8/8] feat: use date-fns to parse the transaction date inside transactionId --- src/app-gocardless/banks/dkb_byladem1.js | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/src/app-gocardless/banks/dkb_byladem1.js b/src/app-gocardless/banks/dkb_byladem1.js index 528fe7e70..e4bf71c40 100644 --- a/src/app-gocardless/banks/dkb_byladem1.js +++ b/src/app-gocardless/banks/dkb_byladem1.js @@ -1,5 +1,6 @@ import Fallback from './integration-bank.js'; import { printIban } from '../utils.js'; +import * as d from 'date-fns'; /** @type {import('./bank.interface.js').IBank} */ export default { @@ -22,25 +23,11 @@ export default { }, sortTransactions(transactions = []) { + const dateFormat = "yyyy-MM-dd-HH.mm.ss.SSSSSS'Z'"; return transactions.sort((a, b) => { - const dateA = transactionIdToDate(a.transactionId); - const dateB = transactionIdToDate(b.transactionId); - return +dateB - +dateA; + const dateA = d.parse(a.transactionId, dateFormat, new Date()); + const dateB = d.parse(b.transactionId, dateFormat, new Date()); + return d.compareAsc(dateA, dateB); }); }, }; - -/** - * Takes a transactionId string and converts it into a `Date`. - * - * @param {string} transactionId The transactionId in the format `YYYY-MM-DD-HH.MM.SS.NNNNNN`. - * @returns {Date} The `Date` representation of the given `transactionId` - */ -function transactionIdToDate(transactionId) { - const datePart = /\d{4}-\d{2}-\d{2}/.exec(transactionId)[0]; - const timePart = /\d{2}\.\d{2}\.\d{2}/ - .exec(transactionId)[0] - .replaceAll('.', ':'); - const dateTimeString = [datePart, timePart].join(' '); - return new Date(dateTimeString); -}