From 46eb2a7c38b6ea48dbd4ed785229804a363d9cec Mon Sep 17 00:00:00 2001 From: 0x4d4e Date: Sat, 28 Jun 2025 15:13:45 +0200 Subject: [PATCH] Added a gocardless bank parser for Raiffeisen AT bank (#5244) * Added a gocardless bank parser for Raiffeisen AT bank * [autofix.ci] apply automated fixes --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- .../banks/raiffeisen_at_rzbaatww.js | 51 +++++++++ .../tests/raiffeisen_at_rzbaatww.spec.js | 100 ++++++++++++++++++ upcoming-release-notes/5244.md | 6 ++ 3 files changed, 157 insertions(+) create mode 100644 packages/sync-server/src/app-gocardless/banks/raiffeisen_at_rzbaatww.js create mode 100644 packages/sync-server/src/app-gocardless/banks/tests/raiffeisen_at_rzbaatww.spec.js create mode 100644 upcoming-release-notes/5244.md diff --git a/packages/sync-server/src/app-gocardless/banks/raiffeisen_at_rzbaatww.js b/packages/sync-server/src/app-gocardless/banks/raiffeisen_at_rzbaatww.js new file mode 100644 index 0000000000..fab38a36f3 --- /dev/null +++ b/packages/sync-server/src/app-gocardless/banks/raiffeisen_at_rzbaatww.js @@ -0,0 +1,51 @@ +import { formatPayeeName } from '../../util/payee-name.js'; +import { title } from '../../util/title/index.js'; + +import Fallback from './integration-bank.js'; + +/** @type {import('./bank.interface.js').IBank} */ +export default { + ...Fallback, + + institutionIds: ['RAIFFEISEN_AT_RZBAATWW'], + + normalizeTransaction(transaction, booked) { + const editedTrans = { ...transaction }; + + let payeeName = formatPayeeName(transaction); + if (!payeeName) { + payeeName = extractPayeeName(transaction); + } + editedTrans.payeeName = payeeName; + + // avoid empty notes if payee is set but no information in unstructured information + // if no structured or unstructured information is provided, return the endToEndId instead + editedTrans.remittanceInformationUnstructured = + transaction.remittanceInformationUnstructured ?? + transaction.remittanceInformationStructured ?? + transaction.endToEndId; + + return Fallback.normalizeTransaction(transaction, booked, editedTrans); + }, +}; + +/** + * Extracts the payee name from the remittanceInformationStructured + * @param {import('../gocardless-node.types.js').Transaction} transaction + */ +function extractPayeeName(transaction) { + const structured = transaction.remittanceInformationStructured; + // The payee name is at the beginning and has a max length of 12 characters + // (if structured information is actually structured ...). + const regex = /(.{12}) \d{4} .* \d{2}\.\d{2}\. \d{2}:\d{2}/; + const matches = structured.match(regex); + if (matches && matches.length > 1 && matches[1]) { + const name = title(matches[1]); + // These transactions never contained creditor information in my tests, thus no + // attempt to add the IBAN to the name... + return name; + } else { + // As a fallback if still no payee is found, the whole information is used + return structured; + } +} diff --git a/packages/sync-server/src/app-gocardless/banks/tests/raiffeisen_at_rzbaatww.spec.js b/packages/sync-server/src/app-gocardless/banks/tests/raiffeisen_at_rzbaatww.spec.js new file mode 100644 index 0000000000..613b1132ef --- /dev/null +++ b/packages/sync-server/src/app-gocardless/banks/tests/raiffeisen_at_rzbaatww.spec.js @@ -0,0 +1,100 @@ +import { mockTransactionAmount } from '../../services/tests/fixtures.js'; +import RaiffeisenAtRzbaatww from '../raiffeisen_at_rzbaatww.js'; + +describe('raiffeisen_at', () => { + describe('#normalizeTransaction', () => { + it('returns the full structured information as payeeName from a transaction with no payee name', () => { + const transaction = { + transactionId: '10_2025-01-01_0123456789', + bookingDate: '2025-01-01', + valueDate: '2025-01-01', + transactionAmount: mockTransactionAmount, + remittanceInformationStructured: 'NOTHING STRUCTURED', + internalTransactionId: '01234567890123456789', + }; + + const normalizedTransaction = RaiffeisenAtRzbaatww.normalizeTransaction( + transaction, + true, + ); + + expect(normalizedTransaction.payeeName).toEqual('NOTHING STRUCTURED'); + }); + + it('returns the expected payeeName from a transaction with payee name inside structuredInformation', () => { + const transaction = { + transactionId: '10_2025-01-01_0123456789', + bookingDate: '2025-01-01', + valueDate: '2025-01-01', + transactionAmount: mockTransactionAmount, + remittanceInformationStructured: 'COMPANY ABCD 2610 D5 01.01. 13:37', + internalTransactionId: '01234567890123456789', + }; + + const normalizedTransaction = RaiffeisenAtRzbaatww.normalizeTransaction( + transaction, + true, + ); + + expect(normalizedTransaction.payeeName).toEqual('Company Abcd'); + }); + + it('returns the creditorName for transactions containing one', () => { + const transaction = { + transactionId: '18_2025-01-01_0123456789', + bookingDate: '2025-01-01', + valueDate: '2025-01-01', + transactionAmount: mockTransactionAmount, + creditorName: 'Reci Pient', + creditorAccount: { + iban: 'AT201100021493538935', + }, + remittanceInformationStructured: 'just some text here', + additionalInformation: 'more info', + internalTransactionId: '01234567890123456789', + }; + const normalizedTransaction = RaiffeisenAtRzbaatww.normalizeTransaction( + transaction, + true, + ); + expect(normalizedTransaction.payeeName).toEqual( + 'Reci Pient (AT20 XXX 8935)', + ); + }); + + it('returns the unstructured information for POS transactions without payee name or useful structured information', () => { + const transaction = { + transactionId: '18_2025-01-01_0123456789', + bookingDate: '2025-01-01', + valueDate: '2025-01-01', + transactionAmount: mockTransactionAmount, + remittanceInformationUnstructured: 'COMPANY NAME CITY 1010', + remittanceInformationStructured: + 'POS 1,11 AT D4 01.01. 13:37', + internalTransactionId: '01234567890123456789', + }; + const normalizedTransaction = RaiffeisenAtRzbaatww.normalizeTransaction( + transaction, + true, + ); + expect(normalizedTransaction.payeeName).toEqual('Company Name City 1010'); + }); + + it('returns the endToEndId in notes if no structured or unstructured remittance information is present', () => { + const transaction = { + transactionId: '18_2025-01-01_0123456789', + bookingDate: '2025-01-01', + valueDate: '2025-01-01', + transactionAmount: mockTransactionAmount, + creditorName: 'Creditor', + endToEndId: 'Transaction 1234', + internalTransactionId: '01234567890123456789', + }; + const normalizedTransaction = RaiffeisenAtRzbaatww.normalizeTransaction( + transaction, + true, + ); + expect(normalizedTransaction.notes).toEqual('Transaction 1234'); + }); + }); +}); diff --git a/upcoming-release-notes/5244.md b/upcoming-release-notes/5244.md new file mode 100644 index 0000000000..72c6b208c6 --- /dev/null +++ b/upcoming-release-notes/5244.md @@ -0,0 +1,6 @@ +--- +category: Enhancements +authors: [0x4d4e] +--- + +Added a GoCardless bank parser for Raiffeisen Bank Austria.