@kreiseck/kasseneck-api 1.7.1 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/CHANGELOG.md +113 -0
  2. package/README.md +170 -1
  3. package/dist/cjs/client/api.d.ts +5 -3
  4. package/dist/cjs/client/api.js +1 -0
  5. package/dist/cjs/client/aufrufe.d.ts +9 -7
  6. package/dist/cjs/client/aufrufe.js +60 -2
  7. package/dist/cjs/client/errors.js +3 -0
  8. package/dist/cjs/client/index.d.ts +2 -2
  9. package/dist/cjs/client/index.js +3 -1
  10. package/dist/cjs/client/reports.d.ts +26 -0
  11. package/dist/cjs/client/reports.js +47 -0
  12. package/dist/cjs/client/transport.d.ts +27 -3
  13. package/dist/cjs/client/transport.js +29 -3
  14. package/dist/cjs/index.d.ts +1 -1
  15. package/dist/cjs/index.js +4 -2
  16. package/dist/cjs/inventory/api.d.ts +21 -2
  17. package/dist/cjs/inventory/api.js +22 -3
  18. package/dist/cjs/inventory/fehler.d.ts +6 -0
  19. package/dist/cjs/inventory/fehler.js +12 -0
  20. package/dist/cjs/inventory/index.d.ts +6 -4
  21. package/dist/cjs/inventory/index.js +33 -3
  22. package/dist/cjs/inventory/inventur.d.ts +104 -0
  23. package/dist/cjs/inventory/inventur.js +245 -0
  24. package/dist/cjs/inventory/lesen.d.ts +20 -1
  25. package/dist/cjs/inventory/lesen.js +307 -0
  26. package/dist/cjs/inventory/typen.d.ts +410 -1
  27. package/dist/cjs/inventory/vertrag.d.ts +94 -8
  28. package/dist/cjs/inventory/vertrag.js +118 -5
  29. package/dist/cjs/invoice/index.d.ts +1 -1
  30. package/dist/cjs/invoice/typen.d.ts +12 -0
  31. package/dist/cjs/invoice/vertrag.d.ts +1 -1
  32. package/dist/cjs/invoice/vertrag.js +3 -0
  33. package/dist/cjs/models/cancellation.d.ts +1 -1
  34. package/dist/cjs/models/cancellation.js +3 -0
  35. package/dist/cjs/models/payment-errors.d.ts +1 -1
  36. package/dist/cjs/models/payment-errors.js +3 -0
  37. package/dist/cjs/models/receipt-email.d.ts +1 -1
  38. package/dist/cjs/models/receipt-email.js +3 -0
  39. package/dist/cjs/models/receipt-errors.d.ts +1 -1
  40. package/dist/cjs/models/receipt-errors.js +3 -0
  41. package/dist/cjs/pos/errors.d.ts +4 -3
  42. package/dist/cjs/pos/errors.js +24 -2
  43. package/dist/cjs/pos/index.d.ts +4 -1
  44. package/dist/cjs/pos/index.js +9 -2
  45. package/dist/cjs/pos/inventur.d.ts +104 -0
  46. package/dist/cjs/pos/inventur.js +117 -0
  47. package/dist/cjs/pos/texte.d.ts +82 -0
  48. package/dist/cjs/pos/texte.js +35 -0
  49. package/dist/cjs/register/errors.d.ts +1 -1
  50. package/dist/cjs/register/errors.js +3 -0
  51. package/dist/cjs/stored/vokabular.d.ts +1 -1
  52. package/dist/cjs/stored/vokabular.js +1 -1
  53. package/dist/cjs/version.d.ts +1 -1
  54. package/dist/cjs/version.js +1 -1
  55. package/dist/esm/client/api.d.ts +5 -3
  56. package/dist/esm/client/api.js +2 -1
  57. package/dist/esm/client/aufrufe.d.ts +9 -7
  58. package/dist/esm/client/aufrufe.js +60 -2
  59. package/dist/esm/client/errors.js +3 -0
  60. package/dist/esm/client/index.d.ts +2 -2
  61. package/dist/esm/client/index.js +2 -2
  62. package/dist/esm/client/reports.d.ts +26 -0
  63. package/dist/esm/client/reports.js +46 -0
  64. package/dist/esm/client/transport.d.ts +27 -3
  65. package/dist/esm/client/transport.js +28 -3
  66. package/dist/esm/index.d.ts +1 -1
  67. package/dist/esm/index.js +1 -1
  68. package/dist/esm/inventory/api.d.ts +21 -2
  69. package/dist/esm/inventory/api.js +23 -4
  70. package/dist/esm/inventory/fehler.d.ts +6 -0
  71. package/dist/esm/inventory/fehler.js +11 -0
  72. package/dist/esm/inventory/index.d.ts +6 -4
  73. package/dist/esm/inventory/index.js +5 -3
  74. package/dist/esm/inventory/inventur.d.ts +104 -0
  75. package/dist/esm/inventory/inventur.js +226 -0
  76. package/dist/esm/inventory/lesen.d.ts +20 -1
  77. package/dist/esm/inventory/lesen.js +301 -0
  78. package/dist/esm/inventory/typen.d.ts +410 -1
  79. package/dist/esm/inventory/vertrag.d.ts +94 -8
  80. package/dist/esm/inventory/vertrag.js +117 -4
  81. package/dist/esm/invoice/index.d.ts +1 -1
  82. package/dist/esm/invoice/typen.d.ts +12 -0
  83. package/dist/esm/invoice/vertrag.d.ts +1 -1
  84. package/dist/esm/invoice/vertrag.js +3 -0
  85. package/dist/esm/models/cancellation.d.ts +1 -1
  86. package/dist/esm/models/cancellation.js +3 -0
  87. package/dist/esm/models/payment-errors.d.ts +1 -1
  88. package/dist/esm/models/payment-errors.js +3 -0
  89. package/dist/esm/models/receipt-email.d.ts +1 -1
  90. package/dist/esm/models/receipt-email.js +3 -0
  91. package/dist/esm/models/receipt-errors.d.ts +1 -1
  92. package/dist/esm/models/receipt-errors.js +3 -0
  93. package/dist/esm/pos/errors.d.ts +4 -3
  94. package/dist/esm/pos/errors.js +24 -2
  95. package/dist/esm/pos/index.d.ts +4 -1
  96. package/dist/esm/pos/index.js +2 -1
  97. package/dist/esm/pos/inventur.d.ts +104 -0
  98. package/dist/esm/pos/inventur.js +109 -0
  99. package/dist/esm/pos/texte.d.ts +82 -0
  100. package/dist/esm/pos/texte.js +35 -0
  101. package/dist/esm/register/errors.d.ts +1 -1
  102. package/dist/esm/register/errors.js +3 -0
  103. package/dist/esm/stored/vokabular.d.ts +1 -1
  104. package/dist/esm/stored/vokabular.js +1 -1
  105. package/dist/esm/version.d.ts +1 -1
  106. package/dist/esm/version.js +1 -1
  107. package/fixtures/hobex-hps-codes.json +1 -1
  108. package/fixtures/invoice-api.schema.json +1 -1
  109. package/fixtures/invoice-texts.json +1 -1
  110. package/fixtures/pos-texts.json +94 -1
  111. package/fixtures/stocktake-quantity-cases.json +55 -0
  112. package/fixtures/surface.json +157 -8
  113. package/fixtures/v3/antworten/belege.json +1 -1
  114. package/fixtures/v3/antworten/belegmail.json +1 -1
  115. package/fixtures/v3/antworten/kasse-belege.json +1 -1
  116. package/fixtures/v3/antworten/kasse.json +2958 -3
  117. package/fixtures/v3/antworten/lager.json +1727 -3
  118. package/fixtures/v3/antworten/rechnungen.json +14 -6
  119. package/fixtures/v3/antworten/storno.json +1 -1
  120. package/fixtures/v3/stored/belege.json +1 -1
  121. package/fixtures/v3/stored/kasse.json +414 -3
  122. package/fixtures/v3/stored/rechnungen.json +1 -1
  123. package/fixtures/v3/v3-vokabular.json +530 -8
  124. package/fixtures/v3/zahlbetrag-faelle.json +1 -1
  125. package/package.json +1 -1
@@ -0,0 +1,109 @@
1
+ import { KasseneckValidationError } from '../client/errors.js';
2
+ import { stornoPruefen, zaehlungPruefen } from '../inventory/inventur.js';
3
+ import { inventur, inventurPosition, inventurZaehlung, liste, naechsterCursor, zaehlungMitPosition } from '../inventory/lesen.js';
4
+ import { anfrage, schluessel } from '../inventory/schreiben.js';
5
+ import { quantityRuleForUnit } from './artikel.js';
6
+ function anfragefehler(name, grund) {
7
+ return new KasseneckValidationError(name, grund, 'request');
8
+ }
9
+ /** Eine Kennung, die gesendet werden muss. */
10
+ function kennung(name, feld, w) {
11
+ if (typeof w !== 'string' || w.trim() === '')
12
+ throw anfragefehler(name, `${feld} fehlt`);
13
+ return w;
14
+ }
15
+ /** Die Optionen flach, ohne `undefined`; Pflichtkennung `stocktakeId`. */
16
+ function mitInventur(name, options) {
17
+ const p = anfrage(name, options);
18
+ kennung(name, 'stocktakeId', p.stocktakeId);
19
+ return p;
20
+ }
21
+ /** Offene Inventuren (fuer Kassen-Benutzer nur `counting` und `review` mit offenem Nachzaehlen). */
22
+ export async function listMyStocktakes(transport, options = {}) {
23
+ const name = 'listMyStocktakes';
24
+ const p = anfrage(name, options);
25
+ if (p.locationId === '')
26
+ delete p.locationId;
27
+ const daten = await transport(name, p);
28
+ return liste(name, daten, 'stocktakes', inventur);
29
+ }
30
+ /** Positionen einer Inventur; ab der Pruefung mit Soll nur mit dem Recht `stocktakeClose`. */
31
+ export async function listMyStocktakeItems(transport, options) {
32
+ const name = 'listMyStocktakeItems';
33
+ const daten = await transport(name, mitInventur(name, options));
34
+ return { items: liste(name, daten, 'items', inventurPosition), nextCursor: naechsterCursor(name, daten) };
35
+ }
36
+ /** Zaehlungen einer Inventur, neueste zuerst; `ownOnly` nur die eigenen. */
37
+ export async function listMyStocktakeCounts(transport, options) {
38
+ const name = 'listMyStocktakeCounts';
39
+ const daten = await transport(name, mitInventur(name, options));
40
+ return { counts: liste(name, daten, 'counts', inventurZaehlung), nextCursor: naechsterCursor(name, daten) };
41
+ }
42
+ /** Eine Zaehlung; Antwort: die Zaehlung und die Position danach (Ist-Summe, kein Soll). */
43
+ export async function recordMyStocktakeCount(transport, options) {
44
+ const name = 'recordMyStocktakeCount';
45
+ const p = mitInventur(name, options);
46
+ schluessel(name, p, { pflicht: true });
47
+ zaehlungPruefen(name, p);
48
+ if (p.cashregisterId !== undefined)
49
+ kennung(name, 'cashregisterId', p.cashregisterId);
50
+ return zaehlungMitPosition(name, await transport(name, p));
51
+ }
52
+ /** Storniert eine Zaehlung mit Grund (fremde nur mit dem Recht `stocktakeClose`). */
53
+ export async function voidMyStocktakeCount(transport, options) {
54
+ const name = 'voidMyStocktakeCount';
55
+ const p = mitInventur(name, options);
56
+ schluessel(name, p, { pflicht: true });
57
+ stornoPruefen(name, p);
58
+ return zaehlungMitPosition(name, await transport(name, p));
59
+ }
60
+ /** Tausendstel je Einheit: drei Nachkommastellen, wie die Mengen am Draht. */
61
+ const STELLEN = 3;
62
+ /**
63
+ * Eine eingetippte Menge in Tausendstel der Basiseinheit, **ohne Gleitkomma**:
64
+ * `'12'` → `12000`, `'0,25'` → `250`, `'1.5'` → `1500`.
65
+ *
66
+ * - Dezimaltrenner Komma oder Punkt, hoechstens drei Nachkommastellen; weitere
67
+ * Nullen am Ende zaehlen nicht (`'1,2340'` → `1234`). `'0'` ist eine gueltige
68
+ * Menge (leer gezaehlt).
69
+ * - **Punkt mit genau drei Ziffern danach und einem Ganzteil ungleich 0**
70
+ * (`'1.000'`, `'12.500'`) ist bei jeder Einheit `null`: in oesterreichischer
71
+ * Schreibweise ist das ein Tausenderpunkt („tausend“), am Ziffernblock ein
72
+ * Dezimalpunkt („eins“); ein Faktor 1000 buchte der Abschluss als Differenz.
73
+ * Mit Komma ist es eindeutig (`'1,000'` → `1000`), ebenso `'0.500'`,
74
+ * `'1.5'`, `'1.25'`.
75
+ * - **Stueckware** nur als ganze Zahl ohne Trenner. Stueckware ist, was
76
+ * `rule` sagt (die gespeicherte Mengenregel des Artikels,
77
+ * `PosArticle.quantityRule`, `'piece'` | `'decimal'`), ohne `rule` die
78
+ * Vorgabe der Einheit (`quantityRuleForUnit`: Stk, g, ml …, auch ohne
79
+ * Einheit). Einzelstuecke (Seriennummer) sind immer Stueckware: dann
80
+ * `'piece'` uebergeben.
81
+ *
82
+ * `null` auch fuer: leer, Vorzeichen, Tausenderleerzeichen, Exponent, mehr als
83
+ * drei Nachkommastellen, groesser als eine sichere Ganzzahl. Die Kasse zeigt
84
+ * dann ihren Satz (`stocktake.quantity_invalid`), statt still zu runden.
85
+ * Gemeinsame Prueffaelle mit dem Dart-Zwilling:
86
+ * `fixtures/stocktake-quantity-cases.json`.
87
+ */
88
+ export function parseQuantityMilli(text, unit, rule) {
89
+ if (typeof text !== 'string')
90
+ return null;
91
+ const m = /^(\d*)(?:([.,])(\d*))?$/.exec(text.trim());
92
+ if (!m)
93
+ return null;
94
+ const ganz = m[1] ?? '';
95
+ const trenner = m[2];
96
+ const roh = m[3] ?? '';
97
+ if (ganz === '' && roh === '')
98
+ return null;
99
+ const stueck = (rule === 'piece' || rule === 'decimal' ? rule : quantityRuleForUnit(unit).rule) === 'piece';
100
+ if (trenner !== undefined && stueck)
101
+ return null;
102
+ if (trenner === '.' && roh.length === 3 && /[1-9]/.test(ganz))
103
+ return null;
104
+ const nachkomma = roh.replace(/0+$/, '');
105
+ if (nachkomma.length > STELLEN)
106
+ return null;
107
+ const milli = BigInt(ganz === '' ? '0' : ganz) * 1000n + BigInt(nachkomma.padEnd(STELLEN, '0'));
108
+ return milli <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(milli) : null;
109
+ }
@@ -555,6 +555,42 @@ declare const MELDUNGEN_ROH: {
555
555
  readonly 'logo.remove_failed': {
556
556
  readonly text: "Entfernen fehlgeschlagen.";
557
557
  };
558
+ readonly 'stocktake.none_open': {
559
+ readonly text: "An diesem Standort läuft gerade keine Inventur. Eine Inventur legt der Inhaber im Panel an.";
560
+ };
561
+ readonly 'stocktake.scan_or_search': {
562
+ readonly text: "Artikel scannen oder suchen.";
563
+ };
564
+ readonly 'stocktake.counted': {
565
+ readonly text: "{name}: {quantity} gezählt.";
566
+ readonly placeholders: readonly ["name", "quantity"];
567
+ };
568
+ readonly 'stocktake.blind_hint': {
569
+ readonly text: "Blind zählen – die Kasse zeigt keinen Buchbestand. Bitte zählen, was wirklich da ist.";
570
+ };
571
+ readonly 'stocktake.recount_hint': {
572
+ readonly text: "Bitte die markierten Positionen nachzählen.";
573
+ };
574
+ readonly 'stocktake.unknown_code': {
575
+ readonly text: "Zu „{code}“ gibt es keinen Artikel. Bitte den Code prüfen oder den Artikel suchen.";
576
+ readonly placeholders: readonly ["code"];
577
+ };
578
+ readonly 'stocktake.quantity_invalid': {
579
+ readonly text: "Bitte eine gültige Menge eingeben – höchstens drei Nachkommastellen, bei Stückware nur ganze Zahlen.";
580
+ };
581
+ readonly 'stocktake.serials_capture': {
582
+ readonly text: "Bitte für jedes Stück die Seriennummer scannen.";
583
+ };
584
+ readonly 'stocktake.serials_mismatch': {
585
+ readonly text: "Je Stück genau eine Seriennummer: {count} erfasst, {quantity} gezählt.";
586
+ readonly placeholders: readonly ["count", "quantity"];
587
+ };
588
+ readonly 'stocktake.reason_missing': {
589
+ readonly text: "Bitte einen Grund für das Stornieren eingeben.";
590
+ };
591
+ readonly 'stocktake.outcome_unknown': {
592
+ readonly text: "Unklar, ob die Zählung angekommen ist. „Erneut senden“ schickt dieselbe Zählung – sie zählt auch dann nur einmal.";
593
+ };
558
594
  readonly 'app.not_in_browser': {
559
595
  readonly text: "Die Kassen-App läuft nicht im Browser – dafür gibt es kasse.kasseneck.at.";
560
596
  readonly only: readonly ["app"];
@@ -1050,6 +1086,52 @@ declare const BESCHRIFTUNGEN_ROH: {
1050
1086
  readonly 'stock.return_disposed': {
1051
1087
  readonly text: "Entsorgt";
1052
1088
  };
1089
+ readonly 'stocktake.title': {
1090
+ readonly text: "Inventur zählen";
1091
+ };
1092
+ readonly 'stocktake.quantity': {
1093
+ readonly text: "Menge";
1094
+ };
1095
+ readonly 'stocktake.defective': {
1096
+ readonly text: "Defekt";
1097
+ };
1098
+ readonly 'stocktake.count_zero': {
1099
+ readonly text: "0 zählen";
1100
+ };
1101
+ readonly 'stocktake.next': {
1102
+ readonly text: "Weiter";
1103
+ };
1104
+ readonly 'stocktake.void': {
1105
+ readonly text: "Stornieren";
1106
+ };
1107
+ readonly 'stocktake.reason': {
1108
+ readonly text: "Grund";
1109
+ };
1110
+ readonly 'stocktake.progress': {
1111
+ readonly text: "{counted} von {total} gezählt";
1112
+ readonly placeholders: readonly ["counted", "total"];
1113
+ };
1114
+ readonly 'stocktake.serial_numbers': {
1115
+ readonly text: "Seriennummern erfassen";
1116
+ };
1117
+ readonly 'stocktake.recount': {
1118
+ readonly text: "Nachzählen";
1119
+ };
1120
+ readonly 'stocktake.resend': {
1121
+ readonly text: "Erneut senden";
1122
+ };
1123
+ readonly 'stocktake.my_counts': {
1124
+ readonly text: "Meine Zählungen";
1125
+ };
1126
+ readonly 'stocktake.sent': {
1127
+ readonly text: "gesendet";
1128
+ };
1129
+ readonly 'stocktake.unconfirmed': {
1130
+ readonly text: "unbestätigt";
1131
+ };
1132
+ readonly 'stocktake.voided': {
1133
+ readonly text: "storniert";
1134
+ };
1053
1135
  };
1054
1136
  export type LabelKey = keyof typeof BESCHRIFTUNGEN_ROH;
1055
1137
  export declare const LABELS: Record<LabelKey, TextEntry>;
@@ -282,6 +282,22 @@ const MELDUNGEN_ROH = {
282
282
  'unpairing.failed': { text: 'Entkoppeln fehlgeschlagen.' },
283
283
  'logo.upload_failed': { text: 'Hochladen fehlgeschlagen.' },
284
284
  'logo.remove_failed': { text: 'Entfernen fehlgeschlagen.' },
285
+ // --- Inventur zählen ------------------------------------------------------
286
+ // Gezählt wird blind und nur online; bei unklarem Ausgang schickt
287
+ // „Erneut senden“ dieselbe Zählung mit demselben Schlüssel (sie zählt genau
288
+ // einmal), nie eine neue. `{quantity}` ist die Menge samt Einheit („12 Stk“),
289
+ // `{count}` die Zahl der erfassten Seriennummern.
290
+ 'stocktake.none_open': { text: 'An diesem Standort läuft gerade keine Inventur. Eine Inventur legt der Inhaber im Panel an.' },
291
+ 'stocktake.scan_or_search': { text: 'Artikel scannen oder suchen.' },
292
+ 'stocktake.counted': { text: '{name}: {quantity} gezählt.', placeholders: ['name', 'quantity'] },
293
+ 'stocktake.blind_hint': { text: 'Blind zählen – die Kasse zeigt keinen Buchbestand. Bitte zählen, was wirklich da ist.' },
294
+ 'stocktake.recount_hint': { text: 'Bitte die markierten Positionen nachzählen.' },
295
+ 'stocktake.unknown_code': { text: 'Zu „{code}“ gibt es keinen Artikel. Bitte den Code prüfen oder den Artikel suchen.', placeholders: ['code'] },
296
+ 'stocktake.quantity_invalid': { text: 'Bitte eine gültige Menge eingeben – höchstens drei Nachkommastellen, bei Stückware nur ganze Zahlen.' },
297
+ 'stocktake.serials_capture': { text: 'Bitte für jedes Stück die Seriennummer scannen.' },
298
+ 'stocktake.serials_mismatch': { text: 'Je Stück genau eine Seriennummer: {count} erfasst, {quantity} gezählt.', placeholders: ['count', 'quantity'] },
299
+ 'stocktake.reason_missing': { text: 'Bitte einen Grund für das Stornieren eingeben.' },
300
+ 'stocktake.outcome_unknown': { text: 'Unklar, ob die Zählung angekommen ist. „Erneut senden“ schickt dieselbe Zählung – sie zählt auch dann nur einmal.' },
285
301
  // --- Nur App -------------------------------------------------------------
286
302
  'app.not_in_browser': { text: 'Die Kassen-App läuft nicht im Browser – dafür gibt es kasse.kasseneck.at.', only: ['app'] },
287
303
  'app.open_in_browser': { text: 'Bitte im Browser öffnen: {target}', placeholders: ['target'], only: ['app'] },
@@ -610,6 +626,25 @@ const BESCHRIFTUNGEN_ROH = {
610
626
  'stock.return_restock': { text: 'Zurück ins Lager' },
611
627
  'stock.return_defective': { text: 'Defekt' },
612
628
  'stock.return_disposed': { text: 'Entsorgt' },
629
+ // --- Inventur zählen ---------------------------------------------------------
630
+ // Knöpfe und Zeilen des Zählbildschirms. „gesendet“ und „unbestätigt“ stehen
631
+ // an den Zählungen der Sitzung (unbestätigt: unklarer Ausgang, „Erneut
632
+ // senden“ mit demselben Schlüssel).
633
+ 'stocktake.title': { text: 'Inventur zählen' },
634
+ 'stocktake.quantity': { text: 'Menge' },
635
+ 'stocktake.defective': { text: 'Defekt' },
636
+ 'stocktake.count_zero': { text: '0 zählen' },
637
+ 'stocktake.next': { text: 'Weiter' },
638
+ 'stocktake.void': { text: 'Stornieren' },
639
+ 'stocktake.reason': { text: 'Grund' },
640
+ 'stocktake.progress': { text: '{counted} von {total} gezählt', placeholders: ['counted', 'total'] },
641
+ 'stocktake.serial_numbers': { text: 'Seriennummern erfassen' },
642
+ 'stocktake.recount': { text: 'Nachzählen' },
643
+ 'stocktake.resend': { text: 'Erneut senden' },
644
+ 'stocktake.my_counts': { text: 'Meine Zählungen' },
645
+ 'stocktake.sent': { text: 'gesendet' },
646
+ 'stocktake.unconfirmed': { text: 'unbestätigt' },
647
+ 'stocktake.voided': { text: 'storniert' },
613
648
  };
614
649
  export const LABELS = BESCHRIFTUNGEN_ROH;
615
650
  /**
@@ -22,7 +22,7 @@ import { KasseneckApiError } from '../client/errors.js';
22
22
  * ueber `KasseneckApiError.code` lesbar; die Kasse braucht dafuer einen
23
23
  * Rueckfallzweig.
24
24
  */
25
- export declare const REGISTER_ERROR_CODES: readonly ["account_not_found", "admin_required", "api_not_approved", "cashregister_in_use", "cashregister_not_assigned", "cashregister_not_found", "cashregister_token_invalid", "cashregister_token_missing", "device_bound_elsewhere", "device_not_found", "device_not_paired", "device_takeover_required", "dialect_mismatch", "internal_translation_error", "licenses_exhausted", "live_not_enabled", "location_outside", "location_required", "login_failed", "login_mode_select_user", "login_unavailable", "method_not_allowed", "mfa_required", "not_found", "pairing_code_expired", "pairing_code_unknown", "pairing_code_used", "pairing_failed", "register_user_no_business", "register_user_not_allowed", "register_user_not_found", "register_user_only", "response_translation_failed", "session_ended", "session_expired", "session_not_running", "session_other_cashregister", "too_many_attempts", "unauthorized", "user_disabled", "user_verification_failed", "validation", "route_missing"];
25
+ export declare const REGISTER_ERROR_CODES: readonly ["account_not_found", "admin_required", "api_not_approved", "app_check_invalid", "app_check_missing", "cashregister_in_use", "cashregister_not_assigned", "cashregister_not_found", "cashregister_token_invalid", "cashregister_token_missing", "device_bound_elsewhere", "device_not_found", "device_not_paired", "device_takeover_required", "dialect_mismatch", "internal_translation_error", "licenses_exhausted", "live_not_enabled", "location_outside", "location_required", "login_failed", "login_mode_select_user", "login_unavailable", "method_not_allowed", "mfa_required", "not_found", "pairing_code_expired", "pairing_code_unknown", "pairing_code_used", "pairing_failed", "password_change_required", "register_user_no_business", "register_user_not_allowed", "register_user_not_found", "register_user_only", "response_translation_failed", "session_ended", "session_expired", "session_not_running", "session_other_cashregister", "too_many_attempts", "unauthorized", "user_disabled", "user_verification_failed", "validation", "route_missing"];
26
26
  export type RegisterErrorCode = typeof REGISTER_ERROR_CODES[number];
27
27
  export declare function isRegisterErrorCode(value: unknown): value is RegisterErrorCode;
28
28
  /** Der Code eines geworfenen Fehlers, wenn es einer der Kassen-Anmeldung ist; sonst `undefined`. */
@@ -27,6 +27,8 @@ export const REGISTER_ERROR_CODES = Object.freeze([
27
27
  'account_not_found',
28
28
  'admin_required',
29
29
  'api_not_approved',
30
+ 'app_check_invalid',
31
+ 'app_check_missing',
30
32
  'cashregister_in_use',
31
33
  'cashregister_not_assigned',
32
34
  'cashregister_not_found',
@@ -52,6 +54,7 @@ export const REGISTER_ERROR_CODES = Object.freeze([
52
54
  'pairing_code_unknown',
53
55
  'pairing_code_used',
54
56
  'pairing_failed',
57
+ 'password_change_required',
55
58
  'register_user_no_business',
56
59
  'register_user_not_allowed',
57
60
  'register_user_not_found',
@@ -1,5 +1,5 @@
1
1
  /** Fingerabdruck des Vertrags-Exports, aus dem diese Datei stammt (`_quelle.sha256`). */
2
- export declare const VOKABULAR_QUELLE = "6e6eae018f1e84116d3942cbfd35bc2d5d6f5c5e5551823c7ed88f15ed407744";
2
+ export declare const VOKABULAR_QUELLE = "4b0152e546f6320ecc6dad18156ac91cc176426c71c57ecbf21c1192a714ecda";
3
3
  /** Schema-Notation des Vertrags: Blatt 'aussen': 'innen', Objekt { __: 'innen', ... }, Liste [ { __: 'innen', ... } ]. */
4
4
  export type SchemaEintrag = string | SchemaObjekt | readonly [SchemaObjekt];
5
5
  export interface SchemaObjekt {
@@ -1,7 +1,7 @@
1
1
  // Erzeugt von scripts/stored-vokabular.mjs aus fixtures/v3/v3-vokabular.json.
2
2
  // Nicht von Hand aendern: test/stored.test.ts vergleicht mit dem Vertrag.
3
3
  /** Fingerabdruck des Vertrags-Exports, aus dem diese Datei stammt (`_quelle.sha256`). */
4
- export const VOKABULAR_QUELLE = "6e6eae018f1e84116d3942cbfd35bc2d5d6f5c5e5551823c7ed88f15ed407744";
4
+ export const VOKABULAR_QUELLE = "4b0152e546f6320ecc6dad18156ac91cc176426c71c57ecbf21c1192a714ecda";
5
5
  /** Schema je Endpunkt (aussen -> innen) und Wertverweise auf die Kataloge. */
6
6
  export const SCHEMAS = {
7
7
  "getReceipt": {
@@ -4,4 +4,4 @@
4
4
  * Client-Version, Nachtrag §6). Ein Test haelt sie gleich mit `package.json`;
5
5
  * beim Anheben der Version beide Stellen aendern.
6
6
  */
7
- export declare const PACKAGE_VERSION = "1.7.1";
7
+ export declare const PACKAGE_VERSION = "1.10.0";
@@ -4,4 +4,4 @@
4
4
  * Client-Version, Nachtrag §6). Ein Test haelt sie gleich mit `package.json`;
5
5
  * beim Anheben der Version beide Stellen aendern.
6
6
  */
7
- export const PACKAGE_VERSION = '1.7.1';
7
+ export const PACKAGE_VERSION = '1.10.0';
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.7.1",
2
+ "version": "1.10.0",
3
3
  "measuredOn": {
4
4
  "tid": "3600335",
5
5
  "hpsVersion": "1.10.0",
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "title": "Kasseneck Rechnungs-API",
4
4
  "version": 2,
5
- "package": "1.7.1",
5
+ "package": "1.10.0",
6
6
  "endpoints": {
7
7
  "createCustomer": {
8
8
  "request": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.7.1",
2
+ "version": "1.10.0",
3
3
  "languages": [
4
4
  "de",
5
5
  "en"
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.7.1",
2
+ "version": "1.10.0",
3
3
  "messages": {
4
4
  "network.no_connection": {
5
5
  "text": "Keine Verbindung zum Server. Bitte die Internetverbindung prüfen und erneut versuchen."
@@ -666,6 +666,50 @@
666
666
  "logo.remove_failed": {
667
667
  "text": "Entfernen fehlgeschlagen."
668
668
  },
669
+ "stocktake.none_open": {
670
+ "text": "An diesem Standort läuft gerade keine Inventur. Eine Inventur legt der Inhaber im Panel an."
671
+ },
672
+ "stocktake.scan_or_search": {
673
+ "text": "Artikel scannen oder suchen."
674
+ },
675
+ "stocktake.counted": {
676
+ "text": "{name}: {quantity} gezählt.",
677
+ "placeholders": [
678
+ "name",
679
+ "quantity"
680
+ ]
681
+ },
682
+ "stocktake.blind_hint": {
683
+ "text": "Blind zählen – die Kasse zeigt keinen Buchbestand. Bitte zählen, was wirklich da ist."
684
+ },
685
+ "stocktake.recount_hint": {
686
+ "text": "Bitte die markierten Positionen nachzählen."
687
+ },
688
+ "stocktake.unknown_code": {
689
+ "text": "Zu „{code}“ gibt es keinen Artikel. Bitte den Code prüfen oder den Artikel suchen.",
690
+ "placeholders": [
691
+ "code"
692
+ ]
693
+ },
694
+ "stocktake.quantity_invalid": {
695
+ "text": "Bitte eine gültige Menge eingeben – höchstens drei Nachkommastellen, bei Stückware nur ganze Zahlen."
696
+ },
697
+ "stocktake.serials_capture": {
698
+ "text": "Bitte für jedes Stück die Seriennummer scannen."
699
+ },
700
+ "stocktake.serials_mismatch": {
701
+ "text": "Je Stück genau eine Seriennummer: {count} erfasst, {quantity} gezählt.",
702
+ "placeholders": [
703
+ "count",
704
+ "quantity"
705
+ ]
706
+ },
707
+ "stocktake.reason_missing": {
708
+ "text": "Bitte einen Grund für das Stornieren eingeben."
709
+ },
710
+ "stocktake.outcome_unknown": {
711
+ "text": "Unklar, ob die Zählung angekommen ist. „Erneut senden“ schickt dieselbe Zählung – sie zählt auch dann nur einmal."
712
+ },
669
713
  "app.not_in_browser": {
670
714
  "text": "Die Kassen-App läuft nicht im Browser – dafür gibt es kasse.kasseneck.at.",
671
715
  "only": [
@@ -1106,6 +1150,55 @@
1106
1150
  },
1107
1151
  "stock.return_disposed": {
1108
1152
  "text": "Entsorgt"
1153
+ },
1154
+ "stocktake.title": {
1155
+ "text": "Inventur zählen"
1156
+ },
1157
+ "stocktake.quantity": {
1158
+ "text": "Menge"
1159
+ },
1160
+ "stocktake.defective": {
1161
+ "text": "Defekt"
1162
+ },
1163
+ "stocktake.count_zero": {
1164
+ "text": "0 zählen"
1165
+ },
1166
+ "stocktake.next": {
1167
+ "text": "Weiter"
1168
+ },
1169
+ "stocktake.void": {
1170
+ "text": "Stornieren"
1171
+ },
1172
+ "stocktake.reason": {
1173
+ "text": "Grund"
1174
+ },
1175
+ "stocktake.progress": {
1176
+ "text": "{counted} von {total} gezählt",
1177
+ "placeholders": [
1178
+ "counted",
1179
+ "total"
1180
+ ]
1181
+ },
1182
+ "stocktake.serial_numbers": {
1183
+ "text": "Seriennummern erfassen"
1184
+ },
1185
+ "stocktake.recount": {
1186
+ "text": "Nachzählen"
1187
+ },
1188
+ "stocktake.resend": {
1189
+ "text": "Erneut senden"
1190
+ },
1191
+ "stocktake.my_counts": {
1192
+ "text": "Meine Zählungen"
1193
+ },
1194
+ "stocktake.sent": {
1195
+ "text": "gesendet"
1196
+ },
1197
+ "stocktake.unconfirmed": {
1198
+ "text": "unbestätigt"
1199
+ },
1200
+ "stocktake.voided": {
1201
+ "text": "storniert"
1109
1202
  }
1110
1203
  },
1111
1204
  "returnDispositionLabels": {
@@ -0,0 +1,55 @@
1
+ {
2
+ "_note": "Prueffaelle parseQuantityMilli (Inventur zaehlen an der Kasse): eingetippter Text, Einheit und gespeicherte Mengenregel des Artikels (rule: piece | decimal | null) -> Tausendstel der Basiseinheit oder null. Stueckware (rule, ohne rule die Vorgabe der Einheit nach quantityRuleForUnit, auch ohne Einheit) nur ganze Zahlen ohne Trenner. Punkt mit genau drei Ziffern und Ganzteil ungleich 0 ist bei jeder Einheit null (Tausenderpunkt oder Dezimalpunkt?). Von Hand gepflegt; der Dart-Zwilling prueft dieselben Faelle.",
3
+ "cases": [
4
+ { "text": "12", "unit": "Stk", "rule": null, "expected": 12000 },
5
+ { "text": "0", "unit": "Stk", "rule": null, "expected": 0 },
6
+ { "text": "00012", "unit": "Stk", "rule": null, "expected": 12000 },
7
+ { "text": " 7 ", "unit": "Stk", "rule": null, "expected": 7000 },
8
+ { "text": "37", "unit": null, "rule": null, "expected": 37000 },
9
+ { "text": "12,5", "unit": "Stk", "rule": null, "expected": null },
10
+ { "text": "12,0", "unit": "Stk", "rule": null, "expected": null },
11
+ { "text": "1.000", "unit": "Stk", "rule": null, "expected": null },
12
+ { "text": "1,5", "unit": null, "rule": null, "expected": null },
13
+ { "text": "250", "unit": "g", "rule": null, "expected": 250000 },
14
+ { "text": "2,5", "unit": "g", "rule": null, "expected": null },
15
+ { "text": "0,25", "unit": "kg", "rule": null, "expected": 250 },
16
+ { "text": "0,250", "unit": "kg", "rule": null, "expected": 250 },
17
+ { "text": "1.5", "unit": "kg", "rule": null, "expected": 1500 },
18
+ { "text": "0,001", "unit": "kg", "rule": null, "expected": 1 },
19
+ { "text": "1,2340", "unit": "kg", "rule": null, "expected": 1234 },
20
+ { "text": "1,2345", "unit": "kg", "rule": null, "expected": null },
21
+ { "text": "1.000", "unit": "kg", "rule": null, "expected": null },
22
+ { "text": ",5", "unit": "kg", "rule": null, "expected": 500 },
23
+ { "text": "5,", "unit": "kg", "rule": null, "expected": 5000 },
24
+ { "text": "1,25", "unit": "l", "rule": null, "expected": 1250 },
25
+ { "text": "2,75", "unit": "m", "rule": null, "expected": 2750 },
26
+ { "text": "", "unit": "kg", "rule": null, "expected": null },
27
+ { "text": " ", "unit": "kg", "rule": null, "expected": null },
28
+ { "text": ",", "unit": "kg", "rule": null, "expected": null },
29
+ { "text": "-1", "unit": "kg", "rule": null, "expected": null },
30
+ { "text": "+1", "unit": "kg", "rule": null, "expected": null },
31
+ { "text": "1e3", "unit": "kg", "rule": null, "expected": null },
32
+ { "text": "1 000", "unit": "kg", "rule": null, "expected": null },
33
+ { "text": "1,2,3", "unit": "kg", "rule": null, "expected": null },
34
+ { "text": "0x10", "unit": "Stk", "rule": null, "expected": null },
35
+ { "text": "zwölf", "unit": "Stk", "rule": null, "expected": null },
36
+ { "text": "9007199254740", "unit": "Stk", "rule": null, "expected": 9007199254740000 },
37
+ { "text": "9007199254741", "unit": "Stk", "rule": null, "expected": null },
38
+ { "text": "12.500", "unit": "kg", "rule": null, "expected": null },
39
+ { "text": "1.200", "unit": "m", "rule": null, "expected": null },
40
+ { "text": "1,000", "unit": "kg", "rule": null, "expected": 1000 },
41
+ { "text": "1,200", "unit": "m", "rule": null, "expected": 1200 },
42
+ { "text": "0.500", "unit": "kg", "rule": null, "expected": 500 },
43
+ { "text": ".500", "unit": "kg", "rule": null, "expected": 500 },
44
+ { "text": "00.500", "unit": "kg", "rule": null, "expected": 500 },
45
+ { "text": "1.25", "unit": "kg", "rule": null, "expected": 1250 },
46
+ { "text": "1.50", "unit": "l", "rule": null, "expected": 1500 },
47
+ { "text": "2,5", "unit": "Stk", "rule": "decimal", "expected": 2500 },
48
+ { "text": "2,5", "unit": "Rolle", "rule": "decimal", "expected": 2500 },
49
+ { "text": "1.5", "unit": null, "rule": "decimal", "expected": 1500 },
50
+ { "text": "1.000", "unit": "Stk", "rule": "decimal", "expected": null },
51
+ { "text": "0,5", "unit": "kg", "rule": "piece", "expected": null },
52
+ { "text": "3", "unit": "kg", "rule": "piece", "expected": 3000 },
53
+ { "text": "12", "unit": "Stk", "rule": "piece", "expected": 12000 }
54
+ ]
55
+ }