@kreiseck/kasseneck-api 0.30.0 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -16,8 +16,10 @@
16
16
  * auf einer Plattform vorkommen kann.
17
17
  */
18
18
  Object.defineProperty(exports, "__esModule", { value: true });
19
- exports.BELEG_MAIL_FEHLER = exports.FEHLERREGELN = exports.MELDUNGEN = void 0;
19
+ exports.BESCHRIFTUNGEN = exports.STORNO_ZAHLUNG_FEHLER = exports.BELEG_MAIL_FEHLER = exports.FEHLERREGELN = exports.MELDUNGEN = void 0;
20
20
  exports.belegMailFehler = belegMailFehler;
21
+ exports.stornoZahlungFehler = stornoZahlungFehler;
22
+ exports.beschriftung = beschriftung;
21
23
  exports.meldung = meldung;
22
24
  exports.meldungGiltFuer = meldungGiltFuer;
23
25
  const MELDUNGEN_ROH = {
@@ -29,16 +31,22 @@ const MELDUNGEN_ROH = {
29
31
  'kopplung.code_fehlt': { text: 'Bitte den Kopplungs-Code eingeben.' },
30
32
  'kopplung.fehlgeschlagen': { text: 'Die Kopplung ist fehlgeschlagen. Bitte erneut versuchen.' },
31
33
  'kopplung.unvollstaendig': { text: 'Die Kopplung ist unvollständig zurückgekommen. Bitte im Panel einen neuen Code erzeugen und noch einmal versuchen.' },
32
- 'geraet.browser_speicher': { text: 'Dieses Gerät konnte nicht gespeichert werden — der Browser-Speicher steht nicht zur Verfügung. Bitte den privaten Modus verlassen.', nur: ['web'] },
34
+ 'geraet.browser_speicher': { text: 'Dieses Gerät konnte nicht gespeichert werden – der Browser-Speicher steht nicht zur Verfügung. Bitte den privaten Modus verlassen.', nur: ['web'] },
33
35
  // --- Anmeldung und Sitzung -----------------------------------------------
34
36
  'anmeldung.pin_fehlt': { text: 'Bitte die PIN eingeben.' },
35
37
  'anmeldung.fehlgeschlagen': { text: 'Die Anmeldung ist fehlgeschlagen. Bitte erneut versuchen.' },
36
38
  'anmeldung.nicht_abgeschlossen': { text: 'Die Anmeldung konnte nicht abgeschlossen werden. Bitte erneut versuchen.' },
37
- 'anmeldung.wird_abgeschlossen': { text: 'Anmeldung wird gerade abgeschlossen — bitte gleich noch einmal drücken.' },
39
+ 'anmeldung.wird_abgeschlossen': { text: 'Anmeldung wird gerade abgeschlossen – bitte gleich noch einmal drücken.' },
38
40
  'sitzung.abgelaufen': { text: 'Die Sitzung ist abgelaufen. Bitte erneut anmelden.' },
39
- 'sitzung.abgelaufen_ohne_netz': { text: 'Keine Verbindung zum Server — die Sitzung ist abgelaufen. Bitte erneut anmelden.' },
41
+ 'sitzung.abgelaufen_ohne_netz': { text: 'Keine Verbindung zum Server – die Sitzung ist abgelaufen. Bitte erneut anmelden.' },
40
42
  'sitzung.keine': { text: 'Keine Sitzung.' },
41
- 'rechte.nicht_aenderbar': { text: 'Das darfst du nicht ändern — das Recht dafür vergibt der Inhaber im Panel.' },
43
+ // Die Sperre nach Untaetigkeit kuendigt sich an: der Countdown laeuft in
44
+ // Sekunden, eine Beruehrung haelt die Schicht offen.
45
+ 'sitzung.meldet_ab': { text: 'Kasse meldet in {sekunden} s ab – Bildschirm berühren, um weiterzuarbeiten.', platzhalter: ['sekunden'] },
46
+ // Wer die Abmelden-Taste ein zweites Mal drueckt, beendet die Schicht –
47
+ // der Satz steht in der Rueckfrage, bevor es so weit ist.
48
+ 'abmelden.noch_einmal': { text: 'Noch einmal drücken beendet die Schicht an dieser Kasse.' },
49
+ 'rechte.nicht_aenderbar': { text: 'Das darfst du nicht ändern – das Recht dafür vergibt der Inhaber im Panel.' },
42
50
  // --- Verkauf und Positionen ----------------------------------------------
43
51
  'kasse.laden_fehlgeschlagen': { text: 'Die Kasse konnte nicht geladen werden.' },
44
52
  'artikel.laden_fehlgeschlagen': { text: 'Die Artikel konnten nicht geladen werden.' },
@@ -46,30 +54,53 @@ const MELDUNGEN_ROH = {
46
54
  'artikel.keine_gefunden': { text: 'Keine Artikel gefunden.' },
47
55
  'artikel.steuersatz_unbekannt': { text: '„{name}“ hat einen Steuersatz, den die Kasse nicht kennt.', platzhalter: ['name'] },
48
56
  'position.betrag_fehlt': { text: 'Bitte einen Betrag eingeben.' },
49
- 'position.bezeichnung_fehlt': { text: 'Bitte eine Bezeichnung eingeben — sie steht am Beleg.' },
57
+ 'position.bezeichnung_fehlt': { text: 'Bitte eine Bezeichnung eingeben – sie steht am Beleg.' },
50
58
  'rabatt.prozent_ungueltig': { text: 'Bitte einen Prozentwert zwischen 0,1 und 100 eingeben.' },
59
+ // --- Kassieren -----------------------------------------------------------
60
+ // Der Grund ueber dem Knopf, solange der Beleg nicht abgeschlossen werden kann.
61
+ 'kassieren.nichts_erfasst': { text: 'Noch nichts erfasst – bitte zuerst eine Position aufnehmen.' },
62
+ 'kassieren.gegeben_fehlt': { text: 'Erst eintippen, was der Gast gibt – der Rückgeld-Rechner ist an.' },
63
+ 'kassieren.gegeben_zu_wenig': { text: 'Gegeben ist weniger als der Betrag.' },
64
+ // Lebenszyklus der Kasse (ausser Betrieb, Signatureinheit …): der Grund
65
+ // kommt vom Server bzw. aus dem Kassenstand und steht hinter dem Doppelpunkt.
66
+ 'kassieren.gesperrt': { text: 'Kassieren gesperrt: {grund}', platzhalter: ['grund'] },
67
+ // Keine gesetzliche Obergrenze – steuerfrei ist aber nur ortsuebliches
68
+ // Trinkgeld (§ 3 Abs 1 Z 16a EStG). Eine Warnung, keine Sperre.
69
+ 'trinkgeld.ueber_haelfte': { text: 'Über 50 % Trinkgeld – wirklich? Steuerfrei ist nur ortsübliches Trinkgeld.' },
51
70
  // --- Abschluss -----------------------------------------------------------
52
71
  'abschluss.fehlgeschlagen': { text: 'Der Abschluss ist fehlgeschlagen.' },
53
- 'abschluss.unklar': { text: 'Unklar, ob der Beleg entstanden ist — die Antwort kam nicht an. Bitte nicht noch einmal abschließen: der Beleg kann bereits erstellt und signiert sein. Im Panel unter „Belege“ nachsehen; nur wenn er dort fehlt, den Verkauf erneut abschließen.' },
72
+ 'abschluss.unklar': { text: 'Unklar, ob der Beleg entstanden ist – die Antwort kam nicht an. Bitte nicht noch einmal abschließen: der Beleg kann bereits erstellt und signiert sein. Im Panel unter „Belege“ nachsehen; nur wenn er dort fehlt, den Verkauf erneut abschließen.' },
54
73
  'abschluss.nullbeleg_fehlgeschlagen': { text: 'Der Nullbeleg konnte nicht erstellt werden.' },
55
74
  'abschluss.signatur_ausgefallen': { text: 'Die Signatureinheit hat nicht geantwortet. Der Beleg ist gültig und trägt den Vermerk „Sicherheitseinrichtung ausgefallen“.' },
75
+ // Erledigen darf nur bestaetigen, was am Server auch wirklich steht – sonst
76
+ // gilt ein Betrag als abgehakt, zu dem es nie einen Beleg gab.
77
+ 'abschluss.erledigen_frage': { text: 'Nur erledigen, wenn der Beleg in der Belegliste steht – sonst bleiben {betrag} ohne Beleg. Bitte zuerst unter „Belege“ nachsehen.', platzhalter: ['betrag'] },
56
78
  // --- Kartenzahlung -------------------------------------------------------
57
79
  'kartenzahlung.nicht_moeglich': { text: 'Kartenzahlung nicht möglich.' },
58
80
  'kartenzahlung.nicht_zustande': { text: 'Die Kartenzahlung ist nicht zustande gekommen.' },
59
81
  'kartenzahlung.nicht_gestartet': { text: 'Kartenzahlung nicht gestartet: {grund}', platzhalter: ['grund'] },
60
- '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.' },
82
+ '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.' },
61
83
  // Die Kennung ist der einzige Anker, um die Zahlung am Terminal-Beleg wiederzufinden — liegt sie vor, gilt dieser Satz statt kartenzahlung.unklar.
62
- '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'] },
84
+ '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'] },
63
85
  'kartenzahlung.wartet_auf_terminal': { text: 'Bitte am Terminal fortfahren …' },
86
+ // Das Kartenfenster des Terminals ist abgelaufen: es bricht selbst ab, die
87
+ // Kasse muss nichts tun.
88
+ 'kartenzahlung.terminal_bricht_ab': { text: 'Das Terminal bricht gleich von selbst ab …' },
64
89
  // Ein geglücktes Kartenergebnis ohne Beleg ist der teuerste Zustand am Tresen:
65
90
  // wer hier erneut kassiert, belastet die Karte ein zweites Mal. Beide Sätze
66
91
  // nennen deshalb Betrag UND Kennung — nur damit findet der Kassier die Zahlung
67
92
  // am Terminal-Beleg wieder — und keiner rät zum Wiederholen.
68
- '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'] },
93
+ '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'] },
69
94
  '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'] },
70
- 'kartenzahlung.connect_nicht_verbunden': { text: 'Kartenzahlung nicht möglich: Kasseneck Connect ist nicht verbunden — Einstellungen → Kasseneck Connect.', nur: ['web'] },
71
- 'terminal.keines_gefunden': { text: 'Kein Hobex-Terminal gefunden — ist es eingeschaltet und im selben Netz wie dieser Rechner?', nur: ['web'] },
72
- 'terminal.nicht_bereit': { text: 'Terminal antwortet, ist aber nicht betriebsbereit: {antwort} — TID prüfen.', platzhalter: ['antwort'], nur: ['web'] },
95
+ 'kartenzahlung.connect_nicht_verbunden': { text: 'Kartenzahlung nicht möglich: Kasseneck Connect ist nicht verbunden – Einstellungen → Kasseneck Connect.', nur: ['web'] },
96
+ // Solange die Karte belastet ist, darf am Korb nichts mehr veraendert werden –
97
+ // sonst passt der Beleg nicht mehr zum Betrag, der schon abgebucht ist.
98
+ 'kartenzahlung.korb_gesperrt_karte_belastet': { text: 'Die Karte ist bereits belastet – Warenkorb, Rabatt und Trinkgeld bleiben gesperrt, bis der Beleg entsteht oder die Karte zurückerstattet ist.' },
99
+ // Entkoppeln trennt das Geraet vom Konto, nicht die gebuchte Kartenzahlung
100
+ // vom Terminal – die muss weiterhin von Hand storniert werden.
101
+ 'kartenzahlung.entkoppeln_karte_belastet': { text: 'Auf diesem Gerät liegt noch eine gebuchte Kartenzahlung ohne Beleg – sie muss von Hand am Terminal zurückerstattet werden (Betrag siehe unten).' },
102
+ 'terminal.keines_gefunden': { text: 'Kein Hobex-Terminal gefunden – ist es eingeschaltet und im selben Netz wie dieser Rechner?', nur: ['web'] },
103
+ 'terminal.nicht_bereit': { text: 'Terminal antwortet, ist aber nicht betriebsbereit: {antwort} – TID prüfen.', platzhalter: ['antwort'], nur: ['web'] },
73
104
  'gptom.app_fehlt': { text: 'Die GP-Tom-App ist auf diesem Gerät nicht da.', nur: ['app'] },
74
105
  'gptom.suche_fehlgeschlagen': { text: 'GP Tom: Suche nach der App fehlgeschlagen: {grund}', platzhalter: ['grund'], nur: ['app'] },
75
106
  'gptom.zahlung_fehlgeschlagen': { text: 'GP Tom: Zahlung fehlgeschlagen: {grund}', platzhalter: ['grund'], nur: ['app'] },
@@ -80,13 +111,13 @@ const MELDUNGEN_ROH = {
80
111
  // liegt einer vor, gilt die Fassung mit Code.
81
112
  'gptom.abgelehnt': { text: 'Das Terminal hat die Zahlung abgelehnt.', nur: ['app'] },
82
113
  'gptom.abgelehnt_mit_code': { text: 'Das Terminal hat die Zahlung abgelehnt ({code}).', platzhalter: ['code'], nur: ['app'] },
83
- 'gptom.nicht_geoeffnet': { text: 'GP Tom ließ sich nicht öffnen — bitte die GP-Tom-App prüfen und erneut versuchen.', nur: ['app'] },
114
+ 'gptom.nicht_geoeffnet': { text: 'GP Tom ließ sich nicht öffnen – bitte die GP-Tom-App prüfen und erneut versuchen.', nur: ['app'] },
84
115
  // Frist abgelaufen ist kein Abbruch: die Karte kann belastet sein, nur die
85
116
  // Antwort blieb aus. Deshalb derselbe Ton wie kartenzahlung.unklar.
86
- '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'] },
87
- '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'] },
117
+ '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'] },
118
+ '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'] },
88
119
  // --- Terminal-Protokoll --------------------------------------------------
89
- 'protokoll.leer': { text: 'Noch keine Einträge — das Protokoll füllt sich mit der ersten Kartenzahlung.' },
120
+ 'protokoll.leer': { text: 'Noch keine Einträge – das Protokoll füllt sich mit der ersten Kartenzahlung.' },
90
121
  'protokoll.kopiert': { text: 'Das Protokoll liegt in der Zwischenablage.' },
91
122
  // --- Belege und Storno ---------------------------------------------------
92
123
  'belege.laden_fehlgeschlagen': { text: 'Die Belege konnten nicht geladen werden.' },
@@ -96,6 +127,62 @@ const MELDUNGEN_ROH = {
96
127
  'storno.fehlgeschlagen': { text: 'Das Storno ist fehlgeschlagen.' },
97
128
  'storno.grund_fehlt': { text: 'Bitte einen Grund wählen.' },
98
129
  'storno.position_fehlt': { text: 'Bitte mindestens eine Position wählen.' },
130
+ // Storno eines Belegs mit mehreren Zahlungen: je Zahlung wird zurückgegeben,
131
+ // und der Server prueft jede Rueckgabe gegen den Rest ihrer Zahlung. Welcher
132
+ // Code welchen Satz bekommt, steht in STORNO_ZAHLUNG_FEHLER.
133
+ 'storno.zahlungen_fehlen': { text: 'Dieser Beleg wurde in mehreren Zahlungen bezahlt – bitte angeben, wie zurückgegeben wird.' },
134
+ 'storno.rueckgabe_zu_hoch': { text: 'Eine Rückgabe ist höher als der Rest ihrer Zahlung – bitte die Beträge prüfen.' },
135
+ 'storno.rueckgabe_ohne_bezug': { text: 'Eine Rückgabe nennt keine Zahlung des Belegs – bitte das Storno neu beginnen.' },
136
+ 'storno.rueckgabe_bezug_unbekannt': { text: 'Eine Rückgabe nennt eine Zahlung, die es am Beleg nicht gibt – bitte das Storno neu beginnen.' },
137
+ // Die Antwort kam, aber der Ausgang ist offen: der Storno-Beleg kann schon
138
+ // signiert sein. Wer jetzt noch einmal storniert, storniert womoeglich doppelt.
139
+ 'storno.ergebnis_unklar': { text: 'Unklar, ob das Storno entstanden ist – es kann bereits signiert sein. Bitte nicht erneut stornieren, sondern die Belegliste in ein paar Minuten neu laden.' },
140
+ 'storno.summe_passt_nicht': { text: 'Die Rückgaben ergeben nicht den Betrag des Stornos – bitte die Beträge prüfen.' },
141
+ // Storno eines Belegs mit mehreren Zahlungen: Karten gehen VON HAND am
142
+ // Terminal zurueck, und zwar erst NACH dem gebuchten Storno -- vorher
143
+ // fliesst kein Geld. Darunter steht die Liste der Karten zum Abhaken; der
144
+ // Satz passt fuer eine oder mehrere Karten, mit oder ohne Anbindung.
145
+ 'storno.karten_gutschreiben': { text: 'Das Storno ist gebucht – bitte jede Karte unten am Terminal gutschreiben und abhaken.' },
146
+ // Wie storno.ergebnis_unklar, nur enthielt die Rueckgabe Karten: bevor
147
+ // klar ist, ob das Storno steht, darf keine Gutschrift laufen -- sonst geht
148
+ // Geld zurueck ohne Storno-Beleg.
149
+ 'storno.ergebnis_unklar_karten': { text: 'Unklar, ob das Storno entstanden ist – es kann bereits signiert sein. Bitte nicht erneut stornieren und noch keine Karte gutschreiben, sondern die Belegliste in ein paar Minuten neu laden. Steht das Storno dort, die Karten laut Storno-Beleg am Terminal gutschreiben.' },
150
+ // Die Liste der Karten nach dem gebuchten Storno wird geschlossen, obwohl
151
+ // nicht jede Karte abgehakt ist: einmal nachfragen -- eine vergessene
152
+ // Gutschrift faellt sonst erst dem Gast auf.
153
+ 'storno.karten_nicht_abgehakt': { text: 'Noch ist nicht jede Karte abgehakt – bitte jede am Terminal gutschreiben. Ist das schon geschehen, zum Schließen noch einmal drücken.' },
154
+ // --- Getrennt zahlen -----------------------------------------------------
155
+ // Ein Tisch zahlt in Teilen, ein Beleg fuer alles. Das Teure ist eine schon
156
+ // belastete Karte: jeder Satz, der sie betrifft, nennt den Betrag, und keiner
157
+ // raet dazu, noch einmal zu kassieren.
158
+ 'getrennt.einstellung_hinweis': { text: 'Ein Tisch zahlt in Teilen: jede Zahlung bar oder mit Karte, mit eigenem Trinkgeld – am Ende ein Beleg für alles.' },
159
+ 'getrennt.betrag_ueber_offen': { text: 'Der Betrag ist höher als offen – höchstens {betrag}.', platzhalter: ['betrag'] },
160
+ 'getrennt.trinkgeld_ueber_betrag': { text: 'Das Trinkgeld ist höher als der Betrag dieser Zahlung.' },
161
+ 'getrennt.gegeben_zu_wenig': { text: 'Gegeben ist weniger als der Betrag dieser Zahlung.' },
162
+ 'getrennt.noch_offen': { text: 'Es sind noch {betrag} offen – abschließen geht erst, wenn alles kassiert ist.', platzhalter: ['betrag'] },
163
+ 'getrennt.summe_passt_nicht': { text: 'Die Zahlungen ergeben nicht den Betrag des Belegs – bitte die Liste prüfen.' },
164
+ 'getrennt.korb_gesperrt': { text: 'Der Warenkorb ist gesperrt, solange Zahlungen kassiert sind – erst abschließen oder die Zahlungen entfernen.' },
165
+ 'getrennt.wechsel_gesperrt': { text: 'Zurück zu Bar oder Karte geht erst, wenn keine Zahlung mehr kassiert ist.' },
166
+ 'getrennt.bar_zurueckgeben': { text: 'Bitte {betrag} Bargeld an den Gast zurückgeben.', platzhalter: ['betrag'] },
167
+ 'getrennt.karte_zurueckbuchen_laeuft': { text: 'Die Kartenzahlung über {betrag} wird am Terminal zurückgebucht …', platzhalter: ['betrag'] },
168
+ 'getrennt.karte_zurueckgebucht': { text: 'Die Kartenzahlung über {betrag} ist am Terminal zurückgebucht.', platzhalter: ['betrag'] },
169
+ 'getrennt.karte_zurueckbuchen_fehlgeschlagen': { text: 'Die Kartenzahlung über {betrag} (Kennung {kennung}) ließ sich nicht zurückbuchen – sie bleibt in der Liste. Bitte am Terminal-Beleg nachsehen und erneut versuchen.', platzhalter: ['betrag', 'kennung'] },
170
+ 'getrennt.karte_zurueckbuchen_unklar': { text: 'Unklar, ob die Rückbuchung über {betrag} (Kennung {kennung}) durchgegangen ist – die Zahlung bleibt in der Liste. Bitte am Terminal-Beleg nachsehen, BEVOR erneut zurückgebucht wird.', platzhalter: ['betrag', 'kennung'] },
171
+ // Das Terminal meldet eine Gutschrift, die hier noch als offene Ruecknahme
172
+ // steht – ein zweites Zurueckbuchen waere die doppelte Rueckgabe.
173
+ 'getrennt.karte_bereits_zurueckgebucht': { text: 'Das Terminal meldet, dass die Kartenzahlung über {betrag} bereits gutgeschrieben wurde (Kennung {kennung}) – bitte am Terminal-Beleg prüfen und nicht erneut zurückbuchen.', platzhalter: ['betrag', 'kennung'] },
174
+ 'getrennt.extern_zurueckbuchen': { text: 'Diese Karte ist nicht an die Kasse angebunden. Bitte {betrag} jetzt am Terminal zurückbuchen und danach bestätigen.', platzhalter: ['betrag'] },
175
+ 'getrennt.sitzung_offen': { text: 'Eine getrennte Zahlung ist nicht abgeschlossen, {betrag} sind schon kassiert. Bitte weiter kassieren oder alles zurückbuchen.', platzhalter: ['betrag'] },
176
+ 'getrennt.sitzung_unlesbar': { text: 'Eine gespeicherte getrennte Zahlung auf diesem Gerät ließ sich nicht lesen – bitte prüfen, ob schon Karten belastet wurden (Kasseneck-Panel oder Terminal), und offene Beträge von Hand ausgleichen.' },
177
+ 'getrennt.ablage_fehlgeschlagen': { text: 'Die getrennte Zahlung ließ sich auf diesem Gerät nicht speichern – bitte die Kasse bis zum Abschluss dieses Belegs nicht neu laden, sonst stehen belastete Karten womöglich nicht mehr in der Liste.' },
178
+ 'getrennt.zu_viele_zahlungen': { text: 'Ein Beleg lässt höchstens 20 Zahlungen zu.' },
179
+ 'getrennt.entkoppeln_offene_karten': { text: 'Auf diesem Gerät ist noch eine getrennte Zahlung mit belasteten Karten offen – sie müssen von Hand am Terminal zurückgebucht werden (Beträge siehe unten).' },
180
+ 'getrennt.positionen_gesperrt': { text: 'Nach Positionen geht nicht mehr – eine Zahlung ist schon als Betrag kassiert.' },
181
+ 'getrennt.positionen_waehlen': { text: 'Bitte zuerst antippen, was dieser Gast zahlt.' },
182
+ // Barzahlung mit Rueckgeld-Rechner: ohne „Gegeben" wird die Zahlung nicht
183
+ // hinzugefuegt – derselbe Grund wie beim Abschluss einer Barzahlung, auf
184
+ // DIESE Zahlung bezogen.
185
+ 'getrennt.gegeben_fehlt': { text: 'Erst eintippen, was der Gast für diese Zahlung gibt – der Rückgeld-Rechner ist an.' },
99
186
  // --- Beleg weitergeben: Link, Teilen, E-Mail -----------------------------
100
187
  // Kopieren, Teilen und Senden fuehren zu demselben Ziel: der oeffentlichen
101
188
  // Belegseite (beleg.kasseneck.at/<fullReceiptId>). Kein Satz davon ist
@@ -108,29 +195,29 @@ const MELDUNGEN_ROH = {
108
195
  // Belege aus dem Altbestand tragen keine `fullReceiptId`; ohne sie gibt es
109
196
  // keine Belegseite. Der Satz nennt den Grund, sonst sucht der Kassier den
110
197
  // Fehler bei sich und versucht es ein zweites Mal.
111
- 'beleg.nicht_teilbar': { text: 'Für diesen Beleg gibt es keinen Link — er stammt aus einer älteren Kasse.' },
198
+ 'beleg.nicht_teilbar': { text: 'Für diesen Beleg gibt es keinen Link – er stammt aus einer älteren Kasse.' },
112
199
  // Ein Testbeleg hat eine Belegseite, ist aber steuerlich nichts wert. Wer
113
200
  // den Link weitergibt, muss das vorher lesen, nicht hinterher.
114
- 'beleg.test_hinweis_teilen': { text: 'Test-Umgebung — der Beleg ist steuerlich nicht gültig.' },
201
+ 'beleg.test_hinweis_teilen': { text: 'Test-Umgebung – der Beleg ist steuerlich nicht gültig.' },
115
202
  'beleg.mail_gesendet': { text: 'Der Beleg wurde an {an} gesendet.', platzhalter: ['an'] },
116
203
  'beleg.mail_adresse_ungueltig': { text: 'Diese E-Mail-Adresse ist nicht gültig.' },
117
- 'beleg.mail_zu_oft': { text: 'Dieser Beleg wurde schon oft gesendet — bitte später noch einmal.' },
204
+ 'beleg.mail_zu_oft': { text: 'Dieser Beleg wurde schon oft gesendet – bitte später noch einmal.' },
118
205
  'beleg.mail_fehlgeschlagen': { text: 'Der Beleg konnte nicht gesendet werden. Bitte noch einmal versuchen.' },
119
206
  // Bewusst nicht derselbe Satz wie `beleg.nicht_gefunden`: dort scheitert das
120
207
  // Oeffnen, hier das Senden. Der Kassier steht vor einem Adressfeld und muss
121
208
  // lesen, dass nichts hinausgegangen ist.
122
- 'beleg.mail_nicht_gefunden': { text: 'Dieser Beleg wurde nicht gefunden — er konnte nicht gesendet werden.' },
209
+ 'beleg.mail_nicht_gefunden': { text: 'Dieser Beleg wurde nicht gefunden – er konnte nicht gesendet werden.' },
123
210
  // --- Druck ---------------------------------------------------------------
124
211
  'druck.fehlgeschlagen': { text: 'Der Ausdruck ist fehlgeschlagen.' },
125
212
  'druck.nicht_moeglich': { text: 'Druck nicht möglich: {grund}', platzhalter: ['grund'] },
126
213
  'druck.testdruck_fehlgeschlagen': { text: 'Der Testdruck ist fehlgeschlagen.' },
127
214
  'druck.testdruck_nicht_moeglich': { text: 'Der Testdruck ist fehlgeschlagen: {grund}', platzhalter: ['grund'] },
128
- 'druck.kein_drucker': { text: 'Kein Bondrucker eingerichtet — in den Einstellungen unter Drucker & Lade.' },
215
+ 'druck.kein_drucker': { text: 'Kein Bondrucker eingerichtet – in den Einstellungen unter Drucker & Lade.' },
129
216
  'druck.drucker_nicht_erreichbar': { text: 'Drucker nicht erreichbar.' },
130
217
  'druck.job_abgelaufen': { text: 'Drucker hat den Beleg nicht abgeholt (abgelaufen).', nur: ['web'] },
131
- 'druck.kein_drucker_gefunden': { text: 'Kein Drucker gefunden — ist er eingeschaltet und im selben Netz wie dieser Rechner?', nur: ['web'] },
132
- 'druck.nur_chrome': { text: '{weg}-Druck geht nur in Chrome oder Edge (Windows, Mac, Android) — nicht in Safari und nicht am iPad.', platzhalter: ['weg'], nur: ['web'] },
133
- 'druck.kein_weg_drucker': { text: 'Kein {weg}-Drucker verbunden — „{weg}-Drucker verbinden“ und den Drucker im Dialog wählen.', platzhalter: ['weg'], nur: ['web'] },
218
+ 'druck.kein_drucker_gefunden': { text: 'Kein Drucker gefunden – ist er eingeschaltet und im selben Netz wie dieser Rechner?', nur: ['web'] },
219
+ 'druck.nur_chrome': { text: '{weg}-Druck geht nur in Chrome oder Edge (Windows, Mac, Android) – nicht in Safari und nicht am iPad.', platzhalter: ['weg'], nur: ['web'] },
220
+ 'druck.kein_weg_drucker': { text: 'Kein {weg}-Drucker verbunden – „{weg}-Drucker verbinden“ und den Drucker im Dialog wählen.', platzhalter: ['weg'], nur: ['web'] },
134
221
  // Der Drucker-Wizard: suchen, verbinden, Testdruck, QR-Probe, erst dann
135
222
  // speichern. Derselbe Ablauf in beiden Kassen — deshalb hat kein Satz ein
136
223
  // `nur`, obwohl der Weg zum Drucker verschieden ist (Bluetooth in der App,
@@ -141,7 +228,7 @@ const MELDUNGEN_ROH = {
141
228
  // Kein Modus druckt einen lesbaren QR-Code: gespeichert wird trotzdem (mit
142
229
  // Raster als Vorgabe), aber der Chef muss wissen, dass der QR-Code dann vom
143
230
  // Bildschirm gelesen werden muss — nach RKSV gehoert er an den Beleg.
144
- 'druck.wizard_qr_keiner_hinweis': { text: 'Kein QR-Code kam sauber — der Beleg zeigt den QR-Code dann am Bildschirm.' },
231
+ 'druck.wizard_qr_keiner_hinweis': { text: 'Kein QR-Code kam sauber – der Beleg zeigt den QR-Code dann am Bildschirm.' },
145
232
  'druck.wizard_nichts_gekommen': { text: 'Nichts gekommen? Drucker an, Papier drin, richtiges Gerät gewählt?' },
146
233
  'druck.wizard_gespeichert': { text: '{name} ist eingerichtet.', platzhalter: ['name'] },
147
234
  // Abbrechen an jeder Stelle: nichts gespeichert. Der Satz sagt genau das,
@@ -151,24 +238,25 @@ const MELDUNGEN_ROH = {
151
238
  'bluetooth.freigabe_fehlt': { text: 'Bitte die Freigabe in den Geräte-Einstellungen erteilen.', nur: ['app'] },
152
239
  'bluetooth.suche_fehlgeschlagen': { text: 'Die Suche ist fehlgeschlagen: {grund}', platzhalter: ['grund'], nur: ['app'] },
153
240
  // --- Kasseneck Connect (nur Browser) -------------------------------------
154
- 'connect.antwortet_nicht': { text: 'Kasseneck Connect antwortet nicht — läuft das Programm auf diesem Rechner?', nur: ['web'] },
155
- 'connect.code_abgelaufen': { text: 'Der Kopplungs-Code ist abgelaufen — im Agent einen neuen erzeugen („kasseneck-connect pair“).', nur: ['web'] },
156
- 'connect.zu_viele_versuche': { text: 'Zu viele Fehlversuche — eine Minute warten und noch einmal versuchen.', nur: ['web'] },
157
- 'connect.drucker_antwortet_nicht': { text: 'Der Drucker antwortet nicht — Strom, Netzwerk und IP prüfen.', nur: ['web'] },
158
- 'connect.nicht_gekoppelt': { text: 'Diese Kasse ist mit Kasseneck Connect nicht gekoppelt — in den Einstellungen „Koppeln“ drücken.', nur: ['web'] },
159
- 'connect.drucker_unbekannt': { text: 'Diesen Drucker kennt Kasseneck Connect nicht (mehr) — bitte neu suchen.', nur: ['web'] },
241
+ 'connect.antwortet_nicht': { text: 'Kasseneck Connect antwortet nicht – läuft das Programm auf diesem Rechner?', nur: ['web'] },
242
+ 'connect.code_abgelaufen': { text: 'Der Kopplungs-Code ist abgelaufen – im Agent einen neuen erzeugen („kasseneck-connect pair“).', nur: ['web'] },
243
+ 'connect.zu_viele_versuche': { text: 'Zu viele Fehlversuche – eine Minute warten und noch einmal versuchen.', nur: ['web'] },
244
+ 'connect.drucker_antwortet_nicht': { text: 'Der Drucker antwortet nicht – Strom, Netzwerk und IP prüfen.', nur: ['web'] },
245
+ 'connect.nicht_gekoppelt': { text: 'Diese Kasse ist mit Kasseneck Connect nicht gekoppelt – in den Einstellungen „Koppeln“ drücken.', nur: ['web'] },
246
+ 'connect.drucker_unbekannt': { text: 'Diesen Drucker kennt Kasseneck Connect nicht (mehr) – bitte neu suchen.', nur: ['web'] },
160
247
  'connect.ursprung_nicht_freigegeben': { text: 'Kasseneck Connect nimmt von dieser Adresse nichts an (Ursprung nicht freigegeben).', nur: ['web'] },
161
- 'connect.nicht_installiert': { text: 'Kasseneck Connect nicht gefunden — bitte installieren.', nur: ['web'] },
248
+ 'connect.nicht_installiert': { text: 'Kasseneck Connect nicht gefunden – bitte installieren.', nur: ['web'] },
249
+ 'connect.entkoppeln_frage': { text: 'Diesen Browser wirklich von Connect trennen? Der Bondruck geht dann nicht mehr.', nur: ['web'] },
162
250
  // --- Einstellungen -------------------------------------------------------
163
251
  'einstellungen.laden_fehlgeschlagen': { text: 'Die Einstellungen konnten nicht geladen werden.' },
164
252
  'einstellungen.speichern_fehlgeschlagen': { text: 'Die Einstellung konnte nicht gespeichert werden.' },
165
- 'einstellungen.noch_nicht_geladen': { text: 'Noch nicht geladen — bitte kurz warten oder neu anmelden.' },
253
+ 'einstellungen.noch_nicht_geladen': { text: 'Noch nicht geladen – bitte kurz warten oder neu anmelden.' },
166
254
  'einstellungen.ip_ungueltig': { text: 'Keine gültige IP-Adresse (z. B. 192.168.1.50).' },
167
255
  'entkopplung.fehlgeschlagen': { text: 'Entkoppeln fehlgeschlagen.' },
168
256
  'logo.hochladen_fehlgeschlagen': { text: 'Hochladen fehlgeschlagen.' },
169
257
  'logo.entfernen_fehlgeschlagen': { text: 'Entfernen fehlgeschlagen.' },
170
258
  // --- Nur App -------------------------------------------------------------
171
- 'app.nicht_im_browser': { text: 'Die Kassen-App läuft nicht im Browser — dafür gibt es kasse.kasseneck.at.', nur: ['app'] },
259
+ 'app.nicht_im_browser': { text: 'Die Kassen-App läuft nicht im Browser – dafür gibt es kasse.kasseneck.at.', nur: ['app'] },
172
260
  'app.im_browser_oeffnen': { text: 'Bitte im Browser öffnen: {ziel}', platzhalter: ['ziel'], nur: ['app'] },
173
261
  };
174
262
  // Auf den gemeinsamen Typ gebracht: `Object.entries(MELDUNGEN)` liefert sonst
@@ -227,6 +315,148 @@ function belegMailFehler(code) {
227
315
  const fehler = exports.BELEG_MAIL_FEHLER;
228
316
  return (code !== undefined && code !== null && fehler[code]) || 'beleg.mail_fehlgeschlagen';
229
317
  }
318
+ /**
319
+ * Storno eines Belegs mit mehreren Zahlungen, dazu der offene Ausgang
320
+ * (`STORNO_OUTCOME_UNKNOWN`, gilt fuer jedes Storno): welcher `code` des
321
+ * Backends welchen Satz bekommt. Dieselbe Idee wie BELEG_MAIL_FEHLER – beide Kassen
322
+ * entscheiden am Code, nie am Satz des Backends. Ein Code, der hier fehlt,
323
+ * geht den allgemeinen Weg der Storno-Fehler (`storno.fehlgeschlagen`).
324
+ */
325
+ exports.STORNO_ZAHLUNG_FEHLER = {
326
+ STORNO_PAYMENTS_REQUIRED: 'storno.zahlungen_fehlen',
327
+ STORNO_REFUND_EXCEEDS_PAYMENT: 'storno.rueckgabe_zu_hoch',
328
+ STORNO_REFUND_REFERENCE_REQUIRED: 'storno.rueckgabe_ohne_bezug',
329
+ STORNO_REFUND_REFERENCE_UNKNOWN: 'storno.rueckgabe_bezug_unbekannt',
330
+ PAYMENTS_SUM_MISMATCH: 'storno.summe_passt_nicht',
331
+ STORNO_OUTCOME_UNKNOWN: 'storno.ergebnis_unklar',
332
+ };
333
+ /**
334
+ * Der Satz zu einem `code` des Backends beim Storno mit mehreren Zahlungen.
335
+ * Einen Code, den dieses Paket noch nicht kennt, faengt der allgemeine Satz
336
+ * `storno.fehlgeschlagen` auf – wie bei `belegMailFehler`.
337
+ */
338
+ function stornoZahlungFehler(code) {
339
+ const fehler = exports.STORNO_ZAHLUNG_FEHLER;
340
+ return (code !== undefined && code !== null && fehler[code]) || 'storno.fehlgeschlagen';
341
+ }
342
+ /**
343
+ * Beschriftungen: Knoepfe, Ueberschriften, Zeilennamen – was kein Satz ist.
344
+ *
345
+ * Getrennt von MELDUNGEN, weil dort jeder Eintrag ein Satz ist (gross am
346
+ * Anfang, Satzzeichen am Schluss) und die Waechter beider Kassen daran
347
+ * Saetze erkennen. „Rest" oder „÷ {n}" sind keine Saetze, muessen aber in
348
+ * beiden Kassen gleich heissen. Dieselben Regeln sonst: Schluessel
349
+ * `bereich.name`, nie umgedeutet, Platzhalter exakt die im Text, `nur` nennt
350
+ * die Seite.
351
+ *
352
+ * Die Woerter der Zahlarten folgen dem Bon („Kartenzahlung", „Barzahlung",
353
+ * „Zahlung {n}", „davon Trinkgeld").
354
+ */
355
+ const BESCHRIFTUNGEN_ROH = {
356
+ // --- Allgemein -------------------------------------------------------------
357
+ 'allgemein.abbrechen': { text: 'Abbrechen' },
358
+ // Das X an einer Meldung nennt, WELCHE es ausblendet – bei mehreren
359
+ // Meldungen in der Ecke sonst fuer Vorlese-Programme lauter gleiche Knoepfe.
360
+ // `{meldung}` ist der Anfang des Satzes (gekuerzt), nicht der ganze.
361
+ 'meldung.ausblenden': { text: 'Meldung ausblenden: {meldung}', platzhalter: ['meldung'] },
362
+ 'meldung.warnung_ausblenden': { text: 'Verstanden – Warnung ausblenden: {meldung}', platzhalter: ['meldung'] },
363
+ // --- Kopplung und Abmelden -----------------------------------------------
364
+ 'kopplung.neu_koppeln': { text: 'Neu koppeln' },
365
+ 'abmelden.frage': { text: 'Wirklich abmelden?' },
366
+ 'abmelden.weiter_arbeiten': { text: 'Weiter arbeiten' },
367
+ 'geraet.entkoppeln': { text: 'Gerät entkoppeln' },
368
+ 'connect.entkoppeln_bestaetigen': { text: 'Entkoppeln bestätigen', nur: ['web'] },
369
+ // --- Kassieren -------------------------------------------------------------
370
+ // Dieselben Woerter im gewohnten Kassieren und je Zahlung bei „Getrennt“.
371
+ 'kassieren.trinkgeld': { text: 'Trinkgeld' },
372
+ 'kassieren.kein': { text: 'kein' },
373
+ 'kassieren.eigener_betrag': { text: 'Eigener Betrag' },
374
+ 'kassieren.passend': { text: 'passend' },
375
+ 'kassieren.gegeben_loeschen': { text: 'Gegeben löschen' },
376
+ 'kassieren.es_fehlen_noch': { text: 'Es fehlen noch' },
377
+ 'kassieren.rueckgeld': { text: 'Rückgeld' },
378
+ // Die Warte-Karte, solange der Betrag am Terminal steht; `{zeit}` ist der
379
+ // Rest des Kartenfensters als m:ss.
380
+ 'kartenzahlung.betrag_am_terminal': { text: 'Betrag steht am Terminal' },
381
+ 'kartenzahlung.karte_vorhalten': { text: 'Karte vorhalten oder stecken · noch {zeit}', platzhalter: ['zeit'] },
382
+ // --- Abschluss -------------------------------------------------------------
383
+ 'abschluss.beleg_vorhanden': { text: 'Beleg ist vorhanden – erledigen' },
384
+ 'abschluss.erledigen': { text: 'Erledigen' },
385
+ // --- Getrennt zahlen -----------------------------------------------------
386
+ 'getrennt.knopf': { text: 'Getrennt' },
387
+ 'getrennt.einstellung': { text: 'Getrennt zahlen' },
388
+ 'getrennt.zu_zahlen': { text: 'Zu zahlen' },
389
+ 'getrennt.offen': { text: 'Offen' },
390
+ 'getrennt.zahlung': { text: 'Zahlung {n}', platzhalter: ['n'] },
391
+ 'getrennt.betrag': { text: 'Betrag' },
392
+ 'getrennt.rest': { text: 'Rest' },
393
+ 'getrennt.teilen': { text: '÷ {n}', platzhalter: ['n'] },
394
+ 'getrennt.trinkgeld_bezug': { text: '% von diesem Betrag' },
395
+ 'getrennt.davon_trinkgeld': { text: 'davon Trinkgeld {betrag}', platzhalter: ['betrag'] },
396
+ 'getrennt.entfernen': { text: 'Entfernen' },
397
+ 'getrennt.alles_abbrechen': { text: 'Alles abbrechen' },
398
+ 'getrennt.weiter_kassieren': { text: 'Weiter kassieren' },
399
+ 'getrennt.alles_zurueckbuchen': { text: 'Alles zurückbuchen' },
400
+ 'getrennt.am_terminal_zurueckgebucht': { text: 'Am Terminal zurückgebucht' },
401
+ 'getrennt.karte_zurueckbuchen': { text: 'Karte zurückbuchen' },
402
+ 'getrennt.trotzdem_neu_koppeln': { text: 'Trotzdem neu koppeln' },
403
+ 'getrennt.zahlung_behalten': { text: 'Zahlung behalten' },
404
+ 'getrennt.klaeren': { text: 'Klären' },
405
+ 'getrennt.erneut_zurueckbuchen': { text: 'Erneut zurückbuchen' },
406
+ 'getrennt.wurde_belastet': { text: 'Wurde belastet – übernehmen' },
407
+ 'getrennt.nicht_belastet': { text: 'Nicht belastet – verwerfen' },
408
+ // Aufteilung: eigener Schritt nach „Weiter“, Tabs „Nach Positionen“ / „Betrag“.
409
+ 'getrennt.weiter': { text: 'Weiter · {betrag} getrennt', platzhalter: ['betrag'] },
410
+ 'getrennt.aufteilung': { text: 'Aufteilung' },
411
+ 'getrennt.zurueck_zahlart': { text: 'Zurück zur Zahlart' },
412
+ 'getrennt.tab_positionen': { text: 'Nach Positionen' },
413
+ 'getrennt.tab_betrag': { text: 'Betrag' },
414
+ 'getrennt.stueck_weniger': { text: '{name}: ein Stück weniger', platzhalter: ['name'] },
415
+ 'getrennt.stueck_mehr': { text: '{name}: ein Stück mehr', platzhalter: ['name'] },
416
+ 'getrennt.gegeben': { text: 'Gegeben (bar)' },
417
+ 'getrennt.gegeben_rueckgeld': { text: 'Gegeben {gegeben} · Rückgeld {rueckgeld}', platzhalter: ['gegeben', 'rueckgeld'] },
418
+ // Je Zahlung oben die Zahlart (Umschalter, kein Ausloeser), unten EIN Knopf,
419
+ // der diese Zahlung mit der gewaehlten Zahlart kassiert.
420
+ 'getrennt.zahlart': { text: 'Zahlart' },
421
+ 'getrennt.art_bar': { text: 'Bar' },
422
+ 'getrennt.art_karte': { text: 'Karte' },
423
+ 'getrennt.zahlung_hinzufuegen': { text: 'Zahlung hinzufügen · {betrag}', platzhalter: ['betrag'] },
424
+ // Nach Positionen: die offenen Stueck als Kacheln in der grossen Flaeche;
425
+ // Antippen nimmt ein Stueck in diese Zahlung, „−“ an der Kachel eines heraus.
426
+ 'getrennt.offene_positionen': { text: 'Offene Positionen' },
427
+ 'getrennt.stueck_offen': { text: '{n} offen', platzhalter: ['n'] },
428
+ 'getrennt.stueck_gewaehlt': { text: '{n} von {offen}', platzhalter: ['n', 'offen'] },
429
+ 'getrennt.nichts_gewaehlt': { text: 'Noch nichts angetippt' },
430
+ 'getrennt.alles_bezahlt': { text: 'Alles bezahlt' },
431
+ // --- Zahlarten in Listen (Kassieren, Storno, Belegliste) -----------------
432
+ 'zahlart.kartenzahlung': { text: 'Kartenzahlung' },
433
+ 'zahlart.barzahlung': { text: 'Barzahlung' },
434
+ // Beleg mit mehreren Zahlungen (`paymentMethod: 'mixed'` oder mehr als eine
435
+ // Zahlung): weder Bar noch Karte, auch nicht im Filter.
436
+ 'zahlart.mehrere': { text: 'Mehrere' },
437
+ // --- Storno ----------------------------------------------------------------
438
+ 'storno.titel': { text: 'Storno zu {beleg}', platzhalter: ['beleg'] },
439
+ // --- Storno eines Belegs mit mehreren Zahlungen --------------------------
440
+ 'storno.wie_zurueckgeben': { text: 'Wie zurückgeben?' },
441
+ 'storno.wie_bezahlt': { text: 'wie bezahlt' },
442
+ 'storno.bar': { text: 'bar' },
443
+ 'storno.alles_bar': { text: 'Alles bar' },
444
+ 'storno.rest_der_zahlung': { text: 'Rest {betrag}', platzhalter: ['betrag'] },
445
+ 'storno.differenz': { text: 'Differenz' },
446
+ };
447
+ exports.BESCHRIFTUNGEN = BESCHRIFTUNGEN_ROH;
448
+ /** Die Beschriftung zum Schluessel, Platzhalter ersetzt; fehlt ein Wert, wirft es wie `meldung`. */
449
+ function beschriftung(schluessel, werte = {}) {
450
+ return ersetze(`beschriftung(${schluessel})`, exports.BESCHRIFTUNGEN[schluessel].text, werte);
451
+ }
452
+ function ersetze(wo, text, werte) {
453
+ return text.replace(/\{([a-z]+)\}/g, (_, name) => {
454
+ const wert = werte[name];
455
+ if (wert === undefined)
456
+ throw new Error(`${wo}: Platzhalter {${name}} ohne Wert`);
457
+ return String(wert);
458
+ });
459
+ }
230
460
  /** Der Satz zum Schluessel, Platzhalter ersetzt. Fehlt ein Wert, wirft es — ein `{status}` am Tresen waere schlimmer. */
231
461
  function meldung(schluessel, werte = {}) {
232
462
  const eintrag = exports.MELDUNGEN[schluessel];
@@ -20,7 +20,7 @@ export type CancellationReason = keyof typeof CANCELLATION_REASONS;
20
20
  export declare function isCancellationReason(value: unknown): value is CancellationReason;
21
21
  /**
22
22
  * Stabile Fehlercodes von `cancelReceipt` — Zwilling von
23
- * `functions/storno-core.js` STORNO_FEHLERCODES. Das Backend legt sie bei
23
+ * `functions/gemeinsam/storno-core.js` STORNO_FEHLERCODES. Das Backend legt sie bei
24
24
  * jedem fachlichen Fehler als `code` neben die Meldung; das Paket reicht sie
25
25
  * als [KasseneckApiError.code] durch. **Entscheide am Code, nie am Text** —
26
26
  * die deutsche Meldung darf sich aendern, der Code nicht.
@@ -28,9 +28,12 @@ export declare function isCancellationReason(value: unknown): value is Cancellat
28
28
  * Nur Auth-/Parameterfehler (Sitzung abgelaufen, Pflichtfeld fehlt) kommen
29
29
  * ohne Code; dort bleibt `code` undefined.
30
30
  *
31
- * Die vier `STORNO_…`-Codes am Ende gehoeren zur Rueckzahlung je Zahlung
32
- * (mehrere Zahlungen je Beleg); Formfehler an `payments` selbst melden die
33
- * Codes aus [PAYMENT_ERROR_CODES].
31
+ * Die vier `STORNO_PAYMENTS_…`/`STORNO_REFUND_…`-Codes gehoeren zur
32
+ * Rueckzahlung je Zahlung (mehrere Zahlungen je Beleg); Formfehler an
33
+ * `payments` selbst melden die Codes aus [PAYMENT_ERROR_CODES].
34
+ * `STORNO_OUTCOME_UNKNOWN` heisst: der Storno-Beleg kann schon signiert sein,
35
+ * die Reservierung bleibt offen – nicht erneut stornieren, die Belegliste
36
+ * nach ein paar Minuten neu laden (unter `/v3`: `cancellation_outcome_unknown`).
34
37
  *
35
38
  * Die Werte hier sind die von `/v1` und intern. Unter `/v3` heissen die
36
39
  * Storno-Codes nach dem /v3-Vokabular anders (`already_cancelled`,
@@ -38,7 +41,7 @@ export declare function isCancellationReason(value: unknown): value is Cancellat
38
41
  * diese Namen kommen mit der Umstellung des Pakets auf `/v3`.
39
42
  * [isCancellationErrorCode] prueft darum exakt.
40
43
  */
41
- export declare const CANCELLATION_ERROR_CODES: readonly ["beleg_nicht_gefunden", "belegart_nicht_stornierbar", "trainingsbeleg", "bereits_storniert", "position_ungueltig", "menge_ueber_rest", "grund_unbekannt", "anmerkung_zu_lang", "items_ungueltig", "kasse_nicht_zugewiesen", "keine_berechtigung", "nur_eigene_belege", "kasse_unvollstaendig", "storno_fehlgeschlagen", "STORNO_PAYMENTS_REQUIRED", "STORNO_REFUND_EXCEEDS_PAYMENT", "STORNO_REFUND_REFERENCE_REQUIRED", "STORNO_REFUND_REFERENCE_UNKNOWN"];
44
+ export declare const CANCELLATION_ERROR_CODES: readonly ["beleg_nicht_gefunden", "belegart_nicht_stornierbar", "trainingsbeleg", "bereits_storniert", "position_ungueltig", "menge_ueber_rest", "grund_unbekannt", "anmerkung_zu_lang", "items_ungueltig", "kasse_nicht_zugewiesen", "keine_berechtigung", "nur_eigene_belege", "kasse_unvollstaendig", "storno_fehlgeschlagen", "STORNO_PAYMENTS_REQUIRED", "STORNO_REFUND_EXCEEDS_PAYMENT", "STORNO_REFUND_REFERENCE_REQUIRED", "STORNO_REFUND_REFERENCE_UNKNOWN", "STORNO_OUTCOME_UNKNOWN"];
42
45
  export type CancellationErrorCode = (typeof CANCELLATION_ERROR_CODES)[number];
43
46
  export declare function isCancellationErrorCode(value: unknown): value is CancellationErrorCode;
44
47
  /** Bezug eines Storno-Belegs auf sein Original. */
@@ -78,6 +81,16 @@ export interface Cancellation {
78
81
  * nichts gewaehrt (Altbestand vor dieser Regel) — der naechste Storno holt nach.
79
82
  */
80
83
  promoAdjustmentCents?: Record<string, number>;
84
+ /**
85
+ * Rueckzahlung je Zahlung, die DIESER Storno-Eintrag auf eine Zahlung des
86
+ * Originals verbucht hat (`refundOf`) -- Zahlungs-ID auf positive Cent,
87
+ * Zwilling von `functions/gemeinsam/storno-core.js` `refundedByPayment`.
88
+ * Ueber alle Eintraege darf je Zahlung nie mehr zurueck, als bezahlt wurde
89
+ * (siehe dort `restJeZahlung`). Ein Eintrag ohne das Feld (vor dieser Regel
90
+ * oder ohne Zahlungsmodell gebucht) zaehlt dabei 0 je Zahlung. Auch am
91
+ * `pending`-Eintrag vorhanden, sobald reserviert wird.
92
+ */
93
+ refundedByPayment?: Record<string, number>;
81
94
  }
82
95
  /** Ab wann eine liegengebliebene Reservierung nicht mehr zaehlt (wie im Backend). */
83
96
  export declare const CANCELLATION_RESERVATION_MS = 120000;
@@ -26,7 +26,7 @@ function isCancellationReason(value) {
26
26
  }
27
27
  /**
28
28
  * Stabile Fehlercodes von `cancelReceipt` — Zwilling von
29
- * `functions/storno-core.js` STORNO_FEHLERCODES. Das Backend legt sie bei
29
+ * `functions/gemeinsam/storno-core.js` STORNO_FEHLERCODES. Das Backend legt sie bei
30
30
  * jedem fachlichen Fehler als `code` neben die Meldung; das Paket reicht sie
31
31
  * als [KasseneckApiError.code] durch. **Entscheide am Code, nie am Text** —
32
32
  * die deutsche Meldung darf sich aendern, der Code nicht.
@@ -34,9 +34,12 @@ function isCancellationReason(value) {
34
34
  * Nur Auth-/Parameterfehler (Sitzung abgelaufen, Pflichtfeld fehlt) kommen
35
35
  * ohne Code; dort bleibt `code` undefined.
36
36
  *
37
- * Die vier `STORNO_…`-Codes am Ende gehoeren zur Rueckzahlung je Zahlung
38
- * (mehrere Zahlungen je Beleg); Formfehler an `payments` selbst melden die
39
- * Codes aus [PAYMENT_ERROR_CODES].
37
+ * Die vier `STORNO_PAYMENTS_…`/`STORNO_REFUND_…`-Codes gehoeren zur
38
+ * Rueckzahlung je Zahlung (mehrere Zahlungen je Beleg); Formfehler an
39
+ * `payments` selbst melden die Codes aus [PAYMENT_ERROR_CODES].
40
+ * `STORNO_OUTCOME_UNKNOWN` heisst: der Storno-Beleg kann schon signiert sein,
41
+ * die Reservierung bleibt offen – nicht erneut stornieren, die Belegliste
42
+ * nach ein paar Minuten neu laden (unter `/v3`: `cancellation_outcome_unknown`).
40
43
  *
41
44
  * Die Werte hier sind die von `/v1` und intern. Unter `/v3` heissen die
42
45
  * Storno-Codes nach dem /v3-Vokabular anders (`already_cancelled`,
@@ -64,6 +67,7 @@ exports.CANCELLATION_ERROR_CODES = [
64
67
  'STORNO_REFUND_EXCEEDS_PAYMENT', // Rueckzahlungen auf eine Zahlung uebersteigen deren Rest
65
68
  'STORNO_REFUND_REFERENCE_REQUIRED', // Karten-Rueckzahlung ohne refundOf einer Kartenzahlung
66
69
  'STORNO_REFUND_REFERENCE_UNKNOWN', // refundOf nennt keine Zahlung des Originals
70
+ 'STORNO_OUTCOME_UNKNOWN', // Ausgang offen: Storno-Beleg evtl. schon signiert, nicht wiederholen
67
71
  ];
68
72
  function isCancellationErrorCode(value) {
69
73
  return typeof value === 'string' && exports.CANCELLATION_ERROR_CODES.includes(value);
@@ -24,7 +24,7 @@ exports.PAYMENT_ERROR_CODES = Object.freeze([
24
24
  'PAYMENTS_INVALID', // payments ist keine Liste oder hat mehr als 20 Eintraege
25
25
  'PAYMENT_METHOD_INVALID', // Zahlart unbekannt oder mixed
26
26
  'PAYMENT_AMOUNT_INVALID', // amountCents keine Ganzzahl, 0 oder falsches Vorzeichen
27
- 'PAYMENT_TENDERED_INVALID', // tenderedCents an Nicht-Bar-Zahlung, zu klein oder doppelt
27
+ 'PAYMENT_TENDERED_INVALID', // tenderedCents an Nicht-Bar-Zahlung, zu klein oder am Storno
28
28
  'PAYMENT_PROVIDER_INVALID', // Karte ohne/mit unbekanntem provider, providerPaymentId fehlt
29
29
  'PAYMENT_PROVIDER_NOT_ALLOWED', // Anbieterfelder an einer Zahlung, die weder Karte noch online ist
30
30
  'PAYMENTS_SUM_MISMATCH', // Summe != Zahlbetrag (Antwort nennt data.expectedCents)
@@ -57,7 +57,8 @@ export interface ReceiptPaymentPayload {
57
57
  *
58
58
  * - `method`: jede Zahlungsart ausser `mixed`.
59
59
  * - `amountCents`: ganze Cent, am Verkauf > 0, am Storno < 0.
60
- * - `tenderedCents`: nur bei Barzahlung am Verkauf, hoechstens an einer Zahlung.
60
+ * - `tenderedCents`: nur bei Barzahlung am Verkauf; jede Barzahlung darf ihren
61
+ * eigenen gegebenen Betrag tragen (getrennt zahlen: Rueckgeld je Gast).
61
62
  * - `provider`/`providerPaymentId`/`providerData`: bei Karten Pflicht
62
63
  * (`providerPaymentId` ausser bei `custom`), bei `online` optional, sonst
63
64
  * nicht erlaubt; am Storno beschreiben sie die Erstattung und sind optional.
@@ -100,8 +100,24 @@ function leseStorno(eintrag) {
100
100
  ...(eintrag.promoAdjustmentCents && typeof eintrag.promoAdjustmentCents === 'object'
101
101
  ? { promoAdjustmentCents: Object.fromEntries(Object.entries(eintrag.promoAdjustmentCents).map(([k, v]) => [k, Number(v)])) }
102
102
  : {}),
103
+ ...(eintrag.refundedByPayment && typeof eintrag.refundedByPayment === 'object' && !Array.isArray(eintrag.refundedByPayment)
104
+ ? { refundedByPayment: gueltigeRueckzahlungen(eintrag.refundedByPayment) }
105
+ : {}),
103
106
  };
104
107
  }
108
+ // Nur ganzzahlige, nicht-negative Cent-Betraege je Zahlungs-ID; ein Eintrag,
109
+ // der das nicht erfuellt (negativ, Kommazahl, kein Zahlenwert), faellt weg --
110
+ // die uebrigen bleiben. Zwilling der Backend-Erzeugung (storno-core.js
111
+ // pruefeRueckzahlungen), die selbst nur so etwas ablegt.
112
+ function gueltigeRueckzahlungen(rueckzahlungen) {
113
+ const raus = {};
114
+ for (const [id, wert] of Object.entries(rueckzahlungen)) {
115
+ const cents = Number(wert);
116
+ if (Number.isInteger(cents) && cents >= 0)
117
+ raus[id] = cents;
118
+ }
119
+ return raus;
120
+ }
105
121
  /**
106
122
  * Zwischensumme in **Cent** — Positionen plus verkaufte Wertgutscheine minus
107
123
  * eingeloeste Promotionsgutscheine. Wertgutschein-Einloesungen wirken erst auf