@kreiseck/kasseneck-api 1.0.0-rc.4 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/CHANGELOG.md +77 -0
  2. package/README.md +40 -4
  3. package/dist/cjs/index.d.ts +1 -1
  4. package/dist/cjs/index.js +4 -1
  5. package/dist/cjs/pos/index.d.ts +1 -1
  6. package/dist/cjs/pos/index.js +6 -1
  7. package/dist/cjs/pos/texte.d.ts +95 -2
  8. package/dist/cjs/pos/texte.js +111 -3
  9. package/dist/cjs/receipt/due.d.ts +33 -0
  10. package/dist/cjs/receipt/due.js +61 -15
  11. package/dist/cjs/receipt/index.d.ts +1 -1
  12. package/dist/cjs/receipt/index.js +4 -1
  13. package/dist/cjs/stored/index.d.ts +1 -0
  14. package/dist/cjs/stored/index.js +7 -0
  15. package/dist/cjs/stored/layout.d.ts +55 -0
  16. package/dist/cjs/stored/layout.js +119 -0
  17. package/dist/cjs/version.d.ts +1 -1
  18. package/dist/cjs/version.js +1 -1
  19. package/dist/esm/index.d.ts +1 -1
  20. package/dist/esm/index.js +1 -1
  21. package/dist/esm/pos/index.d.ts +1 -1
  22. package/dist/esm/pos/index.js +1 -1
  23. package/dist/esm/pos/texte.d.ts +95 -2
  24. package/dist/esm/pos/texte.js +108 -2
  25. package/dist/esm/receipt/due.d.ts +33 -0
  26. package/dist/esm/receipt/due.js +58 -15
  27. package/dist/esm/receipt/index.d.ts +1 -1
  28. package/dist/esm/receipt/index.js +1 -1
  29. package/dist/esm/stored/index.d.ts +1 -0
  30. package/dist/esm/stored/index.js +2 -0
  31. package/dist/esm/stored/layout.d.ts +55 -0
  32. package/dist/esm/stored/layout.js +113 -0
  33. package/dist/esm/version.d.ts +1 -1
  34. package/dist/esm/version.js +1 -1
  35. package/fixtures/hobex-hps-codes.json +1 -1
  36. package/fixtures/invoice-api.schema.json +1 -1
  37. package/fixtures/invoice-texts.json +1 -1
  38. package/fixtures/pos-message-cases.json +31 -1
  39. package/fixtures/pos-texts.json +68 -1
  40. package/fixtures/receipt-due-errors.json +56 -0
  41. package/fixtures/surface.json +11 -1
  42. package/fixtures/v3/antworten/belege.json +1 -1
  43. package/fixtures/v3/antworten/belegmail.json +1 -1
  44. package/fixtures/v3/antworten/kasse-belege.json +1 -1
  45. package/fixtures/v3/antworten/kasse.json +2 -2
  46. package/fixtures/v3/antworten/rechnungen.json +1 -1
  47. package/fixtures/v3/antworten/storno.json +1 -1
  48. package/fixtures/v3/stored/belege.json +1 -1
  49. package/fixtures/v3/stored/kasse.json +2 -2
  50. package/fixtures/v3/stored/rechnungen.json +1 -1
  51. package/fixtures/v3/v3-vokabular.json +1 -1
  52. package/fixtures/v3/zahlbetrag-faelle.json +1 -1
  53. package/package.json +1 -1
@@ -1,8 +1,54 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ReceiptDueError = exports.RECEIPT_DUE_ERROR_REASONS = void 0;
4
+ exports.isReceiptDueError = isReceiptDueError;
3
5
  exports.receiptDueCents = receiptDueCents;
4
6
  exports.receiptDueBreakdown = receiptDueBreakdown;
5
7
  const index_js_1 = require("../enums/index.js");
8
+ /**
9
+ * Warum sich der Zahlbetrag nicht rechnen laesst. Fest wie ein Backend-Code;
10
+ * dieselbe Liste im Dart-Zwilling und in `fixtures/receipt-due-errors.json`.
11
+ */
12
+ exports.RECEIPT_DUE_ERROR_REASONS = Object.freeze([
13
+ 'unknown_receipt_type',
14
+ 'tip_not_allowed',
15
+ 'invalid_item',
16
+ 'invalid_voucher',
17
+ 'invalid_tip',
18
+ 'unknown_payment_method',
19
+ 'tip_conflict',
20
+ 'tip_recipient_missing',
21
+ 'tip_without_goods',
22
+ ]);
23
+ /**
24
+ * Der Zahlbetrag laesst sich aus dieser Eingabe nicht rechnen, etwa
25
+ * Trinkgeld mit Betrag ohne Ware (`reason: 'tip_without_goods'`).
26
+ *
27
+ * Eindeutig **nicht gesendet**: die Rechnung laeuft ganz im Paket, vor jedem
28
+ * Aufruf. `outcome` ist darum immer `'rejected'`; nichts ist geschehen, keine
29
+ * Belegnummer verbraucht, keine Karte belastet. Die Kasse sagt das dem
30
+ * Kassier, bevor sie ein Terminal anspricht. Entscheiden am `code` bzw.
31
+ * `reason`, nie an `message` (die ist fuers Protokoll).
32
+ *
33
+ * Bis 1.0.0-rc.4 warf die Rechnung hier einen `RangeError` ohne Code.
34
+ */
35
+ class ReceiptDueError extends Error {
36
+ name = 'ReceiptDueError';
37
+ /** Stabiler Code fuer jede Ursache. */
38
+ code = 'receipt_due_unavailable';
39
+ /** Die Ursache im Einzelnen. */
40
+ reason;
41
+ /** Immer `'rejected'`: es ging nichts hinaus. */
42
+ outcome = 'rejected';
43
+ constructor(reason, message) {
44
+ super(`Zahlbetrag: ${message}`);
45
+ this.reason = reason;
46
+ }
47
+ }
48
+ exports.ReceiptDueError = ReceiptDueError;
49
+ function isReceiptDueError(error) {
50
+ return error instanceof ReceiptDueError;
51
+ }
6
52
  const UMSATZ = new Set(['standard', 'cancellation', 'training']);
7
53
  const TRINKGELD_ERLAUBT = new Set(['standard', 'training']);
8
54
  const ZAHLARTEN = new Set(['cash', 'creditCard', 'online', 'uberApp', 'uberCard', 'uberCash', 'boltApp', 'boltCard', 'boltCash']);
@@ -15,7 +61,7 @@ function receiptDueBreakdown(items, vouchers, receiptType, options = {}) {
15
61
  const art = belegArt(receiptType);
16
62
  const trinkgelder = trinkgeldAuftraege(options);
17
63
  if (trinkgelder.length > 0 && !TRINKGELD_ERLAUBT.has(art)) {
18
- throw new RangeError(`Zahlbetrag: Trinkgeld gibt es nur bei standard und training, nicht bei "${art}".`);
64
+ throw new ReceiptDueError('tip_not_allowed', `Trinkgeld gibt es nur bei standard und training, nicht bei "${art}".`);
19
65
  }
20
66
  if (!UMSATZ.has(art)) {
21
67
  return { dueCents: 0, counterDeltaCents: 0, valueVoucherFlowCents: 0, bucketsCents: null };
@@ -45,7 +91,7 @@ function receiptDueBreakdown(items, vouchers, receiptType, options = {}) {
45
91
  function belegArt(wert) {
46
92
  const text = typeof wert === 'object' && wert !== null ? wert.value : wert;
47
93
  if (typeof text !== 'string' || !Object.prototype.hasOwnProperty.call(index_js_1.ReceiptType, text)) {
48
- throw new RangeError(`Zahlbetrag: unbekannter Belegtyp "${String(text)}".`);
94
+ throw new ReceiptDueError('unknown_receipt_type', `unbekannter Belegtyp "${String(text)}".`);
49
95
  }
50
96
  return text;
51
97
  }
@@ -57,17 +103,17 @@ function euroToCent(euro) {
57
103
  function innen(item, i) {
58
104
  const satz = typeof item.vat === 'number' ? item.vat : item.vat?.rate;
59
105
  if (typeof satz !== 'number' || !Number.isFinite(satz))
60
- throw new RangeError(`Zahlbetrag: Position ${i + 1} ohne Steuersatz.`);
106
+ throw new ReceiptDueError('invalid_item', `Position ${i + 1} ohne Steuersatz.`);
61
107
  if (!Number.isSafeInteger(item.priceCents))
62
- throw new RangeError(`Zahlbetrag: Position ${i + 1} hat keinen ganzen Cent-Preis.`);
108
+ throw new ReceiptDueError('invalid_item', `Position ${i + 1} hat keinen ganzen Cent-Preis.`);
63
109
  if (typeof item.quantity !== 'number' || !Number.isFinite(item.quantity))
64
- throw new RangeError(`Zahlbetrag: Position ${i + 1} hat keine gueltige Menge.`);
110
+ throw new ReceiptDueError('invalid_item', `Position ${i + 1} hat keine gueltige Menge.`);
65
111
  const tip = item.kind === 'tip' ? (item.recipient?.owner === true ? 'owner' : 'staff') : null;
66
112
  return { amount: item.quantity, priceOne: Math.round(item.priceCents) / 100, vat: satz, tip };
67
113
  }
68
114
  function gutscheinInnen(v, i) {
69
115
  if (!Number.isSafeInteger(v.valueCents))
70
- throw new RangeError(`Zahlbetrag: Gutschein ${i + 1} hat keinen ganzen Cent-Wert.`);
116
+ throw new ReceiptDueError('invalid_voucher', `Gutschein ${i + 1} hat keinen ganzen Cent-Wert.`);
71
117
  return { action: String(v.action), type: String(v.type), value: Math.round(v.valueCents) / 100 };
72
118
  }
73
119
  /** Trinkgeld-Auftraege in der Reihenfolge des Servers: `tip`, dann je Zahlart die Summe der `tipCents`. */
@@ -75,7 +121,7 @@ function trinkgeldAuftraege(options) {
75
121
  const raus = [];
76
122
  const tipCents = (options.payments ?? []).some((z) => z != null && z.tipCents !== undefined);
77
123
  if (options.tip != null && tipCents) {
78
- throw new RangeError('Zahlbetrag: tip und payments[].tipCents gehen nicht zugleich (tip_conflict).');
124
+ throw new ReceiptDueError('tip_conflict', 'tip und payments[].tipCents gehen nicht zugleich (tip_conflict).');
79
125
  }
80
126
  if (options.tip != null) {
81
127
  const tip = options.tip;
@@ -84,14 +130,14 @@ function trinkgeldAuftraege(options) {
84
130
  const recipients = typeof tip === 'number' ? undefined : tip.recipients;
85
131
  if (recipients != null) {
86
132
  if (recipients.length === 0)
87
- throw new RangeError('Zahlbetrag: recipients darf nicht leer sein.');
133
+ throw new ReceiptDueError('invalid_tip', 'recipients darf nicht leer sein.');
88
134
  for (const r of recipients) {
89
135
  pruefeCent(r.cents, 'Trinkgeld-Anteil');
90
136
  if (typeof r.owner !== 'boolean')
91
- throw new RangeError('Zahlbetrag: recipients[].owner muss true oder false sein.');
137
+ throw new ReceiptDueError('invalid_tip', 'recipients[].owner muss true oder false sein.');
92
138
  }
93
139
  if (recipients.reduce((s, r) => s + r.cents, 0) !== cents) {
94
- throw new RangeError('Zahlbetrag: die Anteile des Trinkgelds ergeben nicht den Betrag.');
140
+ throw new ReceiptDueError('invalid_tip', 'die Anteile des Trinkgelds ergeben nicht den Betrag.');
95
141
  }
96
142
  raus.push({ empfaenger: recipients.map((r) => ({ cents: r.cents, owner: r.owner })) });
97
143
  }
@@ -107,7 +153,7 @@ function trinkgeldAuftraege(options) {
107
153
  pruefeCent(z.tipCents, 'tipCents');
108
154
  const methode = typeof z.method === 'object' && z.method !== null ? z.method.value : z.method;
109
155
  if (typeof methode !== 'string' || !ZAHLARTEN.has(methode))
110
- throw new RangeError(`Zahlbetrag: unbekannte Zahlart "${String(methode)}".`);
156
+ throw new ReceiptDueError('unknown_payment_method', `unbekannte Zahlart "${String(methode)}".`);
111
157
  je.set(methode, (je.get(methode) ?? 0) + z.tipCents);
112
158
  }
113
159
  for (const cents of je.values())
@@ -116,7 +162,7 @@ function trinkgeldAuftraege(options) {
116
162
  }
117
163
  function pruefeCent(wert, was) {
118
164
  if (typeof wert !== 'number' || !Number.isInteger(wert) || wert <= 0) {
119
- throw new RangeError(`Zahlbetrag: ${was} muss eine ganze Zahl in Cent > 0 sein.`);
165
+ throw new ReceiptDueError('invalid_tip', `${was} muss eine ganze Zahl in Cent > 0 sein.`);
120
166
  }
121
167
  }
122
168
  /** `tip-core.buildTipItems`: Personal in den Null-%-Satz, Inhaber anteilig auf die Saetze der Ware. */
@@ -124,13 +170,13 @@ function trinkgeldPositionen(auftrag, positionen, standard) {
124
170
  const basis = warenCentsJeSatz(positionen);
125
171
  const summe = Object.values(basis).reduce((s, c) => s + c, 0);
126
172
  if (summe <= 0)
127
- throw new RangeError('Zahlbetrag: Trinkgeld braucht mindestens eine Position mit Betrag.');
173
+ throw new ReceiptDueError('tip_without_goods', 'Trinkgeld braucht mindestens eine Position mit Betrag.');
128
174
  const raus = [];
129
175
  for (const e of auftrag.empfaenger) {
130
176
  let owner = e.owner;
131
177
  if (owner == null) {
132
178
  if (standard !== 'owner' && standard !== 'staff') {
133
- throw new RangeError("Zahlbetrag: tipRecipient fehlt ('owner' oder 'staff', wie der angemeldete Kassen-Benutzer).");
179
+ throw new ReceiptDueError('tip_recipient_missing', "tipRecipient fehlt ('owner' oder 'staff', wie der angemeldete Kassen-Benutzer).");
134
180
  }
135
181
  owner = standard === 'owner';
136
182
  }
@@ -140,7 +186,7 @@ function trinkgeldPositionen(auftrag, positionen, standard) {
140
186
  }
141
187
  const teile = splitOwnerTip(e.cents, basis);
142
188
  if (teile.length === 0)
143
- throw new RangeError('Zahlbetrag: Inhaber-Trinkgeld braucht mindestens eine Position mit Betrag.');
189
+ throw new ReceiptDueError('tip_without_goods', 'Inhaber-Trinkgeld braucht mindestens eine Position mit Betrag.');
144
190
  for (const teil of teile) {
145
191
  if (teil.cents !== 0)
146
192
  raus.push({ amount: 1, priceOne: teil.cents / 100, vat: teil.vat, tip: 'owner' });
@@ -6,4 +6,4 @@ export { type SheetLogoSize, type SheetLogo, type LogoDimensions, type SheetBloc
6
6
  export { rasterizeLogo } from './bild.js';
7
7
  export { type BrandMarkRaster, BRAND_MARK_RASTERS, BRAND_MARK_PATHS } from './marke-daten.js';
8
8
  export { brandMarkImage } from './marke.js';
9
- export { type ReceiptDueTip, type ReceiptDueTipRecipient, type ReceiptDueOptions, type ReceiptDueBuckets, type ReceiptDueBreakdown, receiptDueCents, receiptDueBreakdown, } from './due.js';
9
+ export { type ReceiptDueTip, type ReceiptDueTipRecipient, type ReceiptDueOptions, type ReceiptDueBuckets, type ReceiptDueBreakdown, type ReceiptDueErrorReason, receiptDueCents, receiptDueBreakdown, ReceiptDueError, RECEIPT_DUE_ERROR_REASONS, isReceiptDueError, } from './due.js';
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.receiptDueBreakdown = exports.receiptDueCents = exports.brandMarkImage = exports.BRAND_MARK_PATHS = exports.BRAND_MARK_RASTERS = exports.rasterizeLogo = exports.receiptSheet = exports.qrSheetWidthFraction = exports.logoRasterSize = exports.isLogoPixelSizeAllowed = exports.logoDimensions = exports.paperSizeForChars = exports.DOTS_PER_LINE = exports.DOTS_PER_CHAR = exports.LOGO_MAX_PIXELS = exports.SHEET_LOGO_SIZES = exports.eposDirectStatus = exports.eposDirectPrint = exports.eposParseResponse = exports.eposSoapEnvelope = exports.eposServiceUrl = exports.eposImageXml = exports.eposXmlEscape = exports.eposPrintXmlResult = exports.eposPrintXml = exports.EposConnectionError = exports.CHARS_PER_PAPER_SIZE = exports.gridToText = exports.gridColumnWidths = exports.renderReceiptGrid = exports.escPosLayoutResult = exports.escPosLayoutBytes = exports.assertLogoRaster = exports.formatCents = exports.SMALL_BUSINESS_NOTICE = exports.CURRENT_LAYOUT_RULESET = exports.receiptZdaText = exports.receiptIsSmallBusinessConsistent = exports.receiptAmountsAreZero = exports.receiptIsZero = exports.receiptSignatureIsTest = exports.receiptSignatureFailed = exports.buildReceiptLayout = void 0;
3
+ exports.isReceiptDueError = exports.RECEIPT_DUE_ERROR_REASONS = exports.ReceiptDueError = exports.receiptDueBreakdown = exports.receiptDueCents = exports.brandMarkImage = exports.BRAND_MARK_PATHS = exports.BRAND_MARK_RASTERS = exports.rasterizeLogo = exports.receiptSheet = exports.qrSheetWidthFraction = exports.logoRasterSize = exports.isLogoPixelSizeAllowed = exports.logoDimensions = exports.paperSizeForChars = exports.DOTS_PER_LINE = exports.DOTS_PER_CHAR = exports.LOGO_MAX_PIXELS = exports.SHEET_LOGO_SIZES = exports.eposDirectStatus = exports.eposDirectPrint = exports.eposParseResponse = exports.eposSoapEnvelope = exports.eposServiceUrl = exports.eposImageXml = exports.eposXmlEscape = exports.eposPrintXmlResult = exports.eposPrintXml = exports.EposConnectionError = exports.CHARS_PER_PAPER_SIZE = exports.gridToText = exports.gridColumnWidths = exports.renderReceiptGrid = exports.escPosLayoutResult = exports.escPosLayoutBytes = exports.assertLogoRaster = exports.formatCents = exports.SMALL_BUSINESS_NOTICE = exports.CURRENT_LAYOUT_RULESET = exports.receiptZdaText = exports.receiptIsSmallBusinessConsistent = exports.receiptAmountsAreZero = exports.receiptIsZero = exports.receiptSignatureIsTest = exports.receiptSignatureFailed = exports.buildReceiptLayout = void 0;
4
4
  var layout_js_1 = require("./layout.js");
5
5
  Object.defineProperty(exports, "buildReceiptLayout", { enumerable: true, get: function () { return layout_js_1.buildReceiptLayout; } });
6
6
  Object.defineProperty(exports, "receiptSignatureFailed", { enumerable: true, get: function () { return layout_js_1.receiptSignatureFailed; } });
@@ -53,3 +53,6 @@ Object.defineProperty(exports, "brandMarkImage", { enumerable: true, get: functi
53
53
  var due_js_1 = require("./due.js");
54
54
  Object.defineProperty(exports, "receiptDueCents", { enumerable: true, get: function () { return due_js_1.receiptDueCents; } });
55
55
  Object.defineProperty(exports, "receiptDueBreakdown", { enumerable: true, get: function () { return due_js_1.receiptDueBreakdown; } });
56
+ Object.defineProperty(exports, "ReceiptDueError", { enumerable: true, get: function () { return due_js_1.ReceiptDueError; } });
57
+ Object.defineProperty(exports, "RECEIPT_DUE_ERROR_REASONS", { enumerable: true, get: function () { return due_js_1.RECEIPT_DUE_ERROR_REASONS; } });
58
+ Object.defineProperty(exports, "isReceiptDueError", { enumerable: true, get: function () { return due_js_1.isReceiptDueError; } });
@@ -107,3 +107,4 @@ export declare function invalidStoredPosSettings(stored: {
107
107
  * weg.
108
108
  */
109
109
  export declare function fromStoredArticle(id: string, stored: unknown): PosArticle;
110
+ export { fromStoredLayout, toStoredLayout, fromStoredPrintLogo, toStoredPrintLogo } from './layout.js';
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toStoredPrintLogo = exports.fromStoredPrintLogo = exports.toStoredLayout = exports.fromStoredLayout = void 0;
3
4
  exports.fromStoredReceipt = fromStoredReceipt;
4
5
  exports.fromStoredReceiptWithCompany = fromStoredReceiptWithCompany;
5
6
  exports.fromStoredCompany = fromStoredCompany;
@@ -223,3 +224,9 @@ function invalidStoredPosSettings(stored) {
223
224
  function fromStoredArticle(id, stored) {
224
225
  return (0, artikel_js_1.fromPosArticlePayload)((0, draht_js_1._storedArticleToWire)(id, stored));
225
226
  }
227
+ // ---- Zeilenmodell und Drucklogo ----------------------------------------------
228
+ var layout_js_2 = require("./layout.js");
229
+ Object.defineProperty(exports, "fromStoredLayout", { enumerable: true, get: function () { return layout_js_2.fromStoredLayout; } });
230
+ Object.defineProperty(exports, "toStoredLayout", { enumerable: true, get: function () { return layout_js_2.toStoredLayout; } });
231
+ Object.defineProperty(exports, "fromStoredPrintLogo", { enumerable: true, get: function () { return layout_js_2.fromStoredPrintLogo; } });
232
+ Object.defineProperty(exports, "toStoredPrintLogo", { enumerable: true, get: function () { return layout_js_2.toStoredPrintLogo; } });
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Zeilenmodell und Drucklogo in der inneren Form (deutsch) <-> Form 1.0.
3
+ *
4
+ * Die innere Form ist die, die das Backend speichert und am Draht `/api`
5
+ * (Kanal intern) spricht, und die, in der 0.x das Zeilenmodell im Browser
6
+ * zwischengespeichert hat:
7
+ *
8
+ * - Zeilenmodell: `regelwerk` statt `ruleset`, Bannerzeilen mit `ton`
9
+ * (`belegart`, `warnung`) statt `tone` (`receipt_type`, `warning`);
10
+ * - Drucklogo (`createPrintJob`, Parameter `logo`): `stufe`, `pxBreite`,
11
+ * `pxHoehe`, `breite`, `hoehe`, `zeilen` (Base64, MSB zuerst) statt
12
+ * [PrintLogo] (`size`, `pixelWidth`, `pixelHeight`, `raster`).
13
+ *
14
+ * Dieselben Namen wie der Rand des Backends unter `/v3` (Vokabular
15
+ * `createPrintJob`/`getReceiptWithCompany`: `layout.ruleset` <-> `regelwerk`,
16
+ * `lines[].tone` <-> `ton`, Katalog `LAYOUT_TON`, `logo.scale` <-> `stufe` ...).
17
+ * Ein unbekannter Ton geht woertlich durch, wie ihn auch der Rand durchreicht.
18
+ */
19
+ import type { ReceiptLayout } from '../receipt/layout.js';
20
+ import type { PrintLogo } from '../receipt/layout-escpos.js';
21
+ /**
22
+ * Ein Zeilenmodell in der inneren Form (0.x-Zwischenspeicher, Antwort unter
23
+ * `/api`) als [ReceiptLayout]. Nimmt auch die Form 1.0 an und laesst sie
24
+ * unveraendert; so liest eine Funktion alte und neue Eintraege. Die
25
+ * Reihenfolge der Schluessel bleibt.
26
+ *
27
+ * `null`, wenn es kein ganzes Zeilenmodell ist: fehlt, kein Objekt, keine
28
+ * oder leere `lines`, eine Zeile, die kein Objekt ist, oder kein `paperSize`.
29
+ * Dann baut der Aufrufer neu (`receiptLayoutFromResult` aus Beleg, Firma und
30
+ * `testCashregister`/`testSignature`), und TESTKASSE und die Warnzeilen
31
+ * stehen wieder da. Ein halb lesbares Zeilenmodell gewaenne sonst immer.
32
+ */
33
+ export declare function fromStoredLayout(stored: unknown): ReceiptLayout | null;
34
+ /**
35
+ * Ein [ReceiptLayout] in der inneren Form, fuer einen Aufruf unter `/api`
36
+ * (`createPrintJob`, `renderBelegPdfAdmin`) oder einen Leser der 0.x-Form,
37
+ * in der Reihenfolge der Schluessel wie 0.31 (`lines`, `paperSize`,
38
+ * `regelwerk`, sofern die Eingabe sie so hat). Ohne diese Uebersetzung liest
39
+ * das Backend den Ton nicht, und der Rahmen der Warnzeilen (TESTKASSE,
40
+ * Sicherheitseinrichtung ausgefallen) und des Belegart-Aufdrucks fiele am
41
+ * Bon und im PDF weg.
42
+ */
43
+ export declare function toStoredLayout(layout: ReceiptLayout): Record<string, unknown>;
44
+ /**
45
+ * Ein [PrintLogo] in der inneren Form des Druckjobs (`createPrintJob` unter
46
+ * `/api`): dieselben Punkte, die das Paket unter `/v3` als `logo` schickt.
47
+ */
48
+ export declare function toStoredPrintLogo(logo: PrintLogo): Record<string, unknown>;
49
+ /**
50
+ * Ein Drucklogo in der inneren Form als [PrintLogo]. Die Zeilen sind die
51
+ * Rasterzeilen als Base64 (MSB zuerst, jede Zeile auf volle Bytes
52
+ * aufgefuellt). Wirft `KasseneckValidationError`, wenn Stufe, Masse oder
53
+ * Zeilen nicht zusammenpassen.
54
+ */
55
+ export declare function fromStoredPrintLogo(stored: unknown): PrintLogo;
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.fromStoredLayout = fromStoredLayout;
4
+ exports.toStoredLayout = toStoredLayout;
5
+ exports.toStoredPrintLogo = toStoredPrintLogo;
6
+ exports.fromStoredPrintLogo = fromStoredPrintLogo;
7
+ const escpos_js_1 = require("../printing/escpos.js");
8
+ const errors_js_1 = require("../client/errors.js");
9
+ const istObjekt = (w) => w !== null && typeof w === 'object' && !Array.isArray(w);
10
+ const TON_NACH_1_0 = { belegart: 'receipt_type', warnung: 'warning' };
11
+ const TON_NACH_INNEN = { receipt_type: 'belegart', warning: 'warnung' };
12
+ /**
13
+ * Ein Objekt mit umbenannten Schluesseln, an derselben Stelle: die
14
+ * Reihenfolge bleibt, damit ein gespeicherter Eintrag byte-gleich
15
+ * zurueckkommt (Hash- und Cache-Vergleiche). `werte` ersetzt den Wert eines
16
+ * (alten) Schluessels.
17
+ */
18
+ function umbenannt(o, namen, werte = {}) {
19
+ const raus = {};
20
+ for (const [k, w] of Object.entries(o)) {
21
+ const neu = namen[k] ?? k;
22
+ // Traegt die Eingabe beide Namen, gewinnt der schon neue.
23
+ if (neu !== k && Object.prototype.hasOwnProperty.call(o, neu))
24
+ continue;
25
+ raus[neu] = werte[k] ? werte[k](w) : w;
26
+ }
27
+ return raus;
28
+ }
29
+ const tonNach = (tabelle) => (w) => (typeof w === 'string' ? tabelle[w] ?? w : w);
30
+ /**
31
+ * Ein Zeilenmodell in der inneren Form (0.x-Zwischenspeicher, Antwort unter
32
+ * `/api`) als [ReceiptLayout]. Nimmt auch die Form 1.0 an und laesst sie
33
+ * unveraendert; so liest eine Funktion alte und neue Eintraege. Die
34
+ * Reihenfolge der Schluessel bleibt.
35
+ *
36
+ * `null`, wenn es kein ganzes Zeilenmodell ist: fehlt, kein Objekt, keine
37
+ * oder leere `lines`, eine Zeile, die kein Objekt ist, oder kein `paperSize`.
38
+ * Dann baut der Aufrufer neu (`receiptLayoutFromResult` aus Beleg, Firma und
39
+ * `testCashregister`/`testSignature`), und TESTKASSE und die Warnzeilen
40
+ * stehen wieder da. Ein halb lesbares Zeilenmodell gewaenne sonst immer.
41
+ */
42
+ function fromStoredLayout(stored) {
43
+ if (!istObjekt(stored) || !Array.isArray(stored.lines) || stored.lines.length === 0)
44
+ return null;
45
+ if (!stored.lines.every(istObjekt))
46
+ return null;
47
+ if (typeof stored.paperSize !== 'string' || stored.paperSize === '')
48
+ return null;
49
+ return umbenannt(stored, { regelwerk: 'ruleset' }, {
50
+ lines: (zeilen) => zeilen.map((z) => umbenannt(z, { ton: 'tone' }, { ton: tonNach(TON_NACH_1_0) })),
51
+ });
52
+ }
53
+ /**
54
+ * Ein [ReceiptLayout] in der inneren Form, fuer einen Aufruf unter `/api`
55
+ * (`createPrintJob`, `renderBelegPdfAdmin`) oder einen Leser der 0.x-Form,
56
+ * in der Reihenfolge der Schluessel wie 0.31 (`lines`, `paperSize`,
57
+ * `regelwerk`, sofern die Eingabe sie so hat). Ohne diese Uebersetzung liest
58
+ * das Backend den Ton nicht, und der Rahmen der Warnzeilen (TESTKASSE,
59
+ * Sicherheitseinrichtung ausgefallen) und des Belegart-Aufdrucks fiele am
60
+ * Bon und im PDF weg.
61
+ */
62
+ function toStoredLayout(layout) {
63
+ if (!istObjekt(layout) || !Array.isArray(layout.lines)) {
64
+ throw new errors_js_1.KasseneckValidationError('toStoredLayout', 'layout ohne lines', 'request');
65
+ }
66
+ return umbenannt(layout, { ruleset: 'regelwerk' }, {
67
+ lines: (zeilen) => zeilen.map((z) => (istObjekt(z) ? umbenannt(z, { tone: 'ton' }, { tone: tonNach(TON_NACH_INNEN) }) : z)),
68
+ });
69
+ }
70
+ /**
71
+ * Ein [PrintLogo] in der inneren Form des Druckjobs (`createPrintJob` unter
72
+ * `/api`): dieselben Punkte, die das Paket unter `/v3` als `logo` schickt.
73
+ */
74
+ function toStoredPrintLogo(logo) {
75
+ return {
76
+ stufe: logo.size, pxBreite: logo.pixelWidth, pxHoehe: logo.pixelHeight,
77
+ breite: logo.raster.width, hoehe: logo.raster.height, zeilen: (0, escpos_js_1.rasterRowsBase64)(logo.raster),
78
+ };
79
+ }
80
+ const STUFEN = new Set(['S', 'M', 'L', 'XL']);
81
+ const ganz = (w) => typeof w === 'number' && Number.isInteger(w) && w > 0;
82
+ /**
83
+ * Ein Drucklogo in der inneren Form als [PrintLogo]. Die Zeilen sind die
84
+ * Rasterzeilen als Base64 (MSB zuerst, jede Zeile auf volle Bytes
85
+ * aufgefuellt). Wirft `KasseneckValidationError`, wenn Stufe, Masse oder
86
+ * Zeilen nicht zusammenpassen.
87
+ */
88
+ function fromStoredPrintLogo(stored) {
89
+ const name = 'fromStoredPrintLogo';
90
+ if (!istObjekt(stored))
91
+ throw new errors_js_1.KasseneckValidationError(name, 'kein Logo-Objekt', 'request');
92
+ const { stufe, pxBreite, pxHoehe, breite, hoehe, zeilen } = stored;
93
+ if (typeof stufe !== 'string' || !STUFEN.has(stufe))
94
+ throw new errors_js_1.KasseneckValidationError(name, 'stufe ist nicht S, M, L oder XL', 'request');
95
+ if (!ganz(pxBreite) || !ganz(pxHoehe) || !ganz(breite) || !ganz(hoehe)) {
96
+ throw new errors_js_1.KasseneckValidationError(name, 'pxBreite, pxHoehe, breite und hoehe muessen ganze Zahlen > 0 sein', 'request');
97
+ }
98
+ if (typeof zeilen !== 'string')
99
+ throw new errors_js_1.KasseneckValidationError(name, 'zeilen fehlt', 'request');
100
+ let binaer;
101
+ try {
102
+ binaer = atob(zeilen);
103
+ }
104
+ catch {
105
+ throw new errors_js_1.KasseneckValidationError(name, 'zeilen ist kein Base64', 'request');
106
+ }
107
+ const byteJeZeile = Math.ceil(breite / 8);
108
+ if (binaer.length !== byteJeZeile * hoehe)
109
+ throw new errors_js_1.KasseneckValidationError(name, 'zeilen passen nicht zu breite und hoehe', 'request');
110
+ const dots = new Uint8Array(breite * hoehe);
111
+ for (let y = 0; y < hoehe; y++) {
112
+ for (let x = 0; x < breite; x++) {
113
+ if (binaer.charCodeAt(y * byteJeZeile + (x >> 3)) & (0x80 >> (x & 7)))
114
+ dots[y * breite + x] = 1;
115
+ }
116
+ }
117
+ const raster = { width: breite, height: hoehe, dots };
118
+ return { size: stufe, pixelWidth: pxBreite, pixelHeight: pxHoehe, raster };
119
+ }
@@ -4,4 +4,4 @@
4
4
  * Client-Version, Nachtrag §6). Ein Test haelt sie gleich mit `package.json`;
5
5
  * beim Anheben der Version beide Stellen aendern.
6
6
  */
7
- export declare const PACKAGE_VERSION = "1.0.0-rc.4";
7
+ export declare const PACKAGE_VERSION = "1.0.0";
@@ -7,4 +7,4 @@ exports.PACKAGE_VERSION = void 0;
7
7
  * Client-Version, Nachtrag §6). Ein Test haelt sie gleich mit `package.json`;
8
8
  * beim Anheben der Version beide Stellen aendern.
9
9
  */
10
- exports.PACKAGE_VERSION = '1.0.0-rc.4';
10
+ exports.PACKAGE_VERSION = '1.0.0';
@@ -1,6 +1,6 @@
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
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 RegistrationInfo, type ReceiptPayload, type ReceiptPayloadRead, toReceiptPayload, fromReceiptPayload, receiptSubSumCents, receiptSumCents, type CancellationOf, type CancellationItem, type Cancellation, type CancellationReason, type CancellationErrorCode, type CancellationStatus, CANCELLATION_REASONS, CANCELLATION_STATUSES, CANCELLATION_ERROR_CODES, CANCELLATION_RESERVATION_MS, isCancellationReason, isCancellationErrorCode, cancellationErrorCode, isCancellationError, cancellationFieldErrors, type CancellationFieldError, type ReceiptPayment, type ReceiptPaymentPayload, type ReceiptPaymentInput, fromReceiptPaymentPayload, toReceiptPaymentPayload, type PaymentErrorCode, PAYMENT_ERROR_CODES, isPaymentErrorCode, paymentErrorCode, isPaymentError, paymentFieldErrors, type PaymentFieldError, type ReceiptEmailErrorCode, type ReceiptEmailVia, RECEIPT_EMAIL_ERROR_CODES, RECEIPT_EMAIL_SEND_ERROR_CODES, type ReceiptEmailSendErrorCode, RECEIPT_EMAIL_VIAS, isReceiptEmailErrorCode, receiptEmailErrorCode, isReceiptEmailError, receiptEmailFieldErrors, type ReceiptEmailFieldError, type ReceiptErrorCode, type ReceiptFieldError, RECEIPT_ERROR_CODES, isReceiptErrorCode, receiptErrorCode, isReceiptError, receiptFieldErrors, 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, isZeroKind, 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
3
  export { type AuthCredentials, type KasseneckAuth, type ApiKeyAuthOptions, type RegisterUserAuthOptions, apiKeyAuth, registerUserAuth, DEFAULT_BASE_URL, POS_BASE_URL, DEFAULT_TIMEOUT_MS, type HttpResponseLike, type HttpRequestInit, type FetchLike, type TransportOptions, type TransportBodyFields, type KasseneckTransport, type KasseneckBinaryTransport, ALL_CALLS, type ApiCall, PUBLIC_CALLS, POS_CALLS, type PublicCall, type PosCall, createTransport, createBinaryTransport, KasseneckApiError, KasseneckAuthError, KasseneckHttpError, KasseneckNetworkError, KasseneckValidationError, type ValidationScope, type KasseneckError, type HttpFailureReason, type CauseDigest, type ErrorOutcome, CLIENT_ERROR_CODES, type ClientErrorCode, isKasseneckApiError, isKasseneckAuthError, isKasseneckHttpError, isKasseneckNetworkError, isKasseneckValidationError, isOutcomeUnknown, type ReceiptCommonOptions, type SellReceiptOptions, type TipOptions, type TipRecipientShare, type CancelReceiptOptions, type CancelReceiptResult, type ReceiptWithCompany, sellReceipt, sellReceiptWithCompany, paymentsExpectedCents, cardRefundReference, receiptLayoutFromResult, cancelReceipt, zeroReceipt, getReceipt, getReceiptWithCompany, generateFullReceiptId, getFirstReceiptDate, listMyReceipts, type ListMyReceiptsOptions, type ReceiptList, type ReceiptListStats, listMyCashregisters, checkVoucherCombinationError, type SendReceiptEmailOptions, type SendReceiptEmailResult, sendReceiptEmail, downloadDailyReport, downloadMonthlyReport, getReportV2, type ReportV2, type ReportV2Metadata, type ReportV2Options, type CashboxStatus, type SignatureStatus, getCashboxStatus, getSignatureStatus, type KasseneckApi, createKasseneckApi, } from './client/index.js';
4
- export { type ReceiptDueTip, type ReceiptDueTipRecipient, type ReceiptDueOptions, type ReceiptDueBuckets, type ReceiptDueBreakdown, receiptDueCents, receiptDueBreakdown, } from './receipt/due.js';
4
+ export { type ReceiptDueTip, type ReceiptDueTipRecipient, type ReceiptDueOptions, type ReceiptDueBuckets, type ReceiptDueBreakdown, type ReceiptDueErrorReason, receiptDueCents, receiptDueBreakdown, ReceiptDueError, RECEIPT_DUE_ERROR_REASONS, isReceiptDueError, } from './receipt/due.js';
5
5
  export { parseServerTimeStamp, toViennaWallClock, type ViennaWallClock } from './vienna-time.js';
6
6
  export { type PosBusinessSettings, type PosDeviceSettings, type PosSettings, type ArticleGroup, type PosArticle, POS_BUSINESS_DEFAULTS, POS_DEVICE_DEFAULTS, mergePosSettings, fromArticleGroupPayload, fromPosArticlePayload, listMyArticleGroups, listMyArticles, getPosSettings, setMyPosSettings, setMyRegisterDeviceSettings, distributeDiscount, cancelScopeOf, receiptsScopeOf, } from './pos/index.js';
package/dist/esm/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export { ReceiptType, VatRate, KeckPaymentMethod, CreditCardProvider, VoucherType, VoucherAction, StripeLinkMode, } from './enums/index.js';
2
2
  export { toReceiptItemPayload, fromReceiptItemPayload, receiptItemTotalCents, receiptItemIsValid, negateReceiptItem, isTipItem, toVoucherPayload, fromVoucherPayload, voucherIsValid, toReceiptPayload, fromReceiptPayload, receiptSubSumCents, receiptSumCents, CANCELLATION_REASONS, CANCELLATION_STATUSES, CANCELLATION_ERROR_CODES, CANCELLATION_RESERVATION_MS, isCancellationReason, isCancellationErrorCode, cancellationErrorCode, isCancellationError, cancellationFieldErrors, fromReceiptPaymentPayload, toReceiptPaymentPayload, PAYMENT_ERROR_CODES, isPaymentErrorCode, paymentErrorCode, isPaymentError, paymentFieldErrors, RECEIPT_EMAIL_ERROR_CODES, RECEIPT_EMAIL_SEND_ERROR_CODES, RECEIPT_EMAIL_VIAS, isReceiptEmailErrorCode, receiptEmailErrorCode, isReceiptEmailError, receiptEmailFieldErrors, RECEIPT_ERROR_CODES, isReceiptErrorCode, receiptErrorCode, isReceiptError, receiptFieldErrors, remainingQuantities, fromReceiptCompanyPayload, receiptCompanyTaxInfo, fromCashregisterPayload, fromReceiptSummaryPayload, ZERO_KINDS, isZeroKind, reportMonthFromDate, previousReportMonth, nextReportMonth, reportMonthKey, reportMonthReadable, toStripeUrlSessionPayload, fromStripeUrlSessionPayload, toHobexReceiptPayload, fromHobexReceiptPayload, hobexReceiptToCardPaymentData, hobexReceiptNeedsSignature, } from './models/index.js';
3
3
  export { apiKeyAuth, registerUserAuth, DEFAULT_BASE_URL, POS_BASE_URL, DEFAULT_TIMEOUT_MS, ALL_CALLS, PUBLIC_CALLS, POS_CALLS, createTransport, createBinaryTransport, KasseneckApiError, KasseneckAuthError, KasseneckHttpError, KasseneckNetworkError, KasseneckValidationError, CLIENT_ERROR_CODES, isKasseneckApiError, isKasseneckAuthError, isKasseneckHttpError, isKasseneckNetworkError, isKasseneckValidationError, isOutcomeUnknown, sellReceipt, sellReceiptWithCompany, paymentsExpectedCents, cardRefundReference, receiptLayoutFromResult, cancelReceipt, zeroReceipt, getReceipt, getReceiptWithCompany, generateFullReceiptId, getFirstReceiptDate, listMyReceipts, listMyCashregisters, checkVoucherCombinationError, sendReceiptEmail, downloadDailyReport, downloadMonthlyReport, getReportV2, getCashboxStatus, getSignatureStatus, createKasseneckApi, } from './client/index.js';
4
- export { receiptDueCents, receiptDueBreakdown, } from './receipt/due.js';
4
+ export { receiptDueCents, receiptDueBreakdown, ReceiptDueError, RECEIPT_DUE_ERROR_REASONS, isReceiptDueError, } from './receipt/due.js';
5
5
  export { parseServerTimeStamp, toViennaWallClock } from './vienna-time.js';
6
6
  export { POS_BUSINESS_DEFAULTS, POS_DEVICE_DEFAULTS, mergePosSettings, fromArticleGroupPayload, fromPosArticlePayload, listMyArticleGroups, listMyArticles, getPosSettings, setMyPosSettings, setMyRegisterDeviceSettings, distributeDiscount, cancelScopeOf, receiptsScopeOf, } from './pos/index.js';
@@ -12,4 +12,4 @@ export { distributeDiscount } from '../receipt/discount.js';
12
12
  export { cancelScopeOf, receiptsScopeOf, type RegisterScope, type RegisterUserPerms } from '../register/pairing.js';
13
13
  export { type NetworkPrinter, type PrintJob, type PrintJobStatus, PRINT_JOB_STATUSES, type PrintJobSource, PRINT_JOB_SOURCES, isPrintJobFinished, type CreatePrintJobOptions, listMyPrinters, createPrintJob, getPrintJob, } from './drucker.js';
14
14
  export { listMyTipRecipients } from './trinkgeld.js';
15
- export { MESSAGES, ERROR_RULES, messageText, messageAppliesTo, RECEIPT_EMAIL_ERROR_MESSAGES, receiptEmailErrorMessage, CANCELLATION_PAYMENT_ERROR_MESSAGES, cancellationPaymentErrorMessage, LABELS, labelText, type TextEntry, type MessageKey, type ErrorKind, type Surface, type ReceiptEmailMessageCode, type CancellationPaymentMessageCode, type LabelKey, } from './texte.js';
15
+ export { MESSAGES, ERROR_RULES, ERROR_CODE_RULES, ERROR_OUTCOME_RULES, CALLS_WITH_EFFECT, findErrorRule, messageOutcome, messageText, messageAppliesTo, RECEIPT_EMAIL_ERROR_MESSAGES, receiptEmailErrorMessage, CANCELLATION_PAYMENT_ERROR_MESSAGES, cancellationPaymentErrorMessage, LABELS, labelText, type TextEntry, type MessageKey, type ErrorKind, type ErrorRule, type ErrorRuleDetail, type Surface, type ReceiptEmailMessageCode, type CancellationPaymentMessageCode, type LabelKey, } from './texte.js';
@@ -17,4 +17,4 @@ export { cancelScopeOf, receiptsScopeOf } from '../register/pairing.js';
17
17
  export { PRINT_JOB_STATUSES, PRINT_JOB_SOURCES, isPrintJobFinished, listMyPrinters, createPrintJob, getPrintJob, } from './drucker.js';
18
18
  export { listMyTipRecipients } from './trinkgeld.js';
19
19
  // Was die Kasse selbst sagt: ein Katalog fuer Browser-Kasse und App.
20
- export { MESSAGES, ERROR_RULES, messageText, messageAppliesTo, RECEIPT_EMAIL_ERROR_MESSAGES, receiptEmailErrorMessage, CANCELLATION_PAYMENT_ERROR_MESSAGES, cancellationPaymentErrorMessage, LABELS, labelText, } from './texte.js';
20
+ export { MESSAGES, ERROR_RULES, ERROR_CODE_RULES, ERROR_OUTCOME_RULES, CALLS_WITH_EFFECT, findErrorRule, messageOutcome, messageText, messageAppliesTo, RECEIPT_EMAIL_ERROR_MESSAGES, receiptEmailErrorMessage, CANCELLATION_PAYMENT_ERROR_MESSAGES, cancellationPaymentErrorMessage, LABELS, labelText, } from './texte.js';
@@ -20,6 +20,7 @@
20
20
  * `only`), die Platzhalter und die Arten der Fehlerregeln (Abschnitte
21
21
  * `placeholders` und `structure`).
22
22
  */
23
+ import { type ErrorOutcome } from '../client/errors.js';
23
24
  export type Surface = 'web' | 'app';
24
25
  export interface TextEntry {
25
26
  readonly text: string;
@@ -35,10 +36,19 @@ declare const MELDUNGEN_ROH: {
35
36
  readonly 'network.timeout': {
36
37
  readonly text: "Der Server antwortet nicht. Bitte die Internetverbindung prüfen und erneut versuchen.";
37
38
  };
39
+ readonly 'network.outcome_unknown': {
40
+ readonly text: "Der Server hat nicht geantwortet, der Vorgang kann trotzdem gebucht sein. Bitte vor einem neuen Versuch prüfen, ob der letzte Vorgang schon gebucht ist.";
41
+ };
38
42
  readonly 'server.unexpected': {
39
43
  readonly text: "Der Server hat unerwartet geantwortet (HTTP {status}). Bitte den Support verständigen.";
40
44
  readonly placeholders: readonly ["status"];
41
45
  };
46
+ readonly 'server.connection_disturbed': {
47
+ readonly text: "Die Verbindung zum Kassenserver ist gestört. Bitte kurz warten und erneut versuchen.";
48
+ };
49
+ readonly 'server.response_unreadable': {
50
+ readonly text: "Die Antwort des Kassenservers war nicht lesbar. Bitte die Kasse neu öffnen und vor einem neuen Versuch prüfen, ob der letzte Vorgang schon gebucht ist.";
51
+ };
42
52
  readonly 'pairing.code_missing': {
43
53
  readonly text: "Bitte den Kopplungs-Code eingeben.";
44
54
  };
@@ -543,8 +553,8 @@ export type MessageKey = keyof typeof MELDUNGEN_ROH;
543
553
  export declare const MESSAGES: Record<MessageKey, TextEntry>;
544
554
  /**
545
555
  * In welcher Reihenfolge ein Fehler eingeordnet wird, auf beiden Seiten
546
- * dieselbe. Jede Regel nennt ihre Art (`kind`) und entweder ein Verhalten
547
- * (`behavior`) oder den Schluessel des Satzes (`key`). Die Arten:
556
+ * dieselbe. Genau eine Regel je Art (`kind`), mit einem Verhalten
557
+ * (`behavior`) oder dem Schluessel des Satzes (`key`). Die Arten:
548
558
  * api - HTTP 200, `status:'error'`: der Satz des Backends, woertlich
549
559
  * (`server_text`)
550
560
  * plain_text - schon fuer den Bildschirm geschrieben: sein eigener Text
@@ -557,6 +567,11 @@ export declare const MESSAGES: Record<MessageKey, TextEntry>;
557
567
  * (`fallback`)
558
568
  * Unter 0.x hiessen sie `klartext`, `zeitablauf`, `netz`, `unerwartet`,
559
569
  * `sonst` (Tabelle in `fixtures/renames-1.0.json`).
570
+ *
571
+ * Seit 1.0.0-rc.5 verfeinern [ERROR_CODE_RULES] und [ERROR_OUTCOME_RULES]
572
+ * diese Liste; sie stehen bewusst getrennt, damit ein Leser, der nur nach
573
+ * `kind` sucht, weiter genau den Satz von rc.4 zeigt. [findErrorRule] wendet
574
+ * alle drei in der richtigen Reihenfolge an.
560
575
  */
561
576
  export declare const ERROR_RULES: readonly [{
562
577
  readonly kind: "api";
@@ -578,6 +593,73 @@ export declare const ERROR_RULES: readonly [{
578
593
  readonly behavior: "fallback";
579
594
  }];
580
595
  export type ErrorKind = (typeof ERROR_RULES)[number]['kind'];
596
+ /**
597
+ * Regeln je Code (`error.code`), vor der Regel der Art: sie ersetzen den
598
+ * technischen Satz des Pakets bzw. des Rands durch einen Menschentext. Der
599
+ * technische Satz bleibt in `error.message` fuers Protokoll.
600
+ * - `dialect_mismatch`, `response_translation_failed`, `response_unreadable`:
601
+ * Ausgang unklar, der Vorgang kann gebucht sein; der Satz raet nie zum
602
+ * Wiederholen, sondern zum Nachsehen.
603
+ * - `route_missing`, `not_found`, `internal_translation_error`: am Server
604
+ * geschah nichts, ein neuer Versuch ist sicher (auch auf den Geldwegen:
605
+ * alle drei stehen in `PAYMENT_CALL_REJECTED_CODES`).
606
+ */
607
+ export declare const ERROR_CODE_RULES: readonly [{
608
+ readonly kind: "api";
609
+ readonly codes: readonly ["dialect_mismatch", "response_translation_failed", "response_unreadable"];
610
+ readonly key: "server.response_unreadable";
611
+ }, {
612
+ readonly kind: "api";
613
+ readonly codes: readonly ["route_missing", "not_found", "internal_translation_error"];
614
+ readonly key: "server.connection_disturbed";
615
+ }];
616
+ /**
617
+ * Regeln je Ausgang, nach den Code-Regeln und vor der Regel der Art: eine
618
+ * Frist oder ein Netzfehler auf einem Aufruf mit Wirkung (Ausgang unklar,
619
+ * siehe [messageOutcome]) raet nie zum Wiederholen. Ein Netzfehler auf einem
620
+ * Aufruf ohne Wirkung behaelt den Satz von rc.4 („erneut versuchen“).
621
+ */
622
+ export declare const ERROR_OUTCOME_RULES: readonly [{
623
+ readonly kind: "timeout";
624
+ readonly outcome: "unknown";
625
+ readonly key: "network.outcome_unknown";
626
+ }, {
627
+ readonly kind: "network";
628
+ readonly outcome: "unknown";
629
+ readonly key: "network.outcome_unknown";
630
+ }];
631
+ export type ErrorRule = (typeof ERROR_RULES)[number] | (typeof ERROR_CODE_RULES)[number] | (typeof ERROR_OUTCOME_RULES)[number];
632
+ /** Was ueber einen Fehler bekannt ist, ausser seiner Art. */
633
+ export interface ErrorRuleDetail {
634
+ /** `error.code` eines `KasseneckApiError`. */
635
+ code?: string | null;
636
+ /** Ausgang fuer den Satz, siehe [messageOutcome]. */
637
+ outcome?: ErrorOutcome | null;
638
+ }
639
+ /**
640
+ * Die Regel fuer einen Fehler: zuerst eine Code-Regel mit passender Art und
641
+ * passendem `code`, dann eine Ausgangs-Regel mit passender Art und
642
+ * passendem `outcome`, sonst die Regel der Art aus [ERROR_RULES]. Beide
643
+ * Kassen ordnen so ein.
644
+ */
645
+ export declare function findErrorRule(kind: ErrorKind, detail?: ErrorRuleDetail): ErrorRule;
646
+ /**
647
+ * Kassen-Aufrufe mit Wirkung, die sich nach einem Netzfehler nicht gefahrlos
648
+ * wiederholen lassen: die sechs, deren Ausgang das Paket selbst als unklar
649
+ * fuehrt (Beleg, auch Null- und Startbeleg ueber `createReceipt`, Storno,
650
+ * FinanzOnline, die drei Geldwege), dazu der Druckjob (zweiter Bon) und die
651
+ * Belegmail (zweite Mail). Ob die Anfrage vor dem Abriss schon draussen war,
652
+ * sieht das Paket nicht (fetch wirft in beiden Faellen gleich); darum gilt
653
+ * hier immer der vorsichtige Satz.
654
+ */
655
+ export declare const CALLS_WITH_EFFECT: readonly ["createReceipt", "cancelReceipt", "financeWebService", "hobexPayApi", "hobexRefundApi", "stripeCaptureIntent", "createPrintJob", "sendReceiptEmail"];
656
+ /**
657
+ * Der Ausgang, nach dem der Satz gewaehlt wird: `'unknown'`, wenn das Paket
658
+ * ihn als unklar fuehrt ([isOutcomeUnknown]) oder wenn eine Frist bzw. ein
659
+ * Netzfehler einen Aufruf aus [CALLS_WITH_EFFECT] traf; sonst der Ausgang
660
+ * des Fehlers (`'rejected'`) bzw. `undefined` fuer fremde Fehler.
661
+ */
662
+ export declare function messageOutcome(error: unknown): ErrorOutcome | undefined;
581
663
  /**
582
664
  * Beleg per E-Mail senden: welcher `code` des Backends welchen Satz bekommt.
583
665
  *
@@ -662,6 +744,13 @@ declare const BESCHRIFTUNGEN_ROH: {
662
744
  readonly 'pairing.pair_again': {
663
745
  readonly text: "Neu koppeln";
664
746
  };
747
+ readonly 'register.device_unnamed': {
748
+ readonly text: "Kasse";
749
+ };
750
+ readonly 'login.locked_seconds': {
751
+ readonly text: "Noch {seconds} s gesperrt";
752
+ readonly placeholders: readonly ["seconds"];
753
+ };
665
754
  readonly 'logout.question': {
666
755
  readonly text: "Wirklich abmelden?";
667
756
  };
@@ -731,6 +820,10 @@ declare const BESCHRIFTUNGEN_ROH: {
731
820
  readonly 'split.remaining': {
732
821
  readonly text: "Rest";
733
822
  };
823
+ readonly 'split.remaining_with_rounding': {
824
+ readonly text: "Rest inkl. Rundung {amount} ({cents} ct)";
825
+ readonly placeholders: readonly ["amount", "cents"];
826
+ };
734
827
  readonly 'split.divide': {
735
828
  readonly text: "÷ {n}";
736
829
  readonly placeholders: readonly ["n"];