@kreiseck/kasseneck-api 1.10.1 → 1.11.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 (146) hide show
  1. package/CHANGELOG.md +241 -0
  2. package/dist/cjs/client/aufrufe.d.ts +5 -5
  3. package/dist/cjs/client/aufrufe.js +22 -2
  4. package/dist/cjs/client/errors.js +2 -0
  5. package/dist/cjs/client/receipts.d.ts +34 -1
  6. package/dist/cjs/client/receipts.js +34 -1
  7. package/dist/cjs/index.d.ts +1 -1
  8. package/dist/cjs/index.js +9 -2
  9. package/dist/cjs/inventory/vertrag.d.ts +1 -1
  10. package/dist/cjs/inventory/vertrag.js +2 -0
  11. package/dist/cjs/invoice/vertrag.d.ts +1 -1
  12. package/dist/cjs/invoice/vertrag.js +2 -0
  13. package/dist/cjs/models/cancellation.d.ts +49 -1
  14. package/dist/cjs/models/cancellation.js +139 -1
  15. package/dist/cjs/models/index.d.ts +1 -1
  16. package/dist/cjs/models/index.js +9 -2
  17. package/dist/cjs/models/payment-errors.d.ts +1 -1
  18. package/dist/cjs/models/payment-errors.js +2 -0
  19. package/dist/cjs/models/receipt-email.d.ts +1 -1
  20. package/dist/cjs/models/receipt-email.js +2 -0
  21. package/dist/cjs/models/receipt-errors.d.ts +1 -1
  22. package/dist/cjs/models/receipt-errors.js +2 -0
  23. package/dist/cjs/models/receipt-item.d.ts +10 -0
  24. package/dist/cjs/models/receipt-item.js +3 -0
  25. package/dist/cjs/pos/client.js +10 -0
  26. package/dist/cjs/pos/errors.d.ts +4 -3
  27. package/dist/cjs/pos/errors.js +21 -2
  28. package/dist/cjs/pos/index.d.ts +6 -2
  29. package/dist/cjs/pos/index.js +33 -3
  30. package/dist/cjs/pos/schicht-lesen.d.ts +39 -0
  31. package/dist/cjs/pos/schicht-lesen.js +405 -0
  32. package/dist/cjs/pos/schicht-typen.d.ts +380 -0
  33. package/dist/cjs/pos/schicht-typen.js +59 -0
  34. package/dist/cjs/pos/schicht.d.ts +208 -0
  35. package/dist/cjs/pos/schicht.js +235 -0
  36. package/dist/cjs/pos/settings.d.ts +37 -1
  37. package/dist/cjs/pos/settings.js +43 -3
  38. package/dist/cjs/pos/texte.d.ts +627 -0
  39. package/dist/cjs/pos/texte.js +236 -0
  40. package/dist/cjs/react/index.js +1 -1
  41. package/dist/cjs/receipt/grid.js +2 -2
  42. package/dist/cjs/receipt/layout.d.ts +5 -0
  43. package/dist/cjs/receipt/shift-sheet.d.ts +33 -0
  44. package/dist/cjs/receipt/shift-sheet.js +207 -0
  45. package/dist/cjs/register/errors.d.ts +1 -1
  46. package/dist/cjs/register/errors.js +2 -0
  47. package/dist/cjs/stored/draht.d.ts +1 -1
  48. package/dist/cjs/stored/draht.js +4 -3
  49. package/dist/cjs/stored/einstellungen.js +26 -3
  50. package/dist/cjs/stored/vokabular.d.ts +1 -1
  51. package/dist/cjs/stored/vokabular.js +118 -12
  52. package/dist/cjs/version.d.ts +1 -1
  53. package/dist/cjs/version.js +1 -1
  54. package/dist/esm/client/aufrufe.d.ts +5 -5
  55. package/dist/esm/client/aufrufe.js +22 -2
  56. package/dist/esm/client/errors.js +2 -0
  57. package/dist/esm/client/receipts.d.ts +34 -1
  58. package/dist/esm/client/receipts.js +35 -2
  59. package/dist/esm/index.d.ts +1 -1
  60. package/dist/esm/index.js +1 -1
  61. package/dist/esm/inventory/vertrag.d.ts +1 -1
  62. package/dist/esm/inventory/vertrag.js +2 -0
  63. package/dist/esm/invoice/vertrag.d.ts +1 -1
  64. package/dist/esm/invoice/vertrag.js +2 -0
  65. package/dist/esm/models/cancellation.d.ts +49 -1
  66. package/dist/esm/models/cancellation.js +132 -0
  67. package/dist/esm/models/index.d.ts +1 -1
  68. package/dist/esm/models/index.js +1 -1
  69. package/dist/esm/models/payment-errors.d.ts +1 -1
  70. package/dist/esm/models/payment-errors.js +2 -0
  71. package/dist/esm/models/receipt-email.d.ts +1 -1
  72. package/dist/esm/models/receipt-email.js +2 -0
  73. package/dist/esm/models/receipt-errors.d.ts +1 -1
  74. package/dist/esm/models/receipt-errors.js +2 -0
  75. package/dist/esm/models/receipt-item.d.ts +10 -0
  76. package/dist/esm/models/receipt-item.js +3 -0
  77. package/dist/esm/pos/client.js +11 -1
  78. package/dist/esm/pos/errors.d.ts +4 -3
  79. package/dist/esm/pos/errors.js +21 -2
  80. package/dist/esm/pos/index.d.ts +6 -2
  81. package/dist/esm/pos/index.js +7 -2
  82. package/dist/esm/pos/schicht-lesen.d.ts +39 -0
  83. package/dist/esm/pos/schicht-lesen.js +392 -0
  84. package/dist/esm/pos/schicht-typen.d.ts +380 -0
  85. package/dist/esm/pos/schicht-typen.js +56 -0
  86. package/dist/esm/pos/schicht.d.ts +208 -0
  87. package/dist/esm/pos/schicht.js +222 -0
  88. package/dist/esm/pos/settings.d.ts +37 -1
  89. package/dist/esm/pos/settings.js +40 -2
  90. package/dist/esm/pos/texte.d.ts +627 -0
  91. package/dist/esm/pos/texte.js +236 -0
  92. package/dist/esm/react/index.js +1 -1
  93. package/dist/esm/receipt/grid.js +2 -2
  94. package/dist/esm/receipt/layout.d.ts +5 -0
  95. package/dist/esm/receipt/shift-sheet.d.ts +33 -0
  96. package/dist/esm/receipt/shift-sheet.js +203 -0
  97. package/dist/esm/register/errors.d.ts +1 -1
  98. package/dist/esm/register/errors.js +2 -0
  99. package/dist/esm/stored/draht.d.ts +1 -1
  100. package/dist/esm/stored/draht.js +4 -3
  101. package/dist/esm/stored/einstellungen.js +27 -4
  102. package/dist/esm/stored/vokabular.d.ts +1 -1
  103. package/dist/esm/stored/vokabular.js +118 -12
  104. package/dist/esm/version.d.ts +1 -1
  105. package/dist/esm/version.js +1 -1
  106. package/fixtures/cancellation-tip-cases.json +679 -0
  107. package/fixtures/expected/cash-movement.grid32.txt +15 -0
  108. package/fixtures/expected/cash-movement.grid48.txt +15 -0
  109. package/fixtures/expected/cash-movement.lines.json +171 -0
  110. package/fixtures/expected/shift-close-blind.grid32.txt +28 -0
  111. package/fixtures/expected/shift-close-blind.grid48.txt +27 -0
  112. package/fixtures/expected/shift-close-blind.lines.json +275 -0
  113. package/fixtures/expected/shift-close-put-in.grid32.txt +38 -0
  114. package/fixtures/expected/shift-close-put-in.grid48.txt +35 -0
  115. package/fixtures/expected/shift-close-put-in.lines.json +403 -0
  116. package/fixtures/expected/shift-close-take-out.grid32.txt +38 -0
  117. package/fixtures/expected/shift-close-take-out.grid48.txt +35 -0
  118. package/fixtures/expected/shift-close-take-out.lines.json +403 -0
  119. package/fixtures/expected/shift-close.grid32.txt +37 -0
  120. package/fixtures/expected/shift-close.grid48.txt +35 -0
  121. package/fixtures/expected/shift-close.lines.json +413 -0
  122. package/fixtures/expected/shift-interim.grid32.txt +28 -0
  123. package/fixtures/expected/shift-interim.grid48.txt +27 -0
  124. package/fixtures/expected/shift-interim.lines.json +293 -0
  125. package/fixtures/hobex-hps-codes.json +1 -1
  126. package/fixtures/invoice-api.schema.json +1 -1
  127. package/fixtures/invoice-texts.json +1 -1
  128. package/fixtures/pos-message-cases.json +45 -0
  129. package/fixtures/pos-texts.json +723 -1
  130. package/fixtures/receipt-due-generated.json +1 -1
  131. package/fixtures/shift-cases.json +87 -0
  132. package/fixtures/shift-receipts.json +41 -0
  133. package/fixtures/surface.json +119 -3
  134. package/fixtures/v3/antworten/belege.json +1 -1
  135. package/fixtures/v3/antworten/belegmail.json +1 -1
  136. package/fixtures/v3/antworten/kasse-belege.json +1 -1
  137. package/fixtures/v3/antworten/kasse.json +10874 -4865
  138. package/fixtures/v3/antworten/lager.json +1 -1
  139. package/fixtures/v3/antworten/rechnungen.json +1 -1
  140. package/fixtures/v3/antworten/storno.json +1 -1
  141. package/fixtures/v3/stored/belege.json +18 -6
  142. package/fixtures/v3/stored/kasse.json +4667 -183
  143. package/fixtures/v3/stored/rechnungen.json +1 -1
  144. package/fixtures/v3/v3-vokabular.json +739 -32
  145. package/fixtures/v3/zahlbetrag-faelle.json +1 -1
  146. package/package.json +1 -1
@@ -1,11 +1,12 @@
1
1
  /**
2
2
  * Kachel-Kasse: Einstellungen, Artikelgruppen/Artikel fuer Kacheln,
3
3
  * Rabattverteilung, Drucker, Trinkgeld-Empfaenger, Lager (Standorte, Bestand,
4
- * Standort der Kasse), Inventur zaehlen. Die Aufrufe heissen 1:1 wie
4
+ * Standort der Kasse), Inventur zaehlen, Schicht, Kassenstand und
5
+ * Lade-Protokoll mit ihren Bons. Die Aufrufe heissen 1:1 wie
5
6
  * die Backend-Functions (die Rewrites-Waechter der Web-App leiten daraus ab)
6
7
  * und sprechen den Kassenweg `/api/v3` (Schluessel und Werte englisch).
7
8
  */
8
- export { POS_SHORTCUT_ACTIONS, POS_SHORTCUT_DEFAULTS, POS_BUSINESS_DEFAULTS, POS_DEVICE_DEFAULTS, POS_BUSINESS_VALUES, POS_DEVICE_VALUES, mergePosSettings, sanitizePosSettings, unknownPosSettingValues, posSettingsChanges, POS_SHORTCUT_SHARED_PAIRS, posShortcutConflict,
9
+ export { POS_SHORTCUT_ACTIONS, POS_SHORTCUT_DEFAULTS, POS_BUSINESS_DEFAULTS, POS_DEVICE_DEFAULTS, POS_DEVICE_OPTIONAL_FIELDS, POS_BUSINESS_OPTIONAL_FIELDS, posTipKeptByDefault, POS_BUSINESS_VALUES, POS_DEVICE_VALUES, mergePosSettings, sanitizePosSettings, unknownPosSettingValues, posSettingsChanges, POS_SHORTCUT_SHARED_PAIRS, posShortcutConflict,
9
10
  // Die Enums als Laufzeitlisten, benannt nach ihrem Feld.
10
11
  THEME, FONT_SIZE, WATERMARK, QUANTITY, TIP_MODE, CHECKOUT_MODE, CARD_PROVIDER, RECEIPT_OUTPUT, LAYOUT, CATEGORY_POSITION, TILE_HEIGHT, PRINTER_TYPE, TERMINAL_VIA, TERMINAL_TYPE, PAPER_SIZE, CODE_PAGE, CUT, DRAWER_AUTO_OPEN, QR_MODE, LOGO_SIZE, SETTINGS_FONT_SIZE, TILE_STYLE, AUTO_LOGOUT_MINUTES, DISCOUNT, WATERMARK_SIDE, WATERMARK_STRENGTH, LOGO_SCALE, WATERMARK_SCALE, DONE_SCREEN_SECONDS, } from './settings.js';
11
12
  export { fromArticleGroupPayload, fromPosArticlePayload, listMyArticleGroups, listMyArticles, QUANTITY_RULES, quantityRuleForUnit, quantityDefaults, allowedQuantity, } from './artikel.js';
@@ -19,6 +20,10 @@ export { PRINT_JOB_STATUSES, PRINT_JOB_SOURCES, isPrintJobFinished, listMyPrinte
19
20
  export { listMyTipRecipients } from './trinkgeld.js';
20
21
  export { STOCK_LOCATION_TYPES, listMyStockLocations, listMyStock, setMyCashregisterStockLocation, } from './lager.js';
21
22
  export { listMyStocktakes, listMyStocktakeItems, listMyStocktakeCounts, recordMyStocktakeCount, voidMyStocktakeCount, parseQuantityMilli, } from './inventur.js';
23
+ // Schicht, Kassenstand und Lade-Protokoll (1.9.0): Aufrufe, Kataloge, Helfer, Bons.
24
+ export { getMyDrawerState, openMyShift, recordMyCashMovement, logMyDrawerOpenings, getMyShiftReport, closeMyShift, sumDenominations, parseCentsAllowZero, newIdempotencyKey, shiftTipLines, durationText, } from './schicht.js';
25
+ export { EUR_DENOMINATIONS, MOVEMENT_TYPES, MANUAL_MOVEMENT_TYPES, CASH_LOCATIONS, OPENING_REASONS, MANUAL_OPENING_REASONS, SHIFT_RECEIPT_KINDS, AFTER_COUNT_TYPES, } from './schicht-typen.js';
26
+ export { shiftReceiptLayout, cashMovementLayout } from '../receipt/shift-sheet.js';
22
27
  // Rueckgabe-Wahl beim Storno (Kassen-Dialog); dieselbe Liste wie an der Wurzel.
23
28
  export { RETURN_DISPOSITIONS, isReturnDisposition } from '../models/cancellation.js';
24
29
  // Was die Kasse selbst sagt: ein Katalog fuer Browser-Kasse und App.
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Leser der Schicht-Antworten: aus dem Draht `/v3` werden die Modelle in
3
+ * `schicht-typen.ts`. Paketintern, nicht Teil der Oberflaeche.
4
+ *
5
+ * Regel wie beim Lager: ein Betrag, eine Anzahl oder eine Nummer ist eine
6
+ * Ganzzahl, eine Kennung ein nicht leerer Text, sonst endet der Aufruf mit
7
+ * `KasseneckValidationError`. Ein Ersatzwert wie `0` waere eine falsche
8
+ * Aussage („nichts in der Lade“ statt „Antwort kaputt“). Texte sind
9
+ * nachsichtiger: ein fehlender oder fremder Wert wird `null`.
10
+ *
11
+ * Felder, die nur mit Recht kommen (`expectedCents`, `runningTotals` …),
12
+ * stehen im Modell genau dann, wenn der Server sie sendet.
13
+ */
14
+ import { type ValidationScope } from '../client/errors.js';
15
+ import type { CashMovement, CashMovementReceipt, CashMovementResult, DrawerOpeningsResult, OpenShiftResult, ShiftReport, CloseShiftResult, DrawerState, Shift, ShiftSummaryReceipt } from './schicht-typen.js';
16
+ type Roh = Record<string, unknown>;
17
+ /** Wer liest (fuer die Meldung) und wo im Rumpf. */
18
+ export interface Ort {
19
+ name: string;
20
+ pfad: string;
21
+ /** `response` fuer Antworten, `request` fuer Bon-Daten, die ein Aufrufer uebergibt. */
22
+ scope: ValidationScope;
23
+ }
24
+ /** Ein Objekt, das da sein muss. */
25
+ export declare function pflichtObjekt(ort: Ort, w: unknown): Roh;
26
+ /** Eine Schicht; was nur mit `cashView` kommt, steht nur, wenn gesendet. */
27
+ export declare function schicht(ort: Ort, w: unknown): Shift;
28
+ export declare function ladeStand(ort: Ort, w: unknown): DrawerState;
29
+ export declare function bewegung(ort: Ort, w: unknown): CashMovement;
30
+ /** Bon-Daten eines Z- oder X-Bons; eine andere Art ist ein Fehler. */
31
+ export declare function schichtBon(ort: Ort, w: unknown): ShiftSummaryReceipt;
32
+ /** Bon-Daten einer Kassenbewegung; eine andere Art ist ein Fehler. */
33
+ export declare function bewegungsBon(ort: Ort, w: unknown): CashMovementReceipt;
34
+ export declare function abschluss(ort: Ort, w: unknown): CloseShiftResult;
35
+ export declare function geoeffnet(ort: Ort, w: unknown): OpenShiftResult;
36
+ export declare function gebucht(ort: Ort, w: unknown): CashMovementResult;
37
+ export declare function gemeldet(ort: Ort, w: unknown): DrawerOpeningsResult;
38
+ export declare function bericht(ort: Ort, w: unknown): ShiftReport;
39
+ export {};
@@ -0,0 +1,392 @@
1
+ /**
2
+ * Leser der Schicht-Antworten: aus dem Draht `/v3` werden die Modelle in
3
+ * `schicht-typen.ts`. Paketintern, nicht Teil der Oberflaeche.
4
+ *
5
+ * Regel wie beim Lager: ein Betrag, eine Anzahl oder eine Nummer ist eine
6
+ * Ganzzahl, eine Kennung ein nicht leerer Text, sonst endet der Aufruf mit
7
+ * `KasseneckValidationError`. Ein Ersatzwert wie `0` waere eine falsche
8
+ * Aussage („nichts in der Lade“ statt „Antwort kaputt“). Texte sind
9
+ * nachsichtiger: ein fehlender oder fremder Wert wird `null`.
10
+ *
11
+ * Felder, die nur mit Recht kommen (`expectedCents`, `runningTotals` …),
12
+ * stehen im Modell genau dann, wenn der Server sie sendet.
13
+ */
14
+ import { KasseneckValidationError } from '../client/errors.js';
15
+ import { SHIFT_RECEIPT_KINDS } from './schicht-typen.js';
16
+ const hat = (o, k) => Object.prototype.hasOwnProperty.call(o, k);
17
+ const unter = (ort, teil) => ({ ...ort, pfad: ort.pfad === '' ? teil : `${ort.pfad}.${teil}` });
18
+ function fehler(ort, grund) {
19
+ return new KasseneckValidationError(ort.name, `${ort.scope === 'response' ? 'Antwort' : 'Bon-Daten'}: ${grund}`, ort.scope);
20
+ }
21
+ function objekt(w) {
22
+ return w !== null && typeof w === 'object' && !Array.isArray(w) ? w : null;
23
+ }
24
+ /** Ein Objekt, das da sein muss. */
25
+ export function pflichtObjekt(ort, w) {
26
+ const o = objekt(w);
27
+ if (!o)
28
+ throw fehler(ort, `${ort.pfad || 'data'} ist kein Objekt`);
29
+ return o;
30
+ }
31
+ const text = (w) => (typeof w === 'string' ? w : null);
32
+ function kennung(ort, o, feld) {
33
+ const w = o[feld];
34
+ if (typeof w !== 'string' || w === '')
35
+ throw fehler(ort, `${ort.pfad}.${feld} fehlt`);
36
+ return w;
37
+ }
38
+ function ganzzahl(ort, o, feld) {
39
+ const w = o[feld];
40
+ if (typeof w !== 'number' || !Number.isSafeInteger(w))
41
+ throw fehler(ort, `${ort.pfad}.${feld} ist keine ganze Zahl`);
42
+ return w;
43
+ }
44
+ function ganzzahlOderNull(ort, o, feld) {
45
+ const w = o[feld];
46
+ return w === undefined || w === null ? null : ganzzahl(ort, o, feld);
47
+ }
48
+ function wahr(ort, o, feld) {
49
+ const w = o[feld];
50
+ if (typeof w !== 'boolean')
51
+ throw fehler(ort, `${ort.pfad}.${feld} ist kein Wahrheitswert`);
52
+ return w;
53
+ }
54
+ function wahrOderNull(ort, o, feld) {
55
+ const w = o[feld];
56
+ return w === undefined || w === null ? null : wahr(ort, o, feld);
57
+ }
58
+ function liste(ort, o, feld, lese) {
59
+ const w = o[feld];
60
+ if (!Array.isArray(w))
61
+ throw fehler(ort, `${ort.pfad}.${feld} ist keine Liste`);
62
+ return w.map((e, i) => lese(unter(ort, `${feld}[${i}]`), e));
63
+ }
64
+ /** Setzt ein Feld nur, wenn der Server es sendet (Recht). */
65
+ function wennDa(ziel, o, feld, lese) {
66
+ if (hat(o, feld))
67
+ ziel[feld] = lese();
68
+ }
69
+ /** Setzt ein Feld nur, wenn der Server einen Wert sendet; `null` gilt wie fehlend. */
70
+ function wennWert(ziel, o, feld, lese) {
71
+ if (o[feld] !== undefined && o[feld] !== null)
72
+ ziel[feld] = lese();
73
+ }
74
+ function stueckelung(ort, o, feld) {
75
+ const w = o[feld];
76
+ if (w === undefined || w === null)
77
+ return null;
78
+ const karte = objekt(w);
79
+ if (!karte)
80
+ throw fehler(ort, `${ort.pfad}.${feld} ist keine Stueckelung`);
81
+ const raus = {};
82
+ for (const k of Object.keys(karte))
83
+ raus[k] = ganzzahl(unter(ort, feld), karte, k);
84
+ return raus;
85
+ }
86
+ function akteur(ort, w) {
87
+ if (w === undefined || w === null)
88
+ return null;
89
+ const o = pflichtObjekt(ort, w);
90
+ return { registerUserId: text(o.registerUserId), name: text(o.name), role: text(o.role) ?? '' };
91
+ }
92
+ function unbar(ort, w) {
93
+ const o = pflichtObjekt(ort, w);
94
+ return { method: kennung(ort, o, 'method'), cents: ganzzahl(ort, o, 'cents') };
95
+ }
96
+ function trinkgeld(ort, w) {
97
+ const o = pflichtObjekt(ort, w);
98
+ return {
99
+ registerUserId: kennung(ort, o, 'registerUserId'),
100
+ name: text(o.name),
101
+ receivedImmediatelyCents: ganzzahl(ort, o, 'receivedImmediatelyCents'),
102
+ retainedCents: ganzzahl(ort, o, 'retainedCents'),
103
+ paidOutCents: ganzzahl(ort, o, 'paidOutCents'),
104
+ // Fehlt bei einem Backend vor der Regel R62: dann 0, statt die ganze Antwort zu verwerfen.
105
+ refundedByEmployeeCents: o.refundedByEmployeeCents === undefined || o.refundedByEmployeeCents === null ? 0 : ganzzahl(ort, o, 'refundedByEmployeeCents'),
106
+ };
107
+ }
108
+ /** Trinkgeld mit offenem Anspruch (Kassenstand, Bon). */
109
+ function trinkgeldMitOffen(ort, w) {
110
+ const o = pflichtObjekt(ort, w);
111
+ return {
112
+ ...trinkgeld(ort, o),
113
+ // Fehlt bei einem Backend vor der Regel R64: dann 0.
114
+ carriedOverCents: o.carriedOverCents === undefined || o.carriedOverCents === null ? 0 : ganzzahl(ort, o, 'carriedOverCents'),
115
+ openCents: ganzzahl(ort, o, 'openCents'),
116
+ };
117
+ }
118
+ function laufstand(ort, w) {
119
+ const o = pflichtObjekt(ort, w);
120
+ return {
121
+ cashSalesCents: ganzzahl(ort, o, 'cashSalesCents'),
122
+ cashCancellationsCents: ganzzahl(ort, o, 'cashCancellationsCents'),
123
+ nonCashByMethod: liste(ort, o, 'nonCashByMethod', unbar),
124
+ // Fehlt bei einem Server vor dem Gutschein-Feld: dann 0.
125
+ voucherRedeemedCents: o.voucherRedeemedCents === undefined || o.voucherRedeemedCents === null ? 0 : ganzzahl(ort, o, 'voucherRedeemedCents'),
126
+ receiptCount: ganzzahl(ort, o, 'receiptCount'),
127
+ cancellationCount: ganzzahl(ort, o, 'cancellationCount'),
128
+ cashInCents: ganzzahl(ort, o, 'cashInCents'),
129
+ cashOutCents: ganzzahl(ort, o, 'cashOutCents'),
130
+ tips: liste(ort, o, 'tips', trinkgeld),
131
+ openingsWithoutSale: ganzzahl(ort, o, 'openingsWithoutSale'),
132
+ };
133
+ }
134
+ /** Entnehmen oder Einlegen nach dem Zaehlen; die Art bleibt offen fuer kuenftige Werte (Text). */
135
+ function nachZaehlung(ort, w) {
136
+ const o = pflichtObjekt(ort, w);
137
+ return { type: kennung(ort, o, 'type'), amountCents: ganzzahl(ort, o, 'amountCents') };
138
+ }
139
+ function abgleich(ort, w) {
140
+ if (w === undefined || w === null)
141
+ return null;
142
+ const o = pflichtObjekt(ort, w);
143
+ const abw = o.deviations;
144
+ let deviations = null;
145
+ if (abw !== undefined && abw !== null) {
146
+ const karte = pflichtObjekt(unter(ort, 'deviations'), abw);
147
+ deviations = {};
148
+ for (const [feld, eintrag] of Object.entries(karte)) {
149
+ const e = pflichtObjekt(unter(ort, `deviations.${feld}`), eintrag);
150
+ deviations[feld] = { stored: e.stored, recomputed: e.recomputed };
151
+ }
152
+ }
153
+ return { matches: wahr(ort, o, 'matches'), deviations };
154
+ }
155
+ /** Eine Schicht; was nur mit `cashView` kommt, steht nur, wenn gesendet. */
156
+ export function schicht(ort, w) {
157
+ const o = pflichtObjekt(ort, w);
158
+ const status = o.status;
159
+ if (status !== 'open' && status !== 'closed')
160
+ throw fehler(ort, `${ort.pfad}.status ist unbekannt`);
161
+ const raus = {
162
+ id: kennung(ort, o, 'id'),
163
+ drawerId: kennung(ort, o, 'drawerId'),
164
+ cashregisterId: text(o.cashregisterId),
165
+ number: ganzzahl(ort, o, 'number'),
166
+ status,
167
+ openedAt: text(o.openedAt),
168
+ openedBy: akteur(unter(ort, 'openedBy'), o.openedBy),
169
+ };
170
+ const r = raus;
171
+ // Ohne Recht `shift` fehlen sie (Grunddaten der offenen Schicht).
172
+ wennDa(r, o, 'openedDeviceId', () => text(o.openedDeviceId));
173
+ wennDa(r, o, 'closedAt', () => text(o.closedAt));
174
+ wennDa(r, o, 'closedBy', () => akteur(unter(ort, 'closedBy'), o.closedBy));
175
+ wennDa(r, o, 'closedDeviceId', () => text(o.closedDeviceId));
176
+ wennDa(r, o, 'openingFloatCents', () => ganzzahlOderNull(ort, o, 'openingFloatCents'));
177
+ wennDa(r, o, 'openingDenominations', () => stueckelung(ort, o, 'openingDenominations'));
178
+ wennDa(r, o, 'openingFloatTakenOver', () => wahr(ort, o, 'openingFloatTakenOver'));
179
+ wennDa(r, o, 'comment', () => text(o.comment));
180
+ wennDa(r, o, 'blind', () => wahrOderNull(ort, o, 'blind'));
181
+ wennDa(r, o, 'countedCents', () => ganzzahlOderNull(ort, o, 'countedCents'));
182
+ wennDa(r, o, 'suggestedFloatCents', () => ganzzahlOderNull(ort, o, 'suggestedFloatCents'));
183
+ wennDa(r, o, 'runningTotals', () => laufstand(unter(ort, 'runningTotals'), o.runningTotals));
184
+ wennDa(r, o, 'expectedCents', () => ganzzahlOderNull(ort, o, 'expectedCents'));
185
+ wennDa(r, o, 'differenceCents', () => ganzzahlOderNull(ort, o, 'differenceCents'));
186
+ wennDa(r, o, 'reconciliation', () => abgleich(unter(ort, 'reconciliation'), o.reconciliation));
187
+ // Nur wenn beim Abschluss Geld entnommen oder eingelegt wurde.
188
+ wennWert(r, o, 'afterCount', () => nachZaehlung(unter(ort, 'afterCount'), o.afterCount));
189
+ wennWert(r, o, 'leftInDrawerCents', () => ganzzahl(ort, o, 'leftInDrawerCents'));
190
+ return raus;
191
+ }
192
+ function schichtOderNull(ort, w) {
193
+ return w === null ? null : schicht(ort, w);
194
+ }
195
+ function anspruch(ort, w) {
196
+ const o = pflichtObjekt(ort, w);
197
+ return { registerUserId: kennung(ort, o, 'registerUserId'), name: text(o.name), cents: ganzzahl(ort, o, 'cents') };
198
+ }
199
+ function oeffnung(ort, w) {
200
+ const o = pflichtObjekt(ort, w);
201
+ const von = objekt(o.by);
202
+ return {
203
+ at: text(o.at),
204
+ deviceTime: text(o.deviceTime),
205
+ reason: kennung(ort, o, 'reason'),
206
+ by: von ? { name: text(von.name) } : null,
207
+ ok: wahr(ort, o, 'ok'),
208
+ };
209
+ }
210
+ export function ladeStand(ort, w) {
211
+ const o = pflichtObjekt(ort, w);
212
+ const lade = pflichtObjekt(unter(ort, 'drawer'), o.drawer);
213
+ const ladeOrt = unter(ort, 'drawer');
214
+ const rechte = pflichtObjekt(unter(ort, 'rights'), o.rights);
215
+ const rechteOrt = unter(ort, 'rights');
216
+ const raus = {
217
+ drawer: {
218
+ id: kennung(ladeOrt, lade, 'id'),
219
+ name: text(lade.name),
220
+ shiftMode: wahr(ladeOrt, lade, 'shiftMode'),
221
+ cashregisterId: text(lade.cashregisterId),
222
+ },
223
+ rights: {
224
+ shift: wahr(rechteOrt, rechte, 'shift'),
225
+ cashMove: wahr(rechteOrt, rechte, 'cashMove'),
226
+ cashView: wahr(rechteOrt, rechte, 'cashView'),
227
+ drawer: wahr(rechteOrt, rechte, 'drawer'),
228
+ },
229
+ tipClaims: liste(ort, o, 'tipClaims', anspruch),
230
+ tips: liste(ort, o, 'tips', trinkgeldMitOffen),
231
+ };
232
+ const r = raus;
233
+ wennDa(r, o, 'shift', () => schichtOderNull(unter(ort, 'shift'), o.shift));
234
+ wennDa(r, o, 'longOpenWarning', () => wahr(ort, o, 'longOpenWarning'));
235
+ wennDa(r, o, 'suggestedFloatCents', () => ganzzahlOderNull(ort, o, 'suggestedFloatCents'));
236
+ wennDa(r, o, 'expectedCents', () => ganzzahlOderNull(ort, o, 'expectedCents'));
237
+ wennDa(r, o, 'recentOpenings', () => liste(ort, o, 'recentOpenings', oeffnung));
238
+ wennWert(r, o, 'suggestedFromShiftNumber', () => ganzzahl(ort, o, 'suggestedFromShiftNumber'));
239
+ // Wechselgeld der Lade: nur wenn eingestellt.
240
+ wennWert(raus.drawer, lade, 'changeFloatCents', () => ganzzahl(ladeOrt, lade, 'changeFloatCents'));
241
+ return raus;
242
+ }
243
+ export function bewegung(ort, w) {
244
+ const o = pflichtObjekt(ort, w);
245
+ const empfaenger = objekt(o.recipient);
246
+ const paar = objekt(o.counterpart);
247
+ return {
248
+ id: kennung(ort, o, 'id'),
249
+ number: ganzzahl(ort, o, 'number'),
250
+ shiftId: text(o.shiftId),
251
+ type: kennung(ort, o, 'type'),
252
+ amountCents: ganzzahl(ort, o, 'amountCents'),
253
+ location: text(o.location),
254
+ note: text(o.note),
255
+ by: akteur(unter(ort, 'by'), o.by),
256
+ at: text(o.at),
257
+ recipient: empfaenger ? { registerUserId: text(empfaenger.registerUserId), name: text(empfaenger.name) } : null,
258
+ counterpart: paar ? { drawerId: text(paar.drawerId), movementId: text(paar.movementId) } : null,
259
+ };
260
+ }
261
+ function bonKopf(ort, o) {
262
+ const art = o.kind;
263
+ if (typeof art !== 'string' || !SHIFT_RECEIPT_KINDS.includes(art))
264
+ throw fehler(ort, `${ort.pfad}.kind ist unbekannt`);
265
+ const betrieb = objekt(o.business) ?? {};
266
+ const kasse = objekt(o.cashregister) ?? {};
267
+ const lade = objekt(o.drawer) ?? {};
268
+ return {
269
+ kind: art,
270
+ blind: wahr(ort, o, 'blind'),
271
+ business: { name: text(betrieb.name) },
272
+ cashregister: { id: text(kasse.id), name: text(kasse.name) },
273
+ drawer: { name: text(lade.name) },
274
+ };
275
+ }
276
+ /** Bon-Daten eines Z- oder X-Bons; eine andere Art ist ein Fehler. */
277
+ export function schichtBon(ort, w) {
278
+ const o = pflichtObjekt(ort, w);
279
+ const kopf = bonKopf(ort, o);
280
+ if (kopf.kind === 'cash_movement')
281
+ throw fehler(ort, `${ort.pfad}.kind ist cash_movement, erwartet Z- oder X-Bon`);
282
+ const s = pflichtObjekt(unter(ort, 'shift'), o.shift);
283
+ const sOrt = unter(ort, 'shift');
284
+ const raus = {
285
+ ...kopf,
286
+ kind: kopf.kind,
287
+ shift: {
288
+ number: ganzzahl(sOrt, s, 'number'),
289
+ openedAt: text(s.openedAt),
290
+ closedAt: text(s.closedAt),
291
+ openedByName: text(s.openedByName),
292
+ closedByName: text(s.closedByName),
293
+ },
294
+ openingFloatCents: ganzzahlOderNull(ort, o, 'openingFloatCents'),
295
+ openingFloatTakenOver: wahr(ort, o, 'openingFloatTakenOver'),
296
+ openingDenominations: stueckelung(ort, o, 'openingDenominations'),
297
+ cashSalesCents: ganzzahlOderNull(ort, o, 'cashSalesCents'),
298
+ cashCancellationsCents: ganzzahlOderNull(ort, o, 'cashCancellationsCents'),
299
+ receiptCount: ganzzahl(ort, o, 'receiptCount'),
300
+ cancellationCount: ganzzahl(ort, o, 'cancellationCount'),
301
+ nonCashByMethod: liste(ort, o, 'nonCashByMethod', unbar),
302
+ movements: liste(ort, o, 'movements', (mOrt, mw) => {
303
+ const m = pflichtObjekt(mOrt, mw);
304
+ return {
305
+ number: ganzzahl(mOrt, m, 'number'),
306
+ type: kennung(mOrt, m, 'type'),
307
+ amountCents: ganzzahl(mOrt, m, 'amountCents'),
308
+ note: text(m.note),
309
+ byName: text(m.byName),
310
+ at: text(m.at),
311
+ };
312
+ }),
313
+ tips: liste(ort, o, 'tips', trinkgeldMitOffen),
314
+ expectedCents: ganzzahlOderNull(ort, o, 'expectedCents'),
315
+ countedCents: ganzzahlOderNull(ort, o, 'countedCents'),
316
+ countedDenominations: stueckelung(ort, o, 'countedDenominations'),
317
+ differenceCents: ganzzahlOderNull(ort, o, 'differenceCents'),
318
+ comment: text(o.comment),
319
+ openingsWithoutSale: ganzzahl(ort, o, 'openingsWithoutSale'),
320
+ };
321
+ // Nur wenn beim Abschluss Geld entnommen oder eingelegt wurde; „Bleibt“ blind `null`.
322
+ const r = raus;
323
+ wennWert(r, o, 'afterCount', () => nachZaehlung(unter(ort, 'afterCount'), o.afterCount));
324
+ wennDa(r, o, 'leftInDrawerCents', () => ganzzahlOderNull(ort, o, 'leftInDrawerCents'));
325
+ return raus;
326
+ }
327
+ /** Bon-Daten einer Kassenbewegung; eine andere Art ist ein Fehler. */
328
+ export function bewegungsBon(ort, w) {
329
+ const o = pflichtObjekt(ort, w);
330
+ const kopf = bonKopf(ort, o);
331
+ if (kopf.kind !== 'cash_movement')
332
+ throw fehler(ort, `${ort.pfad}.kind ist ${kopf.kind}, erwartet cash_movement`);
333
+ const s = objekt(o.shift);
334
+ const m = pflichtObjekt(unter(ort, 'movement'), o.movement);
335
+ const mOrt = unter(ort, 'movement');
336
+ return {
337
+ ...kopf,
338
+ kind: 'cash_movement',
339
+ shift: s ? { number: ganzzahl(unter(ort, 'shift'), s, 'number') } : null,
340
+ movement: {
341
+ number: ganzzahl(mOrt, m, 'number'),
342
+ type: kennung(mOrt, m, 'type'),
343
+ amountCents: ganzzahl(mOrt, m, 'amountCents'),
344
+ note: text(m.note),
345
+ location: text(m.location),
346
+ recipientName: text(m.recipientName),
347
+ targetDrawerName: text(m.targetDrawerName),
348
+ byName: text(m.byName),
349
+ at: text(m.at),
350
+ },
351
+ };
352
+ }
353
+ export function abschluss(ort, w) {
354
+ const o = pflichtObjekt(ort, w);
355
+ if (o.status === 'recount')
356
+ return { status: 'recount' };
357
+ if (o.status === 'ready')
358
+ return { status: 'ready' };
359
+ if (o.status !== 'closed')
360
+ throw fehler(ort, `${ort.pfad || 'data'}.status ist unbekannt`);
361
+ const raus = {
362
+ status: 'closed',
363
+ shift: schicht(unter(ort, 'shift'), o.shift),
364
+ receipt: schichtBon(unter(ort, 'receipt'), o.receipt),
365
+ lastShiftOfRegister: wahr(ort, o, 'lastShiftOfRegister'),
366
+ };
367
+ // Bon „Kassenbewegung“ der Entnahme bzw. Einlage, nur wenn Geld bewegt wurde.
368
+ wennWert(raus, o, 'afterCountReceipt', () => bewegungsBon(unter(ort, 'afterCountReceipt'), o.afterCountReceipt));
369
+ return raus;
370
+ }
371
+ export function geoeffnet(ort, w) {
372
+ const o = pflichtObjekt(ort, w);
373
+ const raus = { shift: schicht(unter(ort, 'shift'), o.shift) };
374
+ wennDa(raus, o, 'handoverDifferenceCents', () => ganzzahlOderNull(ort, o, 'handoverDifferenceCents'));
375
+ return raus;
376
+ }
377
+ export function gebucht(ort, w) {
378
+ const o = pflichtObjekt(ort, w);
379
+ return { movement: bewegung(unter(ort, 'movement'), o.movement), receipt: bewegungsBon(unter(ort, 'receipt'), o.receipt) };
380
+ }
381
+ export function gemeldet(ort, w) {
382
+ const o = pflichtObjekt(ort, w);
383
+ return { accepted: ganzzahl(ort, o, 'accepted'), duplicates: ganzzahl(ort, o, 'duplicates'), drawerId: text(o.drawerId) };
384
+ }
385
+ export function bericht(ort, w) {
386
+ const o = pflichtObjekt(ort, w);
387
+ return {
388
+ shift: schicht(unter(ort, 'shift'), o.shift),
389
+ movements: liste(ort, o, 'movements', bewegung),
390
+ receipt: schichtBon(unter(ort, 'receipt'), o.receipt),
391
+ };
392
+ }