@kreiseck/kasseneck-api 0.6.9 → 0.6.44

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 (70) hide show
  1. package/README.md +4 -0
  2. package/dist/cjs/client/index.d.ts +1 -1
  3. package/dist/cjs/client/receipts.d.ts +19 -0
  4. package/dist/cjs/client/receipts.js +51 -0
  5. package/dist/cjs/index.d.ts +2 -2
  6. package/dist/cjs/index.js +3 -2
  7. package/dist/cjs/kasse/settings.d.ts +39 -3
  8. package/dist/cjs/kasse/settings.js +28 -6
  9. package/dist/cjs/models/index.d.ts +1 -1
  10. package/dist/cjs/models/index.js +3 -1
  11. package/dist/cjs/models/receipt-item.d.ts +41 -0
  12. package/dist/cjs/models/receipt-item.js +41 -1
  13. package/dist/cjs/models/receipt.d.ts +7 -0
  14. package/dist/cjs/models/receipt.js +2 -0
  15. package/dist/cjs/printing/escpos.js +7 -0
  16. package/dist/cjs/react/index.js +11 -1
  17. package/dist/cjs/receipt/discount.js +1 -1
  18. package/dist/cjs/receipt/layout-escpos.js +9 -4
  19. package/dist/cjs/receipt/layout.d.ts +6 -3
  20. package/dist/cjs/receipt/layout.js +166 -14
  21. package/dist/cjs/register/pairing.d.ts +15 -0
  22. package/dist/cjs/register/pairing.js +22 -3
  23. package/dist/esm/client/index.d.ts +1 -1
  24. package/dist/esm/client/receipts.d.ts +19 -0
  25. package/dist/esm/client/receipts.js +51 -0
  26. package/dist/esm/index.d.ts +2 -2
  27. package/dist/esm/index.js +1 -1
  28. package/dist/esm/kasse/settings.d.ts +39 -3
  29. package/dist/esm/kasse/settings.js +28 -6
  30. package/dist/esm/models/index.d.ts +1 -1
  31. package/dist/esm/models/index.js +1 -1
  32. package/dist/esm/models/receipt-item.d.ts +41 -0
  33. package/dist/esm/models/receipt-item.js +39 -1
  34. package/dist/esm/models/receipt.d.ts +7 -0
  35. package/dist/esm/models/receipt.js +2 -0
  36. package/dist/esm/printing/escpos.js +7 -0
  37. package/dist/esm/react/index.js +11 -1
  38. package/dist/esm/receipt/discount.js +1 -1
  39. package/dist/esm/receipt/layout-escpos.js +9 -4
  40. package/dist/esm/receipt/layout.d.ts +6 -3
  41. package/dist/esm/receipt/layout.js +167 -15
  42. package/dist/esm/register/pairing.d.ts +15 -0
  43. package/dist/esm/register/pairing.js +22 -3
  44. package/fixtures/belege/rabatt-chef-trinkgeld.json +89 -0
  45. package/fixtures/belege/rabatt-einfach.json +50 -0
  46. package/fixtures/belege/rabatt-trinkgeld.json +75 -0
  47. package/fixtures/belege/rabatt-wertgutschein.json +70 -0
  48. package/fixtures/belege/rabattzeilen.json +5 -3
  49. package/fixtures/belege/storno-rabatt.json +68 -0
  50. package/fixtures/erwartet/rabatt-chef-trinkgeld.grid32.txt +31 -0
  51. package/fixtures/erwartet/rabatt-chef-trinkgeld.grid48.txt +30 -0
  52. package/fixtures/erwartet/rabatt-chef-trinkgeld.lines.json +317 -0
  53. package/fixtures/erwartet/rabatt-einfach.grid32.txt +26 -0
  54. package/fixtures/erwartet/rabatt-einfach.grid48.txt +25 -0
  55. package/fixtures/erwartet/rabatt-einfach.lines.json +250 -0
  56. package/fixtures/erwartet/rabatt-trinkgeld.grid32.txt +31 -0
  57. package/fixtures/erwartet/rabatt-trinkgeld.grid48.txt +30 -0
  58. package/fixtures/erwartet/rabatt-trinkgeld.lines.json +336 -0
  59. package/fixtures/erwartet/rabatt-wertgutschein.grid32.txt +32 -0
  60. package/fixtures/erwartet/rabatt-wertgutschein.grid48.txt +31 -0
  61. package/fixtures/erwartet/rabatt-wertgutschein.lines.json +330 -0
  62. package/fixtures/erwartet/rabattzeilen.grid32.txt +4 -2
  63. package/fixtures/erwartet/rabattzeilen.grid48.txt +4 -2
  64. package/fixtures/erwartet/rabattzeilen.lines.json +18 -8
  65. package/fixtures/erwartet/storno-rabatt.grid32.txt +34 -0
  66. package/fixtures/erwartet/storno-rabatt.grid48.txt +32 -0
  67. package/fixtures/erwartet/storno-rabatt.lines.json +317 -0
  68. package/fixtures/kasse-settings-standard.json +44 -6
  69. package/fixtures/manifest.json +34 -4
  70. package/package.json +1 -1
@@ -56,6 +56,50 @@ function belegZeit(timeStamp) {
56
56
  * `typeof null === 'object'` ist, ergab das beim Lesen von `.label` einen
57
57
  * nackten TypeError.
58
58
  */
59
+ /**
60
+ * Kartenzahlungsblock fuer Hobex (HPS am Geraet wie Cloud-API): die Felder
61
+ * kommen aus `cardPaymentData` in der Form von [hobexReceiptToCardPaymentData]
62
+ * (models/hobex-receipt.ts) — Datum, TID, Genehmigungsnummer, Karte, PAN,
63
+ * Antwortcode. Bei CVM "1" verlangt die Karte eine Unterschrift; dafuer
64
+ * bekommt der Bon eine Linie. Der PAN haengt beim HPS das Ablaufdatum mit
65
+ * Unterstrich an ("…4720_2810") — gedruckt wird nur die maskierte Nummer.
66
+ *
67
+ * Andere Anbieter (GP Tom, SumUp, myPOS, Stripe) drucken in ihren eigenen
68
+ * Apps; ihre Bloecke bleiben bewusst draussen (siehe Kopfkommentar).
69
+ */
70
+ function hobexKartenblock(receipt) {
71
+ const provider = receipt.creditCardProvider;
72
+ const daten = receipt.cardPaymentData;
73
+ if ((provider !== 'hobexHps' && provider !== 'hobexCloudApi') || daten == null) {
74
+ return [];
75
+ }
76
+ const feld = (k) => {
77
+ const v = daten[k];
78
+ return v == null ? '' : String(v);
79
+ };
80
+ const paar = (teile) => teile.filter(Boolean).join(' · ');
81
+ const pan = feld('cardNumber').split('_')[0] ?? '';
82
+ const rc = feld('responseCode');
83
+ const zeilen = [textZeile('Hobex Beleg', 'center', true)];
84
+ const inhalt = [
85
+ feld('date'),
86
+ paar([feld('tid') && `TID ${feld('tid')}`, feld('no') && `Nr. ${feld('no')}`]),
87
+ paar([feld('type'), feld('cardBrand')]),
88
+ pan,
89
+ rc && `RC ${rc}`,
90
+ ];
91
+ for (const zeile of inhalt) {
92
+ if (zeile)
93
+ zeilen.push(textZeile(zeile, 'center'));
94
+ }
95
+ if (feld('cvm') === '1') {
96
+ zeilen.push({ kind: 'space', lines: 2 });
97
+ zeilen.push(textZeile('------------------', 'center'));
98
+ zeilen.push(textZeile('Unterschrift', 'center'));
99
+ }
100
+ zeilen.push({ kind: 'space', lines: 1 });
101
+ return zeilen;
102
+ }
59
103
  function zahlungsartText(wert) {
60
104
  if (wert != null && typeof wert === 'object') {
61
105
  return wert.label;
@@ -154,21 +198,19 @@ function gutscheinText(voucher) {
154
198
  if (name != null && name.toLowerCase().includes(text.toLowerCase())) {
155
199
  return name;
156
200
  }
157
- if (voucher.valueCents != null) {
158
- text += ` ${gutscheinBetrag(voucher.valueCents)} €`;
201
+ // Der Betrag steht in derselben Zeile rechts in der Betragsspalte — ihn
202
+ // auch ins Label zu schreiben war doppelt und brach auf 58 mm haesslich
203
+ // um ("Wertgutschein 3 €" + Folgezeile "€"). Statt dessen der Code: er
204
+ // identifiziert den Gutschein (Mehrzweck: der Kunde will wissen, WELCHER
205
+ // eingeloest wurde).
206
+ if (voucher.code != null && voucher.code !== '') {
207
+ text += ` ${voucher.code}`;
159
208
  }
160
209
  if (name != null && name.length > 0) {
161
210
  text += ` - ${name}`;
162
211
  }
163
212
  return text;
164
213
  }
165
- /**
166
- * Ganze Betraege kurz (`"10"`), krumme mit zwei Nachkommastellen (`"1,50"`) —
167
- * Zwilling von `KeckVoucher.formatVoucherAmount`.
168
- */
169
- function gutscheinBetrag(cents) {
170
- return cents % 100 === 0 ? String(cents / 100) : formatCents(cents);
171
- }
172
214
  // ----------------------------------------------------------------- Bauteile
173
215
  function textZeile(text, align = 'left', bold = false) {
174
216
  return { kind: 'text', text, align, bold };
@@ -196,6 +238,46 @@ function tabellenZeile(werte, paperSize) {
196
238
  })),
197
239
  };
198
240
  }
241
+ /**
242
+ * Trinkgeld auf dem Bon: keine Menge, kein Einzelpreis — Bezeichnung und
243
+ * Summe. Eine Steuer-Kategorie steht hinter dem Betrag (D = Durchlaeufer,
244
+ * sonst Inhaber-Trinkgeld als Umsatz); bei mehreren steht die Aufteilung in
245
+ * einer zweiten, eingerueckten Zeile. Breite 8:4: „Trinkgeld Personal“ (18
246
+ * Zeichen) muss auf 58 mm in die Zeile passen, rechts reichen zehn Zeichen
247
+ * auch fuer „-123,45 D“.
248
+ */
249
+ function trinkgeldZeilen(name, teile) {
250
+ const summe = teile.reduce((s, t) => s + t.cents, 0);
251
+ if (teile.length === 1) {
252
+ return [{
253
+ kind: 'columns',
254
+ columns: [
255
+ { text: name, width: 8, align: 'left' },
256
+ { text: `${formatCents(summe)} ${teile[0].satz.category}`, width: 4, align: 'right' },
257
+ ],
258
+ }];
259
+ }
260
+ const sortiert = [...teile].sort((a, b) => a.satz.category.localeCompare(b.satz.category));
261
+ return [
262
+ {
263
+ kind: 'columns',
264
+ columns: [
265
+ { text: name, width: 8, align: 'left' },
266
+ // Stern statt Kategorie-Buchstabe: fluchtet mit den Warenbetraegen
267
+ // (" B" ist zwei Zeichen, " *" auch) und verweist auf die
268
+ // Aufteilungszeile darunter — klassische Fussnote.
269
+ { text: `${formatCents(summe)} *`, width: 4, align: 'right' },
270
+ ],
271
+ },
272
+ { kind: 'text', text: `* davon ${sortiert.map((t) => `${formatCents(t.cents)} ${t.satz.category}`).join(', ')}`, align: 'left', bold: false },
273
+ ];
274
+ }
275
+ /**
276
+ * Rabatt auf dem Bon: dieselbe Form wie Trinkgeld — Summenzeile, bei einem
277
+ * Steuersatz mit Kategorie-Buchstaben, im Mischfall mit Stern und der
278
+ * Aufteilung als Fussnote darunter (siehe trinkgeldZeilen).
279
+ */
280
+ const rabattZeilen = trinkgeldZeilen;
199
281
  /** Positionszeile: Menge, Bezeichnung, ab Menge 2 der Einzelpreis. */
200
282
  function positionsZeile(item, satz) {
201
283
  const menge = String(item.quantity);
@@ -454,10 +536,58 @@ function buildReceiptLayout(receipt, company, options = {}) {
454
536
  gruppen.set(satz.rate, neu);
455
537
  return neu;
456
538
  };
457
- for (const item of receipt.items) {
458
- const satz = steuersatz(item.vat);
459
- gruppeVon(satz).bruttoCents += (0, index_js_2.receiptItemTotalCents)(item);
460
- lines.push(positionsZeile(item, satz));
539
+ // Trinkgeld-Positionen gleichen Namens werden auf dem Bon zu EINER Zeile
540
+ // mit der Summe zusammengefasst (die Aufteilung des Inhaber-Trinkgelds auf
541
+ // mehrere Steuersaetze bzw. mehrere Empfaenger ist fiskalisch notwendig,
542
+ // fuer den Gast aber nur Rauschen); bei mehr als einer Steuer-Kategorie
543
+ // steht die Aufteilung darunter. USt-Tabelle und DEP bleiben positionsgenau.
544
+ // Rabatt-Positionen gleichen Namens werden wie Trinkgeld zu EINER Zeile mit
545
+ // der Summe zusammengefasst (die Aufteilung je Steuersatz ist fiskalisch
546
+ // notwendig, fuer den Gast aber Rauschen); vor dem ERSTEN Rabatt-Block steht
547
+ // einmalig die Zwischensumme der Waren — der Gast sieht vorher/nachher.
548
+ // USt-Tabelle und DEP bleiben positionsgenau.
549
+ const warenSummeCents = receipt.items
550
+ .filter((it) => !(0, index_js_2.isTipItem)(it) && !(0, index_js_2.isDiscountItem)(it))
551
+ .reduce((summe, it) => summe + (0, index_js_2.receiptItemTotalCents)(it), 0);
552
+ let zwischensummeGezeigt = false;
553
+ let i = 0;
554
+ while (i < receipt.items.length) {
555
+ const item = receipt.items[i];
556
+ if (!(0, index_js_2.isTipItem)(item) && !(0, index_js_2.isDiscountItem)(item)) {
557
+ const satz = steuersatz(item.vat);
558
+ gruppeVon(satz).bruttoCents += (0, index_js_2.receiptItemTotalCents)(item);
559
+ lines.push(positionsZeile(item, satz));
560
+ i += 1;
561
+ continue;
562
+ }
563
+ const artVon = (it) => ((0, index_js_2.isTipItem)(it) ? 'tip' : 'discount');
564
+ const art = artVon(item);
565
+ const gruppe = [];
566
+ while (i < receipt.items.length && artVon(receipt.items[i]) === art && !(!(0, index_js_2.isTipItem)(receipt.items[i]) && !(0, index_js_2.isDiscountItem)(receipt.items[i])) && receipt.items[i].name === item.name) {
567
+ gruppe.push(receipt.items[i]);
568
+ i += 1;
569
+ }
570
+ const teile = [];
571
+ for (const teil of gruppe) {
572
+ const satz = steuersatz(teil.vat);
573
+ gruppeVon(satz).bruttoCents += (0, index_js_2.receiptItemTotalCents)(teil);
574
+ const vorhanden = teile.find((t) => t.satz.category === satz.category);
575
+ if (vorhanden)
576
+ vorhanden.cents += (0, index_js_2.receiptItemTotalCents)(teil);
577
+ else
578
+ teile.push({ satz, cents: (0, index_js_2.receiptItemTotalCents)(teil) });
579
+ }
580
+ if (art === 'discount' && !zwischensummeGezeigt) {
581
+ // Trennlinie vor dem Rabatt-Block: die Warenzeilen enden sichtbar,
582
+ // darunter beginnt die Verrechnung (Zwischensumme, Rabatt).
583
+ lines.push({ kind: 'rule', char: '-' });
584
+ // Zwei Fuellzeichen, wo bei den Warenzeilen " B" steht — so fluchten
585
+ // ALLE Betraege (Waren, Zwischensumme, Rabatt, Trinkgeld) in einer
586
+ // Spalte; die letzten zwei Zeichen sind die Kategorie-/Stern-Spalte.
587
+ lines.push(paarZeile('Zwischensumme', `${formatCents(warenSummeCents)} `, 8, 4));
588
+ zwischensummeGezeigt = true;
589
+ }
590
+ lines.push(...(art === 'discount' ? rabattZeilen(item.name, teile) : trinkgeldZeilen(item.name, teile)));
461
591
  }
462
592
  // --- Gutscheine: verkaufte Wertgutscheine sind Umsatz zu 0 %, eingeloeste
463
593
  // Promotionsgutscheine mindern die Steuergruppen anteilig.
@@ -505,6 +635,24 @@ function buildReceiptLayout(receipt, company, options = {}) {
505
635
  formatCents(bruttoCents),
506
636
  ], paperSize));
507
637
  }
638
+ // Mitarbeiter-Trinkgeld ist als Null-%-Umsatz erfasst (Erlass 2.4.2.1), aber
639
+ // kein Umsatz des Betriebs — die Klarstellung steht direkt unter der
640
+ // 0-%-Zeile, damit der Beleg (auch als Rechnung) nicht nach steuerfreiem
641
+ // Umsatz aussieht. Inhaber-Trinkgeld ist Umsatz und bekommt keine Zeile.
642
+ const durchlaufendesTrinkgeldCents = receipt.items
643
+ .filter((it) => (0, index_js_2.isTipItem)(it) && it.recipient?.owner !== true)
644
+ .reduce((summe, it) => summe + (0, index_js_2.receiptItemTotalCents)(it), 0);
645
+ // Nur wenn die 0-%-Gruppe MEHR enthaelt als das Trinkgeld (Wertgutschein-
646
+ // Verkauf ist ebenfalls Kategorie D, beim Kleinunternehmer ist alles 0 %):
647
+ // dann trennt diese Zeile den Durchlaeufer vom uebrigen Null-Prozent-Betrag.
648
+ // Besteht die 0-%-Gruppe ohnehin nur aus dem Trinkgeld, sagt die Position
649
+ // "Trinkgeld Personal" schon alles — die Wiederholung waere Rauschen.
650
+ const nullGruppeCents = gruppen.get(index_js_1.VatRate.vat0.rate)?.bruttoCents ?? 0;
651
+ if (durchlaufendesTrinkgeldCents !== 0 && nullGruppeCents !== durchlaufendesTrinkgeldCents) {
652
+ // Breite 9:3 — auf 58 mm (32 Zeichen) passt die Beschriftung so in eine
653
+ // Zeile; der Betrag braucht rechts hoechstens acht Zeichen.
654
+ lines.push(paarZeile('Trinkgeld, kein Umsatz:', formatCents(durchlaufendesTrinkgeldCents), 9, 3));
655
+ }
508
656
  lines.push({ kind: 'rule', char: '-' });
509
657
  // --- Summen
510
658
  const summeCents = (0, index_js_2.receiptSumCents)(receipt);
@@ -513,7 +661,9 @@ function buildReceiptLayout(receipt, company, options = {}) {
513
661
  lines.push(paarZeile('Zwischensumme', `${formatCents(zwischensummeCents)} €`));
514
662
  for (const voucher of receipt.vouchers) {
515
663
  if (voucher.action === index_js_1.VoucherAction.redeem && voucher.type === index_js_1.VoucherType.value) {
516
- lines.push(paarZeile(gutscheinText(voucher), `-${formatCents(voucher.valueCents ?? 0)} €`));
664
+ // Breite 8:4 — "Wertgutschein GS-1" (Label + Code) passt so auf 58 mm
665
+ // in die Zeile, statt den Code in eine Folgezeile fliessen zu lassen.
666
+ lines.push(paarZeile(gutscheinText(voucher), `-${formatCents(voucher.valueCents ?? 0)} €`, 8, 4));
517
667
  }
518
668
  }
519
669
  lines.push({ kind: 'rule', char: '-' });
@@ -538,6 +688,8 @@ function buildReceiptLayout(receipt, company, options = {}) {
538
688
  // --- RKSV-QR-Code
539
689
  lines.push({ kind: 'qr', data: receipt.qr });
540
690
  lines.push({ kind: 'space', lines: 1 });
691
+ // --- Kartenzahlungsblock (Hobex) — wie im Vorbild UNTER dem QR-Code.
692
+ lines.push(...hobexKartenblock(receipt));
541
693
  // --- Dankestext und Fusszeilen
542
694
  if (company.thanksMessage.length > 0) {
543
695
  lines.push({ kind: 'space', lines: 1 });
@@ -41,6 +41,11 @@ export interface RegisterGeraeteAngaben {
41
41
  geo?: RegisterGeo | null;
42
42
  }
43
43
  export interface PairRegisterDeviceOptions extends RegisterDeviceConnection, RegisterGeraeteAngaben {
44
+ /**
45
+ * Die Kasse ist schon auf einem anderen Geraet geoeffnet (BELEGT-Rueckfrage):
46
+ * `true` uebernimmt sie hierher und widerruft das alte Geraet.
47
+ */
48
+ takeover?: boolean;
44
49
  /**
45
50
  * Achtstelliger Kopplungs-Code aus dem Panel. Er ist 15 Minuten gueltig,
46
51
  * genau **einmal** verwendbar und gilt fuer eine bestimmte Kasse.
@@ -107,6 +112,16 @@ export interface RegisterDeviceUsers {
107
112
  loginMode: RegisterLoginMode;
108
113
  /** Der Betrieb verlangt die Ortung beim Login (Standortsperre an). */
109
114
  standortsperre: boolean;
115
+ /**
116
+ * Darf die gebundene Kasse ueberhaupt Belege erstellen? `bereit: false`
117
+ * nennt den Grund (ausser Betrieb, Schlussbeleg erstellt) — die Kasse
118
+ * sperrt das Kassieren VOR dem ersten Beleg statt an createReceipt zu
119
+ * scheitern. Fehlt das Feld (aelteres Backend), gilt bereit.
120
+ */
121
+ kasse?: {
122
+ bereit: boolean;
123
+ grund?: string | null;
124
+ } | null;
110
125
  }
111
126
  /**
112
127
  * Rechte eines Kassen-Benutzers (Backend: `perms`, Vorlagen `PERMS_KASSIER`
@@ -71,10 +71,10 @@ function receiptsScopeOf(perms) {
71
71
  * [PairedRegisterDevice]).
72
72
  */
73
73
  async function pairRegisterDevice(options) {
74
- const { code, label, client, geo, ...verbindung } = options;
74
+ const { code, label, client, geo, takeover, ...verbindung } = options;
75
75
  pflicht('pairRegisterDevice', 'code', code);
76
76
  // Der Code ist das Geheimnis dieses Aufrufs — siehe Modulkommentar.
77
- const daten = await transportFuer(verbindung)('pairRegisterDevice', { code, label, client, geo: geo ?? undefined }, undefined, [code]);
77
+ const daten = await transportFuer(verbindung)('pairRegisterDevice', { code, label, client, geo: geo ?? undefined, takeover: takeover === true ? true : undefined }, undefined, [code]);
78
78
  return {
79
79
  deviceId: pflichtfeld('pairRegisterDevice', daten, 'deviceId'),
80
80
  deviceSecret: pflichtfeld('pairRegisterDevice', daten, 'deviceSecret'),
@@ -135,7 +135,26 @@ async function listRegisterUsersForDevice(options) {
135
135
  };
136
136
  const bd = daten?.betriebsdaten;
137
137
  const betriebsdaten = bd && typeof bd === 'object' ? (0, receipt_company_js_1.fromReceiptCompanyPayload)(bd) : null;
138
- return { users, policy: regel(daten?.policy), loginMode: daten?.loginMode === 'pin' ? 'pin' : 'auswahl', settings, betriebsdaten, standortsperre: daten?.standortsperre === true };
138
+ return {
139
+ users,
140
+ policy: regel(daten?.policy),
141
+ loginMode: daten?.loginMode === 'pin' ? 'pin' : 'auswahl',
142
+ settings,
143
+ betriebsdaten,
144
+ standortsperre: daten?.standortsperre === true,
145
+ // 0.6.27 ergaenzte nur den TYP — hier fiel das Feld beim Zusammenbau
146
+ // stumm weg, und die Kasse sah nie eine Sperre. Fehlt es (aelteres
147
+ // Backend), bleibt es undefined: dann gilt bereit.
148
+ kasse: kasseBereit(daten?.kasse),
149
+ };
150
+ }
151
+ function kasseBereit(wert) {
152
+ if (typeof wert !== 'object' || wert === null || Array.isArray(wert))
153
+ return undefined;
154
+ const roh = wert;
155
+ if (typeof roh['bereit'] !== 'boolean')
156
+ return undefined;
157
+ return { bereit: roh['bereit'], grund: typeof roh['grund'] === 'string' ? roh['grund'] : null };
139
158
  }
140
159
  /** Die Regel aus der Antwort — oder `null`, wenn keine brauchbare kommt. */
141
160
  function regel(wert) {
@@ -1,7 +1,7 @@
1
1
  export { type AuthCredentials, type KasseneckAuth, type ApiKeyAuthOptions, type RegisterUserAuthOptions, apiKeyAuth, registerUserAuth, } from './auth.js';
2
2
  export { DEFAULT_BASE_URL, DEFAULT_TIMEOUT_MS, type HttpResponseLike, type HttpRequestInit, type FetchLike, type TransportOptions, type TransportBodyFields, type KasseneckTransport, type KasseneckBinaryTransport, createTransport, createBinaryTransport, } from './transport.js';
3
3
  export { KasseneckApiError, KasseneckAuthError, KasseneckHttpError, KasseneckNetworkError, KasseneckValidationError, type ValidationScope, type KasseneckError, type HttpFailureReason, type CauseDigest, isKasseneckApiError, isKasseneckAuthError, isKasseneckHttpError, isKasseneckNetworkError, isKasseneckValidationError, } from './errors.js';
4
- export { type ReceiptCommonOptions, type SellReceiptOptions, type CancelReceiptOptions, type CreateCancelReceiptOptions, type ReceiptWithCompany, sellReceipt, sellReceiptWithCompany, cancelReceipt, type CancelReceiptResult, createCancelReceipt, zeroReceipt, getReceipt, getReceiptWithCompany, generateFullReceiptId, getFirstReceiptDate, listMyReceipts, type ListMyReceiptsOptions, type ReceiptList, type ReceiptListStats, checkVoucherCombinationError, } from './receipts.js';
4
+ export { type ReceiptCommonOptions, type SellReceiptOptions, type TipOptions, type TipRecipientShare, type CancelReceiptOptions, type CreateCancelReceiptOptions, type ReceiptWithCompany, sellReceipt, sellReceiptWithCompany, cancelReceipt, type CancelReceiptResult, createCancelReceipt, zeroReceipt, getReceipt, getReceiptWithCompany, generateFullReceiptId, getFirstReceiptDate, listMyReceipts, type ListMyReceiptsOptions, type ReceiptList, type ReceiptListStats, checkVoucherCombinationError, } from './receipts.js';
5
5
  export { listMyCashregisters } from './cashregisters.js';
6
6
  export { downloadDailyReport, downloadMonthlyReport } from './reports.js';
7
7
  export { type CashboxStatus, type SignatureStatus, getCashboxStatus, getSignatureStatus, } from './status.js';
@@ -39,6 +39,25 @@ export interface ReceiptCommonOptions {
39
39
  customProjectId?: string;
40
40
  /** Rohdaten der Kartenzahlung (Terminal-/Anbieterantwort). */
41
41
  cardPaymentData?: Record<string, unknown>;
42
+ /**
43
+ * Trinkgeld in Cent — als Zahl (an den angemeldeten Kassen-Benutzer, Zahlart
44
+ * des Belegs) oder als [TipOptions]. Das Backend bucht daraus signierte
45
+ * Positionen `kind:'tip'` (Mitarbeiter 0 % als Durchlaeufer, Inhaber
46
+ * anteilig je Steuersatz). Nur auf `standard` und `training`.
47
+ */
48
+ tip?: number | TipOptions;
49
+ }
50
+ /** Trinkgeld mit eigener Zahlart und/oder Empfaengern (Kassen-Benutzer-IDs, Summe = cents). */
51
+ export interface TipOptions {
52
+ cents: number;
53
+ /** Zahlart des Trinkgelds; ohne Angabe gilt die des Belegs. */
54
+ paymentMethod?: KeckPaymentMethod | KeckPaymentMethodKey;
55
+ /** Empfaenger; ohne Angabe der angemeldete Kassen-Benutzer. Summe der cents = cents. */
56
+ recipients?: TipRecipientShare[];
57
+ }
58
+ export interface TipRecipientShare {
59
+ registerUserId: string;
60
+ cents: number;
42
61
  }
43
62
  /** Belegtyp und Inhalt — die vollstaendige Eingabe von [createReceipt]. */
44
63
  export interface CreateReceiptOptions extends ReceiptCommonOptions {
@@ -73,6 +73,12 @@ function createReceiptParams(options) {
73
73
  }
74
74
  }
75
75
  }
76
+ if (options.tip != null) {
77
+ if (typ.value !== ReceiptType.standard.value && typ.value !== ReceiptType.training.value) {
78
+ throw eingabefehler(`Trinkgeld ist nur bei receiptType standard oder training moeglich, nicht bei "${typ.value}".`);
79
+ }
80
+ params['tip'] = gepruefterTip(options.tip);
81
+ }
76
82
  if (options.customProjectId != null) {
77
83
  params['customProjectId'] = options.customProjectId;
78
84
  }
@@ -358,6 +364,51 @@ function alsNutzlast(werte, wandeln) {
358
364
  throw eingabefehler(ursache instanceof Error ? ursache.message : 'Ungueltige Nutzlast uebergeben.');
359
365
  }
360
366
  }
367
+ /** Positive ganze Cent? */
368
+ function istCentBetrag(wert) {
369
+ return typeof wert === 'number' && Number.isInteger(wert) && wert > 0;
370
+ }
371
+ /**
372
+ * Trinkgeld pruefen und in die Nutzlast-Form bringen (Zahl bleibt Zahl; das
373
+ * Objekt geht mit geprueftem Zahlart-Wert und Empfaengern hinaus). Dieselben
374
+ * Regeln wie das Backend (tip-core.normalizeTip) — nur frueher.
375
+ */
376
+ function gepruefterTip(tip) {
377
+ if (typeof tip === 'number') {
378
+ if (!istCentBetrag(tip))
379
+ throw eingabefehler('Trinkgeld: Betrag muss eine ganze Zahl in Cent > 0 sein.');
380
+ return tip;
381
+ }
382
+ if (tip == null || typeof tip !== 'object' || !istCentBetrag(tip.cents)) {
383
+ throw eingabefehler('Trinkgeld: Betrag muss eine ganze Zahl in Cent > 0 sein.');
384
+ }
385
+ const nutzlast = { cents: tip.cents };
386
+ if (tip.paymentMethod != null)
387
+ nutzlast['paymentMethod'] = gepruefteZahlungsart(tip.paymentMethod);
388
+ if (tip.recipients != null) {
389
+ if (!Array.isArray(tip.recipients) || tip.recipients.length === 0) {
390
+ throw eingabefehler('Trinkgeld: recipients darf nicht leer sein.');
391
+ }
392
+ let summe = 0;
393
+ const gesehen = new Set();
394
+ for (const r of tip.recipients) {
395
+ if (r == null || typeof r.registerUserId !== 'string' || r.registerUserId === '') {
396
+ throw eingabefehler('Trinkgeld: recipients[].registerUserId fehlt.');
397
+ }
398
+ if (!istCentBetrag(r.cents))
399
+ throw eingabefehler('Trinkgeld: recipients[].cents muss eine ganze Zahl > 0 sein.');
400
+ if (gesehen.has(r.registerUserId))
401
+ throw eingabefehler(`Trinkgeld: Kassen-Benutzer ${r.registerUserId} doppelt.`);
402
+ gesehen.add(r.registerUserId);
403
+ summe += r.cents;
404
+ }
405
+ if (summe !== tip.cents) {
406
+ throw eingabefehler(`Trinkgeld: Summe der Empfaenger (${summe}) entspricht nicht dem Betrag (${tip.cents}).`);
407
+ }
408
+ nutzlast['recipients'] = tip.recipients.map((r) => ({ registerUserId: r.registerUserId, cents: r.cents }));
409
+ }
410
+ return nutzlast;
411
+ }
361
412
  /** Zahlungsart des Aufrufers pruefen — unbekannt wirft, bevor etwas rausgeht. */
362
413
  function gepruefteZahlungsart(wert) {
363
414
  if (typeof wert === 'object') {
@@ -1,5 +1,5 @@
1
1
  export { ReceiptType, type ReceiptTypeKey, VatRate, type VatRateKey, KeckPaymentMethod, type KeckPaymentMethodKey, CreditCardProvider, type CreditCardProviderKey, VoucherType, type VoucherTypeKey, VoucherAction, type VoucherActionKey, StripeLinkMode, type StripeLinkModeKey, } from './enums/index.js';
2
- export { type ReceiptItem, type ReceiptItemPayload, type ReceiptItemPayloadRead, toReceiptItemPayload, fromReceiptItemPayload, receiptItemTotalCents, receiptItemIsValid, negateReceiptItem, type Voucher, type VoucherPayload, toVoucherPayload, fromVoucherPayload, voucherIsValid, type Receipt, type ReceiptPayload, type ReceiptPayloadRead, toReceiptPayload, fromReceiptPayload, receiptSubSumCents, receiptSumCents, type CancellationOf, type CancellationItem, type Cancellation, type CancellationReason, CANCELLATION_REASONS, CANCELLATION_RESERVATION_MS, isCancellationReason, remainingQuantities, type ReceiptCompany, type ReceiptCompanyPayload, fromReceiptCompanyPayload, receiptCompanyTaxInfo, type Cashregister, type CashregisterOnboarding, type CashregisterPayload, type CashregisterOnboardingPayload, fromCashregisterPayload, type ReceiptSummary, type ReceiptSummaryPayload, fromReceiptSummaryPayload, type ZeroKind, ZERO_KINDS, istZeroKind, type ReportMonth, reportMonthFromDate, previousReportMonth, nextReportMonth, reportMonthKey, reportMonthReadable, type StripeUrlSession, type StripeUrlSessionPayload, toStripeUrlSessionPayload, fromStripeUrlSessionPayload, type HobexReceipt, type HobexReceiptPayload, toHobexReceiptPayload, fromHobexReceiptPayload, hobexReceiptToCardPaymentData, hobexReceiptNeedsSignature, } from './models/index.js';
3
- export { type AuthCredentials, type KasseneckAuth, type ApiKeyAuthOptions, type RegisterUserAuthOptions, apiKeyAuth, registerUserAuth, DEFAULT_BASE_URL, DEFAULT_TIMEOUT_MS, type HttpResponseLike, type HttpRequestInit, type FetchLike, type TransportOptions, type TransportBodyFields, type KasseneckTransport, type KasseneckBinaryTransport, createTransport, createBinaryTransport, KasseneckApiError, KasseneckAuthError, KasseneckHttpError, KasseneckNetworkError, KasseneckValidationError, type ValidationScope, type KasseneckError, type HttpFailureReason, type CauseDigest, isKasseneckApiError, isKasseneckAuthError, isKasseneckHttpError, isKasseneckNetworkError, isKasseneckValidationError, type ReceiptCommonOptions, type SellReceiptOptions, type CancelReceiptOptions, type CancelReceiptResult, type CreateCancelReceiptOptions, type ReceiptWithCompany, sellReceipt, sellReceiptWithCompany, cancelReceipt, createCancelReceipt, zeroReceipt, getReceipt, getReceiptWithCompany, generateFullReceiptId, getFirstReceiptDate, listMyReceipts, type ListMyReceiptsOptions, type ReceiptList, type ReceiptListStats, listMyCashregisters, checkVoucherCombinationError, downloadDailyReport, downloadMonthlyReport, type CashboxStatus, type SignatureStatus, getCashboxStatus, getSignatureStatus, type KasseneckApi, createKasseneckApi, } from './client/index.js';
2
+ export { type ReceiptItem, type ReceiptItemPayload, type ReceiptItemPayloadRead, toReceiptItemPayload, fromReceiptItemPayload, receiptItemTotalCents, receiptItemIsValid, negateReceiptItem, type TipRecipient, isTipItem, type Voucher, type VoucherPayload, toVoucherPayload, fromVoucherPayload, voucherIsValid, type Receipt, type ReceiptPayload, type ReceiptPayloadRead, toReceiptPayload, fromReceiptPayload, receiptSubSumCents, receiptSumCents, type CancellationOf, type CancellationItem, type Cancellation, type CancellationReason, CANCELLATION_REASONS, CANCELLATION_RESERVATION_MS, isCancellationReason, remainingQuantities, type ReceiptCompany, type ReceiptCompanyPayload, fromReceiptCompanyPayload, receiptCompanyTaxInfo, type Cashregister, type CashregisterOnboarding, type CashregisterPayload, type CashregisterOnboardingPayload, fromCashregisterPayload, type ReceiptSummary, type ReceiptSummaryPayload, fromReceiptSummaryPayload, type ZeroKind, ZERO_KINDS, istZeroKind, type ReportMonth, reportMonthFromDate, previousReportMonth, nextReportMonth, reportMonthKey, reportMonthReadable, type StripeUrlSession, type StripeUrlSessionPayload, toStripeUrlSessionPayload, fromStripeUrlSessionPayload, type HobexReceipt, type HobexReceiptPayload, toHobexReceiptPayload, fromHobexReceiptPayload, hobexReceiptToCardPaymentData, hobexReceiptNeedsSignature, } from './models/index.js';
3
+ export { type AuthCredentials, type KasseneckAuth, type ApiKeyAuthOptions, type RegisterUserAuthOptions, apiKeyAuth, registerUserAuth, DEFAULT_BASE_URL, DEFAULT_TIMEOUT_MS, type HttpResponseLike, type HttpRequestInit, type FetchLike, type TransportOptions, type TransportBodyFields, type KasseneckTransport, type KasseneckBinaryTransport, createTransport, createBinaryTransport, KasseneckApiError, KasseneckAuthError, KasseneckHttpError, KasseneckNetworkError, KasseneckValidationError, type ValidationScope, type KasseneckError, type HttpFailureReason, type CauseDigest, isKasseneckApiError, isKasseneckAuthError, isKasseneckHttpError, isKasseneckNetworkError, isKasseneckValidationError, type ReceiptCommonOptions, type SellReceiptOptions, type TipOptions, type TipRecipientShare, type CancelReceiptOptions, type CancelReceiptResult, type CreateCancelReceiptOptions, type ReceiptWithCompany, sellReceipt, sellReceiptWithCompany, cancelReceipt, createCancelReceipt, zeroReceipt, getReceipt, getReceiptWithCompany, generateFullReceiptId, getFirstReceiptDate, listMyReceipts, type ListMyReceiptsOptions, type ReceiptList, type ReceiptListStats, listMyCashregisters, checkVoucherCombinationError, downloadDailyReport, downloadMonthlyReport, type CashboxStatus, type SignatureStatus, getCashboxStatus, getSignatureStatus, type KasseneckApi, createKasseneckApi, } from './client/index.js';
4
4
  export { parseServerTimeStamp, toViennaWallClock, type ViennaWallClock } from './vienna-time.js';
5
5
  export { type KasseSettingsBetrieb, type KasseSettingsGeraet, type KasseSettings, type ArticleGroup, type KasseArtikel, KASSE_BETRIEB_STANDARD, KASSE_GERAET_STANDARD, mergeKasseSettings, fromArticleGroupPayload, fromKasseArtikelPayload, listMyArticleGroups, listMyArticles, getKasseSettings, setMyKasseSettings, setMyRegisterDeviceSettings, verteileRabatt, cancelScopeOf, receiptsScopeOf, } from './kasse/index.js';
package/dist/esm/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export { ReceiptType, VatRate, KeckPaymentMethod, CreditCardProvider, VoucherType, VoucherAction, StripeLinkMode, } from './enums/index.js';
2
- export { toReceiptItemPayload, fromReceiptItemPayload, receiptItemTotalCents, receiptItemIsValid, negateReceiptItem, toVoucherPayload, fromVoucherPayload, voucherIsValid, toReceiptPayload, fromReceiptPayload, receiptSubSumCents, receiptSumCents, CANCELLATION_REASONS, CANCELLATION_RESERVATION_MS, isCancellationReason, remainingQuantities, fromReceiptCompanyPayload, receiptCompanyTaxInfo, fromCashregisterPayload, fromReceiptSummaryPayload, ZERO_KINDS, istZeroKind, reportMonthFromDate, previousReportMonth, nextReportMonth, reportMonthKey, reportMonthReadable, toStripeUrlSessionPayload, fromStripeUrlSessionPayload, toHobexReceiptPayload, fromHobexReceiptPayload, hobexReceiptToCardPaymentData, hobexReceiptNeedsSignature, } from './models/index.js';
2
+ export { toReceiptItemPayload, fromReceiptItemPayload, receiptItemTotalCents, receiptItemIsValid, negateReceiptItem, isTipItem, toVoucherPayload, fromVoucherPayload, voucherIsValid, toReceiptPayload, fromReceiptPayload, receiptSubSumCents, receiptSumCents, CANCELLATION_REASONS, CANCELLATION_RESERVATION_MS, isCancellationReason, remainingQuantities, fromReceiptCompanyPayload, receiptCompanyTaxInfo, fromCashregisterPayload, fromReceiptSummaryPayload, ZERO_KINDS, istZeroKind, reportMonthFromDate, previousReportMonth, nextReportMonth, reportMonthKey, reportMonthReadable, toStripeUrlSessionPayload, fromStripeUrlSessionPayload, toHobexReceiptPayload, fromHobexReceiptPayload, hobexReceiptToCardPaymentData, hobexReceiptNeedsSignature, } from './models/index.js';
3
3
  export { apiKeyAuth, registerUserAuth, DEFAULT_BASE_URL, DEFAULT_TIMEOUT_MS, createTransport, createBinaryTransport, KasseneckApiError, KasseneckAuthError, KasseneckHttpError, KasseneckNetworkError, KasseneckValidationError, isKasseneckApiError, isKasseneckAuthError, isKasseneckHttpError, isKasseneckNetworkError, isKasseneckValidationError, sellReceipt, sellReceiptWithCompany, cancelReceipt, createCancelReceipt, zeroReceipt, getReceipt, getReceiptWithCompany, generateFullReceiptId, getFirstReceiptDate, listMyReceipts, listMyCashregisters, checkVoucherCombinationError, downloadDailyReport, downloadMonthlyReport, getCashboxStatus, getSignatureStatus, createKasseneckApi, } from './client/index.js';
4
4
  export { parseServerTimeStamp, toViennaWallClock } from './vienna-time.js';
5
5
  export { KASSE_BETRIEB_STANDARD, KASSE_GERAET_STANDARD, mergeKasseSettings, fromArticleGroupPayload, fromKasseArtikelPayload, listMyArticleGroups, listMyArticles, getKasseSettings, setMyKasseSettings, setMyRegisterDeviceSettings, verteileRabatt, cancelScopeOf, receiptsScopeOf, } from './kasse/index.js';
@@ -20,8 +20,15 @@ export type KasseBelegAusgabe = 'qr' | 'druck' | 'mail' | 'sms' | 'fragen';
20
20
  export type KasseLayout = 'rechts' | 'links' | 'vollbild';
21
21
  export type KasseKatpos = 'oben' | 'links';
22
22
  export type KasseHoehe = 'S' | 'M' | 'L';
23
- /** `sdp` = Netzwerk ueber Epson Server Direct Print (der Drucker holt Jobs vom Backend, jeder Browser druckt). */
24
- export type KasseDruckerArt = 'sdp' | 'netz' | 'bt' | 'usb';
23
+ /**
24
+ * `sdp` = Netzwerk ueber Epson Server Direct Print (der Drucker holt Jobs vom Backend, jeder Browser druckt).
25
+ * `connect` = Kasseneck Connect (lokaler Agent auf dem Kassen-Rechner druckt fuer die Browser-Kasse).
26
+ */
27
+ export type KasseDruckerArt = 'sdp' | 'netz' | 'bt' | 'usb' | 'connect';
28
+ /** Terminal-Ansprache: direkt per IP (wie bisher) oder ueber Kasseneck Connect (Agent leitet weiter). */
29
+ export type KasseTerminalVia = 'direkt' | 'connect';
30
+ /** Art des Kartenterminals an dieser Kasse: keines oder Hobex HPS (JSON-REST am Geraet). */
31
+ export type KasseTerminalArt = 'keins' | 'hps';
25
32
  export type KassePapier = 'mm58' | 'mm80';
26
33
  export type KasseZeichensatz = 'CP1252' | 'CP437';
27
34
  export type KasseSchnitt = 'partial' | 'full' | 'none';
@@ -55,6 +62,20 @@ export interface KasseSettingsBetrieb {
55
62
  notiz: boolean;
56
63
  suche: boolean;
57
64
  rabatt: 'aus' | 'an';
65
+ /** Bild-Logo (Download-URL aus dem Kasse-Upload); '' = Kuerzel verwenden. */
66
+ logoBild: string;
67
+ /** Seite des Wasserzeichens — ALT: abgeloest von wzPos, bleibt fuers Mischen alter Staende. */
68
+ wzSeite: 'links' | 'mitte' | 'rechts';
69
+ /** Horizontale Lage der Wasserzeichen-MITTE in Prozent (-25 bis 125 — darf ueber den Rand hinaus). */
70
+ wzPos: number;
71
+ /** Vertikale Lage der Wasserzeichen-Mitte in Prozent (-25 bis 125). */
72
+ wzPosV: number;
73
+ /** Deckkraft des Wasserzeichens in Prozent — bewusst Stufen, kein Schieberegler. */
74
+ wzStaerke: 3 | 6 | 10 | 16;
75
+ /** Groesse des Bild-Logos am BELEG (Ansicht, PDF, Kopfzeile). */
76
+ logoSkala: 'S' | 'M' | 'L' | 'XL';
77
+ /** Groesse des Wasserzeichens — getrennt vom Beleg-Logo. */
78
+ wzSkala: 'S' | 'M' | 'L' | 'XL';
58
79
  zahlBar: boolean;
59
80
  zahlKarte: boolean;
60
81
  kartenanbieter: KasseKartenanbieter;
@@ -67,11 +88,17 @@ export interface KasseSettingsBetrieb {
67
88
  kassierenModus: KasseKassierenModus;
68
89
  /** Trinkgeld-Chips in Prozent (eine Nachkommastelle, max 5, eindeutig, Reihenfolge des Chefs). */
69
90
  tgChips: number[];
91
+ /** Rabatt-Chips in Prozent — dieselben Regeln wie tgChips, vom Chef einstellbar. */
92
+ rabattChips: number[];
70
93
  belegAusgabe: KasseBelegAusgabe;
71
94
  fertigSekunden: 0 | 3 | 5 | 10 | 15 | 30 | 60;
95
+ /** Glas-Optik: Kacheln und Korb leicht durchscheinend (Wasserzeichen schimmert). */
96
+ glas: boolean;
97
+ /** Hilfetexte in den Chef-Einstellungen anzeigen. */
98
+ hinweise: boolean;
72
99
  }
73
100
  /** Aktionen der Kasse, die eine Taste bekommen koennen. */
74
- export type KasseTastenAktion = 'kassieren' | 'abschliessen' | 'abbrechen' | 'frei' | 'bar' | 'karte' | 'passend' | 'belege' | 'letzteZurueck';
101
+ export type KasseTastenAktion = 'kassieren' | 'abschliessen' | 'abbrechen' | 'frei' | 'bar' | 'karte' | 'passend' | 'belege' | 'letzteZurueck' | 'einstellungen' | 'abmelden' | 'trinkgeld' | 'vollbild' | 'gegebenLeeren' | 'korbLeeren';
75
102
  export declare const KASSE_TASTEN_AKTIONEN: readonly KasseTastenAktion[];
76
103
  /** Tastenkarte: Aktion -> Tasten (`Mod+F`, `Enter`, `Escape`, `F5` ...; `Mod` = ⌘ auf dem Mac, Strg sonst). */
77
104
  export type KasseTastenkarte = Record<KasseTastenAktion, string[]>;
@@ -93,6 +120,8 @@ export interface KasseSettingsGeraet {
93
120
  druckerId: string;
94
121
  /** ePOS Device-ID bei `druckerArt 'netz'` (Epson direkt per IP), Vorgabe `local_printer`. */
95
122
  druckerDevid: string;
123
+ /** Kennung des Druckers im lokalen Kasseneck-Connect-Agenten, bei `druckerArt 'connect'`. */
124
+ connectDruckerId: string;
96
125
  papier: KassePapier;
97
126
  zeichensatz: KasseZeichensatz;
98
127
  schnitt: KasseSchnitt;
@@ -100,6 +129,13 @@ export interface KasseSettingsGeraet {
100
129
  ladeAuto: KasseLadeAuto;
101
130
  terminalIp: string;
102
131
  terminalPort: number;
132
+ terminalVia: KasseTerminalVia;
133
+ /** Art des Terminals ('keins' = Kartenzahlung ohne Terminal-Anbindung gesperrt, sofern zahlKarte an). */
134
+ terminalArt: KasseTerminalArt;
135
+ /** Tastenmarken (die kleinen Kuerzel an den Knoepfen) anzeigen. */
136
+ tastenMarken: boolean;
137
+ /** Terminal-ID aus dem Hobex-Vertrag (ohne fuehrende Null); noetig fuer Diagnose und Zahlung. */
138
+ terminalTid: string;
103
139
  }
104
140
  export interface KasseSettings {
105
141
  betrieb: KasseSettingsBetrieb;
@@ -8,14 +8,30 @@
8
8
  * bewusst AUS, bis die Buchung geklaert ist (Spec kachel-kasse § 9).
9
9
  */
10
10
  export const KARTENANBIETER = ['keiner', 'extern', 'hobex', 'mypos', 'stripe'];
11
- export const KASSE_TASTEN_AKTIONEN = ['kassieren', 'abschliessen', 'abbrechen', 'frei', 'bar', 'karte', 'passend', 'belege', 'letzteZurueck'];
11
+ export const KASSE_TASTEN_AKTIONEN = ['kassieren', 'abschliessen', 'abbrechen', 'frei', 'bar', 'karte', 'passend', 'belege', 'letzteZurueck', 'einstellungen', 'abmelden', 'trinkgeld', 'vollbild', 'gegebenLeeren', 'korbLeeren'];
12
12
  export const KASSE_TASTEN_STANDARD = Object.freeze({
13
13
  kassieren: ['Enter'], abschliessen: ['Enter'], abbrechen: ['Escape'],
14
- frei: ['Mod+F'], bar: ['Mod+B'], karte: ['Mod+K'], passend: ['Mod+P'],
15
- belege: ['Mod+E'], letzteZurueck: ['Mod+Backspace'],
14
+ // Mod+F gehoert dem Vollbild; Betrag frei liegt auf D. Bar bleibt auf B,
15
+ // Belege auf E (Entscheidung vom 21.8., der Tausch von 0.6.25 ist zurueck).
16
+ frei: ['Mod+D'], bar: ['Mod+B'], karte: ['Mod+K'], passend: ['Mod+P'],
17
+ // NICHT Mod+E: das faengt Chrome auf dem Mac selbst ab („Auswahl fuer
18
+ // Suche verwenden") — am Geraet belegt. J wie Journal laesst er durch.
19
+ belege: ['Mod+J'], letzteZurueck: ['Mod+Backspace'],
20
+ // NICHT Mod+T: die Taste ist im Browser reserviert (neuer Tab) und kommt
21
+ // nie bei der Seite an. Mod+G laesst Chrome durch.
22
+ einstellungen: ['Mod+S'], abmelden: ['Mod+L'], trinkgeld: ['Mod+G'],
23
+ vollbild: ['Mod+F'],
24
+ // Mod+C = Kopieren des Browsers — im Kassieren-Schritt ist Kopieren fern,
25
+ // und die Bindung gilt nur dort (Bar + Rueckgeld-Rechner an).
26
+ gegebenLeeren: ['Mod+C'],
27
+ // Bewusst DIESELBE Taste wie gegebenLeeren: die beiden leben in
28
+ // verschiedenen Momenten (Korb vor dem Kassieren, Gegeben-Feld darin) —
29
+ // der Verteiler laesst die nicht zustaendige Aktion durchfallen.
30
+ korbLeeren: ['Mod+C'],
16
31
  });
17
32
  export const KASSE_BETRIEB_STANDARD = Object.freeze({
18
- logoText: 'K', logoAn: true, logoGroesse: 'M', wasserzeichen: 'anmeldung', farbe: '#1B46F5',
33
+ // Kasseneck-Teal aus dem Branding-Guide nicht mehr das alte Blau.
34
+ logoText: 'K', logoAn: true, logoGroesse: 'M', wasserzeichen: 'anmeldung', farbe: '#0F6B72',
19
35
  stil: 'klar', schrift: 'M', schriftEinst: 'S', kachelstil: 'streifen', uhr: true,
20
36
  sperrbild: true, foto: true, autoAbMin: 0, abNachVerkauf: false, schnellLogin: true,
21
37
  preisAnzeigen: true, ustAnzeigen: false, emoji: true, katFarben: true, freiErlaubt: true,
@@ -24,15 +40,21 @@ export const KASSE_BETRIEB_STANDARD = Object.freeze({
24
40
  zahlBar: true, zahlKarte: false, kartenanbieter: 'keiner', trinkgeld: false, tgModus: 'beides',
25
41
  tgStufen: { 5: true, 10: true, 15: false, 20: false }, tgChips: [5, 10], tgSplit: true, rueckgeld: true,
26
42
  schnellbar: false, kassierenModus: 'seite',
27
- belegAusgabe: 'qr', fertigSekunden: 0,
43
+ // 'fragen' = Fertig-Seite bietet QR und Bon an — sicherster Standard.
44
+ logoBild: '', wzSeite: 'mitte', wzPos: 50, wzPosV: 50, wzStaerke: 6, logoSkala: 'M', wzSkala: 'M',
45
+ glas: true, hinweise: true,
46
+ rabattChips: [5, 10, 15, 20],
47
+ belegAusgabe: 'fragen', fertigSekunden: 0,
28
48
  });
29
49
  export const KASSE_GERAET_STANDARD = Object.freeze({
30
50
  layout: 'rechts', katpos: 'oben', spaltenExtra: 0, hoehe: 'M', touch: false,
31
51
  tasten: { ...KASSE_TASTEN_STANDARD },
32
52
  druckerAn: false, druckerArt: 'sdp', druckerIp: '', druckerPort: 9100, druckerBt: '', druckerId: '', druckerDevid: 'local_printer',
53
+ connectDruckerId: '',
33
54
  papier: 'mm80', zeichensatz: 'CP1252', schnitt: 'partial',
34
55
  ladeAn: false, ladeAuto: 'bar',
35
- terminalIp: '', terminalPort: 20008,
56
+ terminalIp: '', terminalPort: 8080, terminalVia: 'direkt',
57
+ terminalArt: 'keins', terminalTid: '', tastenMarken: true,
36
58
  });
37
59
  /**
38
60
  * Standard + gespeichert. Landkarten (`saetze`, `tgStufen`) werden je Schluessel
@@ -1,4 +1,4 @@
1
- export { type ReceiptItem, type ReceiptItemPayload, type ReceiptItemPayloadRead, toReceiptItemPayload, fromReceiptItemPayload, receiptItemTotalCents, receiptItemIsValid, negateReceiptItem, } from './receipt-item.js';
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
3
  export { type Receipt, type ReceiptPayload, type ReceiptPayloadRead, toReceiptPayload, fromReceiptPayload, receiptSubSumCents, receiptSumCents, } from './receipt.js';
4
4
  export { type CancellationOf, type CancellationItem, type Cancellation, type CancellationReason, CANCELLATION_REASONS, CANCELLATION_RESERVATION_MS, isCancellationReason, remainingQuantities, } from './cancellation.js';
@@ -1,4 +1,4 @@
1
- export { toReceiptItemPayload, fromReceiptItemPayload, receiptItemTotalCents, receiptItemIsValid, negateReceiptItem, } from './receipt-item.js';
1
+ export { toReceiptItemPayload, fromReceiptItemPayload, receiptItemTotalCents, receiptItemIsValid, negateReceiptItem, isTipItem, isDiscountItem, } from './receipt-item.js';
2
2
  export { toVoucherPayload, fromVoucherPayload, voucherIsValid } from './voucher.js';
3
3
  export { toReceiptPayload, fromReceiptPayload, receiptSubSumCents, receiptSumCents, } from './receipt.js';
4
4
  export { CANCELLATION_REASONS, CANCELLATION_RESERVATION_MS, isCancellationReason, remainingQuantities, } from './cancellation.js';