@kreiseck/kasseneck-api 0.10.0 → 0.12.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.
@@ -1,11 +1,62 @@
1
+ import { QR_DRUCK_PUNKTE, QR_MINDEST_PUNKTE, QR_MODUL_DECKEL, qrGroesseFuer, } from '../printing/index.js';
1
2
  import { renderReceiptGrid, ZEICHEN_JE_PAPIER } from './grid.js';
2
3
  export function eposXmlEscape(text) {
3
4
  return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&apos;');
4
5
  }
5
6
  const NS = 'http://www.epson-pos.com/schemas/2011/03/epos-print';
7
+ /** Nur das XML; wer die QR-Meldungen braucht, nimmt [eposPrintXmlErgebnis]. */
6
8
  export function eposPrintXml(layout, options = {}) {
9
+ return eposPrintXmlErgebnis(layout, options).xml;
10
+ }
11
+ /**
12
+ * Wie [eposPrintXml], gibt aber zusaetzlich zurueck, was dem QR zugestossen
13
+ * ist.
14
+ *
15
+ * **Die QR-Modulgroesse wird gerechnet.** Vorher stand hier fest 6, unabhaengig
16
+ * von der Papierbreite: ein Beleg-QR mit realer RKSV-Nutzlast hat 57 Module,
17
+ * mit Ruhezone 65, bei sechs Punkten also 390 Druckpunkte -- und ein
18
+ * 58-mm-Kopf hat 384. Der Epson schneidet ein zu breites Symbol nicht ab, er
19
+ * laesst es weg; genau daran fehlte am echten Beleg der QR.
20
+ */
21
+ export function eposPrintXmlErgebnis(layout, options = {}) {
7
22
  const grid = renderReceiptGrid(layout, { zeichen: options.zeichen ?? ZEICHEN_JE_PAPIER[layout.paperSize] });
8
- const qrBreite = Math.min(16, Math.max(3, Math.floor(options.qrBreite ?? 6)));
23
+ const deckel = options.qrGroesse ?? 'mittel';
24
+ const fest = options.qrBreite === undefined
25
+ ? null
26
+ : Math.min(16, Math.max(3, Math.floor(options.qrBreite)));
27
+ let qrFehler = null;
28
+ let qrAusweich = null;
29
+ /**
30
+ * Die Breite fuer eine QR-Zeile, oder `null` -- dann geht **kein** Symbol
31
+ * hinaus: ein Element, von dem man weiss, dass der Drucker es weglaesst,
32
+ * taeuscht nur einen Ausdruck vor.
33
+ *
34
+ * Eine leere Nutzlast geht unveraendert den Bestandsweg (Deckel), wie am
35
+ * ESC/POS-Befehl: sie ist ein Datenfehler, kein Papierfehler.
36
+ */
37
+ const qrBreiteFuer = (nutzlast) => {
38
+ if (fest !== null)
39
+ return fest;
40
+ if (nutzlast === '')
41
+ return QR_MODUL_DECKEL[deckel];
42
+ const mass = qrGroesseFuer({
43
+ nutzlast,
44
+ papierbreitePunkte: QR_DRUCK_PUNKTE[layout.paperSize],
45
+ groesse: deckel,
46
+ });
47
+ if (!mass.passt) {
48
+ qrFehler =
49
+ `QR mit ${mass.module} Modulen ist fuer ${layout.paperSize === 'mm58' ? 58 : 80} mm ` +
50
+ `(${QR_DRUCK_PUNKTE[layout.paperSize]} Punkte) zu breit`;
51
+ return null;
52
+ }
53
+ if (mass.unterMindestmass) {
54
+ qrAusweich =
55
+ `QR mit ${mass.module} Modulen passt nur mit ${String(mass.punkte)} Punkten ` +
56
+ `je Modul -- unter dem Mindestmass von ${QR_MINDEST_PUNKTE}`;
57
+ }
58
+ return mass.punkte;
59
+ };
9
60
  const out = [];
10
61
  out.push(`<epos-print xmlns="${NS}">`);
11
62
  out.push('<text lang="de"/>');
@@ -17,11 +68,15 @@ export function eposPrintXml(layout, options = {}) {
17
68
  case 'space':
18
69
  out.push('<feed line="1"/>');
19
70
  break;
20
- case 'qr':
71
+ case 'qr': {
72
+ const breite = qrBreiteFuer(z.qr ?? '');
73
+ if (breite === null)
74
+ break;
21
75
  out.push('<text align="center"/>');
22
- out.push(`<symbol type="qrcode_model_2" level="level_m" width="${qrBreite}" height="0" size="0">${eposXmlEscape(z.qr ?? '')}</symbol>`);
76
+ out.push(`<symbol type="qrcode_model_2" level="level_m" width="${breite}" height="0" size="0">${eposXmlEscape(z.qr ?? '')}</symbol>`);
23
77
  out.push('<text align="left"/>');
24
78
  break;
79
+ }
25
80
  case 'banner':
26
81
  out.push(`<text width="1" height="2" reverse="${z.ton === 'warnung' ? 'true' : 'false'}" em="true">${eposXmlEscape(z.text)}&#10;</text>`);
27
82
  out.push('<text width="1" height="1" reverse="false" em="false"/>');
@@ -41,7 +96,7 @@ export function eposPrintXml(layout, options = {}) {
41
96
  out.push('<cut type="feed"/>');
42
97
  }
43
98
  out.push('</epos-print>');
44
- return out.join('\n');
99
+ return { xml: out.join('\n'), qrFehler, qrAusweich };
45
100
  }
46
101
  const EPOS_DEVID_VORGABE = 'local_printer';
47
102
  export function eposServiceUrl(ip, devid = EPOS_DEVID_VORGABE, timeoutMs = 10000) {
@@ -148,7 +203,12 @@ async function eposDirectSend(innerXml, o, fetchFn) {
148
203
  /** Beleg direkt drucken; wirft bei Netz-/Zertifikatsproblemen, sonst die Drucker-Antwort. */
149
204
  export function eposDirectPrint(layout, o, fetchFn = fetch) {
150
205
  const papier = o.papier ?? layout.paperSize;
151
- return eposDirectSend(eposPrintXml({ ...layout, paperSize: papier }, { zeichen: ZEICHEN_JE_PAPIER[papier] }), o, fetchFn);
206
+ const xmlOptionen = { zeichen: ZEICHEN_JE_PAPIER[papier] };
207
+ if (o.qrBreite !== undefined)
208
+ xmlOptionen.qrBreite = o.qrBreite;
209
+ if (o.qrGroesse !== undefined)
210
+ xmlOptionen.qrGroesse = o.qrGroesse;
211
+ return eposDirectSend(eposPrintXml({ ...layout, paperSize: papier }, xmlOptionen), o, fetchFn);
152
212
  }
153
213
  /** Verbindungstest: leeres Dokument, druckt nichts, liefert den Druckerstatus. */
154
214
  export function eposDirectStatus(o, fetchFn = fetch) {
@@ -1,4 +1,4 @@
1
1
  export { type LayoutAlign, type LayoutColumn, type LayoutTextLine, type LayoutColumnsLine, type LayoutRuleLine, type LayoutSpaceLine, type LayoutQrLine, type LayoutLine, type ReceiptLayout, type BuildReceiptLayoutOptions, buildReceiptLayout, receiptSignatureFailed, receiptSignatureIsTest, receiptIsZero, receiptAmountsAreZero, receiptIsSmallBusinessConsistent, receiptZdaText, type Pruefangaben, AKTUELLES_REGELWERK, type LayoutBannerLine, type LayoutRegelwerk, SMALL_BUSINESS_NOTICE, formatCents, } from './layout.js';
2
- export { type EscPosLayoutOptions, escPosLayoutBytes } from './layout-escpos.js';
2
+ export { type EscPosLayoutOptions, type EscPosLayoutErgebnis, type QrPrintMode, escPosLayoutBytes, escPosLayoutErgebnis, } from './layout-escpos.js';
3
3
  export { type GridLine, type GridLineKind, type ReceiptGrid, type RenderReceiptGridOptions, renderReceiptGrid, gridSpaltenBreiten, gridAlsText, ZEICHEN_JE_PAPIER, } from './grid.js';
4
- export { type EposPrintXmlOptions, type EposDirectOptions, type EposResponse, EposConnectionError, eposPrintXml, eposXmlEscape, eposServiceUrl, eposSoapEnvelope, eposParseResponse, eposDirectPrint, eposDirectStatus } from './epos.js';
4
+ export { type EposPrintXmlOptions, type EposPrintErgebnis, type EposDirectOptions, type EposResponse, EposConnectionError, eposPrintXml, eposPrintXmlErgebnis, eposXmlEscape, eposServiceUrl, eposSoapEnvelope, eposParseResponse, eposDirectPrint, eposDirectStatus } from './epos.js';
@@ -1,4 +1,4 @@
1
1
  export { buildReceiptLayout, receiptSignatureFailed, receiptSignatureIsTest, receiptIsZero, receiptAmountsAreZero, receiptIsSmallBusinessConsistent, receiptZdaText, AKTUELLES_REGELWERK, SMALL_BUSINESS_NOTICE, formatCents, } from './layout.js';
2
- export { escPosLayoutBytes } from './layout-escpos.js';
2
+ export { escPosLayoutBytes, escPosLayoutErgebnis, } from './layout-escpos.js';
3
3
  export { renderReceiptGrid, gridSpaltenBreiten, gridAlsText, ZEICHEN_JE_PAPIER, } from './grid.js';
4
- export { EposConnectionError, eposPrintXml, eposXmlEscape, eposServiceUrl, eposSoapEnvelope, eposParseResponse, eposDirectPrint, eposDirectStatus } from './epos.js';
4
+ export { EposConnectionError, eposPrintXml, eposPrintXmlErgebnis, eposXmlEscape, eposServiceUrl, eposSoapEnvelope, eposParseResponse, eposDirectPrint, eposDirectStatus } from './epos.js';
@@ -1,4 +1,4 @@
1
- import { type PosCodeTable, type PosCutMode, type PosPaperSize, type QrCorrection, type QrSize } from '../printing/index.js';
1
+ import { type PosCodeTable, type PosCutMode, type PosPaperSize, type QrCorrection, type QrMatrix, type QrModulGroesse, type QrSize } from '../printing/index.js';
2
2
  import type { ReceiptLayout } from './layout.js';
3
3
  /**
4
4
  * Bruecke vom Layout-Modell zu ESC/POS-Bytes — der Bondrucker-Ausgabeweg.
@@ -14,6 +14,20 @@ import type { ReceiptLayout } from './layout.js';
14
14
  * ein unerwartetes Zeichen darin soll darum auffallen und nicht still
15
15
  * verfaelscht werden.
16
16
  */
17
+ /**
18
+ * Wie der Beleg-QR auf das Papier kommt — Zwilling von `QrPrintMode`
19
+ * (kasseneck_api/lib/enums/qr_print_mode.dart).
20
+ *
21
+ * - `native` — der Druckbefehl `GS ( k`; der Drucker zeichnet selbst.
22
+ * Ohne Modellbefehl, also byteidentisch zum Bestand.
23
+ * - `nativeModel1` — derselbe Befehl mit ausdruecklicher Wahl von **Modell 1**
24
+ * (`GS ( k 04 00 31 41 31 00`). Fuer guenstige Geraete, die
25
+ * nur diesen aelteren Symboltyp beherrschen; belegt ist
26
+ * eines, das bei Modell 2 unter dem Code eine "0" ausgibt.
27
+ * - `imageRaster` — der QR als Rasterbild. Braucht ein Raster vom Aufrufer
28
+ * (`qrMatrix`), weil dieses Paket keine QR-Codes rechnet.
29
+ */
30
+ export type QrPrintMode = 'native' | 'nativeModel1' | 'imageRaster';
17
31
  export interface EscPosLayoutOptions {
18
32
  /** Papierbreite; Vorgabe ist die des Layouts (dessen Spaltenbreiten daran haengen). */
19
33
  paperSize?: PosPaperSize;
@@ -21,10 +35,38 @@ export interface EscPosLayoutOptions {
21
35
  codeTable?: PosCodeTable | null;
22
36
  /** Papierschnitt am Ende: `true` (voll), `'partial'` oder `false`. Vorgabe `true`. */
23
37
  cut?: boolean | PosCutMode;
24
- /** Modulgroesse des QR-Codes. */
38
+ /**
39
+ * Feste Modulgroesse des QR-Codes. Gesetzt schaltet sie die Rechnung ab —
40
+ * dann passt der Aufrufer selbst auf, dass das Symbol aufs Papier geht.
41
+ */
25
42
  qrSize?: QrSize;
26
43
  /** Fehlerkorrekturstufe des QR-Codes. */
27
44
  qrCorrection?: QrCorrection;
45
+ /** Deckel fuer die gerechnete Modulgroesse; Vorgabe `auto`. */
46
+ qrGroesse?: QrModulGroesse;
47
+ /** Druckweg des QR; Vorgabe `native` (der Bestandsweg). */
48
+ qrModus?: QrPrintMode;
49
+ /**
50
+ * Raster fuer den Bildweg — ohne das gibt es keinen Notausgang.
51
+ *
52
+ * Dieses Paket rechnet keine QR-Codes und verarbeitet keine Bilder (siehe
53
+ * Kopf von `layout.ts`); der Aufrufer bringt das fertige Raster mit, etwa
54
+ * aus der QR-Bibliothek, die er ohnehin fuer den Bildschirm benutzt.
55
+ */
56
+ qrMatrix?: (nutzlast: string) => QrMatrix;
57
+ }
58
+ /**
59
+ * Der Bytestrom **samt** dem, was dem QR unterwegs zugestossen ist.
60
+ *
61
+ * Zwilling von `KeckPrintResult`: `qrFehler` heisst "Beleg ohne QR,
62
+ * nachdrucken oder elektronisch ausgeben", `qrAusweich` heisst "gedruckt,
63
+ * aber der eingestellte Weg taugt fuer dieses Geraet nicht" — das eine gehoert
64
+ * dem Kunden gesagt, das andere dem Chef.
65
+ */
66
+ export interface EscPosLayoutErgebnis {
67
+ bytes: Uint8Array;
68
+ qrFehler: string | null;
69
+ qrAusweich: string | null;
28
70
  }
29
71
  /**
30
72
  * Bytes fuer den Bondrucker -- **aus dem Zeichenraster** ([renderReceiptGrid]):
@@ -33,3 +75,9 @@ export interface EscPosLayoutOptions {
33
75
  * zeigt, druckt der Drucker (Monospace, Font A: 32/48 Zeichen).
34
76
  */
35
77
  export declare function escPosLayoutBytes(layout: ReceiptLayout, options?: EscPosLayoutOptions): Uint8Array;
78
+ /**
79
+ * Wie [escPosLayoutBytes], gibt aber zusaetzlich zurueck, was dem QR
80
+ * zugestossen ist. Die Bytes sind dieselben; wer die Meldungen braucht, nimmt
81
+ * diesen Weg.
82
+ */
83
+ export declare function escPosLayoutErgebnis(layout: ReceiptLayout, options?: EscPosLayoutOptions): EscPosLayoutErgebnis;
@@ -1,4 +1,4 @@
1
- import { createEscPosDocument, escPosBytes, escPosCut, escPosFeed, escPosPrintableText, escPosQrCode, escPosReset, escPosText, } from '../printing/index.js';
1
+ import { createEscPosDocument, escPosBytes, escPosCut, escPosFeed, escPosPrintableText, escPosQrCode, escPosQrRaster, escPosReset, escPosText, QR_DRUCK_PUNKTE, qrGroesseFuer, } from '../printing/index.js';
2
2
  import { renderReceiptGrid, ZEICHEN_JE_PAPIER } from './grid.js';
3
3
  /**
4
4
  * Druckbar gemachtes Layout: Texte durch [escPosPrintableText] (Codepage,
@@ -26,6 +26,14 @@ function druckbaresLayout(layout) {
26
26
  * zeigt, druckt der Drucker (Monospace, Font A: 32/48 Zeichen).
27
27
  */
28
28
  export function escPosLayoutBytes(layout, options = {}) {
29
+ return escPosLayoutErgebnis(layout, options).bytes;
30
+ }
31
+ /**
32
+ * Wie [escPosLayoutBytes], gibt aber zusaetzlich zurueck, was dem QR
33
+ * zugestossen ist. Die Bytes sind dieselben; wer die Meldungen braucht, nimmt
34
+ * diesen Weg.
35
+ */
36
+ export function escPosLayoutErgebnis(layout, options = {}) {
29
37
  const paperSize = options.paperSize ?? layout.paperSize;
30
38
  const dokumentOptionen = { paperSize };
31
39
  if (options.codeTable !== undefined) {
@@ -33,11 +41,59 @@ export function escPosLayoutBytes(layout, options = {}) {
33
41
  }
34
42
  const doc = createEscPosDocument(dokumentOptionen);
35
43
  escPosReset(doc);
44
+ const modus = options.qrModus ?? 'native';
36
45
  const qrOptionen = { align: 'center' };
37
46
  if (options.qrSize !== undefined)
38
47
  qrOptionen.size = options.qrSize;
39
48
  if (options.qrCorrection !== undefined)
40
49
  qrOptionen.correction = options.qrCorrection;
50
+ if (options.qrGroesse !== undefined)
51
+ qrOptionen.groesse = options.qrGroesse;
52
+ if (modus === 'nativeModel1')
53
+ qrOptionen.modell1 = true;
54
+ if (modus === 'imageRaster' && options.qrMatrix === undefined) {
55
+ throw new Error("qrModus 'imageRaster' braucht ein qrMatrix -- dieses Paket rastert nicht selbst");
56
+ }
57
+ const rasterOptionen = { align: 'center' };
58
+ if (options.qrGroesse !== undefined)
59
+ rasterOptionen.groesse = options.qrGroesse;
60
+ /**
61
+ * Eine QR-Zeile des Rasters. Hier sitzt der **Notausgang**: passt das Symbol
62
+ * nativ auch mit der Ausnahmegroesse nicht aufs Papier, druckt der Drucker
63
+ * es GAR NICHT -- er schneidet nicht ab, er laesst weg. Ein Pflichtbeleg
64
+ * ohne QR ist der schlechteste aller Ausgaenge, also geht der QR dann als
65
+ * Bild hinaus, und der Aufrufer erfaehrt es ueber `qrAusweich`.
66
+ *
67
+ * Ohne `qrMatrix` gibt es diesen Weg nicht -- dann bleibt nur die Meldung
68
+ * `qrFehler` aus `escPosQrCode`, und der Beleg geht ohne QR hinaus.
69
+ */
70
+ const qrZeile = (nutzlast) => {
71
+ const raster = options.qrMatrix;
72
+ if (nutzlast !== '' && raster !== undefined) {
73
+ if (modus === 'imageRaster') {
74
+ escPosQrRaster(doc, raster(nutzlast), rasterOptionen);
75
+ return;
76
+ }
77
+ // Feste `qrSize` heisst: der Aufrufer weiss, was er tut -- dann wird
78
+ // weder gerechnet noch ausgewichen.
79
+ if (options.qrSize === undefined) {
80
+ const mass = qrGroesseFuer({
81
+ nutzlast,
82
+ papierbreitePunkte: QR_DRUCK_PUNKTE[paperSize],
83
+ groesse: options.qrGroesse ?? 'auto',
84
+ });
85
+ if (!mass.passt) {
86
+ doc.qrAusweich =
87
+ `QR mit ${mass.module} Modulen passt nativ nicht auf ` +
88
+ `${paperSize === 'mm58' ? 58 : 80} mm (${QR_DRUCK_PUNKTE[paperSize]} Punkte) ` +
89
+ `-- als Bild gedruckt`;
90
+ escPosQrRaster(doc, raster(nutzlast), rasterOptionen);
91
+ return;
92
+ }
93
+ }
94
+ }
95
+ escPosQrCode(doc, nutzlast, qrOptionen);
96
+ };
41
97
  const grid = renderReceiptGrid(druckbaresLayout(layout), { zeichen: ZEICHEN_JE_PAPIER[paperSize] });
42
98
  for (const zeile of grid.lines) {
43
99
  switch (zeile.kind) {
@@ -45,7 +101,7 @@ export function escPosLayoutBytes(layout, options = {}) {
45
101
  escPosFeed(doc, 1);
46
102
  break;
47
103
  case 'qr':
48
- escPosQrCode(doc, zeile.qr ?? '', qrOptionen);
104
+ qrZeile(zeile.qr ?? '');
49
105
  break;
50
106
  case 'banner': {
51
107
  // Belegart/Warnung: fett zwischen zwei Volllinien — derselbe Rahmen-
@@ -65,5 +121,5 @@ export function escPosLayoutBytes(layout, options = {}) {
65
121
  if (options.cut !== false) {
66
122
  escPosCut(doc, options.cut === 'partial' ? 'partial' : 'full');
67
123
  }
68
- return escPosBytes(doc);
124
+ return { bytes: escPosBytes(doc), qrFehler: doc.qrFehler, qrAusweich: doc.qrAusweich };
69
125
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.10.0",
2
+ "version": "0.12.0",
3
3
  "gemessenAn": {
4
4
  "tid": "3600335",
5
5
  "hpsVersion": "1.10.0",
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.10.0",
2
+ "version": "0.12.0",
3
3
  "meldungen": {
4
4
  "netz.keine_verbindung": {
5
5
  "text": "Keine Verbindung zum Server. Bitte die Internetverbindung prüfen und erneut versuchen."
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.10.0",
2
+ "version": "0.12.0",
3
3
  "aufrufe": [
4
4
  "activateCashregister",
5
5
  "cancelReceipt",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kreiseck/kasseneck-api",
3
- "version": "0.10.0",
3
+ "version": "0.12.0",
4
4
  "description": "JavaScript-Zwilling von kasseneck_api (Flutter) fuer das Kasseneck-Backend",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Kreiseck",