lib-fints 1.1.1 → 1.2.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/README.md +4 -0
- package/dist/client.js +33 -0
- package/dist/index.js +1 -0
- package/dist/interactions/portfolioInteraction.js +57 -0
- package/dist/mt535parser.js +196 -0
- package/dist/partedSegment.js +4 -1
- package/dist/segment.js +3 -5
- package/dist/segmentDefinition.js +4 -1
- package/dist/segments/HIBPA.js +4 -1
- package/dist/segments/HIKAZ.js +4 -1
- package/dist/segments/HIKAZS.js +2 -2
- package/dist/segments/HIKIM.js +4 -1
- package/dist/segments/HIKOM.js +4 -1
- package/dist/segments/HIPINS.js +2 -2
- package/dist/segments/HIRMG.js +4 -1
- package/dist/segments/HIRMS.js +4 -1
- package/dist/segments/HISAL.js +4 -1
- package/dist/segments/HISYN.js +4 -1
- package/dist/segments/HITAB.js +4 -1
- package/dist/segments/HITAN.js +4 -1
- package/dist/segments/HITANS.js +2 -2
- package/dist/segments/HIUPA.js +4 -1
- package/dist/segments/HIUPD.js +4 -1
- package/dist/segments/HIWPD.js +15 -0
- package/dist/segments/HKEND.js +4 -1
- package/dist/segments/HKIDN.js +4 -1
- package/dist/segments/HKKAZ.js +4 -1
- package/dist/segments/HKSAL.js +4 -1
- package/dist/segments/HKSYN.js +4 -1
- package/dist/segments/HKTAB.js +4 -1
- package/dist/segments/HKTAN.js +4 -1
- package/dist/segments/HKVVB.js +4 -1
- package/dist/segments/HKWPD.js +23 -0
- package/dist/segments/HNHBK.js +4 -1
- package/dist/segments/HNHBS.js +4 -1
- package/dist/segments/HNSHA.js +4 -1
- package/dist/segments/HNSHK.js +4 -1
- package/dist/segments/HNVSD.js +5 -2
- package/dist/segments/HNVSK.js +5 -2
- package/dist/segments/businessTransactionParameter.js +2 -2
- package/dist/segments/registry.js +4 -0
- package/dist/tests/HIWPD.test.js +25 -0
- package/dist/tests/HKWPD.test.js +38 -0
- package/dist/tests/mt535parser.test.js +157 -0
- package/dist/types/client.d.ts +24 -0
- package/dist/types/client.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/interactions/portfolioInteraction.d.ts +39 -0
- package/dist/types/interactions/portfolioInteraction.d.ts.map +1 -0
- package/dist/types/mt535parser.d.ts +44 -0
- package/dist/types/mt535parser.d.ts.map +1 -0
- package/dist/types/partedSegment.d.ts +1 -0
- package/dist/types/partedSegment.d.ts.map +1 -1
- package/dist/types/segment.d.ts.map +1 -1
- package/dist/types/segmentDefinition.d.ts +1 -0
- package/dist/types/segmentDefinition.d.ts.map +1 -1
- package/dist/types/segments/HIBPA.d.ts +1 -0
- package/dist/types/segments/HIBPA.d.ts.map +1 -1
- package/dist/types/segments/HIKAZ.d.ts +1 -0
- package/dist/types/segments/HIKAZ.d.ts.map +1 -1
- package/dist/types/segments/HIKAZS.d.ts.map +1 -1
- package/dist/types/segments/HIKIM.d.ts +1 -0
- package/dist/types/segments/HIKIM.d.ts.map +1 -1
- package/dist/types/segments/HIKOM.d.ts +1 -0
- package/dist/types/segments/HIKOM.d.ts.map +1 -1
- package/dist/types/segments/HIPINS.d.ts.map +1 -1
- package/dist/types/segments/HIRMG.d.ts +1 -0
- package/dist/types/segments/HIRMG.d.ts.map +1 -1
- package/dist/types/segments/HIRMS.d.ts +1 -0
- package/dist/types/segments/HIRMS.d.ts.map +1 -1
- package/dist/types/segments/HISAL.d.ts +1 -0
- package/dist/types/segments/HISAL.d.ts.map +1 -1
- package/dist/types/segments/HISYN.d.ts +1 -0
- package/dist/types/segments/HISYN.d.ts.map +1 -1
- package/dist/types/segments/HITAB.d.ts +1 -0
- package/dist/types/segments/HITAB.d.ts.map +1 -1
- package/dist/types/segments/HITAN.d.ts +1 -0
- package/dist/types/segments/HITAN.d.ts.map +1 -1
- package/dist/types/segments/HITANS.d.ts.map +1 -1
- package/dist/types/segments/HIUPA.d.ts +1 -0
- package/dist/types/segments/HIUPA.d.ts.map +1 -1
- package/dist/types/segments/HIUPD.d.ts +1 -0
- package/dist/types/segments/HIUPD.d.ts.map +1 -1
- package/dist/types/segments/HIWPD.d.ts +22 -0
- package/dist/types/segments/HIWPD.d.ts.map +1 -0
- package/dist/types/segments/HKEND.d.ts +1 -0
- package/dist/types/segments/HKEND.d.ts.map +1 -1
- package/dist/types/segments/HKIDN.d.ts +1 -0
- package/dist/types/segments/HKIDN.d.ts.map +1 -1
- package/dist/types/segments/HKKAZ.d.ts +1 -0
- package/dist/types/segments/HKKAZ.d.ts.map +1 -1
- package/dist/types/segments/HKSAL.d.ts +1 -0
- package/dist/types/segments/HKSAL.d.ts.map +1 -1
- package/dist/types/segments/HKSYN.d.ts +1 -0
- package/dist/types/segments/HKSYN.d.ts.map +1 -1
- package/dist/types/segments/HKTAB.d.ts +1 -0
- package/dist/types/segments/HKTAB.d.ts.map +1 -1
- package/dist/types/segments/HKTAN.d.ts +1 -0
- package/dist/types/segments/HKTAN.d.ts.map +1 -1
- package/dist/types/segments/HKVVB.d.ts +1 -0
- package/dist/types/segments/HKVVB.d.ts.map +1 -1
- package/dist/types/segments/HKWPD.d.ts +43 -0
- package/dist/types/segments/HKWPD.d.ts.map +1 -0
- package/dist/types/segments/HNHBK.d.ts +1 -0
- package/dist/types/segments/HNHBK.d.ts.map +1 -1
- package/dist/types/segments/HNHBS.d.ts +1 -0
- package/dist/types/segments/HNHBS.d.ts.map +1 -1
- package/dist/types/segments/HNSHA.d.ts +1 -0
- package/dist/types/segments/HNSHA.d.ts.map +1 -1
- package/dist/types/segments/HNSHK.d.ts +1 -0
- package/dist/types/segments/HNSHK.d.ts.map +1 -1
- package/dist/types/segments/HNVSD.d.ts +1 -0
- package/dist/types/segments/HNVSD.d.ts.map +1 -1
- package/dist/types/segments/HNVSK.d.ts +1 -0
- package/dist/types/segments/HNVSK.d.ts.map +1 -1
- package/dist/types/segments/businessTransactionParameter.d.ts +1 -1
- package/dist/types/segments/businessTransactionParameter.d.ts.map +1 -1
- package/dist/types/segments/registry.d.ts.map +1 -1
- package/dist/types/tests/HIWPD.test.d.ts +2 -0
- package/dist/types/tests/HIWPD.test.d.ts.map +1 -0
- package/dist/types/tests/HKWPD.test.d.ts +2 -0
- package/dist/types/tests/HKWPD.test.d.ts.map +1 -0
- package/dist/types/tests/mt535parser.test.d.ts +2 -0
- package/dist/types/tests/mt535parser.test.d.ts.map +1 -0
- package/dist/types/unknownSegment.d.ts +1 -0
- package/dist/types/unknownSegment.d.ts.map +1 -1
- package/dist/unknownSegment.js +4 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -86,6 +86,9 @@ const balanceResponse = await client.getAccountBalance(account.accountNumber);
|
|
|
86
86
|
|
|
87
87
|
// fetch all available statements
|
|
88
88
|
const statementResponse = await client.getAccountStatements(account.accountNumber);
|
|
89
|
+
|
|
90
|
+
// or fetch portfolio from a securities account
|
|
91
|
+
client.getPortfolio(account.accountNumber);
|
|
89
92
|
```
|
|
90
93
|
|
|
91
94
|
These are only the most basic steps needed to retrieve information from the bank. There are still some unanswered questions like "how to handle TANs" or "how to avoid synchronizations every time you start a new session". These are explained in the corresponding sections below.
|
|
@@ -191,6 +194,7 @@ This will print out all sent messages and received responses to the console in a
|
|
|
191
194
|
- Synchronize bank and account information
|
|
192
195
|
- Fetching account balances
|
|
193
196
|
- Fetching account statements
|
|
197
|
+
- Fetching the portfolio of a securities account
|
|
194
198
|
|
|
195
199
|
Implementing further transactions should be straight forward and contributions are highly appreciated
|
|
196
200
|
|
package/dist/client.js
CHANGED
|
@@ -2,10 +2,12 @@ import { Dialog } from './dialog.js';
|
|
|
2
2
|
import { HKTAB } from './segments/HKTAB.js';
|
|
3
3
|
import { StatementInteraction } from './interactions/statementInteraction.js';
|
|
4
4
|
import { BalanceInteraction } from './interactions/balanceInteraction.js';
|
|
5
|
+
import { PortfolioInteraction } from './interactions/portfolioInteraction.js';
|
|
5
6
|
import { FinTSConfig } from './config.js';
|
|
6
7
|
import { TanMediaInteraction } from './interactions/tanMediaInteraction.js';
|
|
7
8
|
import { HKSAL } from './segments/HKSAL.js';
|
|
8
9
|
import { HKKAZ } from './segments/HKKAZ.js';
|
|
10
|
+
import { HKWPD } from './segments/HKWPD.js';
|
|
9
11
|
import { InitDialogInteraction } from './interactions/initDialogInteraction.js';
|
|
10
12
|
/**
|
|
11
13
|
* A client to communicate with a bank over the FinTS protocol
|
|
@@ -128,6 +130,37 @@ export class FinTSClient {
|
|
|
128
130
|
async getAccountStatementsWithTan(tanReference, tan) {
|
|
129
131
|
return this.continueCustomerInteractionWithTan(tanReference, tan);
|
|
130
132
|
}
|
|
133
|
+
/**
|
|
134
|
+
* Checks if the bank supports fetching portfolio information in general or for the given account number when provided
|
|
135
|
+
* @param accountNumber when the account number is provided, checks if the account supports fetching of portfolio information
|
|
136
|
+
* @returns true if the bank (and account) supports fetching portfolio information
|
|
137
|
+
*/
|
|
138
|
+
canGetPortfolio(accountNumber) {
|
|
139
|
+
return accountNumber
|
|
140
|
+
? this.config.isAccountTransactionSupported(accountNumber, HKWPD.Id)
|
|
141
|
+
: this.config.isTransactionSupported(HKWPD.Id);
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Fetches the portfolio information for the given depot account number
|
|
145
|
+
* @param accountNumber - the depot account number to fetch the portfolio for, must be an account available in the config.bankingInformation.UPD.accounts
|
|
146
|
+
* @param currency - optional currency filter for the portfolio statement
|
|
147
|
+
* @param priceQuality - optional price quality filter ('1' for real-time, '2' for delayed)
|
|
148
|
+
* @param maxEntries - optional maximum number of entries to retrieve
|
|
149
|
+
* @returns a portfolio response containing holdings and total value
|
|
150
|
+
*/
|
|
151
|
+
async getPortfolio(accountNumber, currency, priceQuality, maxEntries) {
|
|
152
|
+
return this.startCustomerOrderInteraction(new PortfolioInteraction(accountNumber, currency, priceQuality, maxEntries));
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Continues the portfolio fetching when a TAN is required
|
|
156
|
+
* @param tanReference The TAN reference provided in the first call's response
|
|
157
|
+
|
|
158
|
+
* @param tan The TAN entered by the user, can be omitted if a decoupled TAN method is used
|
|
159
|
+
* @returns a portfolio response containing holdings and total value
|
|
160
|
+
*/
|
|
161
|
+
async getPortfolioWithTan(tanReference, tan) {
|
|
162
|
+
return this.continueCustomerInteractionWithTan(tanReference, tan);
|
|
163
|
+
}
|
|
131
164
|
async startCustomerOrderInteraction(interaction) {
|
|
132
165
|
const dialog = new Dialog(this.config);
|
|
133
166
|
const syncResponse = await this.initDialog(dialog, false, interaction);
|
package/dist/index.js
CHANGED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { CustomerOrderInteraction, } from './customerInteraction.js';
|
|
2
|
+
import { HKWPD } from '../segments/HKWPD.js';
|
|
3
|
+
import { HIWPD } from '../segments/HIWPD.js';
|
|
4
|
+
import { Mt535Parser } from '../mt535parser.js';
|
|
5
|
+
/**
|
|
6
|
+
* Interaction for requesting and parsing stock portfolio information (HKWPD/HIWPD)
|
|
7
|
+
*/
|
|
8
|
+
export class PortfolioInteraction extends CustomerOrderInteraction {
|
|
9
|
+
accountNumber;
|
|
10
|
+
currency;
|
|
11
|
+
priceQuality;
|
|
12
|
+
maxEntries;
|
|
13
|
+
paginationMarker;
|
|
14
|
+
constructor(accountNumber, currency, priceQuality, maxEntries, paginationMarker) {
|
|
15
|
+
super(HKWPD.Id, HIWPD.Id);
|
|
16
|
+
this.accountNumber = accountNumber;
|
|
17
|
+
this.currency = currency;
|
|
18
|
+
this.priceQuality = priceQuality;
|
|
19
|
+
this.maxEntries = maxEntries;
|
|
20
|
+
this.paginationMarker = paginationMarker;
|
|
21
|
+
}
|
|
22
|
+
createSegments(config) {
|
|
23
|
+
const bankAccount = config.getBankAccount(this.accountNumber);
|
|
24
|
+
if (!config.isAccountTransactionSupported(this.accountNumber, this.segId)) {
|
|
25
|
+
throw Error(`Account ${this.accountNumber} does not support business transaction '${this.segId}'`);
|
|
26
|
+
}
|
|
27
|
+
const depotAccount = { ...bankAccount, iban: undefined }; // HKWPD uses KTV which doesn't have IBAN
|
|
28
|
+
const version = config.getMaxSupportedTransactionVersion(HKWPD.Id);
|
|
29
|
+
if (!version) {
|
|
30
|
+
throw Error(`There is no supported version for business transaction '${HKWPD.Id}'`);
|
|
31
|
+
}
|
|
32
|
+
const hkwpd = {
|
|
33
|
+
header: { segId: HKWPD.Id, segNr: 0, version: version },
|
|
34
|
+
depot: depotAccount,
|
|
35
|
+
currency: this.currency,
|
|
36
|
+
priceQuality: this.priceQuality,
|
|
37
|
+
maxEntries: this.maxEntries,
|
|
38
|
+
paginationMarker: this.paginationMarker,
|
|
39
|
+
};
|
|
40
|
+
return [hkwpd];
|
|
41
|
+
}
|
|
42
|
+
handleResponse(response, clientResponse) {
|
|
43
|
+
const hiwpdSegment = response.findSegment(HIWPD.Id);
|
|
44
|
+
if (hiwpdSegment?.portfolioStatement) {
|
|
45
|
+
try {
|
|
46
|
+
// Parse the MT535 data
|
|
47
|
+
const parser = new Mt535Parser(hiwpdSegment.portfolioStatement);
|
|
48
|
+
clientResponse.portfolioStatement = parser.parse();
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
console.warn('Failed to parse MT535 portfolio statement:', error);
|
|
52
|
+
// Fallback: provide raw data if parsing fails
|
|
53
|
+
clientResponse.rawMT535Data = hiwpdSegment.portfolioStatement;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
export var TokenType535;
|
|
2
|
+
(function (TokenType535) {
|
|
3
|
+
TokenType535["DepotValueBlock"] = "DepotValueBlock";
|
|
4
|
+
TokenType535["DepotValueCurrency"] = "DepotValueCurrency";
|
|
5
|
+
TokenType535["FinBlock"] = "FinBlock";
|
|
6
|
+
TokenType535["SecurityIdentification"] = "SecurityIdentification";
|
|
7
|
+
TokenType535["AcquisitionPrice"] = "AcquisitionPrice";
|
|
8
|
+
TokenType535["PriceBlock"] = "PriceBlock";
|
|
9
|
+
TokenType535["AmountBlock"] = "AmountBlock";
|
|
10
|
+
TokenType535["DateTimeBlock"] = "DateTimeBlock";
|
|
11
|
+
TokenType535["DateString"] = "DateString";
|
|
12
|
+
TokenType535["TimeString"] = "TimeString";
|
|
13
|
+
})(TokenType535 || (TokenType535 = {}));
|
|
14
|
+
const tokens535 = {
|
|
15
|
+
[TokenType535.DepotValueBlock]: /:16R:ADDINFO(.*?):16S:ADDINFO/ms,
|
|
16
|
+
[TokenType535.DepotValueCurrency]: /EUR(.*)/ms,
|
|
17
|
+
[TokenType535.FinBlock]: /:16R:FIN(.*?):16S:FIN/gms,
|
|
18
|
+
[TokenType535.SecurityIdentification]: /:35B:(.*?):/ms,
|
|
19
|
+
[TokenType535.AcquisitionPrice]: /:70E::HOLD\/\/\d*STK2(\d*),(\d*)\+([A-Z]{3})/ms,
|
|
20
|
+
[TokenType535.PriceBlock]: /:90([AB])::(.*?):/ms,
|
|
21
|
+
[TokenType535.AmountBlock]: /:93B::(.*?):/ms,
|
|
22
|
+
[TokenType535.DateTimeBlock]: /:98([AC])::(.*?):/ms,
|
|
23
|
+
[TokenType535.DateString]: /(\d{4})(\d{2})(\d{2})/,
|
|
24
|
+
[TokenType535.TimeString]: /^.{14}(\d{2})(\d{2})(\d{2})/ms,
|
|
25
|
+
};
|
|
26
|
+
export class Mt535Parser {
|
|
27
|
+
rawData;
|
|
28
|
+
cleanedRawData;
|
|
29
|
+
constructor(rawData) {
|
|
30
|
+
this.rawData = rawData;
|
|
31
|
+
// The divider can be either \r\n or @@
|
|
32
|
+
const crlfCount = (rawData.match(/\r\n-/g) || []).length;
|
|
33
|
+
const atAtCount = (rawData.match(/@@-/g) || []).length;
|
|
34
|
+
const divider = crlfCount > atAtCount ? '\r\n' : '@@';
|
|
35
|
+
// Remove dividers that are not followed by a colon (tag indicator)
|
|
36
|
+
const regex = new RegExp(divider + '([^:])', 'gms');
|
|
37
|
+
this.cleanedRawData = rawData.replace(regex, '$1');
|
|
38
|
+
}
|
|
39
|
+
parse() {
|
|
40
|
+
const result = {
|
|
41
|
+
holdings: [],
|
|
42
|
+
};
|
|
43
|
+
// Parse total depot value
|
|
44
|
+
result.totalValue = this.parseDepotValue();
|
|
45
|
+
// Parse individual holdings
|
|
46
|
+
result.holdings = this.parseHoldings();
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
parseDepotValue() {
|
|
50
|
+
const addInfoMatch = this.cleanedRawData.match(tokens535[TokenType535.DepotValueBlock]);
|
|
51
|
+
if (addInfoMatch) {
|
|
52
|
+
const eurMatch = addInfoMatch[1].match(tokens535[TokenType535.DepotValueCurrency]);
|
|
53
|
+
if (eurMatch) {
|
|
54
|
+
return parseFloat(eurMatch[1].replace(',', '.'));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
parseHoldings() {
|
|
60
|
+
const holdings = [];
|
|
61
|
+
const finBlocks = this.cleanedRawData.match(tokens535[TokenType535.FinBlock]);
|
|
62
|
+
if (!finBlocks) {
|
|
63
|
+
return holdings;
|
|
64
|
+
}
|
|
65
|
+
for (const block of finBlocks) {
|
|
66
|
+
const holding = this.parseHolding(block);
|
|
67
|
+
if (holding) {
|
|
68
|
+
holdings.push(holding);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return holdings;
|
|
72
|
+
}
|
|
73
|
+
parseHolding(block) {
|
|
74
|
+
const holding = {};
|
|
75
|
+
// Parse ISIN, WKN & Name from :35B:
|
|
76
|
+
// :35B:ISIN DE0005190003/DE/519000BAY.MOTOREN WERKE AG ST
|
|
77
|
+
this.parseSecurityIdentification(block, holding);
|
|
78
|
+
// Parse acquisition price from :70E::HOLD//
|
|
79
|
+
this.parseAcquisitionPrice(block, holding);
|
|
80
|
+
// Parse current price from :90B: or :90A:
|
|
81
|
+
this.parsePrice(block, holding);
|
|
82
|
+
// Parse amount from :93B:
|
|
83
|
+
this.parseAmount(block, holding);
|
|
84
|
+
// Parse date/time from :98A: or :98C:
|
|
85
|
+
this.parseDateTime(block, holding);
|
|
86
|
+
// Calculate value if we have price and amount
|
|
87
|
+
if (holding.amount !== undefined && holding.price !== undefined) {
|
|
88
|
+
// For all currencies, value is price multiplied by amount
|
|
89
|
+
holding.value = holding.price * holding.amount;
|
|
90
|
+
}
|
|
91
|
+
return holding;
|
|
92
|
+
}
|
|
93
|
+
parseSecurityIdentification(block, holding) {
|
|
94
|
+
const match = block.match(tokens535[TokenType535.SecurityIdentification]);
|
|
95
|
+
if (match) {
|
|
96
|
+
const content = match[1];
|
|
97
|
+
// ISIN: characters 5-16 (12 chars)
|
|
98
|
+
const isinMatch = content.match(/^.{5}(.{12})/ms);
|
|
99
|
+
if (isinMatch) {
|
|
100
|
+
holding.isin = isinMatch[1];
|
|
101
|
+
}
|
|
102
|
+
// WKN: characters 21-26 (6 chars)
|
|
103
|
+
const wknMatch = content.match(/^.{21}(.{6})/ms);
|
|
104
|
+
if (wknMatch) {
|
|
105
|
+
holding.wkn = wknMatch[1];
|
|
106
|
+
}
|
|
107
|
+
// Name: everything from character 27 onwards
|
|
108
|
+
const nameMatch = content.match(/^.{27}(.*)/ms);
|
|
109
|
+
if (nameMatch) {
|
|
110
|
+
holding.name = nameMatch[1].trim();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
parseAcquisitionPrice(block, holding) {
|
|
115
|
+
const match = block.match(tokens535[TokenType535.AcquisitionPrice]);
|
|
116
|
+
if (match) {
|
|
117
|
+
holding.acquisitionPrice = parseFloat(`${match[1]}.${match[2]}`);
|
|
118
|
+
if (!holding.currency) {
|
|
119
|
+
holding.currency = match[3];
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
parsePrice(block, holding) {
|
|
124
|
+
const match = block.match(tokens535[TokenType535.PriceBlock]);
|
|
125
|
+
if (match) {
|
|
126
|
+
const type = match[1];
|
|
127
|
+
const content = match[2];
|
|
128
|
+
if (type === 'B') {
|
|
129
|
+
// Currency from characters 11-13 (3 chars)
|
|
130
|
+
const currencyMatch = content.match(/^.{11}(.{3})/ms);
|
|
131
|
+
if (currencyMatch) {
|
|
132
|
+
holding.currency = currencyMatch[1];
|
|
133
|
+
}
|
|
134
|
+
// Price from character 14 onwards
|
|
135
|
+
const priceMatch = content.match(/^.{14}(.*)/ms);
|
|
136
|
+
if (priceMatch) {
|
|
137
|
+
holding.price = parseFloat(priceMatch[1].replace(',', '.'));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
else if (type === 'A') {
|
|
141
|
+
holding.currency = '%';
|
|
142
|
+
// Price from character 11 onwards
|
|
143
|
+
const priceMatch = content.match(/^.{11}(.*)/ms);
|
|
144
|
+
if (priceMatch) {
|
|
145
|
+
holding.price = parseFloat(priceMatch[1].replace(',', '.')) / 100;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
parseAmount(block, holding) {
|
|
151
|
+
const match = block.match(tokens535[TokenType535.AmountBlock]);
|
|
152
|
+
if (match) {
|
|
153
|
+
// Amount from character 11 onwards
|
|
154
|
+
const amountMatch = match[1].match(/^.{11}(.*)/ms);
|
|
155
|
+
if (amountMatch) {
|
|
156
|
+
holding.amount = parseFloat(amountMatch[1].replace(',', '.'));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
parseDateTime(block, holding) {
|
|
161
|
+
const match = block.match(tokens535[TokenType535.DateTimeBlock]);
|
|
162
|
+
if (match) {
|
|
163
|
+
const type = match[1];
|
|
164
|
+
const content = match[2];
|
|
165
|
+
// Date from characters 6-13 (8 chars: YYYYMMDD)
|
|
166
|
+
const dateMatch = content.match(tokens535[TokenType535.DateString]);
|
|
167
|
+
if (dateMatch) {
|
|
168
|
+
const parsedDate = this.parseDate(dateMatch[0]);
|
|
169
|
+
if (type === 'C') {
|
|
170
|
+
// :98C: has a time component HHMMSS starting at character 14
|
|
171
|
+
const timeMatch = content.match(tokens535[TokenType535.TimeString]);
|
|
172
|
+
if (timeMatch) {
|
|
173
|
+
parsedDate.setHours(parseInt(timeMatch[1]), parseInt(timeMatch[2]), parseInt(timeMatch[3]));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
// :98A: time defaults to 00:00:00
|
|
178
|
+
parsedDate.setHours(0, 0, 0, 0);
|
|
179
|
+
}
|
|
180
|
+
holding.date = parsedDate;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
parseDate(dateString) {
|
|
185
|
+
const match = dateString.match(tokens535[TokenType535.DateString]);
|
|
186
|
+
if (!match) {
|
|
187
|
+
throw new Error(`Invalid date format: ${dateString}`);
|
|
188
|
+
}
|
|
189
|
+
try {
|
|
190
|
+
return new Date(parseInt(match[1]), parseInt(match[2]) - 1, parseInt(match[3]));
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
throw new Error(`Invalid date: ${dateString}`, { cause: error });
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
package/dist/partedSegment.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { Text } from './dataElements/Text.js';
|
|
2
2
|
import { SegmentDefinition } from './segmentDefinition.js';
|
|
3
3
|
export class PARTED extends SegmentDefinition {
|
|
4
|
-
static Id =
|
|
4
|
+
static Id = 'PARTED';
|
|
5
|
+
constructor() {
|
|
6
|
+
super(PARTED.Id);
|
|
7
|
+
}
|
|
5
8
|
version = 1;
|
|
6
9
|
elements = [new Text('rawData', 0, 1)];
|
|
7
10
|
}
|
package/dist/segment.js
CHANGED
|
@@ -45,11 +45,9 @@ export function segmentToString(segment) {
|
|
|
45
45
|
const segmentDefinition = getSegmentDefinition(segment.header.segId);
|
|
46
46
|
let texts = segmentDefinition.getElementsForVersion(segment.header.version).map((element) => {
|
|
47
47
|
if (element.maxCount > 1) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
.filter((text) => !!text)
|
|
52
|
-
.join('; '));
|
|
48
|
+
const array = keyedSegment[element.name] ?? [];
|
|
49
|
+
const texts = array.map((value) => element.toString(value));
|
|
50
|
+
return texts.filter((text) => !!text).join('; ');
|
|
53
51
|
}
|
|
54
52
|
else {
|
|
55
53
|
return element.toString(keyedSegment[element.name]);
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { encodeElements } from './encoder.js';
|
|
2
2
|
import { SegmentHeaderGroup } from './segmentHeader.js';
|
|
3
3
|
export class SegmentDefinition {
|
|
4
|
-
id
|
|
4
|
+
id;
|
|
5
|
+
constructor(id) {
|
|
6
|
+
this.id = id;
|
|
7
|
+
}
|
|
5
8
|
static header = new SegmentHeaderGroup();
|
|
6
9
|
getElementsForVersion(version) {
|
|
7
10
|
return this.elements.filter((element) => version >= (element.minVersion ?? 0) && version <= (element.maxVersion ?? Number.MAX_SAFE_INTEGER));
|
package/dist/segments/HIBPA.js
CHANGED
|
@@ -7,8 +7,11 @@ import { BankIdentification } from '../dataGroups/BankIdentification.js';
|
|
|
7
7
|
* General bank parameters
|
|
8
8
|
*/
|
|
9
9
|
export class HIBPA extends SegmentDefinition {
|
|
10
|
-
static Id =
|
|
10
|
+
static Id = 'HIBPA';
|
|
11
11
|
version = 3;
|
|
12
|
+
constructor() {
|
|
13
|
+
super(HIBPA.Id);
|
|
14
|
+
}
|
|
12
15
|
elements = [
|
|
13
16
|
new Numeric('bpdVersion', 1, 1, 3),
|
|
14
17
|
new BankIdentification('bank', 1, 1),
|
package/dist/segments/HIKAZ.js
CHANGED
|
@@ -4,8 +4,11 @@ import { SegmentDefinition } from '../segmentDefinition.js';
|
|
|
4
4
|
* Account transactions within period response
|
|
5
5
|
*/
|
|
6
6
|
export class HIKAZ extends SegmentDefinition {
|
|
7
|
-
static Id =
|
|
7
|
+
static Id = 'HIKAZ';
|
|
8
8
|
version = 7;
|
|
9
|
+
constructor() {
|
|
10
|
+
super(HIKAZ.Id);
|
|
11
|
+
}
|
|
9
12
|
elements = [
|
|
10
13
|
new Binary('bookedTransactions', 1, 1, Number.MAX_SAFE_INTEGER),
|
|
11
14
|
new Binary('notedTransactions', 0, 1, Number.MAX_SAFE_INTEGER),
|
package/dist/segments/HIKAZS.js
CHANGED
|
@@ -5,9 +5,9 @@ import { BusinessTransactionParameter } from './businessTransactionParameter.js'
|
|
|
5
5
|
* Parameters for HKKAZ business transaction
|
|
6
6
|
*/
|
|
7
7
|
export class HIKAZS extends BusinessTransactionParameter {
|
|
8
|
-
static Id =
|
|
8
|
+
static Id = 'HIKAZS';
|
|
9
9
|
version = 7;
|
|
10
10
|
constructor() {
|
|
11
|
-
super([new Numeric('maxDays', 1, 1, 4), new YesNo('entryCountAllowed', 1, 1), new YesNo('allAccountsAllowed', 1, 1, 5)], 6);
|
|
11
|
+
super(HIKAZS.Id, [new Numeric('maxDays', 1, 1, 4), new YesNo('entryCountAllowed', 1, 1), new YesNo('allAccountsAllowed', 1, 1, 5)], 6);
|
|
12
12
|
}
|
|
13
13
|
}
|
package/dist/segments/HIKIM.js
CHANGED
|
@@ -5,7 +5,10 @@ import { SegmentDefinition } from '../segmentDefinition.js';
|
|
|
5
5
|
* Information to the customer of the bank
|
|
6
6
|
*/
|
|
7
7
|
export class HIKIM extends SegmentDefinition {
|
|
8
|
-
static Id =
|
|
8
|
+
static Id = 'HIKIM';
|
|
9
9
|
version = 2;
|
|
10
|
+
constructor() {
|
|
11
|
+
super(HIKIM.Id);
|
|
12
|
+
}
|
|
10
13
|
elements = [new AlphaNumeric('subject', 1, 1, 35), new Text('text', 1, 1, 2048)];
|
|
11
14
|
}
|
package/dist/segments/HIKOM.js
CHANGED
|
@@ -7,8 +7,11 @@ import { SegmentDefinition } from '../segmentDefinition.js';
|
|
|
7
7
|
*
|
|
8
8
|
*/
|
|
9
9
|
export class HIKOM extends SegmentDefinition {
|
|
10
|
-
static Id =
|
|
10
|
+
static Id = 'HIKOM';
|
|
11
11
|
version = 4;
|
|
12
|
+
constructor() {
|
|
13
|
+
super(HIKOM.Id);
|
|
14
|
+
}
|
|
12
15
|
elements = [
|
|
13
16
|
new BankIdentification('bank', 1, 1),
|
|
14
17
|
new Numeric('defLang', 1, 1, 3),
|
package/dist/segments/HIPINS.js
CHANGED
|
@@ -7,10 +7,10 @@ import { BusinessTransactionParameter } from './businessTransactionParameter.js'
|
|
|
7
7
|
* Parameters for PIN/TAN method
|
|
8
8
|
*/
|
|
9
9
|
export class HIPINS extends BusinessTransactionParameter {
|
|
10
|
-
static Id =
|
|
10
|
+
static Id = 'HIPINS';
|
|
11
11
|
version = 1;
|
|
12
12
|
constructor() {
|
|
13
|
-
super([
|
|
13
|
+
super(HIPINS.Id, [
|
|
14
14
|
new Numeric('minPinLen', 0, 1, 2),
|
|
15
15
|
new Numeric('maxPinLen', 0, 1, 2),
|
|
16
16
|
new Numeric('maxTanLen', 0, 1, 2),
|
package/dist/segments/HIRMG.js
CHANGED
|
@@ -6,8 +6,11 @@ import { SegmentDefinition } from '../segmentDefinition.js';
|
|
|
6
6
|
* Responses to the message
|
|
7
7
|
*/
|
|
8
8
|
export class HIRMG extends SegmentDefinition {
|
|
9
|
-
static Id =
|
|
9
|
+
static Id = 'HIRMG';
|
|
10
10
|
static Version = 2;
|
|
11
|
+
constructor() {
|
|
12
|
+
super(HIRMG.Id);
|
|
13
|
+
}
|
|
11
14
|
version = HIRMG.Version;
|
|
12
15
|
elements = [
|
|
13
16
|
new DataGroup('answers', [
|
package/dist/segments/HIRMS.js
CHANGED
|
@@ -6,8 +6,11 @@ import { SegmentDefinition } from '../segmentDefinition.js';
|
|
|
6
6
|
* Responses to the message
|
|
7
7
|
*/
|
|
8
8
|
export class HIRMS extends SegmentDefinition {
|
|
9
|
-
static Id =
|
|
9
|
+
static Id = 'HIRMS';
|
|
10
10
|
static Version = 2;
|
|
11
|
+
constructor() {
|
|
12
|
+
super(HIRMS.Id);
|
|
13
|
+
}
|
|
11
14
|
version = HIRMS.Version;
|
|
12
15
|
elements = [
|
|
13
16
|
new DataGroup('answers', [
|
package/dist/segments/HISAL.js
CHANGED
|
@@ -11,8 +11,11 @@ import { SegmentDefinition } from '../segmentDefinition.js';
|
|
|
11
11
|
* Account balances response
|
|
12
12
|
*/
|
|
13
13
|
export class HISAL extends SegmentDefinition {
|
|
14
|
-
static Id =
|
|
14
|
+
static Id = 'HISAL';
|
|
15
15
|
version = 8;
|
|
16
|
+
constructor() {
|
|
17
|
+
super(HISAL.Id);
|
|
18
|
+
}
|
|
16
19
|
elements = [
|
|
17
20
|
new AccountGroup('account', 1, 1, 1, 6),
|
|
18
21
|
new InternationalAccountGroup('account', 1, 1, 7),
|
package/dist/segments/HISYN.js
CHANGED
|
@@ -5,8 +5,11 @@ import { SegmentDefinition } from '../segmentDefinition.js';
|
|
|
5
5
|
* Synchonisation response
|
|
6
6
|
*/
|
|
7
7
|
export class HISYN extends SegmentDefinition {
|
|
8
|
-
static Id =
|
|
8
|
+
static Id = 'HISYN';
|
|
9
9
|
version = 4;
|
|
10
|
+
constructor() {
|
|
11
|
+
super(HISYN.Id);
|
|
12
|
+
}
|
|
10
13
|
elements = [
|
|
11
14
|
new Identification('systemId', 0, 1),
|
|
12
15
|
new Numeric('msgNr', 0, 1, 4),
|
package/dist/segments/HITAB.js
CHANGED
|
@@ -10,7 +10,10 @@ import { SegmentDefinition } from '../segmentDefinition.js';
|
|
|
10
10
|
* TAN media response
|
|
11
11
|
*/
|
|
12
12
|
export class HITAB extends SegmentDefinition {
|
|
13
|
-
static Id =
|
|
13
|
+
static Id = 'HITAB';
|
|
14
|
+
constructor() {
|
|
15
|
+
super(HITAB.Id);
|
|
16
|
+
}
|
|
14
17
|
version = 5;
|
|
15
18
|
elements = [
|
|
16
19
|
new Numeric('tanUsage', 1, 1, 1),
|
package/dist/segments/HITAN.js
CHANGED
|
@@ -8,8 +8,11 @@ import { SegmentDefinition } from '../segmentDefinition.js';
|
|
|
8
8
|
* TAN response
|
|
9
9
|
*/
|
|
10
10
|
export class HITAN extends SegmentDefinition {
|
|
11
|
-
static Id =
|
|
11
|
+
static Id = 'HITAN';
|
|
12
12
|
version = 7;
|
|
13
|
+
constructor() {
|
|
14
|
+
super(HITAN.Id);
|
|
15
|
+
}
|
|
13
16
|
elements = [
|
|
14
17
|
new AlphaNumeric('tanProcess', 1, 1, 1),
|
|
15
18
|
new Binary('orderHash', 0, 1, 256),
|
package/dist/segments/HITANS.js
CHANGED
|
@@ -8,10 +8,10 @@ import { BusinessTransactionParameter } from './businessTransactionParameter.js'
|
|
|
8
8
|
* Parameters for two-step TAN methods
|
|
9
9
|
*/
|
|
10
10
|
export class HITANS extends BusinessTransactionParameter {
|
|
11
|
-
static Id =
|
|
11
|
+
static Id = 'HITANS';
|
|
12
12
|
version = 7;
|
|
13
13
|
constructor() {
|
|
14
|
-
super([
|
|
14
|
+
super(HITANS.Id, [
|
|
15
15
|
new YesNo('oneStepAllowed', 1, 1),
|
|
16
16
|
new YesNo('multipleTansactions', 1, 1),
|
|
17
17
|
new Numeric('hashMethod', 1, 1, 1),
|
package/dist/segments/HIUPA.js
CHANGED
|
@@ -6,8 +6,11 @@ import { Identification } from '../dataElements/Identification.js';
|
|
|
6
6
|
* User parameters general
|
|
7
7
|
*/
|
|
8
8
|
export class HIUPA extends SegmentDefinition {
|
|
9
|
-
static Id =
|
|
9
|
+
static Id = 'HIUPA';
|
|
10
10
|
version = 4;
|
|
11
|
+
constructor() {
|
|
12
|
+
super(HIUPA.Id);
|
|
13
|
+
}
|
|
11
14
|
elements = [
|
|
12
15
|
new Identification('internalUserId', 1, 1),
|
|
13
16
|
new Numeric('updVersion', 1, 1, 3),
|
package/dist/segments/HIUPD.js
CHANGED
|
@@ -10,8 +10,11 @@ import { SegmentDefinition } from '../segmentDefinition.js';
|
|
|
10
10
|
* User parameters account information
|
|
11
11
|
*/
|
|
12
12
|
export class HIUPD extends SegmentDefinition {
|
|
13
|
-
static Id =
|
|
13
|
+
static Id = 'HIUPD';
|
|
14
14
|
version = 6;
|
|
15
|
+
constructor() {
|
|
16
|
+
super(HIUPD.Id);
|
|
17
|
+
}
|
|
15
18
|
elements = [
|
|
16
19
|
new AccountGroup('account', 0, 1),
|
|
17
20
|
new AlphaNumeric('iban', 0, 1, 34),
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Binary } from '../dataElements/Binary.js';
|
|
2
|
+
import { SegmentDefinition } from '../segmentDefinition.js';
|
|
3
|
+
/**
|
|
4
|
+
* Geschäftsvorfälle: C.4.3.1 Kreditinstitutsrückmeldung
|
|
5
|
+
* Version: 6
|
|
6
|
+
*/
|
|
7
|
+
export class HIWPD extends SegmentDefinition {
|
|
8
|
+
static Id = 'HIWPD';
|
|
9
|
+
static Version = 6;
|
|
10
|
+
constructor() {
|
|
11
|
+
super(HIWPD.Id);
|
|
12
|
+
}
|
|
13
|
+
version = HIWPD.Version;
|
|
14
|
+
elements = [new Binary('portfolioStatement', 1, 1)];
|
|
15
|
+
}
|
package/dist/segments/HKEND.js
CHANGED
|
@@ -4,8 +4,11 @@ import { SegmentDefinition } from '../segmentDefinition.js';
|
|
|
4
4
|
* Dialog end
|
|
5
5
|
*/
|
|
6
6
|
export class HKEND extends SegmentDefinition {
|
|
7
|
-
static Id =
|
|
7
|
+
static Id = 'HKEND';
|
|
8
8
|
static Version = 1;
|
|
9
|
+
constructor() {
|
|
10
|
+
super(HKEND.Id);
|
|
11
|
+
}
|
|
9
12
|
version = HKEND.Version;
|
|
10
13
|
elements = [new Identification('dialogId', 1, 1)];
|
|
11
14
|
}
|
package/dist/segments/HKIDN.js
CHANGED
|
@@ -6,8 +6,11 @@ import { SegmentDefinition } from '../segmentDefinition.js';
|
|
|
6
6
|
* Identification
|
|
7
7
|
*/
|
|
8
8
|
export class HKIDN extends SegmentDefinition {
|
|
9
|
-
static Id =
|
|
9
|
+
static Id = 'HKIDN';
|
|
10
10
|
static Version = 2;
|
|
11
|
+
constructor() {
|
|
12
|
+
super(HKIDN.Id);
|
|
13
|
+
}
|
|
11
14
|
version = HKIDN.Version;
|
|
12
15
|
elements = [
|
|
13
16
|
new BankIdentification('bank', 1, 1),
|
package/dist/segments/HKKAZ.js
CHANGED
|
@@ -9,8 +9,11 @@ import { SegmentDefinition } from '../segmentDefinition.js';
|
|
|
9
9
|
* Request account transactions in a given period
|
|
10
10
|
*/
|
|
11
11
|
export class HKKAZ extends SegmentDefinition {
|
|
12
|
-
static Id =
|
|
12
|
+
static Id = 'HKKAZ';
|
|
13
13
|
static Version = 7;
|
|
14
|
+
constructor() {
|
|
15
|
+
super(HKKAZ.Id);
|
|
16
|
+
}
|
|
14
17
|
version = HKKAZ.Version;
|
|
15
18
|
elements = [
|
|
16
19
|
new AccountGroup('account', 1, 1, 1, 6),
|
package/dist/segments/HKSAL.js
CHANGED
|
@@ -8,8 +8,11 @@ import { SegmentDefinition } from '../segmentDefinition.js';
|
|
|
8
8
|
* Request Account balances
|
|
9
9
|
*/
|
|
10
10
|
export class HKSAL extends SegmentDefinition {
|
|
11
|
-
static Id =
|
|
11
|
+
static Id = 'HKSAL';
|
|
12
12
|
static Version = 8;
|
|
13
|
+
constructor() {
|
|
14
|
+
super(HKSAL.Id);
|
|
15
|
+
}
|
|
13
16
|
version = HKSAL.Version;
|
|
14
17
|
elements = [
|
|
15
18
|
new AccountGroup('account', 1, 1, 1, 6),
|