@actual-app/sync-server 25.10.0-nightly.20250919 → 25.10.0-nightly.20250920
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.
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalize BPER Retail BPMOIT22 transactions by extracting a friendly payee
|
|
3
|
+
* while keeping the raw description in the notes field.
|
|
4
|
+
*/
|
|
5
|
+
import Fallback from './integration-bank.js';
|
|
6
|
+
const CARD_PAYMENT_PREFIX = 'PAGAMENTO SU CIRCUITO INTERNAZIONALE';
|
|
7
|
+
const CARD_PAYMENT_SUFFIX = 'Operazione carta';
|
|
8
|
+
const BONIFICO_PREFIX = 'BONIFICO';
|
|
9
|
+
const BONIFICO_ESTERI_PREFIX = 'BONIFICI ESTERI';
|
|
10
|
+
const SDD_PREFIX = 'ADDEBITO SDD';
|
|
11
|
+
const BOLLETTINO_MARKER = 'CREDITORE:';
|
|
12
|
+
const BONIFICO_ORIGINATOR_REGEX = /o\/c:\s*([A-Z0-9\s.'/&-]+?)(?:ABI|BIC|IBAN|a favore di|Num|EUR|$)/i;
|
|
13
|
+
const SDD_PAYEE_REGEX = /ADDEBITO SDD\s+([A-Z0-9\s.'/&-]+?)(?:N:|ID:|$)/i;
|
|
14
|
+
const BOLLETTINO_PAYEE_REGEX = /CREDITORE:\s*([A-Z0-9\s.'/&-]+)/i;
|
|
15
|
+
// Extract payee for card transactions
|
|
16
|
+
function parseCardPayee(description) {
|
|
17
|
+
const [beforeSuffix] = description.split(CARD_PAYMENT_SUFFIX);
|
|
18
|
+
return beforeSuffix.replace(CARD_PAYMENT_PREFIX, '').trim();
|
|
19
|
+
}
|
|
20
|
+
// Extract originator for bonifico (domestic/foreign transfers)
|
|
21
|
+
function parseBonificoOriginator(description) {
|
|
22
|
+
const match = description.match(BONIFICO_ORIGINATOR_REGEX);
|
|
23
|
+
return match ? match[1].trim() : '';
|
|
24
|
+
}
|
|
25
|
+
// Extract creditor for SDD direct debits
|
|
26
|
+
function parseSddPayee(description) {
|
|
27
|
+
const match = description.match(SDD_PAYEE_REGEX);
|
|
28
|
+
return match ? match[1].trim() : '';
|
|
29
|
+
}
|
|
30
|
+
// Extract creditor for bollettini / utilities
|
|
31
|
+
function parseBollettinoPayee(description) {
|
|
32
|
+
const match = description.match(BOLLETTINO_PAYEE_REGEX);
|
|
33
|
+
return match ? match[1].trim() : '';
|
|
34
|
+
}
|
|
35
|
+
function setPayee(editedTransaction, payee) {
|
|
36
|
+
if (!payee) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
editedTransaction.creditorName = payee;
|
|
40
|
+
editedTransaction.debtorName = payee;
|
|
41
|
+
}
|
|
42
|
+
/** @type {import('./bank.interface.js').IBank} */
|
|
43
|
+
const BperRetailBank = {
|
|
44
|
+
...Fallback,
|
|
45
|
+
institutionIds: ['BPER_RETAIL_BPMOIT22'],
|
|
46
|
+
normalizeTransaction(transaction, booked) {
|
|
47
|
+
const editedTrans = { ...transaction };
|
|
48
|
+
const description = (transaction.remittanceInformationUnstructured || '').trim();
|
|
49
|
+
if (description) {
|
|
50
|
+
editedTrans.remittanceInformationUnstructured = description;
|
|
51
|
+
}
|
|
52
|
+
let payee = '';
|
|
53
|
+
if (description.startsWith(CARD_PAYMENT_PREFIX)) {
|
|
54
|
+
payee = parseCardPayee(description);
|
|
55
|
+
}
|
|
56
|
+
else if (description.startsWith(BONIFICO_PREFIX) ||
|
|
57
|
+
description.startsWith(BONIFICO_ESTERI_PREFIX)) {
|
|
58
|
+
payee = parseBonificoOriginator(description);
|
|
59
|
+
}
|
|
60
|
+
else if (description.startsWith(SDD_PREFIX)) {
|
|
61
|
+
payee = parseSddPayee(description);
|
|
62
|
+
}
|
|
63
|
+
else if (description.includes(BOLLETTINO_MARKER)) {
|
|
64
|
+
payee = parseBollettinoPayee(description);
|
|
65
|
+
}
|
|
66
|
+
setPayee(editedTrans, payee);
|
|
67
|
+
return Fallback.normalizeTransaction(transaction, booked, editedTrans);
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
export default BperRetailBank;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import BperRetail from '../bper_retail_bpmoit22.js';
|
|
2
|
+
const bookingDate = '2025-09-17';
|
|
3
|
+
describe('BPER Retail BPMOIT22', () => {
|
|
4
|
+
describe('#normalizeTransaction', () => {
|
|
5
|
+
it('extracts card merchant between the circuit prefix and operation suffix', () => {
|
|
6
|
+
const transaction = {
|
|
7
|
+
bookingDate,
|
|
8
|
+
remittanceInformationUnstructured: 'PAGAMENTO SU CIRCUITO INTERNAZIONALE HEALTHCARE DISTRICT ZX042 METROPOLIS ITA Operazione carta ****8005 del 15.09.2025',
|
|
9
|
+
transactionAmount: { amount: '-17.80', currency: 'EUR' },
|
|
10
|
+
};
|
|
11
|
+
const normalizedTransaction = BperRetail.normalizeTransaction(transaction, true);
|
|
12
|
+
expect(normalizedTransaction.payeeName).toEqual('Healthcare District Zx042 Metropolis Ita');
|
|
13
|
+
expect(normalizedTransaction.notes).toEqual('PAGAMENTO SU CIRCUITO INTERNAZIONALE HEALTHCARE DISTRICT ZX042 METROPOLIS ITA Operazione carta ****8005 del 15.09.2025');
|
|
14
|
+
});
|
|
15
|
+
it('extracts bonifico originator appearing after o/c:', () => {
|
|
16
|
+
const transaction = {
|
|
17
|
+
bookingDate,
|
|
18
|
+
remittanceInformationUnstructured: 'BONIFICO o/c: ACME CONSULTING SRL ABI-CAB: 03015-03200 a favore di Example Recipient Num. Bon.Sepa 252531000195141 Note di cortesia',
|
|
19
|
+
transactionAmount: { amount: '1000.00', currency: 'EUR' },
|
|
20
|
+
};
|
|
21
|
+
const normalizedTransaction = BperRetail.normalizeTransaction(transaction, true);
|
|
22
|
+
expect(normalizedTransaction.payeeName).toEqual('Acme Consulting Srl');
|
|
23
|
+
expect(normalizedTransaction.notes).toEqual('BONIFICO o/c: ACME CONSULTING SRL ABI-CAB: 03015-03200 a favore di Example Recipient Num. Bon.Sepa 252531000195141 Note di cortesia');
|
|
24
|
+
});
|
|
25
|
+
it('extracts foreign bonifico originator before the BIC marker', () => {
|
|
26
|
+
const transaction = {
|
|
27
|
+
bookingDate,
|
|
28
|
+
remittanceInformationUnstructured: 'BONIFICI ESTERI o/c: GLOBAL PARTNERS LTD BIC: EXAMPGB2L a favore di Example Recipient (BPER) Num. Bon.Sepa 252131000238275BE Memo casuale 1.000,00 EUR',
|
|
29
|
+
transactionAmount: { amount: '1000.00', currency: 'EUR' },
|
|
30
|
+
};
|
|
31
|
+
const normalizedTransaction = BperRetail.normalizeTransaction(transaction, true);
|
|
32
|
+
expect(normalizedTransaction.payeeName).toEqual('Global Partners Ltd');
|
|
33
|
+
expect(normalizedTransaction.notes).toEqual('BONIFICI ESTERI o/c: GLOBAL PARTNERS LTD BIC: EXAMPGB2L a favore di Example Recipient (BPER) Num. Bon.Sepa 252131000238275BE Memo casuale 1.000,00 EUR');
|
|
34
|
+
});
|
|
35
|
+
it('extracts creditor for SDD direct debits', () => {
|
|
36
|
+
const transaction = {
|
|
37
|
+
bookingDate,
|
|
38
|
+
remittanceInformationUnstructured: 'ADDEBITO SDD CLOUD HOSTING LTD N: 1057087621/48 ID:0210000049513 Cod.Cl. K309846700/ Fatt. 109993626070 Deb: Example Account Owner',
|
|
39
|
+
transactionAmount: { amount: '-1.22', currency: 'EUR' },
|
|
40
|
+
};
|
|
41
|
+
const normalizedTransaction = BperRetail.normalizeTransaction(transaction, true);
|
|
42
|
+
expect(normalizedTransaction.payeeName).toEqual('Cloud Hosting Ltd');
|
|
43
|
+
expect(normalizedTransaction.notes).toEqual('ADDEBITO SDD CLOUD HOSTING LTD N: 1057087621/48 ID:0210000049513 Cod.Cl. K309846700/ Fatt. 109993626070 Deb: Example Account Owner');
|
|
44
|
+
});
|
|
45
|
+
it('captures bollettino creditor after CREDITORE:', () => {
|
|
46
|
+
const transaction = {
|
|
47
|
+
bookingDate,
|
|
48
|
+
remittanceInformationUnstructured: 'PAGAMENTI DIVERSI DA INTERNET BANKING E CSA PAGAMENTO BOLLETTINO POSTALE 420251388002409360 DEL 22/06/2025 TRAMITE I.B. / CSA TIPO : 896 CCPOST : 000000000000 CREDITORE: UTILITY COMPANY S.P.A.',
|
|
49
|
+
transactionAmount: { amount: '-171.34', currency: 'EUR' },
|
|
50
|
+
};
|
|
51
|
+
const normalizedTransaction = BperRetail.normalizeTransaction(transaction, true);
|
|
52
|
+
expect(normalizedTransaction.payeeName).toEqual('Utility Company S.P.A.');
|
|
53
|
+
expect(normalizedTransaction.notes).toEqual('PAGAMENTI DIVERSI DA INTERNET BANKING E CSA PAGAMENTO BOLLETTINO POSTALE 420251388002409360 DEL 22/06/2025 TRAMITE I.B. / CSA TIPO : 896 CCPOST : 000000000000 CREDITORE: UTILITY COMPANY S.P.A.');
|
|
54
|
+
});
|
|
55
|
+
it('falls back to the original description when no pattern matches', () => {
|
|
56
|
+
const transaction = {
|
|
57
|
+
bookingDate,
|
|
58
|
+
remittanceInformationUnstructured: 'COMPETENZE SPESE ED ONERI',
|
|
59
|
+
transactionAmount: { amount: '-4.90', currency: 'EUR' },
|
|
60
|
+
};
|
|
61
|
+
const normalizedTransaction = BperRetail.normalizeTransaction(transaction, true);
|
|
62
|
+
expect(normalizedTransaction.payeeName).toEqual('Competenze Spese Ed Oneri');
|
|
63
|
+
expect(normalizedTransaction.notes).toEqual('COMPETENZE SPESE ED ONERI');
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@actual-app/sync-server",
|
|
3
|
-
"version": "25.10.0-nightly.
|
|
3
|
+
"version": "25.10.0-nightly.20250920",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "actual syncing server",
|
|
6
6
|
"bin": {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@actual-app/crdt": "2.1.0",
|
|
31
|
-
"@actual-app/web": "25.10.0-nightly.
|
|
31
|
+
"@actual-app/web": "25.10.0-nightly.20250920",
|
|
32
32
|
"bcrypt": "^6.0.0",
|
|
33
33
|
"better-sqlite3": "^12.2.0",
|
|
34
34
|
"convict": "^6.2.4",
|