lib-fints 1.3.1 → 1.4.1

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.
Files changed (90) hide show
  1. package/README.md +43 -40
  2. package/dist/camtParser.js +412 -0
  3. package/dist/client.js +65 -82
  4. package/dist/dataGroups/SepaAccount.js +17 -0
  5. package/dist/dataGroups/SepaAccountParameters.js +16 -0
  6. package/dist/dialog.js +126 -108
  7. package/dist/httpClient.js +17 -3
  8. package/dist/index.js +1 -0
  9. package/dist/interactions/balanceInteraction.js +1 -1
  10. package/dist/interactions/customerInteraction.js +6 -3
  11. package/dist/interactions/endDialogInteraction.js +17 -0
  12. package/dist/interactions/initDialogInteraction.js +18 -8
  13. package/dist/interactions/sepaAccountInteraction.js +48 -0
  14. package/dist/interactions/statementInteractionCAMT.js +54 -0
  15. package/dist/interactions/{statementInteraction.js → statementInteractionMT940.js} +11 -8
  16. package/dist/interactions/tanMediaInteraction.js +4 -0
  17. package/dist/segments/HICAZ.js +22 -0
  18. package/dist/segments/HICAZS.js +19 -0
  19. package/dist/segments/HISPA.js +14 -0
  20. package/dist/segments/HISPAS.js +14 -0
  21. package/dist/segments/HKCAZ.js +28 -0
  22. package/dist/segments/HKSPA.js +21 -0
  23. package/dist/segments/registry.js +16 -2
  24. package/dist/statement.js +1 -0
  25. package/dist/tests/HICAZS.test.js +18 -0
  26. package/dist/tests/HKCAZ.test.js +47 -0
  27. package/dist/tests/camtParser.test.js +673 -0
  28. package/dist/tests/client.test.js +642 -23
  29. package/dist/tests/dialog.test.js +234 -0
  30. package/dist/types/bankAccount.d.ts +2 -3
  31. package/dist/types/bankAccount.d.ts.map +1 -1
  32. package/dist/types/camtParser.d.ts +31 -0
  33. package/dist/types/camtParser.d.ts.map +1 -0
  34. package/dist/types/client.d.ts +10 -10
  35. package/dist/types/client.d.ts.map +1 -1
  36. package/dist/types/config.d.ts +2 -1
  37. package/dist/types/config.d.ts.map +1 -1
  38. package/dist/types/dataGroups/SepaAccount.d.ts +11 -0
  39. package/dist/types/dataGroups/SepaAccount.d.ts.map +1 -0
  40. package/dist/types/dataGroups/SepaAccountParameters.d.ts +13 -0
  41. package/dist/types/dataGroups/SepaAccountParameters.d.ts.map +1 -0
  42. package/dist/types/dialog.d.ts +12 -8
  43. package/dist/types/dialog.d.ts.map +1 -1
  44. package/dist/types/httpClient.d.ts +2 -1
  45. package/dist/types/httpClient.d.ts.map +1 -1
  46. package/dist/types/index.d.ts +2 -1
  47. package/dist/types/index.d.ts.map +1 -1
  48. package/dist/types/interactions/customerInteraction.d.ts +7 -3
  49. package/dist/types/interactions/customerInteraction.d.ts.map +1 -1
  50. package/dist/types/interactions/endDialogInteraction.d.ts +13 -0
  51. package/dist/types/interactions/endDialogInteraction.d.ts.map +1 -0
  52. package/dist/types/interactions/initDialogInteraction.d.ts +2 -3
  53. package/dist/types/interactions/initDialogInteraction.d.ts.map +1 -1
  54. package/dist/types/interactions/sepaAccountInteraction.d.ts +17 -0
  55. package/dist/types/interactions/sepaAccountInteraction.d.ts.map +1 -0
  56. package/dist/types/interactions/{statementInteraction.d.ts → statementInteractionCAMT.d.ts} +4 -7
  57. package/dist/types/interactions/statementInteractionCAMT.d.ts.map +1 -0
  58. package/dist/types/interactions/statementInteractionMT940.d.ts +13 -0
  59. package/dist/types/interactions/statementInteractionMT940.d.ts.map +1 -0
  60. package/dist/types/interactions/tanMediaInteraction.d.ts.map +1 -1
  61. package/dist/types/mt940parser.d.ts +2 -38
  62. package/dist/types/mt940parser.d.ts.map +1 -1
  63. package/dist/types/segments/HICAZ.d.ts +22 -0
  64. package/dist/types/segments/HICAZ.d.ts.map +1 -0
  65. package/dist/types/segments/HICAZS.d.ts +17 -0
  66. package/dist/types/segments/HICAZS.d.ts.map +1 -0
  67. package/dist/types/segments/HISPA.d.ts +17 -0
  68. package/dist/types/segments/HISPA.d.ts.map +1 -0
  69. package/dist/types/segments/HISPAS.d.ts +14 -0
  70. package/dist/types/segments/HISPAS.d.ts.map +1 -0
  71. package/dist/types/segments/HKCAZ.d.ts +27 -0
  72. package/dist/types/segments/HKCAZ.d.ts.map +1 -0
  73. package/dist/types/segments/HKKAZ.d.ts +1 -1
  74. package/dist/types/segments/HKSAL.d.ts +1 -1
  75. package/dist/types/segments/HKSPA.d.ts +21 -0
  76. package/dist/types/segments/HKSPA.d.ts.map +1 -0
  77. package/dist/types/segments/HKTAN.d.ts +1 -1
  78. package/dist/types/segments/registry.d.ts.map +1 -1
  79. package/dist/types/statement.d.ts +39 -0
  80. package/dist/types/statement.d.ts.map +1 -0
  81. package/dist/types/tests/HICAZS.test.d.ts +2 -0
  82. package/dist/types/tests/HICAZS.test.d.ts.map +1 -0
  83. package/dist/types/tests/HKCAZ.test.d.ts +2 -0
  84. package/dist/types/tests/HKCAZ.test.d.ts.map +1 -0
  85. package/dist/types/tests/camtParser.test.d.ts +2 -0
  86. package/dist/types/tests/camtParser.test.d.ts.map +1 -0
  87. package/dist/types/tests/dialog.test.d.ts +2 -0
  88. package/dist/types/tests/dialog.test.d.ts.map +1 -0
  89. package/package.json +4 -1
  90. package/dist/types/interactions/statementInteraction.d.ts.map +0 -1
@@ -0,0 +1,673 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { CamtParser } from '../camtParser.js';
3
+ describe('CamtParser', () => {
4
+ it('should parse CAMT.052 XML with balances and transactions', () => {
5
+ const camtXml = `<?xml version="1.0" encoding="UTF-8"?>
6
+ <Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.052.001.08">
7
+ <BkToCstmrAcctRpt>
8
+ <GrpHdr>
9
+ <MsgId>camt52_20131118101510__ONLINEBA</MsgId>
10
+ <CreDtTm>2013-11-18T10:15:10+01:00</CreDtTm>
11
+ </GrpHdr>
12
+ <Rpt>
13
+ <Id>camt052_ONLINEBA</Id>
14
+ <ElctrncSeqNb>00001</ElctrncSeqNb>
15
+ <CreDtTm>2013-11-18T10:15:10+01:00</CreDtTm>
16
+ <Acct>
17
+ <Id>
18
+ <IBAN>DE06940594210000027227</IBAN>
19
+ </Id>
20
+ <Ccy>EUR</Ccy>
21
+ </Acct>
22
+ <Bal>
23
+ <Tp>
24
+ <CdOrPrtry>
25
+ <Cd>PRCD</Cd>
26
+ </CdOrPrtry>
27
+ </Tp>
28
+ <Amt Ccy="EUR">1000.00</Amt>
29
+ <CdtDbtInd>CRDT</CdtDbtInd>
30
+ <Dt>
31
+ <Dt>2013-10-31</Dt>
32
+ </Dt>
33
+ </Bal>
34
+ <Bal>
35
+ <Tp>
36
+ <CdOrPrtry>
37
+ <Cd>CLBD</Cd>
38
+ </CdOrPrtry>
39
+ </Tp>
40
+ <Amt Ccy="EUR">1500.00</Amt>
41
+ <CdtDbtInd>CRDT</CdtDbtInd>
42
+ <Dt>
43
+ <Dt>2013-11-04</Dt>
44
+ </Dt>
45
+ </Bal>
46
+ <Ntry>
47
+ <Amt>500.00</Amt>
48
+ <CdtDbtInd>CRDT</CdtDbtInd>
49
+ <BookgDt>
50
+ <Dt>2013-11-01</Dt>
51
+ </BookgDt>
52
+ <ValDt>
53
+ <Dt>2013-11-01</Dt>
54
+ </ValDt>
55
+ <AcctSvcrRef>TXN001</AcctSvcrRef>
56
+ <BkTxCd>
57
+ <Prtry>
58
+ <Cd>TRF</Cd>
59
+ </Prtry>
60
+ </BkTxCd>
61
+ <NtryDtls>
62
+ <TxDtls>
63
+ <Refs>
64
+ <EndToEndId>E2E123</EndToEndId>
65
+ <MndtId>MANDT001</MndtId>
66
+ </Refs>
67
+ <RmtInf>
68
+ <Ustrd>Test payment</Ustrd>
69
+ </RmtInf>
70
+ <RltdPties>
71
+ <Dbtr>
72
+ <Nm>John Doe</Nm>
73
+ </Dbtr>
74
+ <DbtrAcct>
75
+ <Id>
76
+ <IBAN>DE12345678901234567890</IBAN>
77
+ </Id>
78
+ </DbtrAcct>
79
+ <Cdtr>
80
+ <Nm>Jane Doe</Nm>
81
+ </Cdtr>
82
+ <CdtrAcct>
83
+ <Id>
84
+ <IBAN>DE12345678901234567891</IBAN>
85
+ </Id>
86
+ </CdtrAcct>
87
+ </RltdPties>
88
+ <RltdAgts>
89
+ <DbtrAgt>
90
+ <FinInstnId>
91
+ <BIC>BYLADEM1001</BIC>
92
+ </FinInstnId>
93
+ </DbtrAgt>
94
+ <CdtrAgt>
95
+ <FinInstnId>
96
+ <BIC>DEUTDEFF</BIC>
97
+ </FinInstnId>
98
+ </CdtrAgt>
99
+ </RltdAgts>
100
+ </TxDtls>
101
+ </NtryDtls>
102
+ </Ntry>
103
+ </Rpt>
104
+ </BkToCstmrAcctRpt>
105
+ </Document>`;
106
+ const parser = new CamtParser(camtXml);
107
+ const statements = parser.parse();
108
+ expect(statements).toHaveLength(1);
109
+ const statement = statements[0];
110
+ // Check all Statement fields
111
+ expect(statement.account).toBe('DE06940594210000027227');
112
+ expect(statement.number).toBe('camt052_ONLINEBA');
113
+ expect(statement.transactionReference).toBe('00001');
114
+ // Verify optional fields that are not set
115
+ expect(statement.relatedReference).toBeUndefined();
116
+ expect(statement.availableBalance).toBeUndefined();
117
+ expect(statement.forwardBalances).toBeUndefined();
118
+ // Check balances with all fields
119
+ expect(statement.openingBalance).toBeDefined();
120
+ expect(statement.openingBalance.value).toBe(1000.0);
121
+ expect(statement.openingBalance.currency).toBe('EUR');
122
+ expect(statement.openingBalance.date).toBeInstanceOf(Date);
123
+ expect(statement.openingBalance.date.getFullYear()).toBe(2013);
124
+ expect(statement.openingBalance.date.getMonth()).toBe(9); // October (0-based)
125
+ expect(statement.openingBalance.date.getDate()).toBe(31);
126
+ expect(statement.closingBalance).toBeDefined();
127
+ expect(statement.closingBalance.value).toBe(1500.0);
128
+ expect(statement.closingBalance.currency).toBe('EUR');
129
+ expect(statement.closingBalance.date).toBeInstanceOf(Date);
130
+ expect(statement.closingBalance.date.getFullYear()).toBe(2013);
131
+ expect(statement.closingBalance.date.getMonth()).toBe(10); // November (0-based)
132
+ expect(statement.closingBalance.date.getDate()).toBe(4);
133
+ // Check transactions
134
+ expect(statement.transactions).toHaveLength(1);
135
+ const transaction = statement.transactions[0];
136
+ // Check all Transaction fields filled by the parser
137
+ expect(transaction.amount).toBe(500.0);
138
+ expect(transaction.customerReference).toBe('E2E123');
139
+ expect(transaction.bankReference).toBe('TXN001');
140
+ expect(transaction.purpose).toBe('Test payment');
141
+ expect(transaction.remoteName).toBe('John Doe');
142
+ expect(transaction.remoteAccountNumber).toBe('DE12345678901234567890');
143
+ expect(transaction.remoteBankId).toBe('BYLADEM1001'); // Credit transaction uses DbtrAgt BIC
144
+ expect(transaction.e2eReference).toBe('E2E123');
145
+ expect(transaction.mandateReference).toBe('MANDT001');
146
+ // Check date fields
147
+ expect(transaction.valueDate).toBeInstanceOf(Date);
148
+ expect(transaction.valueDate.getFullYear()).toBe(2013);
149
+ expect(transaction.valueDate.getMonth()).toBe(10); // November (0-based)
150
+ expect(transaction.valueDate.getDate()).toBe(1);
151
+ expect(transaction.entryDate).toBeInstanceOf(Date);
152
+ expect(transaction.entryDate.getFullYear()).toBe(2013);
153
+ expect(transaction.entryDate.getMonth()).toBe(10); // November (0-based)
154
+ expect(transaction.entryDate.getDate()).toBe(1);
155
+ // Check transaction type and code fields
156
+ expect(transaction.fundsCode).toBe('CRDT'); // Credit/debit indicator
157
+ expect(transaction.transactionType).toBe(''); // Should be empty as no family code in BkTxCd
158
+ expect(transaction.transactionCode).toBe(''); // Parser only handles structured BkTxCd, not Prtry format
159
+ // Check additional information fields
160
+ expect(transaction.additionalInformation).toBe(''); // No AddtlNtryInf in this test
161
+ expect(transaction.bookingText).toBe(''); // Should match additionalInformation
162
+ // Verify optional fields not set in this test
163
+ expect(transaction.primeNotesNr).toBeUndefined();
164
+ expect(transaction.remoteIdentifier).toBeUndefined();
165
+ expect(transaction.client).toBeUndefined();
166
+ expect(transaction.textKeyExtension).toBeUndefined();
167
+ });
168
+ it('should handle debit transactions correctly', () => {
169
+ const camtXml = `<?xml version="1.0" encoding="UTF-8"?>
170
+ <Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.052.001.08">
171
+ <BkToCstmrAcctRpt>
172
+ <Rpt>
173
+ <Id>test</Id>
174
+ <Acct>
175
+ <Id>
176
+ <IBAN>DE06940594210000027227</IBAN>
177
+ </Id>
178
+ </Acct>
179
+ <Bal>
180
+ <Tp><CdOrPrtry><Cd>PRCD</Cd></CdOrPrtry></Tp>
181
+ <Amt Ccy="EUR">1000.00</Amt>
182
+ <CdtDbtInd>CRDT</CdtDbtInd>
183
+ <Dt><Dt>2013-10-31</Dt></Dt>
184
+ </Bal>
185
+ <Bal>
186
+ <Tp><CdOrPrtry><Cd>CLBD</Cd></CdOrPrtry></Tp>
187
+ <Amt Ccy="EUR">800.00</Amt>
188
+ <CdtDbtInd>CRDT</CdtDbtInd>
189
+ <Dt><Dt>2013-11-01</Dt></Dt>
190
+ </Bal>
191
+ <Ntry>
192
+ <Amt>200.00</Amt>
193
+ <CdtDbtInd>DBIT</CdtDbtInd>
194
+ <BookgDt><Dt>2013-11-01</Dt></BookgDt>
195
+ <ValDt><Dt>2013-11-01</Dt></ValDt>
196
+ <AcctSvcrRef>TXN002</AcctSvcrRef>
197
+ <NtryDtls>
198
+ <TxDtls>
199
+ <Refs>
200
+ <EndToEndId>485315597247918</EndToEndId>
201
+ </Refs>
202
+ <AmtDtls>
203
+ <TxAmt>
204
+ <Amt Ccy="EUR">5.83</Amt>
205
+ </TxAmt>
206
+ </AmtDtls>
207
+ <RltdPties>
208
+ <Dbtr>
209
+ <Nm>Jane Doe</Nm>
210
+ </Dbtr>
211
+ <DbtrAcct>
212
+ <Id>
213
+ <IBAN>DE12345678901234567891</IBAN>
214
+ </Id>
215
+ </DbtrAcct>
216
+ <Cdtr>
217
+ <Nm>John Doe</Nm>
218
+ </Cdtr>
219
+ <CdtrAcct>
220
+ <Id>
221
+ <IBAN>DE12345678901234567890</IBAN>
222
+ </Id>
223
+ </CdtrAcct>
224
+ </RltdPties>
225
+ <RltdAgts>
226
+ <DbtrAgt>
227
+ <FinInstnId>
228
+ <BIC>SOGEDEFF</BIC>
229
+ </FinInstnId>
230
+ </DbtrAgt>
231
+ <CdtrAgt>
232
+ <FinInstnId>
233
+ <BIC>DEUTDEFF</BIC>
234
+ </FinInstnId>
235
+ </CdtrAgt>
236
+ </RltdAgts>
237
+ <Purp>
238
+ <Cd>IDCP</Cd>
239
+ </Purp>
240
+ <RmtInf>
241
+ <Ustrd>Test payment</Ustrd>
242
+ </RmtInf>
243
+ </TxDtls>
244
+ </NtryDtls>
245
+ <AddtlNtryInf>Additional Info</AddtlNtryInf>
246
+ </Ntry>
247
+ </Rpt>
248
+ </BkToCstmrAcctRpt>
249
+ </Document>`;
250
+ const parser = new CamtParser(camtXml);
251
+ const statements = parser.parse();
252
+ expect(statements).toHaveLength(1);
253
+ expect(statements[0].transactions).toHaveLength(1);
254
+ expect(statements[0].transactions[0].amount).toBe(-200.0); // Should be negative for debit
255
+ const transaction = statements[0].transactions[0];
256
+ // Comprehensive Transaction field checks for debit transaction
257
+ expect(transaction.purpose).toBe('Test payment');
258
+ expect(transaction.remoteName).toBe('John Doe');
259
+ expect(transaction.remoteAccountNumber).toBe('DE12345678901234567890');
260
+ expect(transaction.remoteBankId).toBe('DEUTDEFF'); // Debit transaction uses CdtrAgt BIC
261
+ // Check all other fields for debit transaction
262
+ expect(transaction.customerReference).toBe('485315597247918');
263
+ expect(transaction.bankReference).toBe('TXN002');
264
+ expect(transaction.e2eReference).toBe('485315597247918');
265
+ expect(transaction.mandateReference).toBe(''); // No MndtId in this test
266
+ // Date fields
267
+ expect(transaction.valueDate).toBeInstanceOf(Date);
268
+ expect(transaction.valueDate.getFullYear()).toBe(2013);
269
+ expect(transaction.entryDate).toBeInstanceOf(Date);
270
+ expect(transaction.entryDate.getFullYear()).toBe(2013);
271
+ // Transaction type indicators
272
+ expect(transaction.fundsCode).toBe('DBIT'); // Debit indicator
273
+ expect(transaction.transactionType).toBe(''); // No family code
274
+ expect(transaction.transactionCode).toBe(''); // No BkTxCd in this test
275
+ // Additional info fields
276
+ expect(transaction.additionalInformation).toBe('Additional Info');
277
+ expect(transaction.bookingText).toBe('Additional Info');
278
+ });
279
+ it('should handle empty or invalid XML gracefully', () => {
280
+ const parser = new CamtParser('invalid xml');
281
+ expect(() => parser.parse()).toThrow(); // Should throw error for invalid XML
282
+ });
283
+ it('should handle multiple reports', () => {
284
+ const camtXml = `<?xml version="1.0" encoding="UTF-8"?>
285
+ <Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.052.001.08">
286
+ <BkToCstmrAcctRpt>
287
+ <Rpt>
288
+ <Id>report1</Id>
289
+ <Acct><Id><IBAN>DE11111111111111111111</IBAN></Id></Acct>
290
+ <Bal><Tp><CdOrPrtry><Cd>PRCD</Cd></CdOrPrtry></Tp><Amt Ccy="EUR">100.00</Amt><CdtDbtInd>CRDT</CdtDbtInd><Dt><Dt>2023-01-01</Dt></Dt></Bal>
291
+ <Bal><Tp><CdOrPrtry><Cd>CLBD</Cd></CdOrPrtry></Tp><Amt Ccy="EUR">200.00</Amt><CdtDbtInd>CRDT</CdtDbtInd><Dt><Dt>2023-01-01</Dt></Dt></Bal>
292
+ </Rpt>
293
+ <Rpt>
294
+ <Id>report2</Id>
295
+ <Acct><Id><IBAN>DE22222222222222222222</IBAN></Id></Acct>
296
+ <Bal><Tp><CdOrPrtry><Cd>PRCD</Cd></CdOrPrtry></Tp><Amt Ccy="EUR">300.00</Amt><CdtDbtInd>CRDT</CdtDbtInd><Dt><Dt>2023-01-01</Dt></Dt></Bal>
297
+ <Bal><Tp><CdOrPrtry><Cd>CLBD</Cd></CdOrPrtry></Tp><Amt Ccy="EUR">400.00</Amt><CdtDbtInd>CRDT</CdtDbtInd><Dt><Dt>2023-01-01</Dt></Dt></Bal>
298
+ </Rpt>
299
+ </BkToCstmrAcctRpt>
300
+ </Document>`;
301
+ const parser = new CamtParser(camtXml);
302
+ const statements = parser.parse();
303
+ expect(statements).toHaveLength(2);
304
+ expect(statements[0].account).toBe('DE11111111111111111111');
305
+ expect(statements[1].account).toBe('DE22222222222222222222');
306
+ });
307
+ it('should parse comprehensive CAMT XML with all possible fields and bank transaction codes', () => {
308
+ const camtXml = `<?xml version="1.0" encoding="UTF-8"?>
309
+ <Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.052.001.08">
310
+ <BkToCstmrAcctRpt>
311
+ <GrpHdr>
312
+ <MsgId>comprehensive_test</MsgId>
313
+ <CreDtTm>2023-12-22T14:30:00+01:00</CreDtTm>
314
+ </GrpHdr>
315
+ <Rpt>
316
+ <Id>COMPREHENSIVE_TEST</Id>
317
+ <ElctrncSeqNb>987654321</ElctrncSeqNb>
318
+ <CreDtTm>2023-12-22T14:30:00+01:00</CreDtTm>
319
+ <Acct>
320
+ <Id>
321
+ <IBAN>DE89370400440532013000</IBAN>
322
+ </Id>
323
+ <Ccy>EUR</Ccy>
324
+ </Acct>
325
+ <Bal>
326
+ <Tp>
327
+ <CdOrPrtry>
328
+ <Cd>PRCD</Cd>
329
+ </CdOrPrtry>
330
+ </Tp>
331
+ <Amt Ccy="EUR">5000.50</Amt>
332
+ <CdtDbtInd>CRDT</CdtDbtInd>
333
+ <Dt>
334
+ <Dt>2023-12-21</Dt>
335
+ </Dt>
336
+ </Bal>
337
+ <Bal>
338
+ <Tp>
339
+ <CdOrPrtry>
340
+ <Cd>CLBD</Cd>
341
+ </CdOrPrtry>
342
+ </Tp>
343
+ <Amt Ccy="EUR">4750.75</Amt>
344
+ <CdtDbtInd>CRDT</CdtDbtInd>
345
+ <Dt>
346
+ <Dt>2023-12-22</Dt>
347
+ </Dt>
348
+ </Bal>
349
+ <Bal>
350
+ <Tp>
351
+ <CdOrPrtry>
352
+ <Cd>ITBD</Cd>
353
+ </CdOrPrtry>
354
+ </Tp>
355
+ <Amt Ccy="EUR">4500.00</Amt>
356
+ <CdtDbtInd>CRDT</CdtDbtInd>
357
+ <Dt>
358
+ <Dt>2023-12-22</Dt>
359
+ </Dt>
360
+ </Bal>
361
+ <Ntry>
362
+ <Amt>249.75</Amt>
363
+ <CdtDbtInd>DBIT</CdtDbtInd>
364
+ <BookgDt>
365
+ <Dt>2023-12-22</Dt>
366
+ </BookgDt>
367
+ <ValDt>
368
+ <Dt>2023-12-22</Dt>
369
+ </ValDt>
370
+ <AcctSvcrRef>BANK-REF-123456</AcctSvcrRef>
371
+ <BkTxCd>
372
+ <Domn>
373
+ <Cd>PMNT</Cd>
374
+ <Fmly>
375
+ <Cd>ICDT</Cd>
376
+ <SubFmlyCd>ESCT</SubFmlyCd>
377
+ </Fmly>
378
+ </Domn>
379
+ </BkTxCd>
380
+ <NtryDtls>
381
+ <TxDtls>
382
+ <Refs>
383
+ <EndToEndId>NOTPROVIDED</EndToEndId>
384
+ <TxId>TXN-ID-789</TxId>
385
+ <MndtId>MANDATE-REF-456</MndtId>
386
+ </Refs>
387
+ <AmtDtls>
388
+ <TxAmt>
389
+ <Amt Ccy="EUR">249.75</Amt>
390
+ </TxAmt>
391
+ </AmtDtls>
392
+ <RmtInf>
393
+ <Ustrd>SEPA Instant Transfer Payment Reference Text</Ustrd>
394
+ </RmtInf>
395
+ <RltdPties>
396
+ <Dbtr>
397
+ <Nm>Max Mustermann</Nm>
398
+ </Dbtr>
399
+ <DbtrAcct>
400
+ <Id>
401
+ <IBAN>DE89370400440532013000</IBAN>
402
+ </Id>
403
+ </DbtrAcct>
404
+ <Cdtr>
405
+ <Nm>Erika Musterfrau</Nm>
406
+ </Cdtr>
407
+ <CdtrAcct>
408
+ <Id>
409
+ <IBAN>DE12500105170648489890</IBAN>
410
+ </Id>
411
+ </CdtrAcct>
412
+ </RltdPties>
413
+ <RltdAgts>
414
+ <DbtrAgt>
415
+ <FinInstnId>
416
+ <BIC>COBADEFFXXX</BIC>
417
+ </FinInstnId>
418
+ </DbtrAgt>
419
+ <CdtrAgt>
420
+ <FinInstnId>
421
+ <BIC>INGDDEFFXXX</BIC>
422
+ </FinInstnId>
423
+ </CdtrAgt>
424
+ </RltdAgts>
425
+ <Purp>
426
+ <Cd>CBFF</Cd>
427
+ </Purp>
428
+ </TxDtls>
429
+ </NtryDtls>
430
+ <AddtlNtryInf>Additional payment information from bank</AddtlNtryInf>
431
+ </Ntry>
432
+ </Rpt>
433
+ </BkToCstmrAcctRpt>
434
+ </Document>`;
435
+ const parser = new CamtParser(camtXml);
436
+ const statements = parser.parse();
437
+ expect(statements).toHaveLength(1);
438
+ const statement = statements[0];
439
+ // Test all Statement fields with comprehensive data
440
+ expect(statement.account).toBe('DE89370400440532013000');
441
+ expect(statement.number).toBe('COMPREHENSIVE_TEST');
442
+ expect(statement.transactionReference).toBe('987654321');
443
+ expect(statement.relatedReference).toBeUndefined();
444
+ expect(statement.forwardBalances).toBeUndefined();
445
+ // Test opening balance with exact date parsing
446
+ expect(statement.openingBalance).toBeDefined();
447
+ expect(statement.openingBalance.value).toBe(5000.5);
448
+ expect(statement.openingBalance.currency).toBe('EUR');
449
+ expect(statement.openingBalance.date.getFullYear()).toBe(2023);
450
+ expect(statement.openingBalance.date.getMonth()).toBe(11); // December (0-based)
451
+ expect(statement.openingBalance.date.getDate()).toBe(21);
452
+ // Test closing balance
453
+ expect(statement.closingBalance).toBeDefined();
454
+ expect(statement.closingBalance.value).toBe(4750.75);
455
+ expect(statement.closingBalance.currency).toBe('EUR');
456
+ expect(statement.closingBalance.date.getFullYear()).toBe(2023);
457
+ expect(statement.closingBalance.date.getMonth()).toBe(11); // December (0-based)
458
+ expect(statement.closingBalance.date.getDate()).toBe(22);
459
+ // Test available balance (ITBD)
460
+ expect(statement.availableBalance).toBeDefined();
461
+ expect(statement.availableBalance.value).toBe(4500.0);
462
+ expect(statement.availableBalance.currency).toBe('EUR');
463
+ // Test transaction with comprehensive fields
464
+ expect(statement.transactions).toHaveLength(1);
465
+ const transaction = statement.transactions[0];
466
+ // Amount and basic fields
467
+ expect(transaction.amount).toBe(-249.75); // Negative for debit
468
+ expect(transaction.customerReference).toBe('NOTPROVIDED');
469
+ expect(transaction.bankReference).toBe('BANK-REF-123456');
470
+ expect(transaction.purpose).toBe('SEPA Instant Transfer Payment Reference Text');
471
+ // Party information (debit transaction - creditor is remote party)
472
+ expect(transaction.remoteName).toBe('Erika Musterfrau');
473
+ expect(transaction.remoteAccountNumber).toBe('DE12500105170648489890');
474
+ expect(transaction.remoteBankId).toBe('INGDDEFFXXX'); // CdtrAgt BIC for debit
475
+ // Reference fields
476
+ expect(transaction.e2eReference).toBe('NOTPROVIDED');
477
+ expect(transaction.mandateReference).toBe('MANDATE-REF-456');
478
+ // Date fields with exact verification
479
+ expect(transaction.valueDate).toBeInstanceOf(Date);
480
+ expect(transaction.valueDate.getFullYear()).toBe(2023);
481
+ expect(transaction.valueDate.getMonth()).toBe(11); // December
482
+ expect(transaction.valueDate.getDate()).toBe(22);
483
+ expect(transaction.entryDate).toBeInstanceOf(Date);
484
+ expect(transaction.entryDate.getFullYear()).toBe(2023);
485
+ expect(transaction.entryDate.getMonth()).toBe(11);
486
+ expect(transaction.entryDate.getDate()).toBe(22);
487
+ // Bank transaction code structure
488
+ expect(transaction.fundsCode).toBe('PMNT'); // Domain code
489
+ expect(transaction.transactionType).toBe('ICDT'); // Family code
490
+ expect(transaction.transactionCode).toBe('ESCT'); // SubFamily code
491
+ // Additional information
492
+ expect(transaction.additionalInformation).toBe('Additional payment information from bank');
493
+ expect(transaction.bookingText).toBe('Additional payment information from bank');
494
+ // Verify undefined optional fields
495
+ expect(transaction.primeNotesNr).toBeUndefined();
496
+ expect(transaction.remoteIdentifier).toBeUndefined();
497
+ expect(transaction.client).toBeUndefined();
498
+ expect(transaction.textKeyExtension).toBeUndefined();
499
+ });
500
+ it('should handle edge cases and missing optional fields correctly', () => {
501
+ const camtXml = `<?xml version="1.0" encoding="UTF-8"?>
502
+ <Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.052.001.08">
503
+ <BkToCstmrAcctRpt>
504
+ <Rpt>
505
+ <Id>edge-case-test</Id>
506
+ <Acct>
507
+ <Id>
508
+ <IBAN>DE99999999999999999999</IBAN>
509
+ </Id>
510
+ <Ccy>USD</Ccy>
511
+ </Acct>
512
+ <Bal>
513
+ <Tp><CdOrPrtry><Cd>PRCD</Cd></CdOrPrtry></Tp>
514
+ <Amt Ccy="USD">0.01</Amt>
515
+ <CdtDbtInd>DBIT</CdtDbtInd>
516
+ <Dt><Dt>20231222</Dt></Dt>
517
+ </Bal>
518
+ <Bal>
519
+ <Tp><CdOrPrtry><Cd>CLBD</Cd></CdOrPrtry></Tp>
520
+ <Amt Ccy="USD">999.99</Amt>
521
+ <CdtDbtInd>CRDT</CdtDbtInd>
522
+ <Dt><Dt>20231222</Dt></Dt>
523
+ </Bal>
524
+ <Ntry>
525
+ <Amt>1000.00</Amt>
526
+ <CdtDbtInd>CRDT</CdtDbtInd>
527
+ <BookgDt><Dt>20231222</Dt></BookgDt>
528
+ <ValDt><Dt>20231221</Dt></ValDt>
529
+ <AcctSvcrRef></AcctSvcrRef>
530
+ <NtryDtls>
531
+ <TxDtls>
532
+ <Refs>
533
+ <EndToEndId></EndToEndId>
534
+ </Refs>
535
+ <RmtInf>
536
+ <Ustrd></Ustrd>
537
+ </RmtInf>
538
+ <RltdPties>
539
+ <Dbtr>
540
+ <Nm></Nm>
541
+ </Dbtr>
542
+ </RltdPties>
543
+ </TxDtls>
544
+ </NtryDtls>
545
+ </Ntry>
546
+ </Rpt>
547
+ </BkToCstmrAcctRpt>
548
+ </Document>`;
549
+ const parser = new CamtParser(camtXml);
550
+ const statements = parser.parse();
551
+ expect(statements).toHaveLength(1);
552
+ const statement = statements[0];
553
+ // Test edge case balances
554
+ expect(statement.openingBalance.value).toBe(-0.01); // Negative due to DBIT indicator
555
+ expect(statement.openingBalance.currency).toBe('USD');
556
+ expect(statement.closingBalance.value).toBe(999.99);
557
+ expect(statement.closingBalance.currency).toBe('USD');
558
+ // Test date parsing from YYYYMMDD format
559
+ expect(statement.openingBalance.date.getFullYear()).toBe(2023);
560
+ expect(statement.openingBalance.date.getMonth()).toBe(11); // December
561
+ expect(statement.openingBalance.date.getDate()).toBe(22);
562
+ // Test transaction with mostly empty/missing fields
563
+ expect(statement.transactions).toHaveLength(1);
564
+ const transaction = statement.transactions[0];
565
+ expect(transaction.amount).toBe(1000.0);
566
+ expect(transaction.customerReference).toBe(''); // Empty EndToEndId
567
+ expect(transaction.bankReference).toBe(''); // Empty AcctSvcrRef
568
+ expect(transaction.purpose).toBe(''); // Empty Ustrd
569
+ expect(transaction.remoteName).toBe(''); // Empty Nm
570
+ expect(transaction.remoteAccountNumber).toBe(''); // No IBAN provided
571
+ expect(transaction.remoteBankId).toBe(''); // No BIC provided
572
+ expect(transaction.e2eReference).toBe(''); // Empty EndToEndId
573
+ expect(transaction.mandateReference).toBe(''); // No MndtId
574
+ // Test date parsing consistency
575
+ expect(transaction.valueDate.getDate()).toBe(21); // Different from entry date
576
+ expect(transaction.entryDate.getDate()).toBe(22);
577
+ // Test transaction type fields with no BkTxCd
578
+ expect(transaction.fundsCode).toBe('CRDT');
579
+ expect(transaction.transactionType).toBe('');
580
+ expect(transaction.transactionCode).toBe('');
581
+ expect(transaction.additionalInformation).toBe('');
582
+ expect(transaction.bookingText).toBe('');
583
+ });
584
+ it('should handle party structure variations in XML (Dbtr.Pty.Nm format)', () => {
585
+ const camtXml = `<?xml version="1.0" encoding="UTF-8"?>
586
+ <Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.052.001.08">
587
+ <BkToCstmrAcctRpt>
588
+ <Rpt>
589
+ <Id>party-structure-test</Id>
590
+ <Acct>
591
+ <Id>
592
+ <IBAN>DE06940594210000027227</IBAN>
593
+ </Id>
594
+ </Acct>
595
+ <Bal>
596
+ <Tp><CdOrPrtry><Cd>PRCD</Cd></CdOrPrtry></Tp>
597
+ <Amt Ccy="EUR">1000.00</Amt>
598
+ <CdtDbtInd>CRDT</CdtDbtInd>
599
+ <Dt><Dt>2013-10-31</Dt></Dt>
600
+ </Bal>
601
+ <Bal>
602
+ <Tp><CdOrPrtry><Cd>CLBD</Cd></CdOrPrtry></Tp>
603
+ <Amt Ccy="EUR">1200.00</Amt>
604
+ <CdtDbtInd>CRDT</CdtDbtInd>
605
+ <Dt><Dt>2013-11-01</Dt></Dt>
606
+ </Bal>
607
+ <Ntry>
608
+ <Amt>200.00</Amt>
609
+ <CdtDbtInd>CRDT</CdtDbtInd>
610
+ <BookgDt><Dt>2013-11-01</Dt></BookgDt>
611
+ <ValDt><Dt>2013-11-01</Dt></ValDt>
612
+ <AcctSvcrRef>TXN003</AcctSvcrRef>
613
+ <NtryDtls>
614
+ <TxDtls>
615
+ <Refs>
616
+ <EndToEndId>PARTY-TEST-123</EndToEndId>
617
+ </Refs>
618
+ <RmtInf>
619
+ <Ustrd>Payment with party structure</Ustrd>
620
+ </RmtInf>
621
+ <RltdPties>
622
+ <Dbtr>
623
+ <Pty>
624
+ <Nm>John Smith Bank Format</Nm>
625
+ </Pty>
626
+ </Dbtr>
627
+ <DbtrAcct>
628
+ <Id>
629
+ <IBAN>DE12345678901234567890</IBAN>
630
+ </Id>
631
+ </DbtrAcct>
632
+ <Cdtr>
633
+ <Pty>
634
+ <Nm>Jane Smith Bank Format</Nm>
635
+ </Pty>
636
+ </Cdtr>
637
+ <CdtrAcct>
638
+ <Id>
639
+ <IBAN>DE12345678901234567891</IBAN>
640
+ </Id>
641
+ </CdtrAcct>
642
+ </RltdPties>
643
+ <RltdAgts>
644
+ <DbtrAgt>
645
+ <FinInstnId>
646
+ <BICFI>BANKABC1XXX</BICFI>
647
+ </FinInstnId>
648
+ </DbtrAgt>
649
+ <CdtrAgt>
650
+ <FinInstnId>
651
+ <BICFI>BANKDEF2XXX</BICFI>
652
+ </FinInstnId>
653
+ </CdtrAgt>
654
+ </RltdAgts>
655
+ </TxDtls>
656
+ </NtryDtls>
657
+ </Ntry>
658
+ </Rpt>
659
+ </BkToCstmrAcctRpt>
660
+ </Document>`;
661
+ const parser = new CamtParser(camtXml);
662
+ const statements = parser.parse();
663
+ expect(statements).toHaveLength(1);
664
+ expect(statements[0].transactions).toHaveLength(1);
665
+ const transaction = statements[0].transactions[0];
666
+ // Verify that the party name is correctly extracted from Dbtr.Pty.Nm structure
667
+ expect(transaction.remoteName).toBe('John Smith Bank Format');
668
+ expect(transaction.remoteAccountNumber).toBe('DE12345678901234567890');
669
+ expect(transaction.remoteBankId).toBe('BANKABC1XXX'); // Credit transaction uses DbtrAgt BIC
670
+ expect(transaction.purpose).toBe('Payment with party structure');
671
+ expect(transaction.amount).toBe(200.0);
672
+ });
673
+ });