@kreiseck/kasseneck-api 1.16.0 → 1.17.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 (64) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/cjs/client/receipts.js +8 -0
  3. package/dist/cjs/index.d.ts +1 -1
  4. package/dist/cjs/models/voucher.d.ts +46 -4
  5. package/dist/cjs/models/voucher.js +58 -11
  6. package/dist/cjs/pos/artikel.d.ts +9 -0
  7. package/dist/cjs/pos/artikel.js +1 -0
  8. package/dist/cjs/pos/gutschein.d.ts +107 -2
  9. package/dist/cjs/pos/gutschein.js +124 -4
  10. package/dist/cjs/pos/index.d.ts +1 -1
  11. package/dist/cjs/pos/index.js +7 -3
  12. package/dist/cjs/pos/texte.d.ts +39 -0
  13. package/dist/cjs/pos/texte.js +22 -0
  14. package/dist/cjs/receipt/due.d.ts +20 -9
  15. package/dist/cjs/receipt/due.js +1 -0
  16. package/dist/cjs/receipt/gutschein-plan.d.ts +23 -7
  17. package/dist/cjs/receipt/gutschein-plan.js +67 -2
  18. package/dist/cjs/receipt/index.d.ts +1 -1
  19. package/dist/cjs/stored/draht.js +10 -2
  20. package/dist/cjs/stored/vokabular.d.ts +1 -1
  21. package/dist/cjs/stored/vokabular.js +84 -3
  22. package/dist/cjs/version.d.ts +1 -1
  23. package/dist/cjs/version.js +1 -1
  24. package/dist/esm/client/receipts.js +8 -0
  25. package/dist/esm/index.d.ts +1 -1
  26. package/dist/esm/models/voucher.d.ts +46 -4
  27. package/dist/esm/models/voucher.js +56 -11
  28. package/dist/esm/pos/artikel.d.ts +9 -0
  29. package/dist/esm/pos/artikel.js +1 -0
  30. package/dist/esm/pos/gutschein.d.ts +107 -2
  31. package/dist/esm/pos/gutschein.js +123 -3
  32. package/dist/esm/pos/index.d.ts +1 -1
  33. package/dist/esm/pos/index.js +2 -2
  34. package/dist/esm/pos/texte.d.ts +39 -0
  35. package/dist/esm/pos/texte.js +22 -0
  36. package/dist/esm/receipt/due.d.ts +20 -9
  37. package/dist/esm/receipt/due.js +1 -0
  38. package/dist/esm/receipt/gutschein-plan.d.ts +23 -7
  39. package/dist/esm/receipt/gutschein-plan.js +68 -3
  40. package/dist/esm/receipt/index.d.ts +1 -1
  41. package/dist/esm/stored/draht.js +10 -2
  42. package/dist/esm/stored/vokabular.d.ts +1 -1
  43. package/dist/esm/stored/vokabular.js +84 -3
  44. package/dist/esm/version.d.ts +1 -1
  45. package/dist/esm/version.js +1 -1
  46. package/fixtures/hobex-hps-codes.json +1 -1
  47. package/fixtures/invoice-api.schema.json +1 -1
  48. package/fixtures/invoice-texts.json +1 -1
  49. package/fixtures/pos-texts.json +43 -2
  50. package/fixtures/receipt-due-voucher-generated.json +485 -483
  51. package/fixtures/surface.json +17 -1
  52. package/fixtures/v3/antworten/belege.json +24 -12
  53. package/fixtures/v3/antworten/belegmail.json +1 -1
  54. package/fixtures/v3/antworten/kasse-belege.json +24 -12
  55. package/fixtures/v3/antworten/kasse.json +366 -9
  56. package/fixtures/v3/antworten/lager.json +1 -1
  57. package/fixtures/v3/antworten/rechnungen.json +1 -1
  58. package/fixtures/v3/antworten/storno.json +1 -1
  59. package/fixtures/v3/stored/belege.json +1 -1
  60. package/fixtures/v3/stored/kasse.json +2 -2
  61. package/fixtures/v3/stored/rechnungen.json +1 -1
  62. package/fixtures/v3/v3-vokabular.json +414 -10
  63. package/fixtures/v3/zahlbetrag-faelle.json +26 -10
  64. package/package.json +1 -1
@@ -4,7 +4,7 @@
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
5
  exports.KATALOGE = exports.ARTIKEL_FELDER = exports.SCHEMAS = exports.VOKABULAR_QUELLE = void 0;
6
6
  /** Fingerabdruck des Vertrags-Exports, aus dem diese Datei stammt (`_quelle.sha256`). */
7
- exports.VOKABULAR_QUELLE = "d11de425770bc431be9766199e4c9b3e712eb8e0eea84658f771a0f79d98e90e";
7
+ exports.VOKABULAR_QUELLE = "3088cb472f3fef3ed4ff969bf0d37d051363714096616a9263270c58266bde14";
8
8
  /** Schema je Endpunkt (aussen -> innen) und Wertverweise auf die Kataloge. */
9
9
  exports.SCHEMAS = {
10
10
  "getReceipt": {
@@ -54,9 +54,12 @@ exports.SCHEMAS = {
54
54
  "faceValueCents": "nennwertCents",
55
55
  "quantity": "stueck",
56
56
  "articleName": "artikelName",
57
+ "typeId": "sorteId",
58
+ "typeName": "sorteName",
57
59
  "validity": {
58
60
  "__": "gueltigkeit",
59
61
  "pattern": "muster",
62
+ "redeemableYears": "jahre",
60
63
  "redeemableUntil": "einloesbarBis",
61
64
  "refundableUntil": "rueckzahlbarBis",
62
65
  "validUntil": "gueltigBis"
@@ -204,20 +207,72 @@ exports.SCHEMAS = {
204
207
  "vouchers": {
205
208
  "__": "gutscheine",
206
209
  "sale": "verkauf",
210
+ "typeSale": "sortenVerkauf",
207
211
  "valueVoucherType": "wertArt",
208
212
  "vatRateBp": "satzBp",
209
213
  "label": {
210
214
  "__": "etikett",
211
215
  "size": "groesse",
216
+ "rotation": "drehung",
212
217
  "fields": {
213
218
  "__": "felder",
214
219
  "business": "betrieb",
215
220
  "value": "wert",
216
221
  "validity": "gueltigkeit",
217
- "checkPage": "pruefseite"
222
+ "checkPage": "pruefseite",
223
+ "designation": "bezeichnung"
218
224
  },
219
225
  "freeText": "freitext"
220
- }
226
+ },
227
+ "labelDefault": {
228
+ "__": "etikettStandard",
229
+ "size": "groesse",
230
+ "rotation": "drehung",
231
+ "fields": {
232
+ "__": "felder",
233
+ "business": "betrieb",
234
+ "value": "wert",
235
+ "validity": "gueltigkeit",
236
+ "checkPage": "pruefseite",
237
+ "designation": "bezeichnung"
238
+ },
239
+ "freeText": "freitext"
240
+ },
241
+ "types": [
242
+ {
243
+ "__": "sorten",
244
+ "kind": "art",
245
+ "vatRateBp": "satzBp",
246
+ "amount": {
247
+ "__": "betrag",
248
+ "mode": "modus"
249
+ },
250
+ "validity": {
251
+ "__": "gueltigkeit",
252
+ "pattern": "muster",
253
+ "years": "jahre"
254
+ },
255
+ "payout": "auszahlung",
256
+ "label": {
257
+ "__": "etikett",
258
+ "size": "groesse",
259
+ "rotation": "drehung",
260
+ "fields": {
261
+ "__": "felder",
262
+ "business": "betrieb",
263
+ "value": "wert",
264
+ "validity": "gueltigkeit",
265
+ "checkPage": "pruefseite",
266
+ "designation": "bezeichnung"
267
+ },
268
+ "freeText": "freitext"
269
+ },
270
+ "order": "reihenfolge",
271
+ "active": "aktiv",
272
+ "soldCount": "verkauft"
273
+ }
274
+ ],
275
+ "serviceArticles": "leistungsArtikel"
221
276
  }
222
277
  },
223
278
  "werte": {
@@ -272,6 +327,18 @@ exports.SCHEMAS = {
272
327
  "vouchers.valueVoucherType": {
273
328
  "mehrzweck": "multi_purpose",
274
329
  "einzweck": "single_purpose"
330
+ },
331
+ "vouchers.types[].kind": {
332
+ "$catalog": "GUTSCHEIN_ART"
333
+ },
334
+ "vouchers.types[].amount.mode": {
335
+ "$catalog": "GUTSCHEIN_BETRAG_MODUS"
336
+ },
337
+ "vouchers.types[].validity.pattern": {
338
+ "$catalog": "GUTSCHEIN_SORTE_MUSTER"
339
+ },
340
+ "vouchers.types[].payout": {
341
+ "$catalog": "GUTSCHEIN_AUSZAHLUNG"
275
342
  }
276
343
  }
277
344
  },
@@ -282,6 +349,7 @@ exports.SCHEMAS = {
282
349
  "__": "articles",
283
350
  "quantityRule": "mengenregel",
284
351
  "askQuantity": "mengeFragen",
352
+ "serviceVoucher": "gutscheinLeistung",
285
353
  "maxQuantity": "maxMenge",
286
354
  "tile": {
287
355
  "__": "kasse",
@@ -324,6 +392,7 @@ exports.ARTIKEL_FELDER = [
324
392
  "askQuantity",
325
393
  "maxQuantity",
326
394
  "revenueGroupId",
395
+ "serviceVoucher",
327
396
  "active",
328
397
  "groupId",
329
398
  "tile",
@@ -484,11 +553,23 @@ exports.KATALOGE = {
484
553
  "leistung": "service",
485
554
  "aktion": "promotion"
486
555
  },
556
+ "GUTSCHEIN_AUSZAHLUNG": {
557
+ "erlaubt": "allowed",
558
+ "nie": "never"
559
+ },
560
+ "GUTSCHEIN_BETRAG_MODUS": {
561
+ "frei": "free",
562
+ "fix": "fixed"
563
+ },
487
564
  "GUTSCHEIN_MUSTER": {
488
565
  "unbefristet": "unlimited",
489
566
  "1+3": "1+3",
490
567
  "frei": "custom"
491
568
  },
569
+ "GUTSCHEIN_SORTE_MUSTER": {
570
+ "unbefristet": "unlimited",
571
+ "jahre": "years"
572
+ },
492
573
  "KASSE_BETRIEB_WERTE.cardProvider": {
493
574
  "keiner": "none",
494
575
  "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.16.0";
7
+ export declare const PACKAGE_VERSION = "1.17.0";
@@ -7,4 +7,4 @@ exports.PACKAGE_VERSION = void 0;
7
7
  * Client-Version, Nachtrag §6). Ein Test haelt sie gleich mit `package.json`;
8
8
  * beim Anheben der Version beide Stellen aendern.
9
9
  */
10
- exports.PACKAGE_VERSION = '1.16.0';
10
+ exports.PACKAGE_VERSION = '1.17.0';
@@ -1,4 +1,5 @@
1
1
  import { ReceiptType, KeckPaymentMethod, CreditCardProvider, VoucherAction, VoucherType, } from '../enums/index.js';
2
+ import { istWertVerkauf } from '../models/voucher.js';
2
3
  import { fromReceiptCompanyPayload, fromReceiptPayload, toReceiptItemPayload, toVoucherPayload, receiptItemIsValid, voucherIsValid, isNewFormVoucher, voucherServerFields, fromReceiptSummaryPayload, isCancellationReason, isReturnDisposition, RETURN_DISPOSITIONS, isTipRefundSource, TIP_REFUND_SOURCES, RECEIPT_EMAIL_VIAS, readRegistrationInfo, } from '../models/index.js';
3
4
  import { RECEIPT_OUTPUT_ENTRIES_MAX, RECEIPT_OUTPUT_KINDS, RECEIPT_OUTPUT_RESULTS, receiptOutputsFromWire, } from '../models/receipt-output.js';
4
5
  import { VoucherCode } from '@kreiseck/validator';
@@ -122,6 +123,13 @@ export function createReceiptParams(options) {
122
123
  if (typ.value === ReceiptType.training.value && vouchers.some(isNewFormVoucher)) {
123
124
  throw eingabefehler('Gutscheine der neuen Form gibt es nicht auf Schulungsbelegen (receiptType training).');
124
125
  }
126
+ // Gutscheine Stufe 4b: ein Wertgutschein nur ueber eine Sorte, die Sorte nur an einem solchen Verkauf.
127
+ if (vouchers.some((voucher) => istWertVerkauf(voucher) && voucher.typeId == null)) {
128
+ throw eingabefehler('voucher_type_required: Ein Mehrzweck- oder Einzweckgutschein wird nur ueber eine Gutschein-Sorte verkauft (typeId aus getKasseSettings vouchers.types).');
129
+ }
130
+ if (vouchers.some((voucher) => voucher.typeId != null && !istWertVerkauf(voucher))) {
131
+ throw eingabefehler('voucher_invalid: typeId gehoert nur an den Verkauf eines Mehrzweck- oder Einzweckgutscheins.');
132
+ }
125
133
  if (vouchers.some((voucher) => !voucherIsValid(voucher))) {
126
134
  throw eingabefehler('Ungueltiger Gutschein uebergeben.');
127
135
  }
@@ -1,6 +1,6 @@
1
1
  export { ReceiptType, type ReceiptTypeKey, VatRate, type VatRateKey, KeckPaymentMethod, type KeckPaymentMethodKey, CreditCardProvider, type CreditCardProviderKey, VoucherType, type VoucherTypeKey, VoucherAction, type VoucherActionKey, StripeLinkMode, type StripeLinkModeKey, } from './enums/index.js';
2
2
  export { type ReceiptItem, type ReceiptItemPayload, type ReceiptItemPayloadRead, toReceiptItemPayload, fromReceiptItemPayload, receiptItemTotalCents, receiptItemIsValid, negateReceiptItem, type TipRecipient, isTipItem, isVoucherItem, isVoucherFloatItem, type Voucher, type VoucherArticle, type IssuedVoucher, type VoucherPayload, toVoucherPayload, fromVoucherPayload, voucherIsValid, type Receipt, type RegistrationInfo, type ReceiptPayload, type ReceiptPayloadRead, toReceiptPayload, fromReceiptPayload, receiptSubSumCents, receiptSumCents, receiptValueVoucherSign, type CancellationOf, type CancellationItem, type Cancellation, type CancellationReason, type CancellationErrorCode, type CancellationStatus, type ReturnDisposition, RETURN_DISPOSITIONS, CANCELLATION_REASONS, CANCELLATION_STATUSES, CANCELLATION_ERROR_CODES, CANCELLATION_RESERVATION_MS, isCancellationReason, isReturnDisposition, isCancellationErrorCode, cancellationErrorCode, isCancellationError, cancellationFieldErrors, type CancellationFieldError, type ReceiptPayment, type ReceiptPaymentPayload, type ReceiptPaymentInput, fromReceiptPaymentPayload, toReceiptPaymentPayload, type PaymentErrorCode, PAYMENT_ERROR_CODES, isPaymentErrorCode, paymentErrorCode, isPaymentError, paymentFieldErrors, type PaymentFieldError, type ReceiptEmailErrorCode, type ReceiptEmailVia, RECEIPT_EMAIL_ERROR_CODES, RECEIPT_EMAIL_SEND_ERROR_CODES, type ReceiptEmailSendErrorCode, RECEIPT_EMAIL_VIAS, isReceiptEmailErrorCode, receiptEmailErrorCode, isReceiptEmailError, receiptEmailFieldErrors, type ReceiptEmailFieldError, RECEIPT_OUTPUT_KINDS, type ReceiptOutputKind, RECEIPT_OUTPUT_LOG_KINDS, type ReceiptOutputLogKind, RECEIPT_OUTPUT_RESULTS, type ReceiptOutputResult, RECEIPT_OUTPUT_ENTRIES_MAX, type ReceiptOutput, type ReceiptOutputActor, RECEIPT_OUTPUT_ENTRY_ERROR_CODES, type ReceiptOutputEntryErrorCode, RECEIPT_OUTPUT_ERROR_CODES, type ReceiptOutputErrorCode, isReceiptOutputErrorCode, receiptOutputErrorCode, isReceiptOutputError, receiptOutputFieldErrors, type ReceiptOutputFieldError, type ReceiptErrorCode, type ReceiptFieldError, RECEIPT_ERROR_CODES, isReceiptErrorCode, receiptErrorCode, isReceiptError, receiptFieldErrors, remainingQuantities, TIP_REFUND_SOURCES, type TipRefundSource, isTipRefundSource, openStaffTipCents, cancellationItemsForTip, cancellationCashRefundCents, asksTipRefundSource, asksRefundTip, type ReceiptCompany, type ReceiptCompanyPayload, fromReceiptCompanyPayload, receiptCompanyTaxInfo, type Cashregister, type CashregisterOnboarding, type CashregisterPayload, type CashregisterOnboardingPayload, type CashregisterAutoLogout, type CashregisterAutoLogoutPayload, fromCashregisterPayload, type ReceiptSummary, type ReceiptSummaryPayload, fromReceiptSummaryPayload, type ZeroKind, ZERO_KINDS, isZeroKind, type ReportMonth, reportMonthFromDate, previousReportMonth, nextReportMonth, reportMonthKey, reportMonthReadable, type StripeUrlSession, type StripeUrlSessionPayload, toStripeUrlSessionPayload, fromStripeUrlSessionPayload, type HobexReceipt, type HobexReceiptPayload, toHobexReceiptPayload, fromHobexReceiptPayload, hobexReceiptToCardPaymentData, hobexReceiptNeedsSignature, } from './models/index.js';
3
3
  export { type AuthCredentials, type KasseneckAuth, type ApiKeyAuthOptions, type RegisterUserAuthOptions, apiKeyAuth, registerUserAuth, DEFAULT_BASE_URL, POS_BASE_URL, DEFAULT_TIMEOUT_MS, type HttpResponseLike, type HttpRequestInit, type FetchLike, type TransportOptions, type TransportBodyFields, type TransportCallOptions, type KasseneckTransport, type KasseneckBinaryTransport, type KasseneckPdfOrDataTransport, type PdfOrData, ALL_CALLS, type ApiCall, PUBLIC_CALLS, POS_CALLS, UNKNOWN_OUTCOME_CALLS, type PublicCall, type PosCall, createTransport, createBinaryTransport, createPdfOrDataTransport, KasseneckApiError, KasseneckAuthError, KasseneckHttpError, KasseneckNetworkError, KasseneckValidationError, type ValidationScope, type KasseneckError, type HttpFailureReason, type CauseDigest, type ErrorOutcome, CLIENT_ERROR_CODES, type ClientErrorCode, isKasseneckApiError, isKasseneckAuthError, isKasseneckHttpError, isKasseneckNetworkError, isKasseneckValidationError, isOutcomeUnknown, type ReceiptCommonOptions, type SellReceiptOptions, type TipOptions, type TipRecipientShare, type CancelReceiptOptions, type CancelReceiptResult, type ReceiptWithCompany, sellReceipt, sellReceiptWithCompany, paymentsExpectedCents, cardRefundReference, receiptLayoutFromResult, cancelReceipt, zeroReceipt, getReceipt, getReceiptWithCompany, generateFullReceiptId, getFirstReceiptDate, listMyReceipts, type ListMyReceiptsOptions, type ReceiptList, type ReceiptListStats, listMyCashregisters, checkVoucherCombinationError, type SendReceiptEmailOptions, type SendReceiptEmailResult, sendReceiptEmail, type ReceiptOutputEntry, type LogReceiptOutputsOptions, type LogReceiptOutputsResult, type RejectedReceiptOutput, logReceiptOutputs, downloadDailyReport, downloadMonthlyReport, getCashregisterReport, type CashregisterReportPeriod, getReportV2, type ReportV2, type ReportV2Metadata, type ReportV2Options, listReceipts, listAllReceipts, fromListReceiptsSummaryPayload, LIST_RECEIPTS_VIEWS, LIST_RECEIPTS_ORDERS, LIST_RECEIPTS_LIMIT_MAX, LIST_RECEIPTS_DAYS_MAX, LIST_ALL_RECEIPTS_MAX_PAGES, LIST_RECEIPTS_ERROR_CODES, type ListReceiptsErrorCode, type ListReceiptsView, type ListReceiptsOrder, type ListReceiptsOptions, type ListReceiptsWindow, type ListReceiptsGrossByRate, type ListReceiptsSummary, type ListReceiptsDaySummary, type ListReceiptsPage, type ListedReceipt, type CashboxStatus, type SignatureStatus, getCashboxStatus, getSignatureStatus, type KasseneckApi, createKasseneckApi, } from './client/index.js';
4
- export { type ReceiptDueTip, type ReceiptDueTipRecipient, type ReceiptDueOptions, type ReceiptDueBuckets, type ReceiptDueBreakdown, type ReceiptDueErrorReason, receiptDueCents, receiptDueBreakdown, ReceiptDueError, RECEIPT_DUE_ERROR_REASONS, isReceiptDueError, type VoucherDueInfo, type VoucherDueArticle, type VoucherRedeemContext, voucherRedeemMaxCents, VOUCHER_ZERO_RATE_BUCKET_FROM, } from './receipt/due.js';
4
+ export { type ReceiptDueTip, type ReceiptDueTipRecipient, type ReceiptDueOptions, type ReceiptDueBuckets, type ReceiptDueBreakdown, type ReceiptDueErrorReason, receiptDueCents, receiptDueBreakdown, ReceiptDueError, RECEIPT_DUE_ERROR_REASONS, isReceiptDueError, type VoucherDueInfo, type VoucherDueArticle, type VoucherDueType, type VoucherRedeemContext, voucherRedeemMaxCents, VOUCHER_ZERO_RATE_BUCKET_FROM, } from './receipt/due.js';
5
5
  export { parseServerTimeStamp, toViennaWallClock, type ViennaWallClock } from './vienna-time.js';
6
6
  export { type PosBusinessSettings, type PosDeviceSettings, type PosSettings, type ArticleGroup, type PosArticle, POS_BUSINESS_DEFAULTS, POS_DEVICE_DEFAULTS, mergePosSettings, fromArticleGroupPayload, fromPosArticlePayload, listMyArticleGroups, listMyArticles, getPosSettings, setMyPosSettings, setMyRegisterDeviceSettings, distributeDiscount, cancelScopeOf, receiptsScopeOf, stockViewOf, voucherPermsOf, } from './pos/index.js';
@@ -14,6 +14,12 @@ export interface Voucher {
14
14
  name?: string;
15
15
  code?: string;
16
16
  action: VoucherAction | string;
17
+ /**
18
+ * Art des Gutscheins. Beim Verkauf ueber eine Sorte die Art der Sorte
19
+ * (`PosVoucherType.kind`): der Server prueft sie gegen die Sorte und lehnt
20
+ * eine andere ab (`voucher_invalid`). Am Draht darf sie dort fehlen (die Art
21
+ * kommt dann aus der Sorte); ein so gelesener Wunsch geht ohne `type` hinaus.
22
+ */
17
23
  type: VoucherType | string;
18
24
  valueCents?: number;
19
25
  /** Neue Form: Betrag in Cent (Verkauf, Einloesung, Auszahlung). */
@@ -26,6 +32,18 @@ export interface Voucher {
26
32
  positions?: number[];
27
33
  /** Neue Form, Auszahlung: Grund, 1 bis 200 Zeichen. */
28
34
  reason?: string;
35
+ /**
36
+ * Neue Form, Wertgutschein verkaufen: die Gutschein-Sorte
37
+ * (`PosVoucherType.id` aus `getKasseSettings`). Pflicht fuer jeden Verkauf
38
+ * eines Mehrzweck- oder Einzweckgutscheins (Server: `voucher_type_required`),
39
+ * an jeder anderen Aktion oder Art verboten. Am gelesenen Eintrag die Sorte
40
+ * des Verkaufs (fehlt ohne Sorte).
41
+ */
42
+ typeId?: string;
43
+ /** Name der Sorte beim Verkauf (eingefroren). */
44
+ typeName?: string;
45
+ /** Auszahlung des verkauften Gutscheins (`allowed`/`never`, eingefroren). */
46
+ payout?: string;
29
47
  voucherType?: string;
30
48
  voucherId?: string;
31
49
  /** Letzte vier Zeichen des Codes; der Klartext steht nie am Beleg. */
@@ -53,8 +71,13 @@ export interface IssuedVoucher {
53
71
  quantity?: number;
54
72
  /** Nur bei Leistungsgutscheinen: Name des verkauften Artikels; sonst fehlt das Feld. */
55
73
  articleName?: string;
74
+ /** Verkauf ueber eine Sorte: ihre Kennung und ihr Name; sonst fehlen die Felder. */
75
+ typeId?: string;
76
+ typeName?: string;
56
77
  validity?: {
57
78
  pattern: string;
79
+ /** Sorte „N Jahre“: die Zahl N (Muster `1+3` fuer jedes N, die Daten gelten); fehlt sonst. */
80
+ redeemableYears?: number;
58
81
  redeemableUntil: string | null;
59
82
  refundableUntil: string | null;
60
83
  validUntil: string | null;
@@ -87,6 +110,9 @@ export interface VoucherPayload {
87
110
  quantity?: number;
88
111
  positions?: number[];
89
112
  reason?: string;
113
+ typeId?: string;
114
+ typeName?: string;
115
+ payout?: string;
90
116
  voucherType?: string;
91
117
  voucherId?: string;
92
118
  codeSuffix?: string;
@@ -100,10 +126,19 @@ export interface VoucherPayload {
100
126
  }
101
127
  /** Arten der neuen Gutscheinform (die Altwerte sind `value` und `promo`). */
102
128
  export declare const NEUE_GUTSCHEINARTEN: readonly string[];
103
- /** Gehoert der Gutschein zur neuen Form (Art oder Aktion `payout`)? */
104
- export declare function isNewFormVoucher(voucher: Pick<Voucher, 'type' | 'action'>): boolean;
105
- /** Felder, die nur der Server setzt; an einem Wunsch sind sie `voucher_invalid`. */
106
- export declare const GUTSCHEIN_SERVERFELDER: readonly ["voucherId", "codeSuffix", "zeroRateBucket", "voucherType"];
129
+ /** Gehoert der Gutschein zur neuen Form (Art, Aktion `payout` oder Verkauf ueber eine Sorte ohne Art)? */
130
+ export declare function isNewFormVoucher(voucher: Pick<Voucher, 'type' | 'action' | 'typeId'>): boolean;
131
+ /**
132
+ * Wert-Verkauf: ein Mehrzweck- oder Einzweckgutschein wird verkauft (mit Art
133
+ * oder nur ueber die Sorte). Paketintern: Zahlbetrag und Pruefung vor dem
134
+ * Senden entscheiden daran, ob eine Sorte noetig ist.
135
+ */
136
+ export declare function istWertVerkauf(voucher: Pick<Voucher, 'type' | 'action' | 'typeId'>): boolean;
137
+ /**
138
+ * Felder, die nur der Server setzt; an einem Wunsch sind sie `voucher_invalid`.
139
+ * Sortenname und Auszahlung friert der Server beim Verkauf ein (Stufe 4b).
140
+ */
141
+ export declare const GUTSCHEIN_SERVERFELDER: readonly ["voucherId", "codeSuffix", "zeroRateBucket", "voucherType", "typeName", "payout"];
107
142
  /** Welche Serverfelder tragt der Gutschein (leere Liste = keines)? */
108
143
  export declare function voucherServerFields(voucher: Voucher): string[];
109
144
  export declare function toVoucherPayload(voucher: Voucher): VoucherPayload;
@@ -115,3 +150,10 @@ export declare function toVoucherPayload(voucher: Voucher): VoucherPayload;
115
150
  */
116
151
  export declare function voucherIsValid(voucher: Voucher): boolean;
117
152
  export declare function fromVoucherPayload(payload: VoucherPayload): Voucher;
153
+ /**
154
+ * Die Form eines Wunschs der neuen Form. `sorteNoetig: false` laesst einen
155
+ * Wert-Verkauf ohne `typeId` zu: so rechnet der Zahlbetrag den Verkauf eines
156
+ * Aufrufers bis 1.16 mit der alten Gutschein-Einstellung weiter (der Server ab
157
+ * Stufe 4b antwortet darauf `voucher_type_required`). Paketintern.
158
+ */
159
+ export declare function wunschFormGueltig(v: Voucher, sorteNoetig: boolean): boolean;
@@ -8,23 +8,44 @@ export const NEUE_GUTSCHEINARTEN = [
8
8
  VoucherType.service,
9
9
  VoucherType.promotion,
10
10
  ];
11
- /** Gehoert der Gutschein zur neuen Form (Art oder Aktion `payout`)? */
11
+ /**
12
+ * Verkauf ueber eine Sorte ohne Art (`typeId` ohne `type`): die Art kommt aus
13
+ * der Sorte. Zwilling von `gutschein-beleg.istSortenVerkauf`.
14
+ */
15
+ function istSortenVerkaufOhneArt(voucher) {
16
+ return voucher.action === VoucherAction.sell && voucher.type == null && voucher.typeId != null;
17
+ }
18
+ /** Gehoert der Gutschein zur neuen Form (Art, Aktion `payout` oder Verkauf ueber eine Sorte ohne Art)? */
12
19
  export function isNewFormVoucher(voucher) {
13
- return NEUE_GUTSCHEINARTEN.includes(voucher.type) || voucher.action === VoucherAction.payout;
20
+ return NEUE_GUTSCHEINARTEN.includes(voucher.type) || voucher.action === VoucherAction.payout || istSortenVerkaufOhneArt(voucher);
21
+ }
22
+ /**
23
+ * Wert-Verkauf: ein Mehrzweck- oder Einzweckgutschein wird verkauft (mit Art
24
+ * oder nur ueber die Sorte). Paketintern: Zahlbetrag und Pruefung vor dem
25
+ * Senden entscheiden daran, ob eine Sorte noetig ist.
26
+ */
27
+ export function istWertVerkauf(voucher) {
28
+ return voucher.action === VoucherAction.sell
29
+ && (voucher.type === VoucherType.multi_purpose || voucher.type === VoucherType.single_purpose || istSortenVerkaufOhneArt(voucher));
14
30
  }
15
- /** Felder, die nur der Server setzt; an einem Wunsch sind sie `voucher_invalid`. */
16
- export const GUTSCHEIN_SERVERFELDER = ['voucherId', 'codeSuffix', 'zeroRateBucket', 'voucherType'];
31
+ /**
32
+ * Felder, die nur der Server setzt; an einem Wunsch sind sie `voucher_invalid`.
33
+ * Sortenname und Auszahlung friert der Server beim Verkauf ein (Stufe 4b).
34
+ */
35
+ export const GUTSCHEIN_SERVERFELDER = ['voucherId', 'codeSuffix', 'zeroRateBucket', 'voucherType', 'typeName', 'payout'];
17
36
  /** Welche Serverfelder tragt der Gutschein (leere Liste = keines)? */
18
37
  export function voucherServerFields(voucher) {
19
38
  return GUTSCHEIN_SERVERFELDER.filter((feld) => voucher[feld] != null);
20
39
  }
21
40
  const NEUE_FELDER = [
22
- 'amountCents', 'articleId', 'quantity', 'positions', 'reason', 'voucherType', 'voucherId', 'codeSuffix',
41
+ 'amountCents', 'articleId', 'quantity', 'positions', 'reason', 'typeId', 'typeName', 'payout', 'voucherType', 'voucherId', 'codeSuffix',
23
42
  'vatRateBp', 'article', 'balanceAfterCents', 'remainingQuantityAfter', 'taxedWith', 'exempt', 'zeroRateBucket',
24
43
  ];
25
44
  export function toVoucherPayload(voucher) {
26
45
  const action = requireEnumKey(VoucherAction, voucher.action, 'Gutschein-Aktion');
27
- const type = requireEnumKey(VoucherType, voucher.type, 'Gutscheinart');
46
+ // Verkauf ueber eine Sorte ohne Art: `type` bleibt weg wie im Wunsch (die Art kommt aus der Sorte).
47
+ const ohneArt = istSortenVerkaufOhneArt(voucher);
48
+ const type = ohneArt ? undefined : requireEnumKey(VoucherType, voucher.type, 'Gutscheinart');
28
49
  // Felder der neuen Form gehen roh hinaus, soweit gesetzt (auch an einem
29
50
  // gelesenen Altbeleg: dort fehlen sie, die Nutzlast bleibt byte-gleich).
30
51
  const roh = voucher;
@@ -34,7 +55,7 @@ export function toVoucherPayload(voucher) {
34
55
  neu[feld] = roh[feld];
35
56
  return {
36
57
  action,
37
- type,
58
+ ...(ohneArt ? {} : { type }),
38
59
  // Die neue Form kennt kein `valueCents`; nur ein gelesener Eintrag (die
39
60
  // Auszahlung steht beim Server als Wert mit Cent) traegt es weiter.
40
61
  ...(isNewFormVoucher(voucher) && voucher.valueCents == null ? {} : { valueCents: voucher.valueCents ?? null }),
@@ -87,11 +108,13 @@ export function fromVoucherPayload(payload) {
87
108
  : payload.value != null
88
109
  ? euroToCents(payload.value)
89
110
  : undefined;
111
+ // Ein Verkaufswunsch ueber eine Sorte darf ohne Art sein: dann bleibt `type` weg (nicht `undefined`).
112
+ const ohneArt = payload.type == null && payload.action === VoucherAction.sell && payload.typeId != null;
90
113
  return {
91
114
  name: payload.name ?? undefined,
92
115
  code: payload.code ?? undefined,
93
116
  action: readEnumKey(VoucherAction, payload.action),
94
- type: readEnumKey(VoucherType, payload.type),
117
+ ...(ohneArt ? {} : { type: readEnumKey(VoucherType, payload.type) }),
95
118
  valueCents,
96
119
  ...lesenNeu(payload),
97
120
  };
@@ -112,15 +135,31 @@ const MAX_GRUND = 200;
112
135
  const istCent = (n) => typeof n === 'number' && Number.isInteger(n) && n >= 1 && n <= MAX_BETRAG_CENTS;
113
136
  const istMenge = (n) => typeof n === 'number' && Number.isInteger(n) && n >= 1 && n <= MAX_STUECK;
114
137
  const hatText = (t) => typeof t === 'string' && t.trim().length > 0;
138
+ /**
139
+ * Form einer Sorten-Kennung am Wunsch (gutschein-core.ID_MUSTER ohne den
140
+ * Ausschluss): eine reservierte `__…__` ist der Form nach gueltig, der Server
141
+ * kennt sie nur nicht (`voucher_type_not_found`), ebenso der Zahlbetrag.
142
+ */
143
+ const SORTE_ID_FORM = /^[A-Za-z0-9_-]{1,128}$/;
115
144
  /**
116
145
  * Wunsch der neuen Form sendbar? Bildet die Regeln des Servers ab: Serverfelder
117
146
  * nie, Betrag/Menge ganz und positiv, Code bei Einloesung und Auszahlung,
118
147
  * Auszahlung nur Mehrzweck/Einzweck mit Grund (1 bis 200 Zeichen), Aktion nur
119
148
  * einloesbar, Leistung braucht Artikel und Menge (Verkauf) bzw. Positionen
120
- * (Einloesung). Felder, die zu einer anderen Aktion/Art gehoeren, sind
149
+ * (Einloesung), ein Wertgutschein wird nur ueber eine Sorte verkauft
150
+ * (`typeId`). Felder, die zu einer anderen Aktion/Art gehoeren, sind
121
151
  * ebenfalls nicht erlaubt (der Server wiese sie ab).
122
152
  */
123
153
  function newFormVoucherIsValid(v) {
154
+ return wunschFormGueltig(v, true);
155
+ }
156
+ /**
157
+ * Die Form eines Wunschs der neuen Form. `sorteNoetig: false` laesst einen
158
+ * Wert-Verkauf ohne `typeId` zu: so rechnet der Zahlbetrag den Verkauf eines
159
+ * Aufrufers bis 1.16 mit der alten Gutschein-Einstellung weiter (der Server ab
160
+ * Stufe 4b antwortet darauf `voucher_type_required`). Paketintern.
161
+ */
162
+ export function wunschFormGueltig(v, sorteNoetig) {
124
163
  if (voucherServerFields(v).length > 0)
125
164
  return false;
126
165
  const r = v;
@@ -130,6 +169,14 @@ function newFormVoucherIsValid(v) {
130
169
  }
131
170
  if (v.valueCents != null)
132
171
  return false;
172
+ // Die Sorte gehoert nur an einen Wert-Verkauf (Server: sorteId sonst voucher_invalid).
173
+ if (istWertVerkauf(v)) {
174
+ if (v.typeId == null ? sorteNoetig : (typeof v.typeId !== 'string' || !SORTE_ID_FORM.test(v.typeId)))
175
+ return false;
176
+ return v.articleId == null && v.quantity == null && v.positions == null && v.reason == null && istCent(v.amountCents);
177
+ }
178
+ if (v.typeId != null)
179
+ return false;
133
180
  const allein = v.type === VoucherType.multi_purpose || v.type === VoucherType.single_purpose;
134
181
  if (v.action === VoucherAction.payout) {
135
182
  return allein && hatText(v.code) && istCent(v.amountCents) && hatText(v.reason) && v.reason.trim().length <= MAX_GRUND
@@ -140,8 +187,6 @@ function newFormVoucherIsValid(v) {
140
187
  if (allein) {
141
188
  if (v.articleId != null || v.quantity != null || v.positions != null)
142
189
  return false;
143
- if (v.action === VoucherAction.sell)
144
- return istCent(v.amountCents);
145
190
  if (v.action === VoucherAction.redeem)
146
191
  return hatText(v.code) && istCent(v.amountCents);
147
192
  return false;
@@ -88,6 +88,14 @@ export interface PosArticle {
88
88
  * traegt (aeltere Backends) -- dann nicht aus anderen Feldern ableiten.
89
89
  */
90
90
  stockTracked?: boolean | null;
91
+ /**
92
+ * Als Leistungsgutschein verkaufbar (Gutscheine Stufe 4b, Haken im
93
+ * Artikelstamm). Die Kachel „Leistungsgutschein“ bietet nur diese Artikel
94
+ * an; der Server verkauft einen anderen nicht (`voucher_service_not_allowed`).
95
+ * Optional wie `stockLocationIds`, der Leser setzt es immer: nur ein
96
+ * ausdrueckliches `true` gilt.
97
+ */
98
+ serviceVoucher?: boolean;
91
99
  }
92
100
  /** Deckelt eine gewuenschte Menge an der Hoechstmenge des Artikels (null = keine Grenze). */
93
101
  export declare function allowedQuantity(a: Pick<PosArticle, 'maxQuantity'>, wanted: number): number;
@@ -113,6 +121,7 @@ export interface PosArticlePayload {
113
121
  ean?: string | null;
114
122
  internalCode?: string | null;
115
123
  stockTracked?: boolean | null;
124
+ serviceVoucher?: boolean | null;
116
125
  }
117
126
  export declare function fromPosArticlePayload(p: PosArticlePayload): PosArticle;
118
127
  /**
@@ -71,6 +71,7 @@ export function fromPosArticlePayload(p) {
71
71
  ean: textOderNull(p.ean),
72
72
  internalCode: textOderNull(p.internalCode),
73
73
  stockTracked: typeof p.stockTracked === 'boolean' ? p.stockTracked : null,
74
+ serviceVoucher: p.serviceVoucher === true,
74
75
  };
75
76
  }
76
77
  /**
@@ -15,10 +15,27 @@ export declare const VOUCHER_STATUSES: readonly VoucherStatus[];
15
15
  /** Gueltigkeit eines Gutscheins; Daten als `YYYY-MM-DD` (Wiener Tag). Was der Server nicht nennt, fehlt. */
16
16
  export interface VoucherValidity {
17
17
  pattern: 'unlimited' | '1+3' | 'custom';
18
+ /**
19
+ * Gutschein einer Sorte „N Jahre einloesbar, danach 3 Jahre Rueckzahlung“:
20
+ * die Zahl N (1 bis 30); `pattern` ist dann `1+3` fuer jedes N. Fehlt bei
21
+ * Gutscheinen ohne Sorte und bei unbefristeten. Die Daten stehen trotzdem in
22
+ * `redeemableUntil` (+N) und `refundableUntil` (+N+3) und gelten.
23
+ */
24
+ redeemableYears?: number;
18
25
  redeemableUntil?: string;
19
26
  refundableUntil?: string;
20
27
  validUntil?: string;
21
28
  }
29
+ /** Auszahlung eines Gutscheins bzw. einer Sorte: erlaubt oder nie (nur bei unbefristeter Gueltigkeit). */
30
+ export type VoucherPayout = 'allowed' | 'never';
31
+ /** Art einer Gutschein-Sorte: nur Wertgutscheine (Leistung und Aktion sind keine Sorte). */
32
+ export type VoucherTypeKind = 'multi_purpose' | 'single_purpose';
33
+ export declare const VOUCHER_PAYOUTS: readonly VoucherPayout[];
34
+ export declare const VOUCHER_TYPE_KINDS: readonly VoucherTypeKind[];
35
+ /** Betrag einer Sorte: frei an der Kasse eingegeben oder fest. */
36
+ export declare const VOUCHER_AMOUNT_MODES: readonly ('free' | 'fixed')[];
37
+ /** Gueltigkeit einer Sorte: unbefristet oder N Jahre (das Muster der Sorte, nicht das eines Gutscheins). */
38
+ export declare const VOUCHER_TYPE_VALIDITY_PATTERNS: readonly ('unlimited' | 'years')[];
22
39
  /** Der Artikel eines Dienstleistungsgutscheins. */
23
40
  export interface VoucherArticle {
24
41
  id: string;
@@ -50,12 +67,92 @@ export interface VoucherCheck {
50
67
  * inzwischen steuerpflichtigen Konto. Fehlt bei aelteren Servern.
51
68
  */
52
69
  exempt?: boolean;
70
+ /** Name der Sorte, mit der der Gutschein verkauft wurde (beim Verkauf eingefroren); fehlt ohne Sorte. */
71
+ typeName?: string;
72
+ /**
73
+ * Auszahlung dieses Gutscheins (beim Verkauf eingefroren); fehlt ohne
74
+ * Sorte. Bei `never` lehnt der Server jede Auszahlung ab
75
+ * (`voucher_payout_not_allowed`); Einloesen geht unveraendert.
76
+ */
77
+ payout?: VoucherPayout;
78
+ }
79
+ /**
80
+ * Etikett-Vorlage in der `/v3`-Form (Groesse, Layout, Drehung, Felder,
81
+ * Freitext). Das Paket zeichnet keine Etiketten: es reicht die Vorlage als
82
+ * Kopie durch, gezeichnet wird in der Kasse (druck-kit, Kassen-App). Weitere
83
+ * Schluessel eines neueren Servers bleiben erhalten.
84
+ */
85
+ export interface VoucherLabelTemplate {
86
+ /** Etikettgroesse `BxH` in mm, z. B. `40x30`. */
87
+ size: string;
88
+ layout: string;
89
+ /** 0 oder 90 (Grad); fehlt = 0. */
90
+ rotation?: number;
91
+ fields?: Record<string, boolean>;
92
+ freeText?: string[];
93
+ [key: string]: unknown;
94
+ }
95
+ /**
96
+ * Gutschein-Sorte (Gutscheine Stufe 4b): eine Kachel im Reiter „Gutscheine“.
97
+ * Die Kasse bekommt nur verkaufbare Sorten (aktiv und gueltig), in
98
+ * Kachelfolge. Verkauft wird ein Wertgutschein nur ueber eine Sorte:
99
+ * `{ action: 'sell', type: kind, typeId: id, amountCents }` (bei `fixed` genau
100
+ * `amount.cents`); Art, Satz, Gueltigkeit und Auszahlung nimmt der Server aus
101
+ * der Sorte und friert sie am Gutschein ein.
102
+ */
103
+ export interface PosVoucherType {
104
+ id: string;
105
+ name: string;
106
+ kind: VoucherTypeKind;
107
+ /** Satz in Basispunkten, nur bei Einzweck (> 0); Mehrzweck `null`. */
108
+ vatRateBp: number | null;
109
+ /** Betrag: frei (Zahlenfeld) oder fest (sofort in den Korb), Cent ganzzahlig. */
110
+ amount: {
111
+ mode: 'free';
112
+ } | {
113
+ mode: 'fixed';
114
+ cents: number;
115
+ };
116
+ /** Unbefristet oder N Jahre einloesbar, danach 3 Jahre Rueckzahlung (N 1 bis 30). */
117
+ validity: {
118
+ pattern: 'unlimited';
119
+ } | {
120
+ pattern: 'years';
121
+ years: number;
122
+ };
123
+ payout: VoucherPayout;
124
+ /** Wirksame Vorlage der Sorte (eigene, sonst die Standardvorlage); `null`, wenn die Antwort keine brauchbare traegt. */
125
+ label: VoucherLabelTemplate | null;
126
+ order: number;
127
+ active: boolean;
128
+ /** Verkaufte Gutscheine dieser Sorte (nur zur Anzeige). */
129
+ soldCount: number;
53
130
  }
54
- /** Gutschein-Einstellungen der Kasse (Block `vouchers` aus `getKasseSettings`). */
131
+ /**
132
+ * Gutschein-Einstellungen der Kasse (Block `vouchers` aus `getKasseSettings`).
133
+ *
134
+ * Seit Gutscheine Stufe 4b verkauft eine Kasse ueber Sorten: Reiter und
135
+ * Kacheln zeigt sie mit `typeSale` (und dem Recht `voucherSell`), je Sorte aus
136
+ * `types` und die feste Kachel „Leistungsgutschein“ mit `serviceArticles`.
137
+ * `sale`, `valueVoucherType` und `vatRateBp` gelten nur Kassen vor 1.17; der
138
+ * Server schickt `sale` immer als `false`.
139
+ *
140
+ * Die vier neuen Felder sind optional, damit bestehende Literale von
141
+ * Verbrauchern weiter uebersetzen; der Leser ([posVoucherSettingsFromWire])
142
+ * setzt sie immer.
143
+ */
55
144
  export interface PosVoucherSettings {
56
145
  sale: boolean;
57
146
  valueVoucherType: 'multi_purpose' | 'single_purpose' | null;
58
147
  vatRateBp: number | null;
148
+ /** Gutscheine verkaufen (Reiter „Gutscheine“): Schalter des Servers und mindestens eine Sorte oder ein Leistungsartikel. */
149
+ typeSale?: boolean;
150
+ /** Verkaufbare Sorten in Kachelfolge. */
151
+ types?: PosVoucherType[];
152
+ /** Es gibt aktive Artikel, die als Leistungsgutschein freigegeben sind (`PosArticle.serviceVoucher`). */
153
+ serviceArticles?: boolean;
154
+ /** Standardvorlage des Etiketts (Leistungsgutschein, Notausgang); `null` ohne brauchbare Vorlage. */
155
+ labelDefault?: VoucherLabelTemplate | null;
59
156
  }
60
157
  /** Ohne Block `vouchers` (alter Server, Konto ohne Schalter): Verkaufen ist aus. */
61
158
  export declare const VOUCHER_SETTINGS_DEFAULTS: Readonly<PosVoucherSettings>;
@@ -67,7 +164,15 @@ export declare const VOUCHER_SETTINGS_DEFAULTS: Readonly<PosVoucherSettings>;
67
164
  * `multi_purpose`/`single_purpose`/`null`, der Satz eine ganze Zahl >= 0 oder
68
165
  * `null`; Mehrzweck traegt keinen Satz, Einzweck braucht einen, und ein
69
166
  * eingeschalteter Verkauf braucht eine Art. Ein ausgeschalteter Verkauf darf
70
- * eine Art behalten.
167
+ * eine Art behalten. `typeSale` und `serviceArticles` sind Wahrheitswerte,
168
+ * `types` eine Liste (fehlen sie, antwortet ein aelterer Server: aus, leer).
169
+ *
170
+ * Sorten (Gutscheine Stufe 4b) liest der Leser einzeln: eine unstimmige Sorte
171
+ * faellt weg ([sorteVomDraht]), die anderen bleiben; ist danach weder eine
172
+ * Sorte noch ein Leistungsartikel da, ist `typeSale` aus (kein leerer Reiter).
173
+ * Eine unbrauchbare Etikett-Vorlage schliesst nichts: sie wird `null`, die
174
+ * Kasse druckt dann ihren Standard (ein Verkauf scheitert nie an der Vorlage).
175
+ * Das alte Feld `label` (Kassen vor 1.17, Ruling R3) liest das Paket nicht.
71
176
  */
72
177
  export declare function posVoucherSettingsFromWire(block: unknown): PosVoucherSettings;
73
178
  /**