@kreiseck/kasseneck-api 1.18.0 → 1.20.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/CHANGELOG.md +22 -0
- package/README.md +90 -9
- package/dist/cjs/client/api.d.ts +19 -0
- package/dist/cjs/client/api.js +18 -0
- package/dist/cjs/client/aufrufe.d.ts +5 -3
- package/dist/cjs/client/aufrufe.js +20 -0
- package/dist/cjs/client/dep-export.d.ts +242 -0
- package/dist/cjs/client/dep-export.js +336 -0
- package/dist/cjs/client/index.d.ts +3 -2
- package/dist/cjs/client/index.js +22 -2
- package/dist/cjs/client/list-receipts.d.ts +45 -5
- package/dist/cjs/client/list-receipts.js +62 -3
- package/dist/cjs/client/receipts.js +2 -2
- package/dist/cjs/client/transport.d.ts +35 -0
- package/dist/cjs/client/transport.js +37 -2
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/index.js +21 -2
- package/dist/cjs/models/receipt.d.ts +5 -3
- package/dist/cjs/stored/vokabular.d.ts +1 -1
- package/dist/cjs/stored/vokabular.js +1 -1
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/client/api.d.ts +19 -0
- package/dist/esm/client/api.js +19 -1
- package/dist/esm/client/aufrufe.d.ts +5 -3
- package/dist/esm/client/aufrufe.js +20 -0
- package/dist/esm/client/dep-export.d.ts +242 -0
- package/dist/esm/client/dep-export.js +325 -0
- package/dist/esm/client/index.d.ts +3 -2
- package/dist/esm/client/index.js +3 -2
- package/dist/esm/client/list-receipts.d.ts +45 -5
- package/dist/esm/client/list-receipts.js +61 -3
- package/dist/esm/client/receipts.js +2 -2
- package/dist/esm/client/transport.d.ts +35 -0
- package/dist/esm/client/transport.js +36 -2
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/models/receipt.d.ts +5 -3
- package/dist/esm/stored/vokabular.d.ts +1 -1
- package/dist/esm/stored/vokabular.js +1 -1
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/fixtures/hobex-hps-codes.json +1 -1
- package/fixtures/invoice-api.schema.json +1 -1
- package/fixtures/invoice-texts.json +1 -1
- package/fixtures/pos-texts.json +1 -1
- package/fixtures/surface.json +12 -2
- package/fixtures/v3/antworten/belege.json +106 -8
- package/fixtures/v3/antworten/belegmail.json +1 -1
- package/fixtures/v3/antworten/berichte.json +1 -1
- package/fixtures/v3/antworten/kasse-belege.json +1 -1
- package/fixtures/v3/antworten/kasse.json +2 -2
- package/fixtures/v3/antworten/lager.json +1 -1
- package/fixtures/v3/antworten/rechnungen.json +1 -1
- package/fixtures/v3/antworten/storno.json +14 -2
- package/fixtures/v3/stored/belege.json +1 -1
- package/fixtures/v3/stored/kasse.json +2 -2
- package/fixtures/v3/stored/rechnungen.json +1 -1
- package/fixtures/v3/v3-vokabular.json +153 -10
- package/fixtures/v3/zahlbetrag-faelle.json +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DepExportChecksumError = exports.DepExportTimeoutError = exports.DEP_EXPORT_DOWNLOAD_TIMEOUT_MS = exports.DEP_EXPORT_WAIT_TIMEOUT_MS = exports.DEP_EXPORT_POLL_MAX_MS = exports.DEP_EXPORT_POLL_FACTOR = exports.DEP_EXPORT_POLL_START_MS = exports.DEP_EXPORT_FAILURES = exports.DEP_EXPORT_ERROR_CODES = exports.DEP_EXPORT_FILES = exports.DEP_EXPORT_STATUSES = void 0;
|
|
4
|
+
exports.createDepExport = createDepExport;
|
|
5
|
+
exports.getDepExport = getDepExport;
|
|
6
|
+
exports.waitForDepExport = waitForDepExport;
|
|
7
|
+
exports.getDepExportFile = getDepExportFile;
|
|
8
|
+
exports.createDepHandoverLink = createDepHandoverLink;
|
|
9
|
+
exports.revokeDepHandoverLink = revokeDepHandoverLink;
|
|
10
|
+
const errors_js_1 = require("./errors.js");
|
|
11
|
+
/**
|
|
12
|
+
* DEP-Export fuer die Kassennachschau (nur unter `/v3`): ein Export des
|
|
13
|
+
* Datenerfassungsprotokolls fuer einen vom Pruefungsorgan vorgegebenen
|
|
14
|
+
* Zeitraum (§ 19 Abs. 2 RKSV), Dateien zum Abholen und ein Pruefer-Link mit
|
|
15
|
+
* Code, ueber den das Pruefungsorgan die Dateien ohne Anmeldung laedt.
|
|
16
|
+
*
|
|
17
|
+
* **Anmeldung wie `listReceipts`:** API-Schluessel plus `cashregister-token`;
|
|
18
|
+
* die Kasse kommt aus dem Token, nie aus einem Parameter. Ein Export einer
|
|
19
|
+
* anderen Kasse ist `export_not_found`.
|
|
20
|
+
*
|
|
21
|
+
* **Ablauf:** `createDepExport` stellt den Export ein und antwortet sofort
|
|
22
|
+
* (`status: 'running'`; eine Wiederholung mit derselben `requestId` nennt den
|
|
23
|
+
* aktuellen Stand); er laeuft im Hintergrund. `waitForDepExport` fragt bis zum
|
|
24
|
+
* Ende, `getDepExportFile` holt eine Datei, `createDepHandoverLink` erzeugt den
|
|
25
|
+
* Link fuer das Pruefungsorgan.
|
|
26
|
+
*/
|
|
27
|
+
/** Zustand eines Exports: in Arbeit, fertig oder fehlgeschlagen. */
|
|
28
|
+
exports.DEP_EXPORT_STATUSES = Object.freeze(['running', 'done', 'failed']);
|
|
29
|
+
/** Die Dateien eines Exports: DEP-JSON, Protokoll (PDF), Positionen (CSV) und alles zusammen als ZIP. */
|
|
30
|
+
exports.DEP_EXPORT_FILES = Object.freeze(['dep', 'protocol', 'items', 'zip']);
|
|
31
|
+
/**
|
|
32
|
+
* Fehlercodes der fuenf Aufrufe (`code` des [KasseneckApiError]) wie im
|
|
33
|
+
* Vertrag des Backends (`errorCodes.depExport` in `fixtures/v3/v3-vokabular.json`,
|
|
34
|
+
* Katalog `dep_nachschau`), in dessen Reihenfolge; dazu kommen die gemeinsamen
|
|
35
|
+
* Codes von Anmeldung und Rand. Ein Code ausserhalb bleibt ueber
|
|
36
|
+
* `KasseneckApiError.code` lesbar.
|
|
37
|
+
*/
|
|
38
|
+
exports.DEP_EXPORT_ERROR_CODES = Object.freeze([
|
|
39
|
+
'validation', // ungueltige requestId (Details in den Feldfehlern)
|
|
40
|
+
'invalid_period', // from/to kein Datum JJJJ-MM-TT, from in der Zukunft oder to vor from
|
|
41
|
+
'idempotency_conflict', // dieselbe requestId gehoert zu einem Export mit anderem Zeitraum
|
|
42
|
+
'export_running', // fuer diese Kasse laeuft schon ein Export (`details.exportId` nennt ihn)
|
|
43
|
+
'too_many_exports', // Tagesgrenze der Exporte erreicht
|
|
44
|
+
'export_not_found', // unbekannt oder einer anderen Kasse
|
|
45
|
+
'export_not_ready', // Datei oder Link nur zu einem fertigen Export
|
|
46
|
+
'export_failed', // der Export ist fehlgeschlagen
|
|
47
|
+
'invalid_file', // keine der Dateien dep, protocol, items, zip
|
|
48
|
+
'file_not_available', // diese Datei gibt es zu diesem Export nicht (etwa Positionen bei einem alten Export)
|
|
49
|
+
'too_many_links', // Grenze der aktiven Links je Export oder der neuen Links je Tag erreicht
|
|
50
|
+
'link_not_found', // unbekannt oder einer anderen Kasse (ein schon widerrufener Link ist kein Fehler: der Widerruf ist wiederholbar)
|
|
51
|
+
'server_error',
|
|
52
|
+
]);
|
|
53
|
+
/**
|
|
54
|
+
* Warum ein Export fehlgeschlagen ist (`DepExport.error`): die Werte des
|
|
55
|
+
* Katalogs `DEP_EXPORT_FEHLER` im Vertrag. `export_stalled` = nach 35 Minuten
|
|
56
|
+
* nicht fertig.
|
|
57
|
+
*/
|
|
58
|
+
exports.DEP_EXPORT_FAILURES = Object.freeze(['export_failed', 'export_stalled']);
|
|
59
|
+
/** Wartezeiten von [waitForDepExport]: 2 s, dann je x1,5, hoechstens 15 s. */
|
|
60
|
+
exports.DEP_EXPORT_POLL_START_MS = 2_000;
|
|
61
|
+
exports.DEP_EXPORT_POLL_FACTOR = 1.5;
|
|
62
|
+
exports.DEP_EXPORT_POLL_MAX_MS = 15_000;
|
|
63
|
+
/** Vorgabe fuer `waitForDepExport.timeoutMs`. */
|
|
64
|
+
exports.DEP_EXPORT_WAIT_TIMEOUT_MS = 600_000;
|
|
65
|
+
/** Vorgabe fuer `getDepExportFile.downloadTimeoutMs`: der Aufruf selbst und das Laden ueber einen Lese-Link. */
|
|
66
|
+
exports.DEP_EXPORT_DOWNLOAD_TIMEOUT_MS = 600_000;
|
|
67
|
+
/**
|
|
68
|
+
* [waitForDepExport] gab auf, weil der Export nach `timeoutMs` weder fertig
|
|
69
|
+
* noch fehlgeschlagen war. Der Export laeuft serverseitig weiter: mit
|
|
70
|
+
* `getDepExport` (oder erneutem Warten) nachsehen, nicht neu anstossen.
|
|
71
|
+
*/
|
|
72
|
+
class DepExportTimeoutError extends Error {
|
|
73
|
+
name = 'DepExportTimeoutError';
|
|
74
|
+
exportId;
|
|
75
|
+
timeoutMs;
|
|
76
|
+
/** Zustand bei der letzten Abfrage. */
|
|
77
|
+
lastStatus;
|
|
78
|
+
constructor(exportId, timeoutMs, lastStatus) {
|
|
79
|
+
super(`waitForDepExport: Export ${exportId} nach ${timeoutMs} ms nicht fertig (Zustand ${lastStatus})`);
|
|
80
|
+
this.exportId = exportId;
|
|
81
|
+
this.timeoutMs = timeoutMs;
|
|
82
|
+
this.lastStatus = lastStatus;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
exports.DepExportTimeoutError = DepExportTimeoutError;
|
|
86
|
+
/** Das Ergebnis stimmt nicht mit der Pruefsumme ueberein: die Datei ist nicht zu verwenden. */
|
|
87
|
+
class DepExportChecksumError extends errors_js_1.KasseneckValidationError {
|
|
88
|
+
expected;
|
|
89
|
+
actual;
|
|
90
|
+
constructor(exportId, file, expected, actual) {
|
|
91
|
+
super('getDepExportFile', `Pruefsumme der Datei ${file} von ${exportId} stimmt nicht (erwartet ${expected}, berechnet ${actual})`, 'response');
|
|
92
|
+
this.expected = expected;
|
|
93
|
+
this.actual = actual;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
exports.DepExportChecksumError = DepExportChecksumError;
|
|
97
|
+
// ---- Hilfen -------------------------------------------------------------------------------------
|
|
98
|
+
const TAG = /^\d{4}-\d{2}-\d{2}$/;
|
|
99
|
+
const ANFRAGE_ID = /^[A-Za-z0-9_-]{1,64}$/;
|
|
100
|
+
const SHA256 = /^[0-9a-f]{64}$/i;
|
|
101
|
+
const VORGABEN = {
|
|
102
|
+
dep: { fileName: (id) => `${id}.json`, contentType: 'application/json' },
|
|
103
|
+
protocol: { fileName: (id) => `${id}.pdf`, contentType: 'application/pdf' },
|
|
104
|
+
items: { fileName: (id) => `${id}_positionen.csv`, contentType: 'text/csv; charset=utf-8' },
|
|
105
|
+
zip: { fileName: (id) => `${id}.zip`, contentType: 'application/zip' },
|
|
106
|
+
};
|
|
107
|
+
function eingabefehler(name, grund) {
|
|
108
|
+
return new errors_js_1.KasseneckValidationError(name, grund, 'request');
|
|
109
|
+
}
|
|
110
|
+
function antwortfehler(name, grund) {
|
|
111
|
+
return new errors_js_1.KasseneckValidationError(name, grund, 'response');
|
|
112
|
+
}
|
|
113
|
+
const istObjekt = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
114
|
+
function pruefeKennung(name, feld, wert) {
|
|
115
|
+
if (typeof wert !== 'string' || wert.trim() === '')
|
|
116
|
+
throw eingabefehler(name, `${feld} fehlt oder ist leer`);
|
|
117
|
+
return wert;
|
|
118
|
+
}
|
|
119
|
+
/** Ist das ein echter Kalendertag `JJJJ-MM-TT`? (Rechnung ohne Zeitzone.) */
|
|
120
|
+
function istKalendertag(text) {
|
|
121
|
+
if (!TAG.test(text))
|
|
122
|
+
return false;
|
|
123
|
+
const [j, m, t] = text.split('-').map(Number);
|
|
124
|
+
const d = new Date(Date.UTC(j, m - 1, t));
|
|
125
|
+
return d.getUTCFullYear() === j && d.getUTCMonth() === m - 1 && d.getUTCDate() === t;
|
|
126
|
+
}
|
|
127
|
+
async function sha256Hex(bytes) {
|
|
128
|
+
const umgebung = globalThis;
|
|
129
|
+
const subtle = umgebung.crypto?.subtle;
|
|
130
|
+
if (!subtle)
|
|
131
|
+
throw new Error('getDepExportFile: kein globales crypto.subtle vorhanden — Node >= 20.18 verwenden');
|
|
132
|
+
const digest = new Uint8Array(await subtle.digest('SHA-256', bytes));
|
|
133
|
+
return Array.from(digest, (b) => b.toString(16).padStart(2, '0')).join('');
|
|
134
|
+
}
|
|
135
|
+
function exportAus(daten, name) {
|
|
136
|
+
if (!istObjekt(daten))
|
|
137
|
+
throw antwortfehler(name, 'Antwort enthaelt keine Daten (data fehlt)');
|
|
138
|
+
const status = daten['status'];
|
|
139
|
+
if (typeof daten['exportId'] !== 'string' || !exports.DEP_EXPORT_STATUSES.includes(status)) {
|
|
140
|
+
throw antwortfehler(name, 'Antwort ohne exportId oder mit unbekanntem Zustand');
|
|
141
|
+
}
|
|
142
|
+
if (!Array.isArray(daten['handoverLinks']))
|
|
143
|
+
throw antwortfehler(name, 'Antwort ohne handoverLinks');
|
|
144
|
+
return daten;
|
|
145
|
+
}
|
|
146
|
+
// ---- Aufrufe ------------------------------------------------------------------------------------
|
|
147
|
+
/**
|
|
148
|
+
* Stellt einen DEP-Export ein (`createDepExport`) und antwortet sofort mit
|
|
149
|
+
* `status: 'running'`. Hoechstens ein laufender Export je Kasse
|
|
150
|
+
* (`export_running`, `details.exportId` nennt ihn). Mit `requestId` ist der
|
|
151
|
+
* Aufruf wiederholbar: dieselbe Kennung mit demselben Zeitraum liefert den
|
|
152
|
+
* bestehenden Export mit seinem aktuellen Stand (auch `done` oder `failed`),
|
|
153
|
+
* mit anderem Zeitraum `idempotency_conflict`.
|
|
154
|
+
*/
|
|
155
|
+
async function createDepExport(transport, options = {}) {
|
|
156
|
+
const name = 'createDepExport';
|
|
157
|
+
const params = {};
|
|
158
|
+
for (const feld of ['from', 'to']) {
|
|
159
|
+
const wert = options[feld];
|
|
160
|
+
if (wert === undefined)
|
|
161
|
+
continue;
|
|
162
|
+
if (typeof wert !== 'string' || !istKalendertag(wert))
|
|
163
|
+
throw eingabefehler(name, `${feld} muss ein Datum JJJJ-MM-TT sein`);
|
|
164
|
+
params[feld] = wert;
|
|
165
|
+
}
|
|
166
|
+
if (options.from !== undefined && options.to !== undefined && options.to < options.from) {
|
|
167
|
+
throw eingabefehler(name, 'to liegt vor from');
|
|
168
|
+
}
|
|
169
|
+
if (options.requestId !== undefined) {
|
|
170
|
+
if (typeof options.requestId !== 'string' || !ANFRAGE_ID.test(options.requestId)) {
|
|
171
|
+
throw eingabefehler(name, 'requestId: hoechstens 64 Zeichen aus A-Z, a-z, 0-9, _ und -');
|
|
172
|
+
}
|
|
173
|
+
params['requestId'] = options.requestId;
|
|
174
|
+
}
|
|
175
|
+
const daten = await transport(name, params);
|
|
176
|
+
if (!istObjekt(daten) || typeof daten['exportId'] !== 'string' || !exports.DEP_EXPORT_STATUSES.includes(daten['status'])) {
|
|
177
|
+
throw antwortfehler(name, 'Antwort ohne exportId oder mit unbekanntem Zustand');
|
|
178
|
+
}
|
|
179
|
+
return daten;
|
|
180
|
+
}
|
|
181
|
+
/** Zustand, Kennzahlen, Befund und Links eines Exports (`getDepExport`). */
|
|
182
|
+
async function getDepExport(transport, options) {
|
|
183
|
+
const name = 'getDepExport';
|
|
184
|
+
const exportId = pruefeKennung(name, 'exportId', options?.exportId);
|
|
185
|
+
return exportAus(await transport(name, { exportId }), name);
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Wartet, bis der Export fertig ist, und gibt ihn zurueck. Fragt mit
|
|
189
|
+
* [DEP_EXPORT_POLL_START_MS] Abstand, dann je x[DEP_EXPORT_POLL_FACTOR],
|
|
190
|
+
* hoechstens [DEP_EXPORT_POLL_MAX_MS]. Bei `failed` wirft er einen
|
|
191
|
+
* [KasseneckApiError] mit `code` `export_failed` oder `export_stalled`
|
|
192
|
+
* (haengender Export); nach `timeoutMs` einen [DepExportTimeoutError]. Ein
|
|
193
|
+
* Fehler beim Abfragen selbst bricht ab.
|
|
194
|
+
*/
|
|
195
|
+
async function waitForDepExport(transport, options) {
|
|
196
|
+
const name = 'waitForDepExport';
|
|
197
|
+
const exportId = pruefeKennung(name, 'exportId', options?.exportId);
|
|
198
|
+
const timeoutMs = options.timeoutMs ?? exports.DEP_EXPORT_WAIT_TIMEOUT_MS;
|
|
199
|
+
if (!(typeof timeoutMs === 'number' && Number.isFinite(timeoutMs) && timeoutMs > 0)) {
|
|
200
|
+
throw eingabefehler(name, 'timeoutMs muss eine positive Zahl sein');
|
|
201
|
+
}
|
|
202
|
+
const ende = Date.now() + timeoutMs;
|
|
203
|
+
let abstand = exports.DEP_EXPORT_POLL_START_MS;
|
|
204
|
+
for (;;) {
|
|
205
|
+
const stand = await getDepExport(transport, { exportId });
|
|
206
|
+
if (stand.status === 'done')
|
|
207
|
+
return stand;
|
|
208
|
+
if (stand.status === 'failed') {
|
|
209
|
+
throw new errors_js_1.KasseneckApiError('getDepExport', stand.error === 'export_stalled' ? 'Der Export haengt (Stillstand).' : 'Der Export ist fehlgeschlagen.', { exportId, error: stand.error ?? 'export_failed' }, stand.error ?? 'export_failed');
|
|
210
|
+
}
|
|
211
|
+
const rest = ende - Date.now();
|
|
212
|
+
if (rest <= 0)
|
|
213
|
+
throw new DepExportTimeoutError(exportId, timeoutMs, stand.status);
|
|
214
|
+
await new Promise((weiter) => setTimeout(weiter, Math.min(abstand, rest)));
|
|
215
|
+
abstand = Math.min(abstand * exports.DEP_EXPORT_POLL_FACTOR, exports.DEP_EXPORT_POLL_MAX_MS);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
function linkAus(name, wert) {
|
|
219
|
+
if (!istObjekt(wert))
|
|
220
|
+
throw antwortfehler(name, 'Antwort ist weder eine Datei noch ein Lese-Link (data.download fehlt)');
|
|
221
|
+
const { url, expiresAt, size, sha256, fileName, contentType } = wert;
|
|
222
|
+
if (typeof url !== 'string' || typeof expiresAt !== 'string' || typeof fileName !== 'string' || typeof contentType !== 'string' ||
|
|
223
|
+
typeof sha256 !== 'string' || !SHA256.test(sha256) || typeof size !== 'number' || !Number.isSafeInteger(size) || size < 0) {
|
|
224
|
+
throw antwortfehler(name, 'Lese-Link unvollstaendig (url, expiresAt, size, sha256, fileName, contentType)');
|
|
225
|
+
}
|
|
226
|
+
let adresse;
|
|
227
|
+
try {
|
|
228
|
+
adresse = new URL(url);
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
throw antwortfehler(name, 'Lese-Link ist keine Adresse');
|
|
232
|
+
}
|
|
233
|
+
const lokal = adresse.hostname === 'localhost' || adresse.hostname === '127.0.0.1' || adresse.hostname === '[::1]';
|
|
234
|
+
if (adresse.protocol !== 'https:' && !(adresse.protocol === 'http:' && lokal)) {
|
|
235
|
+
throw antwortfehler(name, 'Lese-Link ist keine https-Adresse');
|
|
236
|
+
}
|
|
237
|
+
return { url, expiresAt, size, sha256: sha256.toLowerCase(), fileName, contentType };
|
|
238
|
+
}
|
|
239
|
+
/** Laedt die Bytes hinter einem Lese-Link. Fehlertexte und Fehlerobjekte nennen die Adresse nie (sie traegt die Berechtigung). */
|
|
240
|
+
async function ladeLink(fetchFn, url, timeoutMs) {
|
|
241
|
+
const name = 'getDepExportFile';
|
|
242
|
+
const abbruch = new AbortController();
|
|
243
|
+
let abgelaufen = false;
|
|
244
|
+
const wecker = setTimeout(() => {
|
|
245
|
+
abgelaufen = true;
|
|
246
|
+
abbruch.abort();
|
|
247
|
+
}, timeoutMs);
|
|
248
|
+
try {
|
|
249
|
+
// Ein GET ohne Rumpf und ohne eigene Kopfzeilen; FetchLike verlangt formal einen Rumpf, den ein GET nicht tragen darf.
|
|
250
|
+
const antwort = await fetchFn(url, { method: 'GET', headers: {}, signal: abbruch.signal });
|
|
251
|
+
if (antwort.status !== 200) {
|
|
252
|
+
throw new errors_js_1.KasseneckHttpError(name, antwort.status, antwort.headers.get('content-type') ?? undefined, 'server-error', 'rejected');
|
|
253
|
+
}
|
|
254
|
+
return new Uint8Array(await antwort.arrayBuffer());
|
|
255
|
+
}
|
|
256
|
+
catch (ursache) {
|
|
257
|
+
if (ursache instanceof errors_js_1.KasseneckHttpError)
|
|
258
|
+
throw ursache;
|
|
259
|
+
throw new errors_js_1.KasseneckNetworkError(name, abgelaufen, timeoutMs, {}, 'rejected');
|
|
260
|
+
}
|
|
261
|
+
finally {
|
|
262
|
+
clearTimeout(wecker);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Holt eine Datei eines fertigen Exports (`getDepExportFile`). Der Server
|
|
267
|
+
* liefert sie bis 9 MiB selbst, darueber als signierten Lese-Link (15 Minuten);
|
|
268
|
+
* dem Link folgt dieser Aufruf und laedt die Bytes. In beiden Faellen ist das
|
|
269
|
+
* Ergebnis dasselbe. Geprueft wird die SHA-256-Summe: beim Lese-Link immer
|
|
270
|
+
* gegen die Summe des Servers, sonst und zusaetzlich gegen `options.sha256`,
|
|
271
|
+
* wenn sie bekannt ist. Eine Abweichung wirft [DepExportChecksumError].
|
|
272
|
+
*/
|
|
273
|
+
async function getDepExportFile(transport, fetchFn, options) {
|
|
274
|
+
const name = 'getDepExportFile';
|
|
275
|
+
const exportId = pruefeKennung(name, 'exportId', options?.exportId);
|
|
276
|
+
if (!exports.DEP_EXPORT_FILES.includes(options.file)) {
|
|
277
|
+
throw eingabefehler(name, 'file muss dep, protocol, items oder zip sein');
|
|
278
|
+
}
|
|
279
|
+
if (options.sha256 !== undefined && !(typeof options.sha256 === 'string' && SHA256.test(options.sha256))) {
|
|
280
|
+
throw eingabefehler(name, 'sha256 muss 64 Hexzeichen haben');
|
|
281
|
+
}
|
|
282
|
+
const frist = options.downloadTimeoutMs ?? exports.DEP_EXPORT_DOWNLOAD_TIMEOUT_MS;
|
|
283
|
+
if (!(typeof frist === 'number' && Number.isFinite(frist) && frist > 0)) {
|
|
284
|
+
throw eingabefehler(name, 'downloadTimeoutMs muss eine positive Zahl sein');
|
|
285
|
+
}
|
|
286
|
+
const bekannt = options.sha256?.toLowerCase();
|
|
287
|
+
const vorgabe = VORGABEN[options.file];
|
|
288
|
+
// Eine direkte Datei (bis 9 MiB) kommt im Aufruf selbst: dieselbe Frist wie das Laden ueber den Link, nicht die 30 s
|
|
289
|
+
// des Transports.
|
|
290
|
+
const ergebnis = await transport(name, { exportId, file: options.file }, { timeoutMs: frist });
|
|
291
|
+
let bytes;
|
|
292
|
+
let fileName;
|
|
293
|
+
let contentType;
|
|
294
|
+
let vomServer;
|
|
295
|
+
if ('file' in ergebnis) {
|
|
296
|
+
bytes = ergebnis.file.bytes;
|
|
297
|
+
fileName = ergebnis.file.fileName ?? vorgabe.fileName(exportId);
|
|
298
|
+
contentType = ergebnis.file.contentType ?? vorgabe.contentType;
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
const link = linkAus(name, istObjekt(ergebnis.data) ? ergebnis.data['download'] : undefined);
|
|
302
|
+
bytes = await ladeLink(fetchFn, link.url, frist);
|
|
303
|
+
if (bytes.length !== link.size) {
|
|
304
|
+
throw antwortfehler(name, `Die geladene Datei hat ${bytes.length} Byte, der Server nannte ${link.size}`);
|
|
305
|
+
}
|
|
306
|
+
fileName = link.fileName;
|
|
307
|
+
contentType = link.contentType;
|
|
308
|
+
vomServer = link.sha256;
|
|
309
|
+
}
|
|
310
|
+
const summe = await sha256Hex(bytes);
|
|
311
|
+
for (const erwartet of [vomServer, bekannt]) {
|
|
312
|
+
if (erwartet !== undefined && erwartet !== summe)
|
|
313
|
+
throw new DepExportChecksumError(exportId, options.file, erwartet, summe);
|
|
314
|
+
}
|
|
315
|
+
return { bytes, fileName, contentType, sha256: summe };
|
|
316
|
+
}
|
|
317
|
+
/** Erzeugt einen Pruefer-Link zu einem fertigen Export (`createDepHandoverLink`). Gueltig 14 Tage. */
|
|
318
|
+
async function createDepHandoverLink(transport, options) {
|
|
319
|
+
const name = 'createDepHandoverLink';
|
|
320
|
+
const exportId = pruefeKennung(name, 'exportId', options?.exportId);
|
|
321
|
+
const daten = await transport(name, { exportId });
|
|
322
|
+
if (!istObjekt(daten) || typeof daten['linkId'] !== 'string' || typeof daten['url'] !== 'string' ||
|
|
323
|
+
typeof daten['code'] !== 'string' || typeof daten['expiresAt'] !== 'string') {
|
|
324
|
+
throw antwortfehler(name, 'Antwort ohne linkId, url, code oder expiresAt');
|
|
325
|
+
}
|
|
326
|
+
return { linkId: daten['linkId'], url: daten['url'], code: daten['code'], expiresAt: daten['expiresAt'] };
|
|
327
|
+
}
|
|
328
|
+
/** Widerruft einen Pruefer-Link der eigenen Kasse (`revokeDepHandoverLink`). */
|
|
329
|
+
async function revokeDepHandoverLink(transport, options) {
|
|
330
|
+
const name = 'revokeDepHandoverLink';
|
|
331
|
+
const linkId = pruefeKennung(name, 'linkId', options?.linkId);
|
|
332
|
+
const daten = await transport(name, { linkId });
|
|
333
|
+
if (!istObjekt(daten) || typeof daten['linkId'] !== 'string')
|
|
334
|
+
throw antwortfehler(name, 'Antwort ohne linkId');
|
|
335
|
+
return { linkId: daten['linkId'], revokedAt: typeof daten['revokedAt'] === 'string' ? daten['revokedAt'] : null };
|
|
336
|
+
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
export { type AuthCredentials, type KasseneckAuth, type ApiKeyAuthOptions, type RegisterUserAuthOptions, apiKeyAuth, registerUserAuth, } from './auth.js';
|
|
2
|
-
export { DEFAULT_BASE_URL, POS_BASE_URL, DEFAULT_TIMEOUT_MS, type HttpResponseLike, type HttpRequestInit, type FetchLike, type TransportOptions, type TransportBodyFields, type TransportCallOptions, type KasseneckTransport, type KasseneckBinaryTransport, type KasseneckPdfOrDataTransport, type PdfOrData, createTransport, createBinaryTransport, createPdfOrDataTransport, } from './transport.js';
|
|
2
|
+
export { DEFAULT_BASE_URL, POS_BASE_URL, DEFAULT_TIMEOUT_MS, type HttpResponseLike, type HttpRequestInit, type FetchLike, type TransportOptions, type TransportBodyFields, type TransportCallOptions, type KasseneckTransport, type KasseneckBinaryTransport, type KasseneckPdfOrDataTransport, type PdfOrData, createTransport, createBinaryTransport, createPdfOrDataTransport, createFileOrDataTransport, type FileOrData, type FileOrDataCallOptions, type KasseneckFileOrDataTransport, } from './transport.js';
|
|
3
3
|
export { ALL_CALLS, type ApiCall, PUBLIC_CALLS, POS_CALLS, UNKNOWN_OUTCOME_CALLS, type PublicCall, type PosCall, } from './aufrufe.js';
|
|
4
4
|
export { KasseneckApiError, KasseneckAuthError, KasseneckHttpError, KasseneckNetworkError, KasseneckValidationError, type ValidationScope, type KasseneckError, type HttpFailureReason, type CauseDigest, type ErrorOutcome, CLIENT_ERROR_CODES, type ClientErrorCode, isKasseneckApiError, isKasseneckAuthError, isKasseneckHttpError, isKasseneckNetworkError, isKasseneckValidationError, isOutcomeUnknown, } from './errors.js';
|
|
5
5
|
export { type ReceiptCommonOptions, type SellReceiptOptions, type TipOptions, type TipRecipientShare, type CancelReceiptOptions, type ReceiptWithCompany, sellReceipt, sellReceiptWithCompany, paymentsExpectedCents, cardRefundReference, receiptLayoutFromResult, cancelReceipt, type CancelReceiptResult, zeroReceipt, getReceipt, getReceiptWithCompany, generateFullReceiptId, getFirstReceiptDate, listMyReceipts, type ListMyReceiptsOptions, type ReceiptList, type ReceiptListStats, checkVoucherCombinationError, type SendReceiptEmailOptions, type SendReceiptEmailResult, sendReceiptEmail, type ReceiptOutputEntry, type LogReceiptOutputsOptions, type LogReceiptOutputsResult, type RejectedReceiptOutput, logReceiptOutputs, } from './receipts.js';
|
|
6
6
|
export { listMyCashregisters } from './cashregisters.js';
|
|
7
|
-
export { listReceipts, listAllReceipts, fromListReceiptsSummaryPayload, LIST_RECEIPTS_VIEWS, LIST_RECEIPTS_ORDERS, LIST_RECEIPTS_LIMIT_MAX, LIST_RECEIPTS_DAYS_MAX, LIST_ALL_RECEIPTS_MAX_PAGES, LIST_RECEIPTS_ERROR_CODES, type ListReceiptsErrorCode, type ListReceiptsView, type ListReceiptsOrder, type ListReceiptsOptions, type ListReceiptsWindow, type ListReceiptsGrossByRate, type ListReceiptsSummary, type ListReceiptsDaySummary, type ListReceiptsPage, type ListedReceipt, } from './list-receipts.js';
|
|
7
|
+
export { listReceipts, listAllReceipts, fromListReceiptsSummaryPayload, listedReceiptWithCompany, LIST_RECEIPTS_VIEWS, LIST_RECEIPTS_ORDERS, LIST_RECEIPTS_LIMIT_MAX, LIST_RECEIPTS_DAYS_MAX, LIST_ALL_RECEIPTS_MAX_PAGES, LIST_RECEIPTS_ERROR_CODES, type ListReceiptsErrorCode, type ListReceiptsView, type ListReceiptsOrder, type ListReceiptsOptions, type ListReceiptsWindow, type ListReceiptsGrossByRate, type ListReceiptsSummary, type ListReceiptsDaySummary, type ListReceiptsPage, type ListedReceipt, type ListedFullReceipt, type ListReceiptsHeader, } from './list-receipts.js';
|
|
8
|
+
export { createDepExport, getDepExport, waitForDepExport, getDepExportFile, createDepHandoverLink, revokeDepHandoverLink, DepExportTimeoutError, DepExportChecksumError, DEP_EXPORT_STATUSES, DEP_EXPORT_FILES, DEP_EXPORT_ERROR_CODES, DEP_EXPORT_FAILURES, DEP_EXPORT_POLL_START_MS, DEP_EXPORT_POLL_FACTOR, DEP_EXPORT_POLL_MAX_MS, DEP_EXPORT_WAIT_TIMEOUT_MS, DEP_EXPORT_DOWNLOAD_TIMEOUT_MS, type DepExportStatus, type DepExportFileKind, type DepExportErrorCode, type DepExportFailure, type CreateDepExportOptions, type CreateDepExportResult, type DepExportGap, type DepExportChainBreak, type DepExportFindings, type DepExportItems, type DepHandoverLinkInfo, type DepExport, type WaitForDepExportOptions, type GetDepExportFileOptions, type DepExportFile, type DepHandoverLink, type RevokedDepHandoverLink, } from './dep-export.js';
|
|
8
9
|
export { downloadDailyReport, downloadMonthlyReport, getCashregisterReport, getReportV2, type CashregisterReportPeriod, type ReportV2, type ReportV2Metadata, type ReportV2Options } from './reports.js';
|
|
9
10
|
export { type CashboxStatus, type SignatureStatus, getCashboxStatus, getSignatureStatus, } from './status.js';
|
|
10
11
|
export { type KasseneckApi, createKasseneckApi } from './api.js';
|
package/dist/cjs/client/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
exports.createKasseneckApi = exports.getSignatureStatus = exports.getCashboxStatus = exports.getReportV2 = exports.getCashregisterReport = exports.downloadMonthlyReport = void 0;
|
|
3
|
+
exports.LIST_ALL_RECEIPTS_MAX_PAGES = exports.LIST_RECEIPTS_DAYS_MAX = exports.LIST_RECEIPTS_LIMIT_MAX = exports.LIST_RECEIPTS_ORDERS = exports.LIST_RECEIPTS_VIEWS = exports.listedReceiptWithCompany = exports.fromListReceiptsSummaryPayload = exports.listAllReceipts = exports.listReceipts = exports.listMyCashregisters = exports.logReceiptOutputs = exports.sendReceiptEmail = exports.checkVoucherCombinationError = exports.listMyReceipts = exports.getFirstReceiptDate = exports.generateFullReceiptId = exports.getReceiptWithCompany = exports.getReceipt = exports.zeroReceipt = exports.cancelReceipt = exports.receiptLayoutFromResult = exports.cardRefundReference = exports.paymentsExpectedCents = exports.sellReceiptWithCompany = exports.sellReceipt = exports.isOutcomeUnknown = exports.isKasseneckValidationError = exports.isKasseneckNetworkError = exports.isKasseneckHttpError = exports.isKasseneckAuthError = exports.isKasseneckApiError = exports.CLIENT_ERROR_CODES = exports.KasseneckValidationError = exports.KasseneckNetworkError = exports.KasseneckHttpError = exports.KasseneckAuthError = exports.KasseneckApiError = exports.UNKNOWN_OUTCOME_CALLS = exports.POS_CALLS = exports.PUBLIC_CALLS = exports.ALL_CALLS = exports.createFileOrDataTransport = exports.createPdfOrDataTransport = exports.createBinaryTransport = exports.createTransport = exports.DEFAULT_TIMEOUT_MS = exports.POS_BASE_URL = exports.DEFAULT_BASE_URL = exports.registerUserAuth = exports.apiKeyAuth = void 0;
|
|
4
|
+
exports.createKasseneckApi = exports.getSignatureStatus = exports.getCashboxStatus = exports.getReportV2 = exports.getCashregisterReport = exports.downloadMonthlyReport = exports.downloadDailyReport = exports.DEP_EXPORT_DOWNLOAD_TIMEOUT_MS = exports.DEP_EXPORT_WAIT_TIMEOUT_MS = exports.DEP_EXPORT_POLL_MAX_MS = exports.DEP_EXPORT_POLL_FACTOR = exports.DEP_EXPORT_POLL_START_MS = exports.DEP_EXPORT_FAILURES = exports.DEP_EXPORT_ERROR_CODES = exports.DEP_EXPORT_FILES = exports.DEP_EXPORT_STATUSES = exports.DepExportChecksumError = exports.DepExportTimeoutError = exports.revokeDepHandoverLink = exports.createDepHandoverLink = exports.getDepExportFile = exports.waitForDepExport = exports.getDepExport = exports.createDepExport = exports.LIST_RECEIPTS_ERROR_CODES = void 0;
|
|
5
5
|
var auth_js_1 = require("./auth.js");
|
|
6
6
|
Object.defineProperty(exports, "apiKeyAuth", { enumerable: true, get: function () { return auth_js_1.apiKeyAuth; } });
|
|
7
7
|
Object.defineProperty(exports, "registerUserAuth", { enumerable: true, get: function () { return auth_js_1.registerUserAuth; } });
|
|
@@ -12,6 +12,7 @@ Object.defineProperty(exports, "DEFAULT_TIMEOUT_MS", { enumerable: true, get: fu
|
|
|
12
12
|
Object.defineProperty(exports, "createTransport", { enumerable: true, get: function () { return transport_js_1.createTransport; } });
|
|
13
13
|
Object.defineProperty(exports, "createBinaryTransport", { enumerable: true, get: function () { return transport_js_1.createBinaryTransport; } });
|
|
14
14
|
Object.defineProperty(exports, "createPdfOrDataTransport", { enumerable: true, get: function () { return transport_js_1.createPdfOrDataTransport; } });
|
|
15
|
+
Object.defineProperty(exports, "createFileOrDataTransport", { enumerable: true, get: function () { return transport_js_1.createFileOrDataTransport; } });
|
|
15
16
|
var aufrufe_js_1 = require("./aufrufe.js");
|
|
16
17
|
Object.defineProperty(exports, "ALL_CALLS", { enumerable: true, get: function () { return aufrufe_js_1.ALL_CALLS; } });
|
|
17
18
|
Object.defineProperty(exports, "PUBLIC_CALLS", { enumerable: true, get: function () { return aufrufe_js_1.PUBLIC_CALLS; } });
|
|
@@ -52,12 +53,31 @@ var list_receipts_js_1 = require("./list-receipts.js");
|
|
|
52
53
|
Object.defineProperty(exports, "listReceipts", { enumerable: true, get: function () { return list_receipts_js_1.listReceipts; } });
|
|
53
54
|
Object.defineProperty(exports, "listAllReceipts", { enumerable: true, get: function () { return list_receipts_js_1.listAllReceipts; } });
|
|
54
55
|
Object.defineProperty(exports, "fromListReceiptsSummaryPayload", { enumerable: true, get: function () { return list_receipts_js_1.fromListReceiptsSummaryPayload; } });
|
|
56
|
+
Object.defineProperty(exports, "listedReceiptWithCompany", { enumerable: true, get: function () { return list_receipts_js_1.listedReceiptWithCompany; } });
|
|
55
57
|
Object.defineProperty(exports, "LIST_RECEIPTS_VIEWS", { enumerable: true, get: function () { return list_receipts_js_1.LIST_RECEIPTS_VIEWS; } });
|
|
56
58
|
Object.defineProperty(exports, "LIST_RECEIPTS_ORDERS", { enumerable: true, get: function () { return list_receipts_js_1.LIST_RECEIPTS_ORDERS; } });
|
|
57
59
|
Object.defineProperty(exports, "LIST_RECEIPTS_LIMIT_MAX", { enumerable: true, get: function () { return list_receipts_js_1.LIST_RECEIPTS_LIMIT_MAX; } });
|
|
58
60
|
Object.defineProperty(exports, "LIST_RECEIPTS_DAYS_MAX", { enumerable: true, get: function () { return list_receipts_js_1.LIST_RECEIPTS_DAYS_MAX; } });
|
|
59
61
|
Object.defineProperty(exports, "LIST_ALL_RECEIPTS_MAX_PAGES", { enumerable: true, get: function () { return list_receipts_js_1.LIST_ALL_RECEIPTS_MAX_PAGES; } });
|
|
60
62
|
Object.defineProperty(exports, "LIST_RECEIPTS_ERROR_CODES", { enumerable: true, get: function () { return list_receipts_js_1.LIST_RECEIPTS_ERROR_CODES; } });
|
|
63
|
+
var dep_export_js_1 = require("./dep-export.js");
|
|
64
|
+
Object.defineProperty(exports, "createDepExport", { enumerable: true, get: function () { return dep_export_js_1.createDepExport; } });
|
|
65
|
+
Object.defineProperty(exports, "getDepExport", { enumerable: true, get: function () { return dep_export_js_1.getDepExport; } });
|
|
66
|
+
Object.defineProperty(exports, "waitForDepExport", { enumerable: true, get: function () { return dep_export_js_1.waitForDepExport; } });
|
|
67
|
+
Object.defineProperty(exports, "getDepExportFile", { enumerable: true, get: function () { return dep_export_js_1.getDepExportFile; } });
|
|
68
|
+
Object.defineProperty(exports, "createDepHandoverLink", { enumerable: true, get: function () { return dep_export_js_1.createDepHandoverLink; } });
|
|
69
|
+
Object.defineProperty(exports, "revokeDepHandoverLink", { enumerable: true, get: function () { return dep_export_js_1.revokeDepHandoverLink; } });
|
|
70
|
+
Object.defineProperty(exports, "DepExportTimeoutError", { enumerable: true, get: function () { return dep_export_js_1.DepExportTimeoutError; } });
|
|
71
|
+
Object.defineProperty(exports, "DepExportChecksumError", { enumerable: true, get: function () { return dep_export_js_1.DepExportChecksumError; } });
|
|
72
|
+
Object.defineProperty(exports, "DEP_EXPORT_STATUSES", { enumerable: true, get: function () { return dep_export_js_1.DEP_EXPORT_STATUSES; } });
|
|
73
|
+
Object.defineProperty(exports, "DEP_EXPORT_FILES", { enumerable: true, get: function () { return dep_export_js_1.DEP_EXPORT_FILES; } });
|
|
74
|
+
Object.defineProperty(exports, "DEP_EXPORT_ERROR_CODES", { enumerable: true, get: function () { return dep_export_js_1.DEP_EXPORT_ERROR_CODES; } });
|
|
75
|
+
Object.defineProperty(exports, "DEP_EXPORT_FAILURES", { enumerable: true, get: function () { return dep_export_js_1.DEP_EXPORT_FAILURES; } });
|
|
76
|
+
Object.defineProperty(exports, "DEP_EXPORT_POLL_START_MS", { enumerable: true, get: function () { return dep_export_js_1.DEP_EXPORT_POLL_START_MS; } });
|
|
77
|
+
Object.defineProperty(exports, "DEP_EXPORT_POLL_FACTOR", { enumerable: true, get: function () { return dep_export_js_1.DEP_EXPORT_POLL_FACTOR; } });
|
|
78
|
+
Object.defineProperty(exports, "DEP_EXPORT_POLL_MAX_MS", { enumerable: true, get: function () { return dep_export_js_1.DEP_EXPORT_POLL_MAX_MS; } });
|
|
79
|
+
Object.defineProperty(exports, "DEP_EXPORT_WAIT_TIMEOUT_MS", { enumerable: true, get: function () { return dep_export_js_1.DEP_EXPORT_WAIT_TIMEOUT_MS; } });
|
|
80
|
+
Object.defineProperty(exports, "DEP_EXPORT_DOWNLOAD_TIMEOUT_MS", { enumerable: true, get: function () { return dep_export_js_1.DEP_EXPORT_DOWNLOAD_TIMEOUT_MS; } });
|
|
61
81
|
var reports_js_1 = require("./reports.js");
|
|
62
82
|
Object.defineProperty(exports, "downloadDailyReport", { enumerable: true, get: function () { return reports_js_1.downloadDailyReport; } });
|
|
63
83
|
Object.defineProperty(exports, "downloadMonthlyReport", { enumerable: true, get: function () { return reports_js_1.downloadMonthlyReport; } });
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { KeckPaymentMethod, ReceiptType } from '../enums/index.js';
|
|
2
|
-
import { type CancellationReason, type CancellationStatus, type Receipt, type ReceiptPayment, type ZeroKind } from '../models/index.js';
|
|
2
|
+
import { type CancellationReason, type CancellationStatus, type Receipt, type ReceiptCompany, type ReceiptPayment, type ZeroKind } from '../models/index.js';
|
|
3
3
|
import type { ReportV2Metadata } from './reports.js';
|
|
4
|
+
import type { ReceiptWithCompany } from './receipts.js';
|
|
5
|
+
import type { SheetLogoSize } from '../receipt/blatt.js';
|
|
4
6
|
import { type ManualReceipt } from '../models/manual-receipt.js';
|
|
5
7
|
import type { InternerTransport } from './aufrufe.js';
|
|
6
8
|
/**
|
|
@@ -147,8 +149,26 @@ export interface ListReceiptsSummary {
|
|
|
147
149
|
/** Nur an nacherfassten Belegen: der Handbeleg. */
|
|
148
150
|
manualReceipt?: ManualReceipt;
|
|
149
151
|
}
|
|
150
|
-
/**
|
|
151
|
-
|
|
152
|
+
/**
|
|
153
|
+
* Ein Beleg der vollen Ansicht: das Belegdokument wie bei `getReceipt`, dazu was `getReceipt` in der Huelle
|
|
154
|
+
* traegt, ausser `outputs` und dem Zeilenmodell `layout` (die gibt es nur einzeln ueber `getReceipt`).
|
|
155
|
+
* `headerVersionId` ist die Kopf-Version, die fuer diesen Beleg gilt (auch bei Altbelegen ohne gespeicherte
|
|
156
|
+
* Kennung); ihr Kopf steht in [ListReceiptsPage.headers]. Anbieterdaten der Zahlungen bekommt nur der Betrieb
|
|
157
|
+
* selbst (API-Schluessel oder Kassen-Anmeldung), nie ein Partner-Zugang.
|
|
158
|
+
* Druck- und Anzeigedaten wie bei `getReceiptWithCompany`: [listedReceiptWithCompany].
|
|
159
|
+
*/
|
|
160
|
+
export interface ListedFullReceipt extends Receipt {
|
|
161
|
+
/** Produktionskonto mit Test-Signatureinheit (Aufdruck TESTSIGNATUR); fehlt bei einem Backend vor dem 30.10.2026. */
|
|
162
|
+
testSignature?: boolean;
|
|
163
|
+
}
|
|
164
|
+
/** Ein Eintrag in [ListReceiptsPage.headers]: Kopf und Fuss einer Kopf-Version (Felder wie die Huelle von `getReceipt`). */
|
|
165
|
+
export interface ListReceiptsHeader {
|
|
166
|
+
/** `null` nur, wenn das Konto noch keine Kopf-Version hat (dann der heutige Stand). */
|
|
167
|
+
headerVersionId: string | null;
|
|
168
|
+
company: ReceiptCompany;
|
|
169
|
+
}
|
|
170
|
+
/** Ein Beleg der Liste je Ansicht: `full` das Belegdokument wie bei `getReceipt`, sonst die Zeile. */
|
|
171
|
+
export type ListedReceipt<V extends ListReceiptsView = 'summary'> = V extends 'full' ? ListedFullReceipt : ListReceiptsSummary;
|
|
152
172
|
/**
|
|
153
173
|
* Tagessumme aus der Tagesstatistik der Kasse, nur bei `date` und nur auf
|
|
154
174
|
* der ersten Seite. **Nicht die Summe der Liste:** `receiptCount` zaehlt jeden
|
|
@@ -183,9 +203,28 @@ export interface ListReceiptsPage<V extends ListReceiptsView = 'summary'> {
|
|
|
183
203
|
* `getReportV2().metadata`. Bei der Kurzansicht fehlt das Feld.
|
|
184
204
|
*/
|
|
185
205
|
metadata?: V extends 'full' ? ReportV2Metadata : never;
|
|
206
|
+
/**
|
|
207
|
+
* Nur bei `view: 'full'`: die Kopf-Versionen der Belege dieser Seite, je Version einmal, aelteste zuerst.
|
|
208
|
+
* Fuer den Druck gilt der Eintrag mit der `headerVersionId` des Belegs, nicht `metadata` (heutiger Stand).
|
|
209
|
+
* Fehlt bei einem Backend vor dem 30.10.2026.
|
|
210
|
+
*/
|
|
211
|
+
headers?: V extends 'full' ? ListReceiptsHeader[] : never;
|
|
212
|
+
/** Nur bei `view: 'full'`: Belege einer Testumgebung (Aufdruck TESTKASSE), wie `getReceipt`. */
|
|
213
|
+
testCashregister?: V extends 'full' ? boolean : never;
|
|
214
|
+
/** Nur bei `view: 'full'`: Groesse des Firmenlogos (`logo_scale`), wie `getReceipt`. */
|
|
215
|
+
logoScale?: V extends 'full' ? SheetLogoSize : never;
|
|
186
216
|
}
|
|
187
217
|
/** Zeile `view: summary` vom Draht. */
|
|
188
218
|
export declare function fromListReceiptsSummaryPayload(payload: Record<string, unknown>): ListReceiptsSummary;
|
|
219
|
+
/**
|
|
220
|
+
* Ein Beleg der vollen Ansicht mit den Druck- und Anzeigedaten seiner Seite, in derselben Form wie
|
|
221
|
+
* `getReceiptWithCompany` (ohne `outputs`): der Kopf der Version des Belegs aus `headers` (Nullbelege ohne
|
|
222
|
+
* Logo, wie beim Server), Testkennzeichen, Logo-Stufe. `layout` ist immer `null` -- die Liste traegt kein
|
|
223
|
+
* Zeilenmodell; [receiptLayoutFromResult] baut es dann mit denselben Angaben, oder `getReceiptWithCompany`
|
|
224
|
+
* holt das des Servers. Von einem Backend vor dem 30.10.2026 (ohne `headers`) kommt der Kopf aus
|
|
225
|
+
* `metadata`, dem heutigen Stand.
|
|
226
|
+
*/
|
|
227
|
+
export declare function listedReceiptWithCompany(page: ListReceiptsPage<'full'>, receipt: ListedFullReceipt): ReceiptWithCompany;
|
|
189
228
|
/**
|
|
190
229
|
* Eine Seite Belege der angemeldeten Kasse (`listReceipts`). Die Eingabe wird
|
|
191
230
|
* vor dem Senden nach den Regeln des Servers geprueft (genau eine Form des
|
|
@@ -193,8 +232,9 @@ export declare function fromListReceiptsSummaryPayload(payload: Record<string, u
|
|
|
193
232
|
* `order`, `view`); ein Verstoss wirft [KasseneckValidationError] mit
|
|
194
233
|
* `scope: 'request'`, ohne dass etwas hinausgeht.
|
|
195
234
|
*
|
|
196
|
-
* Mit `view: 'full'` sind die Belege das gespeicherte Belegdokument
|
|
197
|
-
*
|
|
235
|
+
* Mit `view: 'full'` sind die Belege das gespeicherte Belegdokument mit den
|
|
236
|
+
* Angaben, die `getReceipt` liefert ([ListedFullReceipt]; Kopf je Version in
|
|
237
|
+
* `headers`, ohne Zeilenmodell), sonst [ListReceiptsSummary].
|
|
198
238
|
* Alle Seiten auf einmal: [listAllReceipts].
|
|
199
239
|
*/
|
|
200
240
|
export declare function listReceipts<V extends ListReceiptsView = 'summary'>(transport: InternerTransport, options: ListReceiptsOptions<V>): Promise<ListReceiptsPage<V>>;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.LIST_RECEIPTS_ERROR_CODES = exports.LIST_ALL_RECEIPTS_MAX_PAGES = exports.LIST_RECEIPTS_DAYS_MAX = exports.LIST_RECEIPTS_LIMIT_MAX = exports.LIST_RECEIPTS_ORDERS = exports.LIST_RECEIPTS_VIEWS = void 0;
|
|
4
4
|
exports.fromListReceiptsSummaryPayload = fromListReceiptsSummaryPayload;
|
|
5
|
+
exports.listedReceiptWithCompany = listedReceiptWithCompany;
|
|
5
6
|
exports.listReceipts = listReceipts;
|
|
6
7
|
exports.listAllReceipts = listAllReceipts;
|
|
7
8
|
const index_js_1 = require("../enums/index.js");
|
|
@@ -225,6 +226,57 @@ function kopfdaten(roh) {
|
|
|
225
226
|
const label = roh.label;
|
|
226
227
|
return { ...(0, index_js_2.fromReceiptCompanyPayload)(roh), label: typeof label === 'string' ? label : '' };
|
|
227
228
|
}
|
|
229
|
+
const LOGO_STUFEN = ['S', 'M', 'L', 'XL'];
|
|
230
|
+
/** Ein Beleg der vollen Ansicht vom Draht: das Belegdokument und die Zusaetze, die `getReceipt` in der Huelle traegt. */
|
|
231
|
+
function vollerBeleg(roh) {
|
|
232
|
+
const beleg = (0, index_js_2.fromReceiptPayload)(roh);
|
|
233
|
+
if (typeof roh['testSignature'] === 'boolean')
|
|
234
|
+
beleg.testSignature = roh['testSignature'];
|
|
235
|
+
return beleg;
|
|
236
|
+
}
|
|
237
|
+
/** `headers` der vollen Ansicht: je Kopf-Version Kennung und Kopf. */
|
|
238
|
+
function koepfe(roh) {
|
|
239
|
+
if (!Array.isArray(roh) || roh.some((h) => !istObjekt(h)))
|
|
240
|
+
throw antwortfehler('headers ist keine Liste von Kopf-Versionen');
|
|
241
|
+
return roh.map((h) => ({
|
|
242
|
+
headerVersionId: typeof h['headerVersionId'] === 'string' && h['headerVersionId'] !== '' ? h['headerVersionId'] : null,
|
|
243
|
+
company: (0, index_js_2.fromReceiptCompanyPayload)(h),
|
|
244
|
+
}));
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Ein Beleg der vollen Ansicht mit den Druck- und Anzeigedaten seiner Seite, in derselben Form wie
|
|
248
|
+
* `getReceiptWithCompany` (ohne `outputs`): der Kopf der Version des Belegs aus `headers` (Nullbelege ohne
|
|
249
|
+
* Logo, wie beim Server), Testkennzeichen, Logo-Stufe. `layout` ist immer `null` -- die Liste traegt kein
|
|
250
|
+
* Zeilenmodell; [receiptLayoutFromResult] baut es dann mit denselben Angaben, oder `getReceiptWithCompany`
|
|
251
|
+
* holt das des Servers. Von einem Backend vor dem 30.10.2026 (ohne `headers`) kommt der Kopf aus
|
|
252
|
+
* `metadata`, dem heutigen Stand.
|
|
253
|
+
*/
|
|
254
|
+
function listedReceiptWithCompany(page, receipt) {
|
|
255
|
+
const kennung = receipt.headerVersionId ?? null;
|
|
256
|
+
const eintrag = page.headers?.find((h) => h.headerVersionId === kennung);
|
|
257
|
+
let company;
|
|
258
|
+
if (eintrag)
|
|
259
|
+
company = { ...eintrag.company };
|
|
260
|
+
else if (page.metadata) {
|
|
261
|
+
const { label: _label, ...kopf } = page.metadata;
|
|
262
|
+
company = kopf;
|
|
263
|
+
}
|
|
264
|
+
else
|
|
265
|
+
company = (0, index_js_2.fromReceiptCompanyPayload)({});
|
|
266
|
+
const art = typeof receipt.receiptType === 'object' ? receipt.receiptType.value : receipt.receiptType;
|
|
267
|
+
if (art === index_js_1.ReceiptType.zero.value)
|
|
268
|
+
delete company.logoUrl;
|
|
269
|
+
return {
|
|
270
|
+
receipt,
|
|
271
|
+
company,
|
|
272
|
+
testCashregister: page.testCashregister === true,
|
|
273
|
+
testSignature: receipt.testSignature === true,
|
|
274
|
+
headerVersionId: kennung,
|
|
275
|
+
layout: null,
|
|
276
|
+
registrationInfo: receipt.registrationInfo ?? null,
|
|
277
|
+
logoScale: page.logoScale ?? 'M',
|
|
278
|
+
};
|
|
279
|
+
}
|
|
228
280
|
/**
|
|
229
281
|
* Eine Seite Belege der angemeldeten Kasse (`listReceipts`). Die Eingabe wird
|
|
230
282
|
* vor dem Senden nach den Regeln des Servers geprueft (genau eine Form des
|
|
@@ -232,8 +284,9 @@ function kopfdaten(roh) {
|
|
|
232
284
|
* `order`, `view`); ein Verstoss wirft [KasseneckValidationError] mit
|
|
233
285
|
* `scope: 'request'`, ohne dass etwas hinausgeht.
|
|
234
286
|
*
|
|
235
|
-
* Mit `view: 'full'` sind die Belege das gespeicherte Belegdokument
|
|
236
|
-
*
|
|
287
|
+
* Mit `view: 'full'` sind die Belege das gespeicherte Belegdokument mit den
|
|
288
|
+
* Angaben, die `getReceipt` liefert ([ListedFullReceipt]; Kopf je Version in
|
|
289
|
+
* `headers`, ohne Zeilenmodell), sonst [ListReceiptsSummary].
|
|
237
290
|
* Alle Seiten auf einmal: [listAllReceipts].
|
|
238
291
|
*/
|
|
239
292
|
async function listReceipts(transport, options) {
|
|
@@ -254,14 +307,20 @@ async function listReceipts(transport, options) {
|
|
|
254
307
|
throw antwortfehler('nextCursor passt nicht zu hasMore (Text bei hasMore, sonst null)');
|
|
255
308
|
}
|
|
256
309
|
const receipts = (voll
|
|
257
|
-
? liste.map((b) => (
|
|
310
|
+
? liste.map((b) => vollerBeleg(b))
|
|
258
311
|
: liste.map((b) => fromListReceiptsSummaryPayload(b)));
|
|
312
|
+
const stufe = daten['logo_scale'];
|
|
259
313
|
return {
|
|
260
314
|
receipts,
|
|
261
315
|
hasMore: daten['hasMore'],
|
|
262
316
|
nextCursor: daten['hasMore'] ? weiter : null,
|
|
263
317
|
...('daySummary' in daten ? { daySummary: tagessumme(daten['daySummary']) } : {}),
|
|
264
318
|
...(voll && 'metadata' in daten ? { metadata: kopfdaten(daten['metadata']) } : {}),
|
|
319
|
+
...(voll && 'headers' in daten ? { headers: koepfe(daten['headers']) } : {}),
|
|
320
|
+
...(voll && 'testCashregister' in daten ? { testCashregister: (daten['testCashregister'] === true) } : {}),
|
|
321
|
+
...(voll && 'logo_scale' in daten
|
|
322
|
+
? { logoScale: (typeof stufe === 'string' && LOGO_STUFEN.includes(stufe) ? stufe : 'M') }
|
|
323
|
+
: {}),
|
|
265
324
|
};
|
|
266
325
|
}
|
|
267
326
|
/**
|
|
@@ -926,7 +926,7 @@ function cardRefundReference(receipt, paymentId) {
|
|
|
926
926
|
throw new errors_js_1.KasseneckValidationError('cancelReceipt', `Zahlung "${paymentId}" ist keine Kartenzahlung`, 'request');
|
|
927
927
|
}
|
|
928
928
|
if (typeof zahlung.providerPaymentId !== 'string' || zahlung.providerPaymentId === '') {
|
|
929
|
-
throw new errors_js_1.KasseneckValidationError('cancelReceipt', `Kennung der Kartenzahlung "${paymentId}" fehlt: sie
|
|
929
|
+
throw new errors_js_1.KasseneckValidationError('cancelReceipt', `Kennung der Kartenzahlung "${paymentId}" fehlt: der Beleg kam ohne Anbieterdaten (sie bekommt nur der Betrieb selbst, mit API-Schluessel oder Kassen-Anmeldung, nie ein Partner-Zugang)`, 'request');
|
|
930
930
|
}
|
|
931
931
|
return zahlung.providerPaymentId;
|
|
932
932
|
}
|
|
@@ -952,7 +952,7 @@ function pruefeKartenRueckbuchung(zahlungen, original) {
|
|
|
952
952
|
const orig = original != null && typeof z['refundOf'] === 'string' ? original.payments?.find((o) => o.id === z['refundOf']) : undefined;
|
|
953
953
|
if (typeof orig?.providerPaymentId === 'string' && orig.providerPaymentId !== '')
|
|
954
954
|
return;
|
|
955
|
-
throw new errors_js_1.KasseneckValidationError('cancelReceipt', `Zahlung ${i + 1}: Karten-Rueckbuchung ohne Bezug: providerPaymentId der Erstattung am Terminal angeben (oder das Original
|
|
955
|
+
throw new errors_js_1.KasseneckValidationError('cancelReceipt', `Zahlung ${i + 1}: Karten-Rueckbuchung ohne Bezug: providerPaymentId der Erstattung am Terminal angeben (oder das Original mit Anbieterdaten lesen: API-Schluessel des Betriebs oder Kassen-Anmeldung, dort traegt es die Kennung der Kartenzahlung)`, 'request');
|
|
956
956
|
});
|
|
957
957
|
}
|
|
958
958
|
/** Kartenanbieter pruefen — er stammt vom Aufrufer, nicht aus Serverdaten. */
|