@kreiseck/kasseneck-api 0.13.2 → 0.15.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 (195) hide show
  1. package/CHANGELOG.md +98 -0
  2. package/README.md +96 -5
  3. package/dist/cjs/client/aufrufe.d.ts +1 -1
  4. package/dist/cjs/client/aufrufe.js +11 -0
  5. package/dist/cjs/client/receipts.d.ts +7 -0
  6. package/dist/cjs/client/receipts.js +1 -0
  7. package/dist/cjs/kasse/drucker.d.ts +5 -0
  8. package/dist/cjs/kasse/drucker.js +9 -0
  9. package/dist/cjs/kasse/settings.js +5 -2
  10. package/dist/cjs/printing/escpos.d.ts +38 -4
  11. package/dist/cjs/printing/escpos.js +87 -15
  12. package/dist/cjs/printing/index.d.ts +2 -2
  13. package/dist/cjs/printing/index.js +5 -1
  14. package/dist/cjs/printing/qr-groesse.d.ts +26 -21
  15. package/dist/cjs/printing/qr-groesse.js +22 -8
  16. package/dist/cjs/react/index.d.ts +53 -0
  17. package/dist/cjs/react/index.js +81 -0
  18. package/dist/cjs/receipt/bild.d.ts +14 -0
  19. package/dist/cjs/receipt/bild.js +65 -0
  20. package/dist/cjs/receipt/blatt.d.ts +107 -0
  21. package/dist/cjs/receipt/blatt.js +137 -0
  22. package/dist/cjs/receipt/epos.d.ts +19 -8
  23. package/dist/cjs/receipt/epos.js +39 -20
  24. package/dist/cjs/receipt/grid.d.ts +1 -1
  25. package/dist/cjs/receipt/grid.js +9 -1
  26. package/dist/cjs/receipt/index.d.ts +4 -2
  27. package/dist/cjs/receipt/index.js +17 -1
  28. package/dist/cjs/receipt/layout-escpos.d.ts +31 -3
  29. package/dist/cjs/receipt/layout-escpos.js +58 -18
  30. package/dist/cjs/rechnung/api.d.ts +43 -0
  31. package/dist/cjs/rechnung/api.js +35 -0
  32. package/dist/cjs/rechnung/auth.d.ts +21 -0
  33. package/dist/cjs/rechnung/auth.js +36 -0
  34. package/dist/cjs/rechnung/endpunkte.d.ts +48 -0
  35. package/dist/cjs/rechnung/endpunkte.js +129 -0
  36. package/dist/cjs/rechnung/fehler.d.ts +21 -0
  37. package/dist/cjs/rechnung/fehler.js +44 -0
  38. package/dist/cjs/rechnung/index.d.ts +18 -0
  39. package/dist/cjs/rechnung/index.js +52 -0
  40. package/dist/cjs/rechnung/typen.d.ts +224 -0
  41. package/dist/cjs/rechnung/typen.js +9 -0
  42. package/dist/cjs/rechnung/vertrag.d.ts +98 -0
  43. package/dist/cjs/rechnung/vertrag.js +191 -0
  44. package/dist/esm/client/aufrufe.d.ts +1 -1
  45. package/dist/esm/client/aufrufe.js +11 -0
  46. package/dist/esm/client/receipts.d.ts +7 -0
  47. package/dist/esm/client/receipts.js +1 -0
  48. package/dist/esm/kasse/drucker.d.ts +5 -0
  49. package/dist/esm/kasse/drucker.js +9 -0
  50. package/dist/esm/kasse/settings.js +5 -2
  51. package/dist/esm/printing/escpos.d.ts +38 -4
  52. package/dist/esm/printing/escpos.js +84 -15
  53. package/dist/esm/printing/index.d.ts +2 -2
  54. package/dist/esm/printing/index.js +2 -2
  55. package/dist/esm/printing/qr-groesse.d.ts +26 -21
  56. package/dist/esm/printing/qr-groesse.js +21 -8
  57. package/dist/esm/react/index.d.ts +53 -0
  58. package/dist/esm/react/index.js +80 -1
  59. package/dist/esm/receipt/bild.d.ts +14 -0
  60. package/dist/esm/receipt/bild.js +62 -0
  61. package/dist/esm/receipt/blatt.d.ts +107 -0
  62. package/dist/esm/receipt/blatt.js +128 -0
  63. package/dist/esm/receipt/epos.d.ts +19 -8
  64. package/dist/esm/receipt/epos.js +40 -22
  65. package/dist/esm/receipt/grid.d.ts +1 -1
  66. package/dist/esm/receipt/grid.js +9 -1
  67. package/dist/esm/receipt/index.d.ts +4 -2
  68. package/dist/esm/receipt/index.js +4 -2
  69. package/dist/esm/receipt/layout-escpos.d.ts +31 -3
  70. package/dist/esm/receipt/layout-escpos.js +58 -20
  71. package/dist/esm/rechnung/api.d.ts +43 -0
  72. package/dist/esm/rechnung/api.js +32 -0
  73. package/dist/esm/rechnung/auth.d.ts +21 -0
  74. package/dist/esm/rechnung/auth.js +33 -0
  75. package/dist/esm/rechnung/endpunkte.d.ts +48 -0
  76. package/dist/esm/rechnung/endpunkte.js +116 -0
  77. package/dist/esm/rechnung/fehler.d.ts +21 -0
  78. package/dist/esm/rechnung/fehler.js +39 -0
  79. package/dist/esm/rechnung/index.d.ts +18 -0
  80. package/dist/esm/rechnung/index.js +17 -0
  81. package/dist/esm/rechnung/typen.d.ts +224 -0
  82. package/dist/esm/rechnung/typen.js +8 -0
  83. package/dist/esm/rechnung/vertrag.d.ts +98 -0
  84. package/dist/esm/rechnung/vertrag.js +188 -0
  85. package/fixtures/erwartet/karte-eigener.blatt32.json +159 -0
  86. package/fixtures/erwartet/karte-eigener.blatt48.json +159 -0
  87. package/fixtures/erwartet/karte-gptom-ios.blatt32.json +231 -0
  88. package/fixtures/erwartet/karte-gptom-ios.blatt48.json +231 -0
  89. package/fixtures/erwartet/karte-gptom.blatt32.json +231 -0
  90. package/fixtures/erwartet/karte-gptom.blatt48.json +231 -0
  91. package/fixtures/erwartet/karte-hobex-cloud.blatt32.json +201 -0
  92. package/fixtures/erwartet/karte-hobex-cloud.blatt48.json +201 -0
  93. package/fixtures/erwartet/karte-hobex-hps.blatt32.json +225 -0
  94. package/fixtures/erwartet/karte-hobex-hps.blatt48.json +225 -0
  95. package/fixtures/erwartet/karte-mypos.blatt32.json +255 -0
  96. package/fixtures/erwartet/karte-mypos.blatt48.json +243 -0
  97. package/fixtures/erwartet/karte-stripe-eps.blatt32.json +195 -0
  98. package/fixtures/erwartet/karte-stripe-eps.blatt48.json +195 -0
  99. package/fixtures/erwartet/karte-stripe.blatt32.json +213 -0
  100. package/fixtures/erwartet/karte-stripe.blatt48.json +213 -0
  101. package/fixtures/erwartet/karte-sumup.blatt32.json +219 -0
  102. package/fixtures/erwartet/karte-sumup.blatt48.json +207 -0
  103. package/fixtures/erwartet/langer-artikelname.blatt32.json +177 -0
  104. package/fixtures/erwartet/langer-artikelname.blatt48.json +171 -0
  105. package/fixtures/erwartet/logo-probe-hoch.raster32.txt +120 -0
  106. package/fixtures/erwartet/logo-probe.raster32.txt +54 -0
  107. package/fixtures/erwartet/null-ausfall.blatt32.json +207 -0
  108. package/fixtures/erwartet/null-ausfall.blatt48.json +189 -0
  109. package/fixtures/erwartet/null-ausfall.grid32.txt +2 -0
  110. package/fixtures/erwartet/null-ausfall.grid48.txt +2 -0
  111. package/fixtures/erwartet/null-jahr.blatt32.json +213 -0
  112. package/fixtures/erwartet/null-jahr.blatt48.json +189 -0
  113. package/fixtures/erwartet/null-jahr.grid32.txt +2 -0
  114. package/fixtures/erwartet/null-jahr.grid48.txt +2 -0
  115. package/fixtures/erwartet/null-monat.blatt32.json +207 -0
  116. package/fixtures/erwartet/null-monat.blatt48.json +189 -0
  117. package/fixtures/erwartet/null-monat.grid32.txt +2 -0
  118. package/fixtures/erwartet/null-monat.grid48.txt +2 -0
  119. package/fixtures/erwartet/null-pruef.blatt32.json +207 -0
  120. package/fixtures/erwartet/null-pruef.blatt48.json +189 -0
  121. package/fixtures/erwartet/null-pruef.grid32.txt +2 -0
  122. package/fixtures/erwartet/null-pruef.grid48.txt +2 -0
  123. package/fixtures/erwartet/null-schluss.blatt32.json +207 -0
  124. package/fixtures/erwartet/null-schluss.blatt48.json +189 -0
  125. package/fixtures/erwartet/null-schluss.grid32.txt +2 -0
  126. package/fixtures/erwartet/null-schluss.grid48.txt +2 -0
  127. package/fixtures/erwartet/null-start.blatt32.json +207 -0
  128. package/fixtures/erwartet/null-start.blatt48.json +189 -0
  129. package/fixtures/erwartet/null-start.grid32.txt +2 -0
  130. package/fixtures/erwartet/null-start.grid48.txt +2 -0
  131. package/fixtures/erwartet/rabatt-chef-trinkgeld.blatt32.json +213 -0
  132. package/fixtures/erwartet/rabatt-chef-trinkgeld.blatt48.json +207 -0
  133. package/fixtures/erwartet/rabatt-einfach.blatt32.json +183 -0
  134. package/fixtures/erwartet/rabatt-einfach.blatt48.json +177 -0
  135. package/fixtures/erwartet/rabatt-trinkgeld.blatt32.json +213 -0
  136. package/fixtures/erwartet/rabatt-trinkgeld.blatt48.json +207 -0
  137. package/fixtures/erwartet/rabatt-wertgutschein.blatt32.json +219 -0
  138. package/fixtures/erwartet/rabatt-wertgutschein.blatt48.json +213 -0
  139. package/fixtures/erwartet/rabattzeilen.blatt32.json +201 -0
  140. package/fixtures/erwartet/rabattzeilen.blatt48.json +195 -0
  141. package/fixtures/erwartet/signaturausfall-verkauf.blatt32.json +195 -0
  142. package/fixtures/erwartet/signaturausfall-verkauf.blatt48.json +183 -0
  143. package/fixtures/erwartet/storno-rabatt.blatt32.json +249 -0
  144. package/fixtures/erwartet/storno-rabatt.blatt48.json +237 -0
  145. package/fixtures/erwartet/storno-rabatt.grid32.txt +2 -0
  146. package/fixtures/erwartet/storno-rabatt.grid48.txt +2 -0
  147. package/fixtures/erwartet/storno-teil.blatt32.json +207 -0
  148. package/fixtures/erwartet/storno-teil.blatt48.json +201 -0
  149. package/fixtures/erwartet/storno-teil.grid32.txt +2 -0
  150. package/fixtures/erwartet/storno-teil.grid48.txt +2 -0
  151. package/fixtures/erwartet/storno-voll.blatt32.json +219 -0
  152. package/fixtures/erwartet/storno-voll.blatt48.json +213 -0
  153. package/fixtures/erwartet/storno-voll.grid32.txt +2 -0
  154. package/fixtures/erwartet/storno-voll.grid48.txt +2 -0
  155. package/fixtures/erwartet/testkasse-verkauf.blatt32.json +225 -0
  156. package/fixtures/erwartet/testkasse-verkauf.blatt48.json +219 -0
  157. package/fixtures/erwartet/testkasse-verkauf.grid32.txt +5 -1
  158. package/fixtures/erwartet/testkasse-verkauf.grid48.txt +5 -1
  159. package/fixtures/erwartet/testsignatur-verkauf.blatt32.json +237 -0
  160. package/fixtures/erwartet/testsignatur-verkauf.blatt48.json +219 -0
  161. package/fixtures/erwartet/testsignatur-verkauf.grid32.txt +5 -1
  162. package/fixtures/erwartet/testsignatur-verkauf.grid48.txt +5 -1
  163. package/fixtures/erwartet/training.blatt32.json +237 -0
  164. package/fixtures/erwartet/training.blatt48.json +219 -0
  165. package/fixtures/erwartet/training.grid32.txt +2 -0
  166. package/fixtures/erwartet/training.grid48.txt +2 -0
  167. package/fixtures/erwartet/verkauf-bar.blatt32.json +177 -0
  168. package/fixtures/erwartet/verkauf-bar.blatt48.json +171 -0
  169. package/fixtures/erwartet/verkauf-karte.blatt32.json +177 -0
  170. package/fixtures/erwartet/verkauf-karte.blatt48.json +171 -0
  171. package/fixtures/erwartet/verkauf-kleinunternehmer.blatt32.json +183 -0
  172. package/fixtures/erwartet/verkauf-kleinunternehmer.blatt48.json +177 -0
  173. package/fixtures/hobex-hps-codes.json +1 -1
  174. package/fixtures/kasse-settings-standard.json +1 -1
  175. package/fixtures/kasse-texte.json +1 -1
  176. package/fixtures/manifest.json +109 -43
  177. package/fixtures/oberflaeche.json +87 -1
  178. package/fixtures/rechnung-api-beispiele/credit-fehler-grund.json +24 -0
  179. package/fixtures/rechnung-api-beispiele/credit-ok.json +20 -0
  180. package/fixtures/rechnung-api-beispiele/customer-fehler-uid.json +25 -0
  181. package/fixtures/rechnung-api-beispiele/customer-get-fehler-beide.json +15 -0
  182. package/fixtures/rechnung-api-beispiele/customer-ok.json +20 -0
  183. package/fixtures/rechnung-api-beispiele/customer-search-fehler-leer.json +14 -0
  184. package/fixtures/rechnung-api-beispiele/issue-brutto-20.json +27 -0
  185. package/fixtures/rechnung-api-beispiele/issue-fehler-cent.json +26 -0
  186. package/fixtures/rechnung-api-beispiele/issue-fehler-menge.json +26 -0
  187. package/fixtures/rechnung-api-beispiele/issue-fehler-negativ.json +26 -0
  188. package/fixtures/rechnung-api-beispiele/issue-fehler-satz.json +26 -0
  189. package/fixtures/rechnung-api-beispiele/issue-fehler-unbekannt.json +27 -0
  190. package/fixtures/rechnung-api-beispiele/issue-gemischt.json +33 -0
  191. package/fixtures/rechnung-api-beispiele/issue-kleinunternehmer.json +27 -0
  192. package/fixtures/rechnung-api-beispiele/issue-netto-20.json +28 -0
  193. package/fixtures/rechnung-api-beispiele/issue-rabatt.json +28 -0
  194. package/fixtures/rechnung-api.schema.json +672 -0
  195. package/package.json +12 -1
@@ -1,11 +1,12 @@
1
1
  import type { ReceiptLayout } from './layout.js';
2
2
  import type { PosPaperSize } from '../printing/escpos.js';
3
- import { type QrModulGroesse } from '../printing/index.js';
3
+ import { type QrModulGroesse, type RasterBild } from '../printing/index.js';
4
+ import { type DruckLogo } from './layout-escpos.js';
4
5
  /**
5
6
  * ePOS-Print XML (Epson TM-Drucker: Server Direct Print, ePOS-Print ueber
6
7
  * HTTP) -- **aus dem Zeichenraster**: jede Rasterzeile wird eine <text>-Zeile
7
- * mit exakt N Zeichen und Zeilenumbruch, Aufdrucke doppelt hoch (Warnungen
8
- * invers), QR als <symbol>, Leerraum als <feed>, am Ende Schnitt. Kein
8
+ * mit exakt N Zeichen und Zeilenumbruch, Aufdrucke als Rahmen aus
9
+ * Rasterzeilen, QR als <symbol>, Leerraum als <feed>, am Ende Schnitt. Kein
9
10
  * eigenes Setzen -- was Bildschirm, ESC/POS und PDF zeigen, druckt der Epson
10
11
  * Zeile fuer Zeile genauso.
11
12
  *
@@ -22,16 +23,20 @@ export interface EposPrintXmlOptions {
22
23
  */
23
24
  qrBreite?: number;
24
25
  /**
25
- * Deckel fuer die gerechnete QR-Modulgroesse; Vorgabe `mittel`.
26
+ * Deckel fuer die gerechnete QR-Modulgroesse; Vorgabe `auto` (hoechstens 6
27
+ * Punkte je Modul) -- wie ESC/POS, Blatt und der Flutter-Zwilling.
26
28
  *
27
- * Warum `mittel` und nicht `auto`: `auto` deckelt beim Bestandswert des
28
- * **ESC/POS**-Befehls dieses Pakets (4), dieser Weg druckt aber seit jeher
29
- * mit 6. Die Vorgabe nennt den Bestandswert dieses Wegs also ausdruecklich,
30
- * damit ohne Wahl kein Byte anders herauskommt.
29
+ * Bis 0.13 stand hier `mittel`, weil `auto` damals beim ESC/POS-Wert 4
30
+ * deckelte und dieser Weg seit jeher mit 6 druckt. Seit `auto` ueberall 6
31
+ * heisst, ist das derselbe Wert: ohne Wahl kommt kein Byte anders heraus.
31
32
  */
32
33
  qrGroesse?: QrModulGroesse;
33
34
  /** Papierschnitt am Ende, Vorgabe true. */
34
35
  cut?: boolean;
36
+ /** Firmenlogo; ohne Angabe kein Logo. */
37
+ logo?: DruckLogo | null;
38
+ /** "erstellt mit Kasseneck" am Ende. */
39
+ marke?: boolean;
35
40
  }
36
41
  /**
37
42
  * Das XML **samt** dem, was dem QR unterwegs zugestossen ist -- dieselbe
@@ -45,6 +50,8 @@ export interface EposPrintErgebnis {
45
50
  qrAusweich: string | null;
46
51
  }
47
52
  export declare function eposXmlEscape(text: string): string;
53
+ /** Ein Rasterbild als ePOS-`<image>` (einfarbig, Punktmass, Rasterzeilen Base64). */
54
+ export declare function eposBildXml(bild: RasterBild): string;
48
55
  /** Nur das XML; wer die QR-Meldungen braucht, nimmt [eposPrintXmlErgebnis]. */
49
56
  export declare function eposPrintXml(layout: ReceiptLayout, options?: EposPrintXmlOptions): string;
50
57
  /**
@@ -77,6 +84,10 @@ export interface EposDirectOptions {
77
84
  qrBreite?: number;
78
85
  /** Deckel fuer die gerechnete QR-Modulgroesse; siehe [EposPrintXmlOptions.qrGroesse]. */
79
86
  qrGroesse?: QrModulGroesse;
87
+ /** Firmenlogo; ohne Angabe kein Logo. */
88
+ logo?: DruckLogo | null;
89
+ /** "erstellt mit Kasseneck" am Ende. */
90
+ marke?: boolean;
80
91
  timeoutMs?: number;
81
92
  }
82
93
  export interface EposResponse {
@@ -1,8 +1,13 @@
1
- import { QR_DRUCK_PUNKTE, QR_MINDEST_PUNKTE, QR_MODUL_DECKEL, qrGroesseFuer, } from '../printing/index.js';
2
- import { renderReceiptGrid, ZEICHEN_JE_PAPIER } from './grid.js';
1
+ import { QR_DRUCK_PUNKTE, QR_MINDEST_PUNKTE, QR_MODUL_DECKEL, qrGroesseFuer, qrPasstInVersion, rasterZeilenBase64, } from '../printing/index.js';
2
+ import { ZEICHEN_JE_PAPIER } from './grid.js';
3
+ import { blattFuerDruck } from './layout-escpos.js';
3
4
  export function eposXmlEscape(text) {
4
5
  return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&apos;');
5
6
  }
7
+ /** Ein Rasterbild als ePOS-`<image>` (einfarbig, Punktmass, Rasterzeilen Base64). */
8
+ export function eposBildXml(bild) {
9
+ return `<image width="${bild.breite}" height="${bild.hoehe}" color="color_1" mode="mono">${rasterZeilenBase64(bild)}</image>`;
10
+ }
6
11
  const NS = 'http://www.epson-pos.com/schemas/2011/03/epos-print';
7
12
  /** Nur das XML; wer die QR-Meldungen braucht, nimmt [eposPrintXmlErgebnis]. */
8
13
  export function eposPrintXml(layout, options = {}) {
@@ -19,8 +24,9 @@ export function eposPrintXml(layout, options = {}) {
19
24
  * laesst es weg; genau daran fehlte am echten Beleg der QR.
20
25
  */
21
26
  export function eposPrintXmlErgebnis(layout, options = {}) {
22
- const grid = renderReceiptGrid(layout, { zeichen: options.zeichen ?? ZEICHEN_JE_PAPIER[layout.paperSize] });
23
- const deckel = options.qrGroesse ?? 'mittel';
27
+ const zeichen = options.zeichen ?? ZEICHEN_JE_PAPIER[layout.paperSize];
28
+ const deckel = options.qrGroesse ?? 'auto';
29
+ const blatt = blattFuerDruck(layout, { zeichen, logo: options.logo, marke: options.marke, qrGroesse: deckel });
24
30
  const fest = options.qrBreite === undefined
25
31
  ? null
26
32
  : Math.min(16, Math.max(3, Math.floor(options.qrBreite)));
@@ -39,6 +45,12 @@ export function eposPrintXmlErgebnis(layout, options = {}) {
39
45
  return fest;
40
46
  if (nutzlast === '')
41
47
  return QR_MODUL_DECKEL[deckel];
48
+ // Passt der Inhalt in keine QR-Version, gibt es kein Symbol, das der
49
+ // Drucker setzen koennte -- wie beim Blatt (Anteil 0) geht der Beleg ohne QR.
50
+ if (!qrPasstInVersion(nutzlast)) {
51
+ qrFehler = 'QR-Inhalt passt in keine QR-Version -- Beleg ohne QR';
52
+ return null;
53
+ }
42
54
  const mass = qrGroesseFuer({
43
55
  nutzlast,
44
56
  papierbreitePunkte: QR_DRUCK_PUNKTE[layout.paperSize],
@@ -63,32 +75,34 @@ export function eposPrintXmlErgebnis(layout, options = {}) {
63
75
  out.push('<text font="font_a"/>');
64
76
  out.push('<text align="left"/>');
65
77
  out.push('<text width="1" height="1" reverse="false" em="false"/>');
66
- for (const z of grid.lines) {
67
- switch (z.kind) {
68
- case 'space':
69
- out.push('<feed line="1"/>');
78
+ for (const block of blatt.bloecke) {
79
+ switch (block.art) {
80
+ case 'zeile':
81
+ if (block.leer)
82
+ out.push('<feed line="1"/>');
83
+ else if (block.fett) {
84
+ out.push(`<text em="true">${eposXmlEscape(block.text)}&#10;</text>`);
85
+ out.push('<text em="false"/>');
86
+ }
87
+ else
88
+ out.push(`<text>${eposXmlEscape(block.text)}&#10;</text>`);
89
+ break;
90
+ case 'logo':
91
+ if (options.logo) {
92
+ out.push('<text align="center"/>');
93
+ out.push(eposBildXml(options.logo.raster));
94
+ out.push('<text align="left"/>');
95
+ }
70
96
  break;
71
97
  case 'qr': {
72
- const breite = qrBreiteFuer(z.qr ?? '');
98
+ const breite = qrBreiteFuer(block.nutzlast);
73
99
  if (breite === null)
74
100
  break;
75
101
  out.push('<text align="center"/>');
76
- out.push(`<symbol type="qrcode_model_2" level="level_m" width="${breite}" height="0" size="0">${eposXmlEscape(z.qr ?? '')}</symbol>`);
102
+ out.push(`<symbol type="qrcode_model_2" level="level_m" width="${breite}" height="0" size="0">${eposXmlEscape(block.nutzlast)}</symbol>`);
77
103
  out.push('<text align="left"/>');
78
104
  break;
79
105
  }
80
- case 'banner':
81
- out.push(`<text width="1" height="2" reverse="${z.ton === 'warnung' ? 'true' : 'false'}" em="true">${eposXmlEscape(z.text)}&#10;</text>`);
82
- out.push('<text width="1" height="1" reverse="false" em="false"/>');
83
- break;
84
- default:
85
- if (z.bold)
86
- out.push(`<text em="true">${eposXmlEscape(z.text)}&#10;</text>`);
87
- else
88
- out.push(`<text>${eposXmlEscape(z.text)}&#10;</text>`);
89
- if (z.bold)
90
- out.push('<text em="false"/>');
91
- break;
92
106
  }
93
107
  }
94
108
  if (options.cut !== false) {
@@ -208,6 +222,10 @@ export function eposDirectPrint(layout, o, fetchFn = fetch) {
208
222
  xmlOptionen.qrBreite = o.qrBreite;
209
223
  if (o.qrGroesse !== undefined)
210
224
  xmlOptionen.qrGroesse = o.qrGroesse;
225
+ if (o.logo)
226
+ xmlOptionen.logo = o.logo;
227
+ if (o.marke !== undefined)
228
+ xmlOptionen.marke = o.marke;
211
229
  return eposDirectSend(eposPrintXml({ ...layout, paperSize: papier }, xmlOptionen), o, fetchFn);
212
230
  }
213
231
  /** Verbindungstest: leeres Dokument, druckt nichts, liefert den Druckerstatus. */
@@ -27,7 +27,7 @@ export interface GridLine {
27
27
  text: string;
28
28
  kind: GridLineKind;
29
29
  bold: boolean;
30
- /** Bei `banner`: `warnung` = invers/auffaellig, `belegart` = Rahmen. */
30
+ /** Bei `banner` (Rahmen- und Textzeilen): `warnung` = Test/Ausfall, `belegart` = Storno, Nullbeleg … */
31
31
  ton?: 'belegart' | 'warnung';
32
32
  /** Bei `qr`: die Nutzlast. */
33
33
  qr?: string;
@@ -61,10 +61,18 @@ export function renderReceiptGrid(layout, options = {}) {
61
61
  for (const t of wortzeilenText(z.text, zeichen))
62
62
  lines.push({ text: ausrichten(t, zeichen, z.align), kind: 'text', bold: z.bold });
63
63
  break;
64
- case 'banner':
64
+ case 'banner': {
65
+ // Der Rahmen ist Teil des Rasters: eine Zeile '=' ueber die volle Breite
66
+ // davor und danach. So setzt ihn jeder Weg -- Bildschirm, Bon, ePOS, PDF,
67
+ // myPOS -- Zeichen fuer Zeichen gleich. Vorher zeichnete jeder Weg seinen
68
+ // eigenen Rahmen (Rechteck, doppelt hoch, invers, gefuellt).
69
+ const rahmen = '='.repeat(zeichen);
70
+ lines.push({ text: rahmen, kind: 'banner', bold: true, ton: z.ton });
65
71
  for (const t of wortzeilenText(z.text, zeichen))
66
72
  lines.push({ text: ausrichten(t, zeichen, 'center'), kind: 'banner', bold: true, ton: z.ton });
73
+ lines.push({ text: rahmen, kind: 'banner', bold: true, ton: z.ton });
67
74
  break;
75
+ }
68
76
  case 'rule':
69
77
  lines.push({ text: (z.char || '-').charAt(0).repeat(zeichen), kind: 'rule', bold: false });
70
78
  break;
@@ -1,4 +1,6 @@
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, type EscPosLayoutErgebnis, type QrPrintMode, escPosLayoutBytes, escPosLayoutErgebnis, } from './layout-escpos.js';
2
+ export { type EscPosLayoutOptions, type EscPosLayoutErgebnis, type QrPrintMode, type DruckLogo, pruefeLogoRaster, 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 EposPrintErgebnis, type EposDirectOptions, type EposResponse, EposConnectionError, eposPrintXml, eposPrintXmlErgebnis, eposXmlEscape, eposServiceUrl, eposSoapEnvelope, eposParseResponse, eposDirectPrint, eposDirectStatus } from './epos.js';
4
+ export { type EposPrintXmlOptions, type EposPrintErgebnis, type EposDirectOptions, type EposResponse, EposConnectionError, eposPrintXml, eposPrintXmlErgebnis, eposXmlEscape, eposBildXml, eposServiceUrl, eposSoapEnvelope, eposParseResponse, eposDirectPrint, eposDirectStatus } from './epos.js';
5
+ export { type LogoStufe, type BlattLogo, type LogoMass, type BlattBlock, type BelegBlatt, type BelegBlattOptionen, LOGO_STUFEN, LOGO_PIXEL_MAX, PUNKTE_JE_ZEICHEN, PUNKTE_JE_ZEILE, MARKE_TEXT, papierFuerZeichen, logoMass, logoPixelZulaessig, logoRasterMass, qrBlattAnteil, belegBlatt, } from './blatt.js';
6
+ export { logoRaster } from './bild.js';
@@ -1,4 +1,6 @@
1
1
  export { buildReceiptLayout, receiptSignatureFailed, receiptSignatureIsTest, receiptIsZero, receiptAmountsAreZero, receiptIsSmallBusinessConsistent, receiptZdaText, AKTUELLES_REGELWERK, SMALL_BUSINESS_NOTICE, formatCents, } from './layout.js';
2
- export { escPosLayoutBytes, escPosLayoutErgebnis, } from './layout-escpos.js';
2
+ export { pruefeLogoRaster, escPosLayoutBytes, escPosLayoutErgebnis, } from './layout-escpos.js';
3
3
  export { renderReceiptGrid, gridSpaltenBreiten, gridAlsText, ZEICHEN_JE_PAPIER, } from './grid.js';
4
- export { EposConnectionError, eposPrintXml, eposPrintXmlErgebnis, eposXmlEscape, eposServiceUrl, eposSoapEnvelope, eposParseResponse, eposDirectPrint, eposDirectStatus } from './epos.js';
4
+ export { EposConnectionError, eposPrintXml, eposPrintXmlErgebnis, eposXmlEscape, eposBildXml, eposServiceUrl, eposSoapEnvelope, eposParseResponse, eposDirectPrint, eposDirectStatus } from './epos.js';
5
+ export { LOGO_STUFEN, LOGO_PIXEL_MAX, PUNKTE_JE_ZEICHEN, PUNKTE_JE_ZEILE, MARKE_TEXT, papierFuerZeichen, logoMass, logoPixelZulaessig, logoRasterMass, qrBlattAnteil, belegBlatt, } from './blatt.js';
6
+ export { logoRaster } from './bild.js';
@@ -1,5 +1,6 @@
1
- import { type PosCodeTable, type PosCutMode, type PosPaperSize, type QrCorrection, type QrMatrix, type QrModulGroesse, type QrSize } from '../printing/index.js';
1
+ import { type PosCodeTable, type PosCutMode, type PosPaperSize, type QrCorrection, type QrMatrix, type QrModulGroesse, type QrSize, type RasterBild } from '../printing/index.js';
2
2
  import type { ReceiptLayout } from './layout.js';
3
+ import { type BelegBlatt, type LogoStufe, type LogoMass } from './blatt.js';
3
4
  /**
4
5
  * Bruecke vom Layout-Modell zu ESC/POS-Bytes — der Bondrucker-Ausgabeweg.
5
6
  *
@@ -28,6 +29,29 @@ import type { ReceiptLayout } from './layout.js';
28
29
  * (`qrMatrix`), weil dieses Paket keine QR-Codes rechnet.
29
30
  */
30
31
  export type QrPrintMode = 'native' | 'nativeModel1' | 'imageRaster';
32
+ /** Firmenlogo fuer den Druck: Stufe, Pixelmass des Originals und das fertige Rasterbild (`logoRaster`). */
33
+ export interface DruckLogo {
34
+ stufe: LogoStufe;
35
+ pxBreite: number;
36
+ pxHoehe: number;
37
+ raster: RasterBild;
38
+ }
39
+ /** Das Rasterbild muss genau so gross sein, wie das Blatt das Logo setzt -- sonst stuende am Bon ein anderes Logo als am Schirm. */
40
+ export declare function pruefeLogoRaster(logo: DruckLogo, mass: LogoMass, zeichen: number): void;
41
+ /**
42
+ * Baut das Blatt fuer einen Druckweg (ESC/POS, ePOS) an EINER Stelle: die
43
+ * Zuordnung `DruckLogo` -> `BlattLogo` und die Groessenpruefung des mitgebrachten
44
+ * Rasterbilds teilen sich beide Wege -- ohne das muesste eine Aenderung daran an
45
+ * zwei Stellen nachgezogen werden. Wirft VOR jeder Ausgabe (also bevor der
46
+ * Aufrufer auch nur ein Byte/Zeichen geschrieben hat), wenn das Rasterbild nicht
47
+ * zur Logo-Stufe passt.
48
+ */
49
+ export declare function blattFuerDruck(layout: ReceiptLayout, optionen: {
50
+ zeichen: number;
51
+ logo?: DruckLogo | null;
52
+ marke?: boolean;
53
+ qrGroesse: QrModulGroesse;
54
+ }): BelegBlatt;
31
55
  export interface EscPosLayoutOptions {
32
56
  /** Papierbreite; Vorgabe ist die des Layouts (dessen Spaltenbreiten daran haengen). */
33
57
  paperSize?: PosPaperSize;
@@ -40,9 +64,9 @@ export interface EscPosLayoutOptions {
40
64
  * dann passt der Aufrufer selbst auf, dass das Symbol aufs Papier geht.
41
65
  */
42
66
  qrSize?: QrSize;
43
- /** Fehlerkorrekturstufe des QR-Codes. */
67
+ /** Fehlerkorrekturstufe des QR-Codes; Vorgabe `M` (wie ePOS, Bildweg und Blatt). */
44
68
  qrCorrection?: QrCorrection;
45
- /** Deckel fuer die gerechnete Modulgroesse; Vorgabe `auto`. */
69
+ /** Deckel fuer die gerechnete Modulgroesse; Vorgabe `auto` (hoechstens 6 Punkte je Modul). */
46
70
  qrGroesse?: QrModulGroesse;
47
71
  /** Druckweg des QR; Vorgabe `native` (der Bestandsweg). */
48
72
  qrModus?: QrPrintMode;
@@ -54,6 +78,10 @@ export interface EscPosLayoutOptions {
54
78
  * aus der QR-Bibliothek, die er ohnehin fuer den Bildschirm benutzt.
55
79
  */
56
80
  qrMatrix?: (nutzlast: string) => QrMatrix;
81
+ /** Firmenlogo; ohne Angabe kein Logo (Bestand). */
82
+ logo?: DruckLogo | null;
83
+ /** "erstellt mit Kasseneck" am Ende (Konto-Flag `kreiseck_logo`). */
84
+ marke?: boolean;
57
85
  }
58
86
  /**
59
87
  * Der Bytestrom **samt** dem, was dem QR unterwegs zugestossen ist.
@@ -1,5 +1,35 @@
1
- import { createEscPosDocument, escPosBytes, escPosCut, escPosFeed, escPosPrintableText, escPosQrCode, escPosQrRaster, escPosReset, escPosText, QR_DRUCK_PUNKTE, qrGroesseFuer, } from '../printing/index.js';
2
- import { renderReceiptGrid, ZEICHEN_JE_PAPIER } from './grid.js';
1
+ import { createEscPosDocument, escPosBytes, escPosCut, escPosFeed, escPosPrintableText, escPosQrCode, escPosQrRaster, escPosRasterBild, escPosReset, escPosText, QR_DRUCK_PUNKTE, qrGroesseFuer, qrPasstInVersion, } from '../printing/index.js';
2
+ import { belegBlatt, logoRasterMass } from './blatt.js';
3
+ import { ZEICHEN_JE_PAPIER } from './grid.js';
4
+ /** Das Rasterbild muss genau so gross sein, wie das Blatt das Logo setzt -- sonst stuende am Bon ein anderes Logo als am Schirm. */
5
+ export function pruefeLogoRaster(logo, mass, zeichen) {
6
+ const soll = logoRasterMass(mass, zeichen);
7
+ if (logo.raster.breite !== soll.breite || logo.raster.hoehe !== soll.hoehe) {
8
+ throw new Error(`Logo-Raster ${logo.raster.breite}x${logo.raster.hoehe} passt nicht zum Blatt (${soll.breite}x${soll.hoehe})`);
9
+ }
10
+ }
11
+ /**
12
+ * Baut das Blatt fuer einen Druckweg (ESC/POS, ePOS) an EINER Stelle: die
13
+ * Zuordnung `DruckLogo` -> `BlattLogo` und die Groessenpruefung des mitgebrachten
14
+ * Rasterbilds teilen sich beide Wege -- ohne das muesste eine Aenderung daran an
15
+ * zwei Stellen nachgezogen werden. Wirft VOR jeder Ausgabe (also bevor der
16
+ * Aufrufer auch nur ein Byte/Zeichen geschrieben hat), wenn das Rasterbild nicht
17
+ * zur Logo-Stufe passt.
18
+ */
19
+ export function blattFuerDruck(layout, optionen) {
20
+ const blattOptionen = {
21
+ zeichen: optionen.zeichen,
22
+ marke: optionen.marke === true,
23
+ qrGroesse: optionen.qrGroesse,
24
+ };
25
+ if (optionen.logo)
26
+ blattOptionen.logo = { stufe: optionen.logo.stufe, pxBreite: optionen.logo.pxBreite, pxHoehe: optionen.logo.pxHoehe };
27
+ const blatt = belegBlatt(layout, blattOptionen);
28
+ const logoBlock = blatt.bloecke.find((b) => b.art === 'logo');
29
+ if (optionen.logo && logoBlock && logoBlock.art === 'logo')
30
+ pruefeLogoRaster(optionen.logo, logoBlock, blatt.zeichen);
31
+ return blatt;
32
+ }
3
33
  /**
4
34
  * Druckbar gemachtes Layout: Texte durch [escPosPrintableText] (Codepage,
5
35
  * "EUR" statt "€", Striche), damit das Raster mit den Zeichen rechnet, die
@@ -68,6 +98,13 @@ export function escPosLayoutErgebnis(layout, options = {}) {
68
98
  * `qrFehler` aus `escPosQrCode`, und der Beleg geht ohne QR hinaus.
69
99
  */
70
100
  const qrZeile = (nutzlast) => {
101
+ // Ein Inhalt, der in keine QR-Version passt, laesst sich weder als Befehl
102
+ // noch als Bild drucken -- beide Wege wuerfen. Der Beleg geht ohne QR
103
+ // hinaus und sagt es ueber `qrFehler`, wie das Blatt (Anteil 0).
104
+ if (nutzlast !== '' && !qrPasstInVersion(nutzlast)) {
105
+ doc.qrFehler = 'QR-Inhalt passt in keine QR-Version -- Beleg ohne QR';
106
+ return;
107
+ }
71
108
  const raster = options.qrMatrix;
72
109
  if (nutzlast !== '' && raster !== undefined) {
73
110
  if (modus === 'imageRaster') {
@@ -94,27 +131,28 @@ export function escPosLayoutErgebnis(layout, options = {}) {
94
131
  }
95
132
  escPosQrCode(doc, nutzlast, qrOptionen);
96
133
  };
97
- const grid = renderReceiptGrid(druckbaresLayout(layout), { zeichen: ZEICHEN_JE_PAPIER[paperSize] });
98
- for (const zeile of grid.lines) {
99
- switch (zeile.kind) {
100
- case 'space':
101
- escPosFeed(doc, 1);
102
- break;
134
+ // `blattFuerDruck` prueft das Logo-Raster bereits VOR der Rueckgabe -- die
135
+ // Schleife unten schreibt darum nie ein Byte auf ein falsch grosses Logo.
136
+ const blatt = blattFuerDruck(druckbaresLayout(layout), {
137
+ zeichen: ZEICHEN_JE_PAPIER[paperSize],
138
+ logo: options.logo,
139
+ marke: options.marke,
140
+ qrGroesse: options.qrGroesse ?? 'auto',
141
+ });
142
+ for (const block of blatt.bloecke) {
143
+ switch (block.art) {
103
144
  case 'qr':
104
- qrZeile(zeile.qr ?? '');
145
+ qrZeile(block.nutzlast);
105
146
  break;
106
- case 'banner': {
107
- // Belegart/Warnung: fett zwischen zwei Volllinien — derselbe Rahmen-
108
- // Stil wie im Beleg-Viewer und im PDF (frueher druckte der Bon invers
109
- // weiss-auf-schwarz und sah damit anders aus als jede Anzeige).
110
- const rahmen = '='.repeat(ZEICHEN_JE_PAPIER[paperSize]);
111
- escPosText(doc, rahmen, { styles: { align: 'left', bold: true } });
112
- escPosText(doc, zeile.text.trimEnd(), { styles: { align: 'left', bold: true, height: 2 } });
113
- escPosText(doc, rahmen, { styles: { align: 'left', bold: true } });
147
+ case 'logo':
148
+ if (options.logo)
149
+ escPosRasterBild(doc, options.logo.raster, { align: 'center' });
114
150
  break;
115
- }
116
- default:
117
- escPosText(doc, zeile.text.trimEnd(), { styles: { align: 'left', bold: zeile.bold } });
151
+ case 'zeile':
152
+ if (block.leer)
153
+ escPosFeed(doc, 1);
154
+ else
155
+ escPosText(doc, block.text.trimEnd(), { styles: { align: 'left', bold: block.fett } });
118
156
  break;
119
157
  }
120
158
  }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Fassade ueber den Aufrufen der Rechnungs-API: Schluessel einmal binden, dann rufen.
3
+ *
4
+ * Wie [createPartnerApi] bewusst **keine** Klasse — die Aufrufe sind freie
5
+ * Funktionen (endpunkte.ts) und bleiben einzeln importierbar.
6
+ */
7
+ import { type FetchLike } from '../client/transport.js';
8
+ import type { EInvoiceFormat } from './vertrag.js';
9
+ import type { CancelInvoiceRequest, CancelResult, CreditNoteRequest, CreditNoteResult, Customer, CustomerInput, CustomerPage, CustomerSearch, InvoiceDetail, InvoiceListQuery, InvoicePage, IssueInvoiceRequest, IssueResult } from './typen.js';
10
+ export interface RechnungApiOptions {
11
+ /** `api_key` des Kontos (`kr_live_…` / `kr_test_…`). Gehoert auf einen Server. */
12
+ apiKey: string;
13
+ /** Abweichende Basis-URL; Vorgabe `https://api.kasseneck.at/v1`. */
14
+ baseUrl?: string;
15
+ /** Zeitlimit je Aufruf in Millisekunden. */
16
+ timeoutMs?: number;
17
+ /** Eigene `fetch`-Umsetzung (Tests, Proxys). */
18
+ fetch?: FetchLike;
19
+ }
20
+ export interface RechnungApi {
21
+ createCustomer(customer: CustomerInput, optionen?: {
22
+ idempotencyKey?: string;
23
+ }): Promise<Customer>;
24
+ getCustomer(kennung: {
25
+ customerId: string;
26
+ } | {
27
+ externalId: string;
28
+ }): Promise<Customer>;
29
+ updateCustomer(customerId: string, patch: Partial<CustomerInput>): Promise<Customer>;
30
+ searchCustomers(suche: CustomerSearch): Promise<CustomerPage>;
31
+ issueInvoice(anfrage: IssueInvoiceRequest): Promise<IssueResult>;
32
+ cancelInvoice(anfrage: CancelInvoiceRequest): Promise<CancelResult>;
33
+ createCreditNote(anfrage: CreditNoteRequest): Promise<CreditNoteResult>;
34
+ getInvoice(kennung: {
35
+ invoiceId: string;
36
+ } | {
37
+ number: string;
38
+ }): Promise<InvoiceDetail>;
39
+ listInvoices(abfrage?: InvoiceListQuery): Promise<InvoicePage>;
40
+ getInvoicePdf(invoiceId: string): Promise<Uint8Array>;
41
+ getInvoiceXml(invoiceId: string, format?: EInvoiceFormat): Promise<string>;
42
+ }
43
+ export declare function createRechnungApi(optionen: RechnungApiOptions): RechnungApi;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Fassade ueber den Aufrufen der Rechnungs-API: Schluessel einmal binden, dann rufen.
3
+ *
4
+ * Wie [createPartnerApi] bewusst **keine** Klasse — die Aufrufe sind freie
5
+ * Funktionen (endpunkte.ts) und bleiben einzeln importierbar.
6
+ */
7
+ import { createBinaryTransport, createTransport } from '../client/transport.js';
8
+ import { rechnungKeyAuth } from './auth.js';
9
+ import { cancelInvoice, createCreditNote, createCustomer, getCustomer, getInvoice, getInvoicePdf, getInvoiceXml, issueInvoice, listInvoices, searchCustomers, updateCustomer, } from './endpunkte.js';
10
+ export function createRechnungApi(optionen) {
11
+ const transportOptionen = {
12
+ auth: rechnungKeyAuth({ apiKey: optionen.apiKey }),
13
+ baseUrl: optionen.baseUrl,
14
+ timeoutMs: optionen.timeoutMs,
15
+ fetch: optionen.fetch,
16
+ };
17
+ const rufen = createTransport(transportOptionen);
18
+ const rufenBinaer = createBinaryTransport(transportOptionen);
19
+ return {
20
+ createCustomer: (customer, o) => createCustomer(rufen, customer, o),
21
+ getCustomer: (kennung) => getCustomer(rufen, kennung),
22
+ updateCustomer: (id, patch) => updateCustomer(rufen, id, patch),
23
+ searchCustomers: (suche) => searchCustomers(rufen, suche),
24
+ issueInvoice: (anfrage) => issueInvoice(rufen, anfrage),
25
+ cancelInvoice: (anfrage) => cancelInvoice(rufen, anfrage),
26
+ createCreditNote: (anfrage) => createCreditNote(rufen, anfrage),
27
+ getInvoice: (kennung) => getInvoice(rufen, kennung),
28
+ listInvoices: (abfrage) => listInvoices(rufen, abfrage),
29
+ getInvoicePdf: (id) => getInvoicePdf(rufenBinaer, id),
30
+ getInvoiceXml: (id, format) => getInvoiceXml(rufen, id, format),
31
+ };
32
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Anmeldung fuer die Rechnungs-API: der `api_key` des Kontos als Bearer.
3
+ *
4
+ * Anders als `apiKeyAuth` **ohne** `cashregister-token` — eine Rechnung
5
+ * entsteht nicht an einer Kasse, sondern am Konto.
6
+ *
7
+ * **Der Schluessel gehoert auf einen Server.** Mit ihm entstehen festgeschriebene
8
+ * Rechnungen mit fortlaufender Nummer, die sich nur noch per Gutschrift
9
+ * korrigieren lassen.
10
+ *
11
+ * Geprueft wird nur, was ohne Netz sicher falsch ist: ein leerer Wert, ein
12
+ * Partner-Schluessel (`pk_…`) oder ein Kassen-Token (`cb_…`). Die Form des
13
+ * Kontoschluessels selbst bleibt offen, weil das Backend neben `kr_<env>_…`
14
+ * weiterhin aeltere Formen annimmt. Die Meldung nennt nie den Wert, nur seine Art.
15
+ */
16
+ import type { KasseneckAuth } from '../client/auth.js';
17
+ export interface RechnungKeyAuthOptions {
18
+ /** `api_key` des Kontos, z. B. `kr_live_…` oder `kr_test_…`. */
19
+ apiKey: string;
20
+ }
21
+ export declare function rechnungKeyAuth(options: RechnungKeyAuthOptions): KasseneckAuth;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Anmeldung fuer die Rechnungs-API: der `api_key` des Kontos als Bearer.
3
+ *
4
+ * Anders als `apiKeyAuth` **ohne** `cashregister-token` — eine Rechnung
5
+ * entsteht nicht an einer Kasse, sondern am Konto.
6
+ *
7
+ * **Der Schluessel gehoert auf einen Server.** Mit ihm entstehen festgeschriebene
8
+ * Rechnungen mit fortlaufender Nummer, die sich nur noch per Gutschrift
9
+ * korrigieren lassen.
10
+ *
11
+ * Geprueft wird nur, was ohne Netz sicher falsch ist: ein leerer Wert, ein
12
+ * Partner-Schluessel (`pk_…`) oder ein Kassen-Token (`cb_…`). Die Form des
13
+ * Kontoschluessels selbst bleibt offen, weil das Backend neben `kr_<env>_…`
14
+ * weiterhin aeltere Formen annimmt. Die Meldung nennt nie den Wert, nur seine Art.
15
+ */
16
+ import { KasseneckAuthError } from '../client/errors.js';
17
+ export function rechnungKeyAuth(options) {
18
+ const schluessel = typeof options?.apiKey === 'string' ? options.apiKey.trim() : '';
19
+ if (!schluessel) {
20
+ throw new KasseneckAuthError('rechnungKeyAuth: apiKey fehlt');
21
+ }
22
+ if (/^pk_(live|test)_/i.test(schluessel)) {
23
+ throw new KasseneckAuthError('rechnungKeyAuth: das ist ein Partner-Schluessel (pk_…) — die Rechnungs-API nimmt den api_key des Kontos (kr_…)');
24
+ }
25
+ if (/^cb_(live|test)_/i.test(schluessel)) {
26
+ throw new KasseneckAuthError('rechnungKeyAuth: das ist ein Kassen-Token (cb_…) — die Rechnungs-API nimmt den api_key des Kontos (kr_…)');
27
+ }
28
+ // Pro Aufruf ein frisches Objekt, wie bei den anderen Anmeldungen.
29
+ return () => ({
30
+ headers: { Authorization: `Bearer ${schluessel}` },
31
+ params: {},
32
+ });
33
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Die Aufrufe der Rechnungs-API — Kunden, Rechnungen, Gutschriften, Dateien.
3
+ *
4
+ * Jede Funktion nimmt den Transport als ersten Parameter und ist einzeln
5
+ * importierbar; die Fassade [createRechnungApi] bindet ihn nur einmal.
6
+ *
7
+ * **Geprueft wird hier nichts Fachliches.** Die Anfrage geht unveraendert an
8
+ * das Backend, das sie gegen denselben Vertrag prueft (`vertrag.ts`) und einen
9
+ * Formfehler als `validation` mit `errors[]` zurueckgibt — zwei Pruefungen
10
+ * hiessen zwei Wahrheiten, von denen eine veraltet.
11
+ *
12
+ * **Nicht automatisch wiederholen.** Wer nach einem Zeitlimit erneut
13
+ * ausstellt, tut das mit **demselben** `idempotencyKey`: dann kommt die schon
14
+ * ausgestellte Rechnung zurueck (`replayed: true`) statt einer zweiten.
15
+ *
16
+ * Nach dem Senden wird nichts hart gecastet: fehlt ein zugesagtes Feld, wirft
17
+ * der Aufruf `KasseneckValidationError` mit `scope:'response'`.
18
+ */
19
+ import type { InternerBinaerTransport, InternerTransport } from '../client/aufrufe.js';
20
+ import type { EInvoiceFormat } from './vertrag.js';
21
+ import type { CancelInvoiceRequest, CancelResult, CreditNoteRequest, CreditNoteResult, Customer, CustomerInput, CustomerPage, CustomerSearch, InvoiceDetail, InvoiceListQuery, InvoicePage, IssueInvoiceRequest, IssueResult } from './typen.js';
22
+ export declare function createCustomer(rufen: InternerTransport, customer: CustomerInput, optionen?: {
23
+ idempotencyKey?: string;
24
+ }): Promise<Customer>;
25
+ export declare function getCustomer(rufen: InternerTransport, kennung: {
26
+ customerId: string;
27
+ } | {
28
+ externalId: string;
29
+ }): Promise<Customer>;
30
+ export declare function updateCustomer(rufen: InternerTransport, customerId: string, patch: Partial<CustomerInput>): Promise<Customer>;
31
+ export declare function searchCustomers(rufen: InternerTransport, suche: CustomerSearch): Promise<CustomerPage>;
32
+ export declare function issueInvoice(rufen: InternerTransport, anfrage: IssueInvoiceRequest): Promise<IssueResult>;
33
+ export declare function cancelInvoice(rufen: InternerTransport, anfrage: CancelInvoiceRequest): Promise<CancelResult>;
34
+ export declare function createCreditNote(rufen: InternerTransport, anfrage: CreditNoteRequest): Promise<CreditNoteResult>;
35
+ export declare function getInvoice(rufen: InternerTransport, kennung: {
36
+ invoiceId: string;
37
+ } | {
38
+ number: string;
39
+ }): Promise<InvoiceDetail>;
40
+ export declare function listInvoices(rufen: InternerTransport, abfrage?: InvoiceListQuery): Promise<InvoicePage>;
41
+ /** Das PDF der Rechnung (bei Gutschriften: der Gutschrift), mit eingebetteter Factur-X-Datei. */
42
+ export declare function getInvoicePdf(rufenBinaer: InternerBinaerTransport, invoiceId: string): Promise<Uint8Array>;
43
+ /**
44
+ * Die E-Rechnung als XML-Text. Sie kommt im gewohnten Umschlag (`data.xml`)
45
+ * und nicht als rohe Datei: so bleibt ein fachlicher Fehler
46
+ * (`einvoice_incomplete` mit `missing[]`) ein gewoehnlicher Fehler.
47
+ */
48
+ export declare function getInvoiceXml(rufen: InternerTransport, invoiceId: string, format?: EInvoiceFormat): Promise<string>;