@fin.cx/compatibility 5.0.0 → 5.1.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/dist_ts/00_commitinfo_data.js +3 -3
- package/dist_ts/buchungsliste/index.d.ts +80 -0
- package/dist_ts/buchungsliste/index.js +385 -0
- package/dist_ts/buchungsliste/plugins.d.ts +2 -0
- package/dist_ts/buchungsliste/plugins.js +3 -0
- package/dist_ts/encoding/index.d.ts +8 -0
- package/dist_ts/encoding/index.js +16 -1
- package/dist_ts/error.d.ts +21 -4
- package/dist_ts/error.js +3 -2
- package/dist_ts/extf/index.d.ts +8 -6
- package/dist_ts/extf/index.js +7 -6
- package/dist_ts/index.d.ts +1 -0
- package/dist_ts/index.js +2 -1
- package/package.json +12 -7
- package/readme.md +52 -3
- package/ts/00_commitinfo_data.ts +2 -2
- package/ts/buchungsliste/index.ts +499 -0
- package/ts/buchungsliste/plugins.ts +3 -0
- package/ts/encoding/index.ts +17 -0
- package/ts/error.ts +30 -4
- package/ts/extf/index.ts +8 -6
- package/ts/index.ts +1 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@fin.cx/compatibility',
|
|
6
|
-
version: '5.
|
|
7
|
-
description: 'Exchange formats of third-party accounting systems: the EXTF Buchungsstapel in Windows-1252 on the rows of @fin.cx/skr,
|
|
6
|
+
version: '5.1.0',
|
|
7
|
+
description: 'Exchange formats of third-party accounting systems: the EXTF Buchungsstapel in Windows-1252 on the rows of @fin.cx/skr, the document-image archive (document.xml and ZIP), and a payroll program\'s Buchungsliste read into journal drafts'
|
|
8
8
|
};
|
|
9
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
9
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx1QkFBdUI7SUFDN0IsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLDRPQUE0TztDQUMxUCxDQUFBIn0=
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
export { CompatibilityError, type TCompatibilityErrorCode, type ICompatibilityErrorDetails } from '../error.js';
|
|
3
|
+
export type IDatevBuchungsstapelRow = plugins.skrCore.IDatevBuchungsstapelRow;
|
|
4
|
+
/** One row of a Buchungsliste as the file states it: one booking of the amount from the Sollkonto to the Habenkonto. */
|
|
5
|
+
export interface IBuchungslisteRow {
|
|
6
|
+
/** the data row, from 1 */
|
|
7
|
+
row: number;
|
|
8
|
+
/** the line of the file it begins on, from 1: the first line names the fields */
|
|
9
|
+
line: number;
|
|
10
|
+
/** the Belegdatum, midnight UTC; the draft's day */
|
|
11
|
+
belegDatum: Date;
|
|
12
|
+
/** the Buchungsdatum, midnight UTC, where the file states one; kept, not booked (a draft is dated by its Belegdatum) */
|
|
13
|
+
buchungsDatum?: Date;
|
|
14
|
+
/** the Buchungsperiode, where the file states one; kept, not booked */
|
|
15
|
+
buchungsperiode?: number;
|
|
16
|
+
belegnummernkreis?: string;
|
|
17
|
+
belegnummer?: string;
|
|
18
|
+
/** empty where the file states none */
|
|
19
|
+
buchungstext: string;
|
|
20
|
+
/** the Buchungsbetrag in cents as the file states it, negative with a minus sign; never 0 */
|
|
21
|
+
betragCents: number;
|
|
22
|
+
sollkonto: string;
|
|
23
|
+
habenkonto: string;
|
|
24
|
+
/** the Steuerschlüssel, one of DATEV's keys `@fin.cx/skr` knows; empty where the file states none */
|
|
25
|
+
steuerschluessel: plugins.skrCore.TBuKey;
|
|
26
|
+
/** DATEV's KOST1 */
|
|
27
|
+
kostenstelle?: string;
|
|
28
|
+
/** DATEV's KOST2 */
|
|
29
|
+
kostentraeger?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface IBuchungsliste {
|
|
32
|
+
/** in the order of the file */
|
|
33
|
+
rows: IBuchungslisteRow[];
|
|
34
|
+
/** what was read but not taken, each naming its column: a column of a name this reader does not know, or of none */
|
|
35
|
+
warnings: string[];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Reads a Buchungsliste: Windows-1252 bytes, the first line naming the fields, one booking per line after it. The
|
|
39
|
+
* columns are taken by their names (in any case, with or without spaces, the umlauts written out or not), the
|
|
40
|
+
* delimiter from the first line (semicolon, tab or comma); a field may stand in double quotes, a quote written twice
|
|
41
|
+
* inside; lines end in CRLF, LF or CR; a line that states nothing is skipped. A column of a name this reader does not
|
|
42
|
+
* know is not read and named in `warnings`.
|
|
43
|
+
*
|
|
44
|
+
* Refuses with `CompatibilityError` `invalid_buchungsliste`, naming the line, the row and the field: a file in UTF-8
|
|
45
|
+
* with a byte order mark; no first line; a required column missing (Belegdatum, Buchungsbetrag, Sollkonto,
|
|
46
|
+
* Habenkonto), or two columns of one field; a row of another number of fields than the first line; a day not
|
|
47
|
+
* TT.MM.JJJJ; an amount not of the form above, or 0; an account not of 1 to 9 digits; a Steuerschlüssel `@fin.cx/skr`
|
|
48
|
+
* does not know; a Buchungsperiode not of 1 or 2 digits; a Währung other than EUR; and a row that states Zusatzangaben,
|
|
49
|
+
* an EU Land or an EU %, which this version does not map.
|
|
50
|
+
*/
|
|
51
|
+
export declare const readBuchungsliste: (bytesArg: Uint8Array) => IBuchungsliste;
|
|
52
|
+
/**
|
|
53
|
+
* The Buchungsstapel row of a Buchungsliste row (`@fin.cx/skr` `IDatevBuchungsstapelRow`): the amount on the Sollkonto
|
|
54
|
+
* as Konto, the Habenkonto as Gegenkonto. A negative amount keeps both accounts in their places and books the other
|
|
55
|
+
* way, as a Buchungsstapel states it, by its Soll/Haben mark: `H`, the amount without its sign. It is no general
|
|
56
|
+
* reversal (field 118). The Steuerschlüssel is the BU key; the Kostenstelle KOST1 and the Kostenträger KOST2;
|
|
57
|
+
* Belegfeld 1 the Belegnummernkreis and Belegnummer (`belegfeld1Of`), as the file states them: skr's writer maps a
|
|
58
|
+
* value DATEV does not take when it writes the row (skr 7.3.0 and later).
|
|
59
|
+
*/
|
|
60
|
+
export declare const buchungslisteRowToDatevRow: (rowArg: IBuchungslisteRow) => IDatevBuchungsstapelRow;
|
|
61
|
+
/** One row of a Buchungsliste with the Buchungsstapel row and the journal draft it gives. */
|
|
62
|
+
export interface IImportedBuchungslisteRow {
|
|
63
|
+
/** the data row, from 1 */
|
|
64
|
+
row: number;
|
|
65
|
+
buchungslisteRow: IBuchungslisteRow;
|
|
66
|
+
datevRow: IDatevBuchungsstapelRow;
|
|
67
|
+
draft: plugins.skrCore.IJournalDraft;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Reads a Buchungsliste and gives each row's journal draft, as `@fin.cx/skr` `importExtfBuchungsstapel` gives a
|
|
71
|
+
* Buchungsstapel's: each row's Buchungsstapel row (`buchungslisteRowToDatevRow`) booked by skr's
|
|
72
|
+
* `journalDraftOfDatevRow` (its tax unfolded onto the policy's tax accounts by its key or by the automatism of its
|
|
73
|
+
* account) and checked against the policy (`validateJournalDraft`). Refuses the first row that cannot be read or
|
|
74
|
+
* booked with `CompatibilityError` `invalid_buchungsliste`, naming it (`details.row`, `.line`; what the check found in
|
|
75
|
+
* `details.errors`). What the reading did not take is in `warnings` (`readBuchungsliste`).
|
|
76
|
+
*/
|
|
77
|
+
export declare const importBuchungsliste: (bytesArg: Uint8Array, policyArg: plugins.skrCore.IAccountPolicy) => {
|
|
78
|
+
entries: IImportedBuchungslisteRow[];
|
|
79
|
+
warnings: string[];
|
|
80
|
+
};
|
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import { CompatibilityError } from '../error.js';
|
|
3
|
+
import { decodeWindows1252 } from '../encoding/index.js';
|
|
4
|
+
export { CompatibilityError } from '../error.js';
|
|
5
|
+
/** How a field is named in a refusal when the file names no column of it. */
|
|
6
|
+
const FIELD_NAMES = {
|
|
7
|
+
belegdatum: 'Belegdatum',
|
|
8
|
+
buchungsdatum: 'Buchungsdatum',
|
|
9
|
+
buchungsperiode: 'Buchungsperiode',
|
|
10
|
+
belegnummernkreis: 'Belegnummernkreis',
|
|
11
|
+
belegnummer: 'Belegnummer',
|
|
12
|
+
buchungstext: 'Buchungstext',
|
|
13
|
+
buchungsbetrag: 'Buchungsbetrag',
|
|
14
|
+
waehrung: 'Währung',
|
|
15
|
+
sollkonto: 'Sollkonto',
|
|
16
|
+
habenkonto: 'Habenkonto',
|
|
17
|
+
steuerschluessel: 'Steuerschlüssel',
|
|
18
|
+
kostenstelle: 'Kostenstelle',
|
|
19
|
+
kostentraeger: 'Kostenträger',
|
|
20
|
+
zusatzangaben: 'Zusatzangaben',
|
|
21
|
+
euLand: 'EU Land',
|
|
22
|
+
euProzent: 'EU %',
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* The field of each column name, as `columnKeyOf` reads it. The names are the import's (section 2 of its
|
|
26
|
+
* description), and "Währung", "Kostenstelle 1" and "Kostenstelle 2" a payroll program may write instead (unconfirmed):
|
|
27
|
+
* the second cost-centre level is DATEV's KOST2, as the Kostenträger is.
|
|
28
|
+
*/
|
|
29
|
+
const FIELD_OF_COLUMN = new Map([
|
|
30
|
+
['belegdatum', 'belegdatum'],
|
|
31
|
+
['buchungsdatum', 'buchungsdatum'],
|
|
32
|
+
['buchungsperiode', 'buchungsperiode'],
|
|
33
|
+
['belegnummernkreis', 'belegnummernkreis'],
|
|
34
|
+
['belegnummer', 'belegnummer'],
|
|
35
|
+
['buchungstext', 'buchungstext'],
|
|
36
|
+
['buchungsbetrag', 'buchungsbetrag'],
|
|
37
|
+
['waehrung', 'waehrung'],
|
|
38
|
+
['sollkonto', 'sollkonto'],
|
|
39
|
+
['habenkonto', 'habenkonto'],
|
|
40
|
+
['steuerschluessel', 'steuerschluessel'],
|
|
41
|
+
['kostenstelle', 'kostenstelle'],
|
|
42
|
+
['kostenstelle1', 'kostenstelle'],
|
|
43
|
+
['kostentraeger', 'kostentraeger'],
|
|
44
|
+
['kostenstelle2', 'kostentraeger'],
|
|
45
|
+
['zusatzangaben', 'zusatzangaben'],
|
|
46
|
+
['euland', 'euLand'],
|
|
47
|
+
['eu%', 'euProzent'],
|
|
48
|
+
]);
|
|
49
|
+
/** The fields a row cannot be booked without. */
|
|
50
|
+
const REQUIRED_FIELDS = ['belegdatum', 'buchungsbetrag', 'sollkonto', 'habenkonto'];
|
|
51
|
+
/** The fields this version reads but does not map (§ 13b and the OSS cases need a decision it does not make): a row that states one is refused. */
|
|
52
|
+
const UNMAPPED_FIELDS = ['zusatzangaben', 'euLand', 'euProzent'];
|
|
53
|
+
/** The delimiters a Buchungsliste may separate its fields by (unconfirmed), in the order the first line is tried for them. */
|
|
54
|
+
const DELIMITERS = [';', '\t', ','];
|
|
55
|
+
/**
|
|
56
|
+
* A column name as it is compared: in lower case, the umlauts and ß written out (ae, oe, ue, ss), without spaces and
|
|
57
|
+
* other punctuation but `%`. "Kostenträger", "KOSTENTRAEGER" and "Kosten träger" are one name.
|
|
58
|
+
*/
|
|
59
|
+
const columnKeyOf = (nameArg) => nameArg
|
|
60
|
+
.toLowerCase()
|
|
61
|
+
.replace(/ä/gu, 'ae')
|
|
62
|
+
.replace(/ö/gu, 'oe')
|
|
63
|
+
.replace(/ü/gu, 'ue')
|
|
64
|
+
.replace(/ß/gu, 'ss')
|
|
65
|
+
.replace(/[^a-z0-9%]/gu, '');
|
|
66
|
+
/** A Buchungsliste that cannot be read as it is. */
|
|
67
|
+
const refusal = (messageArg, detailsArg) => new CompatibilityError('invalid_buchungsliste', messageArg, detailsArg);
|
|
68
|
+
/**
|
|
69
|
+
* The delimiter the first line separates its names by: the first of semicolon, tab and comma that stands in it outside
|
|
70
|
+
* double quotes (unconfirmed which one lohn+gehalt writes). Refuses a first line that has none: a single name cannot
|
|
71
|
+
* be the four fields a row needs.
|
|
72
|
+
*/
|
|
73
|
+
const delimiterOf = (firstLineArg, lineArg) => {
|
|
74
|
+
const outside = new Set();
|
|
75
|
+
let quoted = false;
|
|
76
|
+
for (const char of firstLineArg) {
|
|
77
|
+
if (char === '"') {
|
|
78
|
+
quoted = !quoted;
|
|
79
|
+
}
|
|
80
|
+
else if (!quoted) {
|
|
81
|
+
outside.add(char);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const delimiter = DELIMITERS.find((delimiterArg) => outside.has(delimiterArg));
|
|
85
|
+
if (delimiter === undefined) {
|
|
86
|
+
throw refusal(`line ${lineArg}: the field names of the first line are separated by none of semicolon, tab and comma`, { line: lineArg });
|
|
87
|
+
}
|
|
88
|
+
return delimiter;
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* The records of the text, each with the line it begins on: fields separated by the delimiter, a record ending at
|
|
92
|
+
* CRLF, LF or CR outside double quotes. A field in double quotes holds the delimiter, a line break and a quote written
|
|
93
|
+
* twice. A quote left open to the end of the text, or text after a closing quote before the next delimiter, is no
|
|
94
|
+
* field of such a file: refused, naming the line.
|
|
95
|
+
*/
|
|
96
|
+
const recordsOf = (textArg, delimiterArg) => {
|
|
97
|
+
const records = [];
|
|
98
|
+
let fields = [];
|
|
99
|
+
let current = '';
|
|
100
|
+
let quoted = false;
|
|
101
|
+
let closed = false;
|
|
102
|
+
let line = 1;
|
|
103
|
+
let recordLine = 1;
|
|
104
|
+
const endField = () => {
|
|
105
|
+
fields.push(current);
|
|
106
|
+
current = '';
|
|
107
|
+
closed = false;
|
|
108
|
+
};
|
|
109
|
+
for (let index = 0; index < textArg.length; index += 1) {
|
|
110
|
+
const char = textArg[index];
|
|
111
|
+
const lineBreak = char === '\n' || (char === '\r' && textArg[index + 1] !== '\n');
|
|
112
|
+
if (quoted) {
|
|
113
|
+
if (char === '"' && textArg[index + 1] === '"') {
|
|
114
|
+
current += '"';
|
|
115
|
+
index += 1;
|
|
116
|
+
}
|
|
117
|
+
else if (char === '"') {
|
|
118
|
+
quoted = false;
|
|
119
|
+
closed = true;
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
current += char;
|
|
123
|
+
line += lineBreak ? 1 : 0;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
else if (char === delimiterArg) {
|
|
127
|
+
endField();
|
|
128
|
+
}
|
|
129
|
+
else if (char === '\r' || char === '\n') {
|
|
130
|
+
if (lineBreak) {
|
|
131
|
+
endField();
|
|
132
|
+
records.push({ line: recordLine, fields });
|
|
133
|
+
fields = [];
|
|
134
|
+
line += 1;
|
|
135
|
+
recordLine = line;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
else if (closed) {
|
|
139
|
+
throw refusal(`line ${line}: field ${fields.length + 1} has text after its closing quote`, { line });
|
|
140
|
+
}
|
|
141
|
+
else if (char === '"' && current === '') {
|
|
142
|
+
quoted = true;
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
current += char;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (quoted) {
|
|
149
|
+
throw refusal(`line ${recordLine}: field ${fields.length + 1} opens a quote that is never closed`, { line: recordLine });
|
|
150
|
+
}
|
|
151
|
+
// the last record, where the text does not end in a line break
|
|
152
|
+
if (current !== '' || closed || fields.length > 0) {
|
|
153
|
+
endField();
|
|
154
|
+
records.push({ line: recordLine, fields });
|
|
155
|
+
}
|
|
156
|
+
return records;
|
|
157
|
+
};
|
|
158
|
+
/** Whether a record states nothing: an empty line, or one of empty fields only. */
|
|
159
|
+
const isBlank = (recordArg) => recordArg.fields.every((fieldArg) => fieldArg.trim() === '');
|
|
160
|
+
/** A day from `TT.MM.JJJJ`, midnight UTC, or undefined. */
|
|
161
|
+
const dayOf = (valueArg) => {
|
|
162
|
+
const match = /^(\d{2})\.(\d{2})\.(\d{4})$/u.exec(valueArg);
|
|
163
|
+
if (!match)
|
|
164
|
+
return undefined;
|
|
165
|
+
const [day, month, year] = [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
166
|
+
const date = new Date(Date.UTC(year, month - 1, day));
|
|
167
|
+
return date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day ? date : undefined;
|
|
168
|
+
};
|
|
169
|
+
/**
|
|
170
|
+
* An amount in cents from a decimal comma with at most 2 digits after it, at most 10 digits before it, optionally
|
|
171
|
+
* grouped by dots in threes and led by a minus sign ("1.234,56", "-250,00", "250"), or undefined.
|
|
172
|
+
*/
|
|
173
|
+
const centsOf = (valueArg) => {
|
|
174
|
+
const match = /^(-?)(\d{1,3}(?:\.\d{3})+|\d+)(?:,(\d{1,2}))?$/u.exec(valueArg);
|
|
175
|
+
if (!match)
|
|
176
|
+
return undefined;
|
|
177
|
+
const euros = match[2].replace(/\./gu, '');
|
|
178
|
+
if (euros.length > 10)
|
|
179
|
+
return undefined;
|
|
180
|
+
const cents = Number(euros) * 100 + Number((match[3] ?? '').padEnd(2, '0'));
|
|
181
|
+
return match[1] === '-' ? -cents : cents;
|
|
182
|
+
};
|
|
183
|
+
/**
|
|
184
|
+
* Reads a Buchungsliste: Windows-1252 bytes, the first line naming the fields, one booking per line after it. The
|
|
185
|
+
* columns are taken by their names (in any case, with or without spaces, the umlauts written out or not), the
|
|
186
|
+
* delimiter from the first line (semicolon, tab or comma); a field may stand in double quotes, a quote written twice
|
|
187
|
+
* inside; lines end in CRLF, LF or CR; a line that states nothing is skipped. A column of a name this reader does not
|
|
188
|
+
* know is not read and named in `warnings`.
|
|
189
|
+
*
|
|
190
|
+
* Refuses with `CompatibilityError` `invalid_buchungsliste`, naming the line, the row and the field: a file in UTF-8
|
|
191
|
+
* with a byte order mark; no first line; a required column missing (Belegdatum, Buchungsbetrag, Sollkonto,
|
|
192
|
+
* Habenkonto), or two columns of one field; a row of another number of fields than the first line; a day not
|
|
193
|
+
* TT.MM.JJJJ; an amount not of the form above, or 0; an account not of 1 to 9 digits; a Steuerschlüssel `@fin.cx/skr`
|
|
194
|
+
* does not know; a Buchungsperiode not of 1 or 2 digits; a Währung other than EUR; and a row that states Zusatzangaben,
|
|
195
|
+
* an EU Land or an EU %, which this version does not map.
|
|
196
|
+
*/
|
|
197
|
+
export const readBuchungsliste = (bytesArg) => {
|
|
198
|
+
if (bytesArg[0] === 0xef && bytesArg[1] === 0xbb && bytesArg[2] === 0xbf) {
|
|
199
|
+
throw refusal('the Buchungsliste begins with the byte order mark of UTF-8; it is read as Windows-1252 (ANSI)', { line: 1 });
|
|
200
|
+
}
|
|
201
|
+
const text = decodeWindows1252(bytesArg);
|
|
202
|
+
const lines = text.split(/\r\n|\n|\r/u);
|
|
203
|
+
// the first line that states anything names the fields
|
|
204
|
+
const firstLineIndex = lines.findIndex((lineArg) => /[^\s;,"]/u.test(lineArg));
|
|
205
|
+
if (firstLineIndex < 0) {
|
|
206
|
+
throw refusal('the Buchungsliste has no first line naming its fields', {});
|
|
207
|
+
}
|
|
208
|
+
const delimiter = delimiterOf(lines[firstLineIndex], firstLineIndex + 1);
|
|
209
|
+
const [captionRecord, ...dataRecords] = recordsOf(text, delimiter).filter((recordArg) => !isBlank(recordArg));
|
|
210
|
+
if (captionRecord === undefined) {
|
|
211
|
+
throw refusal('the Buchungsliste has no first line naming its fields', {});
|
|
212
|
+
}
|
|
213
|
+
const captions = captionRecord.fields.map((fieldArg) => fieldArg.trim());
|
|
214
|
+
// the column of each field the first line names
|
|
215
|
+
const warnings = [];
|
|
216
|
+
const columnOf = new Map();
|
|
217
|
+
captions.forEach((captionArg, columnArg) => {
|
|
218
|
+
const field = FIELD_OF_COLUMN.get(columnKeyOf(captionArg));
|
|
219
|
+
if (field === undefined) {
|
|
220
|
+
warnings.push(captionArg === ''
|
|
221
|
+
? `column ${columnArg + 1} has no name; its values are not read`
|
|
222
|
+
: `column ${columnArg + 1} "${captionArg}" is no field of a Buchungsliste this reader knows; its values are not read`);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
const earlier = columnOf.get(field);
|
|
226
|
+
if (earlier !== undefined) {
|
|
227
|
+
throw refusal(`line ${captionRecord.line}: columns ${earlier + 1} "${captions[earlier]}" and ${columnArg + 1} "${captionArg}" both state the ${FIELD_NAMES[field]}`, { line: captionRecord.line, field: captionArg });
|
|
228
|
+
}
|
|
229
|
+
columnOf.set(field, columnArg);
|
|
230
|
+
});
|
|
231
|
+
const missing = REQUIRED_FIELDS.filter((fieldArg) => !columnOf.has(fieldArg));
|
|
232
|
+
if (missing.length > 0) {
|
|
233
|
+
throw refusal(`line ${captionRecord.line}: the Buchungsliste has no column ${missing.map((fieldArg) => FIELD_NAMES[fieldArg]).join(', ')}`, {
|
|
234
|
+
line: captionRecord.line,
|
|
235
|
+
field: FIELD_NAMES[missing[0]],
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
/** the name of a field's column as the file states it */
|
|
239
|
+
const nameOf = (fieldArg) => {
|
|
240
|
+
const column = columnOf.get(fieldArg);
|
|
241
|
+
return column === undefined ? FIELD_NAMES[fieldArg] : captions[column];
|
|
242
|
+
};
|
|
243
|
+
const rows = dataRecords.map((recordArg, indexArg) => {
|
|
244
|
+
const row = indexArg + 1;
|
|
245
|
+
const { line } = recordArg;
|
|
246
|
+
if (recordArg.fields.length !== captions.length) {
|
|
247
|
+
throw refusal(`row ${row} (line ${line}) has ${recordArg.fields.length} fields, not the ${captions.length} of the first line`, { row, line });
|
|
248
|
+
}
|
|
249
|
+
const at = (fieldArg) => {
|
|
250
|
+
const column = columnOf.get(fieldArg);
|
|
251
|
+
return column === undefined ? '' : recordArg.fields[column].trim();
|
|
252
|
+
};
|
|
253
|
+
const malformed = (fieldArg, messageArg) => refusal(`row ${row} (line ${line}): ${messageArg}`, { row, line, field: nameOf(fieldArg), value: at(fieldArg) });
|
|
254
|
+
const day = (fieldArg) => {
|
|
255
|
+
if (at(fieldArg) === '')
|
|
256
|
+
return undefined;
|
|
257
|
+
const value = dayOf(at(fieldArg));
|
|
258
|
+
if (!value)
|
|
259
|
+
throw malformed(fieldArg, `the ${nameOf(fieldArg)} "${at(fieldArg)}" is no day TT.MM.JJJJ`);
|
|
260
|
+
return value;
|
|
261
|
+
};
|
|
262
|
+
const account = (fieldArg) => {
|
|
263
|
+
if (!/^\d{1,9}$/u.test(at(fieldArg)))
|
|
264
|
+
throw malformed(fieldArg, `the ${nameOf(fieldArg)} "${at(fieldArg)}" is no account number of 1 to 9 digits`);
|
|
265
|
+
return at(fieldArg);
|
|
266
|
+
};
|
|
267
|
+
for (const field of UNMAPPED_FIELDS) {
|
|
268
|
+
if (at(field) !== '') {
|
|
269
|
+
throw malformed(field, `the row states ${nameOf(field)} "${at(field)}"; this version maps no Zusatzangaben (§ 13b), EU Land or EU %`);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
const currency = at('waehrung');
|
|
273
|
+
if (currency !== '' && currency !== 'EUR') {
|
|
274
|
+
throw malformed('waehrung', `the row is in ${currency}; these books are in euro`);
|
|
275
|
+
}
|
|
276
|
+
const belegDatum = day('belegdatum');
|
|
277
|
+
if (!belegDatum)
|
|
278
|
+
throw malformed('belegdatum', `the row states no ${nameOf('belegdatum')}`);
|
|
279
|
+
const buchungsDatum = day('buchungsdatum');
|
|
280
|
+
const period = at('buchungsperiode');
|
|
281
|
+
if (period !== '' && !/^\d{1,2}$/u.test(period)) {
|
|
282
|
+
throw malformed('buchungsperiode', `the ${nameOf('buchungsperiode')} "${period}" is no period of 1 or 2 digits`);
|
|
283
|
+
}
|
|
284
|
+
const betragCents = centsOf(at('buchungsbetrag'));
|
|
285
|
+
if (betragCents === undefined) {
|
|
286
|
+
throw malformed('buchungsbetrag', `the ${nameOf('buchungsbetrag')} "${at('buchungsbetrag')}" is no amount of digits with, if any, a decimal comma and at most 2 decimals (at most 10 digits before it)`);
|
|
287
|
+
}
|
|
288
|
+
if (betragCents === 0)
|
|
289
|
+
throw malformed('buchungsbetrag', `the ${nameOf('buchungsbetrag')} "${at('buchungsbetrag')}" books nothing`);
|
|
290
|
+
const sollkonto = account('sollkonto');
|
|
291
|
+
const habenkonto = account('habenkonto');
|
|
292
|
+
const key = at('steuerschluessel');
|
|
293
|
+
if (!plugins.skrCore.isKnownBuKey(key)) {
|
|
294
|
+
throw malformed('steuerschluessel', `the ${nameOf('steuerschluessel')} "${key}" is no DATEV key these books know (${Object.keys(plugins.skrCore.BU_KEY_RULES).join(', ')})`);
|
|
295
|
+
}
|
|
296
|
+
const texts = {
|
|
297
|
+
...(at('belegnummernkreis') !== '' ? { belegnummernkreis: at('belegnummernkreis') } : {}),
|
|
298
|
+
...(at('belegnummer') !== '' ? { belegnummer: at('belegnummer') } : {}),
|
|
299
|
+
...(at('kostenstelle') !== '' ? { kostenstelle: at('kostenstelle') } : {}),
|
|
300
|
+
...(at('kostentraeger') !== '' ? { kostentraeger: at('kostentraeger') } : {}),
|
|
301
|
+
};
|
|
302
|
+
return {
|
|
303
|
+
row,
|
|
304
|
+
line,
|
|
305
|
+
belegDatum,
|
|
306
|
+
...(buchungsDatum ? { buchungsDatum } : {}),
|
|
307
|
+
...(period !== '' ? { buchungsperiode: Number(period) } : {}),
|
|
308
|
+
buchungstext: at('buchungstext'),
|
|
309
|
+
betragCents,
|
|
310
|
+
sollkonto,
|
|
311
|
+
habenkonto,
|
|
312
|
+
steuerschluessel: key,
|
|
313
|
+
...texts,
|
|
314
|
+
};
|
|
315
|
+
});
|
|
316
|
+
return { rows, warnings };
|
|
317
|
+
};
|
|
318
|
+
/**
|
|
319
|
+
* Belegfeld 1 of a row: its Belegnummer, led by its Belegnummernkreis where the file states both and the Belegnummer
|
|
320
|
+
* does not already begin with it (the payroll program forms the Belegnummer from the Nummernkreis, the year, the month
|
|
321
|
+
* and the Lohnart; whether the file states the Nummernkreis inside it as well is unconfirmed), the two joined without
|
|
322
|
+
* a separator. Either alone where the file states only one.
|
|
323
|
+
*/
|
|
324
|
+
const belegfeld1Of = (rowArg) => {
|
|
325
|
+
const { belegnummernkreis, belegnummer } = rowArg;
|
|
326
|
+
if (belegnummernkreis !== undefined && belegnummer !== undefined) {
|
|
327
|
+
return belegnummer.startsWith(belegnummernkreis) ? belegnummer : `${belegnummernkreis}${belegnummer}`;
|
|
328
|
+
}
|
|
329
|
+
return belegnummer ?? belegnummernkreis;
|
|
330
|
+
};
|
|
331
|
+
/**
|
|
332
|
+
* The Buchungsstapel row of a Buchungsliste row (`@fin.cx/skr` `IDatevBuchungsstapelRow`): the amount on the Sollkonto
|
|
333
|
+
* as Konto, the Habenkonto as Gegenkonto. A negative amount keeps both accounts in their places and books the other
|
|
334
|
+
* way, as a Buchungsstapel states it, by its Soll/Haben mark: `H`, the amount without its sign. It is no general
|
|
335
|
+
* reversal (field 118). The Steuerschlüssel is the BU key; the Kostenstelle KOST1 and the Kostenträger KOST2;
|
|
336
|
+
* Belegfeld 1 the Belegnummernkreis and Belegnummer (`belegfeld1Of`), as the file states them: skr's writer maps a
|
|
337
|
+
* value DATEV does not take when it writes the row (skr 7.3.0 and later).
|
|
338
|
+
*/
|
|
339
|
+
export const buchungslisteRowToDatevRow = (rowArg) => {
|
|
340
|
+
const belegfeld1 = belegfeld1Of(rowArg);
|
|
341
|
+
return {
|
|
342
|
+
umsatzCents: Math.abs(rowArg.betragCents),
|
|
343
|
+
sollHaben: rowArg.betragCents < 0 ? 'H' : 'S',
|
|
344
|
+
konto: rowArg.sollkonto,
|
|
345
|
+
gegenKonto: rowArg.habenkonto,
|
|
346
|
+
buSchluessel: rowArg.steuerschluessel,
|
|
347
|
+
belegDatum: rowArg.belegDatum,
|
|
348
|
+
...(belegfeld1 !== undefined ? { belegfeld1 } : {}),
|
|
349
|
+
buchungstext: rowArg.buchungstext,
|
|
350
|
+
...(rowArg.kostenstelle !== undefined ? { kost1: rowArg.kostenstelle } : {}),
|
|
351
|
+
...(rowArg.kostentraeger !== undefined ? { kost2: rowArg.kostentraeger } : {}),
|
|
352
|
+
};
|
|
353
|
+
};
|
|
354
|
+
/**
|
|
355
|
+
* Reads a Buchungsliste and gives each row's journal draft, as `@fin.cx/skr` `importExtfBuchungsstapel` gives a
|
|
356
|
+
* Buchungsstapel's: each row's Buchungsstapel row (`buchungslisteRowToDatevRow`) booked by skr's
|
|
357
|
+
* `journalDraftOfDatevRow` (its tax unfolded onto the policy's tax accounts by its key or by the automatism of its
|
|
358
|
+
* account) and checked against the policy (`validateJournalDraft`). Refuses the first row that cannot be read or
|
|
359
|
+
* booked with `CompatibilityError` `invalid_buchungsliste`, naming it (`details.row`, `.line`; what the check found in
|
|
360
|
+
* `details.errors`). What the reading did not take is in `warnings` (`readBuchungsliste`).
|
|
361
|
+
*/
|
|
362
|
+
export const importBuchungsliste = (bytesArg, policyArg) => {
|
|
363
|
+
const { rows, warnings } = readBuchungsliste(bytesArg);
|
|
364
|
+
const entries = rows.map((buchungslisteRow) => {
|
|
365
|
+
const { row, line } = buchungslisteRow;
|
|
366
|
+
const datevRow = buchungslisteRowToDatevRow(buchungslisteRow);
|
|
367
|
+
let draft;
|
|
368
|
+
try {
|
|
369
|
+
draft = plugins.skrCore.journalDraftOfDatevRow(datevRow, policyArg);
|
|
370
|
+
}
|
|
371
|
+
catch (error) {
|
|
372
|
+
if (error instanceof plugins.skrCore.DatevImportError) {
|
|
373
|
+
throw refusal(`row ${row} (line ${line}): ${error.message}`, { row, line });
|
|
374
|
+
}
|
|
375
|
+
throw error;
|
|
376
|
+
}
|
|
377
|
+
const validation = plugins.skrCore.validateJournalDraft(draft, policyArg);
|
|
378
|
+
if (!validation.valid) {
|
|
379
|
+
throw refusal(`row ${row} (line ${line}) gives no valid entry: ${validation.errors.join('; ')}`, { row, line, errors: validation.errors });
|
|
380
|
+
}
|
|
381
|
+
return { row, buchungslisteRow, datevRow, draft };
|
|
382
|
+
});
|
|
383
|
+
return { entries, warnings };
|
|
384
|
+
};
|
|
385
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9idWNodW5nc2xpc3RlL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sY0FBYyxDQUFDO0FBQ3hDLE9BQU8sRUFBRSxrQkFBa0IsRUFBbUMsTUFBTSxhQUFhLENBQUM7QUFDbEYsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFFekQsT0FBTyxFQUFFLGtCQUFrQixFQUFpRSxNQUFNLGFBQWEsQ0FBQztBQStDaEgsNkVBQTZFO0FBQzdFLE1BQU0sV0FBVyxHQUFrRDtJQUNqRSxVQUFVLEVBQUUsWUFBWTtJQUN4QixhQUFhLEVBQUUsZUFBZTtJQUM5QixlQUFlLEVBQUUsaUJBQWlCO0lBQ2xDLGlCQUFpQixFQUFFLG1CQUFtQjtJQUN0QyxXQUFXLEVBQUUsYUFBYTtJQUMxQixZQUFZLEVBQUUsY0FBYztJQUM1QixjQUFjLEVBQUUsZ0JBQWdCO0lBQ2hDLFFBQVEsRUFBRSxTQUFTO0lBQ25CLFNBQVMsRUFBRSxXQUFXO0lBQ3RCLFVBQVUsRUFBRSxZQUFZO0lBQ3hCLGdCQUFnQixFQUFFLGlCQUFpQjtJQUNuQyxZQUFZLEVBQUUsY0FBYztJQUM1QixhQUFhLEVBQUUsY0FBYztJQUM3QixhQUFhLEVBQUUsZUFBZTtJQUM5QixNQUFNLEVBQUUsU0FBUztJQUNqQixTQUFTLEVBQUUsTUFBTTtDQUNsQixDQUFDO0FBRUY7Ozs7R0FJRztBQUNILE1BQU0sZUFBZSxHQUE2QyxJQUFJLEdBQUcsQ0FBOEI7SUFDckcsQ0FBQyxZQUFZLEVBQUUsWUFBWSxDQUFDO0lBQzVCLENBQUMsZUFBZSxFQUFFLGVBQWUsQ0FBQztJQUNsQyxDQUFDLGlCQUFpQixFQUFFLGlCQUFpQixDQUFDO0lBQ3RDLENBQUMsbUJBQW1CLEVBQUUsbUJBQW1CLENBQUM7SUFDMUMsQ0FBQyxhQUFhLEVBQUUsYUFBYSxDQUFDO0lBQzlCLENB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
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import * as skrCore from '@fin.cx/skr/core';
|
|
2
|
+
export { skrCore };
|
|
3
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL2J1Y2h1bmdzbGlzdGUvcGx1Z2lucy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGtCQUFrQixDQUFDO0FBRTVDLE9BQU8sRUFBRSxPQUFPLEVBQUUsQ0FBQyJ9
|
|
@@ -18,3 +18,11 @@ export declare const windows1252ByteOf: (codePointArg: number) => number | undef
|
|
|
18
18
|
* text lost which character; nothing is replaced unreported.
|
|
19
19
|
*/
|
|
20
20
|
export declare const encodeWindows1252: (textArg: string) => IWindows1252Result;
|
|
21
|
+
/**
|
|
22
|
+
* Decodes Windows-1252 as the WHATWG Encoding Standard does: the 27 characters
|
|
23
|
+
* of 0x80–0x9F by the table above, every other byte as the code point of its
|
|
24
|
+
* own number (the five bytes the standard leaves to C1 controls included).
|
|
25
|
+
* It does not rest on the platform's decoder: Node 25.2.1 reads 0x96 as
|
|
26
|
+
* U+0096, not as the dash.
|
|
27
|
+
*/
|
|
28
|
+
export declare const decodeWindows1252: (bytesArg: Uint8Array) => string;
|
|
@@ -43,4 +43,19 @@ export const encodeWindows1252 = (textArg) => {
|
|
|
43
43
|
}
|
|
44
44
|
return { bytes: Uint8Array.from(bytes), replaced };
|
|
45
45
|
};
|
|
46
|
-
|
|
46
|
+
const CODE_POINT_OF_HIGH_BYTE = new Map(HIGH_BYTES.map(([codePointArg, byteArg]) => [byteArg, codePointArg]));
|
|
47
|
+
/**
|
|
48
|
+
* Decodes Windows-1252 as the WHATWG Encoding Standard does: the 27 characters
|
|
49
|
+
* of 0x80–0x9F by the table above, every other byte as the code point of its
|
|
50
|
+
* own number (the five bytes the standard leaves to C1 controls included).
|
|
51
|
+
* It does not rest on the platform's decoder: Node 25.2.1 reads 0x96 as
|
|
52
|
+
* U+0096, not as the dash.
|
|
53
|
+
*/
|
|
54
|
+
export const decodeWindows1252 = (bytesArg) => {
|
|
55
|
+
let text = '';
|
|
56
|
+
for (const byte of bytesArg) {
|
|
57
|
+
text += String.fromCharCode(CODE_POINT_OF_HIGH_BYTE.get(byte) ?? byte);
|
|
58
|
+
}
|
|
59
|
+
return text;
|
|
60
|
+
};
|
|
61
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9lbmNvZGluZy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7O0dBTUc7QUFDSCxNQUFNLFVBQVUsR0FBNkM7SUFDM0QsQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLEVBQUUsQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLEVBQUUsQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLEVBQUUsQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLEVBQUUsQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLEVBQUUsQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLEVBQUUsQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDO0lBQzlHLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQyxFQUFFLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQyxFQUFFLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQyxFQUFFLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQyxFQUFFLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQyxFQUFFLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQyxFQUFFLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQztJQUM5RyxDQUFDLE1BQU0sRUFBRSxJQUFJLENBQUMsRUFBRSxDQUFDLE1BQU0sRUFBRSxJQUFJLENBQUMsRUFBRSxDQUFDLE1BQU0sRUFBRSxJQUFJLENBQUMsRUFBRSxDQUFDLE1BQU0sRUFBRSxJQUFJLENBQUMsRUFBRSxDQUFDLE1BQU0sRUFBRSxJQUFJLENBQUMsRUFBRSxDQUFDLE1BQU0sRUFBRSxJQUFJLENBQUMsRUFBRSxDQUFDLE1BQU0sRUFBRSxJQUFJLENBQUM7SUFDOUcsQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLEVBQUUsQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLEVBQUUsQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLEVBQUUsQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFD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
|
package/dist_ts/error.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Why an exchange file could not be written:
|
|
2
|
+
* Why an exchange file could not be written or read:
|
|
3
3
|
* - `invalid_bookings`: a draft the account policy refuses, the message names it; a general reversal among the
|
|
4
4
|
* bookings handed over to a correction Buchungsstapel (a correction file passed as the state handed over); or a row
|
|
5
5
|
* or booking a Buchungsstapel does not hold (no day, an amount that is not positive) or whose Belegdatum lies
|
|
@@ -10,10 +10,27 @@
|
|
|
10
10
|
* - `invalid_documents`: a document of a document-image archive DATEV does not take (its GUID, its format), or none;
|
|
11
11
|
* - `nothing_to_correct`: a correction Buchungsstapel whose current bookings are the ones handed over;
|
|
12
12
|
* - `handed_over_cost_centres_unknown`: a correction of a period whose bookings handed over include one whose cost
|
|
13
|
-
* centres its record does not state (stored before 4.1) and no rows sent were given; the message names it
|
|
13
|
+
* centres its record does not state (stored before 4.1) and no rows sent were given; the message names it;
|
|
14
|
+
* - `invalid_buchungsliste`: a Buchungsliste that cannot be read or booked as it is: no header line, a required
|
|
15
|
+
* column missing, a field that is not of its format, a field this version does not map, or a row that gives no
|
|
16
|
+
* valid entry; `details` names the line, the row and the field where it can.
|
|
14
17
|
*/
|
|
15
|
-
export type TCompatibilityErrorCode = 'invalid_bookings' | 'invalid_header' | 'export_failed' | 'invalid_documents' | 'nothing_to_correct' | 'handed_over_cost_centres_unknown';
|
|
18
|
+
export type TCompatibilityErrorCode = 'invalid_bookings' | 'invalid_header' | 'export_failed' | 'invalid_documents' | 'nothing_to_correct' | 'handed_over_cost_centres_unknown' | 'invalid_buchungsliste';
|
|
19
|
+
/** Where a refusal of a file read stands, as far as it is known; empty for a file written. */
|
|
20
|
+
export interface ICompatibilityErrorDetails {
|
|
21
|
+
/** the data row, from 1 */
|
|
22
|
+
row?: number;
|
|
23
|
+
/** the line of the file, from 1 */
|
|
24
|
+
line?: number;
|
|
25
|
+
/** the field, by the name of its column as the file states it */
|
|
26
|
+
field?: string;
|
|
27
|
+
/** the field's value as the file states it */
|
|
28
|
+
value?: string;
|
|
29
|
+
/** what the check of the entry found (`@fin.cx/skr` `validateJournalDraft`) */
|
|
30
|
+
errors?: string[];
|
|
31
|
+
}
|
|
16
32
|
export declare class CompatibilityError extends Error {
|
|
17
33
|
readonly code: TCompatibilityErrorCode;
|
|
18
|
-
|
|
34
|
+
readonly details: ICompatibilityErrorDetails;
|
|
35
|
+
constructor(codeArg: TCompatibilityErrorCode, messageArg: string, detailsArg?: ICompatibilityErrorDetails);
|
|
19
36
|
}
|
package/dist_ts/error.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export class CompatibilityError extends Error {
|
|
2
|
-
constructor(codeArg, messageArg) {
|
|
2
|
+
constructor(codeArg, messageArg, detailsArg = {}) {
|
|
3
3
|
super(messageArg);
|
|
4
4
|
this.name = 'CompatibilityError';
|
|
5
5
|
this.code = codeArg;
|
|
6
|
+
this.details = detailsArg;
|
|
6
7
|
}
|
|
7
8
|
}
|
|
8
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
9
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXJyb3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9lcnJvci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUF3Q0EsTUFBTSxPQUFPLGtCQUFtQixTQUFRLEtBQUs7SUFJM0MsWUFBWSxPQUFnQyxFQUFFLFVBQWtCLEVBQUUsYUFBeUMsRUFBRTtRQUMzRyxLQUFLLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDbEIsSUFBSSxDQUFDLElBQUksR0FBRyxvQkFBb0IsQ0FBQztRQUNqQyxJQUFJLENBQUMsSUFBSSxHQUFHLE9BQU8sQ0FBQztRQUNwQixJQUFJLENBQUMsT0FBTyxHQUFHLFVBQVUsQ0FBQztJQUM1QixDQUFDO0NBQ0YifQ==
|