@kreiseck/kasseneck-api 1.11.1 → 1.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.
Files changed (187) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +2 -1
  3. package/dist/cjs/client/aufrufe.d.ts +4 -4
  4. package/dist/cjs/client/aufrufe.js +15 -0
  5. package/dist/cjs/client/receipts.d.ts +5 -0
  6. package/dist/cjs/client/receipts.js +65 -6
  7. package/dist/cjs/enums/voucher-action.d.ts +2 -0
  8. package/dist/cjs/enums/voucher-action.js +2 -0
  9. package/dist/cjs/enums/voucher-type.d.ts +10 -0
  10. package/dist/cjs/enums/voucher-type.js +10 -0
  11. package/dist/cjs/index.d.ts +3 -3
  12. package/dist/cjs/index.js +8 -3
  13. package/dist/cjs/models/index.d.ts +2 -2
  14. package/dist/cjs/models/index.js +6 -2
  15. package/dist/cjs/models/receipt-errors.d.ts +1 -1
  16. package/dist/cjs/models/receipt-errors.js +2 -0
  17. package/dist/cjs/models/receipt-item.d.ts +11 -3
  18. package/dist/cjs/models/receipt-item.js +22 -1
  19. package/dist/cjs/models/receipt.d.ts +13 -1
  20. package/dist/cjs/models/receipt.js +18 -2
  21. package/dist/cjs/models/voucher.d.ts +57 -4
  22. package/dist/cjs/models/voucher.js +111 -3
  23. package/dist/cjs/partner/fehler.d.ts +1 -1
  24. package/dist/cjs/partner/fehler.js +2 -0
  25. package/dist/cjs/partner/index.d.ts +1 -1
  26. package/dist/cjs/partner/typen.d.ts +7 -1
  27. package/dist/cjs/partner/typen.js +3 -0
  28. package/dist/cjs/pos/client.d.ts +2 -1
  29. package/dist/cjs/pos/client.js +3 -1
  30. package/dist/cjs/pos/gutschein.d.ts +90 -0
  31. package/dist/cjs/pos/gutschein.js +164 -0
  32. package/dist/cjs/pos/index.d.ts +3 -2
  33. package/dist/cjs/pos/index.js +13 -2
  34. package/dist/cjs/pos/settings.d.ts +3 -0
  35. package/dist/cjs/pos/settings.js +12 -10
  36. package/dist/cjs/pos/texte.d.ts +156 -0
  37. package/dist/cjs/pos/texte.js +72 -1
  38. package/dist/cjs/react/index.js +8 -2
  39. package/dist/cjs/receipt/due.d.ts +83 -2
  40. package/dist/cjs/receipt/due.js +202 -36
  41. package/dist/cjs/receipt/grid.js +13 -1
  42. package/dist/cjs/receipt/gutschein-plan.d.ts +104 -0
  43. package/dist/cjs/receipt/gutschein-plan.js +360 -0
  44. package/dist/cjs/receipt/index.d.ts +1 -1
  45. package/dist/cjs/receipt/index.js +3 -0
  46. package/dist/cjs/receipt/layout.js +175 -8
  47. package/dist/cjs/register/pairing.d.ts +21 -1
  48. package/dist/cjs/register/pairing.js +17 -0
  49. package/dist/cjs/stored/bon-layout.d.ts +8 -0
  50. package/dist/cjs/stored/bon-layout.js +23 -0
  51. package/dist/cjs/stored/draht.d.ts +3 -1
  52. package/dist/cjs/stored/draht.js +53 -6
  53. package/dist/cjs/stored/einstellungen.js +2 -1
  54. package/dist/cjs/stored/gutscheine.d.ts +3 -0
  55. package/dist/cjs/stored/gutscheine.js +60 -0
  56. package/dist/cjs/stored/index.js +2 -17
  57. package/dist/cjs/stored/vokabular.d.ts +6 -4
  58. package/dist/cjs/stored/vokabular.js +48 -2
  59. package/dist/cjs/version.d.ts +1 -1
  60. package/dist/cjs/version.js +1 -1
  61. package/dist/esm/client/aufrufe.d.ts +4 -4
  62. package/dist/esm/client/aufrufe.js +15 -0
  63. package/dist/esm/client/receipts.d.ts +5 -0
  64. package/dist/esm/client/receipts.js +66 -8
  65. package/dist/esm/enums/voucher-action.d.ts +2 -0
  66. package/dist/esm/enums/voucher-action.js +2 -0
  67. package/dist/esm/enums/voucher-type.d.ts +10 -0
  68. package/dist/esm/enums/voucher-type.js +10 -0
  69. package/dist/esm/index.d.ts +3 -3
  70. package/dist/esm/index.js +3 -3
  71. package/dist/esm/models/index.d.ts +2 -2
  72. package/dist/esm/models/index.js +2 -2
  73. package/dist/esm/models/receipt-errors.d.ts +1 -1
  74. package/dist/esm/models/receipt-errors.js +2 -0
  75. package/dist/esm/models/receipt-item.d.ts +11 -3
  76. package/dist/esm/models/receipt-item.js +20 -1
  77. package/dist/esm/models/receipt.d.ts +13 -1
  78. package/dist/esm/models/receipt.js +18 -2
  79. package/dist/esm/models/voucher.d.ts +57 -4
  80. package/dist/esm/models/voucher.js +108 -3
  81. package/dist/esm/partner/fehler.d.ts +1 -1
  82. package/dist/esm/partner/fehler.js +2 -0
  83. package/dist/esm/partner/index.d.ts +1 -1
  84. package/dist/esm/partner/typen.d.ts +7 -1
  85. package/dist/esm/partner/typen.js +3 -0
  86. package/dist/esm/pos/client.d.ts +2 -1
  87. package/dist/esm/pos/client.js +3 -1
  88. package/dist/esm/pos/gutschein.d.ts +90 -0
  89. package/dist/esm/pos/gutschein.js +158 -0
  90. package/dist/esm/pos/index.d.ts +3 -2
  91. package/dist/esm/pos/index.js +4 -2
  92. package/dist/esm/pos/settings.d.ts +3 -0
  93. package/dist/esm/pos/settings.js +2 -0
  94. package/dist/esm/pos/texte.d.ts +156 -0
  95. package/dist/esm/pos/texte.js +70 -0
  96. package/dist/esm/react/index.js +8 -2
  97. package/dist/esm/receipt/due.d.ts +83 -2
  98. package/dist/esm/receipt/due.js +198 -34
  99. package/dist/esm/receipt/grid.js +13 -1
  100. package/dist/esm/receipt/gutschein-plan.d.ts +104 -0
  101. package/dist/esm/receipt/gutschein-plan.js +352 -0
  102. package/dist/esm/receipt/index.d.ts +1 -1
  103. package/dist/esm/receipt/index.js +1 -1
  104. package/dist/esm/receipt/layout.js +175 -8
  105. package/dist/esm/register/pairing.d.ts +21 -1
  106. package/dist/esm/register/pairing.js +16 -0
  107. package/dist/esm/stored/bon-layout.d.ts +8 -0
  108. package/dist/esm/stored/bon-layout.js +20 -0
  109. package/dist/esm/stored/draht.d.ts +3 -1
  110. package/dist/esm/stored/draht.js +52 -6
  111. package/dist/esm/stored/einstellungen.js +3 -2
  112. package/dist/esm/stored/gutscheine.d.ts +3 -0
  113. package/dist/esm/stored/gutscheine.js +56 -0
  114. package/dist/esm/stored/index.js +2 -17
  115. package/dist/esm/stored/vokabular.d.ts +6 -4
  116. package/dist/esm/stored/vokabular.js +48 -2
  117. package/dist/esm/version.d.ts +1 -1
  118. package/dist/esm/version.js +1 -1
  119. package/fixtures/expected/voucher-cancellation.grid32.txt +30 -0
  120. package/fixtures/expected/voucher-cancellation.grid48.txt +29 -0
  121. package/fixtures/expected/voucher-cancellation.lines.json +252 -0
  122. package/fixtures/expected/voucher-cancellation.sheet32.json +206 -0
  123. package/fixtures/expected/voucher-cancellation.sheet48.json +200 -0
  124. package/fixtures/expected/voucher-multi-purpose-redeem.grid32.txt +25 -0
  125. package/fixtures/expected/voucher-multi-purpose-redeem.grid48.txt +25 -0
  126. package/fixtures/expected/voucher-multi-purpose-redeem.lines.json +259 -0
  127. package/fixtures/expected/voucher-multi-purpose-redeem.sheet32.json +176 -0
  128. package/fixtures/expected/voucher-multi-purpose-redeem.sheet48.json +176 -0
  129. package/fixtures/expected/voucher-multi-purpose-sale.grid32.txt +24 -0
  130. package/fixtures/expected/voucher-multi-purpose-sale.grid48.txt +23 -0
  131. package/fixtures/expected/voucher-multi-purpose-sale.lines.json +216 -0
  132. package/fixtures/expected/voucher-multi-purpose-sale.sheet32.json +170 -0
  133. package/fixtures/expected/voucher-multi-purpose-sale.sheet48.json +164 -0
  134. package/fixtures/expected/voucher-payout.grid32.txt +23 -0
  135. package/fixtures/expected/voucher-payout.grid48.txt +22 -0
  136. package/fixtures/expected/voucher-payout.lines.json +216 -0
  137. package/fixtures/expected/voucher-payout.sheet32.json +164 -0
  138. package/fixtures/expected/voucher-payout.sheet48.json +158 -0
  139. package/fixtures/expected/voucher-service-redeem.grid32.txt +26 -0
  140. package/fixtures/expected/voucher-service-redeem.grid48.txt +25 -0
  141. package/fixtures/expected/voucher-service-redeem.lines.json +261 -0
  142. package/fixtures/expected/voucher-service-redeem.sheet32.json +182 -0
  143. package/fixtures/expected/voucher-service-redeem.sheet48.json +176 -0
  144. package/fixtures/expected/voucher-single-purpose-redeem-exempt.grid32.txt +30 -0
  145. package/fixtures/expected/voucher-single-purpose-redeem-exempt.grid48.txt +29 -0
  146. package/fixtures/expected/voucher-single-purpose-redeem-exempt.lines.json +331 -0
  147. package/fixtures/expected/voucher-single-purpose-redeem-exempt.sheet32.json +206 -0
  148. package/fixtures/expected/voucher-single-purpose-redeem-exempt.sheet48.json +200 -0
  149. package/fixtures/expected/voucher-single-purpose-redeem.grid32.txt +25 -0
  150. package/fixtures/expected/voucher-single-purpose-redeem.grid48.txt +24 -0
  151. package/fixtures/expected/voucher-single-purpose-redeem.lines.json +246 -0
  152. package/fixtures/expected/voucher-single-purpose-redeem.sheet32.json +176 -0
  153. package/fixtures/expected/voucher-single-purpose-redeem.sheet48.json +170 -0
  154. package/fixtures/expected/voucher-single-purpose-sale.grid32.txt +26 -0
  155. package/fixtures/expected/voucher-single-purpose-sale.grid48.txt +24 -0
  156. package/fixtures/expected/voucher-single-purpose-sale.lines.json +225 -0
  157. package/fixtures/expected/voucher-single-purpose-sale.sheet32.json +182 -0
  158. package/fixtures/expected/voucher-single-purpose-sale.sheet48.json +170 -0
  159. package/fixtures/hobex-hps-codes.json +1 -1
  160. package/fixtures/invoice-api.schema.json +1 -1
  161. package/fixtures/invoice-texts.json +1 -1
  162. package/fixtures/manifest.json +64 -0
  163. package/fixtures/pos-texts.json +170 -1
  164. package/fixtures/receipt-due-generated.json +1 -1
  165. package/fixtures/receipt-due-voucher-generated.json +494 -0
  166. package/fixtures/receipts/voucher-cancellation.json +76 -0
  167. package/fixtures/receipts/voucher-multi-purpose-redeem.json +62 -0
  168. package/fixtures/receipts/voucher-multi-purpose-sale.json +63 -0
  169. package/fixtures/receipts/voucher-payout.json +65 -0
  170. package/fixtures/receipts/voucher-service-redeem.json +82 -0
  171. package/fixtures/receipts/voucher-single-purpose-redeem-exempt.json +86 -0
  172. package/fixtures/receipts/voucher-single-purpose-redeem.json +71 -0
  173. package/fixtures/receipts/voucher-single-purpose-sale.json +88 -0
  174. package/fixtures/surface.json +35 -5
  175. package/fixtures/v3/antworten/belege.json +3405 -3
  176. package/fixtures/v3/antworten/belegmail.json +5 -2
  177. package/fixtures/v3/antworten/kasse-belege.json +3423 -3
  178. package/fixtures/v3/antworten/kasse.json +621 -14
  179. package/fixtures/v3/antworten/lager.json +1 -1
  180. package/fixtures/v3/antworten/rechnungen.json +5 -2
  181. package/fixtures/v3/antworten/storno.json +214 -3
  182. package/fixtures/v3/stored/belege.json +5 -2
  183. package/fixtures/v3/stored/kasse.json +225 -4
  184. package/fixtures/v3/stored/rechnungen.json +5 -2
  185. package/fixtures/v3/v3-vokabular.json +1375 -23
  186. package/fixtures/v3/zahlbetrag-faelle.json +293 -3
  187. package/package.json +4 -1
@@ -50,6 +50,7 @@ export const REGISTER_PERMS = [
50
50
  'sell', 'cancel', 'articles', 'layout', 'reports', 'takeover',
51
51
  'cancelScope', 'receiptsScope', 'drawer', 'discount', 'tipAssign',
52
52
  'stockView', 'stockCosts', 'stockMove', 'stockLoss', 'stocktakeCount', 'stocktakeClose', 'stockLocation',
53
+ 'voucherSell', 'voucherRedeem', 'voucherManage',
53
54
  ];
54
55
  /** Reichweite lesen, mit der Migration des Backends (register-auth.js). */
55
56
  export function cancelScopeOf(perms) {
@@ -76,6 +77,21 @@ export function stockViewOf(perms) {
76
77
  return false;
77
78
  return perms.stockView === undefined ? true : perms.stockView === true;
78
79
  }
80
+ /**
81
+ * Gutschein-Rechte lesen. Das Backend loest sie bei der Anmeldung auf; ein
82
+ * alter Server (und `/api`-Antworten) kennt sie nicht. Dann gilt je fehlendem
83
+ * Schluessel die Vorgabe des Kassiers: verkaufen und einloesen ja, verwalten
84
+ * nein. Ein ausdrueckliches `false` sperrt; was kein Wahrheitswert ist,
85
+ * erteilt nichts.
86
+ */
87
+ export function voucherPermsOf(perms) {
88
+ const lies = (wert, vorgabe) => (wert === undefined ? vorgabe : wert === true);
89
+ return {
90
+ voucherSell: lies(perms?.voucherSell, true),
91
+ voucherRedeem: lies(perms?.voucherRedeem, true),
92
+ voucherManage: lies(perms?.voucherManage, false),
93
+ };
94
+ }
79
95
  /**
80
96
  * Geraet koppeln: der Kopplungs-Code wird gegen den dauerhaften Ausweis dieses
81
97
  * Geraets getauscht. **Ohne Anmeldung** — der Code ist der Nachweis.
@@ -0,0 +1,8 @@
1
+ import type { ReceiptWithCompany } from '../client/receipts.js';
2
+ import { type ReceiptLayout } from '../receipt/layout.js';
3
+ /**
4
+ * Layout wie `beleg-layout.layoutFuerBeleg`: 80 mm, Regelwerk mindestens das
5
+ * aktuelle. Kann der Server es nicht bauen, liefert er `null` (dann baut
6
+ * [receiptLayoutFromResult] selbst); ebenso hier.
7
+ */
8
+ export declare function layoutWieServer(b: ReceiptWithCompany, regelwerk: unknown): ReceiptLayout | null;
@@ -0,0 +1,20 @@
1
+ import { buildReceiptLayout, CURRENT_LAYOUT_RULESET } from '../receipt/layout.js';
2
+ import { gutscheineFuerPaket, wieVendortesPaket } from './gutscheine.js';
3
+ /**
4
+ * Layout wie `beleg-layout.layoutFuerBeleg`: 80 mm, Regelwerk mindestens das
5
+ * aktuelle. Kann der Server es nicht bauen, liefert er `null` (dann baut
6
+ * [receiptLayoutFromResult] selbst); ebenso hier.
7
+ */
8
+ export function layoutWieServer(b, regelwerk) {
9
+ const ruleset = Math.max(Number(regelwerk) || 1, CURRENT_LAYOUT_RULESET);
10
+ try {
11
+ // Wie layoutFuerBeleg: Gutscheine mit Code stehen am Bon als Positionen,
12
+ // die uebrigen Eintraege so, wie das vendorte Paket des Servers sie liest.
13
+ return buildReceiptLayout(wieVendortesPaket(gutscheineFuerPaket(b.receipt)), b.company, {
14
+ paperSize: 'mm80', ruleset, testCashregister: b.testCashregister, testSignature: b.testSignature, registrationInfo: b.registrationInfo,
15
+ });
16
+ }
17
+ catch {
18
+ return null;
19
+ }
20
+ }
@@ -1,4 +1,4 @@
1
- import { type SchemaEintrag, type SchemaObjekt } from './vokabular.js';
1
+ import { type SchemaEintrag, type SchemaObjekt, type Wertverweis } from './vokabular.js';
2
2
  export type Objekt = Record<string, unknown>;
3
3
  export declare const hat: (o: object, k: string) => boolean;
4
4
  export declare const istObjekt: (w: unknown) => w is Objekt;
@@ -10,6 +10,8 @@ export declare function regel(eintrag: SchemaEintrag): {
10
10
  };
11
11
  /** Innere Namen einer Schema-Ebene (was der Server innen an dieser Stelle kennt). */
12
12
  export declare function innereNamen(schema: SchemaObjekt): Map<string, string>;
13
+ /** Zuordnung eines Wertverweises: benannter Katalog oder eingebettet. */
14
+ export declare function katalogVon(verweis: Wertverweis): Readonly<Record<string, string>> | undefined;
13
15
  /** Rueckgabe-Wahl innen -> aussen (Katalog RUECKGABE; stored.test.ts vergleicht mit dem Vertrag). */
14
16
  export declare const _RUECKGABE_NACH_AUSSEN: Readonly<Record<string, string>>;
15
17
  /**
@@ -67,16 +67,21 @@ function anPfad(obj, teile, fn) {
67
67
  */
68
68
  function werteNachAussen(obj, werte) {
69
69
  let raus = obj;
70
- for (const [pfad, { $catalog }] of Object.entries(werte)) {
71
- const katalog = KATALOGE[$catalog];
70
+ for (const [pfad, verweis] of Object.entries(werte)) {
71
+ const katalog = katalogVon(verweis);
72
72
  if (!katalog)
73
- throw new Error(`stored: Katalog ${$catalog} fehlt im Vokabular`);
73
+ throw new Error(`stored: Katalog ${verweis.$catalog} fehlt im Vokabular`);
74
74
  raus = anPfad(raus, pfad.split('.'), (v) => (typeof v === 'string' && hat(katalog, v) ? katalog[v] : v));
75
75
  }
76
76
  return raus;
77
77
  }
78
+ /** Zuordnung eines Wertverweises: benannter Katalog oder eingebettet. */
79
+ export function katalogVon(verweis) {
80
+ const name = verweis.$catalog;
81
+ return typeof name === 'string' ? KATALOGE[name] : verweis;
82
+ }
78
83
  /** Englische Werte eines Katalogs (was an dieser Stelle hinaus darf). */
79
- const aeussereWerte = (name) => new Set(Object.values(KATALOGE[name] ?? {}));
84
+ const aeussereWerte = (verweis) => new Set(Object.values(katalogVon(verweis) ?? {}));
80
85
  // ---- Belege -----------------------------------------------------------------
81
86
  /**
82
87
  * Interne Marke der Storno-Reservierung: `stornoMarke` am Storno-Beleg,
@@ -131,6 +136,47 @@ function belegRueckgabeNachAussen(beleg) {
131
136
  }
132
137
  return raus;
133
138
  }
139
+ // ---- Gutschein-Eintraege am Beleg ---------------------------------------------
140
+ /** Zwilling von `BELEG_GUTSCHEIN` (api-vokabular-v3): aussen -> innen. */
141
+ const BELEG_GUTSCHEIN = {
142
+ voucherType: 'art', voucherId: 'gutscheinId', codeSuffix: 'codeEnde', amountCents: 'betragCents', vatRateBp: 'satzBp',
143
+ article: 'artikel', articleId: 'artikelId', quantity: 'stueck', positions: 'positionen',
144
+ balanceAfterCents: 'saldoNachCents', remainingQuantityAfter: 'stueckOffenNach', taxedWith: 'versteuertMit', exempt: 'befreit',
145
+ reason: 'grund', zeroRateBucket: 'imNullTopf',
146
+ };
147
+ const BELEG_GUTSCHEIN_AUSSEN = Object.fromEntries(Object.entries(BELEG_GUTSCHEIN).map(([aussen, innen]) => [innen, aussen]));
148
+ const GUTSCHEIN_ARTIKEL_AUSSEN = { satzBp: 'vatRateBp', preisCents: 'priceCents' };
149
+ function umbenannt(o, namen) {
150
+ return Object.fromEntries(Object.entries(o).map(([k, w]) => [hat(namen, k) ? namen[k] : k, w]));
151
+ }
152
+ /** Zwilling von `gutscheinEintragNachAussen`: ohne deutschen Schluessel/Wert dasselbe Objekt (Altbeleg). */
153
+ function gutscheinEintragNachAussen(v) {
154
+ if (!istObjekt(v))
155
+ return v;
156
+ const art = KATALOGE.GUTSCHEIN_ART ?? {};
157
+ const typNeu = typeof v.type === 'string' && hat(art, v.type);
158
+ if (!typNeu && !Object.keys(v).some((k) => hat(BELEG_GUTSCHEIN_AUSSEN, k)))
159
+ return v;
160
+ const raus = {};
161
+ for (const [k, w] of Object.entries(v)) {
162
+ if (k === 'type')
163
+ raus.type = typNeu ? art[w] : w;
164
+ else if (k === 'art')
165
+ raus.voucherType = typeof w === 'string' && hat(art, w) ? art[w] : w;
166
+ else if (k === 'artikel')
167
+ raus.article = istObjekt(w) ? umbenannt(w, GUTSCHEIN_ARTIKEL_AUSSEN) : w;
168
+ else
169
+ raus[hat(BELEG_GUTSCHEIN_AUSSEN, k) ? BELEG_GUTSCHEIN_AUSSEN[k] : k] = w;
170
+ }
171
+ return raus;
172
+ }
173
+ /** Zwilling von `belegGutscheineNachAussen`: `vouchers[]` eines Belegs an Ort und Stelle nach aussen. */
174
+ function belegGutscheineNachAussen(beleg) {
175
+ if (!Array.isArray(beleg.vouchers))
176
+ return beleg;
177
+ const neu = beleg.vouchers.map(gutscheinEintragNachAussen);
178
+ return neu.every((v, i) => v === beleg.vouchers[i]) ? beleg : { ...beleg, vouchers: neu };
179
+ }
134
180
  /**
135
181
  * Zwilling von `beleg-pruefangaben.alsIso`: leer (auch 0, '') -> null;
136
182
  * Firestore-Zeitstempel, Zahl oder Zeichenkette -> ISO-Zeitpunkt in UTC;
@@ -182,7 +228,7 @@ export function pruefangabenDerHuelle(p) {
182
228
  export function _receiptEnvelopeToWire(huelle) {
183
229
  const schema = SCHEMAS.getReceipt;
184
230
  const draht = werteNachAussen(schluesselNachAussen(huelle, schema.data), schema.werte);
185
- return istObjekt(draht.receipt) ? { ...draht, receipt: belegRueckgabeNachAussen(draht.receipt) } : draht;
231
+ return istObjekt(draht.receipt) ? { ...draht, receipt: belegGutscheineNachAussen(belegRueckgabeNachAussen(draht.receipt)) } : draht;
186
232
  }
187
233
  /**
188
234
  * Ein Belegdokument (`users/{uid}/cashregisters/{id}/receipts/{id}`) in der
@@ -238,7 +284,7 @@ export function _storedArticleToWire(id, doc, functionName = 'fromStoredArticle'
238
284
  const schema = SCHEMAS.listMyArticles;
239
285
  const huelle = werteNachAussen(schluesselNachAussen({ articles: [handler] }, schema.data), schema.werte);
240
286
  const artikel = huelle.articles[0];
241
- const katalog = (pfad) => aeussereWerte(schema.werte[pfad].$catalog);
287
+ const katalog = (pfad) => aeussereWerte(schema.werte[pfad]);
242
288
  const raus = nurBekannte(artikel, new Set(ARTIKEL_FELDER), {
243
289
  quantityRule: katalog('articles[].quantityRule'), e1aGroup: katalog('articles[].e1aGroup'),
244
290
  });
@@ -1,6 +1,7 @@
1
1
  import { POS_BUSINESS_DEFAULTS, POS_BUSINESS_VALUES, POS_DEVICE_DEFAULTS, POS_DEVICE_OPTIONAL_FIELDS, POS_DEVICE_VALUES, POS_SHORTCUT_DEFAULTS, POS_SHORTCUT_SHARED_PAIRS, POS_BUSINESS_OPTIONAL_FIELDS, _optionalerWert, } from '../pos/settings.js';
2
2
  import { hat, innereNamen, istObjekt, regel } from './draht.js';
3
- import { KATALOGE, SCHEMAS } from './vokabular.js';
3
+ import { katalogVon } from './draht.js';
4
+ import { SCHEMAS } from './vokabular.js';
4
5
  const bool = (v) => typeof v === 'boolean';
5
6
  const hex = (v) => typeof v === 'string' && /^#[0-9a-fA-F]{6}$/.test(v);
6
7
  const kuerzel = (v) => typeof v === 'string' && /^[A-Za-z0-9]{1,3}$/.test(v);
@@ -71,7 +72,7 @@ function teilSchema(teil) {
71
72
  }
72
73
  function katalogFuer(teil, aussen) {
73
74
  const verweis = SCHEMAS.getKasseSettings.werte[`${teil}.${aussen}`];
74
- return verweis ? KATALOGE[verweis.$catalog] ?? null : null;
75
+ return verweis ? katalogVon(verweis) ?? null : null;
75
76
  }
76
77
  /** Tasten: aeussere und innere Aktionsnamen, Vorgabe und Paare in innerer Form. */
77
78
  const TASTEN_SCHEMA = regel(teilSchema('device').shortcuts).unter;
@@ -0,0 +1,3 @@
1
+ import type { Receipt } from '../models/receipt.js';
2
+ export declare function gutscheineFuerPaket(receipt: Receipt): Receipt;
3
+ export declare function wieVendortesPaket(receipt: Receipt): Receipt;
@@ -0,0 +1,56 @@
1
+ /*
2
+ * Zwilling von `gutscheineFuerPaket` (functions/beleg-layout.js): der Bon
3
+ * kennt Gutscheine mit Code nur als Positionen. Eintraege, deren Betrag schon
4
+ * als Position am Beleg steht (Einzweck, Leistung, Mehrzweck im 0-%-Topf),
5
+ * fallen fuer das Zeilenmodell weg, sonst zaehlte es sie ein zweites Mal; die
6
+ * maskierte Code-Endung wandert an die passende Position. Nur Darstellung,
7
+ * der Beleg selbst bleibt unberuehrt.
8
+ */
9
+ const POSITIONS_ARTEN = ['single_purpose', 'service'];
10
+ const centsDerPosition = (it) => Math.round(Math.abs(it.priceCents * it.quantity));
11
+ const centsDesEintrags = (v) => Number.isInteger(v.amountCents) ? Math.abs(v.amountCents) : Math.abs(v.valueCents ?? 0);
12
+ export function gutscheineFuerPaket(receipt) {
13
+ const positionen = receipt.items.map((it) => ({ ...it }));
14
+ const vergeben = new Set();
15
+ const codeAnPosition = (v, kinds) => {
16
+ const cents = centsDesEintrags(v);
17
+ const i = positionen.findIndex((it, k) => !vergeben.has(k) && it.kind != null && kinds.includes(it.kind)
18
+ && centsDerPosition(it) === cents && !String(it.name || '').includes('····'));
19
+ if (i < 0)
20
+ return;
21
+ vergeben.add(i);
22
+ if (typeof v.code === 'string' && v.code)
23
+ positionen[i].name = `${positionen[i].name} ${v.code}`;
24
+ };
25
+ const bleiben = [];
26
+ for (const v of receipt.vouchers) {
27
+ if (POSITIONS_ARTEN.includes(v.type)) {
28
+ if (v.action === 'sell')
29
+ codeAnPosition(v, ['voucher']);
30
+ continue;
31
+ }
32
+ if (v.zeroRateBucket === true) {
33
+ codeAnPosition(v, ['voucher_float']);
34
+ continue;
35
+ }
36
+ bleiben.push(v);
37
+ }
38
+ return { ...receipt, items: positionen, vouchers: bleiben };
39
+ }
40
+ /*
41
+ * Das Backend setzt den Bon noch mit dem vendorten Paket 0.33 (functions/
42
+ * package.json). Es liest an einem Gutschein nur name, code, action, type und
43
+ * den Wert; die Kennzeichen der neuen Form (`voucherType`, `codeSuffix`,
44
+ * `balanceAfterCents` ...) sieht es nicht. Was nach `gutscheineFuerPaket`
45
+ * bleibt (Mehrzweck, Aktion), zeigt es darum wie einen Altbeleg
46
+ * („Wertgutschein ····-····-5KNZ - Gutschein“). Dieses Paket zeigt solche
47
+ * Eintraege selbst in der Form der Spec („Gutschein ····-5KNZ“ + Rest); fuer
48
+ * das Layout wie der Server fallen die Kennzeichen hier weg. Entfaellt, sobald
49
+ * der Server ein Paket mit der neuen Form vendort und nicht mehr aufbereitet.
50
+ */
51
+ export function wieVendortesPaket(receipt) {
52
+ const vouchers = receipt.vouchers.map((v) => ({
53
+ name: v.name, code: v.code, action: v.action, type: v.type, valueCents: v.valueCents,
54
+ }));
55
+ return { ...receipt, vouchers };
56
+ }
@@ -15,11 +15,12 @@
15
15
  */
16
16
  import { belegAusHuelle, belegMitFirmaAusHuelle } from '../client/receipts.js';
17
17
  import { fromReceiptCompanyPayload } from '../models/receipt-company.js';
18
- import { buildReceiptLayout, CURRENT_LAYOUT_RULESET, receiptSignatureIsTest } from '../receipt/layout.js';
18
+ import { receiptSignatureIsTest } from '../receipt/layout.js';
19
19
  import { sanitizePosSettings } from '../pos/settings.js';
20
20
  import { fromPosArticlePayload } from '../pos/artikel.js';
21
21
  import { KasseneckValidationError } from '../client/errors.js';
22
22
  import { _receiptEnvelopeToWire, _storedArticleToWire, _storedReceiptInner, _storedReceiptToWire, pruefangabenDerHuelle, } from './draht.js';
23
+ import { layoutWieServer } from './bon-layout.js';
23
24
  import { _storedPosSettingsToWire } from './einstellungen.js';
24
25
  const istObjekt = (w) => w !== null && typeof w === 'object' && !Array.isArray(w);
25
26
  // ---- Belege -----------------------------------------------------------------
@@ -93,22 +94,6 @@ function pruefangabenFuer(beleg, angabe) {
93
94
  return pruefangabenDerHuelle(beleg.pruefangaben);
94
95
  return { karteRegistriertAm: angabe?.cardRegisteredAt ?? null, kasseRegistriertAm: angabe?.cashregisterRegisteredAt ?? null };
95
96
  }
96
- /**
97
- * Layout wie `beleg-layout.layoutFuerBeleg`: 80 mm, Regelwerk mindestens das
98
- * aktuelle. Kann der Server es nicht bauen, liefert er `null` (dann baut
99
- * [receiptLayoutFromResult] selbst); ebenso hier.
100
- */
101
- function layoutWieServer(b, regelwerk) {
102
- const ruleset = Math.max(Number(regelwerk) || 1, CURRENT_LAYOUT_RULESET);
103
- try {
104
- return buildReceiptLayout(b.receipt, b.company, {
105
- paperSize: 'mm80', ruleset, testCashregister: b.testCashregister, testSignature: b.testSignature, registrationInfo: b.registrationInfo,
106
- });
107
- }
108
- catch {
109
- return null;
110
- }
111
- }
112
97
  // ---- Firma ------------------------------------------------------------------
113
98
  const KOPF_FELDER = ['companyName', 'street', 'zip', 'city', 'phone', 'uid', 'taxnr', 'isSmallBusiness',
114
99
  'footer1', 'footer2', 'footer3', 'footer4', 'thanksMessage', 'showKreiseckLogo', 'logoUrl'];
@@ -1,16 +1,18 @@
1
1
  /** Fingerabdruck des Vertrags-Exports, aus dem diese Datei stammt (`_quelle.sha256`). */
2
- export declare const VOKABULAR_QUELLE = "e061453f62c8f4af21528eb51a677d6f947553f5b78621d64497f45833dd8b09";
2
+ export declare const VOKABULAR_QUELLE = "733e4385baf4b2aa50605e2ebe5903948ce77b3346aa3bb65c5fd0d450a6898f";
3
3
  /** Schema-Notation des Vertrags: Blatt 'aussen': 'innen', Objekt { __: 'innen', ... }, Liste [ { __: 'innen', ... } ]. */
4
4
  export type SchemaEintrag = string | SchemaObjekt | readonly [SchemaObjekt];
5
5
  export interface SchemaObjekt {
6
6
  readonly [aussen: string]: SchemaEintrag;
7
7
  }
8
+ /** Wertverweis: Name eines Katalogs oder eine eingebettete Zuordnung innen -> aussen. */
9
+ export type Wertverweis = {
10
+ readonly $catalog: string;
11
+ } | Readonly<Record<string, string>>;
8
12
  /** Schema je Endpunkt (aussen -> innen) und Wertverweise auf die Kataloge. */
9
13
  export declare const SCHEMAS: Readonly<Record<'getReceipt' | 'getKasseSettings' | 'listMyArticles', {
10
14
  readonly data: SchemaObjekt;
11
- readonly werte: Readonly<Record<string, {
12
- readonly $catalog: string;
13
- }>>;
15
+ readonly werte: Readonly<Record<string, Wertverweis>>;
14
16
  }>>;
15
17
  /** Die Felder eines Artikels in der Antwort (`listMyArticles`), aussen. */
16
18
  export declare const ARTIKEL_FELDER: readonly string[];
@@ -1,7 +1,7 @@
1
1
  // Erzeugt von scripts/stored-vokabular.mjs aus fixtures/v3/v3-vokabular.json.
2
2
  // Nicht von Hand aendern: test/stored.test.ts vergleicht mit dem Vertrag.
3
3
  /** Fingerabdruck des Vertrags-Exports, aus dem diese Datei stammt (`_quelle.sha256`). */
4
- export const VOKABULAR_QUELLE = "e061453f62c8f4af21528eb51a677d6f947553f5b78621d64497f45833dd8b09";
4
+ export const VOKABULAR_QUELLE = "733e4385baf4b2aa50605e2ebe5903948ce77b3346aa3bb65c5fd0d450a6898f";
5
5
  /** Schema je Endpunkt (aussen -> innen) und Wertverweise auf die Kataloge. */
6
6
  export const SCHEMAS = {
7
7
  "getReceipt": {
@@ -43,7 +43,22 @@ export const SCHEMAS = {
43
43
  "tone": "ton"
44
44
  }
45
45
  ]
46
- }
46
+ },
47
+ "issuedVouchers": [
48
+ {
49
+ "__": "gutscheine",
50
+ "codeSuffix": "codeEnde",
51
+ "faceValueCents": "nennwertCents",
52
+ "quantity": "stueck",
53
+ "validity": {
54
+ "__": "gueltigkeit",
55
+ "pattern": "muster",
56
+ "redeemableUntil": "einloesbarBis",
57
+ "refundableUntil": "rueckzahlbarBis",
58
+ "validUntil": "gueltigBis"
59
+ }
60
+ }
61
+ ]
47
62
  },
48
63
  "werte": {
49
64
  "receipt.cancellationReason": {
@@ -51,6 +66,12 @@ export const SCHEMAS = {
51
66
  },
52
67
  "layout.lines[].tone": {
53
68
  "$catalog": "LAYOUT_TON"
69
+ },
70
+ "issuedVouchers[].type": {
71
+ "$catalog": "GUTSCHEIN_ART"
72
+ },
73
+ "issuedVouchers[].validity.pattern": {
74
+ "$catalog": "GUTSCHEIN_MUSTER"
54
75
  }
55
76
  }
56
77
  },
@@ -157,6 +178,12 @@ export const SCHEMAS = {
157
178
  "terminalVia": "terminalVia",
158
179
  "terminalType": "terminalArt",
159
180
  "shortcutHints": "tastenMarken"
181
+ },
182
+ "vouchers": {
183
+ "__": "gutscheine",
184
+ "sale": "verkauf",
185
+ "valueVoucherType": "wertArt",
186
+ "vatRateBp": "satzBp"
160
187
  }
161
188
  },
162
189
  "werte": {
@@ -207,6 +234,10 @@ export const SCHEMAS = {
207
234
  },
208
235
  "device.terminalType": {
209
236
  "$catalog": "KASSE_GERAET_WERTE.terminalType"
237
+ },
238
+ "vouchers.valueVoucherType": {
239
+ "mehrzweck": "multi_purpose",
240
+ "einzweck": "single_purpose"
210
241
  }
211
242
  }
212
243
  },
@@ -301,6 +332,7 @@ export const KATALOGE = {
301
332
  "afa_gebaeude": "building_depreciation",
302
333
  "anlagenabgang": "asset_disposal_book_value",
303
334
  "instandhaltung_gebaeude": "building_maintenance",
335
+ "bauarbeiten": "construction_work",
304
336
  "reise": "travel_expenses",
305
337
  "fahrkarten": "travel_tickets",
306
338
  "naechtigung_beleg": "lodging_invoice",
@@ -335,6 +367,8 @@ export const KATALOGE = {
335
367
  "geschenke_geschaeftsfreunde": "business_gifts",
336
368
  "bewirtung": "hospitality",
337
369
  "bewirtung_werbung": "hospitality_advertising",
370
+ "kundschaft_aufmerksamkeit": "customer_courtesy",
371
+ "lebensmittel": "groceries",
338
372
  "bewirtung_voll": "hospitality_full",
339
373
  "bewirtung_repraesentation": "hospitality_representation",
340
374
  "sponsoring": "sponsorship",
@@ -365,6 +399,7 @@ export const KATALOGE = {
365
399
  "arbeitslosen_optin": "voluntary_unemployment_insurance",
366
400
  "uebrige_ausgaben": "other_expenses",
367
401
  "telefon_internet": "phone_internet",
402
+ "energie": "energy_utilities",
368
403
  "buero": "office_supplies",
369
404
  "software": "software_subscriptions",
370
405
  "fachliteratur": "professional_literature",
@@ -396,6 +431,17 @@ export const KATALOGE = {
396
431
  "ball_kultur": "non_deductible_events",
397
432
  "durchlaufend_ausgabe": "pass_through_payment"
398
433
  },
434
+ "GUTSCHEIN_ART": {
435
+ "mehrzweck": "multi_purpose",
436
+ "einzweck": "single_purpose",
437
+ "leistung": "service",
438
+ "aktion": "promotion"
439
+ },
440
+ "GUTSCHEIN_MUSTER": {
441
+ "unbefristet": "unlimited",
442
+ "1+3": "1+3",
443
+ "frei": "custom"
444
+ },
399
445
  "KASSE_BETRIEB_WERTE.cardProvider": {
400
446
  "keiner": "none",
401
447
  "extern": "external",
@@ -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.11.1";
7
+ export declare const PACKAGE_VERSION = "1.12.0";
@@ -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 const PACKAGE_VERSION = '1.11.1';
7
+ export const PACKAGE_VERSION = '1.12.0';
@@ -0,0 +1,30 @@
1
+ Melisse Nails e.U.
2
+ Melissenweg 7
3
+ 1070 Wien
4
+ ATU12345675
5
+ +43 1 2345678
6
+
7
+ ================================
8
+ STORNOBELEG
9
+ ================================
10
+ Stornobuchung zu Beleg
11
+ AT0-KASSE1-70
12
+ vom 19.10.2026, 10:15 Uhr
13
+ Grund: Kunde hat storniert
14
+
15
+ Datum: 19.10.2026 10:15:30
16
+ Kassen-ID: KASSE1
17
+ Beleg-ID: AT0-KASSE1-77
18
+
19
+ 1 x Maniküre -35,00 A
20
+ Gutschein ····-9DWT 30,50 A
21
+
22
+ MwSt% MwSt Netto Brutto
23
+ A 20% -0,75 -3,75 -4,50
24
+ --------------------------------
25
+ Gesamt: -4,50 €
26
+ Zahlungsart: Barzahlung
27
+
28
+ [QR-Code]
29
+
30
+ Danke für Ihren Besuch
@@ -0,0 +1,29 @@
1
+ Melisse Nails e.U.
2
+ Melissenweg 7
3
+ 1070 Wien
4
+ ATU12345675
5
+ +43 1 2345678
6
+
7
+ ================================================
8
+ STORNOBELEG
9
+ ================================================
10
+ Stornobuchung zu Beleg AT0-KASSE1-70
11
+ vom 19.10.2026, 10:15 Uhr
12
+ Grund: Kunde hat storniert
13
+
14
+ Datum: 19.10.2026 10:15:30
15
+ Kassen-ID: KASSE1
16
+ Beleg-ID: AT0-KASSE1-77
17
+
18
+ 1 x Maniküre -35,00 A
19
+ Gutschein ····-9DWT 30,50 A
20
+
21
+ MwSt% MwSt Netto Brutto
22
+ A 20% -0,75 -3,75 -4,50
23
+ ------------------------------------------------
24
+ Gesamt: -4,50 €
25
+ Zahlungsart: Barzahlung
26
+
27
+ [QR-Code]
28
+
29
+ Danke für Ihren Besuch