@actual-app/sync-server 25.5.0 → 25.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{app.js → build/app.js} +4 -5
- package/build/bin/actual-server.js +101 -0
- package/build/migrations/1694360000000-create-folders.js +21 -0
- package/{migrations → build/migrations}/1694360479680-create-account-db.js +2 -4
- package/{migrations → build/migrations}/1694362247011-create-secret-table.js +2 -4
- package/build/migrations/1702667624000-rename-nordigen-secrets.js +9 -0
- package/{migrations → build/migrations}/1718889148000-openid.js +4 -10
- package/{migrations → build/migrations}/1719409568000-multiuser.js +10 -26
- package/build/src/account-db.js +182 -0
- package/build/src/accounts/openid.js +287 -0
- package/build/src/accounts/password.js +98 -0
- package/build/src/app-account.js +125 -0
- package/build/src/app-admin.js +317 -0
- package/build/src/app-admin.test.js +303 -0
- package/build/src/app-gocardless/app-gocardless.js +193 -0
- package/build/src/app-gocardless/bank-factory.js +84 -0
- package/build/src/app-gocardless/banks/abanca_caglesmm.js +17 -0
- package/build/src/app-gocardless/banks/abnamro_abnanl2a.js +37 -0
- package/build/src/app-gocardless/banks/american_express_aesudef1.js +32 -0
- package/build/src/app-gocardless/banks/bancsabadell_bsabesbbb.js +22 -0
- package/build/src/app-gocardless/banks/bank.interface.js +1 -0
- package/build/src/app-gocardless/banks/bank_of_ireland_b365_bofiie2d.js +25 -0
- package/build/src/app-gocardless/banks/bankinter_bkbkesmm.js +18 -0
- package/build/src/app-gocardless/banks/belfius_gkccbebb.js +13 -0
- package/build/src/app-gocardless/banks/berliner_sparkasse_beladebexxx.js +48 -0
- package/build/src/app-gocardless/banks/bnp_be_gebabebb.js +64 -0
- package/build/src/app-gocardless/banks/boursobank_bousfrppxxx.js +73 -0
- package/build/src/app-gocardless/banks/cbc_cregbebb.js +27 -0
- package/build/src/app-gocardless/banks/commerzbank_cobadeff.js +43 -0
- package/build/src/app-gocardless/banks/danskebank_dabno22.js +26 -0
- package/build/src/app-gocardless/banks/direkt_heladef1822.js +13 -0
- package/build/src/app-gocardless/banks/easybank_bawaatww.js +42 -0
- package/build/src/app-gocardless/banks/entercard_swednokk.js +28 -0
- package/build/src/app-gocardless/banks/fortuneo_ftnofrp1xxx.js +34 -0
- package/build/src/app-gocardless/banks/hype_hyeeit22.js +63 -0
- package/build/src/app-gocardless/banks/ing_ingbrobu.js +56 -0
- package/build/src/app-gocardless/banks/ing_ingddeff.js +34 -0
- package/build/src/app-gocardless/banks/ing_pl_ingbplpw.js +29 -0
- package/build/src/app-gocardless/banks/integration-bank.js +78 -0
- package/build/src/app-gocardless/banks/isybank_itbbitmm.js +13 -0
- package/build/src/app-gocardless/banks/kbc_kredbebb.js +26 -0
- package/build/src/app-gocardless/banks/lhv-lhvbee22.js +24 -0
- package/build/src/app-gocardless/banks/mbank_retail_brexplpw.js +41 -0
- package/build/src/app-gocardless/banks/nationwide_naiagb21.js +32 -0
- package/build/src/app-gocardless/banks/nbg_ethngraaxxx.js +39 -0
- package/build/src/app-gocardless/banks/norwegian_xx_norwnok1.js +61 -0
- package/build/src/app-gocardless/banks/revolut_revolt21.js +20 -0
- package/build/src/app-gocardless/banks/sandboxfinance_sfin0000.js +21 -0
- package/build/src/app-gocardless/banks/seb_kort_bank_ab.js +63 -0
- package/build/src/app-gocardless/banks/seb_privat.js +19 -0
- package/build/src/app-gocardless/banks/sparnord_spnodk22.js +19 -0
- package/build/src/app-gocardless/banks/spk_karlsruhe_karsde66.js +48 -0
- package/build/src/app-gocardless/banks/spk_marburg_biedenkopf_heladef1mar.js +25 -0
- package/build/src/app-gocardless/banks/spk_worms_alzey_ried_malade51wor.js +14 -0
- package/build/src/app-gocardless/banks/ssk_dusseldorf_dussdeddxxx.js +36 -0
- package/build/src/app-gocardless/banks/swedbank_habalv22.js +30 -0
- package/build/src/app-gocardless/banks/tests/abanca_caglesmm.spec.js +17 -0
- package/build/src/app-gocardless/banks/tests/abnamro_abnanl2a.spec.js +45 -0
- package/build/src/app-gocardless/banks/tests/bancsabadell_bsabesbbb.spec.js +41 -0
- package/build/src/app-gocardless/banks/tests/belfius_gkccbebb.spec.js +16 -0
- package/build/src/app-gocardless/banks/tests/boursobank_bousfrppxxx.spec.js +102 -0
- package/build/src/app-gocardless/banks/tests/cbc_cregbebb.spec.js +24 -0
- package/build/src/app-gocardless/banks/tests/commerzbank_cobadeff.spec.js +105 -0
- package/build/src/app-gocardless/banks/tests/easybank_bawaatww.spec.js +36 -0
- package/build/src/app-gocardless/banks/tests/fortuneo_ftnofrp1xxx.spec.js +159 -0
- package/build/src/app-gocardless/banks/tests/ing_ingddeff.spec.js +267 -0
- package/build/src/app-gocardless/banks/tests/ing_pl_ingbplpw.spec.js +186 -0
- package/build/src/app-gocardless/banks/tests/integration_bank.spec.js +127 -0
- package/build/src/app-gocardless/banks/tests/kbc_kredbebb.spec.js +24 -0
- package/build/src/app-gocardless/banks/tests/lhv-lhvbee22.spec.js +50 -0
- package/build/src/app-gocardless/banks/tests/mbank_retail_brexplpw.spec.js +156 -0
- package/build/src/app-gocardless/banks/tests/nationwide_naiagb21.spec.js +64 -0
- package/build/src/app-gocardless/banks/tests/nbg_ethngraaxxx.spec.js +36 -0
- package/build/src/app-gocardless/banks/tests/revolut_revolt21.spec.js +30 -0
- package/build/src/app-gocardless/banks/tests/sandboxfinance_sfin0000.spec.js +112 -0
- package/build/src/app-gocardless/banks/tests/spk_marburg_biedenkopf_heladef1mar.spec.js +214 -0
- package/build/src/app-gocardless/banks/tests/ssk_dusseldorf_dussdeddxxx.spec.js +60 -0
- package/build/src/app-gocardless/banks/tests/swedbank_habalv22.spec.js +45 -0
- package/build/src/app-gocardless/banks/tests/virgin_nrnbgb22.spec.js +36 -0
- package/{src → build/src}/app-gocardless/banks/util/escape-regexp.js +1 -1
- package/{src → build/src}/app-gocardless/banks/util/extract-payeeName-from-remittanceInfo.js +11 -16
- package/build/src/app-gocardless/banks/virgin_nrnbgb22.js +31 -0
- package/build/src/app-gocardless/errors.js +67 -0
- package/build/src/app-gocardless/gocardless-node.types.js +1 -0
- package/build/src/app-gocardless/gocardless.types.js +1 -0
- package/build/src/app-gocardless/services/gocardless-service.js +504 -0
- package/build/src/app-gocardless/services/tests/fixtures.js +165 -0
- package/build/src/app-gocardless/services/tests/gocardless-service.spec.js +387 -0
- package/build/src/app-gocardless/tests/bank-factory.spec.js +13 -0
- package/build/src/app-gocardless/tests/utils.spec.js +158 -0
- package/build/src/app-gocardless/util/handle-error.js +15 -0
- package/build/src/app-gocardless/utils.js +41 -0
- package/build/src/app-openid.js +83 -0
- package/build/src/app-pluggyai/app-pluggyai.js +164 -0
- package/build/src/app-pluggyai/pluggyai-service.js +97 -0
- package/build/src/app-secrets.js +48 -0
- package/build/src/app-simplefin/app-simplefin.js +335 -0
- package/build/src/app-sync/errors.js +12 -0
- package/build/src/app-sync/services/files-service.js +158 -0
- package/build/src/app-sync/tests/services/files-service.test.js +192 -0
- package/build/src/app-sync/validation.js +65 -0
- package/build/src/app-sync.js +302 -0
- package/build/src/app-sync.test.js +655 -0
- package/build/src/app.js +138 -0
- package/build/src/config-types.js +1 -0
- package/build/src/db.js +50 -0
- package/build/src/load-config.js +274 -0
- package/build/src/migrations.js +23 -0
- package/build/src/scripts/disable-openid.js +31 -0
- package/build/src/scripts/enable-openid.js +36 -0
- package/build/src/scripts/health-check.js +16 -0
- package/build/src/scripts/reset-password.js +40 -0
- package/build/src/scripts/run-migrations.js +6 -0
- package/build/src/secrets.test.js +68 -0
- package/build/src/services/secrets-service.js +79 -0
- package/build/src/services/user-service.js +201 -0
- package/build/src/sync-simple.js +68 -0
- package/{src → build/src}/util/hash.js +1 -2
- package/build/src/util/middlewares.js +49 -0
- package/{src → build/src}/util/paths.js +3 -6
- package/build/src/util/payee-name.js +37 -0
- package/build/src/util/prompt.js +70 -0
- package/build/src/util/title/index.js +43 -0
- package/build/src/util/title/lower-case.js +90 -0
- package/build/src/util/title/specials.js +21 -0
- package/build/src/util/validate-user.js +55 -0
- package/package.json +32 -36
- package/bin/actual-server.js +0 -117
- package/migrations/1694360000000-create-folders.js +0 -25
- package/migrations/1702667624000-rename-nordigen-secrets.js +0 -19
- package/src/account-db.js +0 -239
- package/src/accounts/openid.js +0 -368
- package/src/accounts/password.js +0 -149
- package/src/app-account.js +0 -155
- package/src/app-admin.js +0 -410
- package/src/app-admin.test.js +0 -381
- package/src/app-gocardless/app-gocardless.js +0 -274
- package/src/app-gocardless/bank-factory.js +0 -91
- package/src/app-gocardless/banks/abanca_caglesmm.js +0 -22
- package/src/app-gocardless/banks/abnamro_abnanl2a.js +0 -57
- package/src/app-gocardless/banks/american_express_aesudef1.js +0 -40
- package/src/app-gocardless/banks/bancsabadell_bsabesbbb.js +0 -31
- package/src/app-gocardless/banks/bank.interface.ts +0 -51
- package/src/app-gocardless/banks/bank_of_ireland_b365_bofiie2d.js +0 -39
- package/src/app-gocardless/banks/bankinter_bkbkesmm.js +0 -24
- package/src/app-gocardless/banks/belfius_gkccbebb.js +0 -17
- package/src/app-gocardless/banks/berliner_sparkasse_beladebexxx.js +0 -61
- package/src/app-gocardless/banks/bnp_be_gebabebb.js +0 -73
- package/src/app-gocardless/banks/cbc_cregbebb.js +0 -34
- package/src/app-gocardless/banks/commerzbank_cobadeff.js +0 -54
- package/src/app-gocardless/banks/danskebank_dabno22.js +0 -39
- package/src/app-gocardless/banks/direkt_heladef1822.js +0 -18
- package/src/app-gocardless/banks/easybank_bawaatww.js +0 -50
- package/src/app-gocardless/banks/entercard_swednokk.js +0 -40
- package/src/app-gocardless/banks/fortuneo_ftnofrp1xxx.js +0 -46
- package/src/app-gocardless/banks/hype_hyeeit22.js +0 -74
- package/src/app-gocardless/banks/ing_ingbrobu.js +0 -70
- package/src/app-gocardless/banks/ing_ingddeff.js +0 -47
- package/src/app-gocardless/banks/ing_pl_ingbplpw.js +0 -46
- package/src/app-gocardless/banks/integration-bank.js +0 -115
- package/src/app-gocardless/banks/isybank_itbbitmm.js +0 -18
- package/src/app-gocardless/banks/kbc_kredbebb.js +0 -33
- package/src/app-gocardless/banks/lhv-lhvbee22.js +0 -36
- package/src/app-gocardless/banks/mbank_retail_brexplpw.js +0 -56
- package/src/app-gocardless/banks/nationwide_naiagb21.js +0 -46
- package/src/app-gocardless/banks/nbg_ethngraaxxx.js +0 -51
- package/src/app-gocardless/banks/norwegian_xx_norwnok1.js +0 -74
- package/src/app-gocardless/banks/revolut_revolt21.js +0 -37
- package/src/app-gocardless/banks/sandboxfinance_sfin0000.js +0 -28
- package/src/app-gocardless/banks/seb_kort_bank_ab.js +0 -59
- package/src/app-gocardless/banks/seb_privat.js +0 -29
- package/src/app-gocardless/banks/sparnord_spnodk22.js +0 -24
- package/src/app-gocardless/banks/spk_karlsruhe_karsde66.js +0 -61
- package/src/app-gocardless/banks/spk_marburg_biedenkopf_heladef1mar.js +0 -30
- package/src/app-gocardless/banks/spk_worms_alzey_ried_malade51wor.js +0 -19
- package/src/app-gocardless/banks/ssk_dusseldorf_dussdeddxxx.js +0 -50
- package/src/app-gocardless/banks/swedbank_habalv22.js +0 -47
- package/src/app-gocardless/banks/tests/abanca_caglesmm.spec.js +0 -21
- package/src/app-gocardless/banks/tests/abnamro_abnanl2a.spec.js +0 -61
- package/src/app-gocardless/banks/tests/bancsabadell_bsabesbbb.spec.js +0 -53
- package/src/app-gocardless/banks/tests/belfius_gkccbebb.spec.js +0 -22
- package/src/app-gocardless/banks/tests/cbc_cregbebb.spec.js +0 -34
- package/src/app-gocardless/banks/tests/commerzbank_cobadeff.spec.js +0 -133
- package/src/app-gocardless/banks/tests/easybank_bawaatww.spec.js +0 -54
- package/src/app-gocardless/banks/tests/fortuneo_ftnofrp1xxx.spec.js +0 -206
- package/src/app-gocardless/banks/tests/ing_ingddeff.spec.js +0 -302
- package/src/app-gocardless/banks/tests/ing_pl_ingbplpw.spec.js +0 -202
- package/src/app-gocardless/banks/tests/integration_bank.spec.js +0 -156
- package/src/app-gocardless/banks/tests/kbc_kredbebb.spec.js +0 -38
- package/src/app-gocardless/banks/tests/lhv-lhvbee22.spec.js +0 -68
- package/src/app-gocardless/banks/tests/mbank_retail_brexplpw.spec.js +0 -171
- package/src/app-gocardless/banks/tests/nationwide_naiagb21.spec.js +0 -105
- package/src/app-gocardless/banks/tests/nbg_ethngraaxxx.spec.js +0 -48
- package/src/app-gocardless/banks/tests/revolut_revolt21.spec.js +0 -42
- package/src/app-gocardless/banks/tests/sandboxfinance_sfin0000.spec.js +0 -133
- package/src/app-gocardless/banks/tests/spk_marburg_biedenkopf_heladef1mar.spec.js +0 -255
- package/src/app-gocardless/banks/tests/ssk_dusseldorf_dussdeddxxx.spec.js +0 -100
- package/src/app-gocardless/banks/tests/swedbank_habalv22.spec.js +0 -57
- package/src/app-gocardless/banks/tests/virgin_nrnbgb22.spec.js +0 -54
- package/src/app-gocardless/banks/virgin_nrnbgb22.js +0 -39
- package/src/app-gocardless/errors.js +0 -84
- package/src/app-gocardless/gocardless-node.types.ts +0 -497
- package/src/app-gocardless/gocardless.types.ts +0 -93
- package/src/app-gocardless/link.html +0 -18
- package/src/app-gocardless/services/gocardless-service.js +0 -620
- package/src/app-gocardless/services/tests/fixtures.js +0 -181
- package/src/app-gocardless/services/tests/gocardless-service.spec.js +0 -537
- package/src/app-gocardless/tests/bank-factory.spec.js +0 -20
- package/src/app-gocardless/tests/utils.spec.js +0 -162
- package/src/app-gocardless/util/handle-error.js +0 -16
- package/src/app-gocardless/utils.js +0 -45
- package/src/app-openid.js +0 -108
- package/src/app-pluggyai/app-pluggyai.js +0 -215
- package/src/app-pluggyai/pluggyai-service.js +0 -120
- package/src/app-secrets.js +0 -61
- package/src/app-simplefin/app-simplefin.js +0 -405
- package/src/app-sync/errors.js +0 -13
- package/src/app-sync/services/files-service.js +0 -243
- package/src/app-sync/tests/services/files-service.test.js +0 -247
- package/src/app-sync/validation.js +0 -77
- package/src/app-sync.js +0 -391
- package/src/app-sync.test.js +0 -877
- package/src/app.js +0 -149
- package/src/config-types.ts +0 -44
- package/src/db.js +0 -58
- package/src/load-config.js +0 -307
- package/src/migrations.js +0 -36
- package/src/run-migrations.js +0 -8
- package/src/scripts/disable-openid.js +0 -44
- package/src/scripts/enable-openid.js +0 -53
- package/src/scripts/health-check.js +0 -23
- package/src/scripts/reset-password.js +0 -51
- package/src/secrets.test.js +0 -83
- package/src/services/secrets-service.js +0 -94
- package/src/services/user-service.js +0 -272
- package/src/sync-simple.js +0 -95
- package/src/util/middlewares.js +0 -62
- package/src/util/payee-name.js +0 -45
- package/src/util/prompt.js +0 -88
- package/src/util/title/index.js +0 -59
- package/src/util/title/lower-case.js +0 -93
- package/src/util/title/specials.js +0 -21
- package/src/util/validate-user.js +0 -68
- /package/{src → build/src}/sql/messages.sql +0 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import Fallback from './integration-bank.js';
|
|
2
|
+
import { escapeRegExp } from './util/escape-regexp.js';
|
|
3
|
+
/** @type {import('./bank.interface.js').IBank} */
|
|
4
|
+
export default {
|
|
5
|
+
...Fallback,
|
|
6
|
+
institutionIds: ['COMMERZBANK_COBADEFF'],
|
|
7
|
+
normalizeTransaction(transaction, booked) {
|
|
8
|
+
const editedTrans = { ...transaction };
|
|
9
|
+
// remittanceInformationUnstructured is limited to 140 chars thus ...
|
|
10
|
+
// ... missing information form remittanceInformationUnstructuredArray ...
|
|
11
|
+
// ... so we recreate it.
|
|
12
|
+
editedTrans.remittanceInformationUnstructured =
|
|
13
|
+
transaction.remittanceInformationUnstructuredArray.join(' ');
|
|
14
|
+
// The limitations of remittanceInformationUnstructuredArray ...
|
|
15
|
+
// ... can result in split keywords. We fix these. Other ...
|
|
16
|
+
// ... splits will need to be fixed by user with rules.
|
|
17
|
+
const keywords = [
|
|
18
|
+
'End-to-End-Ref.:',
|
|
19
|
+
'Mandatsref:',
|
|
20
|
+
'Gläubiger-ID:',
|
|
21
|
+
'SEPA-BASISLASTSCHRIFT',
|
|
22
|
+
'Kartenzahlung',
|
|
23
|
+
'Dauerauftrag',
|
|
24
|
+
];
|
|
25
|
+
keywords.forEach(keyword => {
|
|
26
|
+
editedTrans.remittanceInformationUnstructured =
|
|
27
|
+
editedTrans.remittanceInformationUnstructured.replace(
|
|
28
|
+
// There can be spaces in keywords
|
|
29
|
+
RegExp(keyword.split('').join('\\s*'), 'gi'), ', ' + keyword + ' ');
|
|
30
|
+
});
|
|
31
|
+
// Clean up remittanceInformation, deduplicate payee (removing slashes ...
|
|
32
|
+
// ... that are added to the remittanceInformation field), and ...
|
|
33
|
+
// ... remove clutter like "End-to-End-Ref.: NOTPROVIDED"
|
|
34
|
+
const payee = escapeRegExp(transaction.creditorName || transaction.debtorName || '');
|
|
35
|
+
editedTrans.remittanceInformationUnstructured =
|
|
36
|
+
editedTrans.remittanceInformationUnstructured
|
|
37
|
+
.replace(/\s*(,)?\s+/g, '$1 ')
|
|
38
|
+
.replace(RegExp(payee.split(' ').join('(/*| )'), 'gi'), ' ')
|
|
39
|
+
.replace(', End-to-End-Ref.: NOTPROVIDED', '')
|
|
40
|
+
.trim();
|
|
41
|
+
return Fallback.normalizeTransaction(transaction, booked, editedTrans);
|
|
42
|
+
},
|
|
43
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { amountToInteger } from '../utils.js';
|
|
2
|
+
import Fallback from './integration-bank.js';
|
|
3
|
+
/** @type {import('./bank.interface.js').IBank} */
|
|
4
|
+
export default {
|
|
5
|
+
...Fallback,
|
|
6
|
+
institutionIds: ['DANSKEBANK_DABANO22'],
|
|
7
|
+
normalizeTransaction(transaction, booked) {
|
|
8
|
+
const editedTrans = { ...transaction };
|
|
9
|
+
/**
|
|
10
|
+
* Danske Bank appends the EndToEndID: NOTPROVIDED to
|
|
11
|
+
* remittanceInformationUnstructured, cluttering the data.
|
|
12
|
+
*
|
|
13
|
+
* We clean thais up by removing any instances of this string from all transactions.
|
|
14
|
+
*
|
|
15
|
+
*/
|
|
16
|
+
editedTrans.remittanceInformationUnstructured =
|
|
17
|
+
transaction.remittanceInformationUnstructured.replace('\nEndToEndID: NOTPROVIDED', '');
|
|
18
|
+
return Fallback.normalizeTransaction(transaction, booked, editedTrans);
|
|
19
|
+
},
|
|
20
|
+
calculateStartingBalance(sortedTransactions = [], balances = []) {
|
|
21
|
+
const currentBalance = balances.find(balance => balance.balanceType === 'interimAvailable');
|
|
22
|
+
return sortedTransactions.reduce((total, trans) => {
|
|
23
|
+
return total - amountToInteger(trans.transactionAmount.amount);
|
|
24
|
+
}, amountToInteger(currentBalance.balanceAmount.amount));
|
|
25
|
+
},
|
|
26
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import Fallback from './integration-bank.js';
|
|
2
|
+
/** @type {import('./bank.interface.js').IBank} */
|
|
3
|
+
export default {
|
|
4
|
+
...Fallback,
|
|
5
|
+
institutionIds: ['DIREKT_HELADEF1822'],
|
|
6
|
+
normalizeTransaction(transaction, booked) {
|
|
7
|
+
const editedTrans = { ...transaction };
|
|
8
|
+
editedTrans.remittanceInformationUnstructured =
|
|
9
|
+
transaction.remittanceInformationUnstructured ??
|
|
10
|
+
transaction.remittanceInformationStructured;
|
|
11
|
+
return Fallback.normalizeTransaction(transaction, booked, editedTrans);
|
|
12
|
+
},
|
|
13
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import * as d from 'date-fns';
|
|
2
|
+
import { formatPayeeName } from '../../util/payee-name.js';
|
|
3
|
+
import { title } from '../../util/title/index.js';
|
|
4
|
+
import Fallback from './integration-bank.js';
|
|
5
|
+
/** @type {import('./bank.interface.js').IBank} */
|
|
6
|
+
export default {
|
|
7
|
+
...Fallback,
|
|
8
|
+
institutionIds: ['EASYBANK_BAWAATWW'],
|
|
9
|
+
// If date is same, sort by transactionId
|
|
10
|
+
sortTransactions: (transactions = []) => transactions.sort((a, b) => {
|
|
11
|
+
const diff = +new Date(b.valueDate || b.bookingDate) -
|
|
12
|
+
+new Date(a.valueDate || a.bookingDate);
|
|
13
|
+
if (diff !== 0)
|
|
14
|
+
return diff;
|
|
15
|
+
return parseInt(b.transactionId) - parseInt(a.transactionId);
|
|
16
|
+
}),
|
|
17
|
+
normalizeTransaction(transaction, booked) {
|
|
18
|
+
const editedTrans = { ...transaction };
|
|
19
|
+
let payeeName = formatPayeeName(transaction);
|
|
20
|
+
if (!payeeName)
|
|
21
|
+
payeeName = extractPayeeName(transaction);
|
|
22
|
+
editedTrans.payeeName = payeeName;
|
|
23
|
+
return Fallback.normalizeTransaction(transaction, booked, editedTrans);
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Extracts the payee name from the remittanceInformationStructured
|
|
28
|
+
* @param {import('../gocardless-node.types.js').Transaction} transaction
|
|
29
|
+
*/
|
|
30
|
+
function extractPayeeName(transaction) {
|
|
31
|
+
const structured = transaction.remittanceInformationStructured;
|
|
32
|
+
// The payee name is betweeen the transaction timestamp (11.07. 11:36) and the location, that starts with \\
|
|
33
|
+
const regex = /\d{2}\.\d{2}\. \d{2}:\d{2}(.*)\\\\/;
|
|
34
|
+
const matches = structured.match(regex);
|
|
35
|
+
if (matches && matches.length > 1 && matches[1]) {
|
|
36
|
+
return title(matches[1]);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
// As a fallback if still no payee is found, the whole information is used
|
|
40
|
+
return structured;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { amountToInteger } from '../utils.js';
|
|
2
|
+
import Fallback from './integration-bank.js';
|
|
3
|
+
/** @type {import('./bank.interface.js').IBank} */
|
|
4
|
+
export default {
|
|
5
|
+
...Fallback,
|
|
6
|
+
institutionIds: ['ENTERCARD_SWEDNOKK'],
|
|
7
|
+
normalizeTransaction(transaction, booked) {
|
|
8
|
+
const editedTrans = { ...transaction };
|
|
9
|
+
// GoCardless's Entercard integration returns forex transactions with the
|
|
10
|
+
// foreign amount in `transactionAmount`, but at least the amount actually
|
|
11
|
+
// billed to the account is now available in
|
|
12
|
+
// `remittanceInformationUnstructured`.
|
|
13
|
+
const remittanceInformationUnstructured = transaction.remittanceInformationUnstructured;
|
|
14
|
+
if (remittanceInformationUnstructured.startsWith('billingAmount: ')) {
|
|
15
|
+
transaction.transactionAmount = {
|
|
16
|
+
amount: remittanceInformationUnstructured.substring(15),
|
|
17
|
+
currency: 'SEK',
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
editedTrans.date = transaction.valueDate;
|
|
21
|
+
return Fallback.normalizeTransaction(transaction, booked, editedTrans);
|
|
22
|
+
},
|
|
23
|
+
calculateStartingBalance(sortedTransactions = [], balances = []) {
|
|
24
|
+
return sortedTransactions.reduce((total, trans) => {
|
|
25
|
+
return total - amountToInteger(trans.transactionAmount.amount);
|
|
26
|
+
}, amountToInteger(balances[0]?.balanceAmount?.amount || 0));
|
|
27
|
+
},
|
|
28
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { formatPayeeName } from '../../util/payee-name.js';
|
|
2
|
+
import Fallback from './integration-bank.js';
|
|
3
|
+
/** @type {import('./bank.interface.js').IBank} */
|
|
4
|
+
export default {
|
|
5
|
+
...Fallback,
|
|
6
|
+
institutionIds: ['FORTUNEO_FTNOFRP1XXX'],
|
|
7
|
+
normalizeTransaction(transaction, booked) {
|
|
8
|
+
const editedTrans = { ...transaction };
|
|
9
|
+
// Most of the information from the transaction is in the remittanceInformationUnstructuredArray field.
|
|
10
|
+
// We extract the creditor and debtor names from this field.
|
|
11
|
+
// The remittanceInformationUnstructuredArray field usually contain keywords like "Vir" for
|
|
12
|
+
// bank transfers or "Carte 03/06" for card payments, as well as the date.
|
|
13
|
+
// We remove these keywords to get a cleaner payee name.
|
|
14
|
+
const keywordsToRemove = [
|
|
15
|
+
'VIR INST',
|
|
16
|
+
'VIR',
|
|
17
|
+
'PRLV',
|
|
18
|
+
'ANN CARTE',
|
|
19
|
+
'CARTE \\d{2}\\/\\d{2}',
|
|
20
|
+
];
|
|
21
|
+
const details = transaction.remittanceInformationUnstructuredArray.join(' ');
|
|
22
|
+
const amount = transaction.transactionAmount.amount;
|
|
23
|
+
const regex = new RegExp(keywordsToRemove.join('|'), 'g');
|
|
24
|
+
const payeeName = details.replace(regex, '').trim();
|
|
25
|
+
// The amount is negative for outgoing transactions, positive for incoming transactions.
|
|
26
|
+
const isCreditorPayee = parseFloat(amount) < 0;
|
|
27
|
+
// The payee name is the creditor name for outgoing transactions and the debtor name for incoming transactions.
|
|
28
|
+
const creditorName = isCreditorPayee ? payeeName : null;
|
|
29
|
+
const debtorName = isCreditorPayee ? null : payeeName;
|
|
30
|
+
editedTrans.creditorName = creditorName;
|
|
31
|
+
editedTrans.debtorName = debtorName;
|
|
32
|
+
return Fallback.normalizeTransaction(transaction, booked, editedTrans);
|
|
33
|
+
},
|
|
34
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import Fallback from './integration-bank.js';
|
|
2
|
+
/** @type {import('./bank.interface.js').IBank} */
|
|
3
|
+
export default {
|
|
4
|
+
...Fallback,
|
|
5
|
+
institutionIds: ['HYPE_HYEEIT22'],
|
|
6
|
+
normalizeTransaction(transaction, booked) {
|
|
7
|
+
const editedTrans = { ...transaction };
|
|
8
|
+
/** Online card payments - identified by "crd" transaction code
|
|
9
|
+
* always start with PAGAMENTO PRESSO + <payee name>
|
|
10
|
+
*/
|
|
11
|
+
if (transaction.proprietaryBankTransactionCode === 'crd') {
|
|
12
|
+
// remove PAGAMENTO PRESSO and set payee name
|
|
13
|
+
editedTrans.debtorName =
|
|
14
|
+
transaction.remittanceInformationUnstructured?.slice('PAGAMENTO PRESSO '.length);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* In-app money transfers (p2p) and bank transfers (bon) have remittance info structure like
|
|
18
|
+
* DENARO (INVIATO/RICEVUTO) (A/DA) {payee_name} - {payment_info} (p2p)
|
|
19
|
+
* HAI (INVIATO/RICEVUTO) UN BONIFICO (A/DA) {payee_name} - {payment_info} (bon)
|
|
20
|
+
*/
|
|
21
|
+
if (transaction.proprietaryBankTransactionCode === 'p2p' ||
|
|
22
|
+
transaction.proprietaryBankTransactionCode === 'bon') {
|
|
23
|
+
// keep only {payment_info} portion of remittance info
|
|
24
|
+
// NOTE: if {payee_name} contains dashes (unlikely / impossible?), this probably gets bugged!
|
|
25
|
+
const infoIdx = transaction.remittanceInformationUnstructured.indexOf(' - ') + 3;
|
|
26
|
+
editedTrans.remittanceInformationUnstructured =
|
|
27
|
+
infoIdx === -1
|
|
28
|
+
? transaction.remittanceInformationUnstructured
|
|
29
|
+
: transaction.remittanceInformationUnstructured.slice(infoIdx).trim();
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* CONVERT ESCAPED UNICODE TO CODEPOINTS
|
|
33
|
+
* p2p payments allow user to write arbitrary unicode strings as messages
|
|
34
|
+
* gocardless reports unicode codepoints as \Uxxxx
|
|
35
|
+
* so it groups them in 4bytes bundles
|
|
36
|
+
* the code below assumes this is always the case
|
|
37
|
+
*/
|
|
38
|
+
if (transaction.proprietaryBankTransactionCode === 'p2p') {
|
|
39
|
+
let str = transaction.remittanceInformationUnstructured;
|
|
40
|
+
let idx = str.indexOf('\\U');
|
|
41
|
+
let start_idx = idx;
|
|
42
|
+
let codepoints = [];
|
|
43
|
+
while (idx !== -1) {
|
|
44
|
+
codepoints.push(parseInt(str.slice(idx + 2, idx + 6), 16));
|
|
45
|
+
const next_idx = str.indexOf('\\U', idx + 6);
|
|
46
|
+
if (next_idx === idx + 6) {
|
|
47
|
+
idx = next_idx;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
str =
|
|
51
|
+
str.slice(0, start_idx) +
|
|
52
|
+
String.fromCodePoint(...codepoints) +
|
|
53
|
+
str.slice(idx + 6);
|
|
54
|
+
codepoints = [];
|
|
55
|
+
idx = str.indexOf('\\U'); // slight inefficiency?
|
|
56
|
+
start_idx = idx;
|
|
57
|
+
}
|
|
58
|
+
editedTrans.remittanceInformationUnstructured = str;
|
|
59
|
+
}
|
|
60
|
+
editedTrans.date = transaction.valueDate || transaction.bookingDate;
|
|
61
|
+
return Fallback.normalizeTransaction(transaction, booked, editedTrans);
|
|
62
|
+
},
|
|
63
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import Fallback from './integration-bank.js';
|
|
2
|
+
/** @type {import('./bank.interface.js').IBank} */
|
|
3
|
+
export default {
|
|
4
|
+
...Fallback,
|
|
5
|
+
institutionIds: ['ING_INGBROBU'],
|
|
6
|
+
normalizeTransaction(transaction, booked) {
|
|
7
|
+
const editedTrans = { ...transaction };
|
|
8
|
+
//Merchant transactions all have the same transactionId of 'NOTPROVIDED'.
|
|
9
|
+
//For booked transactions, this can be set to the internalTransactionId
|
|
10
|
+
//For pending transactions, this needs to be removed for them to show up in Actual
|
|
11
|
+
//For deduplication to work better, payeeName needs to be standardized
|
|
12
|
+
//and converted from a pending transaction form ("payeeName":"Card no: xxxxxxxxxxxx1111"') to a booked transaction form ("payeeName":"Card no: Xxxx Xxxx Xxxx 1111")
|
|
13
|
+
if (transaction.transactionId === 'NOTPROVIDED') {
|
|
14
|
+
//Some corner case transactions only have the `proprietaryBankTransactionCode` field, this need to be copied to `remittanceInformationUnstructured`
|
|
15
|
+
if (transaction.proprietaryBankTransactionCode &&
|
|
16
|
+
!transaction.remittanceInformationUnstructured) {
|
|
17
|
+
editedTrans.remittanceInformationUnstructured =
|
|
18
|
+
transaction.proprietaryBankTransactionCode;
|
|
19
|
+
}
|
|
20
|
+
if (booked) {
|
|
21
|
+
transaction.transactionId = transaction.internalTransactionId;
|
|
22
|
+
if (transaction.remittanceInformationUnstructured &&
|
|
23
|
+
transaction.remittanceInformationUnstructured
|
|
24
|
+
.toLowerCase()
|
|
25
|
+
.includes('card no:')) {
|
|
26
|
+
editedTrans.creditorName =
|
|
27
|
+
transaction.remittanceInformationUnstructured.split(',')[0];
|
|
28
|
+
//Catch all case for other types of payees
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
editedTrans.creditorName =
|
|
32
|
+
transaction.remittanceInformationUnstructured;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
transaction.transactionId = null;
|
|
37
|
+
if (transaction.remittanceInformationUnstructured &&
|
|
38
|
+
transaction.remittanceInformationUnstructured
|
|
39
|
+
.toLowerCase()
|
|
40
|
+
.includes('card no:')) {
|
|
41
|
+
editedTrans.creditorName =
|
|
42
|
+
transaction.remittanceInformationUnstructured.replace(/x{4}/g, 'Xxxx ');
|
|
43
|
+
//Catch all case for other types of payees
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
editedTrans.creditorName =
|
|
47
|
+
transaction.remittanceInformationUnstructured;
|
|
48
|
+
}
|
|
49
|
+
//Remove remittanceInformationUnstructured from pending transactions, so the `notes` field remains empty (there is no merchant information)
|
|
50
|
+
//Once booked, the right `notes` (containing the merchant) will be populated
|
|
51
|
+
editedTrans.remittanceInformationUnstructured = null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return Fallback.normalizeTransaction(transaction, booked, editedTrans);
|
|
55
|
+
},
|
|
56
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { amountToInteger } from '../utils.js';
|
|
2
|
+
import Fallback from './integration-bank.js';
|
|
3
|
+
/** @type {import('./bank.interface.js').IBank} */
|
|
4
|
+
export default {
|
|
5
|
+
...Fallback,
|
|
6
|
+
institutionIds: ['ING_INGDDEFF'],
|
|
7
|
+
normalizeTransaction(transaction, booked) {
|
|
8
|
+
const editedTrans = { ...transaction };
|
|
9
|
+
const remittanceInformationMatch = /remittanceinformation:(.*)$/.exec(transaction.remittanceInformationUnstructured);
|
|
10
|
+
editedTrans.remittanceInformationUnstructured = remittanceInformationMatch
|
|
11
|
+
? remittanceInformationMatch[1]
|
|
12
|
+
: transaction.remittanceInformationUnstructured;
|
|
13
|
+
return Fallback.normalizeTransaction(transaction, booked, editedTrans);
|
|
14
|
+
},
|
|
15
|
+
sortTransactions(transactions = []) {
|
|
16
|
+
return transactions.sort((a, b) => {
|
|
17
|
+
const diff = +new Date(b.valueDate || b.bookingDate) -
|
|
18
|
+
+new Date(a.valueDate || a.bookingDate);
|
|
19
|
+
if (diff)
|
|
20
|
+
return diff;
|
|
21
|
+
const idA = parseInt(a.transactionId);
|
|
22
|
+
const idB = parseInt(b.transactionId);
|
|
23
|
+
if (!isNaN(idA) && !isNaN(idB))
|
|
24
|
+
return idB - idA;
|
|
25
|
+
return 0;
|
|
26
|
+
});
|
|
27
|
+
},
|
|
28
|
+
calculateStartingBalance(sortedTransactions = [], balances = []) {
|
|
29
|
+
const currentBalance = balances.find(balance => 'interimBooked' === balance.balanceType);
|
|
30
|
+
return sortedTransactions.reduce((total, trans) => {
|
|
31
|
+
return total - amountToInteger(trans.transactionAmount.amount);
|
|
32
|
+
}, amountToInteger(currentBalance.balanceAmount.amount));
|
|
33
|
+
},
|
|
34
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { amountToInteger } from '../utils.js';
|
|
2
|
+
import Fallback from './integration-bank.js';
|
|
3
|
+
/** @type {import('./bank.interface.js').IBank} */
|
|
4
|
+
export default {
|
|
5
|
+
...Fallback,
|
|
6
|
+
institutionIds: ['ING_PL_INGBPLPW'],
|
|
7
|
+
normalizeTransaction(transaction, booked) {
|
|
8
|
+
const editedTrans = { ...transaction };
|
|
9
|
+
editedTrans.date = transaction.valueDate;
|
|
10
|
+
return Fallback.normalizeTransaction(transaction, booked, editedTrans);
|
|
11
|
+
},
|
|
12
|
+
sortTransactions(transactions = []) {
|
|
13
|
+
return transactions.sort((a, b) => {
|
|
14
|
+
return (Number(b.transactionId.substr(2)) - Number(a.transactionId.substr(2)));
|
|
15
|
+
});
|
|
16
|
+
},
|
|
17
|
+
calculateStartingBalance(sortedTransactions = [], balances = []) {
|
|
18
|
+
if (sortedTransactions.length) {
|
|
19
|
+
const oldestTransaction = sortedTransactions[sortedTransactions.length - 1];
|
|
20
|
+
const oldestKnownBalance = amountToInteger(oldestTransaction.balanceAfterTransaction.balanceAmount.amount);
|
|
21
|
+
const oldestTransactionAmount = amountToInteger(oldestTransaction.transactionAmount.amount);
|
|
22
|
+
return oldestKnownBalance - oldestTransactionAmount;
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
return amountToInteger(balances.find(balance => 'interimBooked' === balance.balanceType)
|
|
26
|
+
.balanceAmount.amount);
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import * as d from 'date-fns';
|
|
2
|
+
import { formatPayeeName } from '../../util/payee-name.js';
|
|
3
|
+
import { amountToInteger, printIban, sortByBookingDateOrValueDate, } from '../utils.js';
|
|
4
|
+
const SORTED_BALANCE_TYPE_LIST = [
|
|
5
|
+
'closingBooked',
|
|
6
|
+
'expected',
|
|
7
|
+
'forwardAvailable',
|
|
8
|
+
'interimAvailable',
|
|
9
|
+
'interimBooked',
|
|
10
|
+
'nonInvoiced',
|
|
11
|
+
'openingBooked',
|
|
12
|
+
];
|
|
13
|
+
/** @type {import('./bank.interface.js').IBank} */
|
|
14
|
+
export default {
|
|
15
|
+
institutionIds: ['IntegrationBank'],
|
|
16
|
+
normalizeAccount(account) {
|
|
17
|
+
console.debug('Available account properties for new institution integration', { account: JSON.stringify(account) });
|
|
18
|
+
return {
|
|
19
|
+
account_id: account.id,
|
|
20
|
+
institution: account.institution,
|
|
21
|
+
mask: (account?.iban || '0000').slice(-4),
|
|
22
|
+
iban: account?.iban || null,
|
|
23
|
+
name: [
|
|
24
|
+
account.name ?? account.displayName ?? account.product,
|
|
25
|
+
printIban(account),
|
|
26
|
+
account.currency,
|
|
27
|
+
]
|
|
28
|
+
.filter(Boolean)
|
|
29
|
+
.join(' '),
|
|
30
|
+
official_name: account.product ?? `integration-${account.institution_id}`,
|
|
31
|
+
type: 'checking',
|
|
32
|
+
};
|
|
33
|
+
},
|
|
34
|
+
normalizeTransaction(transaction, _booked, editedTransaction = null) {
|
|
35
|
+
const trans = editedTransaction ?? transaction;
|
|
36
|
+
const date = trans.date ||
|
|
37
|
+
transaction.bookingDate ||
|
|
38
|
+
transaction.bookingDateTime ||
|
|
39
|
+
transaction.valueDate ||
|
|
40
|
+
transaction.valueDateTime;
|
|
41
|
+
// If we couldn't find a valid date field we filter out this transaction
|
|
42
|
+
// and hope that we will import it again once the bank has processed the
|
|
43
|
+
// transaction further.
|
|
44
|
+
if (!date) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
const notes = trans.notes ??
|
|
48
|
+
trans.remittanceInformationUnstructured ??
|
|
49
|
+
trans.remittanceInformationUnstructuredArray?.join(' ');
|
|
50
|
+
transaction.remittanceInformationUnstructuredArrayString =
|
|
51
|
+
transaction.remittanceInformationUnstructuredArray?.join(',');
|
|
52
|
+
transaction.remittanceInformationStructuredArrayString =
|
|
53
|
+
transaction.remittanceInformationStructuredArray?.join(',');
|
|
54
|
+
return {
|
|
55
|
+
...transaction,
|
|
56
|
+
payeeName: trans.payeeName ?? formatPayeeName(trans),
|
|
57
|
+
date: d.format(d.parseISO(date), 'yyyy-MM-dd'),
|
|
58
|
+
notes,
|
|
59
|
+
};
|
|
60
|
+
},
|
|
61
|
+
sortTransactions(transactions = []) {
|
|
62
|
+
console.debug('Available (first 10) transactions properties for new integration of institution in sortTransactions function', { top10Transactions: JSON.stringify(transactions.slice(0, 10)) });
|
|
63
|
+
return sortByBookingDateOrValueDate(transactions);
|
|
64
|
+
},
|
|
65
|
+
calculateStartingBalance(sortedTransactions = [], balances = []) {
|
|
66
|
+
console.debug('Available (first 10) transactions properties for new integration of institution in calculateStartingBalance function', {
|
|
67
|
+
balances: JSON.stringify(balances),
|
|
68
|
+
top10SortedTransactions: JSON.stringify(sortedTransactions.slice(0, 10)),
|
|
69
|
+
});
|
|
70
|
+
const currentBalance = balances
|
|
71
|
+
.filter(item => SORTED_BALANCE_TYPE_LIST.includes(item.balanceType))
|
|
72
|
+
.sort((a, b) => SORTED_BALANCE_TYPE_LIST.indexOf(a.balanceType) -
|
|
73
|
+
SORTED_BALANCE_TYPE_LIST.indexOf(b.balanceType))[0];
|
|
74
|
+
return sortedTransactions.reduce((total, trans) => {
|
|
75
|
+
return total - amountToInteger(trans.transactionAmount.amount);
|
|
76
|
+
}, amountToInteger(currentBalance?.balanceAmount?.amount || 0));
|
|
77
|
+
},
|
|
78
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import Fallback from './integration-bank.js';
|
|
2
|
+
/** @type {import('./bank.interface.js').IBank} */
|
|
3
|
+
export default {
|
|
4
|
+
...Fallback,
|
|
5
|
+
institutionIds: ['ISYBANK_ITBBITMM'],
|
|
6
|
+
// It has been reported that valueDate is more accurate than booking date
|
|
7
|
+
// when it is provided
|
|
8
|
+
normalizeTransaction(transaction, booked) {
|
|
9
|
+
const editedTrans = { ...transaction };
|
|
10
|
+
editedTrans.date = transaction.valueDate ?? transaction.bookingDate;
|
|
11
|
+
return Fallback.normalizeTransaction(transaction, booked, editedTrans);
|
|
12
|
+
},
|
|
13
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import Fallback from './integration-bank.js';
|
|
2
|
+
import { extractPayeeNameFromRemittanceInfo } from './util/extract-payeeName-from-remittanceInfo.js';
|
|
3
|
+
/** @type {import('./bank.interface.js').IBank} */
|
|
4
|
+
export default {
|
|
5
|
+
...Fallback,
|
|
6
|
+
institutionIds: ['KBC_KREDBEBB'],
|
|
7
|
+
/**
|
|
8
|
+
* For negative amounts, the only payee information we have is returned in
|
|
9
|
+
* remittanceInformationUnstructured.
|
|
10
|
+
*/
|
|
11
|
+
normalizeTransaction(transaction, booked) {
|
|
12
|
+
const editedTrans = { ...transaction };
|
|
13
|
+
if (Number(transaction.transactionAmount.amount) > 0) {
|
|
14
|
+
editedTrans.payeeName =
|
|
15
|
+
transaction.debtorName ||
|
|
16
|
+
transaction.remittanceInformationUnstructured ||
|
|
17
|
+
'undefined';
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
editedTrans.payeeName =
|
|
21
|
+
transaction.creditorName ||
|
|
22
|
+
extractPayeeNameFromRemittanceInfo(transaction.remittanceInformationUnstructured, ['Betaling met', 'Domiciliëring', 'Overschrijving']);
|
|
23
|
+
}
|
|
24
|
+
return Fallback.normalizeTransaction(transaction, booked, editedTrans);
|
|
25
|
+
},
|
|
26
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as d from 'date-fns';
|
|
2
|
+
import Fallback from './integration-bank.js';
|
|
3
|
+
/** @type {import('./bank.interface.js').IBank} */
|
|
4
|
+
export default {
|
|
5
|
+
...Fallback,
|
|
6
|
+
institutionIds: ['LHV_LHVBEE22'],
|
|
7
|
+
normalizeTransaction(transaction, booked) {
|
|
8
|
+
const editedTrans = { ...transaction };
|
|
9
|
+
// extract bookingDate and creditorName for card transactions, e.g.
|
|
10
|
+
// (..1234) 2025-01-02 09:32 CrustumOU\Poordi 3\Tallinn\10156 ESTEST
|
|
11
|
+
// bookingDate: 2025-01-02
|
|
12
|
+
// creditorName: CrustumOU
|
|
13
|
+
const cardTxRegex = /^\(\.\.(\d{4})\) (\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}) (.+)$/g;
|
|
14
|
+
const cardTxMatch = cardTxRegex.exec(transaction?.remittanceInformationUnstructured);
|
|
15
|
+
if (cardTxMatch) {
|
|
16
|
+
const extractedDate = d.parse(cardTxMatch[2], 'yyyy-MM-dd', new Date());
|
|
17
|
+
editedTrans.payeeName = cardTxMatch[4].split('\\')[0].trim();
|
|
18
|
+
if (booked && d.isValid(extractedDate)) {
|
|
19
|
+
editedTrans.date = d.format(extractedDate, 'yyyy-MM-dd');
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return Fallback.normalizeTransaction(transaction, booked, editedTrans);
|
|
23
|
+
},
|
|
24
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { amountToInteger } from '../utils.js';
|
|
2
|
+
import Fallback from './integration-bank.js';
|
|
3
|
+
/** @type {import('./bank.interface.js').IBank} */
|
|
4
|
+
export default {
|
|
5
|
+
...Fallback,
|
|
6
|
+
institutionIds: ['MBANK_RETAIL_BREXPLPW'],
|
|
7
|
+
/**
|
|
8
|
+
* When requesting transaction details for MBANK_RETAIL_BREXPLPW
|
|
9
|
+
* using gocardless API, it seems that bookingDate and valueDate are swapped.
|
|
10
|
+
* valueDate will always come before bookingDate, so as a simple fix,
|
|
11
|
+
* I have overwritten integration-bank.normalizeTransaction() here,
|
|
12
|
+
* swapped dates back (by giving valueDate higher priority) and
|
|
13
|
+
* called parent method with edited transaction as argument
|
|
14
|
+
*/
|
|
15
|
+
normalizeTransaction(transaction, booked) {
|
|
16
|
+
const editedTrans = { ...transaction };
|
|
17
|
+
const date = transaction.valueDate ||
|
|
18
|
+
transaction.valueDateTime ||
|
|
19
|
+
transaction.bookingDate ||
|
|
20
|
+
transaction.bookingDateTime;
|
|
21
|
+
editedTrans.date = date;
|
|
22
|
+
return Fallback.normalizeTransaction(transaction, booked, editedTrans);
|
|
23
|
+
},
|
|
24
|
+
sortTransactions(transactions = []) {
|
|
25
|
+
return transactions.sort((a, b) => Number(b.transactionId) - Number(a.transactionId));
|
|
26
|
+
},
|
|
27
|
+
/**
|
|
28
|
+
* For MBANK_RETAIL_BREXPLPW we don't know what balance was
|
|
29
|
+
* after each transaction so we have to calculate it by getting
|
|
30
|
+
* current balance from the account and subtract all the transactions
|
|
31
|
+
*
|
|
32
|
+
* As a current balance we use `interimBooked` balance type because
|
|
33
|
+
* it includes transaction placed during current day
|
|
34
|
+
*/
|
|
35
|
+
calculateStartingBalance(sortedTransactions = [], balances = []) {
|
|
36
|
+
const currentBalance = balances.find(balance => 'interimBooked' === balance.balanceType);
|
|
37
|
+
return sortedTransactions.reduce((total, trans) => {
|
|
38
|
+
return total - amountToInteger(trans.transactionAmount.amount);
|
|
39
|
+
}, amountToInteger(currentBalance.balanceAmount.amount));
|
|
40
|
+
},
|
|
41
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import Fallback from './integration-bank.js';
|
|
2
|
+
/** @type {import('./bank.interface.js').IBank} */
|
|
3
|
+
export default {
|
|
4
|
+
...Fallback,
|
|
5
|
+
institutionIds: ['NATIONWIDE_NAIAGB21'],
|
|
6
|
+
normalizeTransaction(transaction, booked) {
|
|
7
|
+
const editedTrans = { ...transaction };
|
|
8
|
+
// Nationwide can sometimes return pending transactions with a date
|
|
9
|
+
// representing the latest a transaction could be booked. This stops
|
|
10
|
+
// actual's deduplication logic from working as it only checks 7 days
|
|
11
|
+
// ahead/behind and the transactionID from Nationwide changes when a
|
|
12
|
+
// transaction is booked
|
|
13
|
+
if (!booked) {
|
|
14
|
+
const useDate = new Date(Math.min(new Date(transaction.bookingDate).getTime(), new Date().getTime()));
|
|
15
|
+
editedTrans.date = useDate.toISOString().slice(0, 10);
|
|
16
|
+
}
|
|
17
|
+
// Nationwide also occasionally returns erroneous transaction_ids
|
|
18
|
+
// that are malformed and can even change after import. This will ignore
|
|
19
|
+
// these ids and unset them. When a correct ID is returned then it will
|
|
20
|
+
// update via the deduplication logic
|
|
21
|
+
const debitCreditRegex = /^00(DEB|CRED)IT.+$/;
|
|
22
|
+
const validLengths = [
|
|
23
|
+
40, // Nationwide credit cards
|
|
24
|
+
32, // Nationwide current accounts
|
|
25
|
+
];
|
|
26
|
+
if (transaction.transactionId?.match(debitCreditRegex) ||
|
|
27
|
+
!validLengths.includes(transaction.transactionId?.length)) {
|
|
28
|
+
transaction.transactionId = null;
|
|
29
|
+
}
|
|
30
|
+
return Fallback.normalizeTransaction(transaction, booked, editedTrans);
|
|
31
|
+
},
|
|
32
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { amountToInteger } from '../utils.js';
|
|
2
|
+
import Fallback from './integration-bank.js';
|
|
3
|
+
/** @type {import('./bank.interface.js').IBank} */
|
|
4
|
+
export default {
|
|
5
|
+
...Fallback,
|
|
6
|
+
institutionIds: ['NBG_ETHNGRAAXXX'],
|
|
7
|
+
/**
|
|
8
|
+
* Fixes for the pending transactions:
|
|
9
|
+
* - Corrects amount to negative (nbg erroneously omits the minus sign in pending transactions)
|
|
10
|
+
* - Removes prefix 'ΑΓΟΡΑ' from remittance information to align with the booked transaction (necessary for fuzzy matching to work)
|
|
11
|
+
*/
|
|
12
|
+
normalizeTransaction(transaction, booked) {
|
|
13
|
+
const editedTrans = { ...transaction };
|
|
14
|
+
if (!transaction.transactionId &&
|
|
15
|
+
transaction.remittanceInformationUnstructured.startsWith('ΑΓΟΡΑ ')) {
|
|
16
|
+
transaction.transactionAmount = {
|
|
17
|
+
amount: '-' + transaction.transactionAmount.amount,
|
|
18
|
+
currency: transaction.transactionAmount.currency,
|
|
19
|
+
};
|
|
20
|
+
editedTrans.remittanceInformationUnstructured =
|
|
21
|
+
transaction.remittanceInformationUnstructured.substring(6);
|
|
22
|
+
}
|
|
23
|
+
return Fallback.normalizeTransaction(transaction, booked, editedTrans);
|
|
24
|
+
},
|
|
25
|
+
/**
|
|
26
|
+
* For NBG_ETHNGRAAXXX we don't know what balance was
|
|
27
|
+
* after each transaction so we have to calculate it by getting
|
|
28
|
+
* current balance from the account and subtract all the transactions
|
|
29
|
+
*
|
|
30
|
+
* As a current balance we use `interimBooked` balance type because
|
|
31
|
+
* it includes transaction placed during current day
|
|
32
|
+
*/
|
|
33
|
+
calculateStartingBalance(sortedTransactions = [], balances = []) {
|
|
34
|
+
const currentBalance = balances.find(balance => 'interimAvailable' === balance.balanceType);
|
|
35
|
+
return sortedTransactions.reduce((total, trans) => {
|
|
36
|
+
return total - amountToInteger(trans.transactionAmount.amount);
|
|
37
|
+
}, amountToInteger(currentBalance.balanceAmount.amount));
|
|
38
|
+
},
|
|
39
|
+
};
|