@kreiseck/kasseneck-api 1.10.1 → 1.11.1

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 (152) hide show
  1. package/CHANGELOG.md +247 -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 +11 -3
  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 +2 -2
  16. package/dist/cjs/models/index.js +10 -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/models/receipt.d.ts +19 -1
  26. package/dist/cjs/models/receipt.js +28 -4
  27. package/dist/cjs/pos/client.js +10 -0
  28. package/dist/cjs/pos/errors.d.ts +4 -3
  29. package/dist/cjs/pos/errors.js +21 -2
  30. package/dist/cjs/pos/index.d.ts +6 -2
  31. package/dist/cjs/pos/index.js +33 -3
  32. package/dist/cjs/pos/schicht-lesen.d.ts +39 -0
  33. package/dist/cjs/pos/schicht-lesen.js +405 -0
  34. package/dist/cjs/pos/schicht-typen.d.ts +380 -0
  35. package/dist/cjs/pos/schicht-typen.js +59 -0
  36. package/dist/cjs/pos/schicht.d.ts +208 -0
  37. package/dist/cjs/pos/schicht.js +235 -0
  38. package/dist/cjs/pos/settings.d.ts +37 -1
  39. package/dist/cjs/pos/settings.js +43 -3
  40. package/dist/cjs/pos/texte.d.ts +627 -0
  41. package/dist/cjs/pos/texte.js +236 -0
  42. package/dist/cjs/react/index.js +1 -1
  43. package/dist/cjs/receipt/grid.js +2 -2
  44. package/dist/cjs/receipt/layout.d.ts +5 -0
  45. package/dist/cjs/receipt/layout.js +14 -5
  46. package/dist/cjs/receipt/shift-sheet.d.ts +33 -0
  47. package/dist/cjs/receipt/shift-sheet.js +207 -0
  48. package/dist/cjs/register/errors.d.ts +1 -1
  49. package/dist/cjs/register/errors.js +2 -0
  50. package/dist/cjs/stored/draht.d.ts +1 -1
  51. package/dist/cjs/stored/draht.js +4 -3
  52. package/dist/cjs/stored/einstellungen.js +26 -3
  53. package/dist/cjs/stored/vokabular.d.ts +1 -1
  54. package/dist/cjs/stored/vokabular.js +118 -12
  55. package/dist/cjs/version.d.ts +1 -1
  56. package/dist/cjs/version.js +1 -1
  57. package/dist/esm/client/aufrufe.d.ts +5 -5
  58. package/dist/esm/client/aufrufe.js +22 -2
  59. package/dist/esm/client/errors.js +2 -0
  60. package/dist/esm/client/receipts.d.ts +34 -1
  61. package/dist/esm/client/receipts.js +35 -2
  62. package/dist/esm/index.d.ts +1 -1
  63. package/dist/esm/index.js +1 -1
  64. package/dist/esm/inventory/vertrag.d.ts +1 -1
  65. package/dist/esm/inventory/vertrag.js +2 -0
  66. package/dist/esm/invoice/vertrag.d.ts +1 -1
  67. package/dist/esm/invoice/vertrag.js +2 -0
  68. package/dist/esm/models/cancellation.d.ts +49 -1
  69. package/dist/esm/models/cancellation.js +132 -0
  70. package/dist/esm/models/index.d.ts +2 -2
  71. package/dist/esm/models/index.js +2 -2
  72. package/dist/esm/models/payment-errors.d.ts +1 -1
  73. package/dist/esm/models/payment-errors.js +2 -0
  74. package/dist/esm/models/receipt-email.d.ts +1 -1
  75. package/dist/esm/models/receipt-email.js +2 -0
  76. package/dist/esm/models/receipt-errors.d.ts +1 -1
  77. package/dist/esm/models/receipt-errors.js +2 -0
  78. package/dist/esm/models/receipt-item.d.ts +10 -0
  79. package/dist/esm/models/receipt-item.js +3 -0
  80. package/dist/esm/models/receipt.d.ts +19 -1
  81. package/dist/esm/models/receipt.js +27 -4
  82. package/dist/esm/pos/client.js +11 -1
  83. package/dist/esm/pos/errors.d.ts +4 -3
  84. package/dist/esm/pos/errors.js +21 -2
  85. package/dist/esm/pos/index.d.ts +6 -2
  86. package/dist/esm/pos/index.js +7 -2
  87. package/dist/esm/pos/schicht-lesen.d.ts +39 -0
  88. package/dist/esm/pos/schicht-lesen.js +392 -0
  89. package/dist/esm/pos/schicht-typen.d.ts +380 -0
  90. package/dist/esm/pos/schicht-typen.js +56 -0
  91. package/dist/esm/pos/schicht.d.ts +208 -0
  92. package/dist/esm/pos/schicht.js +222 -0
  93. package/dist/esm/pos/settings.d.ts +37 -1
  94. package/dist/esm/pos/settings.js +40 -2
  95. package/dist/esm/pos/texte.d.ts +627 -0
  96. package/dist/esm/pos/texte.js +236 -0
  97. package/dist/esm/react/index.js +1 -1
  98. package/dist/esm/receipt/grid.js +2 -2
  99. package/dist/esm/receipt/layout.d.ts +5 -0
  100. package/dist/esm/receipt/layout.js +15 -6
  101. package/dist/esm/receipt/shift-sheet.d.ts +33 -0
  102. package/dist/esm/receipt/shift-sheet.js +203 -0
  103. package/dist/esm/register/errors.d.ts +1 -1
  104. package/dist/esm/register/errors.js +2 -0
  105. package/dist/esm/stored/draht.d.ts +1 -1
  106. package/dist/esm/stored/draht.js +4 -3
  107. package/dist/esm/stored/einstellungen.js +27 -4
  108. package/dist/esm/stored/vokabular.d.ts +1 -1
  109. package/dist/esm/stored/vokabular.js +118 -12
  110. package/dist/esm/version.d.ts +1 -1
  111. package/dist/esm/version.js +1 -1
  112. package/fixtures/cancellation-tip-cases.json +679 -0
  113. package/fixtures/expected/cash-movement.grid32.txt +15 -0
  114. package/fixtures/expected/cash-movement.grid48.txt +15 -0
  115. package/fixtures/expected/cash-movement.lines.json +171 -0
  116. package/fixtures/expected/shift-close-blind.grid32.txt +28 -0
  117. package/fixtures/expected/shift-close-blind.grid48.txt +27 -0
  118. package/fixtures/expected/shift-close-blind.lines.json +275 -0
  119. package/fixtures/expected/shift-close-put-in.grid32.txt +38 -0
  120. package/fixtures/expected/shift-close-put-in.grid48.txt +35 -0
  121. package/fixtures/expected/shift-close-put-in.lines.json +403 -0
  122. package/fixtures/expected/shift-close-take-out.grid32.txt +38 -0
  123. package/fixtures/expected/shift-close-take-out.grid48.txt +35 -0
  124. package/fixtures/expected/shift-close-take-out.lines.json +403 -0
  125. package/fixtures/expected/shift-close.grid32.txt +37 -0
  126. package/fixtures/expected/shift-close.grid48.txt +35 -0
  127. package/fixtures/expected/shift-close.lines.json +413 -0
  128. package/fixtures/expected/shift-interim.grid32.txt +28 -0
  129. package/fixtures/expected/shift-interim.grid48.txt +27 -0
  130. package/fixtures/expected/shift-interim.lines.json +293 -0
  131. package/fixtures/hobex-hps-codes.json +1 -1
  132. package/fixtures/invoice-api.schema.json +1 -1
  133. package/fixtures/invoice-texts.json +1 -1
  134. package/fixtures/pos-message-cases.json +45 -0
  135. package/fixtures/pos-texts.json +723 -1
  136. package/fixtures/receipt-due-generated.json +1 -1
  137. package/fixtures/shift-cases.json +87 -0
  138. package/fixtures/shift-receipts.json +41 -0
  139. package/fixtures/surface.json +119 -3
  140. package/fixtures/v3/antworten/belege.json +1 -1
  141. package/fixtures/v3/antworten/belegmail.json +1 -1
  142. package/fixtures/v3/antworten/kasse-belege.json +1 -1
  143. package/fixtures/v3/antworten/kasse.json +10874 -4865
  144. package/fixtures/v3/antworten/lager.json +1 -1
  145. package/fixtures/v3/antworten/rechnungen.json +1 -1
  146. package/fixtures/v3/antworten/storno.json +1 -1
  147. package/fixtures/v3/stored/belege.json +18 -6
  148. package/fixtures/v3/stored/kasse.json +4667 -183
  149. package/fixtures/v3/stored/rechnungen.json +1 -1
  150. package/fixtures/v3/v3-vokabular.json +739 -32
  151. package/fixtures/v3/zahlbetrag-faelle.json +1 -1
  152. package/package.json +1 -1
@@ -300,6 +300,7 @@ exports.INVENTORY_ERROR_CODES = [
300
300
  */
301
301
  exports.INVENTORY_REQUEST_ERROR_CODES = [
302
302
  'account_not_found',
303
+ 'action_confirmation_required',
303
304
  'admin_required',
304
305
  'api_not_approved',
305
306
  'app_check_invalid',
@@ -319,6 +320,7 @@ exports.INVENTORY_REQUEST_ERROR_CODES = [
319
320
  'register_user_not_allowed',
320
321
  'register_user_not_found',
321
322
  'response_translation_failed',
323
+ 'second_factor_required',
322
324
  'session_expired',
323
325
  'session_other_cashregister',
324
326
  'unauthorized',
@@ -37,7 +37,7 @@ export type InvoiceErrorCode = (typeof INVOICE_ERROR_CODES)[number];
37
37
  * genau der Katalog des Servers (das Backend vergleicht ihn mit dem Schema);
38
38
  * die Helfer in `./invoice` erkennen beide Listen.
39
39
  */
40
- export declare const INVOICE_REQUEST_ERROR_CODES: readonly ["account_not_found", "admin_required", "api_not_approved", "app_check_invalid", "app_check_missing", "cashregister_not_assigned", "cashregister_not_found", "cashregister_token_invalid", "cashregister_token_missing", "dialect_mismatch", "internal_translation_error", "live_not_enabled", "method_not_allowed", "mfa_required", "not_found", "password_change_required", "register_user_no_business", "register_user_not_allowed", "register_user_not_found", "response_translation_failed", "session_expired", "session_other_cashregister", "unauthorized", "user_disabled", "user_verification_failed", "route_missing"];
40
+ export declare const INVOICE_REQUEST_ERROR_CODES: readonly ["account_not_found", "action_confirmation_required", "admin_required", "api_not_approved", "app_check_invalid", "app_check_missing", "cashregister_not_assigned", "cashregister_not_found", "cashregister_token_invalid", "cashregister_token_missing", "dialect_mismatch", "internal_translation_error", "live_not_enabled", "method_not_allowed", "mfa_required", "not_found", "password_change_required", "register_user_no_business", "register_user_not_allowed", "register_user_not_found", "response_translation_failed", "second_factor_required", "session_expired", "session_other_cashregister", "unauthorized", "user_disabled", "user_verification_failed", "route_missing"];
41
41
  export type InvoiceRequestErrorCode = (typeof INVOICE_REQUEST_ERROR_CODES)[number];
42
42
  /** Gruende einer Gutschrift. Der Server druckt den deutschen Anzeigetext. */
43
43
  export declare const CREDIT_NOTE_REASONS: readonly ["cancellation", "price_reduction", "return", "incorrect_invoice", "other"];
@@ -91,6 +91,7 @@ exports.INVOICE_ERROR_CODES = [
91
91
  */
92
92
  exports.INVOICE_REQUEST_ERROR_CODES = [
93
93
  'account_not_found',
94
+ 'action_confirmation_required',
94
95
  'admin_required',
95
96
  'api_not_approved',
96
97
  'app_check_invalid',
@@ -110,6 +111,7 @@ exports.INVOICE_REQUEST_ERROR_CODES = [
110
111
  'register_user_not_allowed',
111
112
  'register_user_not_found',
112
113
  'response_translation_failed',
114
+ 'second_factor_required',
113
115
  'session_expired',
114
116
  'session_other_cashregister',
115
117
  'unauthorized',
@@ -1,5 +1,6 @@
1
1
  import { KasseneckApiError } from '../client/errors.js';
2
2
  import type { Receipt } from './receipt.js';
3
+ import type { ReceiptPaymentInput } from './receipt-payment.js';
3
4
  /**
4
5
  * Storno-Metadaten — Zwilling von `functions/storno-core.js`.
5
6
  *
@@ -53,7 +54,7 @@ export declare function isReturnDisposition(value: unknown): value is ReturnDisp
53
54
  * [PAYMENT_ERROR_CODES]. Ein Code ausserhalb bleibt ueber
54
55
  * `KasseneckApiError.code` lesbar.
55
56
  */
56
- export declare const CANCELLATION_ERROR_CODES: readonly ["receipt_not_found", "receipt_type_not_cancellable", "training_receipt", "already_cancelled", "invalid_line", "quantity_exceeds_remaining", "unknown_reason", "note_too_long", "invalid_items", "cashregister_not_assigned", "not_permitted", "own_receipts_only", "cashregister_incomplete", "cancellation_failed", "cancellation_payments_required", "cancellation_refund_exceeds_payment", "cancellation_refund_reference_required", "cancellation_refund_reference_unknown", "cancellation_outcome_unknown", "invalid_return_disposition", "account_not_found", "admin_required", "api_not_approved", "app_check_invalid", "app_check_missing", "cashregister_not_found", "cashregister_token_invalid", "cashregister_token_missing", "dialect_mismatch", "internal_translation_error", "live_not_enabled", "method_not_allowed", "mfa_required", "not_found", "password_change_required", "register_user_no_business", "register_user_not_allowed", "register_user_not_found", "response_translation_failed", "session_expired", "session_other_cashregister", "unauthorized", "user_disabled", "user_verification_failed", "validation", "route_missing", "response_unreadable"];
57
+ export declare const CANCELLATION_ERROR_CODES: readonly ["receipt_not_found", "receipt_type_not_cancellable", "training_receipt", "already_cancelled", "invalid_line", "quantity_exceeds_remaining", "unknown_reason", "note_too_long", "invalid_items", "cashregister_not_assigned", "not_permitted", "own_receipts_only", "cashregister_incomplete", "cancellation_failed", "cancellation_payments_required", "cancellation_refund_exceeds_payment", "cancellation_refund_reference_required", "cancellation_refund_reference_unknown", "cancellation_outcome_unknown", "invalid_return_disposition", "tip_refund_conflict", "invalid_tip_refund_source", "tip_refund_source_requires_cash", "tip_keep_not_possible_with_voucher", "account_not_found", "action_confirmation_required", "admin_required", "api_not_approved", "app_check_invalid", "app_check_missing", "cashregister_not_found", "cashregister_token_invalid", "cashregister_token_missing", "dialect_mismatch", "internal_translation_error", "live_not_enabled", "method_not_allowed", "mfa_required", "not_found", "password_change_required", "register_user_no_business", "register_user_not_allowed", "register_user_not_found", "response_translation_failed", "second_factor_required", "session_expired", "session_other_cashregister", "unauthorized", "user_disabled", "user_verification_failed", "validation", "route_missing", "response_unreadable"];
57
58
  export type CancellationErrorCode = (typeof CANCELLATION_ERROR_CODES)[number];
58
59
  export declare function isCancellationErrorCode(value: unknown): value is CancellationErrorCode;
59
60
  /** Der Code eines geworfenen Fehlers, wenn er in [CANCELLATION_ERROR_CODES] steht; sonst `undefined`. */
@@ -131,3 +132,50 @@ export interface CancellationFieldError {
131
132
  }
132
133
  /** Die Feldfehler einer `validation`-Antwort; leer, wenn es keine sind. */
133
134
  export declare function cancellationFieldErrors(error: unknown): CancellationFieldError[];
135
+ /**
136
+ * Woher das Trinkgeld beim Storno zurueckkommt: `drawer` – die Lade zahlt es aus („offen“ sinkt,
137
+ * kann negativ werden); `employee` – die Mitarbeiterin gibt es selbst („verdient“ und „schon
138
+ * genommen“ sinken, die Lade bleibt unberuehrt). Nur bei Barrueckgabe.
139
+ */
140
+ export declare const TIP_REFUND_SOURCES: readonly ["drawer", "employee"];
141
+ export type TipRefundSource = (typeof TIP_REFUND_SOURCES)[number];
142
+ export declare function isTipRefundSource(value: unknown): value is TipRefundSource;
143
+ /**
144
+ * Noch nicht storniertes Mitarbeiter-Trinkgeld in Cent. Groesser 0 heisst: das Storno fragt
145
+ * „Trinkgeld auch zurueckgeben?“ (auch beim Teilstorno). Inhaber-Trinkgeld ist Umsatz und folgt
146
+ * der Ware -- dafuer gibt es keine Frage.
147
+ */
148
+ export declare function openStaffTipCents(receipt: Receipt, nowMs?: number): number;
149
+ /**
150
+ * Fragt das Storno „Trinkgeld auch zurueckgeben?“ – nur bei noch offenem Mitarbeiter-Trinkgeld und
151
+ * ohne Wertgutschein am Beleg. Einen Wertgutschein spiegelt nur ein Storno, der alles nimmt; darum
152
+ * geht das Trinkgeld dort immer mit (der Server weist `refundTip: false` mit
153
+ * `tip_keep_not_possible_with_voucher` ab), die Kasse zeigt stattdessen
154
+ * `cancellation.tip_refunded_with_voucher` und sendet `refundTip: true`. Die Frage nach der Quelle
155
+ * bleibt ([asksTipRefundSource]).
156
+ */
157
+ export declare function asksRefundTip(receipt: Receipt, nowMs?: number): boolean;
158
+ /**
159
+ * Die Positionen, die nach der Antwort auf „Trinkgeld auch zurueckgeben?“ hinausgehen -- dieselbe
160
+ * Regel wie im Backend. `items` fehlt = ganzer Beleg.
161
+ * - Ja: zum Teilstorno kommt das offene Trinkgeld dazu; der ganze Beleg bleibt `undefined`.
162
+ * - Nein: ohne das Mitarbeiter-Trinkgeld; der ganze Beleg wird zur Liste aller Restmengen ausser dem
163
+ * Trinkgeld (leer = nichts mehr zu stornieren). Ist kein Mitarbeiter-Trinkgeld mehr offen (keines
164
+ * oder schon storniert), bleibt der Wunsch, wie er ist – „Nein“ wirkt dann wie keine Wahl.
165
+ */
166
+ export declare function cancellationItemsForTip(receipt: Receipt, items: readonly CancellationItem[] | undefined, refundTip: boolean, nowMs?: number): CancellationItem[] | undefined;
167
+ /**
168
+ * Was dieses Storno bar zurueckgibt (positiv, Cent): mit Rueckzahlungen deren Barteil; ohne
169
+ * (der Server spiegelt die eine Zahlung des Originals) alles, wenn das Original bar bezahlt war.
170
+ * Bei mehr als einer Zahlung ohne Angabe 0: welcher Teil bar zurueckgeht, ist dann offen (ein
171
+ * Teilstorno verlangt ohnehin `payments`). Beide Kassen schicken bei mehr als einer Zahlung darum
172
+ * immer die Rueckzahlungen mit und rechnen hier mit ihnen.
173
+ */
174
+ export declare function cancellationCashRefundCents(receipt: Receipt, payments: readonly ReceiptPaymentInput[] | undefined, dueCents: number): number;
175
+ /**
176
+ * Fragt das Storno nach „Ja“ auch „Woher kommt das Trinkgeld zurueck?“ – nur, wenn das Bargeld des
177
+ * Stornos (`cashRefundCents`, siehe [cancellationCashRefundCents]) das offene Mitarbeiter-Trinkgeld
178
+ * deckt und die Mitarbeiterin wenigstens eines davon schon hatte. Bei Kartenrueckgabe geht es auf
179
+ * die Karte zurueck; ueber Lohn behaltenes Trinkgeld kommt immer aus der Lade.
180
+ */
181
+ export declare function asksTipRefundSource(receipt: Receipt, cashRefundCents: number, nowMs?: number): boolean;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CANCELLATION_RESERVATION_MS = exports.CANCELLATION_STATUSES = exports.CANCELLATION_ERROR_CODES = exports.RETURN_DISPOSITIONS = exports.CANCELLATION_REASONS = void 0;
3
+ exports.TIP_REFUND_SOURCES = exports.CANCELLATION_RESERVATION_MS = exports.CANCELLATION_STATUSES = exports.CANCELLATION_ERROR_CODES = exports.RETURN_DISPOSITIONS = exports.CANCELLATION_REASONS = void 0;
4
4
  exports.isCancellationReason = isCancellationReason;
5
5
  exports.isReturnDisposition = isReturnDisposition;
6
6
  exports.isCancellationErrorCode = isCancellationErrorCode;
@@ -8,7 +8,14 @@ exports.cancellationErrorCode = cancellationErrorCode;
8
8
  exports.isCancellationError = isCancellationError;
9
9
  exports.remainingQuantities = remainingQuantities;
10
10
  exports.cancellationFieldErrors = cancellationFieldErrors;
11
+ exports.isTipRefundSource = isTipRefundSource;
12
+ exports.openStaffTipCents = openStaffTipCents;
13
+ exports.asksRefundTip = asksRefundTip;
14
+ exports.cancellationItemsForTip = cancellationItemsForTip;
15
+ exports.cancellationCashRefundCents = cancellationCashRefundCents;
16
+ exports.asksTipRefundSource = asksTipRefundSource;
11
17
  const fehlercodes_js_1 = require("../client/fehlercodes.js");
18
+ const receipt_item_js_1 = require("./receipt-item.js");
12
19
  /**
13
20
  * Storno-Metadaten — Zwilling von `functions/storno-core.js`.
14
21
  *
@@ -85,8 +92,14 @@ exports.CANCELLATION_ERROR_CODES = Object.freeze([
85
92
  'cancellation_refund_reference_unknown', // refundOf nennt keine Zahlung des Originals
86
93
  'cancellation_outcome_unknown', // Ausgang unklar: nachlesen, nie wiederholen
87
94
  'invalid_return_disposition', // Rueckgabe-Wahl nicht restock, defective oder disposed (Lager)
95
+ // Trinkgeld beim Storno (refundTip/tipRefundSource, Schichten-Spec Nachtrag 5)
96
+ 'tip_refund_conflict', // Wahl passt nicht zum Storno (Trinkgeld trotz Nein, Quelle ohne Trinkgeld)
97
+ 'invalid_tip_refund_source', // tipRefundSource nicht drawer oder employee
98
+ 'tip_refund_source_requires_cash', // eine Quelle nur, wenn das Bargeld des Stornos das Trinkgeld deckt
99
+ 'tip_keep_not_possible_with_voucher', // refundTip false an einem Beleg mit Wertgutschein (das Trinkgeld geht mit)
88
100
  // Anmeldung und Rand (errorCodes.auth ohne Partner-Zugang, errorCodes.edge)
89
101
  'account_not_found',
102
+ 'action_confirmation_required',
90
103
  'admin_required',
91
104
  'api_not_approved',
92
105
  'app_check_invalid',
@@ -105,6 +118,7 @@ exports.CANCELLATION_ERROR_CODES = Object.freeze([
105
118
  'register_user_not_allowed',
106
119
  'register_user_not_found',
107
120
  'response_translation_failed',
121
+ 'second_factor_required',
108
122
  'session_expired',
109
123
  'session_other_cashregister',
110
124
  'unauthorized',
@@ -157,3 +171,127 @@ function remainingQuantities(receipt, nowMs = Date.now()) {
157
171
  function cancellationFieldErrors(error) {
158
172
  return (0, fehlercodes_js_1.feldfehlerVon)(error);
159
173
  }
174
+ // --- Trinkgeld beim Storno (Schichten-Spec, Nachtrag 5, R56–R58) --------------------------------
175
+ //
176
+ // Enthaelt ein Beleg noch nicht storniertes Mitarbeiter-Trinkgeld, fragt die Kasse „Trinkgeld auch
177
+ // zurueckgeben?“ (`refundTip`) und bei Barrueckgabe „Woher kommt das Trinkgeld zurueck?“
178
+ // (`tipRefundSource`). Die Regeln stehen hier, damit Web-Kasse und Kassen-App dasselbe fragen;
179
+ // Zwilling von functions/gemeinsam/storno-core.js (wunschMitTrinkgeld, trinkgeldQuelleAnwenden).
180
+ // Faelle mit dem Dart-Zwilling: fixtures/cancellation-tip-cases.json.
181
+ /**
182
+ * Woher das Trinkgeld beim Storno zurueckkommt: `drawer` – die Lade zahlt es aus („offen“ sinkt,
183
+ * kann negativ werden); `employee` – die Mitarbeiterin gibt es selbst („verdient“ und „schon
184
+ * genommen“ sinken, die Lade bleibt unberuehrt). Nur bei Barrueckgabe.
185
+ */
186
+ exports.TIP_REFUND_SOURCES = Object.freeze(['drawer', 'employee']);
187
+ function isTipRefundSource(value) {
188
+ return typeof value === 'string' && exports.TIP_REFUND_SOURCES.includes(value);
189
+ }
190
+ /** Barzahlarten -- Zwilling von `BAR_ZAHLARTEN` (functions/gemeinsam/tip-core.js). */
191
+ const BAR_ZAHLARTEN = ['cash', 'uberCash', 'boltCash'];
192
+ /** Zahlart als Nutzlast-Schluessel (Enum-Objekt oder Text). */
193
+ function zahlartWert(m) {
194
+ if (m !== null && typeof m === 'object' && typeof m.value === 'string')
195
+ return m.value;
196
+ return typeof m === 'string' ? m : '';
197
+ }
198
+ const istMitarbeiterTrinkgeld = (item) => (0, receipt_item_js_1.isTipItem)(item) && item.recipient?.owner !== true;
199
+ /**
200
+ * Hatte die Mitarbeiterin dieses Trinkgeld schon in der Hand (sofort erhalten, mit Empfaenger)?
201
+ * Ohne Merkmal (Altbestand) gilt wie im Backend: bar = erhalten, sonst nicht.
202
+ */
203
+ function hatteSie(item, receipt) {
204
+ if (!item.recipient?.registerUserId)
205
+ return false;
206
+ if (typeof item.receivedImmediately === 'boolean')
207
+ return item.receivedImmediately;
208
+ return BAR_ZAHLARTEN.includes(zahlartWert(item.paymentMethod ?? receipt.paymentMethod));
209
+ }
210
+ /** Indizes des noch nicht stornierten Mitarbeiter-Trinkgelds mit ihrem Rest. */
211
+ function offenesTrinkgeld(receipt, nowMs) {
212
+ const rest = remainingQuantities(receipt, nowMs);
213
+ return receipt.items
214
+ .map((item, index) => ({ item, index, quantity: rest[index] ?? 0 }))
215
+ .filter(({ item, quantity }) => quantity > 0 && istMitarbeiterTrinkgeld(item))
216
+ .map(({ index, quantity }) => ({ index, quantity }));
217
+ }
218
+ /**
219
+ * Noch nicht storniertes Mitarbeiter-Trinkgeld in Cent. Groesser 0 heisst: das Storno fragt
220
+ * „Trinkgeld auch zurueckgeben?“ (auch beim Teilstorno). Inhaber-Trinkgeld ist Umsatz und folgt
221
+ * der Ware -- dafuer gibt es keine Frage.
222
+ */
223
+ function openStaffTipCents(receipt, nowMs = Date.now()) {
224
+ return offenesTrinkgeld(receipt, nowMs).reduce((s, { index, quantity }) => s + quantity * (receipt.items[index]?.priceCents ?? 0), 0);
225
+ }
226
+ /** Traegt der Beleg einen Wertgutschein (verkauft oder eingeloest)? Zwilling von storno-core.hatWertgutschein. */
227
+ function hatWertgutschein(receipt) {
228
+ return (receipt.vouchers ?? []).some((v) => v != null && zahlartWert(v.type) === 'value');
229
+ }
230
+ /**
231
+ * Fragt das Storno „Trinkgeld auch zurueckgeben?“ – nur bei noch offenem Mitarbeiter-Trinkgeld und
232
+ * ohne Wertgutschein am Beleg. Einen Wertgutschein spiegelt nur ein Storno, der alles nimmt; darum
233
+ * geht das Trinkgeld dort immer mit (der Server weist `refundTip: false` mit
234
+ * `tip_keep_not_possible_with_voucher` ab), die Kasse zeigt stattdessen
235
+ * `cancellation.tip_refunded_with_voucher` und sendet `refundTip: true`. Die Frage nach der Quelle
236
+ * bleibt ([asksTipRefundSource]).
237
+ */
238
+ function asksRefundTip(receipt, nowMs = Date.now()) {
239
+ return openStaffTipCents(receipt, nowMs) > 0 && !hatWertgutschein(receipt);
240
+ }
241
+ /**
242
+ * Die Positionen, die nach der Antwort auf „Trinkgeld auch zurueckgeben?“ hinausgehen -- dieselbe
243
+ * Regel wie im Backend. `items` fehlt = ganzer Beleg.
244
+ * - Ja: zum Teilstorno kommt das offene Trinkgeld dazu; der ganze Beleg bleibt `undefined`.
245
+ * - Nein: ohne das Mitarbeiter-Trinkgeld; der ganze Beleg wird zur Liste aller Restmengen ausser dem
246
+ * Trinkgeld (leer = nichts mehr zu stornieren). Ist kein Mitarbeiter-Trinkgeld mehr offen (keines
247
+ * oder schon storniert), bleibt der Wunsch, wie er ist – „Nein“ wirkt dann wie keine Wahl.
248
+ */
249
+ function cancellationItemsForTip(receipt, items, refundTip, nowMs = Date.now()) {
250
+ const trinkgeld = new Set(receipt.items.map((item, i) => (istMitarbeiterTrinkgeld(item) ? i : -1)).filter((i) => i >= 0));
251
+ if (trinkgeld.size === 0)
252
+ return items === undefined ? undefined : [...items];
253
+ if (refundTip) {
254
+ if (items === undefined)
255
+ return undefined;
256
+ const genannt = new Set(items.map((x) => x.index));
257
+ return [...items, ...offenesTrinkgeld(receipt, nowMs).filter((x) => !genannt.has(x.index))];
258
+ }
259
+ if (items !== undefined)
260
+ return items.filter((x) => !trinkgeld.has(x.index));
261
+ if (offenesTrinkgeld(receipt, nowMs).length === 0)
262
+ return undefined;
263
+ const rest = remainingQuantities(receipt, nowMs);
264
+ return rest.map((quantity, index) => ({ index, quantity })).filter((x) => x.quantity > 0 && !trinkgeld.has(x.index));
265
+ }
266
+ /**
267
+ * Was dieses Storno bar zurueckgibt (positiv, Cent): mit Rueckzahlungen deren Barteil; ohne
268
+ * (der Server spiegelt die eine Zahlung des Originals) alles, wenn das Original bar bezahlt war.
269
+ * Bei mehr als einer Zahlung ohne Angabe 0: welcher Teil bar zurueckgeht, ist dann offen (ein
270
+ * Teilstorno verlangt ohnehin `payments`). Beide Kassen schicken bei mehr als einer Zahlung darum
271
+ * immer die Rueckzahlungen mit und rechnen hier mit ihnen.
272
+ */
273
+ function cancellationCashRefundCents(receipt, payments, dueCents) {
274
+ if (payments !== undefined) {
275
+ return payments.reduce((s, z) => (zahlartWert(z.method) === 'cash' ? s - z.amountCents : s), 0);
276
+ }
277
+ const zahlungen = receipt.payments ?? [];
278
+ if (zahlungen.length > 1)
279
+ return 0;
280
+ const methode = zahlungen.length === 1 ? zahlartWert(zahlungen[0].method) : zahlartWert(receipt.paymentMethod);
281
+ return methode === 'cash' ? dueCents : 0;
282
+ }
283
+ /**
284
+ * Fragt das Storno nach „Ja“ auch „Woher kommt das Trinkgeld zurueck?“ – nur, wenn das Bargeld des
285
+ * Stornos (`cashRefundCents`, siehe [cancellationCashRefundCents]) das offene Mitarbeiter-Trinkgeld
286
+ * deckt und die Mitarbeiterin wenigstens eines davon schon hatte. Bei Kartenrueckgabe geht es auf
287
+ * die Karte zurueck; ueber Lohn behaltenes Trinkgeld kommt immer aus der Lade.
288
+ */
289
+ function asksTipRefundSource(receipt, cashRefundCents, nowMs = Date.now()) {
290
+ // Ohne diese Frage (Karte) und mit `refundTip: true` gilt beim Server: aus der Lade, kein `direkt`.
291
+ const offen = offenesTrinkgeld(receipt, nowMs);
292
+ if (offen.length === 0)
293
+ return false;
294
+ if (!offen.some(({ index }) => hatteSie(receipt.items[index], receipt)))
295
+ return false;
296
+ return cashRefundCents >= openStaffTipCents(receipt, nowMs);
297
+ }
@@ -1,7 +1,7 @@
1
1
  export { type ReceiptItem, type ReceiptItemPayload, type ReceiptItemPayloadRead, toReceiptItemPayload, fromReceiptItemPayload, receiptItemTotalCents, receiptItemIsValid, negateReceiptItem, type TipRecipient, isTipItem, isDiscountItem, } from './receipt-item.js';
2
2
  export { type Voucher, type VoucherPayload, toVoucherPayload, fromVoucherPayload, voucherIsValid } from './voucher.js';
3
- export { type Receipt, type RegistrationInfo, type ReceiptPayload, type ReceiptPayloadRead, toReceiptPayload, fromReceiptPayload, readRegistrationInfo, receiptSubSumCents, receiptSumCents, } from './receipt.js';
4
- export { type CancellationOf, type CancellationItem, type Cancellation, type CancellationReason, type CancellationErrorCode, type CancellationStatus, type ReturnDisposition, RETURN_DISPOSITIONS, CANCELLATION_REASONS, CANCELLATION_STATUSES, CANCELLATION_ERROR_CODES, CANCELLATION_RESERVATION_MS, isCancellationReason, isReturnDisposition, isCancellationErrorCode, cancellationErrorCode, isCancellationError, cancellationFieldErrors, type CancellationFieldError, remainingQuantities, } from './cancellation.js';
3
+ export { type Receipt, type RegistrationInfo, type ReceiptPayload, type ReceiptPayloadRead, toReceiptPayload, fromReceiptPayload, readRegistrationInfo, receiptSubSumCents, receiptSumCents, receiptValueVoucherSign, } from './receipt.js';
4
+ export { type CancellationOf, type CancellationItem, type Cancellation, type CancellationReason, type CancellationErrorCode, type CancellationStatus, type ReturnDisposition, RETURN_DISPOSITIONS, CANCELLATION_REASONS, CANCELLATION_STATUSES, CANCELLATION_ERROR_CODES, CANCELLATION_RESERVATION_MS, isCancellationReason, isReturnDisposition, isCancellationErrorCode, cancellationErrorCode, isCancellationError, cancellationFieldErrors, type CancellationFieldError, remainingQuantities, TIP_REFUND_SOURCES, type TipRefundSource, isTipRefundSource, openStaffTipCents, cancellationItemsForTip, cancellationCashRefundCents, asksTipRefundSource, asksRefundTip, } from './cancellation.js';
5
5
  export { type ReceiptPayment, type ReceiptPaymentPayload, type ReceiptPaymentInput, fromReceiptPaymentPayload, toReceiptPaymentPayload, } from './receipt-payment.js';
6
6
  export { type PaymentErrorCode, PAYMENT_ERROR_CODES, isPaymentErrorCode, paymentErrorCode, isPaymentError, paymentFieldErrors, type PaymentFieldError } from './payment-errors.js';
7
7
  export { type ReceiptEmailErrorCode, type ReceiptEmailVia, RECEIPT_EMAIL_ERROR_CODES, RECEIPT_EMAIL_SEND_ERROR_CODES, type ReceiptEmailSendErrorCode, RECEIPT_EMAIL_VIAS, isReceiptEmailErrorCode, receiptEmailErrorCode, isReceiptEmailError, receiptEmailFieldErrors, type ReceiptEmailFieldError, } from './receipt-email.js';
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.fromReceiptSummaryPayload = exports.fromCashregisterPayload = exports.receiptCompanyTaxInfo = exports.fromReceiptCompanyPayload = exports.receiptFieldErrors = exports.isReceiptError = exports.receiptErrorCode = exports.isReceiptErrorCode = exports.RECEIPT_ERROR_CODES = exports.receiptEmailFieldErrors = exports.isReceiptEmailError = exports.receiptEmailErrorCode = exports.isReceiptEmailErrorCode = exports.RECEIPT_EMAIL_VIAS = exports.RECEIPT_EMAIL_SEND_ERROR_CODES = exports.RECEIPT_EMAIL_ERROR_CODES = exports.paymentFieldErrors = exports.isPaymentError = exports.paymentErrorCode = exports.isPaymentErrorCode = exports.PAYMENT_ERROR_CODES = exports.toReceiptPaymentPayload = exports.fromReceiptPaymentPayload = exports.remainingQuantities = exports.cancellationFieldErrors = exports.isCancellationError = exports.cancellationErrorCode = exports.isCancellationErrorCode = exports.isReturnDisposition = exports.isCancellationReason = exports.CANCELLATION_RESERVATION_MS = exports.CANCELLATION_ERROR_CODES = exports.CANCELLATION_STATUSES = exports.CANCELLATION_REASONS = exports.RETURN_DISPOSITIONS = exports.receiptSumCents = exports.receiptSubSumCents = exports.readRegistrationInfo = exports.fromReceiptPayload = exports.toReceiptPayload = exports.voucherIsValid = exports.fromVoucherPayload = exports.toVoucherPayload = exports.isDiscountItem = exports.isTipItem = exports.negateReceiptItem = exports.receiptItemIsValid = exports.receiptItemTotalCents = exports.fromReceiptItemPayload = exports.toReceiptItemPayload = void 0;
4
- exports.hobexReceiptNeedsSignature = exports.hobexReceiptToCardPaymentData = exports.fromHobexReceiptPayload = exports.toHobexReceiptPayload = exports.fromStripeUrlSessionPayload = exports.toStripeUrlSessionPayload = exports.reportMonthReadable = exports.reportMonthKey = exports.nextReportMonth = exports.previousReportMonth = exports.reportMonthFromDate = exports.isZeroKind = exports.ZERO_KINDS = void 0;
3
+ exports.RECEIPT_ERROR_CODES = exports.receiptEmailFieldErrors = exports.isReceiptEmailError = exports.receiptEmailErrorCode = exports.isReceiptEmailErrorCode = exports.RECEIPT_EMAIL_VIAS = exports.RECEIPT_EMAIL_SEND_ERROR_CODES = exports.RECEIPT_EMAIL_ERROR_CODES = exports.paymentFieldErrors = exports.isPaymentError = exports.paymentErrorCode = exports.isPaymentErrorCode = exports.PAYMENT_ERROR_CODES = exports.toReceiptPaymentPayload = exports.fromReceiptPaymentPayload = exports.asksRefundTip = exports.asksTipRefundSource = exports.cancellationCashRefundCents = exports.cancellationItemsForTip = exports.openStaffTipCents = exports.isTipRefundSource = exports.TIP_REFUND_SOURCES = exports.remainingQuantities = exports.cancellationFieldErrors = exports.isCancellationError = exports.cancellationErrorCode = exports.isCancellationErrorCode = exports.isReturnDisposition = exports.isCancellationReason = exports.CANCELLATION_RESERVATION_MS = exports.CANCELLATION_ERROR_CODES = exports.CANCELLATION_STATUSES = exports.CANCELLATION_REASONS = exports.RETURN_DISPOSITIONS = exports.receiptValueVoucherSign = exports.receiptSumCents = exports.receiptSubSumCents = exports.readRegistrationInfo = exports.fromReceiptPayload = exports.toReceiptPayload = exports.voucherIsValid = exports.fromVoucherPayload = exports.toVoucherPayload = exports.isDiscountItem = exports.isTipItem = exports.negateReceiptItem = exports.receiptItemIsValid = exports.receiptItemTotalCents = exports.fromReceiptItemPayload = exports.toReceiptItemPayload = void 0;
4
+ exports.hobexReceiptNeedsSignature = exports.hobexReceiptToCardPaymentData = exports.fromHobexReceiptPayload = exports.toHobexReceiptPayload = exports.fromStripeUrlSessionPayload = exports.toStripeUrlSessionPayload = exports.reportMonthReadable = exports.reportMonthKey = exports.nextReportMonth = exports.previousReportMonth = exports.reportMonthFromDate = exports.isZeroKind = exports.ZERO_KINDS = exports.fromReceiptSummaryPayload = exports.fromCashregisterPayload = exports.receiptCompanyTaxInfo = exports.fromReceiptCompanyPayload = exports.receiptFieldErrors = exports.isReceiptError = exports.receiptErrorCode = exports.isReceiptErrorCode = void 0;
5
5
  var receipt_item_js_1 = require("./receipt-item.js");
6
6
  Object.defineProperty(exports, "toReceiptItemPayload", { enumerable: true, get: function () { return receipt_item_js_1.toReceiptItemPayload; } });
7
7
  Object.defineProperty(exports, "fromReceiptItemPayload", { enumerable: true, get: function () { return receipt_item_js_1.fromReceiptItemPayload; } });
@@ -20,6 +20,7 @@ Object.defineProperty(exports, "fromReceiptPayload", { enumerable: true, get: fu
20
20
  Object.defineProperty(exports, "readRegistrationInfo", { enumerable: true, get: function () { return receipt_js_1.readRegistrationInfo; } });
21
21
  Object.defineProperty(exports, "receiptSubSumCents", { enumerable: true, get: function () { return receipt_js_1.receiptSubSumCents; } });
22
22
  Object.defineProperty(exports, "receiptSumCents", { enumerable: true, get: function () { return receipt_js_1.receiptSumCents; } });
23
+ Object.defineProperty(exports, "receiptValueVoucherSign", { enumerable: true, get: function () { return receipt_js_1.receiptValueVoucherSign; } });
23
24
  var cancellation_js_1 = require("./cancellation.js");
24
25
  Object.defineProperty(exports, "RETURN_DISPOSITIONS", { enumerable: true, get: function () { return cancellation_js_1.RETURN_DISPOSITIONS; } });
25
26
  Object.defineProperty(exports, "CANCELLATION_REASONS", { enumerable: true, get: function () { return cancellation_js_1.CANCELLATION_REASONS; } });
@@ -33,6 +34,13 @@ Object.defineProperty(exports, "cancellationErrorCode", { enumerable: true, get:
33
34
  Object.defineProperty(exports, "isCancellationError", { enumerable: true, get: function () { return cancellation_js_1.isCancellationError; } });
34
35
  Object.defineProperty(exports, "cancellationFieldErrors", { enumerable: true, get: function () { return cancellation_js_1.cancellationFieldErrors; } });
35
36
  Object.defineProperty(exports, "remainingQuantities", { enumerable: true, get: function () { return cancellation_js_1.remainingQuantities; } });
37
+ Object.defineProperty(exports, "TIP_REFUND_SOURCES", { enumerable: true, get: function () { return cancellation_js_1.TIP_REFUND_SOURCES; } });
38
+ Object.defineProperty(exports, "isTipRefundSource", { enumerable: true, get: function () { return cancellation_js_1.isTipRefundSource; } });
39
+ Object.defineProperty(exports, "openStaffTipCents", { enumerable: true, get: function () { return cancellation_js_1.openStaffTipCents; } });
40
+ Object.defineProperty(exports, "cancellationItemsForTip", { enumerable: true, get: function () { return cancellation_js_1.cancellationItemsForTip; } });
41
+ Object.defineProperty(exports, "cancellationCashRefundCents", { enumerable: true, get: function () { return cancellation_js_1.cancellationCashRefundCents; } });
42
+ Object.defineProperty(exports, "asksTipRefundSource", { enumerable: true, get: function () { return cancellation_js_1.asksTipRefundSource; } });
43
+ Object.defineProperty(exports, "asksRefundTip", { enumerable: true, get: function () { return cancellation_js_1.asksRefundTip; } });
36
44
  var receipt_payment_js_1 = require("./receipt-payment.js");
37
45
  Object.defineProperty(exports, "fromReceiptPaymentPayload", { enumerable: true, get: function () { return receipt_payment_js_1.fromReceiptPaymentPayload; } });
38
46
  Object.defineProperty(exports, "toReceiptPaymentPayload", { enumerable: true, get: function () { return receipt_payment_js_1.toReceiptPaymentPayload; } });
@@ -19,7 +19,7 @@ import { KasseneckApiError } from '../client/errors.js';
19
19
  * ...) und zuletzt `route_missing` (Code des Pakets). Ein Code ausserhalb
20
20
  * bleibt ueber `KasseneckApiError.code` lesbar.
21
21
  */
22
- export declare const PAYMENT_ERROR_CODES: readonly ["payments_invalid", "payment_method_invalid", "payment_amount_invalid", "payment_tendered_invalid", "payment_provider_invalid", "payment_provider_not_allowed", "payments_sum_mismatch", "payments_due_negative", "payments_not_allowed", "payments_conflict", "payments_required", "payment_method_not_supported", "tip_payment_method_invalid", "tip_payment_method_required", "tip_exceeds_payment", "payment_refund_not_allowed", "payment_tip_invalid", "tip_conflict", "account_not_found", "admin_required", "api_not_approved", "app_check_invalid", "app_check_missing", "cashregister_not_assigned", "cashregister_not_found", "cashregister_token_invalid", "cashregister_token_missing", "dialect_mismatch", "internal_translation_error", "live_not_enabled", "method_not_allowed", "mfa_required", "not_found", "password_change_required", "register_user_no_business", "register_user_not_allowed", "register_user_not_found", "response_translation_failed", "session_expired", "session_other_cashregister", "unauthorized", "user_disabled", "user_verification_failed", "validation", "route_missing"];
22
+ export declare const PAYMENT_ERROR_CODES: readonly ["payments_invalid", "payment_method_invalid", "payment_amount_invalid", "payment_tendered_invalid", "payment_provider_invalid", "payment_provider_not_allowed", "payments_sum_mismatch", "payments_due_negative", "payments_not_allowed", "payments_conflict", "payments_required", "payment_method_not_supported", "tip_payment_method_invalid", "tip_payment_method_required", "tip_exceeds_payment", "payment_refund_not_allowed", "payment_tip_invalid", "tip_conflict", "account_not_found", "action_confirmation_required", "admin_required", "api_not_approved", "app_check_invalid", "app_check_missing", "cashregister_not_assigned", "cashregister_not_found", "cashregister_token_invalid", "cashregister_token_missing", "dialect_mismatch", "internal_translation_error", "live_not_enabled", "method_not_allowed", "mfa_required", "not_found", "password_change_required", "register_user_no_business", "register_user_not_allowed", "register_user_not_found", "response_translation_failed", "second_factor_required", "session_expired", "session_other_cashregister", "unauthorized", "user_disabled", "user_verification_failed", "validation", "route_missing"];
23
23
  export type PaymentErrorCode = (typeof PAYMENT_ERROR_CODES)[number];
24
24
  /** Erkennt einen Code aus [PAYMENT_ERROR_CODES], exakt (klein, wie unter `/v3`). */
25
25
  export declare function isPaymentErrorCode(value: unknown): value is PaymentErrorCode;
@@ -47,6 +47,7 @@ exports.PAYMENT_ERROR_CODES = Object.freeze([
47
47
  'tip_conflict', // tip und payments[].tipCents zugleich
48
48
  // Anmeldung und Rand (errorCodes.auth ohne Partner-Zugang, errorCodes.edge)
49
49
  'account_not_found',
50
+ 'action_confirmation_required',
50
51
  'admin_required',
51
52
  'api_not_approved',
52
53
  'app_check_invalid',
@@ -66,6 +67,7 @@ exports.PAYMENT_ERROR_CODES = Object.freeze([
66
67
  'register_user_not_allowed',
67
68
  'register_user_not_found',
68
69
  'response_translation_failed',
70
+ 'second_factor_required',
69
71
  'session_expired',
70
72
  'session_other_cashregister',
71
73
  'unauthorized',
@@ -11,7 +11,7 @@ export type ReceiptEmailSendErrorCode = (typeof RECEIPT_EMAIL_SEND_ERROR_CODES)[
11
11
  * als [KasseneckApiError.code] durch; ein Code ausserhalb bleibt dort lesbar.
12
12
  * Zuletzt `route_missing` (Code des Pakets).
13
13
  */
14
- export declare const RECEIPT_EMAIL_ERROR_CODES: readonly ["invalid_address", "receipt_not_found", "too_many_requests", "send_failed", "account_not_found", "admin_required", "api_not_approved", "app_check_invalid", "app_check_missing", "cashregister_not_assigned", "cashregister_not_found", "cashregister_token_invalid", "cashregister_token_missing", "dialect_mismatch", "internal_translation_error", "live_not_enabled", "method_not_allowed", "mfa_required", "not_found", "password_change_required", "register_user_no_business", "register_user_not_allowed", "register_user_not_found", "response_translation_failed", "session_expired", "session_other_cashregister", "unauthorized", "user_disabled", "user_verification_failed", "validation", "route_missing"];
14
+ export declare const RECEIPT_EMAIL_ERROR_CODES: readonly ["invalid_address", "receipt_not_found", "too_many_requests", "send_failed", "account_not_found", "action_confirmation_required", "admin_required", "api_not_approved", "app_check_invalid", "app_check_missing", "cashregister_not_assigned", "cashregister_not_found", "cashregister_token_invalid", "cashregister_token_missing", "dialect_mismatch", "internal_translation_error", "live_not_enabled", "method_not_allowed", "mfa_required", "not_found", "password_change_required", "register_user_no_business", "register_user_not_allowed", "register_user_not_found", "response_translation_failed", "second_factor_required", "session_expired", "session_other_cashregister", "unauthorized", "user_disabled", "user_verification_failed", "validation", "route_missing"];
15
15
  export type ReceiptEmailErrorCode = (typeof RECEIPT_EMAIL_ERROR_CODES)[number];
16
16
  export declare function isReceiptEmailErrorCode(value: unknown): value is ReceiptEmailErrorCode;
17
17
  /** Der Code eines geworfenen Fehlers, wenn er in [RECEIPT_EMAIL_ERROR_CODES] steht; sonst `undefined`. */
@@ -26,6 +26,7 @@ exports.RECEIPT_EMAIL_ERROR_CODES = Object.freeze([
26
26
  ...exports.RECEIPT_EMAIL_SEND_ERROR_CODES,
27
27
  // Anmeldung und Rand (errorCodes.auth ohne Partner-Zugang, errorCodes.edge)
28
28
  'account_not_found',
29
+ 'action_confirmation_required',
29
30
  'admin_required',
30
31
  'api_not_approved',
31
32
  'app_check_invalid',
@@ -45,6 +46,7 @@ exports.RECEIPT_EMAIL_ERROR_CODES = Object.freeze([
45
46
  'register_user_not_allowed',
46
47
  'register_user_not_found',
47
48
  'response_translation_failed',
49
+ 'second_factor_required',
48
50
  'session_expired',
49
51
  'session_other_cashregister',
50
52
  'unauthorized',
@@ -16,7 +16,7 @@ import { KasseneckApiError } from '../client/errors.js';
16
16
  * ...) stehen in [PAYMENT_ERROR_CODES]. Ein Code ausserhalb bleibt ueber
17
17
  * `KasseneckApiError.code` lesbar.
18
18
  */
19
- export declare const RECEIPT_ERROR_CODES: readonly ["cancellation_reference_unavailable", "cashregister_closed", "cashregister_decommissioned", "final_receipt_expired", "final_receipt_not_allowed", "module_inactive", "not_permitted", "receipt_limit_exceeded", "receipt_not_found", "receipt_outcome_unknown", "receipt_type_invalid", "signature_incomplete", "signature_missing", "signing_failed", "small_business_vat_not_allowed", "tip_invalid", "tip_not_allowed", "tip_recipient_unknown", "validation", "account_not_found", "admin_required", "api_not_approved", "app_check_invalid", "app_check_missing", "cashregister_not_assigned", "cashregister_not_found", "cashregister_token_invalid", "cashregister_token_missing", "dialect_mismatch", "internal_translation_error", "live_not_enabled", "method_not_allowed", "mfa_required", "not_found", "password_change_required", "register_user_no_business", "register_user_not_allowed", "register_user_not_found", "response_translation_failed", "session_expired", "session_other_cashregister", "unauthorized", "user_disabled", "user_verification_failed", "route_missing", "response_unreadable"];
19
+ export declare const RECEIPT_ERROR_CODES: readonly ["cancellation_reference_unavailable", "cashregister_closed", "cashregister_decommissioned", "final_receipt_expired", "final_receipt_not_allowed", "module_inactive", "not_permitted", "receipt_limit_exceeded", "receipt_not_found", "receipt_outcome_unknown", "receipt_type_invalid", "signature_incomplete", "signature_missing", "signing_failed", "small_business_vat_not_allowed", "tip_invalid", "tip_not_allowed", "tip_recipient_unknown", "validation", "account_not_found", "action_confirmation_required", "admin_required", "api_not_approved", "app_check_invalid", "app_check_missing", "cashregister_not_assigned", "cashregister_not_found", "cashregister_token_invalid", "cashregister_token_missing", "dialect_mismatch", "internal_translation_error", "live_not_enabled", "method_not_allowed", "mfa_required", "not_found", "password_change_required", "register_user_no_business", "register_user_not_allowed", "register_user_not_found", "response_translation_failed", "second_factor_required", "session_expired", "session_other_cashregister", "unauthorized", "user_disabled", "user_verification_failed", "route_missing", "response_unreadable"];
20
20
  export type ReceiptErrorCode = (typeof RECEIPT_ERROR_CODES)[number];
21
21
  export declare function isReceiptErrorCode(value: unknown): value is ReceiptErrorCode;
22
22
  /** Der Code eines geworfenen Fehlers, wenn er in [RECEIPT_ERROR_CODES] steht; sonst `undefined`. */
@@ -45,6 +45,7 @@ exports.RECEIPT_ERROR_CODES = Object.freeze([
45
45
  'validation',
46
46
  // Anmeldung und Rand (errorCodes.auth ohne Partner-Zugang, errorCodes.edge)
47
47
  'account_not_found',
48
+ 'action_confirmation_required',
48
49
  'admin_required',
49
50
  'api_not_approved',
50
51
  'app_check_invalid',
@@ -64,6 +65,7 @@ exports.RECEIPT_ERROR_CODES = Object.freeze([
64
65
  'register_user_not_allowed',
65
66
  'register_user_not_found',
66
67
  'response_translation_failed',
68
+ 'second_factor_required',
67
69
  'session_expired',
68
70
  'session_other_cashregister',
69
71
  'unauthorized',
@@ -46,6 +46,12 @@ export interface ReceiptItem {
46
46
  * behaelt der Betrieb es und schuldet es (`false`)? Fehlt bei Altbelegen.
47
47
  */
48
48
  receivedImmediately?: boolean;
49
+ /**
50
+ * Nur Personal-Trinkgeld bar: die Mitarbeiterin hat es beim Verkauf gleich
51
+ * eingesteckt („gleich behalten“). Nur ein Merkmal fuer den Kassenstand;
52
+ * fehlt sonst.
53
+ */
54
+ keptByEmployee?: boolean;
49
55
  /**
50
56
  * Artikel-Verweis (Artikelstamm) — Grundlage der Erloesgruppen-Zuordnung
51
57
  * im Bericht. Optional; Handeingaben haben keinen.
@@ -81,6 +87,8 @@ export interface ReceiptItemPayload {
81
87
  recipient?: TipRecipient | null;
82
88
  paymentMethod?: string;
83
89
  receivedImmediately?: boolean;
90
+ /** Trinkgeld-Position, siehe [ReceiptItem.keptByEmployee]; nur `true`. */
91
+ keptByEmployee?: boolean;
84
92
  /** Artikel-Verweis, siehe [ReceiptItem.articleId]. */
85
93
  articleId?: string;
86
94
  }
@@ -106,6 +114,8 @@ export interface ReceiptItemPayloadRead {
106
114
  paymentMethod?: string | null;
107
115
  /** Trinkgeld-Position, siehe [ReceiptItem.receivedImmediately]. */
108
116
  receivedImmediately?: boolean | null;
117
+ /** Trinkgeld-Position, siehe [ReceiptItem.keptByEmployee]. */
118
+ keptByEmployee?: boolean | null;
109
119
  /** v2: Menge */
110
120
  quantity?: number | null;
111
121
  /** v2: Einzelpreis in Cent */
@@ -39,6 +39,8 @@ function toReceiptItemPayload(item) {
39
39
  nutzlast.paymentMethod = item.paymentMethod;
40
40
  if (typeof item.receivedImmediately === 'boolean')
41
41
  nutzlast.receivedImmediately = item.receivedImmediately;
42
+ if (item.keptByEmployee === true)
43
+ nutzlast.keptByEmployee = true;
42
44
  }
43
45
  // Rabatt-Kennzeichnung reist mit — sonst kaeme ein Storno dieser Position
44
46
  // am Bon wieder als gewoehnliche Warenzeile mit "1 x" an.
@@ -84,6 +86,7 @@ function fromReceiptItemPayload(payload) {
84
86
  recipient: payload.recipient ?? null,
85
87
  ...(payload.paymentMethod != null ? { paymentMethod: payload.paymentMethod } : {}),
86
88
  ...(typeof payload.receivedImmediately === 'boolean' ? { receivedImmediately: payload.receivedImmediately } : {}),
89
+ ...(payload.keptByEmployee === true ? { keptByEmployee: true } : {}),
87
90
  }
88
91
  : payload.kind === 'discount'
89
92
  ? { kind: 'discount' }
@@ -143,11 +143,29 @@ export declare function readRegistrationInfo(roh: {
143
143
  cardRegisteredAt?: unknown;
144
144
  cashregisterRegisteredAt?: unknown;
145
145
  }): RegistrationInfo;
146
+ /**
147
+ * Vorzeichen der Wertgutscheine am Beleg: am Storno-Beleg -1, sonst 1.
148
+ *
149
+ * Das Backend spiegelt Wertgutscheine beim Vollstorno mit UNVERAENDERTER
150
+ * `action` und positivem Wert (storno-core.stornoVouchers); der Fluss dreht
151
+ * sich am Storno um (zahlungen-core.wertgutscheinFlussCents, hier
152
+ * `receiptDueCents`): ein verkaufter Gutschein wird zurueckgezahlt, ein
153
+ * eingeloester mindert die Rueckzahlung.
154
+ */
155
+ export declare function receiptValueVoucherSign(receipt: Receipt): 1 | -1;
146
156
  /**
147
157
  * Zwischensumme in **Cent** — Positionen plus verkaufte Wertgutscheine minus
148
158
  * eingeloeste Promotionsgutscheine. Wertgutschein-Einloesungen wirken erst auf
149
159
  * [receiptSumCents], nicht hier (siehe Dart-Vorbild `subSumCents`/`sumCents`).
160
+ * Am Storno-Beleg zaehlen Wertgutscheine mit umgekehrtem Vorzeichen
161
+ * ([receiptValueVoucherSign]), und ein gespiegelter Promotionsgutschein
162
+ * mindert nichts: den Rabatt nimmt der Server dort ueber Ausgleichspositionen
163
+ * (`kind: 'discount'`) zurueck, und belegToepfe verteilt einen Promo-Eintrag
164
+ * nur auf eine positive Topfsumme, die ein Storno nie hat.
150
165
  */
151
166
  export declare function receiptSubSumCents(receipt: Receipt): number;
152
- /** Gesamtsumme in **Cent** — Zwischensumme minus eingeloeste Wertgutscheine. */
167
+ /**
168
+ * Gesamtsumme in **Cent** — Zwischensumme minus eingeloeste Wertgutscheine
169
+ * (am Storno-Beleg plus, siehe [receiptValueVoucherSign]).
170
+ */
153
171
  export declare function receiptSumCents(receipt: Receipt): number;
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.toReceiptPayload = toReceiptPayload;
4
4
  exports.fromReceiptPayload = fromReceiptPayload;
5
5
  exports.readRegistrationInfo = readRegistrationInfo;
6
+ exports.receiptValueVoucherSign = receiptValueVoucherSign;
6
7
  exports.receiptSubSumCents = receiptSubSumCents;
7
8
  exports.receiptSumCents = receiptSumCents;
8
9
  const index_js_1 = require("../enums/index.js");
@@ -134,29 +135,52 @@ function gueltigeRueckzahlungen(rueckzahlungen) {
134
135
  }
135
136
  return raus;
136
137
  }
138
+ /**
139
+ * Vorzeichen der Wertgutscheine am Beleg: am Storno-Beleg -1, sonst 1.
140
+ *
141
+ * Das Backend spiegelt Wertgutscheine beim Vollstorno mit UNVERAENDERTER
142
+ * `action` und positivem Wert (storno-core.stornoVouchers); der Fluss dreht
143
+ * sich am Storno um (zahlungen-core.wertgutscheinFlussCents, hier
144
+ * `receiptDueCents`): ein verkaufter Gutschein wird zurueckgezahlt, ein
145
+ * eingeloester mindert die Rueckzahlung.
146
+ */
147
+ function receiptValueVoucherSign(receipt) {
148
+ const typ = typeof receipt.receiptType === 'object' ? receipt.receiptType.value : receipt.receiptType;
149
+ return typ === index_js_1.ReceiptType.cancellation.value ? -1 : 1;
150
+ }
137
151
  /**
138
152
  * Zwischensumme in **Cent** — Positionen plus verkaufte Wertgutscheine minus
139
153
  * eingeloeste Promotionsgutscheine. Wertgutschein-Einloesungen wirken erst auf
140
154
  * [receiptSumCents], nicht hier (siehe Dart-Vorbild `subSumCents`/`sumCents`).
155
+ * Am Storno-Beleg zaehlen Wertgutscheine mit umgekehrtem Vorzeichen
156
+ * ([receiptValueVoucherSign]), und ein gespiegelter Promotionsgutschein
157
+ * mindert nichts: den Rabatt nimmt der Server dort ueber Ausgleichspositionen
158
+ * (`kind: 'discount'`) zurueck, und belegToepfe verteilt einen Promo-Eintrag
159
+ * nur auf eine positive Topfsumme, die ein Storno nie hat.
141
160
  */
142
161
  function receiptSubSumCents(receipt) {
162
+ const vz = receiptValueVoucherSign(receipt);
143
163
  let cents = receipt.items.reduce((acc, item) => acc + (0, receipt_item_js_1.receiptItemTotalCents)(item), 0);
144
164
  for (const voucher of receipt.vouchers) {
145
- if (voucher.action === index_js_1.VoucherAction.redeem && voucher.type === index_js_1.VoucherType.promo) {
165
+ if (vz === 1 && voucher.action === index_js_1.VoucherAction.redeem && voucher.type === index_js_1.VoucherType.promo) {
146
166
  cents -= voucher.valueCents ?? 0;
147
167
  }
148
168
  if (voucher.action === index_js_1.VoucherAction.sell && voucher.type === index_js_1.VoucherType.value) {
149
- cents += voucher.valueCents ?? 0;
169
+ cents += vz * (voucher.valueCents ?? 0);
150
170
  }
151
171
  }
152
172
  return cents;
153
173
  }
154
- /** Gesamtsumme in **Cent** — Zwischensumme minus eingeloeste Wertgutscheine. */
174
+ /**
175
+ * Gesamtsumme in **Cent** — Zwischensumme minus eingeloeste Wertgutscheine
176
+ * (am Storno-Beleg plus, siehe [receiptValueVoucherSign]).
177
+ */
155
178
  function receiptSumCents(receipt) {
179
+ const vz = receiptValueVoucherSign(receipt);
156
180
  let cents = receiptSubSumCents(receipt);
157
181
  for (const voucher of receipt.vouchers) {
158
182
  if (voucher.action === index_js_1.VoucherAction.redeem && voucher.type === index_js_1.VoucherType.value) {
159
- cents -= voucher.valueCents ?? 0;
183
+ cents -= vz * (voucher.valueCents ?? 0);
160
184
  }
161
185
  }
162
186
  return cents;
@@ -139,6 +139,16 @@ function pruefeTeil(name, teil, block, standard, werte) {
139
139
  throw new errors_js_1.KasseneckValidationError(name, `${teil}: keine Einstellungen uebergeben`, 'request');
140
140
  }
141
141
  for (const [schluessel, wert] of gesendet) {
142
+ if (teil === 'device' && settings_js_1.POS_DEVICE_OPTIONAL_FIELDS.includes(schluessel)) {
143
+ if (!(0, settings_js_1._optionalerWert)(wert, true))
144
+ throw new errors_js_1.KasseneckValidationError(name, `${teil}.${schluessel}: Kennung oder null erwartet`, 'request');
145
+ continue;
146
+ }
147
+ if (teil === 'business' && settings_js_1.POS_BUSINESS_OPTIONAL_FIELDS.includes(schluessel)) {
148
+ if (typeof wert !== 'boolean')
149
+ throw new errors_js_1.KasseneckValidationError(name, `${teil}.${schluessel}: true oder false erwartet`, 'request');
150
+ continue;
151
+ }
142
152
  if (!Object.prototype.hasOwnProperty.call(standard, schluessel)) {
143
153
  throw new errors_js_1.KasseneckValidationError(name, `${teil}.${schluessel}: unbekanntes Feld`, 'request');
144
154
  }