@actual-app/sync-server 25.7.0-nightly.20250620 → 25.7.0-nightly.20250621

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.
@@ -1,11 +1,12 @@
1
1
  import { title } from '../../util/title/index.js';
2
2
  import Fallback from './integration-bank.js';
3
- const regexCard = /^CARTE \d{2}\/\d{2}\/\d{2} (?<payeeName>.+?)( CB\*)?( ?\d{4,})?$/;
4
- const regexAtmWithdrawal = /^RETRAIT DAB \d{2}\/\d{2}\/\d{2} (?<locationName>.+?) CB\*\d{4,}/;
3
+ const regexCard = /^CARTE (?<date>\d{2}\/\d{2}\/\d{2}) (?<payeeName>.+?)( \d+)?( CB\*\d{4})?$/;
4
+ const regexAtmWithdrawal = /^RETRAIT DAB (?<date>\d{2}\/\d{2}\/\d{2}) (?<locationName>.+?) CB\*\d{4,}/;
5
5
  const regexTransfer = /^VIR /;
6
6
  const regexInstantTransfer = /^VIR INST /;
7
7
  const regexSepa = /^(PRLV|VIR) SEPA /;
8
8
  const regexLoan = /^ECH PRET:/;
9
+ const regexCreditNote = /^AVOIR (?<date>\d{2}\/\d{2}\/\d{2}) (?<payeeName>.+?) CB\*\d{4,}/;
9
10
  /** @type {import('./bank.interface.js').IBank} */
10
11
  export default {
11
12
  ...Fallback,
@@ -13,59 +14,70 @@ export default {
13
14
  normalizeTransaction(transaction, booked) {
14
15
  const editedTrans = { ...transaction };
15
16
  editedTrans.remittanceInformationUnstructuredArray =
16
- // Remove the backslashes that are sometimes present
17
+ // Remove the localisation with backslashes that are sometimes present
17
18
  transaction.remittanceInformationUnstructuredArray
18
- .map(line => line.replace(/\\ ?/g, ' '))
19
+ .map(line => line.replace(/\\.+/g, ''))
19
20
  // Remove an unwanted line that pollutes the remittance information
20
21
  .filter(line => line.startsWith('Réf : ') === false);
21
22
  const infoArray = editedTrans.remittanceInformationUnstructuredArray;
22
23
  const firstLine = infoArray[0];
23
- if (firstLine.match(regexCard)) {
24
+ let match;
25
+ /*
26
+ * Some transactions always have their identifier in the first line (e.g. card),
27
+ * while others have it **randomly** in any of the lines (e.g. transfers).
28
+ */
29
+ // Check the first line for specific patterns
30
+ if ((match = firstLine.match(regexCard))) {
24
31
  // Card transaction
25
- const payeeName = firstLine.replace(regexCard, '$1');
32
+ const payeeName = match.groups.payeeName;
26
33
  editedTrans.payeeName = title(payeeName);
34
+ editedTrans.notes = `Carte ${match.groups.date}`;
35
+ if (infoArray.length > 1) {
36
+ editedTrans.notes += ' ' + infoArray.slice(1).join(' ');
37
+ }
27
38
  }
28
- else if (firstLine.match(regexInstantTransfer)) {
29
- // Instant transfer
30
- editedTrans.payeeName = title(firstLine.replace(regexInstantTransfer, ''));
31
- }
32
- else if (firstLine.match(regexSepa)) {
33
- // SEPA transfer
34
- editedTrans.payeeName = title(firstLine.replace(regexSepa, ''));
35
- }
36
- else if (firstLine.match(regexTransfer) && infoArray.length > 1) {
37
- // Other transfer
38
- // Must be evaluated after the other transfers as they're more specific (here VIR only)
39
- editedTrans.payeeName = title(infoArray[1]);
40
- editedTrans.notes = firstLine.replace(regexTransfer, '');
41
- }
42
- else if (firstLine.match(regexLoan)) {
39
+ else if ((match = firstLine.match(regexLoan))) {
43
40
  // Loan
44
41
  editedTrans.payeeName = 'Prêt bancaire';
45
42
  editedTrans.notes = firstLine;
46
43
  }
47
- else if (firstLine.match(regexAtmWithdrawal)) {
44
+ else if ((match = firstLine.match(regexAtmWithdrawal))) {
48
45
  // ATM withdrawal
49
46
  editedTrans.payeeName = 'Retrait DAB';
50
- editedTrans.notes =
51
- firstLine.match(regexAtmWithdrawal).groups.locationName;
47
+ editedTrans.notes = `Retrait ${match.groups.date} ${match.groups.locationName}`;
52
48
  if (infoArray.length > 1) {
53
- editedTrans.notes += ' ' + infoArray[1];
49
+ editedTrans.notes += ' ' + infoArray.slice(1).join(' ');
54
50
  }
55
51
  }
56
- else {
57
- editedTrans.payeeName = title(firstLine);
52
+ else if ((match = firstLine.match(regexCreditNote))) {
53
+ // Credit note (refund)
54
+ editedTrans.payeeName = title(match.groups.payeeName);
55
+ editedTrans.notes = `Avoir ${match.groups.date}`;
58
56
  }
59
- if (editedTrans.notes === undefined) {
60
- // We managed to extract the payee name, but nothing specific in the notes
61
- if (infoArray.length === 2) {
62
- // If there are only two lines, the second one is the notes
63
- editedTrans.notes = infoArray[1];
57
+ else {
58
+ // For the next patterns, we need to check all lines as the identifier can be anywhere
59
+ if ((match = infoArray.find(line => regexInstantTransfer.test(line)))) {
60
+ // Instant transfer
61
+ editedTrans.payeeName = title(match.replace(regexInstantTransfer, ''));
62
+ editedTrans.notes = infoArray.filter(l => l !== match).join(' ');
63
+ }
64
+ else if ((match = infoArray.find(line => regexSepa.test(line)))) {
65
+ // SEPA transfer
66
+ editedTrans.payeeName = title(match.replace(regexSepa, ''));
67
+ editedTrans.notes = infoArray.filter(l => l !== match).join(' ');
68
+ }
69
+ else if ((match = infoArray.find(line => regexTransfer.test(line)))) {
70
+ // Other transfer
71
+ // Must be evaluated after the other transfers as they're more specific
72
+ // (here VIR only)
73
+ const infoArrayWithoutLine = infoArray.filter(l => l !== match);
74
+ editedTrans.payeeName = title(infoArrayWithoutLine.join(' '));
75
+ editedTrans.notes = match.replace(regexTransfer, '');
64
76
  }
65
77
  else {
66
- // Unfortunately, the order of the lines is not always the same, which causes issues on multiple syncs
67
- // Thus, do not set the notes if we have more than two lines
68
- editedTrans.notes = '';
78
+ // Unknown transaction type
79
+ editedTrans.payeeName = title(firstLine.replace(/ \d+$/, ''));
80
+ editedTrans.notes = infoArray.slice(1).join(' ');
69
81
  }
70
82
  }
71
83
  return Fallback.normalizeTransaction(transaction, booked, editedTrans);
@@ -2,21 +2,26 @@ import BoursoBank from '../boursobank_bousfrppxxx.js';
2
2
  describe('BoursoBank', () => {
3
3
  describe('#normalizeTransaction', () => {
4
4
  it.each([
5
- [['CARTE 01/03/25 PAYEE NAME CB*4567'], 'Payee Name', ''],
6
- [['CARTE 01/03/25 PAYEE NAME'], 'Payee Name', ''],
7
- [['CARTE 01/03/25 PAYEE NAME 7428347'], 'Payee Name', ''],
5
+ [['CARTE 01/03/25 PAYEE NAME CB*4567'], 'Payee Name', 'Carte 01/03/25'],
6
+ [
7
+ ['CARTE 01/03/25 PAYEE NAME 713621 CB*4567'],
8
+ 'Payee Name',
9
+ 'Carte 01/03/25',
10
+ ],
11
+ [['CARTE 01/03/25 PAYEE NAME'], 'Payee Name', 'Carte 01/03/25'],
12
+ [['CARTE 01/03/25 PAYEE NAME 7428347'], 'Payee Name', 'Carte 01/03/25'],
8
13
  [
9
14
  [
10
15
  'CARTE 03/02/25 PAYEE NAME CB*1234',
11
16
  '2,80 NZD / 1 euro = 1,818181818',
12
17
  ],
13
18
  'Payee Name',
14
- '2,80 NZD / 1 euro = 1,818181818',
19
+ 'Carte 03/02/25 2,80 NZD / 1 euro = 1,818181818',
15
20
  ],
16
21
  [
17
22
  ['RETRAIT DAB 01/03/25 My location CB*9876'],
18
23
  'Retrait DAB',
19
- 'My location',
24
+ 'Retrait 01/03/25 My location',
20
25
  ],
21
26
  [
22
27
  [
@@ -24,13 +29,18 @@ describe('BoursoBank', () => {
24
29
  '2,80 NZD / 1 euro = 1,818181818',
25
30
  ],
26
31
  'Retrait DAB',
27
- 'My location 2,80 NZD / 1 euro = 1,818181818',
32
+ 'Retrait 01/03/25 My location 2,80 NZD / 1 euro = 1,818181818',
28
33
  ],
29
34
  [
30
35
  ['VIR Text put by the sender', 'PAYEE NAME'],
31
36
  'Payee Name',
32
37
  'Text put by the sender',
33
38
  ],
39
+ [
40
+ ['PAYEE NAME', 'VIR Text put by the sender'],
41
+ 'Payee Name',
42
+ 'Text put by the sender',
43
+ ],
34
44
  [
35
45
  [
36
46
  'VIR Text put by the sender',
@@ -49,11 +59,31 @@ describe('BoursoBank', () => {
49
59
  'Payee Name',
50
60
  'Text put by the sender',
51
61
  ],
62
+ [
63
+ [
64
+ 'Réf : SOME TEXT PUT BY THE BANK',
65
+ 'VIR Text put by the sender',
66
+ 'PAYEE NAME',
67
+ ],
68
+ 'Payee Name',
69
+ 'Text put by the sender',
70
+ ],
52
71
  [
53
72
  ['VIR INST PAYEE NAME', 'Text put by the sender'],
54
73
  'Payee Name',
55
74
  'Text put by the sender',
56
75
  ],
76
+ [
77
+ ['Text put by the sender', 'VIR INST PAYEE NAME'],
78
+ 'Payee Name',
79
+ 'Text put by the sender',
80
+ ],
81
+ [['VIR INST PAYEE NAME'], 'Payee Name', ''],
82
+ [
83
+ ['PAYEE NAME', 'VIR Text put by the sender'],
84
+ 'Payee Name',
85
+ 'Text put by the sender',
86
+ ],
57
87
  [
58
88
  [
59
89
  'VIR SEPA PAYEE NAME',
@@ -62,7 +92,17 @@ describe('BoursoBank', () => {
62
92
  'YET ANOTHER TEXT',
63
93
  ],
64
94
  'Payee Name',
65
- '',
95
+ 'SOME TEXT ANOTHER TEXT YET ANOTHER TEXT',
96
+ ],
97
+ [
98
+ [
99
+ 'SOME TEXT',
100
+ 'ANOTHER TEXT',
101
+ 'VIR SEPA PAYEE NAME',
102
+ 'YET ANOTHER TEXT',
103
+ ],
104
+ 'Payee Name',
105
+ 'SOME TEXT ANOTHER TEXT YET ANOTHER TEXT',
66
106
  ],
67
107
  [
68
108
  [
@@ -73,15 +113,28 @@ describe('BoursoBank', () => {
73
113
  'PRELEVEMENT FOO BAR BAZ du',
74
114
  ],
75
115
  'Payee Name',
76
- '',
116
+ 'HERE IS SOMETHING SOME OTHER TEXT 30/04/2025 PRELEVEMENT FOO BAR BAZ du',
117
+ ],
118
+ [
119
+ [
120
+ '30/05/2025',
121
+ 'SOME.TEXT.123.456',
122
+ 'PRELEVEMENT FOO BAR BAZ du',
123
+ 'PRLV SEPA Payee Name',
124
+ 'ABC 1934821371',
125
+ ],
126
+ 'Payee Name',
127
+ '30/05/2025 SOME.TEXT.123.456 PRELEVEMENT FOO BAR BAZ du ABC 1934821371',
77
128
  ],
78
129
  [
79
130
  ['ECH PRET:1823918329832913'],
80
131
  'Prêt bancaire',
81
132
  'ECH PRET:1823918329832913',
82
133
  ],
83
- [['PAYEE NAME'], 'Payee Name', ''],
84
- [['PAYEE\\NAME\\ BIS'], 'Payee Name Bis', ''],
134
+ [['PAYEE NAME 411'], 'Payee Name', ''],
135
+ [['PAYEE NAME\\PARIS\\ FR'], 'Payee Name', ''],
136
+ [['PAYEE NAME 1\\PARIS\\ FR'], 'Payee Name', ''],
137
+ [['AVOIR 17/06/25 PAYEE NAME CB*1234'], 'Payee Name', 'Avoir 17/06/25'],
85
138
  ])('normalizes transaction with %s', (remittanceInformationUnstructuredArray, expectedPayeeName, expectedNotes) => {
86
139
  const transaction = {
87
140
  transactionId: '1234567890',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@actual-app/sync-server",
3
- "version": "25.7.0-nightly.20250620",
3
+ "version": "25.7.0-nightly.20250621",
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.7.0-nightly.20250620",
31
+ "@actual-app/web": "25.7.0-nightly.20250621",
32
32
  "bcrypt": "^5.1.1",
33
33
  "better-sqlite3": "^11.10.0",
34
34
  "convict": "^6.2.4",