@kreiseck/kasseneck-api 0.6.8 → 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.
- package/README.md +4 -0
- package/dist/cjs/client/index.d.ts +1 -1
- package/dist/cjs/client/receipts.d.ts +19 -0
- package/dist/cjs/client/receipts.js +51 -0
- package/dist/cjs/index.d.ts +2 -2
- package/dist/cjs/index.js +3 -2
- package/dist/cjs/kasse/settings.d.ts +39 -3
- package/dist/cjs/kasse/settings.js +28 -6
- package/dist/cjs/models/index.d.ts +1 -1
- package/dist/cjs/models/index.js +3 -1
- package/dist/cjs/models/receipt-item.d.ts +41 -0
- package/dist/cjs/models/receipt-item.js +41 -1
- package/dist/cjs/models/receipt.d.ts +7 -0
- package/dist/cjs/models/receipt.js +2 -0
- package/dist/cjs/printing/escpos.js +7 -0
- package/dist/cjs/react/index.js +11 -1
- package/dist/cjs/receipt/discount.js +1 -1
- package/dist/cjs/receipt/layout-escpos.js +9 -4
- package/dist/cjs/receipt/layout.d.ts +6 -3
- package/dist/cjs/receipt/layout.js +166 -14
- package/dist/cjs/register/pairing.d.ts +15 -0
- package/dist/cjs/register/pairing.js +22 -3
- package/dist/esm/client/index.d.ts +1 -1
- package/dist/esm/client/receipts.d.ts +19 -0
- package/dist/esm/client/receipts.js +51 -0
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.js +1 -1
- package/dist/esm/kasse/settings.d.ts +39 -3
- package/dist/esm/kasse/settings.js +28 -6
- package/dist/esm/models/index.d.ts +1 -1
- package/dist/esm/models/index.js +1 -1
- package/dist/esm/models/receipt-item.d.ts +41 -0
- package/dist/esm/models/receipt-item.js +39 -1
- package/dist/esm/models/receipt.d.ts +7 -0
- package/dist/esm/models/receipt.js +2 -0
- package/dist/esm/printing/escpos.js +7 -0
- package/dist/esm/react/index.js +11 -1
- package/dist/esm/receipt/discount.js +1 -1
- package/dist/esm/receipt/layout-escpos.js +9 -4
- package/dist/esm/receipt/layout.d.ts +6 -3
- package/dist/esm/receipt/layout.js +167 -15
- package/dist/esm/register/pairing.d.ts +15 -0
- package/dist/esm/register/pairing.js +22 -3
- package/fixtures/belege/rabatt-chef-trinkgeld.json +89 -0
- package/fixtures/belege/rabatt-einfach.json +50 -0
- package/fixtures/belege/rabatt-trinkgeld.json +75 -0
- package/fixtures/belege/rabatt-wertgutschein.json +70 -0
- package/fixtures/belege/rabattzeilen.json +5 -3
- package/fixtures/belege/storno-rabatt.json +68 -0
- package/fixtures/erwartet/rabatt-chef-trinkgeld.grid32.txt +31 -0
- package/fixtures/erwartet/rabatt-chef-trinkgeld.grid48.txt +30 -0
- package/fixtures/erwartet/rabatt-chef-trinkgeld.lines.json +317 -0
- package/fixtures/erwartet/rabatt-einfach.grid32.txt +26 -0
- package/fixtures/erwartet/rabatt-einfach.grid48.txt +25 -0
- package/fixtures/erwartet/rabatt-einfach.lines.json +250 -0
- package/fixtures/erwartet/rabatt-trinkgeld.grid32.txt +31 -0
- package/fixtures/erwartet/rabatt-trinkgeld.grid48.txt +30 -0
- package/fixtures/erwartet/rabatt-trinkgeld.lines.json +336 -0
- package/fixtures/erwartet/rabatt-wertgutschein.grid32.txt +32 -0
- package/fixtures/erwartet/rabatt-wertgutschein.grid48.txt +31 -0
- package/fixtures/erwartet/rabatt-wertgutschein.lines.json +330 -0
- package/fixtures/erwartet/rabattzeilen.grid32.txt +4 -2
- package/fixtures/erwartet/rabattzeilen.grid48.txt +4 -2
- package/fixtures/erwartet/rabattzeilen.lines.json +18 -8
- package/fixtures/erwartet/storno-rabatt.grid32.txt +34 -0
- package/fixtures/erwartet/storno-rabatt.grid48.txt +32 -0
- package/fixtures/erwartet/storno-rabatt.lines.json +317 -0
- package/fixtures/kasse-settings-standard.json +145 -0
- package/fixtures/manifest.json +34 -4
- package/package.json +3 -2
|
@@ -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
|
-
|
|
158
|
-
|
|
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
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
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
|
-
|
|
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 {
|
|
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') {
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -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
|
-
/**
|
|
24
|
-
|
|
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
|
-
|
|
15
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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';
|
package/dist/esm/models/index.js
CHANGED
|
@@ -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';
|