@kreiseck/kasseneck-api 0.6.7

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 (294) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +2 -0
  3. package/README.md +206 -0
  4. package/dist/cjs/client/api.d.ts +75 -0
  5. package/dist/cjs/client/api.js +40 -0
  6. package/dist/cjs/client/auth.d.ts +52 -0
  7. package/dist/cjs/client/auth.js +72 -0
  8. package/dist/cjs/client/cashregisters.d.ts +21 -0
  9. package/dist/cjs/client/cashregisters.js +36 -0
  10. package/dist/cjs/client/errors.d.ts +159 -0
  11. package/dist/cjs/client/errors.js +226 -0
  12. package/dist/cjs/client/index.d.ts +8 -0
  13. package/dist/cjs/client/index.js +44 -0
  14. package/dist/cjs/client/receipts.d.ts +245 -0
  15. package/dist/cjs/client/receipts.js +452 -0
  16. package/dist/cjs/client/reports.d.ts +43 -0
  17. package/dist/cjs/client/reports.js +73 -0
  18. package/dist/cjs/client/status.d.ts +43 -0
  19. package/dist/cjs/client/status.js +47 -0
  20. package/dist/cjs/client/transport.d.ts +117 -0
  21. package/dist/cjs/client/transport.js +312 -0
  22. package/dist/cjs/enums/credit-card-provider.d.ts +22 -0
  23. package/dist/cjs/enums/credit-card-provider.js +23 -0
  24. package/dist/cjs/enums/define-enum.d.ts +15 -0
  25. package/dist/cjs/enums/define-enum.js +16 -0
  26. package/dist/cjs/enums/index.d.ts +7 -0
  27. package/dist/cjs/enums/index.js +17 -0
  28. package/dist/cjs/enums/keck-payment-method.d.ts +57 -0
  29. package/dist/cjs/enums/keck-payment-method.js +23 -0
  30. package/dist/cjs/enums/receipt-type.d.ts +41 -0
  31. package/dist/cjs/enums/receipt-type.js +18 -0
  32. package/dist/cjs/enums/stripe-link-mode.d.ts +17 -0
  33. package/dist/cjs/enums/stripe-link-mode.js +18 -0
  34. package/dist/cjs/enums/vat-rate.d.ts +40 -0
  35. package/dist/cjs/enums/vat-rate.js +19 -0
  36. package/dist/cjs/enums/voucher-action.d.ts +10 -0
  37. package/dist/cjs/enums/voucher-action.js +11 -0
  38. package/dist/cjs/enums/voucher-type.d.ts +9 -0
  39. package/dist/cjs/enums/voucher-type.js +10 -0
  40. package/dist/cjs/index.d.ts +5 -0
  41. package/dist/cjs/index.js +97 -0
  42. package/dist/cjs/kasse/artikel.d.ts +83 -0
  43. package/dist/cjs/kasse/artikel.js +79 -0
  44. package/dist/cjs/kasse/client.d.ts +10 -0
  45. package/dist/cjs/kasse/client.js +38 -0
  46. package/dist/cjs/kasse/drucker.d.ts +54 -0
  47. package/dist/cjs/kasse/drucker.js +41 -0
  48. package/dist/cjs/kasse/index.d.ts +11 -0
  49. package/dist/cjs/kasse/index.js +38 -0
  50. package/dist/cjs/kasse/settings.d.ts +115 -0
  51. package/dist/cjs/kasse/settings.js +62 -0
  52. package/dist/cjs/models/cancellation.d.ts +51 -0
  53. package/dist/cjs/models/cancellation.js +45 -0
  54. package/dist/cjs/models/cashregister.d.ts +77 -0
  55. package/dist/cjs/models/cashregister.js +53 -0
  56. package/dist/cjs/models/enum-payload.d.ts +26 -0
  57. package/dist/cjs/models/enum-payload.js +50 -0
  58. package/dist/cjs/models/hobex-receipt.d.ts +63 -0
  59. package/dist/cjs/models/hobex-receipt.js +86 -0
  60. package/dist/cjs/models/index.d.ts +10 -0
  61. package/dist/cjs/models/index.js +46 -0
  62. package/dist/cjs/models/receipt-company.d.ts +71 -0
  63. package/dist/cjs/models/receipt-company.js +52 -0
  64. package/dist/cjs/models/receipt-item.d.ts +100 -0
  65. package/dist/cjs/models/receipt-item.js +108 -0
  66. package/dist/cjs/models/receipt-summary.d.ts +92 -0
  67. package/dist/cjs/models/receipt-summary.js +54 -0
  68. package/dist/cjs/models/receipt.d.ts +108 -0
  69. package/dist/cjs/models/receipt.js +119 -0
  70. package/dist/cjs/models/report-month.d.ts +32 -0
  71. package/dist/cjs/models/report-month.js +65 -0
  72. package/dist/cjs/models/stripe-url-session.d.ts +18 -0
  73. package/dist/cjs/models/stripe-url-session.js +20 -0
  74. package/dist/cjs/models/voucher.d.ts +41 -0
  75. package/dist/cjs/models/voucher.js +56 -0
  76. package/dist/cjs/money.d.ts +23 -0
  77. package/dist/cjs/money.js +35 -0
  78. package/dist/cjs/package.json +1 -0
  79. package/dist/cjs/payments/hobex.d.ts +87 -0
  80. package/dist/cjs/payments/hobex.js +167 -0
  81. package/dist/cjs/payments/index.d.ts +33 -0
  82. package/dist/cjs/payments/index.js +42 -0
  83. package/dist/cjs/payments/stripe.d.ts +61 -0
  84. package/dist/cjs/payments/stripe.js +157 -0
  85. package/dist/cjs/printing/escpos.d.ts +219 -0
  86. package/dist/cjs/printing/escpos.js +636 -0
  87. package/dist/cjs/printing/index.d.ts +3 -0
  88. package/dist/cjs/printing/index.js +33 -0
  89. package/dist/cjs/printing/printable.d.ts +27 -0
  90. package/dist/cjs/printing/printable.js +90 -0
  91. package/dist/cjs/printing/webusb.d.ts +89 -0
  92. package/dist/cjs/printing/webusb.js +110 -0
  93. package/dist/cjs/react/index.d.ts +46 -0
  94. package/dist/cjs/react/index.js +51 -0
  95. package/dist/cjs/receipt/discount.d.ts +12 -0
  96. package/dist/cjs/receipt/discount.js +54 -0
  97. package/dist/cjs/receipt/epos.d.ts +53 -0
  98. package/dist/cjs/receipt/epos.js +95 -0
  99. package/dist/cjs/receipt/grid.d.ts +47 -0
  100. package/dist/cjs/receipt/grid.js +122 -0
  101. package/dist/cjs/receipt/index.d.ts +4 -0
  102. package/dist/cjs/receipt/index.js +29 -0
  103. package/dist/cjs/receipt/layout-escpos.d.ts +35 -0
  104. package/dist/cjs/receipt/layout-escpos.js +67 -0
  105. package/dist/cjs/receipt/layout.d.ts +180 -0
  106. package/dist/cjs/receipt/layout.js +594 -0
  107. package/dist/cjs/register/index.d.ts +51 -0
  108. package/dist/cjs/register/index.js +61 -0
  109. package/dist/cjs/register/pairing.d.ts +248 -0
  110. package/dist/cjs/register/pairing.js +279 -0
  111. package/dist/cjs/register/session.d.ts +37 -0
  112. package/dist/cjs/register/session.js +52 -0
  113. package/dist/cjs/vienna-time.d.ts +23 -0
  114. package/dist/cjs/vienna-time.js +114 -0
  115. package/dist/esm/client/api.d.ts +75 -0
  116. package/dist/esm/client/api.js +37 -0
  117. package/dist/esm/client/auth.d.ts +52 -0
  118. package/dist/esm/client/auth.js +68 -0
  119. package/dist/esm/client/cashregisters.d.ts +21 -0
  120. package/dist/esm/client/cashregisters.js +33 -0
  121. package/dist/esm/client/errors.d.ts +159 -0
  122. package/dist/esm/client/errors.js +212 -0
  123. package/dist/esm/client/index.d.ts +8 -0
  124. package/dist/esm/client/index.js +8 -0
  125. package/dist/esm/client/receipts.d.ts +245 -0
  126. package/dist/esm/client/receipts.js +438 -0
  127. package/dist/esm/client/reports.d.ts +43 -0
  128. package/dist/esm/client/reports.js +69 -0
  129. package/dist/esm/client/status.d.ts +43 -0
  130. package/dist/esm/client/status.js +43 -0
  131. package/dist/esm/client/transport.d.ts +117 -0
  132. package/dist/esm/client/transport.js +307 -0
  133. package/dist/esm/enums/credit-card-provider.d.ts +22 -0
  134. package/dist/esm/enums/credit-card-provider.js +20 -0
  135. package/dist/esm/enums/define-enum.d.ts +15 -0
  136. package/dist/esm/enums/define-enum.js +13 -0
  137. package/dist/esm/enums/index.d.ts +7 -0
  138. package/dist/esm/enums/index.js +7 -0
  139. package/dist/esm/enums/keck-payment-method.d.ts +57 -0
  140. package/dist/esm/enums/keck-payment-method.js +20 -0
  141. package/dist/esm/enums/receipt-type.d.ts +41 -0
  142. package/dist/esm/enums/receipt-type.js +15 -0
  143. package/dist/esm/enums/stripe-link-mode.d.ts +17 -0
  144. package/dist/esm/enums/stripe-link-mode.js +15 -0
  145. package/dist/esm/enums/vat-rate.d.ts +40 -0
  146. package/dist/esm/enums/vat-rate.js +16 -0
  147. package/dist/esm/enums/voucher-action.d.ts +10 -0
  148. package/dist/esm/enums/voucher-action.js +8 -0
  149. package/dist/esm/enums/voucher-type.d.ts +9 -0
  150. package/dist/esm/enums/voucher-type.js +7 -0
  151. package/dist/esm/index.d.ts +5 -0
  152. package/dist/esm/index.js +5 -0
  153. package/dist/esm/kasse/artikel.d.ts +83 -0
  154. package/dist/esm/kasse/artikel.js +70 -0
  155. package/dist/esm/kasse/client.d.ts +10 -0
  156. package/dist/esm/kasse/client.js +33 -0
  157. package/dist/esm/kasse/drucker.d.ts +54 -0
  158. package/dist/esm/kasse/drucker.js +36 -0
  159. package/dist/esm/kasse/index.d.ts +11 -0
  160. package/dist/esm/kasse/index.js +13 -0
  161. package/dist/esm/kasse/settings.d.ts +115 -0
  162. package/dist/esm/kasse/settings.js +58 -0
  163. package/dist/esm/models/cancellation.d.ts +51 -0
  164. package/dist/esm/models/cancellation.js +40 -0
  165. package/dist/esm/models/cashregister.d.ts +77 -0
  166. package/dist/esm/models/cashregister.js +50 -0
  167. package/dist/esm/models/enum-payload.d.ts +26 -0
  168. package/dist/esm/models/enum-payload.js +44 -0
  169. package/dist/esm/models/hobex-receipt.d.ts +63 -0
  170. package/dist/esm/models/hobex-receipt.js +80 -0
  171. package/dist/esm/models/index.d.ts +10 -0
  172. package/dist/esm/models/index.js +10 -0
  173. package/dist/esm/models/receipt-company.d.ts +71 -0
  174. package/dist/esm/models/receipt-company.js +48 -0
  175. package/dist/esm/models/receipt-item.d.ts +100 -0
  176. package/dist/esm/models/receipt-item.js +101 -0
  177. package/dist/esm/models/receipt-summary.d.ts +92 -0
  178. package/dist/esm/models/receipt-summary.js +49 -0
  179. package/dist/esm/models/receipt.d.ts +108 -0
  180. package/dist/esm/models/receipt.js +113 -0
  181. package/dist/esm/models/report-month.d.ts +32 -0
  182. package/dist/esm/models/report-month.js +58 -0
  183. package/dist/esm/models/stripe-url-session.d.ts +18 -0
  184. package/dist/esm/models/stripe-url-session.js +16 -0
  185. package/dist/esm/models/voucher.d.ts +41 -0
  186. package/dist/esm/models/voucher.js +51 -0
  187. package/dist/esm/money.d.ts +23 -0
  188. package/dist/esm/money.js +31 -0
  189. package/dist/esm/payments/hobex.d.ts +87 -0
  190. package/dist/esm/payments/hobex.js +162 -0
  191. package/dist/esm/payments/index.d.ts +33 -0
  192. package/dist/esm/payments/index.js +33 -0
  193. package/dist/esm/payments/stripe.d.ts +61 -0
  194. package/dist/esm/payments/stripe.js +153 -0
  195. package/dist/esm/printing/escpos.d.ts +219 -0
  196. package/dist/esm/printing/escpos.js +616 -0
  197. package/dist/esm/printing/index.d.ts +3 -0
  198. package/dist/esm/printing/index.js +3 -0
  199. package/dist/esm/printing/printable.d.ts +27 -0
  200. package/dist/esm/printing/printable.js +87 -0
  201. package/dist/esm/printing/webusb.d.ts +89 -0
  202. package/dist/esm/printing/webusb.js +101 -0
  203. package/dist/esm/react/index.d.ts +46 -0
  204. package/dist/esm/react/index.js +47 -0
  205. package/dist/esm/receipt/discount.d.ts +12 -0
  206. package/dist/esm/receipt/discount.js +51 -0
  207. package/dist/esm/receipt/epos.d.ts +53 -0
  208. package/dist/esm/receipt/epos.js +86 -0
  209. package/dist/esm/receipt/grid.d.ts +47 -0
  210. package/dist/esm/receipt/grid.js +116 -0
  211. package/dist/esm/receipt/index.d.ts +4 -0
  212. package/dist/esm/receipt/index.js +4 -0
  213. package/dist/esm/receipt/layout-escpos.d.ts +35 -0
  214. package/dist/esm/receipt/layout-escpos.js +64 -0
  215. package/dist/esm/receipt/layout.d.ts +180 -0
  216. package/dist/esm/receipt/layout.js +583 -0
  217. package/dist/esm/register/index.d.ts +51 -0
  218. package/dist/esm/register/index.js +51 -0
  219. package/dist/esm/register/pairing.d.ts +248 -0
  220. package/dist/esm/register/pairing.js +270 -0
  221. package/dist/esm/register/session.d.ts +37 -0
  222. package/dist/esm/register/session.js +48 -0
  223. package/dist/esm/vienna-time.d.ts +23 -0
  224. package/dist/esm/vienna-time.js +110 -0
  225. package/fixtures/belege/langer-artikelname.json +43 -0
  226. package/fixtures/belege/null-ausfall.json +41 -0
  227. package/fixtures/belege/null-jahr.json +41 -0
  228. package/fixtures/belege/null-monat.json +41 -0
  229. package/fixtures/belege/null-pruef.json +41 -0
  230. package/fixtures/belege/null-schluss.json +41 -0
  231. package/fixtures/belege/null-start.json +40 -0
  232. package/fixtures/belege/rabattzeilen.json +61 -0
  233. package/fixtures/belege/signaturausfall-verkauf.json +49 -0
  234. package/fixtures/belege/storno-teil.json +48 -0
  235. package/fixtures/belege/storno-voll.json +54 -0
  236. package/fixtures/belege/testkasse-verkauf.json +50 -0
  237. package/fixtures/belege/testsignatur-verkauf.json +50 -0
  238. package/fixtures/belege/training.json +49 -0
  239. package/fixtures/belege/verkauf-bar.json +49 -0
  240. package/fixtures/belege/verkauf-karte.json +49 -0
  241. package/fixtures/belege/verkauf-kleinunternehmer.json +42 -0
  242. package/fixtures/erwartet/langer-artikelname.grid32.txt +25 -0
  243. package/fixtures/erwartet/langer-artikelname.grid48.txt +24 -0
  244. package/fixtures/erwartet/langer-artikelname.lines.json +216 -0
  245. package/fixtures/erwartet/null-ausfall.grid32.txt +28 -0
  246. package/fixtures/erwartet/null-ausfall.grid48.txt +25 -0
  247. package/fixtures/erwartet/null-ausfall.lines.json +220 -0
  248. package/fixtures/erwartet/null-jahr.grid32.txt +29 -0
  249. package/fixtures/erwartet/null-jahr.grid48.txt +25 -0
  250. package/fixtures/erwartet/null-jahr.lines.json +220 -0
  251. package/fixtures/erwartet/null-monat.grid32.txt +28 -0
  252. package/fixtures/erwartet/null-monat.grid48.txt +25 -0
  253. package/fixtures/erwartet/null-monat.lines.json +220 -0
  254. package/fixtures/erwartet/null-pruef.grid32.txt +28 -0
  255. package/fixtures/erwartet/null-pruef.grid48.txt +25 -0
  256. package/fixtures/erwartet/null-pruef.lines.json +220 -0
  257. package/fixtures/erwartet/null-schluss.grid32.txt +28 -0
  258. package/fixtures/erwartet/null-schluss.grid48.txt +25 -0
  259. package/fixtures/erwartet/null-schluss.lines.json +220 -0
  260. package/fixtures/erwartet/null-start.grid32.txt +28 -0
  261. package/fixtures/erwartet/null-start.grid48.txt +25 -0
  262. package/fixtures/erwartet/null-start.lines.json +220 -0
  263. package/fixtures/erwartet/rabattzeilen.grid32.txt +27 -0
  264. package/fixtures/erwartet/rabattzeilen.grid48.txt +26 -0
  265. package/fixtures/erwartet/rabattzeilen.lines.json +286 -0
  266. package/fixtures/erwartet/signaturausfall-verkauf.grid32.txt +28 -0
  267. package/fixtures/erwartet/signaturausfall-verkauf.grid48.txt +26 -0
  268. package/fixtures/erwartet/signaturausfall-verkauf.lines.json +266 -0
  269. package/fixtures/erwartet/storno-teil.grid32.txt +27 -0
  270. package/fixtures/erwartet/storno-teil.grid48.txt +26 -0
  271. package/fixtures/erwartet/storno-teil.lines.json +237 -0
  272. package/fixtures/erwartet/storno-voll.grid32.txt +29 -0
  273. package/fixtures/erwartet/storno-voll.grid48.txt +28 -0
  274. package/fixtures/erwartet/storno-voll.lines.json +277 -0
  275. package/fixtures/erwartet/testkasse-verkauf.grid32.txt +28 -0
  276. package/fixtures/erwartet/testkasse-verkauf.grid48.txt +27 -0
  277. package/fixtures/erwartet/testkasse-verkauf.lines.json +270 -0
  278. package/fixtures/erwartet/testsignatur-verkauf.grid32.txt +30 -0
  279. package/fixtures/erwartet/testsignatur-verkauf.grid48.txt +27 -0
  280. package/fixtures/erwartet/testsignatur-verkauf.lines.json +270 -0
  281. package/fixtures/erwartet/training.grid32.txt +33 -0
  282. package/fixtures/erwartet/training.grid48.txt +30 -0
  283. package/fixtures/erwartet/training.lines.json +277 -0
  284. package/fixtures/erwartet/verkauf-bar.grid32.txt +25 -0
  285. package/fixtures/erwartet/verkauf-bar.grid48.txt +24 -0
  286. package/fixtures/erwartet/verkauf-bar.lines.json +256 -0
  287. package/fixtures/erwartet/verkauf-karte.grid32.txt +25 -0
  288. package/fixtures/erwartet/verkauf-karte.grid48.txt +24 -0
  289. package/fixtures/erwartet/verkauf-karte.lines.json +256 -0
  290. package/fixtures/erwartet/verkauf-kleinunternehmer.grid32.txt +26 -0
  291. package/fixtures/erwartet/verkauf-kleinunternehmer.grid48.txt +25 -0
  292. package/fixtures/erwartet/verkauf-kleinunternehmer.lines.json +226 -0
  293. package/fixtures/manifest.json +107 -0
  294. package/package.json +135 -0
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Server-Zeitstempel parsen: ohne Offset = Wiener Wanduhrzeit, mit
3
+ * Offset/`Z` = bereits ein echter Zeitpunkt. Ergebnis ist immer ein echter
4
+ * Zeitpunkt (JS-`Date`, intern UTC-Millisekunden seit Epoch).
5
+ */
6
+ export declare function parseServerTimeStamp(raw: string): Date;
7
+ /** Wiener Wanduhrzeit: die Ziffern, die eine Uhr in Wien anzeigt. `month` ist 1-12. */
8
+ export interface ViennaWallClock {
9
+ year: number;
10
+ month: number;
11
+ day: number;
12
+ hour: number;
13
+ minute: number;
14
+ second: number;
15
+ millisecond: number;
16
+ }
17
+ /**
18
+ * Echter Zeitpunkt -> Wiener Wanduhrzeit (Gegenstueck zu
19
+ * [parseServerTimeStamp]). Der Versatz kommt aus derselben Sommerzeitregel wie
20
+ * die Hinrichtung; hier ist er direkt anwendbar, weil der Zeitpunkt schon
21
+ * feststeht.
22
+ */
23
+ export declare function toViennaWallClock(zeitpunkt: Date): ViennaWallClock;
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseServerTimeStamp = parseServerTimeStamp;
4
+ exports.toViennaWallClock = toViennaWallClock;
5
+ /**
6
+ * Deutung von Server-Zeitstempeln in der Geschaeftszeitzone Europe/Vienna —
7
+ * Zwilling von `ViennaTime.parseServerTimeStamp` in
8
+ * kasseneck_api/lib/services/vienna_time.dart.
9
+ *
10
+ * Zwei Richtungen: `parseServerTimeStamp` deutet die Nutzlast als Zeitpunkt,
11
+ * `toViennaWallClock` liest zu einem Zeitpunkt die Wiener Wanduhrzeit zurueck.
12
+ * Die Rueckrichtung braucht jeder, der aus einem Zeitpunkt einen fachlichen
13
+ * Kalenderwert ableitet (Berichtsmonat, Tagesabgrenzung): die eingebauten
14
+ * `getMonth()`/`getFullYear()` liefern die Zeitzone des ausfuehrenden
15
+ * Rechners, und ein Beleg vom 1. Maerz 00:30 Wiener Zeit faellt damit auf
16
+ * einer UTC-Maschine in den Februar. Weitere Anzeigehilfen des Dart-Vorbilds
17
+ * (`dayKey`, `now()`) sind nicht Teil dieses Pakets.
18
+ *
19
+ * Der Kasseneck-Server liefert Zeitstempel uneinheitlich: neue Belege als
20
+ * Wiener Wanduhrzeit ohne Offset (`getLokalAustriaTimeFinanzamtFormat()` im
21
+ * Backend, Format `YYYY-MM-DDTHH:mm:ss`), manche aeltere/andere Felder als
22
+ * echten Zeitpunkt mit `Z`/Offset. `new Date(raw)` allein ist dafuer
23
+ * ungeeignet: JS deutet einen offsetlosen ISO-String als **lokale** Zeit des
24
+ * ausfuehrenden Rechners — auf einer Wiener Maschine zufaellig richtig, in
25
+ * jeder anderen Zeitzone (z. B. einer Browser-Kasse im Ausland) falsch.
26
+ */
27
+ const STUNDE_MS = 60 * 60 * 1000;
28
+ const NAIVER_ZEITSTEMPEL = /^(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?$/;
29
+ const HAT_ZEITZONE = /Z$|[+-]\d{2}:?\d{2}$/;
30
+ /**
31
+ * Server-Zeitstempel parsen: ohne Offset = Wiener Wanduhrzeit, mit
32
+ * Offset/`Z` = bereits ein echter Zeitpunkt. Ergebnis ist immer ein echter
33
+ * Zeitpunkt (JS-`Date`, intern UTC-Millisekunden seit Epoch).
34
+ */
35
+ function parseServerTimeStamp(raw) {
36
+ const trimmed = raw.trim();
37
+ if (HAT_ZEITZONE.test(trimmed)) {
38
+ const zeitpunkt = new Date(trimmed);
39
+ // `new Date` macht aus '2026-99-99T00:00:00Z' klaglos ein Invalid Date:
40
+ // eine Zeitzone allein macht noch kein Datum. Ohne diese Pruefung wandert
41
+ // der Unsinn still weiter und wird erst in einer Ableitung zu NaN — dort,
42
+ // wo niemand mehr auf den Zeitstempel schaut.
43
+ if (Number.isNaN(zeitpunkt.getTime())) {
44
+ throw new Error(`Zeitstempel: unlesbares Format "${raw}"`);
45
+ }
46
+ return zeitpunkt;
47
+ }
48
+ const match = NAIVER_ZEITSTEMPEL.exec(trimmed);
49
+ if (!match) {
50
+ throw new Error(`Zeitstempel: unlesbares Format "${raw}"`);
51
+ }
52
+ const [, jahr, monat, tag, stunde, minute, sekunde, bruchteil] = match;
53
+ return fromViennaWallClock({
54
+ year: Number(jahr),
55
+ month: Number(monat),
56
+ day: Number(tag),
57
+ hour: Number(stunde),
58
+ minute: Number(minute),
59
+ second: Number(sekunde),
60
+ millisecond: bruchteil ? Number(bruchteil.padEnd(3, '0').slice(0, 3)) : 0,
61
+ });
62
+ }
63
+ /**
64
+ * Echter Zeitpunkt -> Wiener Wanduhrzeit (Gegenstueck zu
65
+ * [parseServerTimeStamp]). Der Versatz kommt aus derselben Sommerzeitregel wie
66
+ * die Hinrichtung; hier ist er direkt anwendbar, weil der Zeitpunkt schon
67
+ * feststeht.
68
+ */
69
+ function toViennaWallClock(zeitpunkt) {
70
+ const utcMs = zeitpunkt.getTime();
71
+ // Ein Invalid Date wuerde hier lautlos zu NaN in jedem Feld — und damit zu
72
+ // einem NaN-Berichtsmonat beim Aufrufer.
73
+ if (Number.isNaN(utcMs)) {
74
+ throw new Error('Zeitpunkt: unbrauchbarer Wert (Invalid Date)');
75
+ }
76
+ const versatzMs = (istSommerzeit(utcMs) ? 2 : 1) * STUNDE_MS;
77
+ const verschoben = new Date(utcMs + versatzMs);
78
+ return {
79
+ year: verschoben.getUTCFullYear(),
80
+ month: verschoben.getUTCMonth() + 1,
81
+ day: verschoben.getUTCDate(),
82
+ hour: verschoben.getUTCHours(),
83
+ minute: verschoben.getUTCMinutes(),
84
+ second: verschoben.getUTCSeconds(),
85
+ millisecond: verschoben.getUTCMilliseconds(),
86
+ };
87
+ }
88
+ /** Wiener Wanduhrzeit (Ziffern wie angezeigt) -> echter Zeitpunkt (UTC). */
89
+ function fromViennaWallClock(wand) {
90
+ const basisUtcMs = Date.UTC(wand.year, wand.month - 1, wand.day, wand.hour, wand.minute, wand.second, wand.millisecond);
91
+ // Erst Sommerzeit annehmen; stimmt die Annahme fuer den resultierenden
92
+ // Zeitpunkt, war sie richtig — sonst gilt Winterzeit (wie im Dart-Vorbild).
93
+ const sommerVariante = basisUtcMs - 2 * STUNDE_MS;
94
+ if (istSommerzeit(sommerVariante)) {
95
+ return new Date(sommerVariante);
96
+ }
97
+ return new Date(basisUtcMs - 1 * STUNDE_MS);
98
+ }
99
+ /** EU-Sommerzeit: letzter Sonntag im Maerz 01:00 UTC bis letzter Sonntag im Oktober 01:00 UTC. */
100
+ function istSommerzeit(utcMs) {
101
+ const jahr = new Date(utcMs).getUTCFullYear();
102
+ const start = letzterSonntagUtcMs(jahr, 3);
103
+ const ende = letzterSonntagUtcMs(jahr, 10);
104
+ return utcMs >= start && utcMs < ende;
105
+ }
106
+ /** Letzter Sonntag des Monats, 01:00 UTC (EU-Umstellungszeitpunkt). */
107
+ function letzterSonntagUtcMs(jahr, monat) {
108
+ const letzterTag = new Date(Date.UTC(jahr, monat, 0)).getUTCDate();
109
+ // JS-Wochentag (0=Sonntag..6=Samstag) ist bereits die Anzahl Tage seit dem
110
+ // letzten Sonntag — keine Umrechnung noetig (anders als Darts weekday, das
111
+ // Montag=1..Sonntag=7 zaehlt und dafuer `% 7` braucht).
112
+ const wochentag = new Date(Date.UTC(jahr, monat - 1, letzterTag)).getUTCDay();
113
+ return Date.UTC(jahr, monat - 1, letzterTag - wochentag, 1, 0, 0, 0);
114
+ }
@@ -0,0 +1,75 @@
1
+ import type { Cashregister, HobexReceipt, Receipt, ReportMonth, StripeUrlSession } from '../models/index.js';
2
+ import { type CreateStripeLinkOptions, type StripeCaptureResult } from '../payments/stripe.js';
3
+ import { type HobexPayOptions, type HobexRefundOptions } from '../payments/hobex.js';
4
+ import { type TransportOptions } from './transport.js';
5
+ import { type CashboxStatus, type SignatureStatus } from './status.js';
6
+ import { type ListMyReceiptsOptions, type ReceiptList, type ReceiptWithCompany, type SellReceiptOptions, type CancelReceiptOptions, type CancelReceiptResult, type CreateCancelReceiptOptions } from './receipts.js';
7
+ /**
8
+ * Schlichte Factory ueber den Endpunkt-Funktionen: bindet einen Transport
9
+ * einmal und liefert die Aufrufe ohne den wiederkehrenden ersten Parameter.
10
+ * Bewusst **keine** Klasse und keine Vererbung — die Aufrufe sind freie
11
+ * Funktionen (siehe receipts.ts) und bleiben einzeln importierbar; wer sie
12
+ * lieber einzeln nimmt, verliert nichts.
13
+ *
14
+ * Hier stehen **alle** Endpunkt-Aufrufe des Pakets: Belege, Berichte, Status,
15
+ * Zahlungen und die beiden Sitzungs-Aufrufe der Browser-Kasse. Was kein
16
+ * Endpunkt-Aufruf ist (Druck, Beleg-Darstellung), gehoert nicht hierher. Der
17
+ * Grund fuer die Vollstaendigkeit ist der Zweck der Fassade: ein **einziger**
18
+ * Konstruktionsweg je Anmeldung. Fehlte eine Endpunkt-Familie, muesste ein
19
+ * Aufrufer fuer sie zusaetzlich [createTransport] mit denselben Optionen bauen
20
+ * und haette zwei Wege fuer dieselbe Anmeldung nebeneinander.
21
+ *
22
+ * **Eine Ausnahme, und sie folgt aus genau diesem Grund:** Kopplung,
23
+ * Benutzerliste des Geraets und PIN-Anmeldung (`@kreiseck/kasseneck-api/register`)
24
+ * stehen **nicht** hier. Diese Fassade wird mit einer Anmeldung gebaut, jene
25
+ * drei laufen aber, **bevor** es eine gibt. Stuenden sie darin, sagte die
26
+ * Fassade eine Anmeldung zu, die fuer sie nicht gilt: ein Aufrufer riefe
27
+ * `pairRegisterDevice` an einer mit `apiKeyAuth` gebauten Fassade und glaubte,
28
+ * der Schluessel haette dabei etwas zu sagen. Sie bringen ihre
29
+ * Verbindungsangaben stattdessen selbst mit.
30
+ */
31
+ export interface KasseneckApi {
32
+ /** Normalbeleg (Verkauf). */
33
+ sellReceipt(options: SellReceiptOptions): Promise<Receipt>;
34
+ /** Normalbeleg samt Firmen-/Druckdaten fuer den Belegdruck. */
35
+ sellReceiptWithCompany(options: SellReceiptOptions): Promise<ReceiptWithCompany>;
36
+ /** Storno eines bestehenden Belegs — voll oder in Teilen, ueber den Storno-Endpunkt. */
37
+ cancelReceipt(options: CancelReceiptOptions): Promise<CancelReceiptResult>;
38
+ /** Storno aus frei uebergebenen Positionen. */
39
+ createCancelReceipt(options: CreateCancelReceiptOptions): Promise<Receipt>;
40
+ /** Nullbeleg (RKSV-Pruefbeleg). */
41
+ zeroReceipt(): Promise<Receipt>;
42
+ /** Einzelnen Beleg holen. */
43
+ getReceipt(receiptId: string): Promise<Receipt>;
44
+ /** Einzelnen Beleg samt Firmen-/Druckdaten holen. */
45
+ getReceiptWithCompany(receiptId: string): Promise<ReceiptWithCompany>;
46
+ /** Verschluesselte Volltext-Belegnummer erzeugen. */
47
+ generateFullReceiptId(receiptId: string): Promise<string>;
48
+ /** Berichtsmonat des ersten Belegs (nicht fuer den Kassen-Benutzer-Weg). */
49
+ getFirstReceiptDate(): Promise<ReportMonth>;
50
+ /** Belegliste einer Kasse samt Kennzahlen (nur mit ID-Token, siehe receipts.ts). */
51
+ listMyReceipts(options: ListMyReceiptsOptions): Promise<ReceiptList>;
52
+ /** Kassen des angemeldeten Benutzers (nur mit ID-Token, siehe cashregisters.ts). */
53
+ listMyCashregisters(): Promise<Cashregister[]>;
54
+ /** Tagesbericht als PDF (Kalendertag nach Wiener Zeit). */
55
+ downloadDailyReport(date: Date): Promise<Uint8Array>;
56
+ /** Monatsbericht als PDF (Endpunkt `downloadReport`). */
57
+ downloadMonthlyReport(reportMonth: ReportMonth): Promise<Uint8Array>;
58
+ /** Betriebsstatus der Kasse bei FinanzOnline. */
59
+ getCashboxStatus(): Promise<CashboxStatus>;
60
+ /** Status der Signatureinheit bei FinanzOnline. */
61
+ getSignatureStatus(zertifikatNrHex: string): Promise<SignatureStatus>;
62
+ /** Stripe-Zahlungslink erzeugen (Endpunkt `createPaymentLinkStripe`). */
63
+ createStripeLink(options: CreateStripeLinkOptions): Promise<StripeUrlSession>;
64
+ /** Reservierte Stripe-Zahlung einziehen. */
65
+ stripeCaptureIntent(stripeSessionId: string): Promise<StripeCaptureResult>;
66
+ /** Karte ueber die Hobex-Cloud belasten. */
67
+ hobexPay(options: HobexPayOptions): Promise<HobexReceipt>;
68
+ /** Hobex-Cloud-Zahlung erstatten (ohne Rueckgabewert, siehe payments/hobex.ts). */
69
+ hobexRefund(options: HobexRefundOptions): Promise<void>;
70
+ /** Kassen-Sitzung verlaengern; liefert den neuen Ablauf (nur Kassen-Benutzer). */
71
+ renewRegisterSession(): Promise<number>;
72
+ /** Kassen-Sitzung beenden und den Lizenzplatz freigeben (nur Kassen-Benutzer). */
73
+ endRegisterSession(): Promise<void>;
74
+ }
75
+ export declare function createKasseneckApi(options: TransportOptions): KasseneckApi;
@@ -0,0 +1,37 @@
1
+ import { createStripeLink, stripeCaptureIntent, } from '../payments/stripe.js';
2
+ import { hobexPay, hobexRefund } from '../payments/hobex.js';
3
+ import { createTransport, createBinaryTransport } from './transport.js';
4
+ import { downloadDailyReport, downloadMonthlyReport } from './reports.js';
5
+ import { getCashboxStatus, getSignatureStatus } from './status.js';
6
+ import { sellReceipt, sellReceiptWithCompany, cancelReceipt, createCancelReceipt, zeroReceipt, getReceipt, getReceiptWithCompany, generateFullReceiptId, getFirstReceiptDate, listMyReceipts, } from './receipts.js';
7
+ import { listMyCashregisters } from './cashregisters.js';
8
+ import { renewRegisterSession, endRegisterSession } from '../register/session.js';
9
+ export function createKasseneckApi(options) {
10
+ const rufen = createTransport(options);
11
+ // Zweiter Einstiegspunkt fuer die PDF-Downloads; dieselben Optionen,
12
+ // dieselbe Anmeldung — nur die Antwort wird als Bytes gelesen.
13
+ const rufenBinaer = createBinaryTransport(options);
14
+ return {
15
+ sellReceipt: (o) => sellReceipt(rufen, o),
16
+ sellReceiptWithCompany: (o) => sellReceiptWithCompany(rufen, o),
17
+ cancelReceipt: (o) => cancelReceipt(rufen, o),
18
+ createCancelReceipt: (o) => createCancelReceipt(rufen, o),
19
+ zeroReceipt: () => zeroReceipt(rufen),
20
+ getReceipt: (receiptId) => getReceipt(rufen, receiptId),
21
+ getReceiptWithCompany: (receiptId) => getReceiptWithCompany(rufen, receiptId),
22
+ generateFullReceiptId: (receiptId) => generateFullReceiptId(rufen, receiptId),
23
+ getFirstReceiptDate: () => getFirstReceiptDate(rufen),
24
+ listMyReceipts: (o) => listMyReceipts(rufen, o),
25
+ listMyCashregisters: () => listMyCashregisters(rufen),
26
+ downloadDailyReport: (date) => downloadDailyReport(rufenBinaer, date),
27
+ downloadMonthlyReport: (reportMonth) => downloadMonthlyReport(rufenBinaer, reportMonth),
28
+ getCashboxStatus: () => getCashboxStatus(rufen),
29
+ getSignatureStatus: (zertifikatNrHex) => getSignatureStatus(rufen, zertifikatNrHex),
30
+ createStripeLink: (o) => createStripeLink(rufen, o),
31
+ stripeCaptureIntent: (stripeSessionId) => stripeCaptureIntent(rufen, stripeSessionId),
32
+ hobexPay: (o) => hobexPay(rufen, o),
33
+ hobexRefund: (o) => hobexRefund(rufen, o),
34
+ renewRegisterSession: () => renewRegisterSession(rufen),
35
+ endRegisterSession: () => endRegisterSession(rufen),
36
+ };
37
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Die zwei Anmeldewege ans Kasseneck-Backend — gleichrangig, austauschbar,
3
+ * keiner ist der bevorzugte:
4
+ *
5
+ * - `apiKeyAuth` — der Weg der Geraete, POS-Apps und Dritten: der `api_key` als
6
+ * Bearer plus die Kopfzeile `cashregister-token` (wie im Flutter-Zwilling
7
+ * `kasseneck_api`).
8
+ * - `registerUserAuth` — der Weg der Browser-Kasse: ein Kassen-Benutzer, per
9
+ * PIN an einem gekoppelten Geraet angemeldet. Firebase-ID-Token als Bearer,
10
+ * die laufende Sitzung als Kopfzeile `register-session`, die Kasse als
11
+ * Parameter `cashregisterId` in der Nutzlast.
12
+ *
13
+ * Eine Anmeldung liefert **nur** Kopfzeilen und ggf. Zusatzparameter. Sie
14
+ * fuehrt keine eigenen HTTP-Aufrufe, kennt den Transport nicht und haelt keinen
15
+ * Zustand — genau deshalb ist sie austauschbar und einzeln testbar.
16
+ *
17
+ * Das Paket weiss **nichts** von Firebase: `registerUserAuth` bekommt eine
18
+ * Funktion, die ein gueltiges ID-Token liefert (darf `async` sein). Sie wird
19
+ * bei **jedem** Aufruf befragt — Firebase-ID-Tokens laufen nach einer Stunde
20
+ * ab, die Sitzung der Browser-Kasse lebt sogar nur 90 Sekunden und wird alle
21
+ * 30 Sekunden erneuert; ein einmal gemerkter Wert waere also bald tot.
22
+ */
23
+ /** Was eine Anmeldung zu einer Anfrage beisteuert. */
24
+ export interface AuthCredentials {
25
+ /** Kopfzeilen der Anfrage (mindestens `Authorization`). */
26
+ headers: Record<string, string>;
27
+ /** Zusatzparameter, die in die Nutzlast wandern (z. B. `cashregisterId`). */
28
+ params: Record<string, unknown>;
29
+ }
30
+ /**
31
+ * Eine Anmeldung ist eine Funktion, die pro Anfrage frische Zugangsdaten
32
+ * liefert (synchron oder asynchron).
33
+ */
34
+ export type KasseneckAuth = () => AuthCredentials | Promise<AuthCredentials>;
35
+ export interface ApiKeyAuthOptions {
36
+ /** API-Schluessel im Format `kr_<env>_<...>` (opak). */
37
+ apiKey: string;
38
+ /** Kassen-Token der Kasse, an der gearbeitet wird (`cb_<env>_<...>`). */
39
+ cashregisterToken: string;
40
+ }
41
+ export interface RegisterUserAuthOptions {
42
+ /** Liefert ein gueltiges Firebase-ID-Token; wird pro Anfrage befragt. */
43
+ getIdToken: () => string | Promise<string>;
44
+ /** Liefert die laufende Kassen-Sitzung; wird pro Anfrage befragt. */
45
+ getSessionId: () => string | Promise<string>;
46
+ /** Kasse, an der der Kassen-Benutzer angemeldet ist. */
47
+ cashregisterId: string;
48
+ }
49
+ /** Anmeldung per API-Schluessel und Kassen-Token (Geraete, POS-Apps, Dritte). */
50
+ export declare function apiKeyAuth(options: ApiKeyAuthOptions): KasseneckAuth;
51
+ /** Anmeldung eines Kassen-Benutzers der Browser-Kasse (ID-Token + Sitzung). */
52
+ export declare function registerUserAuth(options: RegisterUserAuthOptions): KasseneckAuth;
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Die zwei Anmeldewege ans Kasseneck-Backend — gleichrangig, austauschbar,
3
+ * keiner ist der bevorzugte:
4
+ *
5
+ * - `apiKeyAuth` — der Weg der Geraete, POS-Apps und Dritten: der `api_key` als
6
+ * Bearer plus die Kopfzeile `cashregister-token` (wie im Flutter-Zwilling
7
+ * `kasseneck_api`).
8
+ * - `registerUserAuth` — der Weg der Browser-Kasse: ein Kassen-Benutzer, per
9
+ * PIN an einem gekoppelten Geraet angemeldet. Firebase-ID-Token als Bearer,
10
+ * die laufende Sitzung als Kopfzeile `register-session`, die Kasse als
11
+ * Parameter `cashregisterId` in der Nutzlast.
12
+ *
13
+ * Eine Anmeldung liefert **nur** Kopfzeilen und ggf. Zusatzparameter. Sie
14
+ * fuehrt keine eigenen HTTP-Aufrufe, kennt den Transport nicht und haelt keinen
15
+ * Zustand — genau deshalb ist sie austauschbar und einzeln testbar.
16
+ *
17
+ * Das Paket weiss **nichts** von Firebase: `registerUserAuth` bekommt eine
18
+ * Funktion, die ein gueltiges ID-Token liefert (darf `async` sein). Sie wird
19
+ * bei **jedem** Aufruf befragt — Firebase-ID-Tokens laufen nach einer Stunde
20
+ * ab, die Sitzung der Browser-Kasse lebt sogar nur 90 Sekunden und wird alle
21
+ * 30 Sekunden erneuert; ein einmal gemerkter Wert waere also bald tot.
22
+ */
23
+ import { KasseneckAuthError } from './errors.js';
24
+ /** Anmeldung per API-Schluessel und Kassen-Token (Geraete, POS-Apps, Dritte). */
25
+ export function apiKeyAuth(options) {
26
+ // Fehlende Zugangsdaten fallen hier auf und nicht erst als 401 vom Backend.
27
+ // Die Meldung nennt nur das leere Feld, nie einen Wert.
28
+ if (!options.apiKey) {
29
+ throw new KasseneckAuthError('apiKeyAuth: apiKey fehlt');
30
+ }
31
+ if (!options.cashregisterToken) {
32
+ throw new KasseneckAuthError('apiKeyAuth: cashregisterToken fehlt');
33
+ }
34
+ const { apiKey, cashregisterToken } = options;
35
+ // Pro Aufruf ein frisches Objekt: der Transport (oder ein Aufrufer) darf
36
+ // daran herumschreiben, ohne die naechste Anfrage zu vergiften.
37
+ return () => ({
38
+ headers: {
39
+ Authorization: `Bearer ${apiKey}`,
40
+ 'cashregister-token': cashregisterToken,
41
+ },
42
+ params: {},
43
+ });
44
+ }
45
+ /** Anmeldung eines Kassen-Benutzers der Browser-Kasse (ID-Token + Sitzung). */
46
+ export function registerUserAuth(options) {
47
+ if (!options.cashregisterId) {
48
+ throw new KasseneckAuthError('registerUserAuth: cashregisterId fehlt');
49
+ }
50
+ const { getIdToken, getSessionId, cashregisterId } = options;
51
+ return async () => {
52
+ // Beides bei JEDEM Aufruf frisch — siehe Modulkommentar (Ablaufzeiten).
53
+ const [idToken, sessionId] = await Promise.all([getIdToken(), getSessionId()]);
54
+ if (!idToken) {
55
+ throw new KasseneckAuthError('registerUserAuth: getIdToken lieferte kein Token');
56
+ }
57
+ if (!sessionId) {
58
+ throw new KasseneckAuthError('registerUserAuth: getSessionId lieferte keine Sitzung');
59
+ }
60
+ return {
61
+ headers: {
62
+ Authorization: `Bearer ${idToken}`,
63
+ 'register-session': sessionId,
64
+ },
65
+ params: { cashregisterId },
66
+ };
67
+ };
68
+ }
@@ -0,0 +1,21 @@
1
+ import { type Cashregister } from '../models/index.js';
2
+ import type { KasseneckTransport } from './transport.js';
3
+ /**
4
+ * Kassen-Endpunkte.
5
+ *
6
+ * **Anmeldeweg:** `listMyCashregisters` laeuft im Backend unter
7
+ * `checkRequest(req, 'customer', …, {allowRegisterUser: true})` — der Bearer
8
+ * muss also ein Firebase-ID-Token sein. Mit `apiKeyAuth` (api_key als Bearer)
9
+ * ist dieser Endpunkt **nicht** erreichbar; fuer die Browser-Kasse ist
10
+ * `registerUserAuth` der Weg. Dieses Paket bildet das nicht nach — wer darf,
11
+ * entscheidet allein das Backend. Der Hinweis steht hier, damit ein Leser
12
+ * nicht raten muss.
13
+ */
14
+ /**
15
+ * Die Kassen, die dem angemeldeten Benutzer offenstehen — fuer einen
16
+ * Kassen-Benutzer genau die ihm zugewiesenen (das Backend filtert, siehe
17
+ * `registerAuth.darfKasse`).
18
+ *
19
+ * Ohne diesen Aufruf kann eine Browser-Kasse ihre Kasse nicht auswaehlen.
20
+ */
21
+ export declare function listMyCashregisters(rufen: KasseneckTransport): Promise<Cashregister[]>;
@@ -0,0 +1,33 @@
1
+ import { fromCashregisterPayload } from '../models/index.js';
2
+ import { KasseneckValidationError } from './errors.js';
3
+ /**
4
+ * Kassen-Endpunkte.
5
+ *
6
+ * **Anmeldeweg:** `listMyCashregisters` laeuft im Backend unter
7
+ * `checkRequest(req, 'customer', …, {allowRegisterUser: true})` — der Bearer
8
+ * muss also ein Firebase-ID-Token sein. Mit `apiKeyAuth` (api_key als Bearer)
9
+ * ist dieser Endpunkt **nicht** erreichbar; fuer die Browser-Kasse ist
10
+ * `registerUserAuth` der Weg. Dieses Paket bildet das nicht nach — wer darf,
11
+ * entscheidet allein das Backend. Der Hinweis steht hier, damit ein Leser
12
+ * nicht raten muss.
13
+ */
14
+ /**
15
+ * Die Kassen, die dem angemeldeten Benutzer offenstehen — fuer einen
16
+ * Kassen-Benutzer genau die ihm zugewiesenen (das Backend filtert, siehe
17
+ * `registerAuth.darfKasse`).
18
+ *
19
+ * Ohne diesen Aufruf kann eine Browser-Kasse ihre Kasse nicht auswaehlen.
20
+ */
21
+ export async function listMyCashregisters(rufen) {
22
+ const daten = await rufen('listMyCashregisters');
23
+ const liste = daten?.cashregisters;
24
+ if (!Array.isArray(liste)) {
25
+ throw new KasseneckValidationError('listMyCashregisters', 'Antwort enthaelt keine Kassenliste (data.cashregisters fehlt)', 'response');
26
+ }
27
+ return liste.map((eintrag, i) => {
28
+ const nutzlast = (typeof eintrag === 'object' && eintrag !== null ? eintrag : {});
29
+ // Die Dokument-ID steht in dieser Antwort im Eintrag selbst; der zweite
30
+ // Parameter ist nur der Rueckfall (siehe fromCashregisterPayload).
31
+ return fromCashregisterPayload(nutzlast, nutzlast.id ?? String(i));
32
+ });
33
+ }
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Die fuenf Fehlerarten des Kasseneck-Clients — bewusst getrennt, weil ein
3
+ * Aufrufer auf jede anders reagieren muss:
4
+ *
5
+ * - `KasseneckValidationError` — die **Form** stimmt nicht: entweder ging
6
+ * mangels brauchbarer Eingabe gar keine Anfrage raus, oder die Antwort war
7
+ * nicht verwertbar. `scope` trennt die beiden Richtungen. `'request'`: die
8
+ * Eingabe des Aufrufers verletzt eine Regel, die dieses Paket schon vor dem
9
+ * Senden kennt (Beleg ohne Positionen, unmoegliche Gutscheinkombination) —
10
+ * das ist ein Programmierfehler im Aufrufer, kein Betriebszustand.
11
+ * `'response'`: das Backend meldete Erfolg, die Nutzlast enthielt aber
12
+ * nicht, was der Aufruf zusagt.
13
+ *
14
+ * - `KasseneckApiError` — **fachlicher** Fehler. Das Backend antwortet immer
15
+ * mit HTTP 200 und legt Erfolg/Misserfolg in den Rumpf
16
+ * (`{status:'success'|'error', message, data}`, siehe `successResponse`/
17
+ * `errorResponse` im Backend). Ein `status:'error'` heisst: die Anfrage kam
18
+ * an und wurde abgelehnt (gesperrte Kasse, fehlendes Modul, ungueltiger
19
+ * Parameter). Wiederholen hilft nicht.
20
+ * - `KasseneckHttpError` — die Antwort war **keine** verwertbare Huelle:
21
+ * HTTP 500/404, leerer Rumpf, oder HTTP 200 mit HTML statt JSON (typisch,
22
+ * wenn ein Aufruf mangels Rewrite auf der Single-Page-App landet). Beim
23
+ * Bericht-Download gelten dieselben Gruende fuer alles, was kein PDF ist.
24
+ * `reason` trennt die Faelle maschinenlesbar.
25
+ * - `KasseneckNetworkError` — die Antwort kam gar nicht: Netz weg, DNS,
26
+ * abgebrochene Verbindung oder Zeitueberschreitung (`timedOut`).
27
+ * - `KasseneckAuthError` — es kam nicht einmal zur Anfrage, weil die Anmeldung
28
+ * scheiterte (fehlende Zugangsdaten, oder der Token-/Sitzungsgeber warf).
29
+ * In der Browser-Kasse mit ihrer 90-Sekunden-Sitzung ist das Alltag, kein
30
+ * Sonderfall.
31
+ *
32
+ * **Geheimnisse gehoeren in keinen dieser Fehler.** Fehlermeldungen landen in
33
+ * Protokollen und Fehlerdiensten; weder `api_key`, Kassen-Token, Firebase-
34
+ * ID-Token noch Sitzungsbezeichner duerfen dorthin. Deshalb tragen die Fehler
35
+ * ausschliesslich Funktionsname, HTTP-Status/Inhaltstyp, den vom Paket
36
+ * formulierten Grund und die vom Backend formulierte Meldung — nie Kopfzeilen,
37
+ * nie den gesendeten Rumpf und auch nicht den empfangenen Rumpf (der koennte
38
+ * bei einem fremden Proxy alles Moegliche zurueckspiegeln).
39
+ *
40
+ * Aus demselben Grund haengt **keine fremde Ursache** als `cause` an diesen
41
+ * Fehlern: `console.error(err)` und `util.inspect` drucken die Ursachenkette
42
+ * mit, und fremde HTTP-Bibliotheken haengen ihre Anfrage an ihre Fehler (axios
43
+ * `config.headers`, got `options.headers`) — mit dem Bearer-Schluessel darin.
44
+ * Statt der Ursache selbst traegt `KasseneckNetworkError` ihre **verdichtete**
45
+ * Form: `causeName`/`causeCode`, beide nur, wenn sie wie ein Bezeichner
46
+ * aussehen **und** mit keinem der gesendeten Geheimnisse ueberlappen (siehe
47
+ * `causeDigest`).
48
+ *
49
+ * Der Formfilter allein traegt diese Zusage naemlich nicht: der
50
+ * Sitzungsbezeichner der Browser-Kasse ist selbst bezeichner-foermig und kaeme
51
+ * durch. Verdichtet werden darf nur, wo die gesendeten Geheimnisse bekannt
52
+ * sind — also nach dem Bauen der Anfrage. `KasseneckAuthError` traegt darum
53
+ * **gar keine** Ursachen-Verdichtung: dort ist noch nichts gesendet, es gibt
54
+ * keine Liste zum Abgleichen, und Diagnose ist an dieser Stelle weniger wert
55
+ * als die Zusage.
56
+ */
57
+ /** Verdichtete, geheimnisfreie Form einer fremden Fehlerursache. */
58
+ export interface CauseDigest {
59
+ causeName?: string | undefined;
60
+ causeCode?: string | undefined;
61
+ }
62
+ /**
63
+ * Reduziert eine fremde Ursache auf Name und Code — und verwirft beides, wenn
64
+ * es kein Bezeichner ist oder mit einem der uebergebenen Geheimnisse
65
+ * ueberlappt (ein Sitzungsbezeichner sieht durchaus bezeichner-artig aus).
66
+ *
67
+ * `geheimnisse` hat bewusst **keinen** Vorgabewert: eine Verdichtung ohne
68
+ * Abgleichliste waere nur ein Formfilter und damit keine Zusage. Wo die
69
+ * gesendeten Werte nicht bekannt sind, wird gar nicht verdichtet.
70
+ */
71
+ export declare function causeDigest(ursache: unknown, geheimnisse: readonly string[]): CauseDigest;
72
+ /** Fachlicher Fehler: HTTP 200, aber `status: 'error'` im Rumpf. */
73
+ export declare class KasseneckApiError extends Error {
74
+ readonly name = "KasseneckApiError";
75
+ /** Aufgerufene Backend-Funktion, z. B. `createReceipt`. */
76
+ readonly functionName: string;
77
+ /** Meldung des Backends, unveraendert (`message` aus der Huelle). */
78
+ readonly serverMessage: string;
79
+ constructor(functionName: string, serverMessage: string);
80
+ }
81
+ /** Warum die Antwort keine verwertbare Huelle war. */
82
+ export type HttpFailureReason = 'server-error' | 'empty-body' | 'not-json' | 'missing-status';
83
+ /** Antwort ohne verwertbare Huelle (HTTP-Fehler, leerer Rumpf, HTML statt JSON). */
84
+ export declare class KasseneckHttpError extends Error {
85
+ readonly name = "KasseneckHttpError";
86
+ readonly functionName: string;
87
+ /** HTTP-Statuscode der Antwort (bei HTML-statt-JSON durchaus 200). */
88
+ readonly statusCode: number;
89
+ /** Inhaltstyp der Antwort, falls die Gegenstelle einen gesetzt hat. */
90
+ readonly contentType: string | undefined;
91
+ /** Maschinenlesbarer Grund — trennt den Rewrite-Fall vom 500er ohne Textparsen. */
92
+ readonly reason: HttpFailureReason;
93
+ constructor(functionName: string, statusCode: number, contentType: string | undefined, reason: HttpFailureReason);
94
+ }
95
+ /** Es kam keine Antwort: Netzfehler oder Zeitueberschreitung. */
96
+ export declare class KasseneckNetworkError extends Error {
97
+ readonly name = "KasseneckNetworkError";
98
+ readonly functionName: string;
99
+ /** true = das Zeitlimit lief ab und die Anfrage wurde abgebrochen. */
100
+ readonly timedOut: boolean;
101
+ /** Das geltende Zeitlimit in Millisekunden. */
102
+ readonly timeoutMs: number;
103
+ /** Name der zugrunde liegenden Ursache, sofern unbedenklich (s. `causeDigest`). */
104
+ readonly causeName: string | undefined;
105
+ /** Code der zugrunde liegenden Ursache, sofern unbedenklich (z. B. `ECONNREFUSED`). */
106
+ readonly causeCode: string | undefined;
107
+ constructor(functionName: string, timedOut: boolean, timeoutMs: number, ursache?: CauseDigest);
108
+ }
109
+ /**
110
+ * Die Anmeldung scheiterte — es ging keine Anfrage raus.
111
+ *
112
+ * Dieser Fehler traegt **nichts** aus der fremden Ursache: weder ihre Meldung
113
+ * (ein Token-Geber fuehrt gern den Token mit, den er gerade nicht erneuern
114
+ * konnte) noch eine Verdichtung ihres Namens/Codes. Zum Zeitpunkt des
115
+ * Anmeldefehlers ist noch nichts gesendet, es gibt also keine Liste der
116
+ * Geheimnisse, gegen die verdichtete Werte geprueft werden koennten — und ein
117
+ * reiner Formfilter laesst genau die Form durch, die ein Sitzungsbezeichner
118
+ * hat.
119
+ */
120
+ export declare class KasseneckAuthError extends Error {
121
+ readonly name = "KasseneckAuthError";
122
+ /** Betroffene Backend-Funktion, falls der Fehler bei einem Aufruf entstand. */
123
+ readonly functionName: string | undefined;
124
+ /** Vom Paket formulierter Grund — geheimnisfrei, anders als fremde Meldungen. */
125
+ readonly reason: string;
126
+ constructor(reason: string, options?: {
127
+ functionName?: string;
128
+ });
129
+ }
130
+ /** Woran die Form haengt: an der Eingabe des Aufrufers oder an der Antwort. */
131
+ export type ValidationScope = 'request' | 'response';
132
+ /**
133
+ * Die Form stimmt nicht — bei `scope: 'request'` ging deshalb keine Anfrage
134
+ * raus, bei `scope: 'response'` kam eine unbrauchbare zurueck.
135
+ *
136
+ * Es gilt dieselbe Zusage wie fuer die uebrigen Fehlerarten: `reason` ist vom
137
+ * Paket formuliert und geheimnisfrei. Bei `'response'` wird insbesondere
138
+ * **nichts aus der Antwort** uebernommen — ein fremder Proxy koennte dort
139
+ * alles Moegliche zurueckspiegeln.
140
+ */
141
+ export declare class KasseneckValidationError extends Error {
142
+ readonly name = "KasseneckValidationError";
143
+ /**
144
+ * Betroffener Vorgang — meist die Backend-Funktion, bei Pruefungen ohne
145
+ * Aufruf der Name der Paketfunktion (z. B. `buildReceiptLayout`).
146
+ */
147
+ readonly functionName: string;
148
+ /** Vom Paket formulierter Grund. */
149
+ readonly reason: string;
150
+ readonly scope: ValidationScope;
151
+ constructor(functionName: string, reason: string, scope: ValidationScope);
152
+ }
153
+ /** Alle Fehler, die dieses Paket wirft. */
154
+ export type KasseneckError = KasseneckApiError | KasseneckHttpError | KasseneckNetworkError | KasseneckAuthError | KasseneckValidationError;
155
+ export declare function isKasseneckApiError(fehler: unknown): fehler is KasseneckApiError;
156
+ export declare function isKasseneckHttpError(fehler: unknown): fehler is KasseneckHttpError;
157
+ export declare function isKasseneckNetworkError(fehler: unknown): fehler is KasseneckNetworkError;
158
+ export declare function isKasseneckAuthError(fehler: unknown): fehler is KasseneckAuthError;
159
+ export declare function isKasseneckValidationError(fehler: unknown): fehler is KasseneckValidationError;