@kreiseck/kasseneck-api 1.11.1 → 1.12.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 (187) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +2 -1
  3. package/dist/cjs/client/aufrufe.d.ts +4 -4
  4. package/dist/cjs/client/aufrufe.js +15 -0
  5. package/dist/cjs/client/receipts.d.ts +5 -0
  6. package/dist/cjs/client/receipts.js +65 -6
  7. package/dist/cjs/enums/voucher-action.d.ts +2 -0
  8. package/dist/cjs/enums/voucher-action.js +2 -0
  9. package/dist/cjs/enums/voucher-type.d.ts +10 -0
  10. package/dist/cjs/enums/voucher-type.js +10 -0
  11. package/dist/cjs/index.d.ts +3 -3
  12. package/dist/cjs/index.js +8 -3
  13. package/dist/cjs/models/index.d.ts +2 -2
  14. package/dist/cjs/models/index.js +6 -2
  15. package/dist/cjs/models/receipt-errors.d.ts +1 -1
  16. package/dist/cjs/models/receipt-errors.js +2 -0
  17. package/dist/cjs/models/receipt-item.d.ts +11 -3
  18. package/dist/cjs/models/receipt-item.js +22 -1
  19. package/dist/cjs/models/receipt.d.ts +13 -1
  20. package/dist/cjs/models/receipt.js +18 -2
  21. package/dist/cjs/models/voucher.d.ts +57 -4
  22. package/dist/cjs/models/voucher.js +111 -3
  23. package/dist/cjs/partner/fehler.d.ts +1 -1
  24. package/dist/cjs/partner/fehler.js +2 -0
  25. package/dist/cjs/partner/index.d.ts +1 -1
  26. package/dist/cjs/partner/typen.d.ts +7 -1
  27. package/dist/cjs/partner/typen.js +3 -0
  28. package/dist/cjs/pos/client.d.ts +2 -1
  29. package/dist/cjs/pos/client.js +3 -1
  30. package/dist/cjs/pos/gutschein.d.ts +90 -0
  31. package/dist/cjs/pos/gutschein.js +164 -0
  32. package/dist/cjs/pos/index.d.ts +3 -2
  33. package/dist/cjs/pos/index.js +13 -2
  34. package/dist/cjs/pos/settings.d.ts +3 -0
  35. package/dist/cjs/pos/settings.js +12 -10
  36. package/dist/cjs/pos/texte.d.ts +156 -0
  37. package/dist/cjs/pos/texte.js +72 -1
  38. package/dist/cjs/react/index.js +8 -2
  39. package/dist/cjs/receipt/due.d.ts +83 -2
  40. package/dist/cjs/receipt/due.js +202 -36
  41. package/dist/cjs/receipt/grid.js +13 -1
  42. package/dist/cjs/receipt/gutschein-plan.d.ts +104 -0
  43. package/dist/cjs/receipt/gutschein-plan.js +360 -0
  44. package/dist/cjs/receipt/index.d.ts +1 -1
  45. package/dist/cjs/receipt/index.js +3 -0
  46. package/dist/cjs/receipt/layout.js +175 -8
  47. package/dist/cjs/register/pairing.d.ts +21 -1
  48. package/dist/cjs/register/pairing.js +17 -0
  49. package/dist/cjs/stored/bon-layout.d.ts +8 -0
  50. package/dist/cjs/stored/bon-layout.js +23 -0
  51. package/dist/cjs/stored/draht.d.ts +3 -1
  52. package/dist/cjs/stored/draht.js +53 -6
  53. package/dist/cjs/stored/einstellungen.js +2 -1
  54. package/dist/cjs/stored/gutscheine.d.ts +3 -0
  55. package/dist/cjs/stored/gutscheine.js +60 -0
  56. package/dist/cjs/stored/index.js +2 -17
  57. package/dist/cjs/stored/vokabular.d.ts +6 -4
  58. package/dist/cjs/stored/vokabular.js +48 -2
  59. package/dist/cjs/version.d.ts +1 -1
  60. package/dist/cjs/version.js +1 -1
  61. package/dist/esm/client/aufrufe.d.ts +4 -4
  62. package/dist/esm/client/aufrufe.js +15 -0
  63. package/dist/esm/client/receipts.d.ts +5 -0
  64. package/dist/esm/client/receipts.js +66 -8
  65. package/dist/esm/enums/voucher-action.d.ts +2 -0
  66. package/dist/esm/enums/voucher-action.js +2 -0
  67. package/dist/esm/enums/voucher-type.d.ts +10 -0
  68. package/dist/esm/enums/voucher-type.js +10 -0
  69. package/dist/esm/index.d.ts +3 -3
  70. package/dist/esm/index.js +3 -3
  71. package/dist/esm/models/index.d.ts +2 -2
  72. package/dist/esm/models/index.js +2 -2
  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 +11 -3
  76. package/dist/esm/models/receipt-item.js +20 -1
  77. package/dist/esm/models/receipt.d.ts +13 -1
  78. package/dist/esm/models/receipt.js +18 -2
  79. package/dist/esm/models/voucher.d.ts +57 -4
  80. package/dist/esm/models/voucher.js +108 -3
  81. package/dist/esm/partner/fehler.d.ts +1 -1
  82. package/dist/esm/partner/fehler.js +2 -0
  83. package/dist/esm/partner/index.d.ts +1 -1
  84. package/dist/esm/partner/typen.d.ts +7 -1
  85. package/dist/esm/partner/typen.js +3 -0
  86. package/dist/esm/pos/client.d.ts +2 -1
  87. package/dist/esm/pos/client.js +3 -1
  88. package/dist/esm/pos/gutschein.d.ts +90 -0
  89. package/dist/esm/pos/gutschein.js +158 -0
  90. package/dist/esm/pos/index.d.ts +3 -2
  91. package/dist/esm/pos/index.js +4 -2
  92. package/dist/esm/pos/settings.d.ts +3 -0
  93. package/dist/esm/pos/settings.js +2 -0
  94. package/dist/esm/pos/texte.d.ts +156 -0
  95. package/dist/esm/pos/texte.js +70 -0
  96. package/dist/esm/react/index.js +8 -2
  97. package/dist/esm/receipt/due.d.ts +83 -2
  98. package/dist/esm/receipt/due.js +198 -34
  99. package/dist/esm/receipt/grid.js +13 -1
  100. package/dist/esm/receipt/gutschein-plan.d.ts +104 -0
  101. package/dist/esm/receipt/gutschein-plan.js +352 -0
  102. package/dist/esm/receipt/index.d.ts +1 -1
  103. package/dist/esm/receipt/index.js +1 -1
  104. package/dist/esm/receipt/layout.js +175 -8
  105. package/dist/esm/register/pairing.d.ts +21 -1
  106. package/dist/esm/register/pairing.js +16 -0
  107. package/dist/esm/stored/bon-layout.d.ts +8 -0
  108. package/dist/esm/stored/bon-layout.js +20 -0
  109. package/dist/esm/stored/draht.d.ts +3 -1
  110. package/dist/esm/stored/draht.js +52 -6
  111. package/dist/esm/stored/einstellungen.js +3 -2
  112. package/dist/esm/stored/gutscheine.d.ts +3 -0
  113. package/dist/esm/stored/gutscheine.js +56 -0
  114. package/dist/esm/stored/index.js +2 -17
  115. package/dist/esm/stored/vokabular.d.ts +6 -4
  116. package/dist/esm/stored/vokabular.js +48 -2
  117. package/dist/esm/version.d.ts +1 -1
  118. package/dist/esm/version.js +1 -1
  119. package/fixtures/expected/voucher-cancellation.grid32.txt +30 -0
  120. package/fixtures/expected/voucher-cancellation.grid48.txt +29 -0
  121. package/fixtures/expected/voucher-cancellation.lines.json +252 -0
  122. package/fixtures/expected/voucher-cancellation.sheet32.json +206 -0
  123. package/fixtures/expected/voucher-cancellation.sheet48.json +200 -0
  124. package/fixtures/expected/voucher-multi-purpose-redeem.grid32.txt +25 -0
  125. package/fixtures/expected/voucher-multi-purpose-redeem.grid48.txt +25 -0
  126. package/fixtures/expected/voucher-multi-purpose-redeem.lines.json +259 -0
  127. package/fixtures/expected/voucher-multi-purpose-redeem.sheet32.json +176 -0
  128. package/fixtures/expected/voucher-multi-purpose-redeem.sheet48.json +176 -0
  129. package/fixtures/expected/voucher-multi-purpose-sale.grid32.txt +24 -0
  130. package/fixtures/expected/voucher-multi-purpose-sale.grid48.txt +23 -0
  131. package/fixtures/expected/voucher-multi-purpose-sale.lines.json +216 -0
  132. package/fixtures/expected/voucher-multi-purpose-sale.sheet32.json +170 -0
  133. package/fixtures/expected/voucher-multi-purpose-sale.sheet48.json +164 -0
  134. package/fixtures/expected/voucher-payout.grid32.txt +23 -0
  135. package/fixtures/expected/voucher-payout.grid48.txt +22 -0
  136. package/fixtures/expected/voucher-payout.lines.json +216 -0
  137. package/fixtures/expected/voucher-payout.sheet32.json +164 -0
  138. package/fixtures/expected/voucher-payout.sheet48.json +158 -0
  139. package/fixtures/expected/voucher-service-redeem.grid32.txt +26 -0
  140. package/fixtures/expected/voucher-service-redeem.grid48.txt +25 -0
  141. package/fixtures/expected/voucher-service-redeem.lines.json +261 -0
  142. package/fixtures/expected/voucher-service-redeem.sheet32.json +182 -0
  143. package/fixtures/expected/voucher-service-redeem.sheet48.json +176 -0
  144. package/fixtures/expected/voucher-single-purpose-redeem-exempt.grid32.txt +30 -0
  145. package/fixtures/expected/voucher-single-purpose-redeem-exempt.grid48.txt +29 -0
  146. package/fixtures/expected/voucher-single-purpose-redeem-exempt.lines.json +331 -0
  147. package/fixtures/expected/voucher-single-purpose-redeem-exempt.sheet32.json +206 -0
  148. package/fixtures/expected/voucher-single-purpose-redeem-exempt.sheet48.json +200 -0
  149. package/fixtures/expected/voucher-single-purpose-redeem.grid32.txt +25 -0
  150. package/fixtures/expected/voucher-single-purpose-redeem.grid48.txt +24 -0
  151. package/fixtures/expected/voucher-single-purpose-redeem.lines.json +246 -0
  152. package/fixtures/expected/voucher-single-purpose-redeem.sheet32.json +176 -0
  153. package/fixtures/expected/voucher-single-purpose-redeem.sheet48.json +170 -0
  154. package/fixtures/expected/voucher-single-purpose-sale.grid32.txt +26 -0
  155. package/fixtures/expected/voucher-single-purpose-sale.grid48.txt +24 -0
  156. package/fixtures/expected/voucher-single-purpose-sale.lines.json +225 -0
  157. package/fixtures/expected/voucher-single-purpose-sale.sheet32.json +182 -0
  158. package/fixtures/expected/voucher-single-purpose-sale.sheet48.json +170 -0
  159. package/fixtures/hobex-hps-codes.json +1 -1
  160. package/fixtures/invoice-api.schema.json +1 -1
  161. package/fixtures/invoice-texts.json +1 -1
  162. package/fixtures/manifest.json +64 -0
  163. package/fixtures/pos-texts.json +170 -1
  164. package/fixtures/receipt-due-generated.json +1 -1
  165. package/fixtures/receipt-due-voucher-generated.json +494 -0
  166. package/fixtures/receipts/voucher-cancellation.json +76 -0
  167. package/fixtures/receipts/voucher-multi-purpose-redeem.json +62 -0
  168. package/fixtures/receipts/voucher-multi-purpose-sale.json +63 -0
  169. package/fixtures/receipts/voucher-payout.json +65 -0
  170. package/fixtures/receipts/voucher-service-redeem.json +82 -0
  171. package/fixtures/receipts/voucher-single-purpose-redeem-exempt.json +86 -0
  172. package/fixtures/receipts/voucher-single-purpose-redeem.json +71 -0
  173. package/fixtures/receipts/voucher-single-purpose-sale.json +88 -0
  174. package/fixtures/surface.json +35 -5
  175. package/fixtures/v3/antworten/belege.json +3405 -3
  176. package/fixtures/v3/antworten/belegmail.json +5 -2
  177. package/fixtures/v3/antworten/kasse-belege.json +3423 -3
  178. package/fixtures/v3/antworten/kasse.json +621 -14
  179. package/fixtures/v3/antworten/lager.json +1 -1
  180. package/fixtures/v3/antworten/rechnungen.json +5 -2
  181. package/fixtures/v3/antworten/storno.json +214 -3
  182. package/fixtures/v3/stored/belege.json +5 -2
  183. package/fixtures/v3/stored/kasse.json +225 -4
  184. package/fixtures/v3/stored/rechnungen.json +5 -2
  185. package/fixtures/v3/v3-vokabular.json +1375 -23
  186. package/fixtures/v3/zahlbetrag-faelle.json +293 -3
  187. package/package.json +4 -1
@@ -1,6 +1,9 @@
1
1
  import { ReceiptType, type ReceiptTypeKey, KeckPaymentMethod } from '../enums/index.js';
2
2
  import type { ReceiptItem } from '../models/receipt-item.js';
3
3
  import type { Voucher } from '../models/voucher.js';
4
+ import type { PosVoucherSettings, VoucherCheck } from '../pos/gutschein.js';
5
+ import { type VoucherDueArticle, type VoucherDueInfo } from './gutschein-plan.js';
6
+ export { VOUCHER_ZERO_RATE_BUCKET_FROM, type VoucherDueArticle, type VoucherDueInfo } from './gutschein-plan.js';
4
7
  /**
5
8
  * Zahlbetrag eines Belegs in ganzen Cent: Zwilling von `createReceipt` im
6
9
  * Backend (Nachtrag §8), also `beleg-toepfe.belegToepfe(...).zaehlerDeltaCents`
@@ -34,6 +37,19 @@ import type { Voucher } from '../models/voucher.js';
34
37
  * sobald Trinkgeld ohne `recipients` vorkommt. Ohne angemeldeten Benutzer
35
38
  * (Geraete-Schluessel) gilt `'staff'`.
36
39
  *
40
+ * **Gutscheine der neuen Form** (Mehrzweck, Einzweck, Leistung, Aktion,
41
+ * Auszahlung): der Server macht daraus Positionen und Eintraege
42
+ * (`gutschein-beleg.plane`, Zwilling in `gutschein-plan.ts`). Was er dafuer
43
+ * aus seinen Daten liest, gibt die Kasse mit: je Einloesung/Auszahlung die
44
+ * Pruefung aus `checkVoucher` (`voucherInfo`, ueber den Code zugeordnet), fuer
45
+ * einen Einzweck-Verkauf die Gutschein-Einstellung der Kasse
46
+ * (`voucherSettings`, Satz), fuer einen Leistungsgutschein-Verkauf den Artikel
47
+ * (`voucherArticles`, Preis und Satz) und `smallBusiness` fuer ein
48
+ * Kleinunternehmer-Konto. Was fehlt oder was der Server abwiese, wirft
49
+ * `invalid_voucher` mit dem erwarteten Server-Code in `voucherErrorCode`.
50
+ * Ein Beleg nur aus Auszahlungen hat einen negativen Zahlbetrag (genau eine
51
+ * Barzahlung ueber diesen Betrag).
52
+ *
37
53
  * **Nicht rechenbar:** jede unbrauchbare Eingabe wirft [ReceiptDueError]
38
54
  * (`code: 'receipt_due_unavailable'`, `reason` im Einzelnen), nie still eine
39
55
  * falsche Zahl. Dann ist nichts gesendet.
@@ -62,6 +78,42 @@ export interface ReceiptDueOptions {
62
78
  }>;
63
79
  /** Pflicht, sobald Trinkgeld ohne `recipients` vorkommt (siehe Modulkommentar). */
64
80
  tipRecipient?: ReceiptDueTipRecipient;
81
+ /**
82
+ * Je Einloesung bzw. Auszahlung der neuen Form die Pruefung aus
83
+ * `checkVoucher`, ueber den Code zugeordnet. Fehlt sie fuer einen Code,
84
+ * wirft die Rechnung `invalid_voucher` mit `voucherErrorCode:
85
+ * 'voucher_not_found'` (die Kasse hat den Code nicht geprueft).
86
+ */
87
+ voucherInfo?: readonly VoucherDueInfo[];
88
+ /**
89
+ * Gutschein-Einstellung der Kasse (`getKasseSettings`, Block `vouchers`):
90
+ * Pflicht fuer jeden Verkauf eines Mehrzweck- oder Einzweckgutscheins (der
91
+ * Server verkauft nur mit Einstellung). Fehlt sie oder ist keine Art
92
+ * gewaehlt: `voucher_settings_missing`; eine andere Art als die
93
+ * eingestellte: `voucher_invalid`.
94
+ */
95
+ voucherSettings?: PosVoucherSettings;
96
+ /**
97
+ * Artikel fuer den Verkauf von Dienstleistungsgutscheinen (Preis und Satz,
98
+ * wie im Artikelstamm). Fehlt der verkaufte Artikel hier:
99
+ * `voucher_item_mismatch`, wie beim Server fuer einen unbekannten Artikel.
100
+ */
101
+ voucherArticles?: readonly VoucherDueArticle[];
102
+ /**
103
+ * Das Konto ist Kleinunternehmer (Konto-Fakten): Einzweck/Leistung werden zu
104
+ * 0 % verkauft, eine Einzweck-Einloesung verteilt sich anteilig auf alle
105
+ * Saetze des Korbs. Dasselbe gilt fuer einen befreit verkauften Gutschein
106
+ * (`VoucherCheck.exempt`) in jedem Konto, wie der Server (`kuEingefroren || ku`).
107
+ */
108
+ smallBusiness?: boolean;
109
+ /**
110
+ * Stichtag des 0-%-Topfs fuer Mehrzweck-Verkaeufe (`YYYY-MM-DD`). Vorgabe
111
+ * [VOUCHER_ZERO_RATE_BUCKET_FROM], wie im Backend. Nur angeben, wenn der
112
+ * Server ihn nachweislich anders fuehrt (Test-Umgebung).
113
+ */
114
+ zeroRateBucketFrom?: string;
115
+ /** Wiener Tag des Belegs (`YYYY-MM-DD`); Vorgabe: heute in Wien. */
116
+ today?: string;
65
117
  }
66
118
  /** Die sechs RKSV-Toepfe in Cent, Namen wie am Beleg des Backends. */
67
119
  export interface ReceiptDueBuckets {
@@ -108,7 +160,13 @@ export declare class ReceiptDueError extends Error {
108
160
  readonly reason: ReceiptDueErrorReason;
109
161
  /** Immer `'rejected'`: es ging nichts hinaus. */
110
162
  readonly outcome: "rejected";
111
- constructor(reason: ReceiptDueErrorReason, message: string);
163
+ /**
164
+ * Nur bei `invalid_voucher`: der Gutschein-Code, mit dem der Server
165
+ * denselben Beleg abwiese (`voucher_rate_mismatch`, `voucher_exceeds_total`,
166
+ * `voucher_not_found` …), fuer die Meldung an der Kasse.
167
+ */
168
+ readonly voucherErrorCode?: string;
169
+ constructor(reason: ReceiptDueErrorReason, message: string, voucherErrorCode?: string);
112
170
  }
113
171
  export declare function isReceiptDueError(error: unknown): error is ReceiptDueError;
114
172
  type BelegArt = ReceiptType | ReceiptTypeKey | string;
@@ -116,4 +174,27 @@ type BelegArt = ReceiptType | ReceiptTypeKey | string;
116
174
  export declare function receiptDueCents(items: readonly ReceiptItem[], vouchers: readonly Voucher[], receiptType: BelegArt, options?: ReceiptDueOptions): number;
117
175
  /** Wie [receiptDueCents], dazu Zaehler-Delta, Wertgutschein-Anteil und Toepfe. */
118
176
  export declare function receiptDueBreakdown(items: readonly ReceiptItem[], vouchers: readonly Voucher[], receiptType: BelegArt, options?: ReceiptDueOptions): ReceiptDueBreakdown;
119
- export {};
177
+ /** Schon gewaehlte Gutscheine und Optionen des Belegs, vor dem Gutschein, der vorbelegt wird. */
178
+ export interface VoucherRedeemContext {
179
+ vouchers?: readonly Voucher[];
180
+ options?: ReceiptDueOptions;
181
+ }
182
+ /**
183
+ * Hoechstbetrag zum Vorbelegen einer Einloesung an der Kasse, in Cent, nie
184
+ * negativ: der groesste Betrag, den der Server fuer diesen Gutschein annimmt.
185
+ *
186
+ * Obergrenze: Saldo, offener Betrag; bei Einzweck die deckbaren Positionen im
187
+ * Topf seines Satzes abzueglich der schon gewaehlten Einloesungen dort. Befreit
188
+ * verkauft bzw. als Kleinunternehmer verteilt der Server anteilig auf alle
189
+ * Saetze (`kuTeile`); dann begrenzt jeder Topf: sein Anteil plus die schon
190
+ * gewaehlten Einloesungen dort darf seine Positionen nicht uebersteigen.
191
+ *
192
+ * Unter dieser Grenze wird jeder Kandidat mit genau der Rechnung von
193
+ * [receiptDueCents] geprueft (Rundung halber Cent, groesster Rest), zuerst
194
+ * Cent fuer Cent abwaerts (64 Schritte). Findet das nichts, sucht eine
195
+ * Bisektion ueber [1, Grenze] und prueft danach 16 Cent aufwaerts: am Rand
196
+ * des groessten Rests ist die Annahme nicht monoton. Leistung (wird ueber
197
+ * Positionen eingeloest), Aktion (Rabatt rechnet der Server) und jeder nicht
198
+ * aktive Gutschein: 0.
199
+ */
200
+ export declare function voucherRedeemMaxCents(items: readonly ReceiptItem[], voucher: VoucherCheck, before?: VoucherRedeemContext): number;
@@ -1,10 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ReceiptDueError = exports.RECEIPT_DUE_ERROR_REASONS = void 0;
3
+ exports.ReceiptDueError = exports.RECEIPT_DUE_ERROR_REASONS = exports.VOUCHER_ZERO_RATE_BUCKET_FROM = void 0;
4
4
  exports.isReceiptDueError = isReceiptDueError;
5
5
  exports.receiptDueCents = receiptDueCents;
6
6
  exports.receiptDueBreakdown = receiptDueBreakdown;
7
+ exports.voucherRedeemMaxCents = voucherRedeemMaxCents;
7
8
  const index_js_1 = require("../enums/index.js");
9
+ const validator_1 = require("@kreiseck/validator");
10
+ const vienna_time_js_1 = require("../vienna-time.js");
11
+ const gutschein_plan_js_1 = require("./gutschein-plan.js");
12
+ var gutschein_plan_js_2 = require("./gutschein-plan.js");
13
+ Object.defineProperty(exports, "VOUCHER_ZERO_RATE_BUCKET_FROM", { enumerable: true, get: function () { return gutschein_plan_js_2.VOUCHER_ZERO_RATE_BUCKET_FROM; } });
8
14
  /**
9
15
  * Warum sich der Zahlbetrag nicht rechnen laesst. Fest wie ein Backend-Code;
10
16
  * dieselbe Liste im Dart-Zwilling und in `fixtures/receipt-due-errors.json`.
@@ -40,9 +46,17 @@ class ReceiptDueError extends Error {
40
46
  reason;
41
47
  /** Immer `'rejected'`: es ging nichts hinaus. */
42
48
  outcome = 'rejected';
43
- constructor(reason, message) {
49
+ /**
50
+ * Nur bei `invalid_voucher`: der Gutschein-Code, mit dem der Server
51
+ * denselben Beleg abwiese (`voucher_rate_mismatch`, `voucher_exceeds_total`,
52
+ * `voucher_not_found` …), fuer die Meldung an der Kasse.
53
+ */
54
+ voucherErrorCode;
55
+ constructor(reason, message, voucherErrorCode) {
44
56
  super(`Zahlbetrag: ${message}`);
45
57
  this.reason = reason;
58
+ if (voucherErrorCode !== undefined)
59
+ this.voucherErrorCode = voucherErrorCode;
46
60
  }
47
61
  }
48
62
  exports.ReceiptDueError = ReceiptDueError;
@@ -58,35 +72,192 @@ function receiptDueCents(items, vouchers, receiptType, options = {}) {
58
72
  }
59
73
  /** Wie [receiptDueCents], dazu Zaehler-Delta, Wertgutschein-Anteil und Toepfe. */
60
74
  function receiptDueBreakdown(items, vouchers, receiptType, options = {}) {
75
+ return rechne(items, vouchers, receiptType, options).ergebnis;
76
+ }
77
+ function rechne(items, vouchers, receiptType, options) {
61
78
  const art = belegArt(receiptType);
62
79
  const trinkgelder = trinkgeldAuftraege(options);
63
80
  if (trinkgelder.length > 0 && !TRINKGELD_ERLAUBT.has(art)) {
64
81
  throw new ReceiptDueError('tip_not_allowed', `Trinkgeld gibt es nur bei standard und training, nicht bei "${art}".`);
65
82
  }
66
83
  if (!UMSATZ.has(art)) {
67
- return { dueCents: 0, counterDeltaCents: 0, valueVoucherFlowCents: 0, bucketsCents: null };
84
+ return { ergebnis: { dueCents: 0, counterDeltaCents: 0, valueVoucherFlowCents: 0, bucketsCents: null }, kunde: [], minusJeTopf: new Map(), auszahlungCents: 0 };
68
85
  }
69
86
  const positionen = items.map(innen);
70
- const gutscheine = vouchers.map(gutscheinInnen);
71
87
  // index.js createReceipt: erst `tip`, dann je Zahlart aus payments[].tipCents;
72
88
  // jede Runde haengt ihre Positionen hinten an (die Basis bleibt die Ware).
73
89
  for (const auftrag of trinkgelder)
74
90
  positionen.push(...trinkgeldPositionen(auftrag, positionen, options.tipRecipient));
91
+ // Danach der Gutschein-Plan (gutschein-beleg.plane): seine Positionen stehen hinter Ware und Trinkgeld.
92
+ const plan = (0, gutschein_plan_js_1.planeGutscheine)(positionen, vouchers, art, {
93
+ voucherInfo: options.voucherInfo,
94
+ voucherSettings: options.voucherSettings,
95
+ voucherArticles: options.voucherArticles,
96
+ smallBusiness: options.smallBusiness,
97
+ zeroRateBucketFrom: options.zeroRateBucketFrom,
98
+ today: options.today ?? wienerTag(),
99
+ });
100
+ if ('fehler' in plan)
101
+ throw new ReceiptDueError('invalid_voucher', plan.text, plan.fehler);
102
+ const kunde = positionen.slice();
103
+ positionen.push(...plan.zusatz);
104
+ // gutschein-beleg.zahlbetrag: nur Wert und Rabatt, ohne Mehrzweck-Verkaeufe im 0-%-Topf.
105
+ const gutscheine = plan.gutscheine.filter((g) => !(g.action === index_js_1.VoucherAction.sell && g.imNullTopf === true));
75
106
  const t = belegToepfe(positionen, gutscheine);
76
107
  const fluss = wertgutscheinFlussCents(gutscheine, art);
108
+ const dueCents = t.zaehlerDeltaCents + fluss;
109
+ // Ohne Auszahlungen darf der Zahlbetrag nicht negativ werden (Einloesung hoeher als die Summe).
110
+ if (plan.mitPlan && dueCents + plan.auszahlungCents < 0) {
111
+ throw new ReceiptDueError('invalid_voucher', 'die Gutscheine uebersteigen die Summe des Belegs.', 'voucher_exceeds_total');
112
+ }
77
113
  return {
78
- dueCents: t.zaehlerDeltaCents + fluss,
79
- counterDeltaCents: t.zaehlerDeltaCents,
80
- valueVoucherFlowCents: fluss,
81
- bucketsCents: {
82
- amountRateStandard: euroToCent(t.amountRateStandard),
83
- amountRateReduced1: euroToCent(t.amountRateReduced1),
84
- amountRateReduced2: euroToCent(t.amountRateReduced2),
85
- amountRateZero: euroToCent(t.amountRateZero),
86
- amountRateSpecial: euroToCent(t.amountRateSpecial),
87
- amountRatOthers: euroToCent(t.amountRatOthers),
114
+ ergebnis: {
115
+ dueCents,
116
+ counterDeltaCents: t.zaehlerDeltaCents,
117
+ valueVoucherFlowCents: fluss,
118
+ bucketsCents: {
119
+ amountRateStandard: euroToCent(t.amountRateStandard),
120
+ amountRateReduced1: euroToCent(t.amountRateReduced1),
121
+ amountRateReduced2: euroToCent(t.amountRateReduced2),
122
+ amountRateZero: euroToCent(t.amountRateZero),
123
+ amountRateSpecial: euroToCent(t.amountRateSpecial),
124
+ amountRatOthers: euroToCent(t.amountRatOthers),
125
+ },
88
126
  },
127
+ kunde,
128
+ minusJeTopf: plan.minusJeTopf,
129
+ auszahlungCents: plan.auszahlungCents,
130
+ };
131
+ }
132
+ /**
133
+ * Hoechstbetrag zum Vorbelegen einer Einloesung an der Kasse, in Cent, nie
134
+ * negativ: der groesste Betrag, den der Server fuer diesen Gutschein annimmt.
135
+ *
136
+ * Obergrenze: Saldo, offener Betrag; bei Einzweck die deckbaren Positionen im
137
+ * Topf seines Satzes abzueglich der schon gewaehlten Einloesungen dort. Befreit
138
+ * verkauft bzw. als Kleinunternehmer verteilt der Server anteilig auf alle
139
+ * Saetze (`kuTeile`); dann begrenzt jeder Topf: sein Anteil plus die schon
140
+ * gewaehlten Einloesungen dort darf seine Positionen nicht uebersteigen.
141
+ *
142
+ * Unter dieser Grenze wird jeder Kandidat mit genau der Rechnung von
143
+ * [receiptDueCents] geprueft (Rundung halber Cent, groesster Rest), zuerst
144
+ * Cent fuer Cent abwaerts (64 Schritte). Findet das nichts, sucht eine
145
+ * Bisektion ueber [1, Grenze] und prueft danach 16 Cent aufwaerts: am Rand
146
+ * des groessten Rests ist die Annahme nicht monoton. Leistung (wird ueber
147
+ * Positionen eingeloest), Aktion (Rabatt rechnet der Server) und jeder nicht
148
+ * aktive Gutschein: 0.
149
+ */
150
+ function voucherRedeemMaxCents(items, voucher, before = {}) {
151
+ if (voucher.status !== 'active')
152
+ return 0;
153
+ if (voucher.type !== 'multi_purpose' && voucher.type !== 'single_purpose')
154
+ return 0;
155
+ const saldo = Number.isInteger(voucher.balanceCents) ? voucher.balanceCents : 0;
156
+ const options = before.options ?? {};
157
+ const vorher = before.vouchers ?? [];
158
+ const r = rechne(items, vorher, 'standard', options);
159
+ // Rundung je Topf: hoechstens ein halber Cent je Topf ueber dem offenen Betrag.
160
+ let oben = Math.min(saldo, r.ergebnis.dueCents + r.auszahlungCents + 6, HOECHSTBETRAG_CENTS);
161
+ if (voucher.type === 'single_purpose') {
162
+ const deckbar = r.kunde.filter(gutschein_plan_js_1.istDeckbar);
163
+ const topfBasis = (topf) => (0, gutschein_plan_js_1.centSumme)(deckbar.filter((it) => (0, gutschein_plan_js_1.topfFuerSatz)(it.vat) === topf));
164
+ if (voucher.exempt === true || options.smallBusiness === true) {
165
+ oben = Math.min(oben, kuGrenze(r.kunde, topfBasis, r.minusJeTopf));
166
+ }
167
+ else {
168
+ if (!Number.isInteger(voucher.vatRateBp))
169
+ return 0;
170
+ const topf = (0, gutschein_plan_js_1.topfFuerSatz)(voucher.vatRateBp / 100);
171
+ oben = Math.min(oben, topfBasis(topf) - (r.minusJeTopf.get(topf) ?? 0));
172
+ }
173
+ }
174
+ if (oben <= 0)
175
+ return 0;
176
+ const code = probeCode(vorher);
177
+ const info = [...(options.voucherInfo ?? []), { code, check: voucher }];
178
+ const passt = (cents) => {
179
+ try {
180
+ rechne(items, [...vorher, { action: 'redeem', type: voucher.type, code, amountCents: cents }], 'standard', { ...options, voucherInfo: info });
181
+ return true;
182
+ }
183
+ catch (fehler) {
184
+ if (fehler instanceof ReceiptDueError)
185
+ return false;
186
+ throw fehler;
187
+ }
89
188
  };
189
+ return groessterBetrag(oben, passt);
190
+ }
191
+ /** Schritte der linearen Suche, bevor die Bisektion uebernimmt. */
192
+ const LINEAR_SCHRITTE = 64;
193
+ /** Cent, die nach der Bisektion aufwaerts geprueft werden (nicht monotoner Rand). */
194
+ const NACHPRUEFEN = 16;
195
+ /** Groesster Betrag in [1, oben], den `passt` annimmt; 0, wenn keiner gefunden wird. */
196
+ function groessterBetrag(oben, passt) {
197
+ const untenLinear = Math.max(0, oben - LINEAR_SCHRITTE);
198
+ for (let cents = oben; cents > untenLinear; cents--) {
199
+ if (passt(cents))
200
+ return cents;
201
+ }
202
+ // Bisektion ueber [1, untenLinear]: `ja` angenommen (0 gilt als angenommen), `nein` abgewiesen.
203
+ let ja = 0;
204
+ let nein = untenLinear + 1;
205
+ while (nein - ja > 1) {
206
+ const mitte = ja + Math.floor((nein - ja) / 2);
207
+ if (passt(mitte))
208
+ ja = mitte;
209
+ else
210
+ nein = mitte;
211
+ }
212
+ let bester = ja;
213
+ for (let cents = ja + 1; cents <= Math.min(untenLinear, ja + NACHPRUEFEN); cents++) {
214
+ if (passt(cents))
215
+ bester = cents;
216
+ }
217
+ return bester;
218
+ }
219
+ /**
220
+ * Obergrenze fuer eine anteilige Einloesung (`kuTeile`): je Satz ist der Anteil
221
+ * mindestens `floor(c * basis / summe)`, je Topf also mehr als
222
+ * `c * basisTopf / summe - saetzeImTopf`. Mit dem freien Rest des Topfs
223
+ * (Positionen minus schon gewaehlte Einloesungen) folgt
224
+ * `c <= (frei + saetzeImTopf) * summe / basisTopf`.
225
+ */
226
+ function kuGrenze(kunde, topfBasis, minusJeTopf) {
227
+ const saetze = (0, gutschein_plan_js_1.kuSaetze)(kunde);
228
+ const summe = saetze.reduce((a, x) => a + x.basis, 0);
229
+ let grenze = summe;
230
+ const jeTopf = new Map();
231
+ for (const x of saetze) {
232
+ const topf = (0, gutschein_plan_js_1.topfFuerSatz)(x.satzBp / 100);
233
+ const t = jeTopf.get(topf) ?? { basis: 0, anzahl: 0 };
234
+ t.basis += x.basis;
235
+ t.anzahl += 1;
236
+ jeTopf.set(topf, t);
237
+ }
238
+ for (const [topf, t] of jeTopf) {
239
+ const frei = topfBasis(topf) - (minusJeTopf.get(topf) ?? 0);
240
+ grenze = Math.min(grenze, Math.floor(((frei + t.anzahl) * summe) / t.basis));
241
+ }
242
+ return Math.max(0, grenze);
243
+ }
244
+ const HOECHSTBETRAG_CENTS = 1_000_000;
245
+ /** Ein gueltiger Code, der unter den schon gewaehlten Gutscheinen nicht vorkommt (nur fuer die Probe-Rechnung). */
246
+ function probeCode(vorher) {
247
+ const belegt = new Set(vorher.map((v) => {
248
+ const g = typeof v.code === 'string' ? validator_1.VoucherCode.validate(v.code) : null;
249
+ return g && g.ok ? g.normalized : null;
250
+ }));
251
+ for (let i = 0;; i++) {
252
+ const code = validator_1.VoucherCode.generate(Uint8Array.from({ length: 11 }, (_, k) => (i + k * 7) & 31));
253
+ if (!belegt.has(code))
254
+ return code;
255
+ }
256
+ }
257
+ /** Heute in Wien als `YYYY-MM-DD`. */
258
+ function wienerTag() {
259
+ const w = (0, vienna_time_js_1.toViennaWallClock)(new Date());
260
+ return `${w.year}-${String(w.month).padStart(2, '0')}-${String(w.day).padStart(2, '0')}`;
90
261
  }
91
262
  function belegArt(wert) {
92
263
  const text = typeof wert === 'object' && wert !== null ? wert.value : wert;
@@ -109,12 +280,15 @@ function innen(item, i) {
109
280
  if (typeof item.quantity !== 'number' || !Number.isFinite(item.quantity))
110
281
  throw new ReceiptDueError('invalid_item', `Position ${i + 1} hat keine gueltige Menge.`);
111
282
  const tip = item.kind === 'tip' ? (item.recipient?.owner === true ? 'owner' : 'staff') : null;
112
- return { amount: item.quantity, priceOne: Math.round(item.priceCents) / 100, vat: satz, tip };
113
- }
114
- function gutscheinInnen(v, i) {
115
- if (!Number.isSafeInteger(v.valueCents))
116
- throw new ReceiptDueError('invalid_voucher', `Gutschein ${i + 1} hat keinen ganzen Cent-Wert.`);
117
- return { action: String(v.action), type: String(v.type), value: Math.round(v.valueCents) / 100 };
283
+ return {
284
+ amount: item.quantity,
285
+ priceOne: Math.round(item.priceCents) / 100,
286
+ vat: satz,
287
+ tip,
288
+ // `kind: null` ist wie keines (der Server prueft `!it.kind`).
289
+ ...(item.kind != null ? { kind: item.kind } : {}),
290
+ ...(item.articleId != null ? { articleId: item.articleId } : {}),
291
+ };
118
292
  }
119
293
  /** Trinkgeld-Auftraege in der Reihenfolge des Servers: `tip`, dann je Zahlart die Summe der `tipCents`. */
120
294
  function trinkgeldAuftraege(options) {
@@ -234,18 +408,6 @@ function splitOwnerTip(cents, jeSatz) {
234
408
  return verteilt.map(({ vat, cents: c }) => ({ vat, cents: c }));
235
409
  }
236
410
  const FUENF = ['amountRateStandard', 'amountRateReduced1', 'amountRateReduced2', 'amountRateZero', 'amountRateSpecial'];
237
- /** `vat-buckets.bucketFuerSatz`. */
238
- function topfFuerSatz(vat) {
239
- switch (vat) {
240
- case 20: return 'amountRateStandard';
241
- case 10: return 'amountRateReduced1';
242
- case 13: return 'amountRateReduced2';
243
- case 0: return 'amountRateZero';
244
- case 19: return 'amountRateSpecial';
245
- case 4.9: return 'amountRateSpecial';
246
- default: return 'amountRatOthers';
247
- }
248
- }
249
411
  /** `beleg-toepfe.belegToepfe`, Schritt fuer Schritt. */
250
412
  function belegToepfe(positionen, gutscheine) {
251
413
  const t = { amountRateStandard: 0, amountRateReduced1: 0, amountRateReduced2: 0, amountRateZero: 0, amountRateSpecial: 0, amountRatOthers: 0 };
@@ -254,7 +416,7 @@ function belegToepfe(positionen, gutscheine) {
254
416
  if (p.tip === 'staff')
255
417
  personal += p.amount * p.priceOne;
256
418
  else
257
- t[topfFuerSatz(p.vat)] += p.priceOne * p.amount;
419
+ t[(0, gutschein_plan_js_1.topfFuerSatz)(p.vat)] += p.priceOne * p.amount;
258
420
  }
259
421
  const gesamt = t.amountRateStandard + t.amountRateReduced1 + t.amountRateReduced2 + t.amountRateZero + t.amountRateSpecial;
260
422
  if (gutscheine.length > 0) {
@@ -290,17 +452,21 @@ function belegToepfe(positionen, gutscheine) {
290
452
  euroToCent(t.amountRateZero) + euroToCent(t.amountRateSpecial) + euroToCent(t.amountRatOthers);
291
453
  return { ...t, zaehlerDeltaCents };
292
454
  }
293
- /** `zahlungen-core.wertgutscheinFlussCents`. */
455
+ /**
456
+ * `zahlungen-core.wertgutscheinFlussCents`: Eintraege im 0-%-Topf
457
+ * (`imNullTopf`) stehen schon als Position; eine Auszahlung ohne
458
+ * Topf-Position mindert den Zahlbetrag.
459
+ */
294
460
  function wertgutscheinFlussCents(gutscheine, art) {
295
461
  const vz = art === 'cancellation' ? -1 : 1;
296
462
  let cents = 0;
297
463
  for (const v of gutscheine) {
298
- if (v.type !== index_js_1.VoucherType.value || !Number.isFinite(v.value))
464
+ if (v.type !== index_js_1.VoucherType.value || v.imNullTopf === true || !Number.isFinite(v.value))
299
465
  continue;
300
466
  const c = euroToCent(v.value);
301
467
  if (v.action === index_js_1.VoucherAction.sell)
302
468
  cents += c;
303
- else if (v.action === index_js_1.VoucherAction.redeem)
469
+ else if (v.action === index_js_1.VoucherAction.redeem || v.action === index_js_1.VoucherAction.payout)
304
470
  cents -= c;
305
471
  }
306
472
  return vz * cents + 0;
@@ -63,10 +63,22 @@ function renderReceiptGrid(layout, options = {}) {
63
63
  const lines = [];
64
64
  for (const z of layout.lines) {
65
65
  switch (z.kind) {
66
- case 'text':
66
+ case 'text': {
67
+ // Haengender Einzug: ein linksbuendiger Text mit fuehrenden Leerzeichen
68
+ // (Vermerke unter einer Gutscheinzeile, „ Grund: …“) bricht mit
69
+ // demselben Einzug um, auf 32 wie auf 48 Zeichen. Passt er in eine
70
+ // Zeile, ergibt das dieselben Zeichen wie ohne Einzugsregel.
71
+ const einzug = z.align === 'left' ? z.text.length - z.text.trimStart().length : 0;
72
+ if (einzug > 0 && einzug < zeichen && z.text.trimStart() !== '') {
73
+ const vorn = ' '.repeat(einzug);
74
+ for (const t of (0, escpos_js_1.wrapText)(z.text.slice(einzug), zeichen - einzug))
75
+ lines.push({ text: ausrichten(vorn + t, zeichen, 'left'), kind: 'text', bold: z.bold });
76
+ break;
77
+ }
67
78
  for (const t of (0, escpos_js_1.wrapText)(z.text, zeichen))
68
79
  lines.push({ text: ausrichten(t, zeichen, z.align), kind: 'text', bold: z.bold });
69
80
  break;
81
+ }
70
82
  case 'banner': {
71
83
  // Der Rahmen ist Teil des Rasters: eine Zeile '=' ueber die volle Breite
72
84
  // davor und danach. So setzt ihn jeder Weg -- Bildschirm, Bon, ePOS, PDF,
@@ -0,0 +1,104 @@
1
+ import type { Voucher } from '../models/voucher.js';
2
+ import type { PosVoucherSettings, VoucherArticle, VoucherCheck } from '../pos/gutschein.js';
3
+ /**
4
+ * Gutscheine der neuen Form im Zahlbetrag: Zwilling von
5
+ * `gutschein-beleg.plane` im Backend, soweit er den Zahlbetrag bestimmt.
6
+ *
7
+ * Der Server macht aus den Wuenschen zusaetzliche Positionen (`kind`
8
+ * `voucher` bzw. `voucher_float`, hinten an Ware und Trinkgeld angehaengt, in
9
+ * der Reihenfolge der Wuensche) und Eintraege in Belegform; nur Eintraege der
10
+ * Art Wert (`value`) und Rabatt (`promo`) gehen danach in die Toepfe und in
11
+ * den Wertgutschein-Fluss (`gutschein-beleg.zahlbetrag`). Genau das steht
12
+ * hier, mit derselben Euro-Arithmetik:
13
+ *
14
+ * - Mehrzweck einloesen: Eintrag `value`, mindert den Zahlbetrag als Fluss.
15
+ * - Mehrzweck verkaufen: Eintrag `value` (+Fluss); ab dem Stichtag des
16
+ * 0-%-Topfs stattdessen Position im 0-%-Satz ohne Fluss.
17
+ * - Mehrzweck auszahlen: immer Position im 0-%-Satz (minus), kein Fluss.
18
+ * - Einzweck: Position im Satz (Verkauf aus der Einstellung der Kasse,
19
+ * Einloesung und Auszahlung aus der Pruefung des Gutscheins); als
20
+ * Kleinunternehmer eingeloest anteilig auf alle Saetze des Korbs.
21
+ * - Leistung verkaufen: Stueck mal Artikelpreis im Artikelsatz; einloesen: je
22
+ * Satz eine Minuszeile ueber die gewaehlten Positionen.
23
+ * - Aktion: Rabatt wie `promo`, Betrag aus der Pruefung, hoechstens die Summe.
24
+ *
25
+ * Kein Fehler wird still geschluckt: was der Server mit einem Gutschein-Code
26
+ * abwiese, liefert `fehler` mit genau diesem Code (`voucher_*`). Der
27
+ * Klartext-Code steht nie in einem Text.
28
+ */
29
+ /** Position in der inneren Form des Backends (`beleg-toepfe.interneForm`). */
30
+ export interface Innen {
31
+ amount: number;
32
+ priceOne: number;
33
+ vat: number;
34
+ tip: 'staff' | 'owner' | null;
35
+ /** `kind` der Position (Trinkgeld, Rabatt, Gutscheinzeile); Ware hat keines. */
36
+ kind?: string;
37
+ articleId?: string;
38
+ }
39
+ /** Gutschein-Eintrag in Belegform, soweit Toepfe und Fluss ihn lesen. */
40
+ export interface Gutschein {
41
+ action: string;
42
+ type: string;
43
+ value: number;
44
+ /** Betrag steht schon als Position im 0-%-Topf: kein Fluss mehr. */
45
+ imNullTopf?: boolean;
46
+ }
47
+ /** Je Einloesung bzw. Auszahlung die gepruefte Sicht des Gutscheins (aus `checkVoucher`), zugeordnet ueber den Code. */
48
+ export interface VoucherDueInfo {
49
+ /** Der Code, wie er gesendet wird (tolerant: Gross/klein, Bindestriche, `O`/`0`, `I`/`L`/`1`). */
50
+ code: string;
51
+ check: VoucherCheck;
52
+ }
53
+ /** Artikel fuer den Verkauf eines Dienstleistungsgutscheins: Preis und Satz wie im Artikelstamm. */
54
+ export type VoucherDueArticle = Pick<VoucherArticle, 'id' | 'priceCents' | 'vatRateBp'>;
55
+ /**
56
+ * Stichtag des 0-%-Topfs fuer Mehrzweck-Verkaeufe (Backend
57
+ * `gutschein-core.NULLTOPF_AB`). Solange er in der Zukunft liegt, zaehlt ein
58
+ * Mehrzweck-Verkauf als Fluss; ab dem Tag als Position im 0-%-Satz. Der
59
+ * Zahlbetrag bleibt gleich, Toepfe und Fluss verschieben sich.
60
+ */
61
+ export declare const VOUCHER_ZERO_RATE_BUCKET_FROM = "2099-12-31";
62
+ export interface PlanOptionen {
63
+ voucherInfo?: readonly VoucherDueInfo[];
64
+ voucherSettings?: PosVoucherSettings;
65
+ voucherArticles?: readonly VoucherDueArticle[];
66
+ smallBusiness?: boolean;
67
+ zeroRateBucketFrom?: string;
68
+ /** Wiener Tag `YYYY-MM-DD`; der Aufrufer setzt den heutigen, wenn er fehlt. */
69
+ today: string;
70
+ }
71
+ export interface Plan {
72
+ /** Positionen, die der Server hinten anhaengt. */
73
+ zusatz: Innen[];
74
+ /** Eintraege, die Toepfe und Fluss lesen (`gutschein-beleg.zahlbetrag`). */
75
+ gutscheine: Gutschein[];
76
+ /** Summe der Auszahlungen in Cent. */
77
+ auszahlungCents: number;
78
+ /** Mindestens ein Wunsch der neuen Form: der Server rechnet mit `plane`. */
79
+ mitPlan: boolean;
80
+ /** Summe der Einloese-Minuszeilen je Topf (Deckung, Vorbelegung). */
81
+ minusJeTopf: Map<string, number>;
82
+ }
83
+ export interface PlanFehler {
84
+ fehler: string;
85
+ text: string;
86
+ }
87
+ type TopfName = 'amountRateStandard' | 'amountRateReduced1' | 'amountRateReduced2' | 'amountRateZero' | 'amountRateSpecial' | 'amountRatOthers';
88
+ /** `vat-buckets.bucketFuerSatz`. */
89
+ export declare function topfFuerSatz(vat: number): TopfName;
90
+ /** Position, die ein Gutschein decken darf: weder Trinkgeld noch Gutscheinzeile. */
91
+ export declare function istDeckbar(p: Innen): boolean;
92
+ /** Wie beleg-toepfe: Euro in Positionsreihenfolge aufsummieren, erst die Summe auf Cent runden. */
93
+ export declare function centSumme(liste: readonly Innen[]): number;
94
+ /** Die Saetze, auf die `kuTeile` verteilt: je Satz `centSumme` der deckbaren Positionen, nur positive. */
95
+ export declare function kuSaetze(kunde: readonly Innen[]): Array<{
96
+ satzBp: number;
97
+ basis: number;
98
+ }>;
99
+ /**
100
+ * Wuensche und gelesene Eintraege in Positionen und Belegeintraege, wie
101
+ * `plane` sie baut. `kunde` sind Ware und Trinkgeld in innerer Form.
102
+ */
103
+ export declare function planeGutscheine(kunde: readonly Innen[], vouchers: readonly Voucher[], art: string, o: PlanOptionen): Plan | PlanFehler;
104
+ export {};