From 785a7fe9913767a403bd90945504eff7082bf035 Mon Sep 17 00:00:00 2001 From: nsulzer Date: Sun, 1 Dec 2024 17:37:18 +0100 Subject: [PATCH] Add GoCardless integration for ABNAMRO_ABNANL2A --- src/app-gocardless/bank-factory.js | 2 + src/app-gocardless/banks/abnamro_abnanl2a.js | 79 +++++++++++++++++++ .../banks/tests/abnamro_abnanl2a.spec.js | 33 ++++++++ upcoming-release-notes/513.md | 6 ++ 4 files changed, 120 insertions(+) create mode 100644 src/app-gocardless/banks/abnamro_abnanl2a.js create mode 100644 src/app-gocardless/banks/tests/abnamro_abnanl2a.spec.js create mode 100644 upcoming-release-notes/513.md diff --git a/src/app-gocardless/bank-factory.js b/src/app-gocardless/bank-factory.js index c368b94f..a2c88eaa 100644 --- a/src/app-gocardless/bank-factory.js +++ b/src/app-gocardless/bank-factory.js @@ -1,4 +1,5 @@ import AbancaCaglesmm from './banks/abanca-caglesmm.js'; +import AbnamroAbnanl2a from './banks/abnamro_abnanl2a.js'; import AmericanExpressAesudef1 from './banks/american-express-aesudef1.js'; import BancsabadellBsabesbb from './banks/bancsabadell-bsabesbbb.js'; import BankinterBkbkesmm from './banks/bankinter-bkbkesmm.js'; @@ -31,6 +32,7 @@ import VirginNrnbgb22 from './banks/virgin_nrnbgb22.js'; export const banks = [ AbancaCaglesmm, + AbnamroAbnanl2a, AmericanExpressAesudef1, BancsabadellBsabesbb, BankinterBkbkesmm, diff --git a/src/app-gocardless/banks/abnamro_abnanl2a.js b/src/app-gocardless/banks/abnamro_abnanl2a.js new file mode 100644 index 00000000..1b6bd8fd --- /dev/null +++ b/src/app-gocardless/banks/abnamro_abnanl2a.js @@ -0,0 +1,79 @@ +import Fallback from './integration-bank.js'; +import { printIban, amountToInteger } from '../utils.js'; +import { formatPayeeName } from '../../util/payee-name.js'; + +/** @type {import('./bank.interface.js').IBank} */ +export default { + ...Fallback, + + institutionIds: ['ABNAMRO_ABNANL2A'], + + accessValidForDays: 180, + + normalizeAccount(account) { + return { + account_id: account.id, + institution: account.institution, + mask: account.iban.slice(-4), + iban: account.iban, + name: [account.name, printIban(account)].join(' '), + official_name: account.product, + type: 'checking', + }; + }, + + normalizeTransaction(transaction, _booked) { + // There is no remittanceInformationUnstructured, so we'll make it + let remittanceInformationUnstructured = + transaction.remittanceInformationUnstructuredArray.join(' '); + + // Remove clutter to extract the payee from remittanceInformationUnstructured ... + // ... when not otherwise provided. + const matches = + remittanceInformationUnstructured.match(/Betaalpas(.+),PAS/); + const payeeName = matches + ? matches[1].replace(/.+\*/, '').trim() + : undefined; + transaction.debtorName = transaction.debtorName || payeeName; + transaction.creditorName = transaction.creditorName || payeeName; + + // There are anumber of superfluous keywords in the remittanceInformation. + // Remove them to aboid clutter in notes. + const keywordsToRemove = ['.EA, Betaalpas', ',PAS\\d{3}', 'NR:.+, ']; + const regex = new RegExp(keywordsToRemove.join('|'), 'g'); + transaction.remittanceInformationUnstructured = + remittanceInformationUnstructured.replace(regex, '').trim(); + + return { + ...transaction, + payeeName: formatPayeeName(transaction), + date: transaction.valueDateTime.slice(0, 10), + }; + }, + + sortTransactions(transactions = []) { + return transactions.sort( + (a, b) => +new Date(b.valueDateTime) - +new Date(a.valueDateTime), + ); + }, + + calculateStartingBalance(sortedTransactions = [], balances = []) { + if (sortedTransactions.length) { + const oldestTransaction = + sortedTransactions[sortedTransactions.length - 1]; + const oldestKnownBalance = amountToInteger( + oldestTransaction.balanceAfterTransaction.balanceAmount.amount, + ); + const oldestTransactionAmount = amountToInteger( + oldestTransaction.transactionAmount.amount, + ); + + return oldestKnownBalance - oldestTransactionAmount; + } else { + return amountToInteger( + balances.find((balance) => 'interimBooked' === balance.balanceType) + .balanceAmount.amount, + ); + } + }, +}; diff --git a/src/app-gocardless/banks/tests/abnamro_abnanl2a.spec.js b/src/app-gocardless/banks/tests/abnamro_abnanl2a.spec.js new file mode 100644 index 00000000..295b4107 --- /dev/null +++ b/src/app-gocardless/banks/tests/abnamro_abnanl2a.spec.js @@ -0,0 +1,33 @@ +import AbnamroAbnanl2a from '../abnamro_abnanl2a.js'; + +describe('AbnamroAbnanl2a', () => { + describe('#normalizeTransaction', () => { + it('correctly extracts the payee and when not provided', () => { + const transaction = { + transactionId: '0123456789012345', + bookingDate: '2023-12-11', + valueDateTime: '2023-12-09T15:43:37.950', + transactionAmount: { + amount: '-10.00', + currency: 'EUR', + }, + remittanceInformationUnstructuredArray: [ + 'BEA, Betaalpas', + 'My Payee Name,PAS123', + 'NR:123A4B, 09.12.23/15:43', + 'CITY', + ], + }; + + const normalizedTransaction = AbnamroAbnanl2a.normalizeTransaction( + transaction, + false, + ); + + expect(normalizedTransaction.remittanceInformationUnstructured).toEqual( + 'My Payee Name 09.12.23/15:43 CITY', + ); + expect(normalizedTransaction.payeeName).toEqual('My Payee Name'); + }); + }); +}); diff --git a/upcoming-release-notes/513.md b/upcoming-release-notes/513.md new file mode 100644 index 00000000..0bd85677 --- /dev/null +++ b/upcoming-release-notes/513.md @@ -0,0 +1,6 @@ +--- +category: Enhancements +authors: [nsulzer] +--- + +Add GoCardless integration for ABNAMRO_ABNANL2A \ No newline at end of file