@kreiseck/kasseneck-api 1.15.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 (69) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +2 -0
  3. package/dist/cjs/client/list-receipts.d.ts +6 -0
  4. package/dist/cjs/client/list-receipts.js +8 -0
  5. package/dist/cjs/client/receipts.js +8 -0
  6. package/dist/cjs/index.d.ts +1 -1
  7. package/dist/cjs/models/voucher.d.ts +46 -4
  8. package/dist/cjs/models/voucher.js +58 -11
  9. package/dist/cjs/pos/artikel.d.ts +9 -0
  10. package/dist/cjs/pos/artikel.js +1 -0
  11. package/dist/cjs/pos/gutschein.d.ts +107 -2
  12. package/dist/cjs/pos/gutschein.js +124 -4
  13. package/dist/cjs/pos/index.d.ts +1 -1
  14. package/dist/cjs/pos/index.js +7 -3
  15. package/dist/cjs/pos/texte.d.ts +39 -0
  16. package/dist/cjs/pos/texte.js +22 -0
  17. package/dist/cjs/receipt/due.d.ts +20 -9
  18. package/dist/cjs/receipt/due.js +1 -0
  19. package/dist/cjs/receipt/gutschein-plan.d.ts +23 -7
  20. package/dist/cjs/receipt/gutschein-plan.js +67 -2
  21. package/dist/cjs/receipt/index.d.ts +1 -1
  22. package/dist/cjs/stored/draht.js +10 -2
  23. package/dist/cjs/stored/vokabular.d.ts +1 -1
  24. package/dist/cjs/stored/vokabular.js +84 -3
  25. package/dist/cjs/version.d.ts +1 -1
  26. package/dist/cjs/version.js +1 -1
  27. package/dist/esm/client/list-receipts.d.ts +6 -0
  28. package/dist/esm/client/list-receipts.js +9 -1
  29. package/dist/esm/client/receipts.js +8 -0
  30. package/dist/esm/index.d.ts +1 -1
  31. package/dist/esm/models/voucher.d.ts +46 -4
  32. package/dist/esm/models/voucher.js +56 -11
  33. package/dist/esm/pos/artikel.d.ts +9 -0
  34. package/dist/esm/pos/artikel.js +1 -0
  35. package/dist/esm/pos/gutschein.d.ts +107 -2
  36. package/dist/esm/pos/gutschein.js +123 -3
  37. package/dist/esm/pos/index.d.ts +1 -1
  38. package/dist/esm/pos/index.js +2 -2
  39. package/dist/esm/pos/texte.d.ts +39 -0
  40. package/dist/esm/pos/texte.js +22 -0
  41. package/dist/esm/receipt/due.d.ts +20 -9
  42. package/dist/esm/receipt/due.js +1 -0
  43. package/dist/esm/receipt/gutschein-plan.d.ts +23 -7
  44. package/dist/esm/receipt/gutschein-plan.js +68 -3
  45. package/dist/esm/receipt/index.d.ts +1 -1
  46. package/dist/esm/stored/draht.js +10 -2
  47. package/dist/esm/stored/vokabular.d.ts +1 -1
  48. package/dist/esm/stored/vokabular.js +84 -3
  49. package/dist/esm/version.d.ts +1 -1
  50. package/dist/esm/version.js +1 -1
  51. package/fixtures/hobex-hps-codes.json +1 -1
  52. package/fixtures/invoice-api.schema.json +1 -1
  53. package/fixtures/invoice-texts.json +1 -1
  54. package/fixtures/pos-texts.json +43 -2
  55. package/fixtures/receipt-due-voucher-generated.json +485 -483
  56. package/fixtures/surface.json +17 -1
  57. package/fixtures/v3/antworten/belege.json +24 -12
  58. package/fixtures/v3/antworten/belegmail.json +1 -1
  59. package/fixtures/v3/antworten/kasse-belege.json +24 -12
  60. package/fixtures/v3/antworten/kasse.json +366 -9
  61. package/fixtures/v3/antworten/lager.json +1 -1
  62. package/fixtures/v3/antworten/rechnungen.json +1 -1
  63. package/fixtures/v3/antworten/storno.json +1 -1
  64. package/fixtures/v3/stored/belege.json +1 -1
  65. package/fixtures/v3/stored/kasse.json +2 -2
  66. package/fixtures/v3/stored/rechnungen.json +1 -1
  67. package/fixtures/v3/v3-vokabular.json +429 -11
  68. package/fixtures/v3/zahlbetrag-faelle.json +26 -10
  69. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,30 @@ Was vor 0.7.0 geschah, steht in der Commit-Historie (`git log`); ab hier wird
4
4
  es hier geführt. Ein Eintrag nennt die Änderung **und ihren Grund** —
5
5
  nur der Grund überlebt den nächsten Umbau.
6
6
 
7
+ ## 1.17.0 - 2026-10-29
8
+
9
+ Vouchers at the register, stage 4b: voucher types (Gutschein-Sorten). A business names any number of voucher types (multi- or single-purpose, free or fixed amount, validity, payout, label template); the register shows one tile per type in a tab "Gutscheine" and sells a value voucher only through a type. Additive for reading, with three behaviour changes for selling (see the items on `typeId` and the due amount).
10
+
11
+ - **Voucher types in the register settings: `PosVoucherSettings.typeSale`, `types`, `serviceArticles`, `labelDefault`.** `typeSale` shows the tab (server switch and at least one sellable type or service article), `types` are the sellable types in tile order (`PosVoucherType`: `id`, `name`, `kind`, `vatRateBp`, `amount` free or fixed, `validity` unlimited or years, `payout`, `label`, `order`, `active`, `soldCount`), `serviceArticles` adds the fixed tile "Leistungsgutschein", `labelDefault` is the business's standard label template. The four fields are optional in the type so existing literals still compile; `posVoucherSettingsFromWire` always sets them (an older server: off, empty, `null`). Fail-closed as before: a malformed `typeSale`, `serviceArticles` or `types` closes the whole block, a malformed type is dropped on its own (unknown kind, rate not matching the kind, fixed amount outside 1.00-10,000.00 €, years outside 1-30, payout `never` with a limited validity, inactive, reserved id), and `typeSale` is off when no type and no service article is left. An unusable label template becomes `null` and never closes anything (a sale never fails on the label). The old field `label` (registers before 1.17) is not read. Reason: from stage 4b the server sells value vouchers only through a voucher type and keeps `sale` always `false`, so registers before 1.17 never offer a sale it would reject.
12
+ - **New types and lists in `./pos`:** `PosVoucherType`, `VoucherLabelTemplate` (passed through as a copy; the package does not draw labels), `VoucherPayout`, `VoucherTypeKind`, `VOUCHER_TYPE_KINDS`, `VOUCHER_AMOUNT_MODES`, `VOUCHER_TYPE_VALIDITY_PATTERNS`, `VOUCHER_PAYOUTS` (equal to the `/v3` catalogues, also in `fixtures/surface.json`). Reason: web register and app read the same model.
13
+ - **A value voucher is sold only with `typeId`:** `Voucher.typeId` carries the voucher type, the wish is `{ action: 'sell', type: kind, typeId, amountCents }` (with a fixed amount exactly `amount.cents`). `type` may be left out on the wire (the kind then comes from the type); a wish read that way goes out without `type`. Behaviour change: `voucherIsValid` is `false` for a multi- or single-purpose sale without `typeId`, and `createReceipt` rejects it before sending (message starts with `voucher_type_required`); `typeId` on any other action or kind is rejected as `voucher_invalid`, and so are `typeName` and `payout` on a wish (server fields, `GUTSCHEIN_SERVERFELDER`). Reason: the server answers a value sale without type `voucher_type_required`; nothing is sent that it would refuse.
14
+ - **Due amount with voucher types: `ReceiptDueOptions.voucherTypes` (`VoucherDueType` = `id`, `kind`, `vatRateBp`, `amount` of a `PosVoucherType`).** A sale through a type takes kind and rate from the type (multi-purpose as value flow, from the zero-rate date in the 0 % bucket; single-purpose as an item at the rate of the type, 0 % for a small business). A type the register does not pass (or one the server would not sell) throws `voucher_type_not_found`, a different fixed amount `voucher_amount_mismatch`, and so does a `type` other than the kind of the type (the owner switched the kind after the register loaded its types; the register reloads them on this code), a reserved id `voucher_type_not_found`. Behaviour change: a value sale without `typeId` still computes with a `voucherSettings` that names a kind exactly as in 1.16 (for existing callers), but without `voucherSettings` or with one naming no kind (`valueVoucherType: null`, as every stage-4b server sends) it now throws `voucher_type_required` instead of `voucher_settings_missing`. Reason: the register computes the amount to charge before the card terminal, with the same rules and codes as the server's `gutschein-beleg.plane`.
15
+ - **Payout locked per voucher:** `VoucherCheck.payout` (`allowed`/`never`, frozen at sale) and `VoucherCheck.typeName` from `checkVoucher`; a payout of a `never` voucher throws `voucher_payout_not_allowed` in the due amount (also when expired). An unknown payout value reads as `never` without throwing, because redeeming must not fail on a new value. Reason: a business may exclude payouts for unlimited voucher types; the server refuses them.
16
+ - **Validity in years:** `VoucherValidity.redeemableYears` (1-30) from `checkVoucher`, also in `IssuedVoucher.validity`. The patterns stay `unlimited`, `1+3`, `custom`; the type's own pattern `years` never appears on a voucher. A voucher of a type with N years comes as `1+3` for every N, with the explicit dates (`redeemableUntil` = sale + N years, `refundableUntil` = sale + N + 3 years) and `redeemableYears` = N; read N from `redeemableYears`, never from the pattern. Reason: registers before 1.17 keep redeeming such vouchers and print "einlösbar bis <date>" from `redeemableUntil` on a reprint (with `custom` and no `validUntil` they printed no validity line at all).
17
+ - **Service vouchers only for approved articles: `PosArticle.serviceVoucher`** (only an explicit `true` counts; the reader always sets it, also `fromStoredArticle` from `gutscheinLeistung`), and `VoucherDueArticle.serviceVoucher`: `false` throws `voucher_service_not_allowed`, absent computes as before. Reason: the tile "Leistungsgutschein" offers only approved articles, the server sells no other.
18
+ - **Receipt entries of a sale through a type** read and write back `typeId`, `typeName` and `payout`; `IssuedVoucher` gains `typeId` and `typeName`; the `./stored` twin maps `sorteId`, `sorteName` and `auszahlung` (`erlaubt`/`nie`) like the server. Reason: the receipt and the label show the type name, and a cached receipt must write back unchanged.
19
+ - **5 labels and 5 sentences:** `voucher.tab` (Gutscheine), `voucher.enter_amount` (Betrag eingeben), `voucher.service_tile` (Leistungsgutschein), `voucher.kind_multi_short` (Mehrzweck), `voucher.kind_single_short` (Einzweck {rate} %); `VOUCHER_ERROR_MESSAGES` maps the new register codes to `voucher_error.type_required`, `type_not_found`, `amount_mismatch`, `service_not_allowed`, `payout_not_allowed` (also in `fixtures/pos-texts.json`). The codes of the customer area (`invalid_voucher_type`, `voucher_type_in_use`, `too_many_voucher_types`) are not register codes and get no sentence here. Reason: web register and app say the same words for the same tile and error.
20
+ - **One sentence of the register itself: `voucher.type_changed`** ("Diese Gutschein-Sorte hat sich geändert. Bitte die Zeile entfernen und die Kachel neu antippen.", also in `fixtures/pos-texts.json`). The register shows it on a voucher sale line in the cart that no longer matches the reloaded types (type changed, deactivated or deleted); the sentences after a server rejection stay `voucher_error.type_not_found` and `voucher_error.amount_mismatch`. Reason: those say "Die Kasse lädt die Sorten neu", which is over once the types are reloaded; the line blocks payment until it is removed and the tile tapped again, and the cashier must be told so.
21
+ - Contract re-fetched from the backend (stage 4b): the server names the position of a single-purpose sale with the type name, so the print digests of `sale_single_purpose_voucher` (both channels) are redrawn; the generated due cases (`fixtures/receipt-due-voucher-generated.json`, seed 20261027) now sell through types, with and without `type`, free and fixed, and cover the rejections above.
22
+
23
+ ## 1.16.0 - 2026-10-28
24
+
25
+ Header data for the full view of `listReceipts`, and the new upper limit of `getReportV2`. Additive.
26
+
27
+ - **`ListReceiptsPage.metadata`** (only with `view: 'full'`): the business data for the receipt header, the same `ReportV2Metadata` as `getReportV2().metadata` (`companyName`, address, `taxNumber`, `vatId`, footers, logo, `label` of the cash register); missing on the summary view. A `metadata` that is not an object is a response error. Reason: an app that prints a receipt again needs the header, and moving from `getReportV2` to `listReceipts` must not lose it.
28
+ - **`getReportV2`: the server now refuses more than 62 calendar days between `start` and `end`** with `range_too_long` (`KasseneckApiError`), without reading any receipt. The package does not check this before sending. Reason: one call could load an unbounded number of receipts; lists belong to `listReceipts`.
29
+ - Contract `v3` from the backend after keck#790.
30
+
7
31
  ## 1.15.0 - 2026-10-28
8
32
 
9
33
  Receipt lists page by page: `listReceipts` and `listAllReceipts` for the new public endpoint `listReceipts` (`/v3` only). Additive.
package/README.md CHANGED
@@ -792,6 +792,8 @@ receipt in 24 hours, 30 per register per hour) and `send_failed`.
792
792
 
793
793
  ## Receipt printing: QR code, logo, printers
794
794
 
795
+ `view: 'full'` returns the stored receipts plus `page.metadata` (business data for the receipt header, the same as `getReportV2().metadata`). `getReportV2` itself accepts at most 62 days between `start` and `end` (`range_too_long`).
796
+
795
797
  ### The QR code fits the paper
796
798
 
797
799
  The native QR command is given a module size in dots and does not check
@@ -1,5 +1,6 @@
1
1
  import { KeckPaymentMethod, ReceiptType } from '../enums/index.js';
2
2
  import { type CancellationReason, type CancellationStatus, type Receipt, type ReceiptPayment, type ZeroKind } from '../models/index.js';
3
+ import type { ReportV2Metadata } from './reports.js';
3
4
  import type { InternerTransport } from './aufrufe.js';
4
5
  /**
5
6
  * Belege einer Kasse auflisten (`listReceipts`, nur unter `/v3`): ein Tag oder
@@ -174,6 +175,11 @@ export interface ListReceiptsPage<V extends ListReceiptsView = 'summary'> {
174
175
  * den Tag keine Statistik gibt. Auf allen anderen Seiten fehlt das Feld.
175
176
  */
176
177
  daySummary?: ListReceiptsDaySummary | null;
178
+ /**
179
+ * Nur bei `view: 'full'`: Firmendaten fuer den Belegkopf (Nachdruck), dieselben Felder wie
180
+ * `getReportV2().metadata`. Bei der Kurzansicht fehlt das Feld.
181
+ */
182
+ metadata?: V extends 'full' ? ReportV2Metadata : never;
177
183
  }
178
184
  /** Zeile `view: summary` vom Draht. */
179
185
  export declare function fromListReceiptsSummaryPayload(payload: Record<string, unknown>): ListReceiptsSummary;
@@ -212,6 +212,13 @@ function tagessumme(roh) {
212
212
  revenueByRate: saetze.filter(istObjekt).map((z) => ({ rateBp: cent(z['rateBp']), grossCents: cent(z['grossCents']) })),
213
213
  };
214
214
  }
215
+ /** `metadata` der vollen Ansicht wie bei getReportV2: Firmendaten plus Bezeichnung der Kasse. */
216
+ function kopfdaten(roh) {
217
+ if (!istObjekt(roh))
218
+ throw antwortfehler('metadata ist kein Objekt');
219
+ const label = roh.label;
220
+ return { ...(0, index_js_2.fromReceiptCompanyPayload)(roh), label: typeof label === 'string' ? label : '' };
221
+ }
215
222
  /**
216
223
  * Eine Seite Belege der angemeldeten Kasse (`listReceipts`). Die Eingabe wird
217
224
  * vor dem Senden nach den Regeln des Servers geprueft (genau eine Form des
@@ -248,6 +255,7 @@ async function listReceipts(transport, options) {
248
255
  hasMore: daten['hasMore'],
249
256
  nextCursor: daten['hasMore'] ? weiter : null,
250
257
  ...('daySummary' in daten ? { daySummary: tagessumme(daten['daySummary']) } : {}),
258
+ ...(voll && 'metadata' in daten ? { metadata: kopfdaten(daten['metadata']) } : {}),
251
259
  };
252
260
  }
253
261
  /**
@@ -20,6 +20,7 @@ exports.belegAusHuelle = belegAusHuelle;
20
20
  exports.belegMitFirmaAusHuelle = belegMitFirmaAusHuelle;
21
21
  exports.paymentsExpectedCents = paymentsExpectedCents;
22
22
  const index_js_1 = require("../enums/index.js");
23
+ const voucher_js_1 = require("../models/voucher.js");
23
24
  const index_js_2 = require("../models/index.js");
24
25
  const receipt_output_js_1 = require("../models/receipt-output.js");
25
26
  const validator_1 = require("@kreiseck/validator");
@@ -143,6 +144,13 @@ function createReceiptParams(options) {
143
144
  if (typ.value === index_js_1.ReceiptType.training.value && vouchers.some(index_js_2.isNewFormVoucher)) {
144
145
  throw eingabefehler('Gutscheine der neuen Form gibt es nicht auf Schulungsbelegen (receiptType training).');
145
146
  }
147
+ // Gutscheine Stufe 4b: ein Wertgutschein nur ueber eine Sorte, die Sorte nur an einem solchen Verkauf.
148
+ if (vouchers.some((voucher) => (0, voucher_js_1.istWertVerkauf)(voucher) && voucher.typeId == null)) {
149
+ throw eingabefehler('voucher_type_required: Ein Mehrzweck- oder Einzweckgutschein wird nur ueber eine Gutschein-Sorte verkauft (typeId aus getKasseSettings vouchers.types).');
150
+ }
151
+ if (vouchers.some((voucher) => voucher.typeId != null && !(0, voucher_js_1.istWertVerkauf)(voucher))) {
152
+ throw eingabefehler('voucher_invalid: typeId gehoert nur an den Verkauf eines Mehrzweck- oder Einzweckgutscheins.');
153
+ }
146
154
  if (vouchers.some((voucher) => !(0, index_js_2.voucherIsValid)(voucher))) {
147
155
  throw eingabefehler('Ungueltiger Gutschein uebergeben.');
148
156
  }
@@ -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;
@@ -2,10 +2,12 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.GUTSCHEIN_SERVERFELDER = exports.NEUE_GUTSCHEINARTEN = void 0;
4
4
  exports.isNewFormVoucher = isNewFormVoucher;
5
+ exports.istWertVerkauf = istWertVerkauf;
5
6
  exports.voucherServerFields = voucherServerFields;
6
7
  exports.toVoucherPayload = toVoucherPayload;
7
8
  exports.voucherIsValid = voucherIsValid;
8
9
  exports.fromVoucherPayload = fromVoucherPayload;
10
+ exports.wunschFormGueltig = wunschFormGueltig;
9
11
  const index_js_1 = require("../enums/index.js");
10
12
  const money_js_1 = require("../money.js");
11
13
  const enum_payload_js_1 = require("./enum-payload.js");
@@ -16,23 +18,44 @@ exports.NEUE_GUTSCHEINARTEN = [
16
18
  index_js_1.VoucherType.service,
17
19
  index_js_1.VoucherType.promotion,
18
20
  ];
19
- /** Gehoert der Gutschein zur neuen Form (Art oder Aktion `payout`)? */
21
+ /**
22
+ * Verkauf ueber eine Sorte ohne Art (`typeId` ohne `type`): die Art kommt aus
23
+ * der Sorte. Zwilling von `gutschein-beleg.istSortenVerkauf`.
24
+ */
25
+ function istSortenVerkaufOhneArt(voucher) {
26
+ return voucher.action === index_js_1.VoucherAction.sell && voucher.type == null && voucher.typeId != null;
27
+ }
28
+ /** Gehoert der Gutschein zur neuen Form (Art, Aktion `payout` oder Verkauf ueber eine Sorte ohne Art)? */
20
29
  function isNewFormVoucher(voucher) {
21
- return exports.NEUE_GUTSCHEINARTEN.includes(voucher.type) || voucher.action === index_js_1.VoucherAction.payout;
30
+ return exports.NEUE_GUTSCHEINARTEN.includes(voucher.type) || voucher.action === index_js_1.VoucherAction.payout || istSortenVerkaufOhneArt(voucher);
31
+ }
32
+ /**
33
+ * Wert-Verkauf: ein Mehrzweck- oder Einzweckgutschein wird verkauft (mit Art
34
+ * oder nur ueber die Sorte). Paketintern: Zahlbetrag und Pruefung vor dem
35
+ * Senden entscheiden daran, ob eine Sorte noetig ist.
36
+ */
37
+ function istWertVerkauf(voucher) {
38
+ return voucher.action === index_js_1.VoucherAction.sell
39
+ && (voucher.type === index_js_1.VoucherType.multi_purpose || voucher.type === index_js_1.VoucherType.single_purpose || istSortenVerkaufOhneArt(voucher));
22
40
  }
23
- /** Felder, die nur der Server setzt; an einem Wunsch sind sie `voucher_invalid`. */
24
- exports.GUTSCHEIN_SERVERFELDER = ['voucherId', 'codeSuffix', 'zeroRateBucket', 'voucherType'];
41
+ /**
42
+ * Felder, die nur der Server setzt; an einem Wunsch sind sie `voucher_invalid`.
43
+ * Sortenname und Auszahlung friert der Server beim Verkauf ein (Stufe 4b).
44
+ */
45
+ exports.GUTSCHEIN_SERVERFELDER = ['voucherId', 'codeSuffix', 'zeroRateBucket', 'voucherType', 'typeName', 'payout'];
25
46
  /** Welche Serverfelder tragt der Gutschein (leere Liste = keines)? */
26
47
  function voucherServerFields(voucher) {
27
48
  return exports.GUTSCHEIN_SERVERFELDER.filter((feld) => voucher[feld] != null);
28
49
  }
29
50
  const NEUE_FELDER = [
30
- 'amountCents', 'articleId', 'quantity', 'positions', 'reason', 'voucherType', 'voucherId', 'codeSuffix',
51
+ 'amountCents', 'articleId', 'quantity', 'positions', 'reason', 'typeId', 'typeName', 'payout', 'voucherType', 'voucherId', 'codeSuffix',
31
52
  'vatRateBp', 'article', 'balanceAfterCents', 'remainingQuantityAfter', 'taxedWith', 'exempt', 'zeroRateBucket',
32
53
  ];
33
54
  function toVoucherPayload(voucher) {
34
55
  const action = (0, enum_payload_js_1.requireEnumKey)(index_js_1.VoucherAction, voucher.action, 'Gutschein-Aktion');
35
- const type = (0, enum_payload_js_1.requireEnumKey)(index_js_1.VoucherType, voucher.type, 'Gutscheinart');
56
+ // Verkauf ueber eine Sorte ohne Art: `type` bleibt weg wie im Wunsch (die Art kommt aus der Sorte).
57
+ const ohneArt = istSortenVerkaufOhneArt(voucher);
58
+ const type = ohneArt ? undefined : (0, enum_payload_js_1.requireEnumKey)(index_js_1.VoucherType, voucher.type, 'Gutscheinart');
36
59
  // Felder der neuen Form gehen roh hinaus, soweit gesetzt (auch an einem
37
60
  // gelesenen Altbeleg: dort fehlen sie, die Nutzlast bleibt byte-gleich).
38
61
  const roh = voucher;
@@ -42,7 +65,7 @@ function toVoucherPayload(voucher) {
42
65
  neu[feld] = roh[feld];
43
66
  return {
44
67
  action,
45
- type,
68
+ ...(ohneArt ? {} : { type }),
46
69
  // Die neue Form kennt kein `valueCents`; nur ein gelesener Eintrag (die
47
70
  // Auszahlung steht beim Server als Wert mit Cent) traegt es weiter.
48
71
  ...(isNewFormVoucher(voucher) && voucher.valueCents == null ? {} : { valueCents: voucher.valueCents ?? null }),
@@ -95,11 +118,13 @@ function fromVoucherPayload(payload) {
95
118
  : payload.value != null
96
119
  ? (0, money_js_1.euroToCents)(payload.value)
97
120
  : undefined;
121
+ // Ein Verkaufswunsch ueber eine Sorte darf ohne Art sein: dann bleibt `type` weg (nicht `undefined`).
122
+ const ohneArt = payload.type == null && payload.action === index_js_1.VoucherAction.sell && payload.typeId != null;
98
123
  return {
99
124
  name: payload.name ?? undefined,
100
125
  code: payload.code ?? undefined,
101
126
  action: (0, enum_payload_js_1.readEnumKey)(index_js_1.VoucherAction, payload.action),
102
- type: (0, enum_payload_js_1.readEnumKey)(index_js_1.VoucherType, payload.type),
127
+ ...(ohneArt ? {} : { type: (0, enum_payload_js_1.readEnumKey)(index_js_1.VoucherType, payload.type) }),
103
128
  valueCents,
104
129
  ...lesenNeu(payload),
105
130
  };
@@ -120,15 +145,31 @@ const MAX_GRUND = 200;
120
145
  const istCent = (n) => typeof n === 'number' && Number.isInteger(n) && n >= 1 && n <= MAX_BETRAG_CENTS;
121
146
  const istMenge = (n) => typeof n === 'number' && Number.isInteger(n) && n >= 1 && n <= MAX_STUECK;
122
147
  const hatText = (t) => typeof t === 'string' && t.trim().length > 0;
148
+ /**
149
+ * Form einer Sorten-Kennung am Wunsch (gutschein-core.ID_MUSTER ohne den
150
+ * Ausschluss): eine reservierte `__…__` ist der Form nach gueltig, der Server
151
+ * kennt sie nur nicht (`voucher_type_not_found`), ebenso der Zahlbetrag.
152
+ */
153
+ const SORTE_ID_FORM = /^[A-Za-z0-9_-]{1,128}$/;
123
154
  /**
124
155
  * Wunsch der neuen Form sendbar? Bildet die Regeln des Servers ab: Serverfelder
125
156
  * nie, Betrag/Menge ganz und positiv, Code bei Einloesung und Auszahlung,
126
157
  * Auszahlung nur Mehrzweck/Einzweck mit Grund (1 bis 200 Zeichen), Aktion nur
127
158
  * einloesbar, Leistung braucht Artikel und Menge (Verkauf) bzw. Positionen
128
- * (Einloesung). Felder, die zu einer anderen Aktion/Art gehoeren, sind
159
+ * (Einloesung), ein Wertgutschein wird nur ueber eine Sorte verkauft
160
+ * (`typeId`). Felder, die zu einer anderen Aktion/Art gehoeren, sind
129
161
  * ebenfalls nicht erlaubt (der Server wiese sie ab).
130
162
  */
131
163
  function newFormVoucherIsValid(v) {
164
+ return wunschFormGueltig(v, true);
165
+ }
166
+ /**
167
+ * Die Form eines Wunschs der neuen Form. `sorteNoetig: false` laesst einen
168
+ * Wert-Verkauf ohne `typeId` zu: so rechnet der Zahlbetrag den Verkauf eines
169
+ * Aufrufers bis 1.16 mit der alten Gutschein-Einstellung weiter (der Server ab
170
+ * Stufe 4b antwortet darauf `voucher_type_required`). Paketintern.
171
+ */
172
+ function wunschFormGueltig(v, sorteNoetig) {
132
173
  if (voucherServerFields(v).length > 0)
133
174
  return false;
134
175
  const r = v;
@@ -138,6 +179,14 @@ function newFormVoucherIsValid(v) {
138
179
  }
139
180
  if (v.valueCents != null)
140
181
  return false;
182
+ // Die Sorte gehoert nur an einen Wert-Verkauf (Server: sorteId sonst voucher_invalid).
183
+ if (istWertVerkauf(v)) {
184
+ if (v.typeId == null ? sorteNoetig : (typeof v.typeId !== 'string' || !SORTE_ID_FORM.test(v.typeId)))
185
+ return false;
186
+ return v.articleId == null && v.quantity == null && v.positions == null && v.reason == null && istCent(v.amountCents);
187
+ }
188
+ if (v.typeId != null)
189
+ return false;
141
190
  const allein = v.type === index_js_1.VoucherType.multi_purpose || v.type === index_js_1.VoucherType.single_purpose;
142
191
  if (v.action === index_js_1.VoucherAction.payout) {
143
192
  return allein && hatText(v.code) && istCent(v.amountCents) && hatText(v.reason) && v.reason.trim().length <= MAX_GRUND
@@ -148,8 +197,6 @@ function newFormVoucherIsValid(v) {
148
197
  if (allein) {
149
198
  if (v.articleId != null || v.quantity != null || v.positions != null)
150
199
  return false;
151
- if (v.action === index_js_1.VoucherAction.sell)
152
- return istCent(v.amountCents);
153
200
  if (v.action === index_js_1.VoucherAction.redeem)
154
201
  return hatText(v.code) && istCent(v.amountCents);
155
202
  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
  /**
@@ -82,6 +82,7 @@ function fromPosArticlePayload(p) {
82
82
  ean: textOderNull(p.ean),
83
83
  internalCode: textOderNull(p.internalCode),
84
84
  stockTracked: typeof p.stockTracked === 'boolean' ? p.stockTracked : null,
85
+ serviceVoucher: p.serviceVoucher === true,
85
86
  };
86
87
  }
87
88
  /**
@@ -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
  /**