@kreiseck/kasseneck-api 0.7.0 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +87 -15
  3. package/dist/cjs/client/aufrufe.d.ts +1 -1
  4. package/dist/cjs/client/aufrufe.js +1 -0
  5. package/dist/cjs/client/errors.d.ts +8 -4
  6. package/dist/cjs/client/errors.js +12 -9
  7. package/dist/cjs/client/receipts.d.ts +22 -0
  8. package/dist/cjs/client/receipts.js +16 -0
  9. package/dist/cjs/client/transport.js +5 -4
  10. package/dist/cjs/index.d.ts +1 -1
  11. package/dist/cjs/index.js +4 -2
  12. package/dist/cjs/models/cancellation.d.ts +28 -0
  13. package/dist/cjs/models/cancellation.js +31 -1
  14. package/dist/cjs/models/hobex-receipt.js +9 -2
  15. package/dist/cjs/models/index.d.ts +1 -1
  16. package/dist/cjs/models/index.js +3 -1
  17. package/dist/cjs/models/receipt-summary.d.ts +2 -0
  18. package/dist/cjs/models/receipt-summary.js +4 -1
  19. package/dist/cjs/models/receipt.js +8 -1
  20. package/dist/cjs/models/voucher.js +18 -2
  21. package/dist/cjs/payments/hobex-hps/connect-client.d.ts +104 -0
  22. package/dist/cjs/payments/hobex-hps/connect-client.js +159 -0
  23. package/dist/cjs/payments/hobex-hps/errors.d.ts +122 -0
  24. package/dist/cjs/payments/hobex-hps/errors.js +158 -0
  25. package/dist/cjs/payments/hobex-hps/events.d.ts +22 -0
  26. package/dist/cjs/payments/hobex-hps/events.js +2 -0
  27. package/dist/cjs/payments/hobex-hps/index.d.ts +27 -0
  28. package/dist/cjs/payments/hobex-hps/index.js +67 -0
  29. package/dist/cjs/payments/hobex-hps/outcome.d.ts +46 -0
  30. package/dist/cjs/payments/hobex-hps/outcome.js +7 -0
  31. package/dist/cjs/payments/hobex-hps/payments.d.ts +149 -0
  32. package/dist/cjs/payments/hobex-hps/payments.js +573 -0
  33. package/dist/cjs/payments/hobex-hps/receipt.d.ts +28 -0
  34. package/dist/cjs/payments/hobex-hps/receipt.js +60 -0
  35. package/dist/cjs/payments/hobex-hps/transaction-id.d.ts +65 -0
  36. package/dist/cjs/payments/hobex-hps/transaction-id.js +91 -0
  37. package/dist/cjs/payments/hobex-hps/transaction-response.d.ts +175 -0
  38. package/dist/cjs/payments/hobex-hps/transaction-response.js +266 -0
  39. package/dist/cjs/payments/hobex.js +9 -6
  40. package/dist/cjs/payments/index.d.ts +26 -14
  41. package/dist/cjs/payments/index.js +59 -15
  42. package/dist/cjs/printing/index.d.ts +1 -1
  43. package/dist/cjs/printing/index.js +2 -1
  44. package/dist/cjs/printing/webusb.d.ts +37 -7
  45. package/dist/cjs/printing/webusb.js +76 -18
  46. package/dist/cjs/receipt/epos.d.ts +18 -0
  47. package/dist/cjs/receipt/epos.js +78 -6
  48. package/dist/cjs/receipt/index.d.ts +1 -1
  49. package/dist/cjs/receipt/index.js +2 -1
  50. package/dist/cjs/receipt/layout.js +19 -0
  51. package/dist/cjs/register/index.d.ts +1 -1
  52. package/dist/cjs/register/index.js +2 -1
  53. package/dist/cjs/register/pairing.d.ts +59 -0
  54. package/dist/cjs/register/pairing.js +51 -2
  55. package/dist/esm/client/aufrufe.d.ts +1 -1
  56. package/dist/esm/client/aufrufe.js +1 -0
  57. package/dist/esm/client/errors.d.ts +8 -4
  58. package/dist/esm/client/errors.js +12 -9
  59. package/dist/esm/client/receipts.d.ts +22 -0
  60. package/dist/esm/client/receipts.js +16 -0
  61. package/dist/esm/client/transport.js +5 -4
  62. package/dist/esm/index.d.ts +1 -1
  63. package/dist/esm/index.js +1 -1
  64. package/dist/esm/models/cancellation.d.ts +28 -0
  65. package/dist/esm/models/cancellation.js +29 -0
  66. package/dist/esm/models/hobex-receipt.js +10 -3
  67. package/dist/esm/models/index.d.ts +1 -1
  68. package/dist/esm/models/index.js +1 -1
  69. package/dist/esm/models/receipt-summary.d.ts +2 -0
  70. package/dist/esm/models/receipt-summary.js +4 -1
  71. package/dist/esm/models/receipt.js +8 -1
  72. package/dist/esm/models/voucher.js +18 -2
  73. package/dist/esm/payments/hobex-hps/connect-client.d.ts +104 -0
  74. package/dist/esm/payments/hobex-hps/connect-client.js +156 -0
  75. package/dist/esm/payments/hobex-hps/errors.d.ts +122 -0
  76. package/dist/esm/payments/hobex-hps/errors.js +149 -0
  77. package/dist/esm/payments/hobex-hps/events.d.ts +22 -0
  78. package/dist/esm/payments/hobex-hps/events.js +1 -0
  79. package/dist/esm/payments/hobex-hps/index.d.ts +27 -0
  80. package/dist/esm/payments/hobex-hps/index.js +26 -0
  81. package/dist/esm/payments/hobex-hps/outcome.d.ts +46 -0
  82. package/dist/esm/payments/hobex-hps/outcome.js +4 -0
  83. package/dist/esm/payments/hobex-hps/payments.d.ts +149 -0
  84. package/dist/esm/payments/hobex-hps/payments.js +570 -0
  85. package/dist/esm/payments/hobex-hps/receipt.d.ts +28 -0
  86. package/dist/esm/payments/hobex-hps/receipt.js +57 -0
  87. package/dist/esm/payments/hobex-hps/transaction-id.d.ts +65 -0
  88. package/dist/esm/payments/hobex-hps/transaction-id.js +86 -0
  89. package/dist/esm/payments/hobex-hps/transaction-response.d.ts +175 -0
  90. package/dist/esm/payments/hobex-hps/transaction-response.js +254 -0
  91. package/dist/esm/payments/hobex.js +9 -6
  92. package/dist/esm/payments/index.d.ts +26 -14
  93. package/dist/esm/payments/index.js +26 -14
  94. package/dist/esm/printing/index.d.ts +1 -1
  95. package/dist/esm/printing/index.js +1 -1
  96. package/dist/esm/printing/webusb.d.ts +37 -7
  97. package/dist/esm/printing/webusb.js +74 -17
  98. package/dist/esm/receipt/epos.d.ts +18 -0
  99. package/dist/esm/receipt/epos.js +76 -6
  100. package/dist/esm/receipt/index.d.ts +1 -1
  101. package/dist/esm/receipt/index.js +1 -1
  102. package/dist/esm/receipt/layout.js +19 -0
  103. package/dist/esm/register/index.d.ts +1 -1
  104. package/dist/esm/register/index.js +1 -1
  105. package/dist/esm/register/pairing.d.ts +59 -0
  106. package/dist/esm/register/pairing.js +50 -2
  107. package/fixtures/belege/storno-rabatt.json +3 -2
  108. package/fixtures/belege/storno-teil.json +2 -1
  109. package/fixtures/belege/storno-voll.json +2 -1
  110. package/fixtures/erwartet/storno-rabatt.grid32.txt +1 -0
  111. package/fixtures/erwartet/storno-rabatt.grid48.txt +1 -0
  112. package/fixtures/erwartet/storno-rabatt.lines.json +6 -0
  113. package/fixtures/erwartet/storno-teil.grid32.txt +1 -0
  114. package/fixtures/erwartet/storno-teil.grid48.txt +1 -0
  115. package/fixtures/erwartet/storno-teil.lines.json +6 -0
  116. package/fixtures/erwartet/storno-voll.grid32.txt +1 -0
  117. package/fixtures/erwartet/storno-voll.grid48.txt +1 -0
  118. package/fixtures/erwartet/storno-voll.lines.json +6 -0
  119. package/fixtures/hobex-hps-codes.json +83 -0
  120. package/fixtures/manifest.json +12 -12
  121. package/fixtures/oberflaeche.json +2 -1
  122. package/package.json +3 -2
package/CHANGELOG.md CHANGED
@@ -4,6 +4,38 @@ Was vor 0.7.0 geschah, steht in der Commit-Historie (`git log`); ab hier wird
4
4
  es hier geführt. Ein Eintrag nennt die Änderung **und ihren Grund** —
5
5
  nur der Grund überlebt den nächsten Umbau.
6
6
 
7
+ ## 0.7.2
8
+
9
+ ### `55` („PIN falsch") ist eine gemessene Host-Ablehnung → `declined`
10
+
11
+ **Anlass:** Vorfall vom 02.09.2026 am Produktivterminal 3556988 (HPS 1.11.4,
12
+ Firmware 2.3.9), vom Dart-Zwilling `kasseneck_api` 6.4.0 übernommen — die
13
+ erste echte Host-Ablehnung, die je beobachtet wurde. Die Zahlung antwortete
14
+ direkt mit `55`, die Statusabfrage danach elfmal in Folge ebenfalls. Als
15
+ Wissenslücke kostete der Code 90 s Klärung ins Budget, `unresolved`, einen
16
+ stehenden Merker und eine Rückfrage an den Bediener — für eine falsch
17
+ getippte PIN.
18
+
19
+ - Neu: `WRONG_PIN_CODE`, in `HPS_MEASURED_CODES` als schlüssig geführt.
20
+ - Bewusst **keine** Familienregel für zweistellige Host-Codes: ISO 8583 führt
21
+ dort auch Genehmigungen (`08`, `10`, `11`, `85`). Die Zwei-`9027`-Regel
22
+ greift bei Host-Codes nicht (der Status antwortet mit dem Code selbst);
23
+ ungemessene Host-Codes enden weiterhin bei `unresolved`.
24
+ - `fixtures/hobex-hps-codes.json` neu erzeugt; die Vertragsdatei nennt jetzt
25
+ unter `ergaenztAn`, welcher Code von welchem Gerät stammt.
26
+
27
+ ### Neu: `HpsPaymentResult.lastResponse` und Terminal-Klartext im Nachweis
28
+
29
+ Am 02.09.2026 sah der Bediener bei einer Antwort `55` „PIN falsch" nur
30
+ „Ausgang unklar", musste raten und buchte die abgelehnte Zahlung als bezahlt;
31
+ 75 EUR Umsatz waren weg. Der Klartext hätte die Entscheidung getragen.
32
+
33
+ - `lastResponse`: die letzte Terminal-Antwort bei `unresolved`, auch wenn sie
34
+ nichts entschied — Material für Anzeige und Katalog, nie ein Beleg.
35
+ `response` bleibt bei `unresolved` weiterhin ungesetzt.
36
+ - Der Nachweis nennt bei einem unbekannten Code den Klartext des Terminals:
37
+ `Terminal nennt einen unbekannten Code (55) "PIN falsch"`.
38
+
7
39
  ## 0.7.0
8
40
 
9
41
  ### Neu: Unterpfad `./partner` — die Partner-API
package/README.md CHANGED
@@ -162,12 +162,55 @@ ergebnis.cancellationOf; // Bezug auf das Original
162
162
  ergebnis.remaining; // Restmengen des Originals danach
163
163
  ```
164
164
 
165
+ **Veraltet:** `createCancelReceipt` (Storno über `createReceipt` mit frei
166
+ übergebenen, negierten Positionen) bleibt aus Kompatibilität erreichbar, ist aber
167
+ `@deprecated` — kein Bezug zum Original, keine Restmengen, kein Schutz vor
168
+ doppeltem Storno, keine Gutscheine. Das Backend legt bei diesem Weg
169
+ `deprecation` in die Antwort.
170
+
165
171
  Der Server negiert die Positionen, prüft Restmengen und Rechte („nur eigene
166
172
  Belege" oder „alle") und verkettet Original und Storno. Ein Storno-Beleg lässt
167
173
  sich nicht stornieren, ein voll stornierter Beleg nicht noch einmal. Am
168
174
  gelesenen Original liefert `remainingQuantities(receipt)` die Reste vorab (für
169
175
  den Storno-Dialog); die Wahrheit hat der Server.
170
176
 
177
+ **Fehler entscheidet man am Code, nicht am Text.** Jeder fachliche Fehler von
178
+ `cancelReceipt` trägt `KasseneckApiError.code` aus `CANCELLATION_ERROR_CODES`
179
+ (z. B. `bereits_storniert`, `menge_ueber_rest`, `nur_eigene_belege`). Die
180
+ deutsche Meldung (`serverMessage`) ist Anzeige und darf sich ändern.
181
+
182
+ ```ts
183
+ import { isKasseneckApiError, isCancellationErrorCode } from '@kreiseck/kasseneck-api';
184
+
185
+ try {
186
+ await api.cancelReceipt({ receipt: beleg, reason: 'fehleingabe' });
187
+ } catch (fehler) {
188
+ if (isKasseneckApiError(fehler) && isCancellationErrorCode(fehler.code)) {
189
+ switch (fehler.code) {
190
+ case 'bereits_storniert': // Beleg im Dialog als „storniert" zeigen, Knopf sperren
191
+ case 'menge_ueber_rest': // Restmengen neu laden (jemand war schneller)
192
+ case 'nur_eigene_belege': // Chef holen
193
+ break;
194
+ }
195
+ }
196
+ throw fehler;
197
+ }
198
+ ```
199
+
200
+ **Gutscheine.** Ein Wertgutschein wird nur beim Vollstorno (ohne `items`)
201
+ gespiegelt — er ist unteilbar. Ein Rabattgutschein ist am Original bereits in
202
+ den Umsatz eingerechnet; **jeder** Storno nimmt ihn anteilig der stornierten
203
+ Menge zurück: bei 3 Stück à 10 € mit 6 € Rabatt sind 8 € je Stück Entgelt, der
204
+ Storno-Beleg trägt dann „−10,00" plus eine Zeile „Gutschein-Ausgleich +2,00".
205
+ Was ein Storno gewährt hat, steht am Eintrag in `receipt.cancellations[]` als
206
+ `promoAdjustmentCents` (Cent je Steuertopf) — die Kasse kann es im Dialog
207
+ zeigen, rechnen muss sie nichts.
208
+
209
+ **Bon.** Der Kopfblock des Storno-Bons nennt Bezug, Datum des Originals und
210
+ Grund: „STORNOBELEG / Stornobuchung zu Beleg KASSE1-ID-42 / vom 11.08.2026,
211
+ 09:02 Uhr / Grund: Fehleingabe". Das Datum kommt aus `cancellationOf.timeStamp`
212
+ (Backend seit 2026-09-04); Altbelege ohne bleiben ohne die Zeile.
213
+
171
214
  ## Partner-API (`./partner`)
172
215
 
173
216
  Für Softwarehäuser, die Kasseneck in ihr eigenes Produkt einbauen: Betriebe
@@ -292,7 +335,7 @@ app.post('/kasseneck-webhook', express.raw({ type: '*/*' }), async (req, res) =>
292
335
  | `@kreiseck/kasseneck-api` | Endpunkte, Anmeldung, Transport, Modelle, Enums, Fehler — alles, was mit dem Backend spricht. |
293
336
  | `…/receipt` | Beleg-Layout als Datenmodell (framework-frei) und die Brücke zu ESC/POS. |
294
337
  | `…/printing` | ESC/POS-Erzeugung: Bytefolgen für Bondrucker, ohne jeden Transport. |
295
- | `…/payments` | Stripe-Zahllinks und Hobex-Cloud (beides HTTP-Endpunkte des Backends). |
338
+ | `…/payments` | Stripe-Zahllinks, Hobex-Cloud (beides HTTP-Endpunkte des Backends) und Hobex **HPS** über **Kasseneck Connect** (lokaler Geräte-Agent, spricht mit dem Terminal). |
296
339
  | `…/register` | Anmeldung der Browser-Kasse: Gerät koppeln und entkoppeln, Benutzer auflisten, per PIN anmelden, Sitzung erneuern und beenden. |
297
340
  | `…/kasse` | Kachel-Kasse: Kassen-Einstellungen (betriebsweit / je Gerät), Artikelgruppen und Artikel für Kacheln, Rabattverteilung je Steuersatz, Reichweiten der Kassen-Rechte |
298
341
  | `…/partner` | Partner-API: Betriebe anlegen, FinanzOnline-Link, Signatur, Kassen, Zugangsdaten, Webhooks samt Signaturprüfung. **Gehört auf einen Server.** |
@@ -301,16 +344,40 @@ app.post('/kasseneck-webhook', express.raw({ type: '*/*' }), async (req, res) =>
301
344
 
302
345
  So zieht sich niemand den React-Adapter in ein Node-Programm.
303
346
 
304
- ## Was hier grundsätzlich nicht dazugehört
347
+ ## Hobex HPS über Kasseneck Connect
348
+
349
+ Ein Browser hat weiterhin keine rohen TCP-Sockets — ein **direkter**
350
+ Terminal-Kontakt wie beim Flutter-Paket `kasseneck_api` (`HpsClient`) bleibt
351
+ deshalb außerhalb der Reichweite dieses Pakets. **Kasseneck Connect** ist aber
352
+ ein lokaler Geräte-Agent mit gewöhnlicher HTTP-Schnittstelle, der für die Kasse
353
+ mit dem Terminal spricht — und darüber geht es:
354
+
355
+ ```ts
356
+ import { createHpsConnectClient, createHpsPayments } from '@kreiseck/kasseneck-api/payments';
357
+
358
+ const client = createHpsConnectClient({ token: kopplungsToken });
359
+ const zahlweg = createHpsPayments(client, { host: '192.168.1.50', tid: '3600335' });
305
360
 
306
- **Hobex HPS, myPOS und SumUp sind nicht Teil dieses Pakets und werden es auch
307
- nicht.** Hobex HPS spricht lokal über TCP mit dem Terminal, myPOS und SumUp
308
- sind Android-SDKs. Ein Browser kann das nicht, und kein Bündler ändert daran
309
- etwas. Wer diese Terminals braucht, nimmt das Flutter-Paket `kasseneck_api`.
361
+ const ergebnis = await zahlweg.pay({ amountCents: 1050 });
362
+ // ergebnis.outcome: 'approved' | 'declined' | 'unresolved' nie geraten.
363
+ // ergebnis.transactionId ist IMMER gesetzt, auch bei 'unresolved'.
364
+ ```
310
365
 
311
- Ebenfalls nicht enthalten: die Druckeransteuerung selbst (dieses Paket erzeugt
312
- die Bytes, es verschickt sie nicht), Firmenlogos und Rasterbilder, und die
313
- PDF-Erzeugung.
366
+ Der Ausgang ist immer einer von drei: `approved`, `declined` (beweisbar nichts
367
+ belastet) oder `unresolved` (Ausgang unbekannt, eine Wiederholung könnte ein
368
+ zweites Mal belasten). Was das bedeutet und warum es so gebaut ist, steht in
369
+ `src/payments/hobex-hps/payments.ts` — dort ist die Dokumentation der Maßstab,
370
+ nicht dieses README.
371
+
372
+ **Nur `pay` — bewusst kein `refund`/`cancel`.** Kasseneck Connect exponiert
373
+ dafür (noch) keinen Endpunkt; eine Gutschrift oder ein Storno am HPS-Terminal
374
+ braucht weiterhin die Flutter-App. **myPOS** und **SumUp** bleiben
375
+ Android-SDKs ohne Entsprechung hier.
376
+
377
+ ## Was hier grundsätzlich nicht dazugehört
378
+
379
+ Die Druckeransteuerung selbst (dieses Paket erzeugt die Bytes, es verschickt
380
+ sie nicht), Firmenlogos und Rasterbilder, und die PDF-Erzeugung.
314
381
 
315
382
  ## Entwicklung
316
383
 
@@ -348,19 +415,24 @@ Wird eine Ausnahme erreichbar, schlägt die Prüfung an: Sonst sänke die Zahl n
348
415
  ## Vertragsdateien für die Zwillinge
349
416
 
350
417
  Dieses Paket ist die Quelle für das Dart-Paket `kasseneck_api` und den
351
- Backend-Validator `kasse-settings-core.js`. Zwei Dateien in `fixtures/` reisen
352
- im Tarball mit und sagen in Maschinenform, worauf sich alle drei geeinigt haben:
418
+ Backend-Validator `kasse-settings-core.js`. Drei Dateien in `fixtures/` reisen
419
+ im Tarball mit und sagen in Maschinenform, worauf sich beide Seiten geeinigt haben:
353
420
 
354
421
  | Datei | Inhalt |
355
422
  |---|---|
356
423
  | `kasse-settings-standard.json` | Feldnamen und Standardwerte der Kassen-Einstellungen |
424
+ | `oberflaeche.json` | Aufrufnamen, Enum-Werte, Rechte-Schlüssel, Tasten-Aktionen |
425
+ | `hobex-hps-codes.json` | Gemessene HPS-Ergebniscodes, ihre Bedeutung und ob sie einen Ausgang festschreiben — der Vertrag hinter `.../payments/hobex-hps`s `isConclusive`. |
426
+
357
427
  | `oberflaeche.json` | Aufrufnamen, Enum-Werte, Rechte-Schlüssel, Tasten-Aktionen, Partner-Listen |
358
428
 
359
- Beide werden erzeugt (`npm run fixtures:kasse`, `npm run fixtures:oberflaeche`)
360
- und nie von Hand geändert; die CI prüft nach jedem Lauf, dass sie zum Code passen.
429
+ Alle drei werden erzeugt (`npm run fixtures:kasse`, `npm run fixtures:oberflaeche`,
430
+ `npm run fixtures:hobex-hps-codes`) und nie von Hand geändert; die CI prüft
431
+ nach jedem Lauf, dass sie zum Code passen.
361
432
 
362
- `oberflaeche.json` trägt die Paketversion. **Nach jedem `npm version` müssen deshalb beide
363
- Dateien neu erzeugt und mitcommittet werden**, sonst wird die CI rot.
433
+ `oberflaeche.json` und `hobex-hps-codes.json` tragen die Paketversion. **Nach
434
+ jedem `npm version` müssen deshalb alle drei Dateien neu erzeugt und
435
+ mitcommittet werden**, sonst wird die CI rot.
364
436
 
365
437
  ### Und die Gegenrichtung
366
438
 
@@ -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 ["activateCashregister", "cancelReceipt", "createCustomerCashregister", "createPartnerCustomer", "checkPartnerCustomerEmail", "createPartnerWebhook", "createPaymentLinkStripe", "createPrintJob", "createReceipt", "deletePartnerWebhook", "downloadDailyReport", "downloadReport", "endRegisterSession", "financeWebService", "generateFullReceiptId", "getCustomerCredentials", "getCustomerSignatureStatus", "getFirstReceiptDate", "getKasseSettings", "getPartnerCustomer", "getPartnerInfo", "getPrintJob", "getReceipt", "hobexPayApi", "hobexRefundApi", "listCustomerCashregisters", "listMyArticleGroups", "listMyArticles", "listMyCashregisters", "listMyPrinters", "listMyReceipts", "listMyTipRecipients", "listPartnerCustomers", "listPartnerWebhookDeliveries", "listPartnerWebhooks", "listRegisterUsersForDevice", "pairRegisterDevice", "registerPinLogin", "registerUserLogin", "renewRegisterSession", "requestCustomerSignature", "sendPartnerCustomerFonLink", "sendPartnerWebhookTest", "setMyKasseSettings", "setMyRegisterDeviceSettings", "stripeCaptureIntent", "unpairRegisterDevice", "rotatePartnerWebhookSecret", "updatePartnerWebhook"];
16
+ export declare const AUFRUFE: readonly ["activateCashregister", "cancelReceipt", "createCustomerCashregister", "createPartnerCustomer", "checkPartnerCustomerEmail", "createPartnerWebhook", "createPaymentLinkStripe", "createPrintJob", "createReceipt", "deletePartnerWebhook", "downloadDailyReport", "downloadReport", "endRegisterSession", "financeWebService", "generateFullReceiptId", "getCustomerCredentials", "getCustomerSignatureStatus", "getFirstReceiptDate", "getKasseSettings", "getPartnerCustomer", "getPartnerInfo", "getPrintJob", "getReceipt", "hobexPayApi", "hobexRefundApi", "listCustomerCashregisters", "listMyArticleGroups", "listMyArticles", "listMyCashregisters", "listMyPrinters", "listMyReceipts", "listMyTipRecipients", "listPartnerCustomers", "listPartnerWebhookDeliveries", "listPartnerWebhooks", "listRegisterUsersForDevice", "listRegisterSessionsForDevice", "pairRegisterDevice", "registerPinLogin", "registerUserLogin", "renewRegisterSession", "requestCustomerSignature", "sendPartnerCustomerFonLink", "sendPartnerWebhookTest", "setMyKasseSettings", "setMyRegisterDeviceSettings", "stripeCaptureIntent", "unpairRegisterDevice", "rotatePartnerWebhookSecret", "updatePartnerWebhook"];
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>;
@@ -38,6 +38,7 @@ exports.AUFRUFE = [
38
38
  'listPartnerWebhookDeliveries',
39
39
  'listPartnerWebhooks',
40
40
  'listRegisterUsersForDevice',
41
+ 'listRegisterSessionsForDevice',
41
42
  'pairRegisterDevice',
42
43
  'registerPinLogin',
43
44
  'registerUserLogin',
@@ -95,9 +95,13 @@ export declare class KasseneckApiError extends Error {
95
95
  /** Meldung des Backends, unveraendert (`message` aus der Huelle). */
96
96
  readonly serverMessage: string;
97
97
  /**
98
- * Maschinenlesbarer Fehlercode aus `data.code`, sofern die Antwort einen
99
- * fuehrt (`vertrag_offen`, `rate_limited`, …). Die aelteren Endpunkte des
100
- * Backends antworten ohne Code dann `undefined`.
98
+ * Stabiler, maschinenlesbarer Fehlercode des Backends, wenn der Endpunkt
99
+ * einen legt. Zwei Ablageorte kommen vor und beide zaehlen: das Feld `code`
100
+ * neben `message` (cancelReceipt, siehe CANCELLATION_ERROR_CODES) und
101
+ * `data.code` (Partner-API: `vertrag_offen`, `rate_limited`, …). Nur
102
+ * Bezeichner gelten — ein Freitext waere kein Code. Die aelteren Endpunkte
103
+ * antworten ohne Code — dann `undefined`. **Daran entscheiden, nie an
104
+ * [serverMessage]:** der Text darf sich aendern, der Code nicht.
101
105
  */
102
106
  readonly code: string | undefined;
103
107
  /**
@@ -106,7 +110,7 @@ export declare class KasseneckApiError extends Error {
106
110
  * beilegt. Immer ein Objekt, notfalls ein leeres.
107
111
  */
108
112
  readonly details: Record<string, unknown>;
109
- constructor(functionName: string, serverMessage: string, details?: Record<string, unknown>);
113
+ constructor(functionName: string, serverMessage: string, details?: Record<string, unknown>, code?: string);
110
114
  }
111
115
  /** Warum die Antwort keine verwertbare Huelle war. */
112
116
  export type HttpFailureReason = 'server-error' | 'empty-body' | 'not-json' | 'missing-status';
@@ -181,9 +181,13 @@ class KasseneckApiError extends Error {
181
181
  /** Meldung des Backends, unveraendert (`message` aus der Huelle). */
182
182
  serverMessage;
183
183
  /**
184
- * Maschinenlesbarer Fehlercode aus `data.code`, sofern die Antwort einen
185
- * fuehrt (`vertrag_offen`, `rate_limited`, …). Die aelteren Endpunkte des
186
- * Backends antworten ohne Code dann `undefined`.
184
+ * Stabiler, maschinenlesbarer Fehlercode des Backends, wenn der Endpunkt
185
+ * einen legt. Zwei Ablageorte kommen vor und beide zaehlen: das Feld `code`
186
+ * neben `message` (cancelReceipt, siehe CANCELLATION_ERROR_CODES) und
187
+ * `data.code` (Partner-API: `vertrag_offen`, `rate_limited`, …). Nur
188
+ * Bezeichner gelten — ein Freitext waere kein Code. Die aelteren Endpunkte
189
+ * antworten ohne Code — dann `undefined`. **Daran entscheiden, nie an
190
+ * [serverMessage]:** der Text darf sich aendern, der Code nicht.
187
191
  */
188
192
  code;
189
193
  /**
@@ -192,16 +196,15 @@ class KasseneckApiError extends Error {
192
196
  * beilegt. Immer ein Objekt, notfalls ein leeres.
193
197
  */
194
198
  details;
195
- constructor(functionName, serverMessage, details = {}) {
199
+ constructor(functionName, serverMessage, details = {}, code) {
196
200
  super(`${functionName} fehlgeschlagen: ${serverMessage}`);
197
201
  this.functionName = functionName;
198
202
  this.serverMessage = serverMessage;
199
203
  this.details = details;
200
- // Der Code steht in `details` und zusaetzlich als eigenes Feld: das eine
201
- // ist die Nutzlast, das andere die Frage, die ein Aufrufer wirklich
202
- // stellt. Nur Bezeichner gelten ein Freitext waere kein Code.
203
- const code = details['code'];
204
- this.code = typeof code === 'string' && BEZEICHNER.test(code) ? code : undefined;
204
+ // Der Code neben `message` hat Vorrang; fehlt er, gilt `data.code` aus
205
+ // den Details. So bleibt die Klasse fuer beide Ablageorte dieselbe.
206
+ const kandidat = code !== undefined ? code : details['code'];
207
+ this.code = typeof kandidat === 'string' && BEZEICHNER.test(kandidat) ? kandidat : undefined;
205
208
  }
206
209
  }
207
210
  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;
@@ -169,6 +185,12 @@ export declare function cancelReceipt(rufen: InternerTransport, options: CancelR
169
185
  * Stornobeleg aus frei uebergebenen Positionen — fuer den Fall, dass der
170
186
  * Originalbeleg nicht als Objekt vorliegt. Die Positionen gehen **unveraendert**
171
187
  * hinaus; das Vorzeichen setzt der Aufrufer.
188
+ *
189
+ * @deprecated Alter Storno-Weg ohne Bezug: kein Verweis auf das Original,
190
+ * keine Restmengen, kein Schutz vor doppeltem Storno, Gutscheine werden nicht
191
+ * zurueckgenommen. Das Backend nimmt ihn weiter an und legt `deprecation` in
192
+ * die Antwort. Stattdessen [cancelReceipt] (Endpunkt `cancelReceipt`): Bezug,
193
+ * Grund, Teilstorno, Fehlercodes.
172
194
  */
173
195
  export declare function createCancelReceipt(rufen: InternerTransport, options: CreateCancelReceiptOptions): Promise<Receipt>;
174
196
  /** Nullbeleg (RKSV-Pruefbeleg) — ohne Positionen und ohne Zahlungsart. */
@@ -174,6 +174,12 @@ async function cancelReceipt(rufen, options) {
174
174
  * Stornobeleg aus frei uebergebenen Positionen — fuer den Fall, dass der
175
175
  * Originalbeleg nicht als Objekt vorliegt. Die Positionen gehen **unveraendert**
176
176
  * hinaus; das Vorzeichen setzt der Aufrufer.
177
+ *
178
+ * @deprecated Alter Storno-Weg ohne Bezug: kein Verweis auf das Original,
179
+ * keine Restmengen, kein Schutz vor doppeltem Storno, Gutscheine werden nicht
180
+ * zurueckgenommen. Das Backend nimmt ihn weiter an und legt `deprecation` in
181
+ * die Antwort. Stattdessen [cancelReceipt] (Endpunkt `cancelReceipt`): Bezug,
182
+ * Grund, Teilstorno, Fehlercodes.
177
183
  */
178
184
  function createCancelReceipt(rufen, options) {
179
185
  return createReceipt(rufen, { ...options, receiptType: index_js_1.ReceiptType.cancellation });
@@ -399,6 +405,16 @@ function gepruefterTip(tip) {
399
405
  const nutzlast = { cents: tip.cents };
400
406
  if (tip.paymentMethod != null)
401
407
  nutzlast['paymentMethod'] = gepruefteZahlungsart(tip.paymentMethod);
408
+ // Nur mitschicken, wenn gesetzt: fehlt das Feld, entscheidet die
409
+ // Voreinstellung des Betriebs. Ein `false` waere dort eine Aussage, kein
410
+ // Weglassen. Der Wortlaut des Fehlers ist der des Backends
411
+ // (tip-core.normalizeTip) -- wer ihn hier sieht, sieht denselben Satz.
412
+ if (tip.sofortErhalten != null) {
413
+ if (typeof tip.sofortErhalten !== 'boolean') {
414
+ throw eingabefehler('Trinkgeld: sofortErhalten muss true oder false sein.');
415
+ }
416
+ nutzlast['sofortErhalten'] = tip.sofortErhalten;
417
+ }
402
418
  if (tip.recipients != null) {
403
419
  if (!Array.isArray(tip.recipients) || tip.recipients.length === 0) {
404
420
  throw eingabefehler('Trinkgeld: recipients darf nicht leer sein.');
@@ -181,7 +181,7 @@ function jsonAuswerten(text, functionName, statusCode, inhaltstyp, geheimnisse)
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, huelle.data, geheimnisse);
184
+ throw fachfehler(functionName, huelle.message, huelle.data, geheimnisse, 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, geheimnisse)
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, huelle.data, geheimnisse);
230
+ throw fachfehler(functionName, huelle.message, huelle.data, geheimnisse, 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, daten, geheimnisse) {
243
+ function fachfehler(functionName, message, daten, geheimnisse, code) {
244
244
  const meldung = typeof message === 'string' && message.trim() ? message : 'Unbekannter Fehler';
245
- return new errors_js_1.KasseneckApiError(functionName, meldung, (0, errors_js_1.fehlerDetails)(daten, geheimnisse));
245
+ // Nur ein Text zaehlt als Code -- alles andere waere ein geratener Vertrag.
246
+ return new errors_js_1.KasseneckApiError(functionName, meldung, (0, errors_js_1.fehlerDetails)(daten, geheimnisse), typeof code === 'string' && code ? code : undefined);
246
247
  }
247
248
  /**
248
249
  * Nutzlast aus Auth- und Aufruferparametern. Auth-Parameter bilden die
@@ -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.KasseneckAuthError = exports.KasseneckApiError = 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.isCancellationReason = exports.CANCELLATION_RESERVATION_MS = 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 = 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
- amountCents: Math.round((payload.amount ?? 0) * 100),
48
- tipCents: Math.round((payload.tip ?? 0) * 100),
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';
@@ -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: { receiptId: payload.cancellationOf.receiptId ?? null } } : {}),
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 } : {}),