@actual-app/sync-server 25.4.0-alpha.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/.dockerignore +12 -0
- package/README.md +19 -0
- package/app.js +11 -0
- package/babel.config.json +3 -0
- package/bin/@actual-app/sync-server +55 -0
- package/docker/alpine.Dockerfile +62 -0
- package/docker/ubuntu.Dockerfile +63 -0
- package/docker-compose.yml +29 -0
- package/jest.config.json +19 -0
- package/jest.global-setup.js +101 -0
- package/jest.global-teardown.js +6 -0
- package/migrations/1694360000000-create-folders.js +25 -0
- package/migrations/1694360479680-create-account-db.js +30 -0
- package/migrations/1694362247011-create-secret-table.js +16 -0
- package/migrations/1702667624000-rename-nordigen-secrets.js +19 -0
- package/migrations/1718889148000-openid.js +41 -0
- package/migrations/1719409568000-multiuser.js +116 -0
- package/package.json +64 -0
- package/src/account-db.js +239 -0
- package/src/accounts/openid.js +361 -0
- package/src/accounts/password.js +149 -0
- package/src/app-account.js +155 -0
- package/src/app-admin.js +410 -0
- package/src/app-admin.test.js +381 -0
- package/src/app-gocardless/README.md +198 -0
- package/src/app-gocardless/app-gocardless.js +274 -0
- package/src/app-gocardless/bank-factory.js +91 -0
- package/src/app-gocardless/banks/abanca_caglesmm.js +22 -0
- package/src/app-gocardless/banks/abnamro_abnanl2a.js +57 -0
- package/src/app-gocardless/banks/american_express_aesudef1.js +40 -0
- package/src/app-gocardless/banks/bancsabadell_bsabesbbb.js +31 -0
- package/src/app-gocardless/banks/bank.interface.ts +51 -0
- package/src/app-gocardless/banks/bank_of_ireland_b365_bofiie2d.js +39 -0
- package/src/app-gocardless/banks/bankinter_bkbkesmm.js +24 -0
- package/src/app-gocardless/banks/belfius_gkccbebb.js +17 -0
- package/src/app-gocardless/banks/berliner_sparkasse_beladebexxx.js +61 -0
- package/src/app-gocardless/banks/bnp_be_gebabebb.js +73 -0
- package/src/app-gocardless/banks/cbc_cregbebb.js +34 -0
- package/src/app-gocardless/banks/commerzbank_cobadeff.js +51 -0
- package/src/app-gocardless/banks/danskebank_dabno22.js +39 -0
- package/src/app-gocardless/banks/direkt_heladef1822.js +18 -0
- package/src/app-gocardless/banks/easybank_bawaatww.js +50 -0
- package/src/app-gocardless/banks/entercard_swednokk.js +40 -0
- package/src/app-gocardless/banks/fortuneo_ftnofrp1xxx.js +46 -0
- package/src/app-gocardless/banks/hype_hyeeit22.js +74 -0
- package/src/app-gocardless/banks/ing_ingbrobu.js +70 -0
- package/src/app-gocardless/banks/ing_ingddeff.js +47 -0
- package/src/app-gocardless/banks/ing_pl_ingbplpw.js +46 -0
- package/src/app-gocardless/banks/integration-bank.js +115 -0
- package/src/app-gocardless/banks/isybank_itbbitmm.js +18 -0
- package/src/app-gocardless/banks/kbc_kredbebb.js +33 -0
- package/src/app-gocardless/banks/lhv-lhvbee22.js +36 -0
- package/src/app-gocardless/banks/mbank_retail_brexplpw.js +56 -0
- package/src/app-gocardless/banks/nationwide_naiagb21.js +46 -0
- package/src/app-gocardless/banks/nbg_ethngraaxxx.js +51 -0
- package/src/app-gocardless/banks/norwegian_xx_norwnok1.js +74 -0
- package/src/app-gocardless/banks/revolut_revolt21.js +37 -0
- package/src/app-gocardless/banks/sandboxfinance_sfin0000.js +28 -0
- package/src/app-gocardless/banks/seb_kort_bank_ab.js +58 -0
- package/src/app-gocardless/banks/seb_privat.js +29 -0
- package/src/app-gocardless/banks/sparnord_spnodk22.js +24 -0
- package/src/app-gocardless/banks/spk_karlsruhe_karsde66.js +61 -0
- package/src/app-gocardless/banks/spk_marburg_biedenkopf_heladef1mar.js +30 -0
- package/src/app-gocardless/banks/spk_worms_alzey_ried_malade51wor.js +19 -0
- package/src/app-gocardless/banks/ssk_dusseldorf_dussdeddxxx.js +50 -0
- package/src/app-gocardless/banks/swedbank_habalv22.js +47 -0
- package/src/app-gocardless/banks/tests/abanca_caglesmm.spec.js +21 -0
- package/src/app-gocardless/banks/tests/abnamro_abnanl2a.spec.js +61 -0
- package/src/app-gocardless/banks/tests/bancsabadell_bsabesbbb.spec.js +53 -0
- package/src/app-gocardless/banks/tests/belfius_gkccbebb.spec.js +22 -0
- package/src/app-gocardless/banks/tests/cbc_cregbebb.spec.js +34 -0
- package/src/app-gocardless/banks/tests/commerzbank_cobadeff.spec.js +110 -0
- package/src/app-gocardless/banks/tests/easybank_bawaatww.spec.js +54 -0
- package/src/app-gocardless/banks/tests/fortuneo_ftnofrp1xxx.spec.js +206 -0
- package/src/app-gocardless/banks/tests/ing_ingddeff.spec.js +302 -0
- package/src/app-gocardless/banks/tests/ing_pl_ingbplpw.spec.js +202 -0
- package/src/app-gocardless/banks/tests/integration_bank.spec.js +158 -0
- package/src/app-gocardless/banks/tests/kbc_kredbebb.spec.js +38 -0
- package/src/app-gocardless/banks/tests/lhv-lhvbee22.spec.js +68 -0
- package/src/app-gocardless/banks/tests/mbank_retail_brexplpw.spec.js +171 -0
- package/src/app-gocardless/banks/tests/nationwide_naiagb21.spec.js +105 -0
- package/src/app-gocardless/banks/tests/nbg_ethngraaxxx.spec.js +48 -0
- package/src/app-gocardless/banks/tests/revolut_revolt21.spec.js +42 -0
- package/src/app-gocardless/banks/tests/sandboxfinance_sfin0000.spec.js +133 -0
- package/src/app-gocardless/banks/tests/spk_marburg_biedenkopf_heladef1mar.spec.js +256 -0
- package/src/app-gocardless/banks/tests/ssk_dusseldorf_dussdeddxxx.spec.js +102 -0
- package/src/app-gocardless/banks/tests/swedbank_habalv22.spec.js +57 -0
- package/src/app-gocardless/banks/tests/virgin_nrnbgb22.spec.js +54 -0
- package/src/app-gocardless/banks/util/extract-payeeName-from-remittanceInfo.js +36 -0
- package/src/app-gocardless/banks/virgin_nrnbgb22.js +39 -0
- package/src/app-gocardless/errors.js +84 -0
- package/src/app-gocardless/gocardless-node.types.ts +497 -0
- package/src/app-gocardless/gocardless.types.ts +93 -0
- package/src/app-gocardless/link.html +18 -0
- package/src/app-gocardless/services/gocardless-service.js +620 -0
- package/src/app-gocardless/services/tests/fixtures.js +181 -0
- package/src/app-gocardless/services/tests/gocardless-service.spec.js +537 -0
- package/src/app-gocardless/tests/bank-factory.spec.js +20 -0
- package/src/app-gocardless/tests/utils.spec.js +162 -0
- package/src/app-gocardless/util/handle-error.js +16 -0
- package/src/app-gocardless/utils.js +45 -0
- package/src/app-openid.js +108 -0
- package/src/app-pluggyai/app-pluggyai.js +215 -0
- package/src/app-pluggyai/pluggyai-service.js +120 -0
- package/src/app-secrets.js +61 -0
- package/src/app-simplefin/app-simplefin.js +418 -0
- package/src/app-sync/errors.js +13 -0
- package/src/app-sync/services/files-service.js +243 -0
- package/src/app-sync/tests/services/files-service.test.js +250 -0
- package/src/app-sync/validation.js +77 -0
- package/src/app-sync.js +391 -0
- package/src/app-sync.test.js +877 -0
- package/src/app.js +145 -0
- package/src/config-types.ts +44 -0
- package/src/db.js +58 -0
- package/src/load-config.js +307 -0
- package/src/migrations.js +36 -0
- package/src/run-migrations.js +8 -0
- package/src/scripts/disable-openid.js +44 -0
- package/src/scripts/enable-openid.js +53 -0
- package/src/scripts/health-check.js +23 -0
- package/src/scripts/reset-password.js +51 -0
- package/src/secrets.test.js +83 -0
- package/src/services/secrets-service.js +94 -0
- package/src/services/user-service.js +272 -0
- package/src/sql/messages.sql +9 -0
- package/src/sync-simple.js +95 -0
- package/src/util/hash.js +5 -0
- package/src/util/middlewares.js +62 -0
- package/src/util/paths.js +13 -0
- package/src/util/payee-name.js +45 -0
- package/src/util/prompt.js +88 -0
- package/src/util/title/index.js +59 -0
- package/src/util/title/lower-case.js +93 -0
- package/src/util/title/specials.js +21 -0
- package/src/util/validate-user.js +68 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import SwedbankHabaLV22 from '../swedbank_habalv22.js';
|
|
2
|
+
|
|
3
|
+
describe('#normalizeTransaction', () => {
|
|
4
|
+
const bookedCardTransaction = {
|
|
5
|
+
transactionId: '2024102900000000-1',
|
|
6
|
+
bookingDate: '2024-10-29',
|
|
7
|
+
valueDate: '2024-10-29',
|
|
8
|
+
transactionAmount: {
|
|
9
|
+
amount: '-22.99',
|
|
10
|
+
currency: 'EUR',
|
|
11
|
+
},
|
|
12
|
+
creditorName: 'SOME CREDITOR NAME',
|
|
13
|
+
remittanceInformationUnstructured:
|
|
14
|
+
'PIRKUMS 424242******4242 28.10.2024 22.99 EUR (111111) SOME CREDITOR NAME',
|
|
15
|
+
bankTransactionCode: 'PMNT-CCRD-POSD',
|
|
16
|
+
internalTransactionId: 'fa000f86afb2cc7678bcff0000000000',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
it('extracts card transaction date', () => {
|
|
20
|
+
expect(
|
|
21
|
+
SwedbankHabaLV22.normalizeTransaction(bookedCardTransaction, true).date,
|
|
22
|
+
).toEqual('2024-10-28');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it.each([
|
|
26
|
+
['regular text', 'Some info'],
|
|
27
|
+
['partial card text', 'PIRKUMS xxx'],
|
|
28
|
+
['null value', null],
|
|
29
|
+
])('normalizes non-card transaction with %s', (_, remittanceInfo) => {
|
|
30
|
+
const transaction = {
|
|
31
|
+
...bookedCardTransaction,
|
|
32
|
+
remittanceInformationUnstructured: remittanceInfo,
|
|
33
|
+
};
|
|
34
|
+
const normalized = SwedbankHabaLV22.normalizeTransaction(transaction, true);
|
|
35
|
+
|
|
36
|
+
expect(normalized.bookingDate).toEqual('2024-10-29');
|
|
37
|
+
expect(normalized.date).toEqual('2024-10-29');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const pendingCardTransaction = {
|
|
41
|
+
transactionId: '2024102900000000-1',
|
|
42
|
+
valueDate: '2024-10-29',
|
|
43
|
+
transactionAmount: {
|
|
44
|
+
amount: '-22.99',
|
|
45
|
+
currency: 'EUR',
|
|
46
|
+
},
|
|
47
|
+
remittanceInformationUnstructured:
|
|
48
|
+
'PIRKUMS 424242******4242 28.10.24 13:37 22.99 EUR (111111) SOME CREDITOR NAME',
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
it('extracts pending card transaction creditor name', () => {
|
|
52
|
+
expect(
|
|
53
|
+
SwedbankHabaLV22.normalizeTransaction(pendingCardTransaction, false)
|
|
54
|
+
.payeeName,
|
|
55
|
+
).toEqual('Some Creditor Name');
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { mockTransactionAmount } from '../../services/tests/fixtures.js';
|
|
2
|
+
import Virgin from '../virgin_nrnbgb22.js';
|
|
3
|
+
|
|
4
|
+
describe('Virgin', () => {
|
|
5
|
+
describe('#normalizeTransaction', () => {
|
|
6
|
+
it('does not alter simple payee information', () => {
|
|
7
|
+
const transaction = {
|
|
8
|
+
bookingDate: '2024-01-01T00:00:00Z',
|
|
9
|
+
remittanceInformationUnstructured: 'DIRECT DEBIT PAYMENT',
|
|
10
|
+
transactionAmount: mockTransactionAmount,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const normalizedTransaction = Virgin.normalizeTransaction(
|
|
14
|
+
transaction,
|
|
15
|
+
true,
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
expect(normalizedTransaction.payeeName).toEqual('Direct Debit Payment');
|
|
19
|
+
expect(normalizedTransaction.notes).toEqual('DIRECT DEBIT PAYMENT');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('formats bank transfer payee and references', () => {
|
|
23
|
+
const transaction = {
|
|
24
|
+
bookingDate: '2024-01-01T00:00:00Z',
|
|
25
|
+
remittanceInformationUnstructured: 'FPS, Joe Bloggs, Food',
|
|
26
|
+
transactionAmount: mockTransactionAmount,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const normalizedTransaction = Virgin.normalizeTransaction(
|
|
30
|
+
transaction,
|
|
31
|
+
true,
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
expect(normalizedTransaction.payeeName).toEqual('Joe Bloggs');
|
|
35
|
+
expect(normalizedTransaction.notes).toEqual('Food');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('removes method information from payee name', () => {
|
|
39
|
+
const transaction = {
|
|
40
|
+
bookingDate: '2024-01-01T00:00:00Z',
|
|
41
|
+
remittanceInformationUnstructured: 'Card 99, Tesco Express',
|
|
42
|
+
transactionAmount: mockTransactionAmount,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const normalizedTransaction = Virgin.normalizeTransaction(
|
|
46
|
+
transaction,
|
|
47
|
+
true,
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
expect(normalizedTransaction.payeeName).toEqual('Tesco Express');
|
|
51
|
+
expect(normalizedTransaction.notes).toEqual('Card 99, Tesco Express');
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
/**
|
|
3
|
+
* Extracts the payee name from the unstructured remittance information string based on pattern detection.
|
|
4
|
+
*
|
|
5
|
+
* This function scans the `remittanceInformationUnstructured` string for the presence of
|
|
6
|
+
* any of the specified patterns and removes the substring from the position of the last
|
|
7
|
+
* occurrence of the most relevant pattern. If no patterns are found, it returns the original string.
|
|
8
|
+
*
|
|
9
|
+
* @param {string} [remittanceInformationUnstructured=''] - The unstructured remittance information from which to extract the payee name.
|
|
10
|
+
* @param {string[]} [patterns=[]] - An array of patterns to look for within the remittance information.
|
|
11
|
+
* These patterns are used to identify and remove unwanted parts of the remittance information.
|
|
12
|
+
* @returns {string} - The extracted payee name, cleaned of any matched patterns, or the original
|
|
13
|
+
* remittance information if no patterns are found.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* const remittanceInfo = 'John Doe Paiement Maestro par Carte de débit CBC 05-09-2024 à 15.43 heures 6703 19XX XXXX X...';
|
|
17
|
+
* const patterns = ['Paiement', 'Domiciliation', 'Transfert', 'Ordre permanent'];
|
|
18
|
+
* const payeeName = extractPayeeNameFromRemittanceInfo(remittanceInfo, patterns); // --> 'John Doe'
|
|
19
|
+
*/
|
|
20
|
+
export function extractPayeeNameFromRemittanceInfo(
|
|
21
|
+
remittanceInformationUnstructured,
|
|
22
|
+
patterns,
|
|
23
|
+
) {
|
|
24
|
+
if (!remittanceInformationUnstructured || !patterns.length) {
|
|
25
|
+
return remittanceInformationUnstructured;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const indexForRemoval = patterns.reduce((maxIndex, pattern) => {
|
|
29
|
+
const index = remittanceInformationUnstructured.lastIndexOf(pattern);
|
|
30
|
+
return index > maxIndex ? index : maxIndex;
|
|
31
|
+
}, -1);
|
|
32
|
+
|
|
33
|
+
return indexForRemoval > -1
|
|
34
|
+
? remittanceInformationUnstructured.substring(0, indexForRemoval).trim()
|
|
35
|
+
: remittanceInformationUnstructured;
|
|
36
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import Fallback from './integration-bank.js';
|
|
2
|
+
|
|
3
|
+
/** @type {import('./bank.interface.js').IBank} */
|
|
4
|
+
export default {
|
|
5
|
+
...Fallback,
|
|
6
|
+
|
|
7
|
+
institutionIds: ['VIRGIN_NRNBGB22'],
|
|
8
|
+
|
|
9
|
+
normalizeTransaction(transaction, booked) {
|
|
10
|
+
const editedTrans = { ...transaction };
|
|
11
|
+
|
|
12
|
+
const transferPrefixes = ['MOB', 'FPS'];
|
|
13
|
+
const methodRegex = /^(Card|WLT)\s\d+/;
|
|
14
|
+
|
|
15
|
+
const parts = transaction.remittanceInformationUnstructured.split(', ');
|
|
16
|
+
|
|
17
|
+
if (transferPrefixes.includes(parts[0])) {
|
|
18
|
+
// Transfer remittance information begins with either "MOB" or "FPS"
|
|
19
|
+
// the second field contains the payee and the third contains the
|
|
20
|
+
// reference
|
|
21
|
+
|
|
22
|
+
editedTrans.creditorName = parts[1];
|
|
23
|
+
editedTrans.debtorName = parts[1];
|
|
24
|
+
editedTrans.remittanceInformationUnstructured = parts[2];
|
|
25
|
+
} else if (parts[0].match(methodRegex)) {
|
|
26
|
+
// The payee is prefixed with the payment method, eg "Card 11, {payee}"
|
|
27
|
+
|
|
28
|
+
editedTrans.creditorName = parts[1];
|
|
29
|
+
editedTrans.debtorName = parts[1];
|
|
30
|
+
} else {
|
|
31
|
+
// Simple payee name
|
|
32
|
+
|
|
33
|
+
editedTrans.creditorName = transaction.remittanceInformationUnstructured;
|
|
34
|
+
editedTrans.debtorName = transaction.remittanceInformationUnstructured;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return Fallback.normalizeTransaction(transaction, booked, editedTrans);
|
|
38
|
+
},
|
|
39
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
export class RequisitionNotLinked extends Error {
|
|
2
|
+
constructor(params = {}) {
|
|
3
|
+
super('Requisition not linked yet');
|
|
4
|
+
this.details = params;
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export class AccountNotLinkedToRequisition extends Error {
|
|
9
|
+
constructor(accountId, requisitionId) {
|
|
10
|
+
super('Provided account id is not linked to given requisition');
|
|
11
|
+
this.details = {
|
|
12
|
+
accountId,
|
|
13
|
+
requisitionId,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class GenericGoCardlessError extends Error {
|
|
19
|
+
constructor(data = {}) {
|
|
20
|
+
super('GoCardless returned error');
|
|
21
|
+
this.details = data;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class GoCardlessClientError extends Error {
|
|
26
|
+
constructor(message, details) {
|
|
27
|
+
super(message);
|
|
28
|
+
this.details = details;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export class InvalidInputDataError extends GoCardlessClientError {
|
|
33
|
+
constructor(response) {
|
|
34
|
+
super('Invalid provided parameters', response);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export class InvalidGoCardlessTokenError extends GoCardlessClientError {
|
|
39
|
+
constructor(response) {
|
|
40
|
+
super('Token is invalid or expired', response);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export class AccessDeniedError extends GoCardlessClientError {
|
|
45
|
+
constructor(response) {
|
|
46
|
+
super('IP address access denied', response);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export class NotFoundError extends GoCardlessClientError {
|
|
51
|
+
constructor(response) {
|
|
52
|
+
super('Resource not found', response);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export class ResourceSuspended extends GoCardlessClientError {
|
|
57
|
+
constructor(response) {
|
|
58
|
+
super(
|
|
59
|
+
'Resource was suspended due to numerous errors that occurred while accessing it',
|
|
60
|
+
response,
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export class RateLimitError extends GoCardlessClientError {
|
|
66
|
+
constructor(response) {
|
|
67
|
+
super(
|
|
68
|
+
'Daily request limit set by the Institution has been exceeded',
|
|
69
|
+
response,
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export class UnknownError extends GoCardlessClientError {
|
|
75
|
+
constructor(response) {
|
|
76
|
+
super('Request to Institution returned an error', response);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export class ServiceError extends GoCardlessClientError {
|
|
81
|
+
constructor(response) {
|
|
82
|
+
super('Institution service unavailable', response);
|
|
83
|
+
}
|
|
84
|
+
}
|