@kreiseck/kasseneck-api 0.8.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/dist/cjs/kasse/texte.d.ts +39 -0
  3. package/dist/cjs/kasse/texte.js +20 -0
  4. package/dist/cjs/receipt/layout.d.ts +11 -5
  5. package/dist/cjs/receipt/layout.js +248 -26
  6. package/dist/esm/kasse/texte.d.ts +39 -0
  7. package/dist/esm/kasse/texte.js +20 -0
  8. package/dist/esm/receipt/layout.d.ts +11 -5
  9. package/dist/esm/receipt/layout.js +247 -26
  10. package/fixtures/belege/karte-eigener.json +44 -0
  11. package/fixtures/belege/karte-gptom-ios.json +59 -0
  12. package/fixtures/belege/karte-gptom.json +59 -0
  13. package/fixtures/belege/karte-hobex-cloud.json +54 -0
  14. package/fixtures/belege/karte-hobex-hps.json +54 -0
  15. package/fixtures/belege/karte-mypos.json +55 -0
  16. package/fixtures/belege/karte-stripe-eps.json +52 -0
  17. package/fixtures/belege/karte-stripe.json +57 -0
  18. package/fixtures/belege/karte-sumup.json +53 -0
  19. package/fixtures/erwartet/karte-eigener.grid32.txt +22 -0
  20. package/fixtures/erwartet/karte-eigener.grid48.txt +22 -0
  21. package/fixtures/erwartet/karte-eigener.lines.json +216 -0
  22. package/fixtures/erwartet/karte-gptom-ios.grid32.txt +34 -0
  23. package/fixtures/erwartet/karte-gptom-ios.grid48.txt +34 -0
  24. package/fixtures/erwartet/karte-gptom-ios.lines.json +286 -0
  25. package/fixtures/erwartet/karte-gptom.grid32.txt +34 -0
  26. package/fixtures/erwartet/karte-gptom.grid48.txt +34 -0
  27. package/fixtures/erwartet/karte-gptom.lines.json +286 -0
  28. package/fixtures/erwartet/karte-hobex-cloud.grid32.txt +29 -0
  29. package/fixtures/erwartet/karte-hobex-cloud.grid48.txt +29 -0
  30. package/fixtures/erwartet/karte-hobex-cloud.lines.json +256 -0
  31. package/fixtures/erwartet/karte-hobex-hps.grid32.txt +33 -0
  32. package/fixtures/erwartet/karte-hobex-hps.grid48.txt +33 -0
  33. package/fixtures/erwartet/karte-hobex-hps.lines.json +272 -0
  34. package/fixtures/erwartet/karte-mypos.grid32.txt +38 -0
  35. package/fixtures/erwartet/karte-mypos.grid48.txt +36 -0
  36. package/fixtures/erwartet/karte-mypos.lines.json +353 -0
  37. package/fixtures/erwartet/karte-stripe-eps.grid32.txt +28 -0
  38. package/fixtures/erwartet/karte-stripe-eps.grid48.txt +28 -0
  39. package/fixtures/erwartet/karte-stripe-eps.lines.json +250 -0
  40. package/fixtures/erwartet/karte-stripe.grid32.txt +31 -0
  41. package/fixtures/erwartet/karte-stripe.grid48.txt +31 -0
  42. package/fixtures/erwartet/karte-stripe.lines.json +268 -0
  43. package/fixtures/erwartet/karte-sumup.grid32.txt +32 -0
  44. package/fixtures/erwartet/karte-sumup.grid48.txt +30 -0
  45. package/fixtures/erwartet/karte-sumup.lines.json +316 -0
  46. package/fixtures/hobex-hps-codes.json +1 -1
  47. package/fixtures/kasse-texte.json +60 -1
  48. package/fixtures/manifest.json +54 -0
  49. package/fixtures/oberflaeche.json +1 -1
  50. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,89 @@ Was vor 0.7.0 geschah, steht in der Commit-Historie (`git log`); ab hier wird
4
4
  es hier geführt. Ein Eintrag nennt die Änderung **und ihren Grund** —
5
5
  nur der Grund überlebt den nächsten Umbau.
6
6
 
7
+ ## 0.9.1
8
+
9
+ ### Elf Saetze fuer die vier Ausgaenge einer Kartenzahlung und das Terminal-Protokoll
10
+
11
+ **Anlass:** Die Kassen-App bekommt GP Tom mit denselben vier Ausgaengen, die
12
+ die Browser-Kasse schon kennt — geglueckt, abgebrochen, gescheitert, unklar —
13
+ und dazu ein Terminal-Protokoll. Beide Kassen duerfen nur Katalogsaetze zeigen;
14
+ ohne diese Schluessel haette die App eigene Formulierungen erfunden, und der
15
+ Kassier haette am Tresen zwei verschiedene Woerter fuer dieselbe Lage gelesen.
16
+
17
+ **Grund fuer die Auswahl:** Der teuerste Fehler an der Kasse ist die zweite
18
+ Belastung derselben Karte. Deshalb tragen die neuen Saetze zu den unsicheren
19
+ Ausgaengen alles, was der Kassier braucht, um NICHT zu wiederholen: Betrag,
20
+ Kennung und den Hinweis auf den Terminal-Beleg.
21
+
22
+ - **GP Tom** (nur App, wie die uebrigen `gptom.`-Saetze): `gptom.abgelehnt` und
23
+ `gptom.abgelehnt_mit_code` (das Terminal hat entschieden — sicher nichts
24
+ gebucht), `gptom.nicht_geoeffnet` (die App liess sich nicht starten),
25
+ `gptom.zeit_abgelaufen` / `gptom.zeit_abgelaufen_mit_kennung` (Frist abgelaufen
26
+ ist **kein** Abbruch: die Karte kann belastet sein).
27
+ - **Karte gebucht, Beleg fehlt:** `kartenzahlung.karte_gebucht_beleg_offen` und
28
+ `kartenzahlung.karte_gebucht_korb_geaendert`, beide mit `betrag` und `kennung`.
29
+ Der zweite nennt die Entscheidung, die er verlangt: Beleg erstellen oder am
30
+ Terminal stornieren und hier verwerfen.
31
+ - **Warteschirm:** `kartenzahlung.wartet_auf_terminal`.
32
+ - **Terminal-Protokoll:** `protokoll.leer`, `protokoll.kopiert` — ohne `nur`,
33
+ weil an dem Satz nichts plattformgebunden ist.
34
+
35
+ **Neue abgeleitete Pruefungen** statt einer gepflegten Liste: kein Satz steht
36
+ zweimal im Katalog; jeder `gptom.`-Satz gilt nur in der App; jede Fassung
37
+ `…_mit_kennung` ist ihre Grundfassung plus dem Anker und teilt deren `nur`;
38
+ jeder unklare Ausgang warnt vor dem zweiten Kassieren.
39
+
40
+ `FEHLERREGELN` bleibt unveraendert — die neuen Saetze sind Ausgaenge einer
41
+ Kartenzahlung, keine neue Art, einen Transportfehler einzuordnen.
42
+
43
+ ## 0.9.0
44
+
45
+ ### Alle Kartenzahlungsbloecke im Belegmodell — nicht nur Hobex
46
+
47
+ **Anlass:** Am Bon aus der Flutter-App standen bei einer Stripe-Zahlung Marke,
48
+ letzte vier Ziffern, 3-D Secure, Betrag, Zahlzeitpunkt und Referenz. Dasselbe
49
+ Blatt als PDF geoeffnet sagte nur „Zahlungsart: Kartenzahlung". Betroffen waren
50
+ vier der sieben Anbieter: GP Tom, SumUp, myPOS und Stripe.
51
+
52
+ **Ursache:** Bis 0.8.0 trug dieses Modell nur den Hobex-Block, begruendet damit,
53
+ die uebrigen Anbieter druckten „in ihren eigenen Apps". Das Argument gilt fuer
54
+ die Frage, wer das TERMINAL bedient — nicht dafuer, was auf dem BELEGDOKUMENT
55
+ steht. Aus der einen Regel wurde beim Bau des Zeilenmodells die andere. Im
56
+ Backend hatte die vorherige PDF-Erzeugung (`generateReceiptPdf`) alle sieben
57
+ Anbieter; mit der Umstellung auf dieses Modell fielen vier still weg.
58
+
59
+ - Neu: `gpTomBlock`, `sumupBlock`, `myposBlock`, `stripeBlock` — Zeile fuer
60
+ Zeile portiert aus `print_paper.dart` des Dart-Zwillings, inklusive der
61
+ Eigenheiten: GP Tom schreibt `transactionType` im Plugin-`toMap` mit
62
+ Tippfehler (`transacitonType`), myPOS liefert den Zeitpunkt als
63
+ `YYMMDDhhmmss` ohne Trenner, Stripe kennt neben Karte auch EPS.
64
+ - `stripeReceiptLines` wird exportiert, wie im Dart-Paket: Druck und Anzeige
65
+ sollen dieselbe Quelle haben, nicht zwei.
66
+ - Ein myPOS-Zeitpunkt, der nicht wie `YYMMDDhhmmss` aussieht, wird
67
+ **unveraendert** durchgereicht statt zerschnitten — eine halb geratene
68
+ Uhrzeit auf einem Beleg ist schlimmer als ein roher Wert.
69
+ - Ein unbekannter Anbieter ergibt weiterhin keinen Block, still und ohne
70
+ Fehler: ein Beleg muss sich immer zeigen lassen.
71
+
72
+ ### Neun Golden-Belege fuer Kartenzahlungen
73
+
74
+ **Anlass:** Von den 22 Golden-Belegen trug **keiner** eine Kartenzahlung.
75
+ Deshalb konnte der Verlust der vier Bloecke passieren, ohne dass irgendetwas
76
+ rot wurde.
77
+
78
+ - Neu: `karte-hobex-hps`, `karte-hobex-cloud`, `karte-gptom`, `karte-gptom-ios`,
79
+ `karte-sumup`, `karte-mypos`, `karte-stripe`, `karte-stripe-eps`,
80
+ `karte-eigener`. Die Paare decken beide Zweige ab, wo es welche gibt
81
+ (Unterschriftsfeld ja/nein, Karte gegen EPS, Tippfehler-Schluessel).
82
+ - Neu: eine Pruefung, die die Anbieterliste **aus dem Enum** nimmt statt aus
83
+ einer Handliste — wer einen Anbieter aufnimmt, wird rot, bis ein Golden-Beleg
84
+ dazuliegt. Sie fand beim ersten Lauf sofort zwei weitere Luecken
85
+ (`gpTomIos`, `hobexCloudApi`).
86
+ - Neu: eine zweite Pruefung, dass ein Beleg MIT Terminaldaten auch wirklich
87
+ einen Block traegt. Ein Golden-Beleg allein beweist nur, dass die Datei da
88
+ ist.
89
+
7
90
  ## 0.7.2
8
91
 
9
92
  ### `55` („PIN falsch") ist eine gemessene Host-Ablehnung → `declined`
@@ -124,6 +124,17 @@ declare const MELDUNGEN_ROH: {
124
124
  readonly text: "Unklar, ob die Kartenzahlung durchgegangen ist — die Verbindung zum Terminal riss ab. Bitte am Terminal-Beleg nachsehen, BEVOR neu kassiert wird: die Karte kann belastet sein. Kennung der Zahlung: {kennung}.";
125
125
  readonly platzhalter: readonly ["kennung"];
126
126
  };
127
+ readonly 'kartenzahlung.wartet_auf_terminal': {
128
+ readonly text: "Bitte am Terminal fortfahren …";
129
+ };
130
+ readonly 'kartenzahlung.karte_gebucht_beleg_offen': {
131
+ readonly text: "Die Karte ist bereits mit {betrag} belastet (Kennung {kennung}) — der Beleg dazu fehlt noch. Bitte jetzt den Beleg erstellen und nicht erneut kassieren.";
132
+ readonly platzhalter: readonly ["betrag", "kennung"];
133
+ };
134
+ readonly 'kartenzahlung.karte_gebucht_korb_geaendert': {
135
+ readonly text: "Es gibt eine gebuchte Kartenzahlung über {betrag} (Kennung {kennung}), aber der Korb hat sich seither geändert. Bitte zuerst entscheiden: den Beleg zur gebuchten Zahlung erstellen oder die Zahlung am Terminal stornieren und hier verwerfen.";
136
+ readonly platzhalter: readonly ["betrag", "kennung"];
137
+ };
127
138
  readonly 'kartenzahlung.connect_nicht_verbunden': {
128
139
  readonly text: "Kartenzahlung nicht möglich: Kasseneck Connect ist nicht verbunden — Einstellungen → Kasseneck Connect.";
129
140
  readonly nur: readonly ["web"];
@@ -161,6 +172,34 @@ declare const MELDUNGEN_ROH: {
161
172
  readonly platzhalter: readonly ["code"];
162
173
  readonly nur: readonly ["app"];
163
174
  };
175
+ readonly 'gptom.abgelehnt': {
176
+ readonly text: "Das Terminal hat die Zahlung abgelehnt.";
177
+ readonly nur: readonly ["app"];
178
+ };
179
+ readonly 'gptom.abgelehnt_mit_code': {
180
+ readonly text: "Das Terminal hat die Zahlung abgelehnt ({code}).";
181
+ readonly platzhalter: readonly ["code"];
182
+ readonly nur: readonly ["app"];
183
+ };
184
+ readonly 'gptom.nicht_geoeffnet': {
185
+ readonly text: "GP Tom ließ sich nicht öffnen — bitte die GP-Tom-App prüfen und erneut versuchen.";
186
+ readonly nur: readonly ["app"];
187
+ };
188
+ readonly 'gptom.zeit_abgelaufen': {
189
+ readonly text: "Das Terminal hat in der Frist nicht geantwortet — die Karte kann belastet sein. Bitte am Terminal-Beleg nachsehen, BEVOR neu kassiert wird.";
190
+ readonly nur: readonly ["app"];
191
+ };
192
+ readonly 'gptom.zeit_abgelaufen_mit_kennung': {
193
+ readonly text: "Das Terminal hat in der Frist nicht geantwortet — die Karte kann belastet sein. Bitte am Terminal-Beleg nachsehen, BEVOR neu kassiert wird. Kennung der Zahlung: {kennung}.";
194
+ readonly platzhalter: readonly ["kennung"];
195
+ readonly nur: readonly ["app"];
196
+ };
197
+ readonly 'protokoll.leer': {
198
+ readonly text: "Noch keine Einträge — das Protokoll füllt sich mit der ersten Kartenzahlung.";
199
+ };
200
+ readonly 'protokoll.kopiert': {
201
+ readonly text: "Das Protokoll liegt in der Zwischenablage.";
202
+ };
164
203
  readonly 'belege.laden_fehlgeschlagen': {
165
204
  readonly text: "Die Belege konnten nicht geladen werden.";
166
205
  };
@@ -59,6 +59,13 @@ const MELDUNGEN_ROH = {
59
59
  'kartenzahlung.unklar': { text: 'Unklar, ob die Kartenzahlung durchgegangen ist — die Verbindung zum Terminal riss ab. Bitte am Terminal-Beleg nachsehen, BEVOR neu kassiert wird: die Karte kann belastet sein.' },
60
60
  // Die Kennung ist der einzige Anker, um die Zahlung am Terminal-Beleg wiederzufinden — liegt sie vor, gilt dieser Satz statt kartenzahlung.unklar.
61
61
  'kartenzahlung.unklar_mit_kennung': { text: 'Unklar, ob die Kartenzahlung durchgegangen ist — die Verbindung zum Terminal riss ab. Bitte am Terminal-Beleg nachsehen, BEVOR neu kassiert wird: die Karte kann belastet sein. Kennung der Zahlung: {kennung}.', platzhalter: ['kennung'] },
62
+ 'kartenzahlung.wartet_auf_terminal': { text: 'Bitte am Terminal fortfahren …' },
63
+ // Ein geglücktes Kartenergebnis ohne Beleg ist der teuerste Zustand am Tresen:
64
+ // wer hier erneut kassiert, belastet die Karte ein zweites Mal. Beide Sätze
65
+ // nennen deshalb Betrag UND Kennung — nur damit findet der Kassier die Zahlung
66
+ // am Terminal-Beleg wieder — und keiner rät zum Wiederholen.
67
+ 'kartenzahlung.karte_gebucht_beleg_offen': { text: 'Die Karte ist bereits mit {betrag} belastet (Kennung {kennung}) — der Beleg dazu fehlt noch. Bitte jetzt den Beleg erstellen und nicht erneut kassieren.', platzhalter: ['betrag', 'kennung'] },
68
+ 'kartenzahlung.karte_gebucht_korb_geaendert': { text: 'Es gibt eine gebuchte Kartenzahlung über {betrag} (Kennung {kennung}), aber der Korb hat sich seither geändert. Bitte zuerst entscheiden: den Beleg zur gebuchten Zahlung erstellen oder die Zahlung am Terminal stornieren und hier verwerfen.', platzhalter: ['betrag', 'kennung'] },
62
69
  'kartenzahlung.connect_nicht_verbunden': { text: 'Kartenzahlung nicht möglich: Kasseneck Connect ist nicht verbunden — Einstellungen → Kasseneck Connect.', nur: ['web'] },
63
70
  'terminal.keines_gefunden': { text: 'Kein Hobex-Terminal gefunden — ist es eingeschaltet und im selben Netz wie dieser Rechner?', nur: ['web'] },
64
71
  'terminal.nicht_bereit': { text: 'Terminal antwortet, ist aber nicht betriebsbereit: {antwort} — TID prüfen.', platzhalter: ['antwort'], nur: ['web'] },
@@ -67,6 +74,19 @@ const MELDUNGEN_ROH = {
67
74
  'gptom.zahlung_fehlgeschlagen': { text: 'GP Tom: Zahlung fehlgeschlagen: {grund}', platzhalter: ['grund'], nur: ['app'] },
68
75
  'gptom.terminal_antwortet_nicht': { text: 'Das Terminal hat nicht geantwortet: {grund}', platzhalter: ['grund'], nur: ['app'] },
69
76
  'gptom.zahlung_nicht_abgeschlossen': { text: 'Die Zahlung wurde nicht abgeschlossen ({code}).', platzhalter: ['code'], nur: ['app'] },
77
+ // Abgelehnt heißt: das Terminal hat entschieden, es ist sicher nichts gebucht.
78
+ // Der Code ist die einzige Handhabe, mit der der Inhaber bei GP nachfragen kann —
79
+ // liegt einer vor, gilt die Fassung mit Code.
80
+ 'gptom.abgelehnt': { text: 'Das Terminal hat die Zahlung abgelehnt.', nur: ['app'] },
81
+ 'gptom.abgelehnt_mit_code': { text: 'Das Terminal hat die Zahlung abgelehnt ({code}).', platzhalter: ['code'], nur: ['app'] },
82
+ 'gptom.nicht_geoeffnet': { text: 'GP Tom ließ sich nicht öffnen — bitte die GP-Tom-App prüfen und erneut versuchen.', nur: ['app'] },
83
+ // Frist abgelaufen ist kein Abbruch: die Karte kann belastet sein, nur die
84
+ // Antwort blieb aus. Deshalb derselbe Ton wie kartenzahlung.unklar.
85
+ 'gptom.zeit_abgelaufen': { text: 'Das Terminal hat in der Frist nicht geantwortet — die Karte kann belastet sein. Bitte am Terminal-Beleg nachsehen, BEVOR neu kassiert wird.', nur: ['app'] },
86
+ 'gptom.zeit_abgelaufen_mit_kennung': { text: 'Das Terminal hat in der Frist nicht geantwortet — die Karte kann belastet sein. Bitte am Terminal-Beleg nachsehen, BEVOR neu kassiert wird. Kennung der Zahlung: {kennung}.', platzhalter: ['kennung'], nur: ['app'] },
87
+ // --- Terminal-Protokoll --------------------------------------------------
88
+ 'protokoll.leer': { text: 'Noch keine Einträge — das Protokoll füllt sich mit der ersten Kartenzahlung.' },
89
+ 'protokoll.kopiert': { text: 'Das Protokoll liegt in der Zwischenablage.' },
70
90
  // --- Belege und Storno ---------------------------------------------------
71
91
  'belege.laden_fehlgeschlagen': { text: 'Die Belege konnten nicht geladen werden.' },
72
92
  'belege.keine_im_zeitraum': { text: 'Keine Belege in diesem Zeitraum.' },
@@ -22,11 +22,15 @@ import type { PosPaperSize } from '../printing/escpos.js';
22
22
  *
23
23
  * Bewusst **nicht** enthalten: Firmenlogo und Rasterbilder (brauchen
24
24
  * Bildverarbeitung, die im Browser anders aussieht als in Node), PDF-Erzeugung
25
- * und Druckeransteuerung. Von den anbieterspezifischen Kartenzahlungsbloecken
26
- * des Vorbilds (GP Tom, SumUp, myPOS, Stripe) ist nur der **Hobex-Block**
27
- * enthalten: die Browser-Kasse bedient das HPS-Terminal selbst (via Kasseneck
28
- * Connect) und druckt den Bon aus genau diesem Modell — die uebrigen Anbieter
29
- * drucken in ihren eigenen Apps.
25
+ * und Druckeransteuerung.
26
+ *
27
+ * Die anbieterspezifischen Kartenzahlungsbloecke sind dagegen **alle**
28
+ * enthalten (Hobex, GP Tom, SumUp, myPOS, Stripe). Bis 0.8.0 trug das Modell
29
+ * nur den Hobex-Block, weil die Browser-Kasse nur das HPS-Terminal selbst
30
+ * bedient und die uebrigen Anbieter „in ihren eigenen Apps drucken". Das
31
+ * Argument gilt fuer die Frage, wer das TERMINAL bedient — nicht dafuer, was
32
+ * auf dem BELEGDOKUMENT steht. Aus der einen Regel wurde die andere, und
33
+ * derselbe Beleg trug im PDF weniger als auf dem Bon.
30
34
  */
31
35
  /** Ausrichtung einer Zeile bzw. einer Spalte. */
32
36
  export type LayoutAlign = 'left' | 'center' | 'right';
@@ -129,6 +133,8 @@ export interface BuildReceiptLayoutOptions {
129
133
  * diesem Paket ausnahmslos ganzzahlig gerechnet).
130
134
  */
131
135
  export declare function formatCents(cents: number): string;
136
+ /** Zwilling von `stripeReceiptLines` (print_paper.dart). */
137
+ export declare function stripeReceiptLines(daten: Record<string, unknown>, cardPaymentId: string | null): string[];
132
138
  /**
133
139
  * Hinweis auf die Steuerbefreiung, den ein Kleinunternehmerbeleg tragen muss —
134
140
  * ohne ihn stuende in der USt-Tabelle „D 0 %" ohne jede Begruendung, und ab
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SMALL_BUSINESS_NOTICE = exports.AKTUELLES_REGELWERK = void 0;
4
4
  exports.formatCents = formatCents;
5
+ exports.stripeReceiptLines = stripeReceiptLines;
5
6
  exports.receiptSignatureFailed = receiptSignatureFailed;
6
7
  exports.receiptSignatureIsTest = receiptSignatureIsTest;
7
8
  exports.receiptIsZero = receiptIsZero;
@@ -71,6 +72,36 @@ function originalDatum(timeStamp) {
71
72
  * `typeof null === 'object'` ist, ergab das beim Lesen von `.label` einen
72
73
  * nackten TypeError.
73
74
  */
75
+ /**
76
+ * Die anbieterspezifischen Kartenzahlungsbloecke.
77
+ *
78
+ * WARUM DIESE BLOECKE HIER STEHEN
79
+ *
80
+ * Bis 0.8.0 trug dieses Modell nur den Hobex-Block, mit der Begruendung, die
81
+ * uebrigen Anbieter druckten "in ihren eigenen Apps". Das Argument gilt fuer
82
+ * die Frage, wer das TERMINAL bedient -- nicht dafuer, was auf dem
83
+ * BELEGDOKUMENT steht. Aus der einen Regel wurde die andere, und damit
84
+ * verschwanden vier Bloecke: Wer denselben Beleg als PDF oeffnete, sah "nur
85
+ * Kartenzahlung", waehrend der Bon daneben Marke, letzte Ziffern, Betrag und
86
+ * Referenz auswies. Vorher (backend `generateReceiptPdf`) hatte das PDF alle
87
+ * sieben Anbieter.
88
+ *
89
+ * Gebaut werden die Zeilen deshalb hier, an genau EINER Stelle. Wer sie
90
+ * anderswo noch einmal baut, oeffnet die Drift wieder.
91
+ *
92
+ * Vorbild ist `print_paper.dart` im Dart-Paket (`_gpTom`, `_hobexHps`,
93
+ * `_sumup`, `_mypos`, `_stripe`); die Fixtures unter `fixtures/belege/karte-*`
94
+ * halten je Anbieter eine Zeile-fuer-Zeile-Probe fest.
95
+ */
96
+ /** Ein Feld aus `cardPaymentData` als Text; fehlend/null wird leer. */
97
+ function kartenFeld(daten, schluessel) {
98
+ const v = daten[schluessel];
99
+ return v == null ? '' : String(v);
100
+ }
101
+ /** Nicht-leere Teile mit Mittelpunkt verbinden. */
102
+ function verbunden(teile) {
103
+ return teile.filter(Boolean).join(' · ');
104
+ }
74
105
  /**
75
106
  * Kartenzahlungsblock fuer Hobex (HPS am Geraet wie Cloud-API): die Felder
76
107
  * kommen aus `cardPaymentData` in der Form von [hobexReceiptToCardPaymentData]
@@ -78,43 +109,234 @@ function originalDatum(timeStamp) {
78
109
  * Antwortcode. Bei CVM "1" verlangt die Karte eine Unterschrift; dafuer
79
110
  * bekommt der Bon eine Linie. Der PAN haengt beim HPS das Ablaufdatum mit
80
111
  * Unterstrich an ("…4720_2810") — gedruckt wird nur die maskierte Nummer.
81
- *
82
- * Andere Anbieter (GP Tom, SumUp, myPOS, Stripe) drucken in ihren eigenen
83
- * Apps; ihre Bloecke bleiben bewusst draussen (siehe Kopfkommentar).
84
112
  */
85
- function hobexKartenblock(receipt) {
86
- const provider = receipt.creditCardProvider;
87
- const daten = receipt.cardPaymentData;
88
- if ((provider !== 'hobexHps' && provider !== 'hobexCloudApi') || daten == null) {
89
- return [];
90
- }
91
- const feld = (k) => {
92
- const v = daten[k];
93
- return v == null ? '' : String(v);
94
- };
95
- const paar = (teile) => teile.filter(Boolean).join(' · ');
96
- const pan = feld('cardNumber').split('_')[0] ?? '';
97
- const rc = feld('responseCode');
113
+ function hobexBlock(daten) {
114
+ const feld = (k) => kartenFeld(daten, k);
98
115
  const zeilen = [textZeile('Hobex Beleg', 'center', true)];
99
116
  const inhalt = [
100
117
  feld('date'),
101
- paar([feld('tid') && `TID ${feld('tid')}`, feld('no') && `Nr. ${feld('no')}`]),
102
- paar([feld('type'), feld('cardBrand')]),
103
- pan,
104
- rc && `RC ${rc}`,
118
+ verbunden([feld('tid') && `TID ${feld('tid')}`, feld('no') && `Nr. ${feld('no')}`]),
119
+ verbunden([feld('type'), feld('cardBrand')]),
120
+ feld('cardNumber').split('_')[0] ?? '',
121
+ feld('responseCode') && `RC ${feld('responseCode')}`,
122
+ ];
123
+ for (const zeile of inhalt) {
124
+ if (zeile)
125
+ zeilen.push(textZeile(zeile, 'center'));
126
+ }
127
+ if (feld('cvm') === '1')
128
+ zeilen.push(...unterschriftsfeld());
129
+ return zeilen;
130
+ }
131
+ /** Zwei Leerzeilen, Linie, Wort -- das Unterschriftsfeld auf dem Bon. */
132
+ function unterschriftsfeld() {
133
+ return [
134
+ { kind: 'space', lines: 2 },
135
+ textZeile('------------------', 'center'),
136
+ textZeile('Unterschrift', 'center'),
137
+ ];
138
+ }
139
+ /**
140
+ * GP-Tom-`transactionType`: das Plugin-`toMap` schreibt den Schluessel mit
141
+ * Tippfehler (`transacitonType`), aeltere Daten tragen ihn gar nicht. Beide
142
+ * Schreibweisen werden gelesen -- ein Beleg von damals soll nicht anders
143
+ * aussehen als einer von heute.
144
+ */
145
+ function gpTomVorgangsart(daten) {
146
+ switch (daten.transactionType ?? daten.transacitonType) {
147
+ case 1: return 'Sale';
148
+ case 2: return 'Void';
149
+ case 3: return 'Refund';
150
+ case 4: return 'Close Batch';
151
+ default: return '';
152
+ }
153
+ }
154
+ /** GP-Tom-Betrag: ganze Zahl = Cent, sonst schon ein Eurobetrag. */
155
+ function gpTomBetrag(wert) {
156
+ if (typeof wert !== 'number')
157
+ return '-';
158
+ return Number.isInteger(wert) ? formatCents(wert) : wert.toFixed(2).replace('.', ',');
159
+ }
160
+ function gpTomBlock(daten) {
161
+ const feld = (k) => kartenFeld(daten, k);
162
+ const art = gpTomVorgangsart(daten);
163
+ const zeilen = [textZeile('GP Tom Beleg', 'center', true)];
164
+ const inhalt = [
165
+ `Batch: ${feld('batchNumber')}`,
166
+ `Receipt: ${feld('externalTransactionID')}`,
167
+ `TID: ${feld('terminalID')}`,
168
+ feld('emvAid'),
169
+ (daten.emvAppLable != null || daten.cardDataEntry != null)
170
+ ? `${feld('emvAppLable')} ${feld('cardDataEntry')}`.trim() : '',
171
+ feld('cardNumber'),
172
+ `${art ? `${art} ` : ''}Amount ${feld('currencyCode')} ${gpTomBetrag(daten.amount)}`,
173
+ daten.pinOk === true ? 'PIN OK' : 'PIN NOT OK',
174
+ `Authorization Code ${feld('approvedCode')}`,
175
+ `Sequence Number: ${feld('sequenceNumber')}`,
105
176
  ];
106
177
  for (const zeile of inhalt) {
107
178
  if (zeile)
108
179
  zeilen.push(textZeile(zeile, 'center'));
109
180
  }
110
- if (feld('cvm') === '1') {
111
- zeilen.push({ kind: 'space', lines: 2 });
112
- zeilen.push(textZeile('------------------', 'center'));
113
- zeilen.push(textZeile('Unterschrift', 'center'));
181
+ return zeilen;
182
+ }
183
+ function sumupBlock(daten) {
184
+ const feld = (k) => kartenFeld(daten, k);
185
+ const betrag = typeof daten.amount === 'number'
186
+ ? (Number.isInteger(daten.amount) ? formatCents(daten.amount) : daten.amount.toFixed(2).replace('.', ','))
187
+ : '-';
188
+ return [
189
+ textZeile('Sumup Beleg', 'center', true),
190
+ paarZeile('Kartentyp:', feld('cardType') || 'n/a'),
191
+ paarZeile('Kartennummer:', `**** **** **** ${feld('cardLastDigits')}`),
192
+ paarZeile('Zahlungstyp:', feld('paymentType') || 'n/a'),
193
+ paarZeile('Gesamtbetrag:', `${betrag} ${feld('currency')}`.trim()),
194
+ paarZeile('Transaktionscode:', feld('transactionCode') || '-'),
195
+ paarZeile('Modus:', feld('entryMode').toUpperCase()),
196
+ ];
197
+ }
198
+ /**
199
+ * myPOS liefert den Zeitpunkt als `YYMMDDhhmmss` ohne Trenner. Ein Wert, der
200
+ * nicht so aussieht, wird UNVERAENDERT durchgereicht statt zerschnitten: eine
201
+ * halb geratene Uhrzeit auf einem Beleg ist schlimmer als ein roher Wert.
202
+ */
203
+ function myposZeitpunkt(wert) {
204
+ const t = /^(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$/.exec(wert);
205
+ if (!t)
206
+ return wert;
207
+ return `${t[3]}.${t[2]}.20${t[1]} ${t[4]}:${t[5]}:${t[6]}`;
208
+ }
209
+ function myposBlock(daten) {
210
+ const feld = (k) => kartenFeld(daten, k);
211
+ const zeilen = [
212
+ textZeile('MyPos Beleg', 'center', true),
213
+ paarZeile('TERMINAL ID:', feld('TID') || '-'),
214
+ paarZeile('DATUM:', myposZeitpunkt(feld('date_time'))),
215
+ ];
216
+ if (feld('application_name'))
217
+ zeilen.push(textZeile(feld('application_name'), 'center', true));
218
+ zeilen.push(paarZeile('KARTE:', feld('pan') || '-'));
219
+ if (daten.signature_required === true)
220
+ zeilen.push(...unterschriftsfeld());
221
+ zeilen.push(paarZeile('STAN:', feld('STAN') ? feld('STAN').padStart(6, '0') : '-'));
222
+ zeilen.push(paarZeile('AUTH. CODE:', feld('authorization_code') || '-'));
223
+ zeilen.push(paarZeile('RRN:', feld('reference_number') || '-'));
224
+ zeilen.push(paarZeile('AID:', feld('AID') || '-'));
225
+ return zeilen;
226
+ }
227
+ const STRIPE_KARTENART = {
228
+ debit: 'Debitkarte',
229
+ credit: 'Kreditkarte',
230
+ prepaid: 'Prepaid-Karte',
231
+ };
232
+ const STRIPE_WALLET = {
233
+ apple_pay: 'Apple Pay',
234
+ google_pay: 'Google Pay',
235
+ };
236
+ /** EPS-Bank-Slug (`bank_austria`) -> Anzeigename (`Bank Austria`). */
237
+ function stripeEpsBank(slug) {
238
+ return slug.split('_').filter(Boolean)
239
+ .map((w) => w[0].toUpperCase() + w.slice(1)).join(' ');
240
+ }
241
+ function stripeMarkenZeile(daten) {
242
+ const teile = [];
243
+ if (daten.cardBrand != null)
244
+ teile.push(String(daten.cardBrand));
245
+ const art = STRIPE_KARTENART[String(daten.cardFunding)];
246
+ if (art)
247
+ teile.push(art);
248
+ let zeile = teile.join(' ');
249
+ if (daten.wallet != null) {
250
+ const wallet = STRIPE_WALLET[String(daten.wallet)] ?? String(daten.wallet);
251
+ zeile = zeile ? `${zeile} (${wallet})` : `(${wallet})`;
252
+ }
253
+ return zeile;
254
+ }
255
+ /**
256
+ * Stripe-Zahlzeitpunkt: Sekunden seit Epoche, gedruckt in **Wiener** Zeit.
257
+ * Ueber `toViennaWallClock`, nicht ueber die lokale Zeitzone -- ein Beleg
258
+ * traegt Ortszeit des Betriebs, nicht die des Geraets, das ihn oeffnet.
259
+ */
260
+ function stripeZeitpunkt(sekunden) {
261
+ const t = (0, vienna_time_js_1.toViennaWallClock)(new Date(sekunden * 1000));
262
+ const zwei = (n) => String(n).padStart(2, '0');
263
+ return `${zwei(t.day)}.${zwei(t.month)}.${t.year} ${zwei(t.hour)}:${zwei(t.minute)}`;
264
+ }
265
+ /** Zwilling von `stripeReceiptLines` (print_paper.dart). */
266
+ function stripeReceiptLines(daten, cardPaymentId) {
267
+ const zeilen = [];
268
+ const art = daten.paymentMethodType == null ? null : String(daten.paymentMethodType);
269
+ if (art === 'card') {
270
+ const marke = stripeMarkenZeile(daten);
271
+ if (marke)
272
+ zeilen.push(marke);
273
+ if (daten.cardLastDigits != null)
274
+ zeilen.push(`**** **** **** ${daten.cardLastDigits}`);
275
+ if (daten.threeDSecure === 'authenticated')
276
+ zeilen.push('3-D Secure: ja');
277
+ }
278
+ else if (art === 'eps') {
279
+ if (daten.epsBank != null)
280
+ zeilen.push(`EPS - ${stripeEpsBank(String(daten.epsBank))}`);
281
+ }
282
+ else if (art != null) {
283
+ zeilen.push(art.toUpperCase());
284
+ }
285
+ if (typeof daten.amount === 'number' && Number.isInteger(daten.amount)) {
286
+ const waehrung = daten.currency == null ? 'EUR' : String(daten.currency).toUpperCase();
287
+ zeilen.push(`Gesamtbetrag ${formatCents(daten.amount)} ${waehrung}`);
288
+ }
289
+ if (typeof daten.paidAt === 'number' && Number.isInteger(daten.paidAt)) {
290
+ zeilen.push(`Bezahlt: ${stripeZeitpunkt(daten.paidAt)}`);
291
+ }
292
+ if (daten.statementDescriptor != null)
293
+ zeilen.push(`Abrechnung: ${daten.statementDescriptor}`);
294
+ if (cardPaymentId != null)
295
+ zeilen.push(`Referenz: ${cardPaymentId}`);
296
+ return zeilen;
297
+ }
298
+ function stripeBlock(daten, cardPaymentId) {
299
+ const zeilen = [textZeile('Online-Zahlung (Stripe)', 'center', true)];
300
+ for (const zeile of stripeReceiptLines(daten, cardPaymentId)) {
301
+ zeilen.push(textZeile(zeile, 'center'));
114
302
  }
115
- zeilen.push({ kind: 'space', lines: 1 });
116
303
  return zeilen;
117
304
  }
305
+ /**
306
+ * Der Kartenzahlungsblock des Belegs -- fuer JEDEN Anbieter, den das Backend
307
+ * kennt. Ohne `cardPaymentData` bleibt der Block weg; ein unbekannter Anbieter
308
+ * ebenfalls, still und ohne Fehler: ein Beleg muss sich immer zeigen lassen.
309
+ */
310
+ function kartenblock(receipt) {
311
+ const daten = receipt.cardPaymentData;
312
+ if (daten == null)
313
+ return [];
314
+ const zeilen = (() => {
315
+ switch (receipt.creditCardProvider) {
316
+ case 'hobexHps':
317
+ case 'hobexCloudApi':
318
+ return hobexBlock(daten);
319
+ case 'gpTomAndroid':
320
+ case 'gpTomIos':
321
+ return gpTomBlock(daten);
322
+ case 'sumup':
323
+ return sumupBlock(daten);
324
+ case 'myposPro':
325
+ return myposBlock(daten);
326
+ case 'stripe':
327
+ return stripeBlock(daten, receipt.cardPaymentId ?? null);
328
+ default:
329
+ return [];
330
+ }
331
+ })();
332
+ if (zeilen.length === 0)
333
+ return [];
334
+ // Eine Leerzeile hinter JEDEM Block, nicht nur hinter manchen. Das Vorbild
335
+ // ist hier uneinheitlich (nur `_sumup` setzt am Ende ein `addFeed()`), was
336
+ // den Abstand zur Fusszeile vom Anbieter abhaengig machte. Die Leerzeile
337
+ // gehoert aber zum Blatt, nicht zum Terminal.
338
+ return [...zeilen, { kind: 'space', lines: 1 }];
339
+ }
118
340
  function zahlungsartText(wert) {
119
341
  if (wert != null && typeof wert === 'object') {
120
342
  return wert.label;
@@ -708,7 +930,7 @@ function buildReceiptLayout(receipt, company, options = {}) {
708
930
  lines.push({ kind: 'qr', data: receipt.qr });
709
931
  lines.push({ kind: 'space', lines: 1 });
710
932
  // --- Kartenzahlungsblock (Hobex) — wie im Vorbild UNTER dem QR-Code.
711
- lines.push(...hobexKartenblock(receipt));
933
+ lines.push(...kartenblock(receipt));
712
934
  // --- Dankestext und Fusszeilen
713
935
  if (company.thanksMessage.length > 0) {
714
936
  lines.push({ kind: 'space', lines: 1 });
@@ -124,6 +124,17 @@ declare const MELDUNGEN_ROH: {
124
124
  readonly text: "Unklar, ob die Kartenzahlung durchgegangen ist — die Verbindung zum Terminal riss ab. Bitte am Terminal-Beleg nachsehen, BEVOR neu kassiert wird: die Karte kann belastet sein. Kennung der Zahlung: {kennung}.";
125
125
  readonly platzhalter: readonly ["kennung"];
126
126
  };
127
+ readonly 'kartenzahlung.wartet_auf_terminal': {
128
+ readonly text: "Bitte am Terminal fortfahren …";
129
+ };
130
+ readonly 'kartenzahlung.karte_gebucht_beleg_offen': {
131
+ readonly text: "Die Karte ist bereits mit {betrag} belastet (Kennung {kennung}) — der Beleg dazu fehlt noch. Bitte jetzt den Beleg erstellen und nicht erneut kassieren.";
132
+ readonly platzhalter: readonly ["betrag", "kennung"];
133
+ };
134
+ readonly 'kartenzahlung.karte_gebucht_korb_geaendert': {
135
+ readonly text: "Es gibt eine gebuchte Kartenzahlung über {betrag} (Kennung {kennung}), aber der Korb hat sich seither geändert. Bitte zuerst entscheiden: den Beleg zur gebuchten Zahlung erstellen oder die Zahlung am Terminal stornieren und hier verwerfen.";
136
+ readonly platzhalter: readonly ["betrag", "kennung"];
137
+ };
127
138
  readonly 'kartenzahlung.connect_nicht_verbunden': {
128
139
  readonly text: "Kartenzahlung nicht möglich: Kasseneck Connect ist nicht verbunden — Einstellungen → Kasseneck Connect.";
129
140
  readonly nur: readonly ["web"];
@@ -161,6 +172,34 @@ declare const MELDUNGEN_ROH: {
161
172
  readonly platzhalter: readonly ["code"];
162
173
  readonly nur: readonly ["app"];
163
174
  };
175
+ readonly 'gptom.abgelehnt': {
176
+ readonly text: "Das Terminal hat die Zahlung abgelehnt.";
177
+ readonly nur: readonly ["app"];
178
+ };
179
+ readonly 'gptom.abgelehnt_mit_code': {
180
+ readonly text: "Das Terminal hat die Zahlung abgelehnt ({code}).";
181
+ readonly platzhalter: readonly ["code"];
182
+ readonly nur: readonly ["app"];
183
+ };
184
+ readonly 'gptom.nicht_geoeffnet': {
185
+ readonly text: "GP Tom ließ sich nicht öffnen — bitte die GP-Tom-App prüfen und erneut versuchen.";
186
+ readonly nur: readonly ["app"];
187
+ };
188
+ readonly 'gptom.zeit_abgelaufen': {
189
+ readonly text: "Das Terminal hat in der Frist nicht geantwortet — die Karte kann belastet sein. Bitte am Terminal-Beleg nachsehen, BEVOR neu kassiert wird.";
190
+ readonly nur: readonly ["app"];
191
+ };
192
+ readonly 'gptom.zeit_abgelaufen_mit_kennung': {
193
+ readonly text: "Das Terminal hat in der Frist nicht geantwortet — die Karte kann belastet sein. Bitte am Terminal-Beleg nachsehen, BEVOR neu kassiert wird. Kennung der Zahlung: {kennung}.";
194
+ readonly platzhalter: readonly ["kennung"];
195
+ readonly nur: readonly ["app"];
196
+ };
197
+ readonly 'protokoll.leer': {
198
+ readonly text: "Noch keine Einträge — das Protokoll füllt sich mit der ersten Kartenzahlung.";
199
+ };
200
+ readonly 'protokoll.kopiert': {
201
+ readonly text: "Das Protokoll liegt in der Zwischenablage.";
202
+ };
164
203
  readonly 'belege.laden_fehlgeschlagen': {
165
204
  readonly text: "Die Belege konnten nicht geladen werden.";
166
205
  };
@@ -54,6 +54,13 @@ const MELDUNGEN_ROH = {
54
54
  'kartenzahlung.unklar': { text: 'Unklar, ob die Kartenzahlung durchgegangen ist — die Verbindung zum Terminal riss ab. Bitte am Terminal-Beleg nachsehen, BEVOR neu kassiert wird: die Karte kann belastet sein.' },
55
55
  // Die Kennung ist der einzige Anker, um die Zahlung am Terminal-Beleg wiederzufinden — liegt sie vor, gilt dieser Satz statt kartenzahlung.unklar.
56
56
  'kartenzahlung.unklar_mit_kennung': { text: 'Unklar, ob die Kartenzahlung durchgegangen ist — die Verbindung zum Terminal riss ab. Bitte am Terminal-Beleg nachsehen, BEVOR neu kassiert wird: die Karte kann belastet sein. Kennung der Zahlung: {kennung}.', platzhalter: ['kennung'] },
57
+ 'kartenzahlung.wartet_auf_terminal': { text: 'Bitte am Terminal fortfahren …' },
58
+ // Ein geglücktes Kartenergebnis ohne Beleg ist der teuerste Zustand am Tresen:
59
+ // wer hier erneut kassiert, belastet die Karte ein zweites Mal. Beide Sätze
60
+ // nennen deshalb Betrag UND Kennung — nur damit findet der Kassier die Zahlung
61
+ // am Terminal-Beleg wieder — und keiner rät zum Wiederholen.
62
+ 'kartenzahlung.karte_gebucht_beleg_offen': { text: 'Die Karte ist bereits mit {betrag} belastet (Kennung {kennung}) — der Beleg dazu fehlt noch. Bitte jetzt den Beleg erstellen und nicht erneut kassieren.', platzhalter: ['betrag', 'kennung'] },
63
+ 'kartenzahlung.karte_gebucht_korb_geaendert': { text: 'Es gibt eine gebuchte Kartenzahlung über {betrag} (Kennung {kennung}), aber der Korb hat sich seither geändert. Bitte zuerst entscheiden: den Beleg zur gebuchten Zahlung erstellen oder die Zahlung am Terminal stornieren und hier verwerfen.', platzhalter: ['betrag', 'kennung'] },
57
64
  'kartenzahlung.connect_nicht_verbunden': { text: 'Kartenzahlung nicht möglich: Kasseneck Connect ist nicht verbunden — Einstellungen → Kasseneck Connect.', nur: ['web'] },
58
65
  'terminal.keines_gefunden': { text: 'Kein Hobex-Terminal gefunden — ist es eingeschaltet und im selben Netz wie dieser Rechner?', nur: ['web'] },
59
66
  'terminal.nicht_bereit': { text: 'Terminal antwortet, ist aber nicht betriebsbereit: {antwort} — TID prüfen.', platzhalter: ['antwort'], nur: ['web'] },
@@ -62,6 +69,19 @@ const MELDUNGEN_ROH = {
62
69
  'gptom.zahlung_fehlgeschlagen': { text: 'GP Tom: Zahlung fehlgeschlagen: {grund}', platzhalter: ['grund'], nur: ['app'] },
63
70
  'gptom.terminal_antwortet_nicht': { text: 'Das Terminal hat nicht geantwortet: {grund}', platzhalter: ['grund'], nur: ['app'] },
64
71
  'gptom.zahlung_nicht_abgeschlossen': { text: 'Die Zahlung wurde nicht abgeschlossen ({code}).', platzhalter: ['code'], nur: ['app'] },
72
+ // Abgelehnt heißt: das Terminal hat entschieden, es ist sicher nichts gebucht.
73
+ // Der Code ist die einzige Handhabe, mit der der Inhaber bei GP nachfragen kann —
74
+ // liegt einer vor, gilt die Fassung mit Code.
75
+ 'gptom.abgelehnt': { text: 'Das Terminal hat die Zahlung abgelehnt.', nur: ['app'] },
76
+ 'gptom.abgelehnt_mit_code': { text: 'Das Terminal hat die Zahlung abgelehnt ({code}).', platzhalter: ['code'], nur: ['app'] },
77
+ 'gptom.nicht_geoeffnet': { text: 'GP Tom ließ sich nicht öffnen — bitte die GP-Tom-App prüfen und erneut versuchen.', nur: ['app'] },
78
+ // Frist abgelaufen ist kein Abbruch: die Karte kann belastet sein, nur die
79
+ // Antwort blieb aus. Deshalb derselbe Ton wie kartenzahlung.unklar.
80
+ 'gptom.zeit_abgelaufen': { text: 'Das Terminal hat in der Frist nicht geantwortet — die Karte kann belastet sein. Bitte am Terminal-Beleg nachsehen, BEVOR neu kassiert wird.', nur: ['app'] },
81
+ 'gptom.zeit_abgelaufen_mit_kennung': { text: 'Das Terminal hat in der Frist nicht geantwortet — die Karte kann belastet sein. Bitte am Terminal-Beleg nachsehen, BEVOR neu kassiert wird. Kennung der Zahlung: {kennung}.', platzhalter: ['kennung'], nur: ['app'] },
82
+ // --- Terminal-Protokoll --------------------------------------------------
83
+ 'protokoll.leer': { text: 'Noch keine Einträge — das Protokoll füllt sich mit der ersten Kartenzahlung.' },
84
+ 'protokoll.kopiert': { text: 'Das Protokoll liegt in der Zwischenablage.' },
65
85
  // --- Belege und Storno ---------------------------------------------------
66
86
  'belege.laden_fehlgeschlagen': { text: 'Die Belege konnten nicht geladen werden.' },
67
87
  'belege.keine_im_zeitraum': { text: 'Keine Belege in diesem Zeitraum.' },
@@ -22,11 +22,15 @@ import type { PosPaperSize } from '../printing/escpos.js';
22
22
  *
23
23
  * Bewusst **nicht** enthalten: Firmenlogo und Rasterbilder (brauchen
24
24
  * Bildverarbeitung, die im Browser anders aussieht als in Node), PDF-Erzeugung
25
- * und Druckeransteuerung. Von den anbieterspezifischen Kartenzahlungsbloecken
26
- * des Vorbilds (GP Tom, SumUp, myPOS, Stripe) ist nur der **Hobex-Block**
27
- * enthalten: die Browser-Kasse bedient das HPS-Terminal selbst (via Kasseneck
28
- * Connect) und druckt den Bon aus genau diesem Modell — die uebrigen Anbieter
29
- * drucken in ihren eigenen Apps.
25
+ * und Druckeransteuerung.
26
+ *
27
+ * Die anbieterspezifischen Kartenzahlungsbloecke sind dagegen **alle**
28
+ * enthalten (Hobex, GP Tom, SumUp, myPOS, Stripe). Bis 0.8.0 trug das Modell
29
+ * nur den Hobex-Block, weil die Browser-Kasse nur das HPS-Terminal selbst
30
+ * bedient und die uebrigen Anbieter „in ihren eigenen Apps drucken". Das
31
+ * Argument gilt fuer die Frage, wer das TERMINAL bedient — nicht dafuer, was
32
+ * auf dem BELEGDOKUMENT steht. Aus der einen Regel wurde die andere, und
33
+ * derselbe Beleg trug im PDF weniger als auf dem Bon.
30
34
  */
31
35
  /** Ausrichtung einer Zeile bzw. einer Spalte. */
32
36
  export type LayoutAlign = 'left' | 'center' | 'right';
@@ -129,6 +133,8 @@ export interface BuildReceiptLayoutOptions {
129
133
  * diesem Paket ausnahmslos ganzzahlig gerechnet).
130
134
  */
131
135
  export declare function formatCents(cents: number): string;
136
+ /** Zwilling von `stripeReceiptLines` (print_paper.dart). */
137
+ export declare function stripeReceiptLines(daten: Record<string, unknown>, cardPaymentId: string | null): string[];
132
138
  /**
133
139
  * Hinweis auf die Steuerbefreiung, den ein Kleinunternehmerbeleg tragen muss —
134
140
  * ohne ihn stuende in der USt-Tabelle „D 0 %" ohne jede Begruendung, und ab