@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
@@ -0,0 +1,380 @@
1
+ /**
2
+ * Schicht, Kassenstand und Lade-Protokoll: Kataloge und Modelle, wie sie am
3
+ * Kassenweg `/api/v3` ankommen (Backend schicht-endpoints.js, Vokabular
4
+ * `KASSEN_BEWEGUNG_ART`, `KASSEN_ORT`, `LADE_OEFFNUNG_GRUND`,
5
+ * `SCHICHT_BON_ART`). Die Modelle sind die Drahtform, Feld fuer Feld.
6
+ *
7
+ * **Fehlt ein Feld, fehlt das Recht.** Ohne `cashView` sendet der Server kein
8
+ * Soll, kein Ist, keinen Laufstand und keinen Vorschlag (Blindzaehlung); im
9
+ * Modell fehlen sie dann auch (`undefined`), nie stehen sie als `0` da. Auf
10
+ * dem Bon (`ShiftSummaryReceipt`) stehen dieselben Werte blind als `null`.
11
+ *
12
+ * Alle Betraege in Cent (Ganzzahl, Vorzeichen wie gebucht), Zeiten als
13
+ * ISO 8601 (UTC), Stueckelung als Karte „Wert in Cent“ -> Anzahl
14
+ * (`{ "2000": 1, "500": 3 }`).
15
+ */
16
+ /** Scheine und Muenzen in Euro-Cent, vom groessten Schein abwaerts (Backend `EUR_STUECKELUNG`). */
17
+ export declare const EUR_DENOMINATIONS: readonly [50000, 20000, 10000, 5000, 2000, 1000, 500, 200, 100, 50, 20, 10, 5, 2, 1];
18
+ export type EurDenomination = (typeof EUR_DENOMINATIONS)[number];
19
+ /**
20
+ * Alle Arten einer Kassenbewegung (Katalog `KASSEN_BEWEGUNG_ART`). Die ersten
21
+ * acht sendet die Kasse ([MANUAL_MOVEMENT_TYPES]); `transfer_in` (Gegenseite
22
+ * einer Umbuchung), `expense`, `count_difference` und `handover_difference`
23
+ * vergibt nur der Server.
24
+ */
25
+ export declare const MOVEMENT_TYPES: readonly ["change_in", "cash_drop", "owner_deposit", "owner_withdrawal", "tip_payout", "other_in", "other_out", "transfer_out", "transfer_in", "expense", "count_difference", "handover_difference"];
26
+ export type MovementType = (typeof MOVEMENT_TYPES)[number];
27
+ /**
28
+ * Was die Kasse von Hand buchen darf. Pflichtangaben je Art (sonst lehnt der
29
+ * Server ab): `note` bei `other_in`/`other_out`, `recipientId` bei
30
+ * `tip_payout`, `targetDrawerId` bei `transfer_out`. `location` bei
31
+ * `change_in`/`cash_drop` ist freiwillig.
32
+ */
33
+ export declare const MANUAL_MOVEMENT_TYPES: readonly ["change_in", "cash_drop", "owner_deposit", "owner_withdrawal", "tip_payout", "other_in", "other_out", "transfer_out"];
34
+ export type ManualMovementType = (typeof MANUAL_MOVEMENT_TYPES)[number];
35
+ /** Woher Wechselgeld kommt bzw. wohin abgeschoepft wird (Katalog `KASSEN_ORT`). */
36
+ export declare const CASH_LOCATIONS: readonly ["bank", "safe"];
37
+ export type CashLocation = (typeof CASH_LOCATIONS)[number];
38
+ /**
39
+ * Warum die Lade aufging (Katalog `LADE_OEFFNUNG_GRUND`). `sale`,
40
+ * `cash_movement`, `shift_open` und `shift_close` meldet die Kasse selbst beim
41
+ * jeweiligen Vorgang; von Hand waehlt der Kassier aus [MANUAL_OPENING_REASONS].
42
+ */
43
+ export declare const OPENING_REASONS: readonly ["sale", "cash_movement", "shift_open", "shift_close", "change", "check", "misoperation", "other"];
44
+ export type OpeningReason = (typeof OPENING_REASONS)[number];
45
+ /** Die Gruende des Knopfs „Lade“; `other` braucht einen Text (`note`). */
46
+ export declare const MANUAL_OPENING_REASONS: readonly ["change", "check", "misoperation", "other"];
47
+ export type ManualOpeningReason = (typeof MANUAL_OPENING_REASONS)[number];
48
+ /**
49
+ * Was nach dem Zaehlen beim Schicht-Abschluss mit dem Geld geschieht (Katalog
50
+ * `NACH_ZAEHLUNG_ART`): `cash_drop` = entnehmen (gebucht als Abschoepfung),
51
+ * `change_in` = einlegen (gebucht als Wechselgeld). Beides Geldtransit, kein Umsatz.
52
+ */
53
+ export declare const AFTER_COUNT_TYPES: readonly ["cash_drop", "change_in"];
54
+ export type AfterCountType = (typeof AFTER_COUNT_TYPES)[number];
55
+ /**
56
+ * Entnehmen oder Einlegen nach dem Zaehlen: `amountCents` immer positiv, die
57
+ * Richtung sagt `type`. Gezaehlt wird immer die ganze Lade; was danach bleibt,
58
+ * steht in `leftInDrawerCents` (Schicht, Z-Bon).
59
+ */
60
+ export interface AfterCount {
61
+ /** Ein Wert aus [AFTER_COUNT_TYPES] oder ein kuenftiger. */
62
+ type: AfterCountType | (string & {});
63
+ amountCents: number;
64
+ }
65
+ /** Arten der Bon-Daten (Katalog `SCHICHT_BON_ART`): Z-Bon, X-Bon, Kassenbewegung. */
66
+ export declare const SHIFT_RECEIPT_KINDS: readonly ["shift_close", "shift_interim", "cash_movement"];
67
+ export type ShiftReceiptKind = (typeof SHIFT_RECEIPT_KINDS)[number];
68
+ export type ShiftStatus = 'open' | 'closed';
69
+ /** Wer gehandelt hat: Kassen-Benutzer, Inhaber (Panel) oder Admin. */
70
+ export type ShiftActorRole = 'register_user' | 'owner' | 'admin';
71
+ /** Stueckelung: Wert in Cent (als Text) -> Anzahl. */
72
+ export type Denominations = Record<string, number>;
73
+ export interface ShiftActor {
74
+ registerUserId: string | null;
75
+ name: string | null;
76
+ role: ShiftActorRole | string;
77
+ }
78
+ export interface DrawerInfo {
79
+ id: string;
80
+ name: string | null;
81
+ /** Schichtbetrieb: verkauft wird erst nach dem Oeffnen einer Schicht. */
82
+ shiftMode: boolean;
83
+ cashregisterId: string | null;
84
+ /**
85
+ * Wechselgeld der Lade in Cent („Wechselgeld in der Lade“): bleibt nach jedem
86
+ * Schicht-Abschluss drin, mehr wird beim Schliessen entnommen. Fehlt, wenn
87
+ * nicht eingestellt (dann bleibt alles in der Lade, wie bisher).
88
+ */
89
+ changeFloatCents?: number;
90
+ }
91
+ /** Die vier Rechte des angemeldeten Benutzers an der Lade. */
92
+ export interface DrawerRights {
93
+ /** Schicht oeffnen und schliessen. */
94
+ shift: boolean;
95
+ /** Ein- und Auszahlungen buchen. */
96
+ cashMove: boolean;
97
+ /** Soll, Ist und Differenz sehen; ohne dieses Recht wird blind gezaehlt. */
98
+ cashView: boolean;
99
+ /** Die Lade von Hand oeffnen (mit Grund). */
100
+ drawer: boolean;
101
+ }
102
+ export interface NonCashAmount {
103
+ /** Zahlart, wie am Beleg (`card` …). */
104
+ method: string;
105
+ cents: number;
106
+ }
107
+ export interface ShiftTipTotals {
108
+ registerUserId: string;
109
+ name: string | null;
110
+ /** Steht dem Mitarbeiter aus der Lade zu (bar oder Karte am Terminal des Betriebs). */
111
+ receivedImmediatelyCents: number;
112
+ /** Behaelt der Betrieb und zahlt es ueber den Lohn („über Lohn“). */
113
+ retainedCents: number;
114
+ /** In dieser Schicht aus der Lade ausgezahlt („schon genommen“). */
115
+ paidOutCents: number;
116
+ /**
117
+ * Beim Storno selbst zurueckgegeben („Gibt die Mitarbeiterin direkt“, „selbst zurückgegeben“).
118
+ * Eigene Zahl, damit „schon genommen“ nie negativ wird: offen = verdient − genommen + selbst
119
+ * zurueckgegeben. Fehlt es in der Antwort (Backend vor dieser Regel), gilt 0.
120
+ */
121
+ refundedByEmployeeCents: number;
122
+ }
123
+ /**
124
+ * Trinkgeld eines Mitarbeiters mit seinem offenen Anspruch an die Lade
125
+ * (`openCents`, „offen“): im Kassenstand ([DrawerState.tips]) und auf dem
126
+ * Z- und X-Bon. Was davon gezeigt wird, sagt [shiftTipLines].
127
+ */
128
+ export interface ShiftTip extends ShiftTipTotals {
129
+ /**
130
+ * Offene Forderung an die Lade beim Oeffnen der Schicht („aus früheren Schichten“, Fix-Runde 2,
131
+ * R64); ohne offene Schicht der ganze Anspruch. Fehlt es (Backend bzw. Schicht vor dieser Regel), 0.
132
+ * Fuer den Leser geht auf: offen = aus früheren Schichten + verdient − über Lohn − schon genommen
133
+ * + selbst zurückgegeben.
134
+ */
135
+ carriedOverCents: number;
136
+ openCents: number;
137
+ }
138
+ /** Laufstand der Schicht (nur mit `cashView`). */
139
+ export interface ShiftRunningTotals {
140
+ cashSalesCents: number;
141
+ cashCancellationsCents: number;
142
+ /** Nach Zahlart sortiert. */
143
+ nonCashByMethod: NonCashAmount[];
144
+ /** Eingelöste Gutscheine in Cent (kein Bargeld, nicht in der Lade); fehlt bei älteren Servern, dann 0. */
145
+ voucherRedeemedCents: number;
146
+ receiptCount: number;
147
+ cancellationCount: number;
148
+ cashInCents: number;
149
+ cashOutCents: number;
150
+ /** Nach Name sortiert. */
151
+ tips: ShiftTipTotals[];
152
+ openingsWithoutSale: number;
153
+ }
154
+ /** Nachrechnung des Laufstands beim Abschluss: stimmt er, oder welche Felder weichen ab. */
155
+ export interface ShiftReconciliation {
156
+ matches: boolean;
157
+ /** Je abweichendem Feld (Name wie in [ShiftRunningTotals]) der gespeicherte und der nachgerechnete Wert. */
158
+ deviations: Record<string, {
159
+ stored: unknown;
160
+ recomputed: unknown;
161
+ }> | null;
162
+ }
163
+ export interface Shift {
164
+ id: string;
165
+ drawerId: string;
166
+ cashregisterId: string | null;
167
+ number: number;
168
+ status: ShiftStatus;
169
+ openedAt: string | null;
170
+ openedBy: ShiftActor | null;
171
+ openedDeviceId?: string | null;
172
+ closedAt?: string | null;
173
+ closedBy?: ShiftActor | null;
174
+ closedDeviceId?: string | null;
175
+ /** Startgeld; blind `null`, wenn es uebernommen wurde. */
176
+ openingFloatCents?: number | null;
177
+ openingDenominations?: Denominations | null;
178
+ openingFloatTakenOver?: boolean;
179
+ comment?: string | null;
180
+ /** Wurde blind gezaehlt (nach dem Abschluss); offen `null`. */
181
+ blind?: boolean | null;
182
+ countedCents?: number | null;
183
+ suggestedFloatCents?: number | null;
184
+ runningTotals?: ShiftRunningTotals;
185
+ expectedCents?: number | null;
186
+ differenceCents?: number | null;
187
+ reconciliation?: ShiftReconciliation | null;
188
+ /** Entnommen bzw. eingelegt nach dem Zaehlen. */
189
+ afterCount?: AfterCount;
190
+ /** Was danach in der Lade bleibt (Ist ∓ Betrag); fehlt blind (ohne `cashView`). */
191
+ leftInDrawerCents?: number;
192
+ }
193
+ /** Offener Trinkgeld-Anspruch eines Mitarbeiters (auszahlbar beim Abschluss oder per `tip_payout`). */
194
+ export interface TipClaim {
195
+ registerUserId: string;
196
+ name: string | null;
197
+ cents: number;
198
+ }
199
+ /** Eine gemeldete Oeffnung der Lade (nur mit `cashView`). */
200
+ export interface DrawerOpening {
201
+ /** Serverzeit des Eingangs. */
202
+ at: string | null;
203
+ /** Zeit am Geraet (bei nachgesendeten Oeffnungen frueher als `at`). */
204
+ deviceTime: string | null;
205
+ reason: OpeningReason | string;
206
+ by: {
207
+ name: string | null;
208
+ } | null;
209
+ /** Ging der Impuls an die Lade durch? */
210
+ ok: boolean;
211
+ }
212
+ /** Stand der Lade fuer die Kasse: Lade, Rechte, Schicht und was das Recht `cashView` zeigt. */
213
+ export interface DrawerState {
214
+ drawer: DrawerInfo;
215
+ rights: DrawerRights;
216
+ /** Die offene Schicht oder `null`; ohne Recht `shift` nur ihre Grunddaten, ohne Betrag (siehe [Shift]). */
217
+ shift?: Shift | null;
218
+ /** Schicht seit mindestens 16 Stunden offen; fehlt ohne Recht `shift`. */
219
+ longOpenWarning?: boolean;
220
+ tipClaims: TipClaim[];
221
+ /**
222
+ * Trinkgeld je Mitarbeiter in der laufenden Schicht und offene Ansprueche
223
+ * (auch aus frueheren Schichten), nach Name; fuer jede Sitzung, auch ohne
224
+ * `cashView` (Trinkgeld ist kein Bar-Soll). Wer ueberall 0 hat, fehlt.
225
+ */
226
+ tips: ShiftTip[];
227
+ /** Vorschlag fuer das Startgeld (letzter Stand); bei offener Schicht `null`. Nur mit `shift` und `cashView`. */
228
+ suggestedFloatCents?: number | null;
229
+ /** Soll in der Lade; ohne Schicht `null`. Nur mit `shift` und `cashView`. */
230
+ expectedCents?: number | null;
231
+ /** Die letzten Oeffnungen, neueste zuerst; nur mit `cashView`. */
232
+ recentOpenings?: DrawerOpening[];
233
+ /**
234
+ * Nummer der Schicht, aus der der Vorschlag stammt („von Schicht {number}
235
+ * übernommen“). Nur ohne offene Schicht und nur, wenn die letzte Schicht
236
+ * mit einem Betrag „Bleibt in der Kassenlade“ schloss.
237
+ */
238
+ suggestedFromShiftNumber?: number;
239
+ }
240
+ export interface CashMovement {
241
+ id: string;
242
+ number: number;
243
+ shiftId: string | null;
244
+ type: MovementType | string;
245
+ /** Mit Vorzeichen: Auszahlungen negativ. */
246
+ amountCents: number;
247
+ location: CashLocation | null;
248
+ note: string | null;
249
+ by: ShiftActor | null;
250
+ at: string | null;
251
+ recipient: {
252
+ registerUserId: string | null;
253
+ name: string | null;
254
+ } | null;
255
+ /** Gegenbuchung einer Umbuchung in der anderen Lade. */
256
+ counterpart: {
257
+ drawerId: string | null;
258
+ movementId: string | null;
259
+ } | null;
260
+ }
261
+ /** Kopf jedes Bons der Schicht. */
262
+ export interface ShiftReceiptHeader {
263
+ kind: ShiftReceiptKind;
264
+ /** Ohne `cashView` erstellt: Soll, Ist, Differenz und Barumsatz sind `null`. */
265
+ blind: boolean;
266
+ business: {
267
+ name: string | null;
268
+ };
269
+ cashregister: {
270
+ id: string | null;
271
+ name: string | null;
272
+ };
273
+ drawer: {
274
+ name: string | null;
275
+ };
276
+ }
277
+ export interface ShiftReceiptMovement {
278
+ number: number;
279
+ type: MovementType | string;
280
+ amountCents: number;
281
+ note: string | null;
282
+ byName: string | null;
283
+ at: string | null;
284
+ }
285
+ /** Trinkgeld auf dem Z- und X-Bon: offen = Anspruch beim Abschluss bzw. jetzt. */
286
+ export type ShiftReceiptTip = ShiftTip;
287
+ /** Bon-Daten des Z-Bons (`shift_close`) bzw. X-Bons (`shift_interim`). */
288
+ export interface ShiftSummaryReceipt extends ShiftReceiptHeader {
289
+ kind: 'shift_close' | 'shift_interim';
290
+ shift: {
291
+ number: number;
292
+ openedAt: string | null;
293
+ closedAt: string | null;
294
+ openedByName: string | null;
295
+ closedByName: string | null;
296
+ };
297
+ openingFloatCents: number | null;
298
+ openingFloatTakenOver: boolean;
299
+ openingDenominations: Denominations | null;
300
+ cashSalesCents: number | null;
301
+ cashCancellationsCents: number | null;
302
+ receiptCount: number;
303
+ cancellationCount: number;
304
+ nonCashByMethod: NonCashAmount[];
305
+ movements: ShiftReceiptMovement[];
306
+ tips: ShiftReceiptTip[];
307
+ expectedCents: number | null;
308
+ /** Beim X-Bon immer `null`. */
309
+ countedCents: number | null;
310
+ countedDenominations: Denominations | null;
311
+ differenceCents: number | null;
312
+ comment: string | null;
313
+ openingsWithoutSale: number;
314
+ /** Entnommen bzw. eingelegt nach dem Zaehlen; fehlt, wenn kein Geld bewegt wurde. */
315
+ afterCount?: AfterCount;
316
+ /**
317
+ * „Bleibt in der Kassenlade“; nur zusammen mit `afterCount`, blind `null`
318
+ * (mit „Entnommen“ ergaebe es sonst das Ist).
319
+ */
320
+ leftInDrawerCents?: number | null;
321
+ }
322
+ /** Bon-Daten einer Kassenbewegung. */
323
+ export interface CashMovementReceipt extends ShiftReceiptHeader {
324
+ kind: 'cash_movement';
325
+ shift: {
326
+ number: number;
327
+ } | null;
328
+ movement: {
329
+ number: number;
330
+ type: MovementType | string;
331
+ amountCents: number;
332
+ note: string | null;
333
+ location: CashLocation | null;
334
+ recipientName: string | null;
335
+ targetDrawerName: string | null;
336
+ byName: string | null;
337
+ at: string | null;
338
+ };
339
+ }
340
+ export type ShiftReceipt = ShiftSummaryReceipt | CashMovementReceipt;
341
+ export interface OpenShiftResult {
342
+ shift: Shift;
343
+ /** Unterschied zum uebergebenen Bestand beim Zaehlen; nur mit `cashView`. */
344
+ handoverDifferenceCents?: number | null;
345
+ }
346
+ export interface CashMovementResult {
347
+ movement: CashMovement;
348
+ receipt: CashMovementReceipt;
349
+ }
350
+ export interface DrawerOpeningsResult {
351
+ accepted: number;
352
+ /** Schon gemeldet (gleicher Schluessel): zaehlt nicht doppelt. */
353
+ duplicates: number;
354
+ /** Die Lade, in die gemeldet wurde: eine stillgelegte meldet in die Hauptlade. */
355
+ drawerId: string | null;
356
+ }
357
+ export interface ShiftReport {
358
+ shift: Shift;
359
+ /** Blind ohne `count_difference`/`handover_difference`. */
360
+ movements: CashMovement[];
361
+ /** X-Bon (offene Schicht) bzw. Z-Bon zum Nachdruck. */
362
+ receipt: ShiftSummaryReceipt;
363
+ }
364
+ /**
365
+ * Ergebnis von `closeMyShift`: `recount` = bitte noch einmal zaehlen (kein
366
+ * Fehler), `ready` = nur geprueft (`checkOnly`), Schliessen mit diesem Betrag
367
+ * ginge, `closed` = geschlossen mit Z-Bon.
368
+ */
369
+ export type CloseShiftResult = {
370
+ status: 'recount';
371
+ } | {
372
+ status: 'ready';
373
+ } | {
374
+ status: 'closed';
375
+ shift: Shift;
376
+ receipt: ShiftSummaryReceipt;
377
+ /** Bon „Kassenbewegung“ der Entnahme bzw. Einlage („Bon fürs Kuvert“); nur wenn Geld bewegt wurde. */
378
+ afterCountReceipt?: CashMovementReceipt;
379
+ lastShiftOfRegister: boolean;
380
+ };
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Schicht, Kassenstand und Lade-Protokoll: Kataloge und Modelle, wie sie am
3
+ * Kassenweg `/api/v3` ankommen (Backend schicht-endpoints.js, Vokabular
4
+ * `KASSEN_BEWEGUNG_ART`, `KASSEN_ORT`, `LADE_OEFFNUNG_GRUND`,
5
+ * `SCHICHT_BON_ART`). Die Modelle sind die Drahtform, Feld fuer Feld.
6
+ *
7
+ * **Fehlt ein Feld, fehlt das Recht.** Ohne `cashView` sendet der Server kein
8
+ * Soll, kein Ist, keinen Laufstand und keinen Vorschlag (Blindzaehlung); im
9
+ * Modell fehlen sie dann auch (`undefined`), nie stehen sie als `0` da. Auf
10
+ * dem Bon (`ShiftSummaryReceipt`) stehen dieselben Werte blind als `null`.
11
+ *
12
+ * Alle Betraege in Cent (Ganzzahl, Vorzeichen wie gebucht), Zeiten als
13
+ * ISO 8601 (UTC), Stueckelung als Karte „Wert in Cent“ -> Anzahl
14
+ * (`{ "2000": 1, "500": 3 }`).
15
+ */
16
+ /** Scheine und Muenzen in Euro-Cent, vom groessten Schein abwaerts (Backend `EUR_STUECKELUNG`). */
17
+ export const EUR_DENOMINATIONS = Object.freeze([50000, 20000, 10000, 5000, 2000, 1000, 500, 200, 100, 50, 20, 10, 5, 2, 1]);
18
+ /**
19
+ * Alle Arten einer Kassenbewegung (Katalog `KASSEN_BEWEGUNG_ART`). Die ersten
20
+ * acht sendet die Kasse ([MANUAL_MOVEMENT_TYPES]); `transfer_in` (Gegenseite
21
+ * einer Umbuchung), `expense`, `count_difference` und `handover_difference`
22
+ * vergibt nur der Server.
23
+ */
24
+ export const MOVEMENT_TYPES = Object.freeze([
25
+ 'change_in', 'cash_drop', 'owner_deposit', 'owner_withdrawal', 'tip_payout', 'other_in', 'other_out', 'transfer_out',
26
+ 'transfer_in', 'expense', 'count_difference', 'handover_difference',
27
+ ]);
28
+ /**
29
+ * Was die Kasse von Hand buchen darf. Pflichtangaben je Art (sonst lehnt der
30
+ * Server ab): `note` bei `other_in`/`other_out`, `recipientId` bei
31
+ * `tip_payout`, `targetDrawerId` bei `transfer_out`. `location` bei
32
+ * `change_in`/`cash_drop` ist freiwillig.
33
+ */
34
+ export const MANUAL_MOVEMENT_TYPES = Object.freeze([
35
+ 'change_in', 'cash_drop', 'owner_deposit', 'owner_withdrawal', 'tip_payout', 'other_in', 'other_out', 'transfer_out',
36
+ ]);
37
+ /** Woher Wechselgeld kommt bzw. wohin abgeschoepft wird (Katalog `KASSEN_ORT`). */
38
+ export const CASH_LOCATIONS = Object.freeze(['bank', 'safe']);
39
+ /**
40
+ * Warum die Lade aufging (Katalog `LADE_OEFFNUNG_GRUND`). `sale`,
41
+ * `cash_movement`, `shift_open` und `shift_close` meldet die Kasse selbst beim
42
+ * jeweiligen Vorgang; von Hand waehlt der Kassier aus [MANUAL_OPENING_REASONS].
43
+ */
44
+ export const OPENING_REASONS = Object.freeze([
45
+ 'sale', 'cash_movement', 'shift_open', 'shift_close', 'change', 'check', 'misoperation', 'other',
46
+ ]);
47
+ /** Die Gruende des Knopfs „Lade“; `other` braucht einen Text (`note`). */
48
+ export const MANUAL_OPENING_REASONS = Object.freeze(['change', 'check', 'misoperation', 'other']);
49
+ /**
50
+ * Was nach dem Zaehlen beim Schicht-Abschluss mit dem Geld geschieht (Katalog
51
+ * `NACH_ZAEHLUNG_ART`): `cash_drop` = entnehmen (gebucht als Abschoepfung),
52
+ * `change_in` = einlegen (gebucht als Wechselgeld). Beides Geldtransit, kein Umsatz.
53
+ */
54
+ export const AFTER_COUNT_TYPES = Object.freeze(['cash_drop', 'change_in']);
55
+ /** Arten der Bon-Daten (Katalog `SCHICHT_BON_ART`): Z-Bon, X-Bon, Kassenbewegung. */
56
+ export const SHIFT_RECEIPT_KINDS = Object.freeze(['shift_close', 'shift_interim', 'cash_movement']);
@@ -0,0 +1,208 @@
1
+ import type { InternerTransport } from '../client/aufrufe.js';
2
+ import { type AfterCountType, type CashLocation, type CashMovementResult, type CloseShiftResult, type Denominations, type DrawerOpeningsResult, type DrawerState, type ManualMovementType, type OpeningReason, type OpenShiftResult, type ShiftReport, type ShiftTipTotals } from './schicht-typen.js';
3
+ /**
4
+ * Schicht, Kassenstand und Lade-Protokoll an der Kasse (Backend
5
+ * schicht-endpoints.js). Alle sechs Aufrufe gibt es nur ueber den Kassenweg
6
+ * `/api/v3`; sie laufen mit dem Kassen-Benutzer-Weg (`registerUserAuth`).
7
+ *
8
+ * Rechte am Server: `shift` (Schicht oeffnen und schliessen, Bericht),
9
+ * `cashMove` (Ein- und Auszahlungen), `cashView` (Soll, Ist und Differenz
10
+ * sehen; ohne dieses Recht wird blind gezaehlt), `drawer` (Lade von Hand
11
+ * oeffnen). [getMyDrawerState] braucht kein Recht und nennt alle vier.
12
+ *
13
+ * **Schreiben mit Idempotenzschluessel:** [openMyShift],
14
+ * [recordMyCashMovement] und [closeMyShift] verlangen `idempotencyKey`, jeder
15
+ * Eintrag von [logMyDrawerOpenings] ebenso. Bei unklarem Ausgang
16
+ * (`outcome: 'unknown'`) denselben Vorgang mit **demselben** Schluessel noch
17
+ * einmal senden; er wirkt genau einmal. Ein neuer Schluessel ([newIdempotencyKey])
18
+ * gehoert nur zu einem neuen Vorgang.
19
+ *
20
+ * **Vor dem Senden geprueft wird nur, was ohne Netz sicher falsch ist:**
21
+ * fehlende Kennungen, ein fehlender Schluessel, Betraege, die keine Ganzzahl
22
+ * sind, eine leere Liste. Alles Fachliche (Katalog, Pflichtangaben je Art,
23
+ * Kassenstand, Anspruch) prueft der Server und meldet es mit seinem Code.
24
+ */
25
+ export interface GetMyDrawerStateOptions {
26
+ /** Ohne Angabe gilt die Hauptlade der Kasse (`cashregisterId`, beim Kassen-Benutzer-Weg die Kasse der Anmeldung). */
27
+ drawerId?: string;
28
+ cashregisterId?: string;
29
+ }
30
+ export interface OpenMyShiftOptions {
31
+ /** Bei „Erneut senden“ derselbe Schluessel wie beim ersten Versuch. */
32
+ idempotencyKey: string;
33
+ drawerId: string;
34
+ /**
35
+ * Gezaehltes Startgeld in Cent (`0` erlaubt). Genau eines von
36
+ * `openingFloatCents` und `takeOver: true`.
37
+ */
38
+ openingFloatCents?: number;
39
+ /** Stueckelung zum gezaehlten Startgeld; die Summe muss stimmen ([sumDenominations]). */
40
+ denominations?: Denominations | null;
41
+ /** Den Bestand der Vorschicht ungezaehlt uebernehmen („Bestand übernehmen“). */
42
+ takeOver?: boolean;
43
+ }
44
+ export interface RecordMyCashMovementOptions {
45
+ idempotencyKey: string;
46
+ drawerId: string;
47
+ type: ManualMovementType;
48
+ /** Positiv; das Vorzeichen setzt der Server nach der Art. */
49
+ amountCents: number;
50
+ /** Freiwillig bei `change_in` und `cash_drop` (woher bzw. wohin); ohne Angabe bleibt der Ort leer. */
51
+ location?: CashLocation;
52
+ /** Grund; Pflicht bei `other_in` und `other_out`, hoechstens 200 Zeichen. */
53
+ note?: string;
54
+ /** Kassen-Benutzer, Pflicht bei `tip_payout`. */
55
+ recipientId?: string;
56
+ /** Ziel-Lade, Pflicht bei `transfer_out`. */
57
+ targetDrawerId?: string;
58
+ }
59
+ /** Eine Oeffnung der Lade, wie die Kasse sie puffert und meldet. */
60
+ export interface DrawerOpeningEntry {
61
+ /** Je Eintrag ein eigener Schluessel: ein zweites Melden zaehlt nicht doppelt. */
62
+ idempotencyKey: string;
63
+ reason: OpeningReason;
64
+ /** Ging der Impuls an die Lade durch? Fehlgeschlagene werden mit `false` gemeldet. */
65
+ ok: boolean;
66
+ /** Zeit am Geraet: Millisekunden seit 1970 oder ISO 8601. */
67
+ deviceTime: number | string;
68
+ /** Text, Pflicht bei `other`. */
69
+ note?: string;
70
+ /** Beleg bei `sale`. */
71
+ receiptId?: string;
72
+ /** Fehlertext eines fehlgeschlagenen Impulses. */
73
+ error?: string;
74
+ /** Lade der Oeffnung (gepuffert); muss `drawerId` der Anfrage sein, sonst `validation`. */
75
+ drawerId?: string;
76
+ /**
77
+ * Wer die Lade oeffnete (gepuffert: der damals angemeldete Mitarbeiter). Der
78
+ * Server nimmt ihn nur, wenn er zum Betrieb gehoert und der Kasse zugewiesen
79
+ * ist, den Namen aus dem Konto; sonst gilt der sendende Benutzer.
80
+ */
81
+ operator?: {
82
+ registerUserId: string;
83
+ name?: string | null;
84
+ };
85
+ }
86
+ export interface LogMyDrawerOpeningsOptions {
87
+ drawerId: string;
88
+ /** 1–50 Eintraege; offline gepufferte Oeffnungen werden spaeter nachgesendet. */
89
+ entries: DrawerOpeningEntry[];
90
+ }
91
+ export interface GetMyShiftReportOptions {
92
+ shiftId: string;
93
+ }
94
+ /** Trinkgeld-Auszahlung beim Abschluss: hoechstens der offene Anspruch (`DrawerState.tipClaims`). */
95
+ export interface TipPayout {
96
+ registerUserId: string;
97
+ cents: number;
98
+ }
99
+ export interface CloseMyShiftOptions {
100
+ idempotencyKey: string;
101
+ shiftId: string;
102
+ /** Gezaehltes Bargeld in Cent (`0` erlaubt). */
103
+ countedCents: number;
104
+ denominations?: Denominations | null;
105
+ /** Wird angenommen, wirkt aber nicht: den Versuch zaehlt der Server (hoechstens zwei). */
106
+ attempt?: number;
107
+ /** Begruendung; Pflicht, wenn die Abweichung die Schwelle uebersteigt (`comment_required`). */
108
+ comment?: string;
109
+ tipPayouts?: TipPayout[];
110
+ /**
111
+ * Nach dem Zaehlen Geld entnehmen (`cash_drop`) oder einlegen (`change_in`),
112
+ * `amountCents` positiv. Entnehmen hoechstens den gezaehlten Betrag
113
+ * (`cash_movement_exceeds_balance`); ein Betrag ueber 0 braucht das Recht
114
+ * `cashMove` (`after_count_not_permitted`). `0` oder ohne Angabe: alles
115
+ * bleibt in der Lade. Gebucht wird nur beim tatsaechlichen Schliessen, nie bei
116
+ * `recount`; die Antwort traegt dann `afterCountReceipt` („Bon fürs Kuvert“).
117
+ */
118
+ afterCount?: AfterCountOption | null;
119
+ /**
120
+ * Nur pruefen, nicht schliessen: dieselbe Pruefung wie beim Schliessen, damit
121
+ * die Kasse vor „Geld entnehmen“ weiss, ob nachgezaehlt (`recount`) oder
122
+ * begruendet (`comment_required`) werden muss. Antwort `ready`, wenn das
123
+ * Schliessen mit diesem Betrag ginge; gebucht wird nichts (auch kein
124
+ * `afterCount`). Die Zaehlung zaehlt als einer der zwei Versuche; schliesst
125
+ * danach derselbe Betrag, gilt er als dieselbe Zaehlung.
126
+ */
127
+ checkOnly?: boolean;
128
+ }
129
+ /** Entnehmen oder Einlegen beim Abschluss (siehe [CloseMyShiftOptions.afterCount]). */
130
+ export interface AfterCountOption {
131
+ type: AfterCountType;
132
+ amountCents: number;
133
+ }
134
+ /** Stand der Lade: Rechte, offene Schicht, Trinkgeld-Ansprueche und (mit `cashView`) Soll und letzte Oeffnungen. */
135
+ export declare function getMyDrawerState(transport: InternerTransport, options?: GetMyDrawerStateOptions): Promise<DrawerState>;
136
+ /** Schicht oeffnen: gezaehltes Startgeld oder den Bestand uebernehmen (Recht `shift`). */
137
+ export declare function openMyShift(transport: InternerTransport, options: OpenMyShiftOptions): Promise<OpenShiftResult>;
138
+ /** Ein- oder Auszahlung buchen (Recht `cashMove`); Antwort mit den Bon-Daten „Kassenbewegung“. */
139
+ export declare function recordMyCashMovement(transport: InternerTransport, options: RecordMyCashMovementOptions): Promise<CashMovementResult>;
140
+ /** Oeffnungen der Lade melden (auch nachgesendet); doppelt gemeldete zaehlen einmal. */
141
+ export declare function logMyDrawerOpenings(transport: InternerTransport, options: LogMyDrawerOpeningsOptions): Promise<DrawerOpeningsResult>;
142
+ /** Bericht der Schicht: Schicht, Bewegungen und Bon-Daten (X-Bon, bei geschlossener Schicht der Z-Bon). */
143
+ export declare function getMyShiftReport(transport: InternerTransport, options: GetMyShiftReportOptions): Promise<ShiftReport>;
144
+ /**
145
+ * Schicht schliessen (Recht `shift`). `status: 'recount'` heisst: bitte noch
146
+ * einmal zaehlen und erneut senden (kein Fehler); nach dem zweiten Zaehlen
147
+ * schliesst der Server, bei grosser Abweichung nur mit `comment`. Mit
148
+ * `checkOnly` nur pruefen: `status: 'ready'` statt Schliessen.
149
+ */
150
+ export declare function closeMyShift(transport: InternerTransport, options: CloseMyShiftOptions): Promise<CloseShiftResult>;
151
+ /**
152
+ * Summe einer Stueckelung in Cent, oder `null`, wenn sie keine ist: ein
153
+ * Schluessel, der kein Euro-Wert ist ([EUR_DENOMINATIONS], genau so
154
+ * geschrieben, `"500"`, nicht `"0500"`), eine Anzahl, die keine ganze Zahl
155
+ * von 0 bis 100 000 ist. Dieselbe Regel wie am Server
156
+ * (`invalid_denominations`). Gemeinsame Faelle mit dem Dart-Zwilling:
157
+ * `fixtures/shift-cases.json`.
158
+ */
159
+ export declare function sumDenominations(denominations: Readonly<Record<string | number, number>>): number | null;
160
+ /**
161
+ * Ein eingetippter Betrag in Cent, **ohne Gleitkomma**: `'12,5'` → `1250`,
162
+ * `'0'` → `0` (anders als beim Kassieren ist 0 hier gueltig: eine leere Lade,
163
+ * kein Startgeld). Dezimaltrenner Komma oder Punkt mit einer oder zwei
164
+ * Nachkommastellen; `null` fuer leer, Vorzeichen, Tausendertrenner
165
+ * (`'1.000'`, `'1 000'`), drei Nachkommastellen, Euro-Zeichen und alles
166
+ * ueber einer sicheren Ganzzahl. Gemeinsame Faelle mit dem Dart-Zwilling:
167
+ * `fixtures/shift-cases.json`.
168
+ */
169
+ export declare function parseCentsAllowZero(text: string): number | null;
170
+ /**
171
+ * Ein frischer Idempotenzschluessel fuer einen neuen Vorgang: `kasse-` und
172
+ * 32 Hex-Zeichen aus dem sicheren Zufall (`crypto.getRandomValues`), dieselbe
173
+ * Form wie beim Zaehlen der Inventur in der Kassen-App. Fuer „Erneut senden“
174
+ * nie einen neuen ziehen, sondern den alten behalten.
175
+ */
176
+ export declare function newIdempotencyKey(): string;
177
+ /** Zeilen des Trinkgelds eines Mitarbeiters (siehe [shiftTipLines]). */
178
+ export type ShiftTipLineKey = 'shift_receipt.tip_carried_over' | 'shift_receipt.tip_earned' | 'shift_receipt.tip_retained' | 'shift_receipt.tip_paid_out' | 'shift_receipt.tip_refunded_by_employee' | 'shift_receipt.tip_open';
179
+ export interface ShiftTipLine {
180
+ key: ShiftTipLineKey;
181
+ /** Das Wort aus dem Katalog ([labelText]). */
182
+ text: string;
183
+ cents: number;
184
+ }
185
+ /**
186
+ * Was vom Trinkgeld eines Mitarbeiters dasteht – auf dem Bon, im Kassenstand
187
+ * und beim Abschluss gleich (Rulings R50/R51): „verdient“ (sofort + über
188
+ * Lohn) immer, „über Lohn“, „schon genommen“, „selbst zurückgegeben“ (R62)
189
+ * und „offen“ nur, wenn nicht 0. Davor „aus früheren Schichten“ (R64, nur ≠ 0):
190
+ * offen = aus früheren Schichten + verdient − über Lohn − schon genommen +
191
+ * selbst zurückgegeben.
192
+ * Ist alles 0, gibt es keine Zeile, und der Mitarbeiter entfaellt ganz. Ohne
193
+ * `openCents` (Laufstand) zaehlt offen als 0. Gemeinsame Faelle mit dem
194
+ * Dart-Zwilling: `fixtures/shift-cases.json`.
195
+ */
196
+ export declare function shiftTipLines(tip: Omit<ShiftTipTotals, 'refundedByEmployeeCents'> & {
197
+ refundedByEmployeeCents?: number;
198
+ carriedOverCents?: number;
199
+ openCents?: number;
200
+ }): ShiftTipLine[];
201
+ /**
202
+ * Dauer für Menschen aus ganzen Minuten: unter 1 Std. „45 Min.“, unter 1 Tag
203
+ * „8 Std. 32 Min.“ (bei 0 Minuten nur „8 Std.“), ab 1 Tag „1 Tag 3 Std.“ bzw.
204
+ * „2 Tage“ (bei 0 Stunden ohne Stunden). Negatives und Unsinn zählt als 0;
205
+ * Bruchteile werden abgeschnitten. Gemeinsame Fälle mit dem Dart-Zwilling:
206
+ * `fixtures/shift-cases.json` (`durationText`).
207
+ */
208
+ export declare function durationText(minutes: number): string;