@kreiseck/kasseneck-api 0.6.46 → 0.6.50
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 +100 -15
- package/dist/cjs/client/aufrufe.d.ts +1 -1
- package/dist/cjs/client/aufrufe.js +1 -0
- package/dist/cjs/client/errors.d.ts +7 -1
- package/dist/cjs/client/errors.js +8 -1
- package/dist/cjs/client/receipts.d.ts +16 -0
- package/dist/cjs/client/receipts.js +10 -0
- package/dist/cjs/client/transport.js +5 -4
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/index.js +4 -2
- package/dist/cjs/models/cancellation.d.ts +28 -0
- package/dist/cjs/models/cancellation.js +31 -1
- package/dist/cjs/models/hobex-receipt.js +9 -2
- package/dist/cjs/models/index.d.ts +1 -1
- package/dist/cjs/models/index.js +3 -1
- package/dist/cjs/models/receipt-summary.d.ts +2 -0
- package/dist/cjs/models/receipt-summary.js +4 -1
- package/dist/cjs/models/receipt.js +8 -1
- package/dist/cjs/models/voucher.js +18 -2
- package/dist/cjs/payments/hobex-hps/connect-client.d.ts +104 -0
- package/dist/cjs/payments/hobex-hps/connect-client.js +159 -0
- package/dist/cjs/payments/hobex-hps/errors.d.ts +122 -0
- package/dist/cjs/payments/hobex-hps/errors.js +158 -0
- package/dist/cjs/payments/hobex-hps/events.d.ts +22 -0
- package/dist/cjs/payments/hobex-hps/events.js +2 -0
- package/dist/cjs/payments/hobex-hps/index.d.ts +27 -0
- package/dist/cjs/payments/hobex-hps/index.js +66 -0
- package/dist/cjs/payments/hobex-hps/outcome.d.ts +31 -0
- package/dist/cjs/payments/hobex-hps/outcome.js +7 -0
- package/dist/cjs/payments/hobex-hps/payments.d.ts +149 -0
- package/dist/cjs/payments/hobex-hps/payments.js +550 -0
- package/dist/cjs/payments/hobex-hps/receipt.d.ts +28 -0
- package/dist/cjs/payments/hobex-hps/receipt.js +60 -0
- package/dist/cjs/payments/hobex-hps/transaction-id.d.ts +65 -0
- package/dist/cjs/payments/hobex-hps/transaction-id.js +91 -0
- package/dist/cjs/payments/hobex-hps/transaction-response.d.ts +163 -0
- package/dist/cjs/payments/hobex-hps/transaction-response.js +245 -0
- package/dist/cjs/payments/hobex.js +9 -6
- package/dist/cjs/payments/index.d.ts +26 -14
- package/dist/cjs/payments/index.js +59 -15
- package/dist/cjs/printing/index.d.ts +1 -1
- package/dist/cjs/printing/index.js +2 -1
- package/dist/cjs/printing/webusb.d.ts +37 -7
- package/dist/cjs/printing/webusb.js +76 -18
- package/dist/cjs/receipt/epos.d.ts +18 -0
- package/dist/cjs/receipt/epos.js +78 -6
- package/dist/cjs/receipt/index.d.ts +1 -1
- package/dist/cjs/receipt/index.js +2 -1
- package/dist/cjs/receipt/layout.js +19 -0
- package/dist/cjs/register/index.d.ts +1 -1
- package/dist/cjs/register/index.js +2 -1
- package/dist/cjs/register/pairing.d.ts +59 -0
- package/dist/cjs/register/pairing.js +51 -2
- package/dist/esm/client/aufrufe.d.ts +1 -1
- package/dist/esm/client/aufrufe.js +1 -0
- package/dist/esm/client/errors.d.ts +7 -1
- package/dist/esm/client/errors.js +8 -1
- package/dist/esm/client/receipts.d.ts +16 -0
- package/dist/esm/client/receipts.js +10 -0
- package/dist/esm/client/transport.js +5 -4
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/models/cancellation.d.ts +28 -0
- package/dist/esm/models/cancellation.js +29 -0
- package/dist/esm/models/hobex-receipt.js +10 -3
- package/dist/esm/models/index.d.ts +1 -1
- package/dist/esm/models/index.js +1 -1
- package/dist/esm/models/receipt-summary.d.ts +2 -0
- package/dist/esm/models/receipt-summary.js +4 -1
- package/dist/esm/models/receipt.js +8 -1
- package/dist/esm/models/voucher.js +18 -2
- package/dist/esm/payments/hobex-hps/connect-client.d.ts +104 -0
- package/dist/esm/payments/hobex-hps/connect-client.js +156 -0
- package/dist/esm/payments/hobex-hps/errors.d.ts +122 -0
- package/dist/esm/payments/hobex-hps/errors.js +149 -0
- package/dist/esm/payments/hobex-hps/events.d.ts +22 -0
- package/dist/esm/payments/hobex-hps/events.js +1 -0
- package/dist/esm/payments/hobex-hps/index.d.ts +27 -0
- package/dist/esm/payments/hobex-hps/index.js +26 -0
- package/dist/esm/payments/hobex-hps/outcome.d.ts +31 -0
- package/dist/esm/payments/hobex-hps/outcome.js +4 -0
- package/dist/esm/payments/hobex-hps/payments.d.ts +149 -0
- package/dist/esm/payments/hobex-hps/payments.js +547 -0
- package/dist/esm/payments/hobex-hps/receipt.d.ts +28 -0
- package/dist/esm/payments/hobex-hps/receipt.js +57 -0
- package/dist/esm/payments/hobex-hps/transaction-id.d.ts +65 -0
- package/dist/esm/payments/hobex-hps/transaction-id.js +86 -0
- package/dist/esm/payments/hobex-hps/transaction-response.d.ts +163 -0
- package/dist/esm/payments/hobex-hps/transaction-response.js +233 -0
- package/dist/esm/payments/hobex.js +9 -6
- package/dist/esm/payments/index.d.ts +26 -14
- package/dist/esm/payments/index.js +26 -14
- package/dist/esm/printing/index.d.ts +1 -1
- package/dist/esm/printing/index.js +1 -1
- package/dist/esm/printing/webusb.d.ts +37 -7
- package/dist/esm/printing/webusb.js +74 -17
- package/dist/esm/receipt/epos.d.ts +18 -0
- package/dist/esm/receipt/epos.js +76 -6
- package/dist/esm/receipt/index.d.ts +1 -1
- package/dist/esm/receipt/index.js +1 -1
- package/dist/esm/receipt/layout.js +19 -0
- package/dist/esm/register/index.d.ts +1 -1
- package/dist/esm/register/index.js +1 -1
- package/dist/esm/register/pairing.d.ts +59 -0
- package/dist/esm/register/pairing.js +50 -2
- package/fixtures/belege/storno-rabatt.json +3 -2
- package/fixtures/belege/storno-teil.json +2 -1
- package/fixtures/belege/storno-voll.json +2 -1
- package/fixtures/erwartet/storno-rabatt.grid32.txt +1 -0
- package/fixtures/erwartet/storno-rabatt.grid48.txt +1 -0
- package/fixtures/erwartet/storno-rabatt.lines.json +6 -0
- package/fixtures/erwartet/storno-teil.grid32.txt +1 -0
- package/fixtures/erwartet/storno-teil.grid48.txt +1 -0
- package/fixtures/erwartet/storno-teil.lines.json +6 -0
- package/fixtures/erwartet/storno-voll.grid32.txt +1 -0
- package/fixtures/erwartet/storno-voll.grid48.txt +1 -0
- package/fixtures/erwartet/storno-voll.lines.json +6 -0
- package/fixtures/hobex-hps-codes.json +67 -0
- package/fixtures/manifest.json +12 -12
- package/fixtures/oberflaeche.json +2 -1
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -167,6 +167,43 @@ sich nicht stornieren, ein voll stornierter Beleg nicht noch einmal. Am
|
|
|
167
167
|
gelesenen Original liefert `remainingQuantities(receipt)` die Reste vorab (für
|
|
168
168
|
den Storno-Dialog); die Wahrheit hat der Server.
|
|
169
169
|
|
|
170
|
+
**Fehler entscheidet man am Code, nicht am Text.** Jeder fachliche Fehler von
|
|
171
|
+
`cancelReceipt` trägt `KasseneckApiError.code` aus `CANCELLATION_ERROR_CODES`
|
|
172
|
+
(z. B. `bereits_storniert`, `menge_ueber_rest`, `nur_eigene_belege`). Die
|
|
173
|
+
deutsche Meldung (`serverMessage`) ist Anzeige und darf sich ändern.
|
|
174
|
+
|
|
175
|
+
```ts
|
|
176
|
+
import { isKasseneckApiError, isCancellationErrorCode } from '@kreiseck/kasseneck-api';
|
|
177
|
+
|
|
178
|
+
try {
|
|
179
|
+
await api.cancelReceipt({ receipt: beleg, reason: 'fehleingabe' });
|
|
180
|
+
} catch (fehler) {
|
|
181
|
+
if (isKasseneckApiError(fehler) && isCancellationErrorCode(fehler.code)) {
|
|
182
|
+
switch (fehler.code) {
|
|
183
|
+
case 'bereits_storniert': // Beleg im Dialog als „storniert" zeigen, Knopf sperren
|
|
184
|
+
case 'menge_ueber_rest': // Restmengen neu laden (jemand war schneller)
|
|
185
|
+
case 'nur_eigene_belege': // Chef holen
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
throw fehler;
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
**Gutscheine.** Ein Wertgutschein wird nur beim Vollstorno (ohne `items`)
|
|
194
|
+
gespiegelt — er ist unteilbar. Ein Rabattgutschein ist am Original bereits in
|
|
195
|
+
den Umsatz eingerechnet; **jeder** Storno nimmt ihn anteilig der stornierten
|
|
196
|
+
Menge zurück: bei 3 Stück à 10 € mit 6 € Rabatt sind 8 € je Stück Entgelt, der
|
|
197
|
+
Storno-Beleg trägt dann „−10,00" plus eine Zeile „Gutschein-Ausgleich +2,00".
|
|
198
|
+
Was ein Storno gewährt hat, steht am Eintrag in `receipt.cancellations[]` als
|
|
199
|
+
`promoAdjustmentCents` (Cent je Steuertopf) — die Kasse kann es im Dialog
|
|
200
|
+
zeigen, rechnen muss sie nichts.
|
|
201
|
+
|
|
202
|
+
**Bon.** Der Kopfblock des Storno-Bons nennt Bezug, Datum des Originals und
|
|
203
|
+
Grund: „STORNOBELEG / Stornobuchung zu Beleg KASSE1-ID-42 / vom 11.08.2026,
|
|
204
|
+
09:02 Uhr / Grund: Fehleingabe". Das Datum kommt aus `cancellationOf.timeStamp`
|
|
205
|
+
(Backend seit 2026-09-04); Altbelege ohne bleiben ohne die Zeile.
|
|
206
|
+
|
|
170
207
|
## Unterpfade
|
|
171
208
|
|
|
172
209
|
| Unterpfad | Inhalt |
|
|
@@ -174,7 +211,7 @@ den Storno-Dialog); die Wahrheit hat der Server.
|
|
|
174
211
|
| `@kreiseck/kasseneck-api` | Endpunkte, Anmeldung, Transport, Modelle, Enums, Fehler — alles, was mit dem Backend spricht. |
|
|
175
212
|
| `…/receipt` | Beleg-Layout als Datenmodell (framework-frei) und die Brücke zu ESC/POS. |
|
|
176
213
|
| `…/printing` | ESC/POS-Erzeugung: Bytefolgen für Bondrucker, ohne jeden Transport. |
|
|
177
|
-
| `…/payments` | Stripe-Zahllinks
|
|
214
|
+
| `…/payments` | Stripe-Zahllinks, Hobex-Cloud (beides HTTP-Endpunkte des Backends) und Hobex **HPS** über **Kasseneck Connect** (lokaler Geräte-Agent, spricht mit dem Terminal). |
|
|
178
215
|
| `…/register` | Anmeldung der Browser-Kasse: Gerät koppeln und entkoppeln, Benutzer auflisten, per PIN anmelden, Sitzung erneuern und beenden. |
|
|
179
216
|
| `…/kasse` | Kachel-Kasse: Kassen-Einstellungen (betriebsweit / je Gerät), Artikelgruppen und Artikel für Kacheln, Rabattverteilung je Steuersatz, Reichweiten der Kassen-Rechte |
|
|
180
217
|
| `…/react` | Dünner React-Adapter, der ein Beleg-Layout zeichnet. Braucht React. |
|
|
@@ -182,16 +219,40 @@ den Storno-Dialog); die Wahrheit hat der Server.
|
|
|
182
219
|
|
|
183
220
|
So zieht sich niemand den React-Adapter in ein Node-Programm.
|
|
184
221
|
|
|
185
|
-
##
|
|
222
|
+
## Hobex HPS über Kasseneck Connect
|
|
223
|
+
|
|
224
|
+
Ein Browser hat weiterhin keine rohen TCP-Sockets — ein **direkter**
|
|
225
|
+
Terminal-Kontakt wie beim Flutter-Paket `kasseneck_api` (`HpsClient`) bleibt
|
|
226
|
+
deshalb außerhalb der Reichweite dieses Pakets. **Kasseneck Connect** ist aber
|
|
227
|
+
ein lokaler Geräte-Agent mit gewöhnlicher HTTP-Schnittstelle, der für die Kasse
|
|
228
|
+
mit dem Terminal spricht — und darüber geht es:
|
|
229
|
+
|
|
230
|
+
```ts
|
|
231
|
+
import { createHpsConnectClient, createHpsPayments } from '@kreiseck/kasseneck-api/payments';
|
|
186
232
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
sind Android-SDKs. Ein Browser kann das nicht, und kein Bündler ändert daran
|
|
190
|
-
etwas. Wer diese Terminals braucht, nimmt das Flutter-Paket `kasseneck_api`.
|
|
233
|
+
const client = createHpsConnectClient({ token: kopplungsToken });
|
|
234
|
+
const zahlweg = createHpsPayments(client, { host: '192.168.1.50', tid: '3600335' });
|
|
191
235
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
236
|
+
const ergebnis = await zahlweg.pay({ amountCents: 1050 });
|
|
237
|
+
// ergebnis.outcome: 'approved' | 'declined' | 'unresolved' — nie geraten.
|
|
238
|
+
// ergebnis.transactionId ist IMMER gesetzt, auch bei 'unresolved'.
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Der Ausgang ist immer einer von drei: `approved`, `declined` (beweisbar nichts
|
|
242
|
+
belastet) oder `unresolved` (Ausgang unbekannt, eine Wiederholung könnte ein
|
|
243
|
+
zweites Mal belasten). Was das bedeutet und warum es so gebaut ist, steht in
|
|
244
|
+
`src/payments/hobex-hps/payments.ts` — dort ist die Dokumentation der Maßstab,
|
|
245
|
+
nicht dieses README.
|
|
246
|
+
|
|
247
|
+
**Nur `pay` — bewusst kein `refund`/`cancel`.** Kasseneck Connect exponiert
|
|
248
|
+
dafür (noch) keinen Endpunkt; eine Gutschrift oder ein Storno am HPS-Terminal
|
|
249
|
+
braucht weiterhin die Flutter-App. **myPOS** und **SumUp** bleiben
|
|
250
|
+
Android-SDKs ohne Entsprechung hier.
|
|
251
|
+
|
|
252
|
+
## Was hier grundsätzlich nicht dazugehört
|
|
253
|
+
|
|
254
|
+
Die Druckeransteuerung selbst (dieses Paket erzeugt die Bytes, es verschickt
|
|
255
|
+
sie nicht), Firmenlogos und Rasterbilder, und die PDF-Erzeugung.
|
|
195
256
|
|
|
196
257
|
## Entwicklung
|
|
197
258
|
|
|
@@ -199,28 +260,52 @@ PDF-Erzeugung.
|
|
|
199
260
|
npm test # Testsuite in drei Zeitzonen (Wien, UTC, Kiritimati)
|
|
200
261
|
npm run build # ESM- und CJS-Bau nach dist/, inkl. Prüfung der exports
|
|
201
262
|
npm run check:consumer # baut den Tarball und übersetzt zwei Verbraucher (CJS/ESM)
|
|
263
|
+
npm run check:erreichbar # fragt die öffentliche Adresse: antwortet dort zu jedem Aufruf eine Function?
|
|
202
264
|
```
|
|
203
265
|
|
|
204
266
|
Die drei Zeitzonen sind kein Übereifer: Zeitfehler sind auf einer Wiener
|
|
205
267
|
Maschine zufällig richtig. Belegzeiten werden konsequent als **Wiener
|
|
206
268
|
Wanduhrzeit** gedeutet (`parseServerTimeStamp`), nie über `new Date(text)`.
|
|
207
269
|
|
|
270
|
+
### `check:erreichbar` — spricht als einzige mit `api.kasseneck.at`
|
|
271
|
+
|
|
272
|
+
Testsuite und `check:consumer` laufen gegen Attrappen bzw. gegen den Tarball;
|
|
273
|
+
keine von beiden setzt je einen Aufruf ab. Fehlt einem Aufruf die
|
|
274
|
+
Hosting-Weiterleitung, liefert die veröffentlichte Adresse die
|
|
275
|
+
HTML-Auffangseite statt der Function — und das sieht keine Attrappe.
|
|
276
|
+
|
|
277
|
+
Die Prüfung braucht **keine Zugangsdaten**. Ein Aufruf ohne Anmeldung
|
|
278
|
+
antwortet, wenn dort eine Function steht, mit
|
|
279
|
+
`{"status":"error","message":"Ungültiger Request: Authorization key erwartet."}`.
|
|
280
|
+
Genau das ist der Beweis: Der Aufruf wurde angenommen und die Anmeldung
|
|
281
|
+
geprüft. Eine HTML-Seite oder ein 404 ist der Beweis, dass dort keine Function
|
|
282
|
+
steht. Deshalb prüft das Skript auf ein `status`-Feld und nicht auf Erfolg.
|
|
283
|
+
|
|
284
|
+
Bewusst außerhalb von `npm test`: Sie braucht Netz. Ist keines da, sagt sie es
|
|
285
|
+
und endet mit 0. Aufrufe, die unter `/v1` absichtlich keine Weiterleitung
|
|
286
|
+
haben — der Kassen-Weg über `kasse.kasseneck.at/api`, die Aufrufe mit
|
|
287
|
+
Firebase-ID-Token — stehen mit Grund in `scripts/erreichbarkeit-ausnahmen.json`.
|
|
288
|
+
Wird eine Ausnahme erreichbar, schlägt die Prüfung an: Sonst sänke die Zahl nie.
|
|
289
|
+
|
|
208
290
|
## Vertragsdateien für die Zwillinge
|
|
209
291
|
|
|
210
292
|
Dieses Paket ist die Quelle für das Dart-Paket `kasseneck_api` und den
|
|
211
|
-
Backend-Validator `kasse-settings-core.js`.
|
|
212
|
-
im Tarball mit und sagen in Maschinenform, worauf sich
|
|
293
|
+
Backend-Validator `kasse-settings-core.js`. Drei Dateien in `fixtures/` reisen
|
|
294
|
+
im Tarball mit und sagen in Maschinenform, worauf sich beide Seiten geeinigt haben:
|
|
213
295
|
|
|
214
296
|
| Datei | Inhalt |
|
|
215
297
|
|---|---|
|
|
216
298
|
| `kasse-settings-standard.json` | Feldnamen und Standardwerte der Kassen-Einstellungen |
|
|
217
299
|
| `oberflaeche.json` | Aufrufnamen, Enum-Werte, Rechte-Schlüssel, Tasten-Aktionen |
|
|
300
|
+
| `hobex-hps-codes.json` | Gemessene HPS-Ergebniscodes, ihre Bedeutung und ob sie einen Ausgang festschreiben — der Vertrag hinter `.../payments/hobex-hps`s `isConclusive`. |
|
|
218
301
|
|
|
219
|
-
|
|
220
|
-
und nie von Hand geändert; die CI prüft
|
|
302
|
+
Alle drei werden erzeugt (`npm run fixtures:kasse`, `npm run fixtures:oberflaeche`,
|
|
303
|
+
`npm run fixtures:hobex-hps-codes`) und nie von Hand geändert; die CI prüft
|
|
304
|
+
nach jedem Lauf, dass sie zum Code passen.
|
|
221
305
|
|
|
222
|
-
`oberflaeche.json`
|
|
223
|
-
|
|
306
|
+
`oberflaeche.json` und `hobex-hps-codes.json` tragen die Paketversion. **Nach
|
|
307
|
+
jedem `npm version` müssen deshalb alle drei Dateien neu erzeugt und
|
|
308
|
+
mitcommittet werden**, sonst wird die CI rot.
|
|
224
309
|
|
|
225
310
|
## Lizenz
|
|
226
311
|
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* umhuellt, muss ihn weiterhin absetzen koennen.
|
|
14
14
|
*/
|
|
15
15
|
import type { TransportBodyFields } from './transport.js';
|
|
16
|
-
export declare const AUFRUFE: readonly ["cancelReceipt", "createPaymentLinkStripe", "createPrintJob", "createReceipt", "downloadDailyReport", "downloadReport", "endRegisterSession", "financeWebService", "generateFullReceiptId", "getFirstReceiptDate", "getKasseSettings", "getPrintJob", "getReceipt", "hobexPayApi", "hobexRefundApi", "listMyArticleGroups", "listMyArticles", "listMyCashregisters", "listMyPrinters", "listMyReceipts", "listMyTipRecipients", "listRegisterUsersForDevice", "pairRegisterDevice", "registerPinLogin", "registerUserLogin", "renewRegisterSession", "setMyKasseSettings", "setMyRegisterDeviceSettings", "stripeCaptureIntent", "unpairRegisterDevice"];
|
|
16
|
+
export declare const AUFRUFE: readonly ["cancelReceipt", "createPaymentLinkStripe", "createPrintJob", "createReceipt", "downloadDailyReport", "downloadReport", "endRegisterSession", "financeWebService", "generateFullReceiptId", "getFirstReceiptDate", "getKasseSettings", "getPrintJob", "getReceipt", "hobexPayApi", "hobexRefundApi", "listMyArticleGroups", "listMyArticles", "listMyCashregisters", "listMyPrinters", "listMyReceipts", "listMyTipRecipients", "listRegisterUsersForDevice", "listRegisterSessionsForDevice", "pairRegisterDevice", "registerPinLogin", "registerUserLogin", "renewRegisterSession", "setMyKasseSettings", "setMyRegisterDeviceSettings", "stripeCaptureIntent", "unpairRegisterDevice"];
|
|
17
17
|
export type Aufruf = typeof AUFRUFE[number];
|
|
18
18
|
/** Wie [KasseneckTransport], nur mit bekanntem Aufrufnamen. Nicht exportiert nach aussen. */
|
|
19
19
|
export type InternerTransport = <T = unknown>(functionName: Aufruf, params?: Record<string, unknown>, extraBodyFields?: TransportBodyFields, secretParams?: readonly string[]) => Promise<T>;
|
|
@@ -76,7 +76,13 @@ export declare class KasseneckApiError extends Error {
|
|
|
76
76
|
readonly functionName: string;
|
|
77
77
|
/** Meldung des Backends, unveraendert (`message` aus der Huelle). */
|
|
78
78
|
readonly serverMessage: string;
|
|
79
|
-
|
|
79
|
+
/**
|
|
80
|
+
* Stabiler Fehlercode des Backends (`code` aus der Huelle), wenn der
|
|
81
|
+
* Endpunkt einen legt — heute `cancelReceipt` (siehe CANCELLATION_ERROR_CODES).
|
|
82
|
+
* Daran entscheiden, nie an [serverMessage]: der Text darf sich aendern.
|
|
83
|
+
*/
|
|
84
|
+
readonly code: string | undefined;
|
|
85
|
+
constructor(functionName: string, serverMessage: string, code?: string);
|
|
80
86
|
}
|
|
81
87
|
/** Warum die Antwort keine verwertbare Huelle war. */
|
|
82
88
|
export type HttpFailureReason = 'server-error' | 'empty-body' | 'not-json' | 'missing-status';
|
|
@@ -101,10 +101,17 @@ class KasseneckApiError extends Error {
|
|
|
101
101
|
functionName;
|
|
102
102
|
/** Meldung des Backends, unveraendert (`message` aus der Huelle). */
|
|
103
103
|
serverMessage;
|
|
104
|
-
|
|
104
|
+
/**
|
|
105
|
+
* Stabiler Fehlercode des Backends (`code` aus der Huelle), wenn der
|
|
106
|
+
* Endpunkt einen legt — heute `cancelReceipt` (siehe CANCELLATION_ERROR_CODES).
|
|
107
|
+
* Daran entscheiden, nie an [serverMessage]: der Text darf sich aendern.
|
|
108
|
+
*/
|
|
109
|
+
code;
|
|
110
|
+
constructor(functionName, serverMessage, code) {
|
|
105
111
|
super(`${functionName} fehlgeschlagen: ${serverMessage}`);
|
|
106
112
|
this.functionName = functionName;
|
|
107
113
|
this.serverMessage = serverMessage;
|
|
114
|
+
this.code = code;
|
|
108
115
|
}
|
|
109
116
|
}
|
|
110
117
|
exports.KasseneckApiError = KasseneckApiError;
|
|
@@ -54,6 +54,22 @@ export interface TipOptions {
|
|
|
54
54
|
paymentMethod?: KeckPaymentMethod | KeckPaymentMethodKey;
|
|
55
55
|
/** Empfaenger; ohne Angabe der angemeldete Kassen-Benutzer. Summe der cents = cents. */
|
|
56
56
|
recipients?: TipRecipientShare[];
|
|
57
|
+
/**
|
|
58
|
+
* Hat der Empfaenger das Geld schon? `true` = ja (Bargeld mitgenommen,
|
|
59
|
+
* Kartentrinkgeld sofort aus der Lade ausgezahlt), `false` = der Betrieb
|
|
60
|
+
* behaelt es und schuldet es.
|
|
61
|
+
*
|
|
62
|
+
* Entscheidend ist NICHT die Zahlart, sondern der Besitz -- § 2j Abs 2 AVRAG
|
|
63
|
+
* kennt beide Faelle. Ohne Angabe gilt die Voreinstellung des Betriebs (bar:
|
|
64
|
+
* schon erhalten, bargeldlos: einbehalten); diese Angabe braucht es nur fuer
|
|
65
|
+
* die Ausnahme.
|
|
66
|
+
*
|
|
67
|
+
* NUR mit dem Recht `tipAssign` bei persoenlicher Anmeldung: das Merkmal
|
|
68
|
+
* entscheidet, ob der Betrieb Geld schuldet, und gewoehnliches Personal soll
|
|
69
|
+
* das nicht am Geraet umstellen koennen. Ueber einen Geraete-API-Schluessel
|
|
70
|
+
* (ohne angemeldeten Kassen-Benutzer) gilt die Einschraenkung nicht.
|
|
71
|
+
*/
|
|
72
|
+
sofortErhalten?: boolean;
|
|
57
73
|
}
|
|
58
74
|
export interface TipRecipientShare {
|
|
59
75
|
registerUserId: string;
|
|
@@ -399,6 +399,16 @@ function gepruefterTip(tip) {
|
|
|
399
399
|
const nutzlast = { cents: tip.cents };
|
|
400
400
|
if (tip.paymentMethod != null)
|
|
401
401
|
nutzlast['paymentMethod'] = gepruefteZahlungsart(tip.paymentMethod);
|
|
402
|
+
// Nur mitschicken, wenn gesetzt: fehlt das Feld, entscheidet die
|
|
403
|
+
// Voreinstellung des Betriebs. Ein `false` waere dort eine Aussage, kein
|
|
404
|
+
// Weglassen. Der Wortlaut des Fehlers ist der des Backends
|
|
405
|
+
// (tip-core.normalizeTip) -- wer ihn hier sieht, sieht denselben Satz.
|
|
406
|
+
if (tip.sofortErhalten != null) {
|
|
407
|
+
if (typeof tip.sofortErhalten !== 'boolean') {
|
|
408
|
+
throw eingabefehler('Trinkgeld: sofortErhalten muss true oder false sein.');
|
|
409
|
+
}
|
|
410
|
+
nutzlast['sofortErhalten'] = tip.sofortErhalten;
|
|
411
|
+
}
|
|
402
412
|
if (tip.recipients != null) {
|
|
403
413
|
if (!Array.isArray(tip.recipients) || tip.recipients.length === 0) {
|
|
404
414
|
throw eingabefehler('Trinkgeld: recipients darf nicht leer sein.');
|
|
@@ -181,7 +181,7 @@ function jsonAuswerten(text, functionName, statusCode, inhaltstyp) {
|
|
|
181
181
|
}
|
|
182
182
|
// Alles, was nicht ausdruecklich Erfolg ist, gilt als fachlicher Fehler —
|
|
183
183
|
// ein unbekannter Statuswert darf nie stillschweigend als Erfolg durchgehen.
|
|
184
|
-
throw fachfehler(functionName, huelle.message);
|
|
184
|
+
throw fachfehler(functionName, huelle.message, huelle.code);
|
|
185
185
|
}
|
|
186
186
|
/**
|
|
187
187
|
* Auswertung des Binaerwegs. Der Kern der Zusage: **ein Aufrufer bekommt nie
|
|
@@ -227,7 +227,7 @@ function pdfAuswerten(bytes, functionName, statusCode, inhaltstyp) {
|
|
|
227
227
|
}
|
|
228
228
|
// Derselbe fachliche Fehler wie auf dem JSON-Weg — fuer den Aufrufer macht
|
|
229
229
|
// es keinen Unterschied, ob er ein PDF oder eine Nutzlast erwartet hat.
|
|
230
|
-
throw fachfehler(functionName, huelle.message);
|
|
230
|
+
throw fachfehler(functionName, huelle.message, huelle.code);
|
|
231
231
|
}
|
|
232
232
|
/** `%PDF` am Anfang — die Kennung jeder PDF-Datei. */
|
|
233
233
|
function istPdf(bytes) {
|
|
@@ -240,9 +240,10 @@ function alsHuelle(wert) {
|
|
|
240
240
|
}
|
|
241
241
|
return wert;
|
|
242
242
|
}
|
|
243
|
-
function fachfehler(functionName, message) {
|
|
243
|
+
function fachfehler(functionName, message, code) {
|
|
244
244
|
const meldung = typeof message === 'string' && message.trim() ? message : 'Unbekannter Fehler';
|
|
245
|
-
|
|
245
|
+
// Nur ein Text zaehlt als Code -- alles andere waere ein geratener Vertrag.
|
|
246
|
+
return new errors_js_1.KasseneckApiError(functionName, meldung, typeof code === 'string' && code ? code : undefined);
|
|
246
247
|
}
|
|
247
248
|
/**
|
|
248
249
|
* Nutzlast aus Auth- und Aufruferparametern. Auth-Parameter bilden die
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { ReceiptType, type ReceiptTypeKey, VatRate, type VatRateKey, KeckPaymentMethod, type KeckPaymentMethodKey, CreditCardProvider, type CreditCardProviderKey, VoucherType, type VoucherTypeKey, VoucherAction, type VoucherActionKey, StripeLinkMode, type StripeLinkModeKey, } from './enums/index.js';
|
|
2
|
-
export { type ReceiptItem, type ReceiptItemPayload, type ReceiptItemPayloadRead, toReceiptItemPayload, fromReceiptItemPayload, receiptItemTotalCents, receiptItemIsValid, negateReceiptItem, type TipRecipient, isTipItem, type Voucher, type VoucherPayload, toVoucherPayload, fromVoucherPayload, voucherIsValid, type Receipt, type ReceiptPayload, type ReceiptPayloadRead, toReceiptPayload, fromReceiptPayload, receiptSubSumCents, receiptSumCents, type CancellationOf, type CancellationItem, type Cancellation, type CancellationReason, CANCELLATION_REASONS, CANCELLATION_RESERVATION_MS, isCancellationReason, remainingQuantities, type ReceiptCompany, type ReceiptCompanyPayload, fromReceiptCompanyPayload, receiptCompanyTaxInfo, type Cashregister, type CashregisterOnboarding, type CashregisterPayload, type CashregisterOnboardingPayload, fromCashregisterPayload, type ReceiptSummary, type ReceiptSummaryPayload, fromReceiptSummaryPayload, type ZeroKind, ZERO_KINDS, istZeroKind, type ReportMonth, reportMonthFromDate, previousReportMonth, nextReportMonth, reportMonthKey, reportMonthReadable, type StripeUrlSession, type StripeUrlSessionPayload, toStripeUrlSessionPayload, fromStripeUrlSessionPayload, type HobexReceipt, type HobexReceiptPayload, toHobexReceiptPayload, fromHobexReceiptPayload, hobexReceiptToCardPaymentData, hobexReceiptNeedsSignature, } from './models/index.js';
|
|
2
|
+
export { type ReceiptItem, type ReceiptItemPayload, type ReceiptItemPayloadRead, toReceiptItemPayload, fromReceiptItemPayload, receiptItemTotalCents, receiptItemIsValid, negateReceiptItem, type TipRecipient, isTipItem, type Voucher, type VoucherPayload, toVoucherPayload, fromVoucherPayload, voucherIsValid, type Receipt, type ReceiptPayload, type ReceiptPayloadRead, toReceiptPayload, fromReceiptPayload, receiptSubSumCents, receiptSumCents, type CancellationOf, type CancellationItem, type Cancellation, type CancellationReason, type CancellationErrorCode, CANCELLATION_REASONS, CANCELLATION_ERROR_CODES, CANCELLATION_RESERVATION_MS, isCancellationReason, isCancellationErrorCode, remainingQuantities, type ReceiptCompany, type ReceiptCompanyPayload, fromReceiptCompanyPayload, receiptCompanyTaxInfo, type Cashregister, type CashregisterOnboarding, type CashregisterPayload, type CashregisterOnboardingPayload, fromCashregisterPayload, type ReceiptSummary, type ReceiptSummaryPayload, fromReceiptSummaryPayload, type ZeroKind, ZERO_KINDS, istZeroKind, type ReportMonth, reportMonthFromDate, previousReportMonth, nextReportMonth, reportMonthKey, reportMonthReadable, type StripeUrlSession, type StripeUrlSessionPayload, toStripeUrlSessionPayload, fromStripeUrlSessionPayload, type HobexReceipt, type HobexReceiptPayload, toHobexReceiptPayload, fromHobexReceiptPayload, hobexReceiptToCardPaymentData, hobexReceiptNeedsSignature, } from './models/index.js';
|
|
3
3
|
export { type AuthCredentials, type KasseneckAuth, type ApiKeyAuthOptions, type RegisterUserAuthOptions, apiKeyAuth, registerUserAuth, DEFAULT_BASE_URL, DEFAULT_TIMEOUT_MS, type HttpResponseLike, type HttpRequestInit, type FetchLike, type TransportOptions, type TransportBodyFields, type KasseneckTransport, type KasseneckBinaryTransport, AUFRUFE, type Aufruf, createTransport, createBinaryTransport, KasseneckApiError, KasseneckAuthError, KasseneckHttpError, KasseneckNetworkError, KasseneckValidationError, type ValidationScope, type KasseneckError, type HttpFailureReason, type CauseDigest, isKasseneckApiError, isKasseneckAuthError, isKasseneckHttpError, isKasseneckNetworkError, isKasseneckValidationError, type ReceiptCommonOptions, type SellReceiptOptions, type TipOptions, type TipRecipientShare, type CancelReceiptOptions, type CancelReceiptResult, type CreateCancelReceiptOptions, type ReceiptWithCompany, sellReceipt, sellReceiptWithCompany, cancelReceipt, createCancelReceipt, zeroReceipt, getReceipt, getReceiptWithCompany, generateFullReceiptId, getFirstReceiptDate, listMyReceipts, type ListMyReceiptsOptions, type ReceiptList, type ReceiptListStats, listMyCashregisters, checkVoucherCombinationError, downloadDailyReport, downloadMonthlyReport, type CashboxStatus, type SignatureStatus, getCashboxStatus, getSignatureStatus, type KasseneckApi, createKasseneckApi, } from './client/index.js';
|
|
4
4
|
export { parseServerTimeStamp, toViennaWallClock, type ViennaWallClock } from './vienna-time.js';
|
|
5
5
|
export { type KasseSettingsBetrieb, type KasseSettingsGeraet, type KasseSettings, type ArticleGroup, type KasseArtikel, KASSE_BETRIEB_STANDARD, KASSE_GERAET_STANDARD, mergeKasseSettings, fromArticleGroupPayload, fromKasseArtikelPayload, listMyArticleGroups, listMyArticles, getKasseSettings, setMyKasseSettings, setMyRegisterDeviceSettings, verteileRabatt, cancelScopeOf, receiptsScopeOf, } from './kasse/index.js';
|
package/dist/cjs/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
exports.receiptsScopeOf = exports.cancelScopeOf = exports.verteileRabatt = exports.setMyRegisterDeviceSettings = exports.setMyKasseSettings = exports.getKasseSettings = exports.listMyArticles = exports.listMyArticleGroups = exports.fromKasseArtikelPayload = exports.fromArticleGroupPayload = exports.mergeKasseSettings = exports.KASSE_GERAET_STANDARD = exports.KASSE_BETRIEB_STANDARD = exports.toViennaWallClock = exports.parseServerTimeStamp = exports.createKasseneckApi = exports.getSignatureStatus = exports.getCashboxStatus = exports.downloadMonthlyReport = exports.downloadDailyReport = exports.checkVoucherCombinationError = exports.listMyCashregisters = exports.listMyReceipts = exports.getFirstReceiptDate = exports.generateFullReceiptId = exports.getReceiptWithCompany = exports.getReceipt = exports.zeroReceipt = exports.createCancelReceipt = exports.cancelReceipt = exports.sellReceiptWithCompany = exports.sellReceipt = exports.isKasseneckValidationError = exports.isKasseneckNetworkError = exports.isKasseneckHttpError = exports.isKasseneckAuthError = exports.isKasseneckApiError = exports.KasseneckValidationError = exports.KasseneckNetworkError = exports.KasseneckHttpError = void 0;
|
|
3
|
+
exports.createBinaryTransport = exports.createTransport = exports.AUFRUFE = exports.DEFAULT_TIMEOUT_MS = exports.DEFAULT_BASE_URL = exports.registerUserAuth = exports.apiKeyAuth = exports.hobexReceiptNeedsSignature = exports.hobexReceiptToCardPaymentData = exports.fromHobexReceiptPayload = exports.toHobexReceiptPayload = exports.fromStripeUrlSessionPayload = exports.toStripeUrlSessionPayload = exports.reportMonthReadable = exports.reportMonthKey = exports.nextReportMonth = exports.previousReportMonth = exports.reportMonthFromDate = exports.istZeroKind = exports.ZERO_KINDS = exports.fromReceiptSummaryPayload = exports.fromCashregisterPayload = exports.receiptCompanyTaxInfo = exports.fromReceiptCompanyPayload = exports.remainingQuantities = exports.isCancellationErrorCode = exports.isCancellationReason = exports.CANCELLATION_RESERVATION_MS = exports.CANCELLATION_ERROR_CODES = exports.CANCELLATION_REASONS = exports.receiptSumCents = exports.receiptSubSumCents = exports.fromReceiptPayload = exports.toReceiptPayload = exports.voucherIsValid = exports.fromVoucherPayload = exports.toVoucherPayload = exports.isTipItem = exports.negateReceiptItem = exports.receiptItemIsValid = exports.receiptItemTotalCents = exports.fromReceiptItemPayload = exports.toReceiptItemPayload = exports.StripeLinkMode = exports.VoucherAction = exports.VoucherType = exports.CreditCardProvider = exports.KeckPaymentMethod = exports.VatRate = exports.ReceiptType = void 0;
|
|
4
|
+
exports.receiptsScopeOf = exports.cancelScopeOf = exports.verteileRabatt = exports.setMyRegisterDeviceSettings = exports.setMyKasseSettings = exports.getKasseSettings = exports.listMyArticles = exports.listMyArticleGroups = exports.fromKasseArtikelPayload = exports.fromArticleGroupPayload = exports.mergeKasseSettings = exports.KASSE_GERAET_STANDARD = exports.KASSE_BETRIEB_STANDARD = exports.toViennaWallClock = exports.parseServerTimeStamp = exports.createKasseneckApi = exports.getSignatureStatus = exports.getCashboxStatus = exports.downloadMonthlyReport = exports.downloadDailyReport = exports.checkVoucherCombinationError = exports.listMyCashregisters = exports.listMyReceipts = exports.getFirstReceiptDate = exports.generateFullReceiptId = exports.getReceiptWithCompany = exports.getReceipt = exports.zeroReceipt = exports.createCancelReceipt = exports.cancelReceipt = exports.sellReceiptWithCompany = exports.sellReceipt = exports.isKasseneckValidationError = exports.isKasseneckNetworkError = exports.isKasseneckHttpError = exports.isKasseneckAuthError = exports.isKasseneckApiError = exports.KasseneckValidationError = exports.KasseneckNetworkError = exports.KasseneckHttpError = exports.KasseneckAuthError = exports.KasseneckApiError = void 0;
|
|
5
5
|
var index_js_1 = require("./enums/index.js");
|
|
6
6
|
Object.defineProperty(exports, "ReceiptType", { enumerable: true, get: function () { return index_js_1.ReceiptType; } });
|
|
7
7
|
Object.defineProperty(exports, "VatRate", { enumerable: true, get: function () { return index_js_1.VatRate; } });
|
|
@@ -25,8 +25,10 @@ Object.defineProperty(exports, "fromReceiptPayload", { enumerable: true, get: fu
|
|
|
25
25
|
Object.defineProperty(exports, "receiptSubSumCents", { enumerable: true, get: function () { return index_js_2.receiptSubSumCents; } });
|
|
26
26
|
Object.defineProperty(exports, "receiptSumCents", { enumerable: true, get: function () { return index_js_2.receiptSumCents; } });
|
|
27
27
|
Object.defineProperty(exports, "CANCELLATION_REASONS", { enumerable: true, get: function () { return index_js_2.CANCELLATION_REASONS; } });
|
|
28
|
+
Object.defineProperty(exports, "CANCELLATION_ERROR_CODES", { enumerable: true, get: function () { return index_js_2.CANCELLATION_ERROR_CODES; } });
|
|
28
29
|
Object.defineProperty(exports, "CANCELLATION_RESERVATION_MS", { enumerable: true, get: function () { return index_js_2.CANCELLATION_RESERVATION_MS; } });
|
|
29
30
|
Object.defineProperty(exports, "isCancellationReason", { enumerable: true, get: function () { return index_js_2.isCancellationReason; } });
|
|
31
|
+
Object.defineProperty(exports, "isCancellationErrorCode", { enumerable: true, get: function () { return index_js_2.isCancellationErrorCode; } });
|
|
30
32
|
Object.defineProperty(exports, "remainingQuantities", { enumerable: true, get: function () { return index_js_2.remainingQuantities; } });
|
|
31
33
|
Object.defineProperty(exports, "fromReceiptCompanyPayload", { enumerable: true, get: function () { return index_js_2.fromReceiptCompanyPayload; } });
|
|
32
34
|
Object.defineProperty(exports, "receiptCompanyTaxInfo", { enumerable: true, get: function () { return index_js_2.receiptCompanyTaxInfo; } });
|
|
@@ -18,10 +18,29 @@ export declare const CANCELLATION_REASONS: Readonly<{
|
|
|
18
18
|
}>;
|
|
19
19
|
export type CancellationReason = keyof typeof CANCELLATION_REASONS;
|
|
20
20
|
export declare function isCancellationReason(value: unknown): value is CancellationReason;
|
|
21
|
+
/**
|
|
22
|
+
* Stabile Fehlercodes von `cancelReceipt` — Zwilling von
|
|
23
|
+
* `functions/storno-core.js` STORNO_FEHLERCODES. Das Backend legt sie bei
|
|
24
|
+
* jedem fachlichen Fehler als `code` neben die Meldung; das Paket reicht sie
|
|
25
|
+
* als [KasseneckApiError.code] durch. **Entscheide am Code, nie am Text** —
|
|
26
|
+
* die deutsche Meldung darf sich aendern, der Code nicht.
|
|
27
|
+
*
|
|
28
|
+
* Nur Auth-/Parameterfehler (Sitzung abgelaufen, Pflichtfeld fehlt) kommen
|
|
29
|
+
* ohne Code; dort bleibt `code` undefined.
|
|
30
|
+
*/
|
|
31
|
+
export declare const CANCELLATION_ERROR_CODES: readonly ["beleg_nicht_gefunden", "belegart_nicht_stornierbar", "trainingsbeleg", "bereits_storniert", "position_ungueltig", "menge_ueber_rest", "grund_unbekannt", "anmerkung_zu_lang", "items_ungueltig", "kasse_nicht_zugewiesen", "keine_berechtigung", "nur_eigene_belege", "kasse_unvollstaendig", "storno_fehlgeschlagen"];
|
|
32
|
+
export type CancellationErrorCode = (typeof CANCELLATION_ERROR_CODES)[number];
|
|
33
|
+
export declare function isCancellationErrorCode(value: unknown): value is CancellationErrorCode;
|
|
21
34
|
/** Bezug eines Storno-Belegs auf sein Original. */
|
|
22
35
|
export interface CancellationOf {
|
|
23
36
|
receiptId: string;
|
|
24
37
|
fullReceiptId: string | null;
|
|
38
|
+
/**
|
|
39
|
+
* Zeitstempel des Originals (Server-Format, Wiener Wanduhr) — das Layout
|
|
40
|
+
* nennt ihn im Kopfblock des Storno-Bons („vom 11.08.2026, 09:02 Uhr").
|
|
41
|
+
* Fehlt bei Altbelegen; dann bleibt die Zeile weg.
|
|
42
|
+
*/
|
|
43
|
+
timeStamp?: string;
|
|
25
44
|
}
|
|
26
45
|
/** Eine stornierte Position: Index im Original und Menge. */
|
|
27
46
|
export interface CancellationItem {
|
|
@@ -40,6 +59,15 @@ export interface Cancellation {
|
|
|
40
59
|
by: string | null;
|
|
41
60
|
note: string | null;
|
|
42
61
|
items: CancellationItem[];
|
|
62
|
+
/**
|
|
63
|
+
* Rabattgutschein-Ausgleich, den DIESER Storno gewaehrt hat — Cent je
|
|
64
|
+
* Steuertopf des Backends (`amountRateStandard`, `amountRateReduced1`, …).
|
|
65
|
+
* Ein Rabattgutschein ist am Original schon in den Umsatz eingerechnet;
|
|
66
|
+
* jeder Storno nimmt ihn anteilig der stornierten Menge zurueck (bei 3 Stueck
|
|
67
|
+
* a 10 EUR mit 6 EUR Rabatt: 2,00 je Stueck). Fehlt das Feld, hat der Eintrag
|
|
68
|
+
* nichts gewaehrt (Altbestand vor dieser Regel) — der naechste Storno holt nach.
|
|
69
|
+
*/
|
|
70
|
+
promoAdjustmentCents?: Record<string, number>;
|
|
43
71
|
}
|
|
44
72
|
/** Ab wann eine liegengebliebene Reservierung nicht mehr zaehlt (wie im Backend). */
|
|
45
73
|
export declare const CANCELLATION_RESERVATION_MS = 120000;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CANCELLATION_RESERVATION_MS = exports.CANCELLATION_REASONS = void 0;
|
|
3
|
+
exports.CANCELLATION_RESERVATION_MS = exports.CANCELLATION_ERROR_CODES = exports.CANCELLATION_REASONS = void 0;
|
|
4
4
|
exports.isCancellationReason = isCancellationReason;
|
|
5
|
+
exports.isCancellationErrorCode = isCancellationErrorCode;
|
|
5
6
|
exports.remainingQuantities = remainingQuantities;
|
|
6
7
|
/**
|
|
7
8
|
* Storno-Metadaten — Zwilling von `functions/storno-core.js`.
|
|
@@ -23,6 +24,35 @@ exports.CANCELLATION_REASONS = Object.freeze({
|
|
|
23
24
|
function isCancellationReason(value) {
|
|
24
25
|
return typeof value === 'string' && Object.prototype.hasOwnProperty.call(exports.CANCELLATION_REASONS, value);
|
|
25
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* Stabile Fehlercodes von `cancelReceipt` — Zwilling von
|
|
29
|
+
* `functions/storno-core.js` STORNO_FEHLERCODES. Das Backend legt sie bei
|
|
30
|
+
* jedem fachlichen Fehler als `code` neben die Meldung; das Paket reicht sie
|
|
31
|
+
* als [KasseneckApiError.code] durch. **Entscheide am Code, nie am Text** —
|
|
32
|
+
* die deutsche Meldung darf sich aendern, der Code nicht.
|
|
33
|
+
*
|
|
34
|
+
* Nur Auth-/Parameterfehler (Sitzung abgelaufen, Pflichtfeld fehlt) kommen
|
|
35
|
+
* ohne Code; dort bleibt `code` undefined.
|
|
36
|
+
*/
|
|
37
|
+
exports.CANCELLATION_ERROR_CODES = [
|
|
38
|
+
'beleg_nicht_gefunden', // Original fehlt oder gehoert nicht zu dieser Kasse
|
|
39
|
+
'belegart_nicht_stornierbar', // Original ist selbst Storno-, Null- oder Startbeleg
|
|
40
|
+
'trainingsbeleg', // Trainingsbelege werden nicht storniert
|
|
41
|
+
'bereits_storniert', // keine Restmenge mehr (auch beim positionslosen Beleg)
|
|
42
|
+
'position_ungueltig', // Index unbekannt oder doppelt
|
|
43
|
+
'menge_ueber_rest', // Menge nicht ganzzahlig >= 1 oder groesser als der Rest
|
|
44
|
+
'grund_unbekannt', // reason fehlt oder nicht im Katalog
|
|
45
|
+
'anmerkung_zu_lang', // note laenger als 200 Zeichen
|
|
46
|
+
'items_ungueltig', // items ist keine Liste
|
|
47
|
+
'kasse_nicht_zugewiesen', // Register-Benutzer darf diese Kasse nicht
|
|
48
|
+
'keine_berechtigung', // Register-Benutzer ohne Storno-Recht
|
|
49
|
+
'nur_eigene_belege', // Recht "eigene", fremder Beleg
|
|
50
|
+
'kasse_unvollstaendig', // api_key/token fehlen am Konto bzw. an der Kasse
|
|
51
|
+
'storno_fehlgeschlagen', // der Storno-Beleg selbst wurde abgelehnt (z. B. Signatur)
|
|
52
|
+
];
|
|
53
|
+
function isCancellationErrorCode(value) {
|
|
54
|
+
return typeof value === 'string' && exports.CANCELLATION_ERROR_CODES.includes(value);
|
|
55
|
+
}
|
|
26
56
|
/** Ab wann eine liegengebliebene Reservierung nicht mehr zaehlt (wie im Backend). */
|
|
27
57
|
exports.CANCELLATION_RESERVATION_MS = 120_000;
|
|
28
58
|
/**
|
|
@@ -44,8 +44,15 @@ function fromHobexReceiptPayload(payload) {
|
|
|
44
44
|
responseCode: payload.responseCode,
|
|
45
45
|
transactionType: payload.transactionType,
|
|
46
46
|
currency: payload.currency,
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
// Euro->Cent ueber die eine gehaertete Stelle im Paket ([euroToCents],
|
|
48
|
+
// money.ts) statt einer eigenen `Math.round(x * 100)` -- die Hobex-Antwort
|
|
49
|
+
// ist eine fremde Antwort und wird hier nur auf transactionId/
|
|
50
|
+
// transactionDate geprueft (siehe payments/hobex.ts, belegAusNutzlast);
|
|
51
|
+
// amount/tip bleiben ungeprueft. Ein nicht-numerischer oder
|
|
52
|
+
// nicht-endlicher Wert ergab bis zu dieser Fassung `NaN` -- lautlos, ohne
|
|
53
|
+
// dass irgendwo geworfen wurde. [euroToCents] liefert dafuer `0`.
|
|
54
|
+
amountCents: (0, money_js_1.euroToCents)(payload.amount),
|
|
55
|
+
tipCents: (0, money_js_1.euroToCents)(payload.tip),
|
|
49
56
|
cvm: String(payload.cvm),
|
|
50
57
|
// Die Cloud-API-Nutzlast traegt keinen eigenen Provider-Schluessel — der
|
|
51
58
|
// Provider ergibt sich aus dem Transportweg (Cloud vs. HPS), nicht aus
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { type ReceiptItem, type ReceiptItemPayload, type ReceiptItemPayloadRead, toReceiptItemPayload, fromReceiptItemPayload, receiptItemTotalCents, receiptItemIsValid, negateReceiptItem, type TipRecipient, isTipItem, isDiscountItem, } from './receipt-item.js';
|
|
2
2
|
export { type Voucher, type VoucherPayload, toVoucherPayload, fromVoucherPayload, voucherIsValid } from './voucher.js';
|
|
3
3
|
export { type Receipt, type ReceiptPayload, type ReceiptPayloadRead, toReceiptPayload, fromReceiptPayload, receiptSubSumCents, receiptSumCents, } from './receipt.js';
|
|
4
|
-
export { type CancellationOf, type CancellationItem, type Cancellation, type CancellationReason, CANCELLATION_REASONS, CANCELLATION_RESERVATION_MS, isCancellationReason, remainingQuantities, } from './cancellation.js';
|
|
4
|
+
export { type CancellationOf, type CancellationItem, type Cancellation, type CancellationReason, type CancellationErrorCode, CANCELLATION_REASONS, CANCELLATION_ERROR_CODES, CANCELLATION_RESERVATION_MS, isCancellationReason, isCancellationErrorCode, remainingQuantities, } from './cancellation.js';
|
|
5
5
|
export { type ReceiptCompany, type ReceiptCompanyPayload, fromReceiptCompanyPayload, receiptCompanyTaxInfo, } from './receipt-company.js';
|
|
6
6
|
export { type Cashregister, type CashregisterOnboarding, type CashregisterPayload, type CashregisterOnboardingPayload, fromCashregisterPayload, } from './cashregister.js';
|
|
7
7
|
export { type ReceiptSummary, type ReceiptSummaryPayload, fromReceiptSummaryPayload, type ZeroKind, ZERO_KINDS, istZeroKind } from './receipt-summary.js';
|
package/dist/cjs/models/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.hobexReceiptNeedsSignature = exports.hobexReceiptToCardPaymentData = exports.fromHobexReceiptPayload = exports.toHobexReceiptPayload = exports.fromStripeUrlSessionPayload = exports.toStripeUrlSessionPayload = exports.reportMonthReadable = exports.reportMonthKey = exports.nextReportMonth = exports.previousReportMonth = exports.reportMonthFromDate = exports.istZeroKind = exports.ZERO_KINDS = exports.fromReceiptSummaryPayload = exports.fromCashregisterPayload = exports.receiptCompanyTaxInfo = exports.fromReceiptCompanyPayload = exports.remainingQuantities = exports.isCancellationReason = exports.CANCELLATION_RESERVATION_MS = exports.CANCELLATION_REASONS = exports.receiptSumCents = exports.receiptSubSumCents = exports.fromReceiptPayload = exports.toReceiptPayload = exports.voucherIsValid = exports.fromVoucherPayload = exports.toVoucherPayload = exports.isDiscountItem = exports.isTipItem = exports.negateReceiptItem = exports.receiptItemIsValid = exports.receiptItemTotalCents = exports.fromReceiptItemPayload = exports.toReceiptItemPayload = void 0;
|
|
3
|
+
exports.hobexReceiptNeedsSignature = exports.hobexReceiptToCardPaymentData = exports.fromHobexReceiptPayload = exports.toHobexReceiptPayload = exports.fromStripeUrlSessionPayload = exports.toStripeUrlSessionPayload = exports.reportMonthReadable = exports.reportMonthKey = exports.nextReportMonth = exports.previousReportMonth = exports.reportMonthFromDate = exports.istZeroKind = exports.ZERO_KINDS = exports.fromReceiptSummaryPayload = exports.fromCashregisterPayload = exports.receiptCompanyTaxInfo = exports.fromReceiptCompanyPayload = exports.remainingQuantities = exports.isCancellationErrorCode = exports.isCancellationReason = exports.CANCELLATION_RESERVATION_MS = exports.CANCELLATION_ERROR_CODES = exports.CANCELLATION_REASONS = exports.receiptSumCents = exports.receiptSubSumCents = exports.fromReceiptPayload = exports.toReceiptPayload = exports.voucherIsValid = exports.fromVoucherPayload = exports.toVoucherPayload = exports.isDiscountItem = exports.isTipItem = exports.negateReceiptItem = exports.receiptItemIsValid = exports.receiptItemTotalCents = exports.fromReceiptItemPayload = exports.toReceiptItemPayload = void 0;
|
|
4
4
|
var receipt_item_js_1 = require("./receipt-item.js");
|
|
5
5
|
Object.defineProperty(exports, "toReceiptItemPayload", { enumerable: true, get: function () { return receipt_item_js_1.toReceiptItemPayload; } });
|
|
6
6
|
Object.defineProperty(exports, "fromReceiptItemPayload", { enumerable: true, get: function () { return receipt_item_js_1.fromReceiptItemPayload; } });
|
|
@@ -20,8 +20,10 @@ Object.defineProperty(exports, "receiptSubSumCents", { enumerable: true, get: fu
|
|
|
20
20
|
Object.defineProperty(exports, "receiptSumCents", { enumerable: true, get: function () { return receipt_js_1.receiptSumCents; } });
|
|
21
21
|
var cancellation_js_1 = require("./cancellation.js");
|
|
22
22
|
Object.defineProperty(exports, "CANCELLATION_REASONS", { enumerable: true, get: function () { return cancellation_js_1.CANCELLATION_REASONS; } });
|
|
23
|
+
Object.defineProperty(exports, "CANCELLATION_ERROR_CODES", { enumerable: true, get: function () { return cancellation_js_1.CANCELLATION_ERROR_CODES; } });
|
|
23
24
|
Object.defineProperty(exports, "CANCELLATION_RESERVATION_MS", { enumerable: true, get: function () { return cancellation_js_1.CANCELLATION_RESERVATION_MS; } });
|
|
24
25
|
Object.defineProperty(exports, "isCancellationReason", { enumerable: true, get: function () { return cancellation_js_1.isCancellationReason; } });
|
|
26
|
+
Object.defineProperty(exports, "isCancellationErrorCode", { enumerable: true, get: function () { return cancellation_js_1.isCancellationErrorCode; } });
|
|
25
27
|
Object.defineProperty(exports, "remainingQuantities", { enumerable: true, get: function () { return cancellation_js_1.remainingQuantities; } });
|
|
26
28
|
var receipt_company_js_1 = require("./receipt-company.js");
|
|
27
29
|
Object.defineProperty(exports, "fromReceiptCompanyPayload", { enumerable: true, get: function () { return receipt_company_js_1.fromReceiptCompanyPayload; } });
|
|
@@ -56,6 +56,7 @@ export interface ReceiptSummary {
|
|
|
56
56
|
/** Nur am Storno-Beleg: das Original. */
|
|
57
57
|
cancellationOf?: {
|
|
58
58
|
receiptId: string | null;
|
|
59
|
+
timeStamp?: string;
|
|
59
60
|
};
|
|
60
61
|
cancellationReason?: string;
|
|
61
62
|
/** offen | teil | voll -- Storno-Stand des Originals. */
|
|
@@ -84,6 +85,7 @@ export interface ReceiptSummaryPayload {
|
|
|
84
85
|
} | null;
|
|
85
86
|
cancellationOf?: {
|
|
86
87
|
receiptId?: string | null;
|
|
88
|
+
timeStamp?: string | null;
|
|
87
89
|
} | null;
|
|
88
90
|
cancellationReason?: string | null;
|
|
89
91
|
stornoStand?: string | null;
|
|
@@ -46,7 +46,10 @@ function fromReceiptSummaryPayload(payload) {
|
|
|
46
46
|
signatureOk: payload.signature_ok !== false,
|
|
47
47
|
items: Array.isArray(payload.items) ? payload.items.map((i) => ({ name: i?.name ?? '', quantity: typeof i?.quantity === 'number' ? i.quantity : 0 })) : [],
|
|
48
48
|
...(payload.operator ? { operator: { uid: payload.operator.uid ?? null, name: payload.operator.name ?? '' } } : {}),
|
|
49
|
-
...(payload.cancellationOf ? { cancellationOf: {
|
|
49
|
+
...(payload.cancellationOf ? { cancellationOf: {
|
|
50
|
+
receiptId: payload.cancellationOf.receiptId ?? null,
|
|
51
|
+
...(typeof payload.cancellationOf.timeStamp === 'string' && payload.cancellationOf.timeStamp ? { timeStamp: payload.cancellationOf.timeStamp } : {}),
|
|
52
|
+
} } : {}),
|
|
50
53
|
...(payload.cancellationReason ? { cancellationReason: payload.cancellationReason } : {}),
|
|
51
54
|
stornoStand: payload.stornoStand === 'teil' || payload.stornoStand === 'voll' ? payload.stornoStand : 'offen',
|
|
52
55
|
...(istZeroKind(payload.zeroKind) ? { zeroKind: payload.zeroKind } : {}),
|
|
@@ -76,7 +76,11 @@ function fromReceiptPayload(payload) {
|
|
|
76
76
|
signatureSuccess: payload.signatureSuccess ?? undefined,
|
|
77
77
|
customProjectId: payload.customProjectId ?? undefined,
|
|
78
78
|
...(typeof payload.tipCents === 'number' ? { tipCents: payload.tipCents } : {}),
|
|
79
|
-
...(payload.cancellationOf ? { cancellationOf: {
|
|
79
|
+
...(payload.cancellationOf ? { cancellationOf: {
|
|
80
|
+
receiptId: payload.cancellationOf.receiptId,
|
|
81
|
+
fullReceiptId: payload.cancellationOf.fullReceiptId ?? null,
|
|
82
|
+
...(typeof payload.cancellationOf.timeStamp === 'string' && payload.cancellationOf.timeStamp ? { timeStamp: payload.cancellationOf.timeStamp } : {}),
|
|
83
|
+
} } : {}),
|
|
80
84
|
...(payload.cancellationReason ? { cancellationReason: payload.cancellationReason } : {}),
|
|
81
85
|
...((0, receipt_summary_js_1.istZeroKind)(payload.zeroKind) ? { zeroKind: payload.zeroKind } : {}),
|
|
82
86
|
...(payload.cancellations ? { cancellations: payload.cancellations.map(leseStorno) } : {}),
|
|
@@ -90,6 +94,9 @@ function leseStorno(eintrag) {
|
|
|
90
94
|
by: eintrag.by ?? null,
|
|
91
95
|
note: eintrag.note ?? null,
|
|
92
96
|
items: (eintrag.items ?? []).map((p) => ({ index: Number(p.index), quantity: Number(p.quantity) })),
|
|
97
|
+
...(eintrag.promoAdjustmentCents && typeof eintrag.promoAdjustmentCents === 'object'
|
|
98
|
+
? { promoAdjustmentCents: Object.fromEntries(Object.entries(eintrag.promoAdjustmentCents).map(([k, v]) => [k, Number(v)])) }
|
|
99
|
+
: {}),
|
|
93
100
|
};
|
|
94
101
|
}
|
|
95
102
|
/**
|
|
@@ -4,6 +4,7 @@ exports.toVoucherPayload = toVoucherPayload;
|
|
|
4
4
|
exports.voucherIsValid = voucherIsValid;
|
|
5
5
|
exports.fromVoucherPayload = fromVoucherPayload;
|
|
6
6
|
const index_js_1 = require("../enums/index.js");
|
|
7
|
+
const money_js_1 = require("../money.js");
|
|
7
8
|
const enum_payload_js_1 = require("./enum-payload.js");
|
|
8
9
|
function toVoucherPayload(voucher) {
|
|
9
10
|
return {
|
|
@@ -35,16 +36,31 @@ function voucherIsValid(voucher) {
|
|
|
35
36
|
if (voucher.type === index_js_1.VoucherType.promo && voucher.valueCents == null) {
|
|
36
37
|
return false;
|
|
37
38
|
}
|
|
38
|
-
|
|
39
|
+
// `!Number.isFinite` VOR den Vergleich, nicht dahinter: `NaN <= 0` und
|
|
40
|
+
// `Infinity <= 0` sind beide `false`, ein Vergleich mit einem
|
|
41
|
+
// nicht-endlichen Wert ist also IMMER falsch und liesse ihn hier
|
|
42
|
+
// durchrutschen. Gemessen (28.08.2026): ein Gutschein mit `valueCents: NaN`
|
|
43
|
+
// (aus einem kaputten `value` in der Nutzlast, siehe [fromVoucherPayload])
|
|
44
|
+
// oder `valueCents: Infinity` galt bis zu dieser Fassung als GUELTIG und
|
|
45
|
+
// waere still in die Belegsummen gewandert. Diese Pruefung greift
|
|
46
|
+
// unabhaengig davon, ob `valueCents` ueber [fromVoucherPayload] oder direkt
|
|
47
|
+
// gesetzt wurde -- die gehaertete Umrechnung dort allein reicht nicht.
|
|
48
|
+
if (voucher.valueCents != null && (!Number.isFinite(voucher.valueCents) || voucher.valueCents <= 0)) {
|
|
39
49
|
return false;
|
|
40
50
|
}
|
|
41
51
|
return true;
|
|
42
52
|
}
|
|
43
53
|
function fromVoucherPayload(payload) {
|
|
54
|
+
// Euro->Cent ueber die eine gehaertete Stelle im Paket ([euroToCents],
|
|
55
|
+
// money.ts) statt einer eigenen `Math.round(payload.value * 100)` --
|
|
56
|
+
// [euroToCents] prueft `Number.isFinite` und liefert fuer einen
|
|
57
|
+
// nicht-numerischen oder nicht-endlichen Wert `0` statt eines lautlosen
|
|
58
|
+
// `NaN`. Siehe auch [voucherIsValid]: `0` faellt dort ueber die
|
|
59
|
+
// "<= 0"-Pruefung, ein `NaN` waere durchgerutscht.
|
|
44
60
|
const valueCents = payload.valueCents != null
|
|
45
61
|
? payload.valueCents
|
|
46
62
|
: payload.value != null
|
|
47
|
-
?
|
|
63
|
+
? (0, money_js_1.euroToCents)(payload.value)
|
|
48
64
|
: undefined;
|
|
49
65
|
return {
|
|
50
66
|
name: payload.name ?? undefined,
|