@kreiseck/kasseneck-api 0.6.8 → 0.6.44

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 (70) hide show
  1. package/README.md +4 -0
  2. package/dist/cjs/client/index.d.ts +1 -1
  3. package/dist/cjs/client/receipts.d.ts +19 -0
  4. package/dist/cjs/client/receipts.js +51 -0
  5. package/dist/cjs/index.d.ts +2 -2
  6. package/dist/cjs/index.js +3 -2
  7. package/dist/cjs/kasse/settings.d.ts +39 -3
  8. package/dist/cjs/kasse/settings.js +28 -6
  9. package/dist/cjs/models/index.d.ts +1 -1
  10. package/dist/cjs/models/index.js +3 -1
  11. package/dist/cjs/models/receipt-item.d.ts +41 -0
  12. package/dist/cjs/models/receipt-item.js +41 -1
  13. package/dist/cjs/models/receipt.d.ts +7 -0
  14. package/dist/cjs/models/receipt.js +2 -0
  15. package/dist/cjs/printing/escpos.js +7 -0
  16. package/dist/cjs/react/index.js +11 -1
  17. package/dist/cjs/receipt/discount.js +1 -1
  18. package/dist/cjs/receipt/layout-escpos.js +9 -4
  19. package/dist/cjs/receipt/layout.d.ts +6 -3
  20. package/dist/cjs/receipt/layout.js +166 -14
  21. package/dist/cjs/register/pairing.d.ts +15 -0
  22. package/dist/cjs/register/pairing.js +22 -3
  23. package/dist/esm/client/index.d.ts +1 -1
  24. package/dist/esm/client/receipts.d.ts +19 -0
  25. package/dist/esm/client/receipts.js +51 -0
  26. package/dist/esm/index.d.ts +2 -2
  27. package/dist/esm/index.js +1 -1
  28. package/dist/esm/kasse/settings.d.ts +39 -3
  29. package/dist/esm/kasse/settings.js +28 -6
  30. package/dist/esm/models/index.d.ts +1 -1
  31. package/dist/esm/models/index.js +1 -1
  32. package/dist/esm/models/receipt-item.d.ts +41 -0
  33. package/dist/esm/models/receipt-item.js +39 -1
  34. package/dist/esm/models/receipt.d.ts +7 -0
  35. package/dist/esm/models/receipt.js +2 -0
  36. package/dist/esm/printing/escpos.js +7 -0
  37. package/dist/esm/react/index.js +11 -1
  38. package/dist/esm/receipt/discount.js +1 -1
  39. package/dist/esm/receipt/layout-escpos.js +9 -4
  40. package/dist/esm/receipt/layout.d.ts +6 -3
  41. package/dist/esm/receipt/layout.js +167 -15
  42. package/dist/esm/register/pairing.d.ts +15 -0
  43. package/dist/esm/register/pairing.js +22 -3
  44. package/fixtures/belege/rabatt-chef-trinkgeld.json +89 -0
  45. package/fixtures/belege/rabatt-einfach.json +50 -0
  46. package/fixtures/belege/rabatt-trinkgeld.json +75 -0
  47. package/fixtures/belege/rabatt-wertgutschein.json +70 -0
  48. package/fixtures/belege/rabattzeilen.json +5 -3
  49. package/fixtures/belege/storno-rabatt.json +68 -0
  50. package/fixtures/erwartet/rabatt-chef-trinkgeld.grid32.txt +31 -0
  51. package/fixtures/erwartet/rabatt-chef-trinkgeld.grid48.txt +30 -0
  52. package/fixtures/erwartet/rabatt-chef-trinkgeld.lines.json +317 -0
  53. package/fixtures/erwartet/rabatt-einfach.grid32.txt +26 -0
  54. package/fixtures/erwartet/rabatt-einfach.grid48.txt +25 -0
  55. package/fixtures/erwartet/rabatt-einfach.lines.json +250 -0
  56. package/fixtures/erwartet/rabatt-trinkgeld.grid32.txt +31 -0
  57. package/fixtures/erwartet/rabatt-trinkgeld.grid48.txt +30 -0
  58. package/fixtures/erwartet/rabatt-trinkgeld.lines.json +336 -0
  59. package/fixtures/erwartet/rabatt-wertgutschein.grid32.txt +32 -0
  60. package/fixtures/erwartet/rabatt-wertgutschein.grid48.txt +31 -0
  61. package/fixtures/erwartet/rabatt-wertgutschein.lines.json +330 -0
  62. package/fixtures/erwartet/rabattzeilen.grid32.txt +4 -2
  63. package/fixtures/erwartet/rabattzeilen.grid48.txt +4 -2
  64. package/fixtures/erwartet/rabattzeilen.lines.json +18 -8
  65. package/fixtures/erwartet/storno-rabatt.grid32.txt +34 -0
  66. package/fixtures/erwartet/storno-rabatt.grid48.txt +32 -0
  67. package/fixtures/erwartet/storno-rabatt.lines.json +317 -0
  68. package/fixtures/kasse-settings-standard.json +145 -0
  69. package/fixtures/manifest.json +34 -4
  70. package/package.json +3 -2
package/README.md CHANGED
@@ -118,6 +118,10 @@ const { receipt, company } = await api.sellReceiptWithCompany({
118
118
  { name: 'Café Latte', quantity: 2, vat: VatRate.vat20, priceCents: 390 },
119
119
  { name: 'Marmeladeweckerl', quantity: 1, vat: VatRate.vat10, priceCents: 250 },
120
120
  ],
121
+ // Trinkgeld in Cent (optional): das Backend bucht daraus eine signierte
122
+ // Position „Trinkgeld“ — Mitarbeiter 0 % als Durchläufer, Inhaber als Umsatz.
123
+ // Als Objekt mit eigener Zahlart/Empfängern: { cents, paymentMethod, recipients }.
124
+ tip: 100,
121
125
  });
122
126
 
123
127
  // Layout bauen (reines Datenmodell: Zeilen, Ausrichtung, Spalten, QR-Code) …
@@ -1,7 +1,7 @@
1
1
  export { type AuthCredentials, type KasseneckAuth, type ApiKeyAuthOptions, type RegisterUserAuthOptions, apiKeyAuth, registerUserAuth, } from './auth.js';
2
2
  export { DEFAULT_BASE_URL, DEFAULT_TIMEOUT_MS, type HttpResponseLike, type HttpRequestInit, type FetchLike, type TransportOptions, type TransportBodyFields, type KasseneckTransport, type KasseneckBinaryTransport, createTransport, createBinaryTransport, } from './transport.js';
3
3
  export { KasseneckApiError, KasseneckAuthError, KasseneckHttpError, KasseneckNetworkError, KasseneckValidationError, type ValidationScope, type KasseneckError, type HttpFailureReason, type CauseDigest, isKasseneckApiError, isKasseneckAuthError, isKasseneckHttpError, isKasseneckNetworkError, isKasseneckValidationError, } from './errors.js';
4
- export { type ReceiptCommonOptions, type SellReceiptOptions, type CancelReceiptOptions, type CreateCancelReceiptOptions, type ReceiptWithCompany, sellReceipt, sellReceiptWithCompany, cancelReceipt, type CancelReceiptResult, createCancelReceipt, zeroReceipt, getReceipt, getReceiptWithCompany, generateFullReceiptId, getFirstReceiptDate, listMyReceipts, type ListMyReceiptsOptions, type ReceiptList, type ReceiptListStats, checkVoucherCombinationError, } from './receipts.js';
4
+ export { type ReceiptCommonOptions, type SellReceiptOptions, type TipOptions, type TipRecipientShare, type CancelReceiptOptions, type CreateCancelReceiptOptions, type ReceiptWithCompany, sellReceipt, sellReceiptWithCompany, cancelReceipt, type CancelReceiptResult, createCancelReceipt, zeroReceipt, getReceipt, getReceiptWithCompany, generateFullReceiptId, getFirstReceiptDate, listMyReceipts, type ListMyReceiptsOptions, type ReceiptList, type ReceiptListStats, checkVoucherCombinationError, } from './receipts.js';
5
5
  export { listMyCashregisters } from './cashregisters.js';
6
6
  export { downloadDailyReport, downloadMonthlyReport } from './reports.js';
7
7
  export { type CashboxStatus, type SignatureStatus, getCashboxStatus, getSignatureStatus, } from './status.js';
@@ -39,6 +39,25 @@ export interface ReceiptCommonOptions {
39
39
  customProjectId?: string;
40
40
  /** Rohdaten der Kartenzahlung (Terminal-/Anbieterantwort). */
41
41
  cardPaymentData?: Record<string, unknown>;
42
+ /**
43
+ * Trinkgeld in Cent — als Zahl (an den angemeldeten Kassen-Benutzer, Zahlart
44
+ * des Belegs) oder als [TipOptions]. Das Backend bucht daraus signierte
45
+ * Positionen `kind:'tip'` (Mitarbeiter 0 % als Durchlaeufer, Inhaber
46
+ * anteilig je Steuersatz). Nur auf `standard` und `training`.
47
+ */
48
+ tip?: number | TipOptions;
49
+ }
50
+ /** Trinkgeld mit eigener Zahlart und/oder Empfaengern (Kassen-Benutzer-IDs, Summe = cents). */
51
+ export interface TipOptions {
52
+ cents: number;
53
+ /** Zahlart des Trinkgelds; ohne Angabe gilt die des Belegs. */
54
+ paymentMethod?: KeckPaymentMethod | KeckPaymentMethodKey;
55
+ /** Empfaenger; ohne Angabe der angemeldete Kassen-Benutzer. Summe der cents = cents. */
56
+ recipients?: TipRecipientShare[];
57
+ }
58
+ export interface TipRecipientShare {
59
+ registerUserId: string;
60
+ cents: number;
42
61
  }
43
62
  /** Belegtyp und Inhalt — die vollstaendige Eingabe von [createReceipt]. */
44
63
  export interface CreateReceiptOptions extends ReceiptCommonOptions {
@@ -87,6 +87,12 @@ function createReceiptParams(options) {
87
87
  }
88
88
  }
89
89
  }
90
+ if (options.tip != null) {
91
+ if (typ.value !== index_js_1.ReceiptType.standard.value && typ.value !== index_js_1.ReceiptType.training.value) {
92
+ throw eingabefehler(`Trinkgeld ist nur bei receiptType standard oder training moeglich, nicht bei "${typ.value}".`);
93
+ }
94
+ params['tip'] = gepruefterTip(options.tip);
95
+ }
90
96
  if (options.customProjectId != null) {
91
97
  params['customProjectId'] = options.customProjectId;
92
98
  }
@@ -372,6 +378,51 @@ function alsNutzlast(werte, wandeln) {
372
378
  throw eingabefehler(ursache instanceof Error ? ursache.message : 'Ungueltige Nutzlast uebergeben.');
373
379
  }
374
380
  }
381
+ /** Positive ganze Cent? */
382
+ function istCentBetrag(wert) {
383
+ return typeof wert === 'number' && Number.isInteger(wert) && wert > 0;
384
+ }
385
+ /**
386
+ * Trinkgeld pruefen und in die Nutzlast-Form bringen (Zahl bleibt Zahl; das
387
+ * Objekt geht mit geprueftem Zahlart-Wert und Empfaengern hinaus). Dieselben
388
+ * Regeln wie das Backend (tip-core.normalizeTip) — nur frueher.
389
+ */
390
+ function gepruefterTip(tip) {
391
+ if (typeof tip === 'number') {
392
+ if (!istCentBetrag(tip))
393
+ throw eingabefehler('Trinkgeld: Betrag muss eine ganze Zahl in Cent > 0 sein.');
394
+ return tip;
395
+ }
396
+ if (tip == null || typeof tip !== 'object' || !istCentBetrag(tip.cents)) {
397
+ throw eingabefehler('Trinkgeld: Betrag muss eine ganze Zahl in Cent > 0 sein.');
398
+ }
399
+ const nutzlast = { cents: tip.cents };
400
+ if (tip.paymentMethod != null)
401
+ nutzlast['paymentMethod'] = gepruefteZahlungsart(tip.paymentMethod);
402
+ if (tip.recipients != null) {
403
+ if (!Array.isArray(tip.recipients) || tip.recipients.length === 0) {
404
+ throw eingabefehler('Trinkgeld: recipients darf nicht leer sein.');
405
+ }
406
+ let summe = 0;
407
+ const gesehen = new Set();
408
+ for (const r of tip.recipients) {
409
+ if (r == null || typeof r.registerUserId !== 'string' || r.registerUserId === '') {
410
+ throw eingabefehler('Trinkgeld: recipients[].registerUserId fehlt.');
411
+ }
412
+ if (!istCentBetrag(r.cents))
413
+ throw eingabefehler('Trinkgeld: recipients[].cents muss eine ganze Zahl > 0 sein.');
414
+ if (gesehen.has(r.registerUserId))
415
+ throw eingabefehler(`Trinkgeld: Kassen-Benutzer ${r.registerUserId} doppelt.`);
416
+ gesehen.add(r.registerUserId);
417
+ summe += r.cents;
418
+ }
419
+ if (summe !== tip.cents) {
420
+ throw eingabefehler(`Trinkgeld: Summe der Empfaenger (${summe}) entspricht nicht dem Betrag (${tip.cents}).`);
421
+ }
422
+ nutzlast['recipients'] = tip.recipients.map((r) => ({ registerUserId: r.registerUserId, cents: r.cents }));
423
+ }
424
+ return nutzlast;
425
+ }
375
426
  /** Zahlungsart des Aufrufers pruefen — unbekannt wirft, bevor etwas rausgeht. */
376
427
  function gepruefteZahlungsart(wert) {
377
428
  if (typeof wert === 'object') {
@@ -1,5 +1,5 @@
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
- export { type ReceiptItem, type ReceiptItemPayload, type ReceiptItemPayloadRead, toReceiptItemPayload, fromReceiptItemPayload, receiptItemTotalCents, receiptItemIsValid, negateReceiptItem, type Voucher, type VoucherPayload, toVoucherPayload, fromVoucherPayload, voucherIsValid, type Receipt, type ReceiptPayload, type ReceiptPayloadRead, toReceiptPayload, fromReceiptPayload, receiptSubSumCents, receiptSumCents, type CancellationOf, type CancellationItem, type Cancellation, type CancellationReason, CANCELLATION_REASONS, CANCELLATION_RESERVATION_MS, isCancellationReason, remainingQuantities, type ReceiptCompany, type ReceiptCompanyPayload, fromReceiptCompanyPayload, receiptCompanyTaxInfo, type Cashregister, type CashregisterOnboarding, type CashregisterPayload, type CashregisterOnboardingPayload, fromCashregisterPayload, type ReceiptSummary, type ReceiptSummaryPayload, fromReceiptSummaryPayload, type ZeroKind, ZERO_KINDS, istZeroKind, 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
- export { type AuthCredentials, type KasseneckAuth, type ApiKeyAuthOptions, type RegisterUserAuthOptions, apiKeyAuth, registerUserAuth, DEFAULT_BASE_URL, DEFAULT_TIMEOUT_MS, type HttpResponseLike, type HttpRequestInit, type FetchLike, type TransportOptions, type TransportBodyFields, type KasseneckTransport, type KasseneckBinaryTransport, createTransport, createBinaryTransport, KasseneckApiError, KasseneckAuthError, KasseneckHttpError, KasseneckNetworkError, KasseneckValidationError, type ValidationScope, type KasseneckError, type HttpFailureReason, type CauseDigest, isKasseneckApiError, isKasseneckAuthError, isKasseneckHttpError, isKasseneckNetworkError, isKasseneckValidationError, type ReceiptCommonOptions, type SellReceiptOptions, type CancelReceiptOptions, type CancelReceiptResult, type CreateCancelReceiptOptions, type ReceiptWithCompany, sellReceipt, sellReceiptWithCompany, cancelReceipt, createCancelReceipt, zeroReceipt, getReceipt, getReceiptWithCompany, generateFullReceiptId, getFirstReceiptDate, listMyReceipts, type ListMyReceiptsOptions, type ReceiptList, type ReceiptListStats, listMyCashregisters, checkVoucherCombinationError, downloadDailyReport, downloadMonthlyReport, type CashboxStatus, type SignatureStatus, getCashboxStatus, getSignatureStatus, type KasseneckApi, createKasseneckApi, } from './client/index.js';
2
+ export { type ReceiptItem, type ReceiptItemPayload, type ReceiptItemPayloadRead, toReceiptItemPayload, fromReceiptItemPayload, receiptItemTotalCents, receiptItemIsValid, negateReceiptItem, type TipRecipient, isTipItem, type Voucher, type VoucherPayload, toVoucherPayload, fromVoucherPayload, voucherIsValid, type Receipt, type ReceiptPayload, type ReceiptPayloadRead, toReceiptPayload, fromReceiptPayload, receiptSubSumCents, receiptSumCents, type CancellationOf, type CancellationItem, type Cancellation, type CancellationReason, CANCELLATION_REASONS, CANCELLATION_RESERVATION_MS, isCancellationReason, remainingQuantities, type ReceiptCompany, type ReceiptCompanyPayload, fromReceiptCompanyPayload, receiptCompanyTaxInfo, type Cashregister, type CashregisterOnboarding, type CashregisterPayload, type CashregisterOnboardingPayload, fromCashregisterPayload, type ReceiptSummary, type ReceiptSummaryPayload, fromReceiptSummaryPayload, type ZeroKind, ZERO_KINDS, istZeroKind, 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
+ export { type AuthCredentials, type KasseneckAuth, type ApiKeyAuthOptions, type RegisterUserAuthOptions, apiKeyAuth, registerUserAuth, DEFAULT_BASE_URL, DEFAULT_TIMEOUT_MS, type HttpResponseLike, type HttpRequestInit, type FetchLike, type TransportOptions, type TransportBodyFields, type KasseneckTransport, type KasseneckBinaryTransport, createTransport, createBinaryTransport, KasseneckApiError, KasseneckAuthError, KasseneckHttpError, KasseneckNetworkError, KasseneckValidationError, type ValidationScope, type KasseneckError, type HttpFailureReason, type CauseDigest, isKasseneckApiError, isKasseneckAuthError, isKasseneckHttpError, isKasseneckNetworkError, isKasseneckValidationError, type ReceiptCommonOptions, type SellReceiptOptions, type TipOptions, type TipRecipientShare, type CancelReceiptOptions, type CancelReceiptResult, type CreateCancelReceiptOptions, type ReceiptWithCompany, sellReceipt, sellReceiptWithCompany, cancelReceipt, createCancelReceipt, zeroReceipt, getReceipt, getReceiptWithCompany, generateFullReceiptId, getFirstReceiptDate, listMyReceipts, type ListMyReceiptsOptions, type ReceiptList, type ReceiptListStats, listMyCashregisters, checkVoucherCombinationError, downloadDailyReport, downloadMonthlyReport, type CashboxStatus, type SignatureStatus, getCashboxStatus, getSignatureStatus, type KasseneckApi, createKasseneckApi, } from './client/index.js';
4
4
  export { parseServerTimeStamp, toViennaWallClock, type ViennaWallClock } from './vienna-time.js';
5
5
  export { type KasseSettingsBetrieb, type KasseSettingsGeraet, type KasseSettings, type ArticleGroup, type KasseArtikel, KASSE_BETRIEB_STANDARD, KASSE_GERAET_STANDARD, mergeKasseSettings, fromArticleGroupPayload, fromKasseArtikelPayload, listMyArticleGroups, listMyArticles, getKasseSettings, setMyKasseSettings, setMyRegisterDeviceSettings, verteileRabatt, cancelScopeOf, receiptsScopeOf, } from './kasse/index.js';
package/dist/cjs/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.KasseneckNetworkError = exports.KasseneckHttpError = exports.KasseneckAuthError = exports.KasseneckApiError = exports.createBinaryTransport = exports.createTransport = exports.DEFAULT_TIMEOUT_MS = exports.DEFAULT_BASE_URL = exports.registerUserAuth = exports.apiKeyAuth = exports.hobexReceiptNeedsSignature = exports.hobexReceiptToCardPaymentData = exports.fromHobexReceiptPayload = exports.toHobexReceiptPayload = exports.fromStripeUrlSessionPayload = exports.toStripeUrlSessionPayload = exports.reportMonthReadable = exports.reportMonthKey = exports.nextReportMonth = exports.previousReportMonth = exports.reportMonthFromDate = exports.istZeroKind = exports.ZERO_KINDS = exports.fromReceiptSummaryPayload = exports.fromCashregisterPayload = exports.receiptCompanyTaxInfo = exports.fromReceiptCompanyPayload = exports.remainingQuantities = exports.isCancellationReason = exports.CANCELLATION_RESERVATION_MS = exports.CANCELLATION_REASONS = exports.receiptSumCents = exports.receiptSubSumCents = exports.fromReceiptPayload = exports.toReceiptPayload = exports.voucherIsValid = exports.fromVoucherPayload = exports.toVoucherPayload = exports.negateReceiptItem = exports.receiptItemIsValid = exports.receiptItemTotalCents = exports.fromReceiptItemPayload = exports.toReceiptItemPayload = exports.StripeLinkMode = exports.VoucherAction = exports.VoucherType = exports.CreditCardProvider = exports.KeckPaymentMethod = exports.VatRate = exports.ReceiptType = void 0;
4
- exports.receiptsScopeOf = exports.cancelScopeOf = exports.verteileRabatt = exports.setMyRegisterDeviceSettings = exports.setMyKasseSettings = exports.getKasseSettings = exports.listMyArticles = exports.listMyArticleGroups = exports.fromKasseArtikelPayload = exports.fromArticleGroupPayload = exports.mergeKasseSettings = exports.KASSE_GERAET_STANDARD = exports.KASSE_BETRIEB_STANDARD = exports.toViennaWallClock = exports.parseServerTimeStamp = exports.createKasseneckApi = exports.getSignatureStatus = exports.getCashboxStatus = exports.downloadMonthlyReport = exports.downloadDailyReport = exports.checkVoucherCombinationError = exports.listMyCashregisters = exports.listMyReceipts = exports.getFirstReceiptDate = exports.generateFullReceiptId = exports.getReceiptWithCompany = exports.getReceipt = exports.zeroReceipt = exports.createCancelReceipt = exports.cancelReceipt = exports.sellReceiptWithCompany = exports.sellReceipt = exports.isKasseneckValidationError = exports.isKasseneckNetworkError = exports.isKasseneckHttpError = exports.isKasseneckAuthError = exports.isKasseneckApiError = exports.KasseneckValidationError = void 0;
3
+ exports.KasseneckHttpError = exports.KasseneckAuthError = exports.KasseneckApiError = exports.createBinaryTransport = exports.createTransport = exports.DEFAULT_TIMEOUT_MS = exports.DEFAULT_BASE_URL = exports.registerUserAuth = exports.apiKeyAuth = exports.hobexReceiptNeedsSignature = exports.hobexReceiptToCardPaymentData = exports.fromHobexReceiptPayload = exports.toHobexReceiptPayload = exports.fromStripeUrlSessionPayload = exports.toStripeUrlSessionPayload = exports.reportMonthReadable = exports.reportMonthKey = exports.nextReportMonth = exports.previousReportMonth = exports.reportMonthFromDate = exports.istZeroKind = exports.ZERO_KINDS = exports.fromReceiptSummaryPayload = exports.fromCashregisterPayload = exports.receiptCompanyTaxInfo = exports.fromReceiptCompanyPayload = exports.remainingQuantities = exports.isCancellationReason = exports.CANCELLATION_RESERVATION_MS = exports.CANCELLATION_REASONS = exports.receiptSumCents = exports.receiptSubSumCents = exports.fromReceiptPayload = exports.toReceiptPayload = exports.voucherIsValid = exports.fromVoucherPayload = exports.toVoucherPayload = exports.isTipItem = exports.negateReceiptItem = exports.receiptItemIsValid = exports.receiptItemTotalCents = exports.fromReceiptItemPayload = exports.toReceiptItemPayload = exports.StripeLinkMode = exports.VoucherAction = exports.VoucherType = exports.CreditCardProvider = exports.KeckPaymentMethod = exports.VatRate = exports.ReceiptType = void 0;
4
+ exports.receiptsScopeOf = exports.cancelScopeOf = exports.verteileRabatt = exports.setMyRegisterDeviceSettings = exports.setMyKasseSettings = exports.getKasseSettings = exports.listMyArticles = exports.listMyArticleGroups = exports.fromKasseArtikelPayload = exports.fromArticleGroupPayload = exports.mergeKasseSettings = exports.KASSE_GERAET_STANDARD = exports.KASSE_BETRIEB_STANDARD = exports.toViennaWallClock = exports.parseServerTimeStamp = exports.createKasseneckApi = exports.getSignatureStatus = exports.getCashboxStatus = exports.downloadMonthlyReport = exports.downloadDailyReport = exports.checkVoucherCombinationError = exports.listMyCashregisters = exports.listMyReceipts = exports.getFirstReceiptDate = exports.generateFullReceiptId = exports.getReceiptWithCompany = exports.getReceipt = exports.zeroReceipt = exports.createCancelReceipt = exports.cancelReceipt = exports.sellReceiptWithCompany = exports.sellReceipt = exports.isKasseneckValidationError = exports.isKasseneckNetworkError = exports.isKasseneckHttpError = exports.isKasseneckAuthError = exports.isKasseneckApiError = exports.KasseneckValidationError = exports.KasseneckNetworkError = void 0;
5
5
  var index_js_1 = require("./enums/index.js");
6
6
  Object.defineProperty(exports, "ReceiptType", { enumerable: true, get: function () { return index_js_1.ReceiptType; } });
7
7
  Object.defineProperty(exports, "VatRate", { enumerable: true, get: function () { return index_js_1.VatRate; } });
@@ -16,6 +16,7 @@ Object.defineProperty(exports, "fromReceiptItemPayload", { enumerable: true, get
16
16
  Object.defineProperty(exports, "receiptItemTotalCents", { enumerable: true, get: function () { return index_js_2.receiptItemTotalCents; } });
17
17
  Object.defineProperty(exports, "receiptItemIsValid", { enumerable: true, get: function () { return index_js_2.receiptItemIsValid; } });
18
18
  Object.defineProperty(exports, "negateReceiptItem", { enumerable: true, get: function () { return index_js_2.negateReceiptItem; } });
19
+ Object.defineProperty(exports, "isTipItem", { enumerable: true, get: function () { return index_js_2.isTipItem; } });
19
20
  Object.defineProperty(exports, "toVoucherPayload", { enumerable: true, get: function () { return index_js_2.toVoucherPayload; } });
20
21
  Object.defineProperty(exports, "fromVoucherPayload", { enumerable: true, get: function () { return index_js_2.fromVoucherPayload; } });
21
22
  Object.defineProperty(exports, "voucherIsValid", { enumerable: true, get: function () { return index_js_2.voucherIsValid; } });
@@ -20,8 +20,15 @@ export type KasseBelegAusgabe = 'qr' | 'druck' | 'mail' | 'sms' | 'fragen';
20
20
  export type KasseLayout = 'rechts' | 'links' | 'vollbild';
21
21
  export type KasseKatpos = 'oben' | 'links';
22
22
  export type KasseHoehe = 'S' | 'M' | 'L';
23
- /** `sdp` = Netzwerk ueber Epson Server Direct Print (der Drucker holt Jobs vom Backend, jeder Browser druckt). */
24
- export type KasseDruckerArt = 'sdp' | 'netz' | 'bt' | 'usb';
23
+ /**
24
+ * `sdp` = Netzwerk ueber Epson Server Direct Print (der Drucker holt Jobs vom Backend, jeder Browser druckt).
25
+ * `connect` = Kasseneck Connect (lokaler Agent auf dem Kassen-Rechner druckt fuer die Browser-Kasse).
26
+ */
27
+ export type KasseDruckerArt = 'sdp' | 'netz' | 'bt' | 'usb' | 'connect';
28
+ /** Terminal-Ansprache: direkt per IP (wie bisher) oder ueber Kasseneck Connect (Agent leitet weiter). */
29
+ export type KasseTerminalVia = 'direkt' | 'connect';
30
+ /** Art des Kartenterminals an dieser Kasse: keines oder Hobex HPS (JSON-REST am Geraet). */
31
+ export type KasseTerminalArt = 'keins' | 'hps';
25
32
  export type KassePapier = 'mm58' | 'mm80';
26
33
  export type KasseZeichensatz = 'CP1252' | 'CP437';
27
34
  export type KasseSchnitt = 'partial' | 'full' | 'none';
@@ -55,6 +62,20 @@ export interface KasseSettingsBetrieb {
55
62
  notiz: boolean;
56
63
  suche: boolean;
57
64
  rabatt: 'aus' | 'an';
65
+ /** Bild-Logo (Download-URL aus dem Kasse-Upload); '' = Kuerzel verwenden. */
66
+ logoBild: string;
67
+ /** Seite des Wasserzeichens — ALT: abgeloest von wzPos, bleibt fuers Mischen alter Staende. */
68
+ wzSeite: 'links' | 'mitte' | 'rechts';
69
+ /** Horizontale Lage der Wasserzeichen-MITTE in Prozent (-25 bis 125 — darf ueber den Rand hinaus). */
70
+ wzPos: number;
71
+ /** Vertikale Lage der Wasserzeichen-Mitte in Prozent (-25 bis 125). */
72
+ wzPosV: number;
73
+ /** Deckkraft des Wasserzeichens in Prozent — bewusst Stufen, kein Schieberegler. */
74
+ wzStaerke: 3 | 6 | 10 | 16;
75
+ /** Groesse des Bild-Logos am BELEG (Ansicht, PDF, Kopfzeile). */
76
+ logoSkala: 'S' | 'M' | 'L' | 'XL';
77
+ /** Groesse des Wasserzeichens — getrennt vom Beleg-Logo. */
78
+ wzSkala: 'S' | 'M' | 'L' | 'XL';
58
79
  zahlBar: boolean;
59
80
  zahlKarte: boolean;
60
81
  kartenanbieter: KasseKartenanbieter;
@@ -67,11 +88,17 @@ export interface KasseSettingsBetrieb {
67
88
  kassierenModus: KasseKassierenModus;
68
89
  /** Trinkgeld-Chips in Prozent (eine Nachkommastelle, max 5, eindeutig, Reihenfolge des Chefs). */
69
90
  tgChips: number[];
91
+ /** Rabatt-Chips in Prozent — dieselben Regeln wie tgChips, vom Chef einstellbar. */
92
+ rabattChips: number[];
70
93
  belegAusgabe: KasseBelegAusgabe;
71
94
  fertigSekunden: 0 | 3 | 5 | 10 | 15 | 30 | 60;
95
+ /** Glas-Optik: Kacheln und Korb leicht durchscheinend (Wasserzeichen schimmert). */
96
+ glas: boolean;
97
+ /** Hilfetexte in den Chef-Einstellungen anzeigen. */
98
+ hinweise: boolean;
72
99
  }
73
100
  /** Aktionen der Kasse, die eine Taste bekommen koennen. */
74
- export type KasseTastenAktion = 'kassieren' | 'abschliessen' | 'abbrechen' | 'frei' | 'bar' | 'karte' | 'passend' | 'belege' | 'letzteZurueck';
101
+ export type KasseTastenAktion = 'kassieren' | 'abschliessen' | 'abbrechen' | 'frei' | 'bar' | 'karte' | 'passend' | 'belege' | 'letzteZurueck' | 'einstellungen' | 'abmelden' | 'trinkgeld' | 'vollbild' | 'gegebenLeeren' | 'korbLeeren';
75
102
  export declare const KASSE_TASTEN_AKTIONEN: readonly KasseTastenAktion[];
76
103
  /** Tastenkarte: Aktion -> Tasten (`Mod+F`, `Enter`, `Escape`, `F5` ...; `Mod` = ⌘ auf dem Mac, Strg sonst). */
77
104
  export type KasseTastenkarte = Record<KasseTastenAktion, string[]>;
@@ -93,6 +120,8 @@ export interface KasseSettingsGeraet {
93
120
  druckerId: string;
94
121
  /** ePOS Device-ID bei `druckerArt 'netz'` (Epson direkt per IP), Vorgabe `local_printer`. */
95
122
  druckerDevid: string;
123
+ /** Kennung des Druckers im lokalen Kasseneck-Connect-Agenten, bei `druckerArt 'connect'`. */
124
+ connectDruckerId: string;
96
125
  papier: KassePapier;
97
126
  zeichensatz: KasseZeichensatz;
98
127
  schnitt: KasseSchnitt;
@@ -100,6 +129,13 @@ export interface KasseSettingsGeraet {
100
129
  ladeAuto: KasseLadeAuto;
101
130
  terminalIp: string;
102
131
  terminalPort: number;
132
+ terminalVia: KasseTerminalVia;
133
+ /** Art des Terminals ('keins' = Kartenzahlung ohne Terminal-Anbindung gesperrt, sofern zahlKarte an). */
134
+ terminalArt: KasseTerminalArt;
135
+ /** Tastenmarken (die kleinen Kuerzel an den Knoepfen) anzeigen. */
136
+ tastenMarken: boolean;
137
+ /** Terminal-ID aus dem Hobex-Vertrag (ohne fuehrende Null); noetig fuer Diagnose und Zahlung. */
138
+ terminalTid: string;
103
139
  }
104
140
  export interface KasseSettings {
105
141
  betrieb: KasseSettingsBetrieb;
@@ -12,14 +12,30 @@ Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.KASSE_GERAET_STANDARD = exports.KASSE_BETRIEB_STANDARD = exports.KASSE_TASTEN_STANDARD = exports.KASSE_TASTEN_AKTIONEN = exports.KARTENANBIETER = void 0;
13
13
  exports.mergeKasseSettings = mergeKasseSettings;
14
14
  exports.KARTENANBIETER = ['keiner', 'extern', 'hobex', 'mypos', 'stripe'];
15
- exports.KASSE_TASTEN_AKTIONEN = ['kassieren', 'abschliessen', 'abbrechen', 'frei', 'bar', 'karte', 'passend', 'belege', 'letzteZurueck'];
15
+ exports.KASSE_TASTEN_AKTIONEN = ['kassieren', 'abschliessen', 'abbrechen', 'frei', 'bar', 'karte', 'passend', 'belege', 'letzteZurueck', 'einstellungen', 'abmelden', 'trinkgeld', 'vollbild', 'gegebenLeeren', 'korbLeeren'];
16
16
  exports.KASSE_TASTEN_STANDARD = Object.freeze({
17
17
  kassieren: ['Enter'], abschliessen: ['Enter'], abbrechen: ['Escape'],
18
- frei: ['Mod+F'], bar: ['Mod+B'], karte: ['Mod+K'], passend: ['Mod+P'],
19
- belege: ['Mod+E'], letzteZurueck: ['Mod+Backspace'],
18
+ // Mod+F gehoert dem Vollbild; Betrag frei liegt auf D. Bar bleibt auf B,
19
+ // Belege auf E (Entscheidung vom 21.8., der Tausch von 0.6.25 ist zurueck).
20
+ frei: ['Mod+D'], bar: ['Mod+B'], karte: ['Mod+K'], passend: ['Mod+P'],
21
+ // NICHT Mod+E: das faengt Chrome auf dem Mac selbst ab („Auswahl fuer
22
+ // Suche verwenden") — am Geraet belegt. J wie Journal laesst er durch.
23
+ belege: ['Mod+J'], letzteZurueck: ['Mod+Backspace'],
24
+ // NICHT Mod+T: die Taste ist im Browser reserviert (neuer Tab) und kommt
25
+ // nie bei der Seite an. Mod+G laesst Chrome durch.
26
+ einstellungen: ['Mod+S'], abmelden: ['Mod+L'], trinkgeld: ['Mod+G'],
27
+ vollbild: ['Mod+F'],
28
+ // Mod+C = Kopieren des Browsers — im Kassieren-Schritt ist Kopieren fern,
29
+ // und die Bindung gilt nur dort (Bar + Rueckgeld-Rechner an).
30
+ gegebenLeeren: ['Mod+C'],
31
+ // Bewusst DIESELBE Taste wie gegebenLeeren: die beiden leben in
32
+ // verschiedenen Momenten (Korb vor dem Kassieren, Gegeben-Feld darin) —
33
+ // der Verteiler laesst die nicht zustaendige Aktion durchfallen.
34
+ korbLeeren: ['Mod+C'],
20
35
  });
21
36
  exports.KASSE_BETRIEB_STANDARD = Object.freeze({
22
- logoText: 'K', logoAn: true, logoGroesse: 'M', wasserzeichen: 'anmeldung', farbe: '#1B46F5',
37
+ // Kasseneck-Teal aus dem Branding-Guide nicht mehr das alte Blau.
38
+ logoText: 'K', logoAn: true, logoGroesse: 'M', wasserzeichen: 'anmeldung', farbe: '#0F6B72',
23
39
  stil: 'klar', schrift: 'M', schriftEinst: 'S', kachelstil: 'streifen', uhr: true,
24
40
  sperrbild: true, foto: true, autoAbMin: 0, abNachVerkauf: false, schnellLogin: true,
25
41
  preisAnzeigen: true, ustAnzeigen: false, emoji: true, katFarben: true, freiErlaubt: true,
@@ -28,15 +44,21 @@ exports.KASSE_BETRIEB_STANDARD = Object.freeze({
28
44
  zahlBar: true, zahlKarte: false, kartenanbieter: 'keiner', trinkgeld: false, tgModus: 'beides',
29
45
  tgStufen: { 5: true, 10: true, 15: false, 20: false }, tgChips: [5, 10], tgSplit: true, rueckgeld: true,
30
46
  schnellbar: false, kassierenModus: 'seite',
31
- belegAusgabe: 'qr', fertigSekunden: 0,
47
+ // 'fragen' = Fertig-Seite bietet QR und Bon an — sicherster Standard.
48
+ logoBild: '', wzSeite: 'mitte', wzPos: 50, wzPosV: 50, wzStaerke: 6, logoSkala: 'M', wzSkala: 'M',
49
+ glas: true, hinweise: true,
50
+ rabattChips: [5, 10, 15, 20],
51
+ belegAusgabe: 'fragen', fertigSekunden: 0,
32
52
  });
33
53
  exports.KASSE_GERAET_STANDARD = Object.freeze({
34
54
  layout: 'rechts', katpos: 'oben', spaltenExtra: 0, hoehe: 'M', touch: false,
35
55
  tasten: { ...exports.KASSE_TASTEN_STANDARD },
36
56
  druckerAn: false, druckerArt: 'sdp', druckerIp: '', druckerPort: 9100, druckerBt: '', druckerId: '', druckerDevid: 'local_printer',
57
+ connectDruckerId: '',
37
58
  papier: 'mm80', zeichensatz: 'CP1252', schnitt: 'partial',
38
59
  ladeAn: false, ladeAuto: 'bar',
39
- terminalIp: '', terminalPort: 20008,
60
+ terminalIp: '', terminalPort: 8080, terminalVia: 'direkt',
61
+ terminalArt: 'keins', terminalTid: '', tastenMarken: true,
40
62
  });
41
63
  /**
42
64
  * Standard + gespeichert. Landkarten (`saetze`, `tgStufen`) werden je Schluessel
@@ -1,4 +1,4 @@
1
- export { type ReceiptItem, type ReceiptItemPayload, type ReceiptItemPayloadRead, toReceiptItemPayload, fromReceiptItemPayload, receiptItemTotalCents, receiptItemIsValid, negateReceiptItem, } from './receipt-item.js';
1
+ export { type ReceiptItem, type ReceiptItemPayload, type ReceiptItemPayloadRead, toReceiptItemPayload, fromReceiptItemPayload, receiptItemTotalCents, receiptItemIsValid, negateReceiptItem, type TipRecipient, isTipItem, isDiscountItem, } from './receipt-item.js';
2
2
  export { type Voucher, type VoucherPayload, toVoucherPayload, fromVoucherPayload, voucherIsValid } from './voucher.js';
3
3
  export { type Receipt, type ReceiptPayload, type ReceiptPayloadRead, toReceiptPayload, fromReceiptPayload, receiptSubSumCents, receiptSumCents, } from './receipt.js';
4
4
  export { type CancellationOf, type CancellationItem, type Cancellation, type CancellationReason, CANCELLATION_REASONS, CANCELLATION_RESERVATION_MS, isCancellationReason, remainingQuantities, } from './cancellation.js';
@@ -1,12 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.hobexReceiptNeedsSignature = exports.hobexReceiptToCardPaymentData = exports.fromHobexReceiptPayload = exports.toHobexReceiptPayload = exports.fromStripeUrlSessionPayload = exports.toStripeUrlSessionPayload = exports.reportMonthReadable = exports.reportMonthKey = exports.nextReportMonth = exports.previousReportMonth = exports.reportMonthFromDate = exports.istZeroKind = exports.ZERO_KINDS = exports.fromReceiptSummaryPayload = exports.fromCashregisterPayload = exports.receiptCompanyTaxInfo = exports.fromReceiptCompanyPayload = exports.remainingQuantities = exports.isCancellationReason = exports.CANCELLATION_RESERVATION_MS = exports.CANCELLATION_REASONS = exports.receiptSumCents = exports.receiptSubSumCents = exports.fromReceiptPayload = exports.toReceiptPayload = exports.voucherIsValid = exports.fromVoucherPayload = exports.toVoucherPayload = exports.negateReceiptItem = exports.receiptItemIsValid = exports.receiptItemTotalCents = exports.fromReceiptItemPayload = exports.toReceiptItemPayload = void 0;
3
+ exports.hobexReceiptNeedsSignature = exports.hobexReceiptToCardPaymentData = exports.fromHobexReceiptPayload = exports.toHobexReceiptPayload = exports.fromStripeUrlSessionPayload = exports.toStripeUrlSessionPayload = exports.reportMonthReadable = exports.reportMonthKey = exports.nextReportMonth = exports.previousReportMonth = exports.reportMonthFromDate = exports.istZeroKind = exports.ZERO_KINDS = exports.fromReceiptSummaryPayload = exports.fromCashregisterPayload = exports.receiptCompanyTaxInfo = exports.fromReceiptCompanyPayload = exports.remainingQuantities = exports.isCancellationReason = exports.CANCELLATION_RESERVATION_MS = exports.CANCELLATION_REASONS = exports.receiptSumCents = exports.receiptSubSumCents = exports.fromReceiptPayload = exports.toReceiptPayload = exports.voucherIsValid = exports.fromVoucherPayload = exports.toVoucherPayload = exports.isDiscountItem = exports.isTipItem = exports.negateReceiptItem = exports.receiptItemIsValid = exports.receiptItemTotalCents = exports.fromReceiptItemPayload = exports.toReceiptItemPayload = void 0;
4
4
  var receipt_item_js_1 = require("./receipt-item.js");
5
5
  Object.defineProperty(exports, "toReceiptItemPayload", { enumerable: true, get: function () { return receipt_item_js_1.toReceiptItemPayload; } });
6
6
  Object.defineProperty(exports, "fromReceiptItemPayload", { enumerable: true, get: function () { return receipt_item_js_1.fromReceiptItemPayload; } });
7
7
  Object.defineProperty(exports, "receiptItemTotalCents", { enumerable: true, get: function () { return receipt_item_js_1.receiptItemTotalCents; } });
8
8
  Object.defineProperty(exports, "receiptItemIsValid", { enumerable: true, get: function () { return receipt_item_js_1.receiptItemIsValid; } });
9
9
  Object.defineProperty(exports, "negateReceiptItem", { enumerable: true, get: function () { return receipt_item_js_1.negateReceiptItem; } });
10
+ Object.defineProperty(exports, "isTipItem", { enumerable: true, get: function () { return receipt_item_js_1.isTipItem; } });
11
+ Object.defineProperty(exports, "isDiscountItem", { enumerable: true, get: function () { return receipt_item_js_1.isDiscountItem; } });
10
12
  var voucher_js_1 = require("./voucher.js");
11
13
  Object.defineProperty(exports, "toVoucherPayload", { enumerable: true, get: function () { return voucher_js_1.toVoucherPayload; } });
12
14
  Object.defineProperty(exports, "fromVoucherPayload", { enumerable: true, get: function () { return voucher_js_1.fromVoucherPayload; } });
@@ -27,7 +27,36 @@ export interface ReceiptItem {
27
27
  quantity: number;
28
28
  vat: VatRate | number;
29
29
  priceCents: number;
30
+ /**
31
+ * Trinkgeld-Position (vom Backend aus dem Parameter `tip` erzeugt, siehe
32
+ * [ReceiptCommonOptions.tip]) oder Rabatt-Position (`verteileRabatt`).
33
+ * Mitarbeiter-Trinkgeld ist Durchlaeufer (0 %, kein Umsatz),
34
+ * Inhaber-Trinkgeld (`recipient.owner`) Umsatz. Rabatt-Positionen sind
35
+ * negative Entgeltminderungen je Steuersatz; die Kennzeichnung steuert nur
36
+ * die Bon-Darstellung (Summenzeile mit Aufteilung), nie die Beträge.
37
+ */
38
+ kind?: 'tip' | 'discount';
39
+ /** Empfaenger der Trinkgeld-Position; `null` = nicht zugeordnet. */
40
+ recipient?: TipRecipient | null;
41
+ /** Zahlart der Trinkgeld-Position (kann von der des Belegs abweichen). */
42
+ paymentMethod?: string;
43
+ /**
44
+ * Artikel-Verweis (Artikelstamm) — Grundlage der Erloesgruppen-Zuordnung
45
+ * im Bericht. Optional; Handeingaben haben keinen.
46
+ */
47
+ articleId?: string;
30
48
  }
49
+ /** Empfaenger einer Trinkgeld-Position (Kassen-Benutzer, Snapshot des Namens). */
50
+ export interface TipRecipient {
51
+ registerUserId: string;
52
+ name: string;
53
+ /** true: Inhaber — das Trinkgeld ist Umsatz mit USt. */
54
+ owner?: boolean;
55
+ }
56
+ /** Trinkgeld-Position? Die eine Erkennungsstelle — niemand prueft `kind` selbst. */
57
+ export declare function isTipItem(item: ReceiptItem | ReceiptItemPayloadRead): boolean;
58
+ /** Rabatt-Position? Die eine Erkennungsstelle — niemand prueft `kind` selbst. */
59
+ export declare function isDiscountItem(item: ReceiptItem | ReceiptItemPayloadRead): boolean;
31
60
  /**
32
61
  * Nutzlast-Form, die dieses Paket **schreibt** (v2):
33
62
  * `{ name, quantity, unitPriceCents, vatRate }`.
@@ -37,6 +66,12 @@ export interface ReceiptItemPayload {
37
66
  quantity: number;
38
67
  unitPriceCents: number;
39
68
  vatRate: number;
69
+ /** Trinkgeld-/Rabatt-Kennzeichnung, siehe [ReceiptItem.kind]. */
70
+ kind?: 'tip' | 'discount';
71
+ recipient?: TipRecipient | null;
72
+ paymentMethod?: string;
73
+ /** Artikel-Verweis, siehe [ReceiptItem.articleId]. */
74
+ articleId?: string;
40
75
  }
41
76
  /**
42
77
  * Nutzlast-Form, die dieses Paket **liest** — v2 und v1 nebeneinander, alles
@@ -54,6 +89,10 @@ export interface ReceiptItemPayload {
54
89
  */
55
90
  export interface ReceiptItemPayloadRead {
56
91
  name?: string | null;
92
+ /** Trinkgeld-Position (Backend), siehe [ReceiptItem.kind]. */
93
+ kind?: string | null;
94
+ recipient?: TipRecipient | null;
95
+ paymentMethod?: string | null;
57
96
  /** v2: Menge */
58
97
  quantity?: number | null;
59
98
  /** v2: Einzelpreis in Cent */
@@ -68,6 +107,8 @@ export interface ReceiptItemPayloadRead {
68
107
  priceOne?: number | null;
69
108
  /** v1: Steuersatz */
70
109
  vat?: number | null;
110
+ /** Artikel-Verweis, siehe [ReceiptItem.articleId]. */
111
+ articleId?: string | null;
71
112
  }
72
113
  export declare function toReceiptItemPayload(item: ReceiptItem): ReceiptItemPayload;
73
114
  /**
@@ -1,5 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isTipItem = isTipItem;
4
+ exports.isDiscountItem = isDiscountItem;
3
5
  exports.toReceiptItemPayload = toReceiptItemPayload;
4
6
  exports.fromReceiptItemPayload = fromReceiptItemPayload;
5
7
  exports.receiptItemTotalCents = receiptItemTotalCents;
@@ -7,17 +9,42 @@ exports.receiptItemIsValid = receiptItemIsValid;
7
9
  exports.negateReceiptItem = negateReceiptItem;
8
10
  const index_js_1 = require("../enums/index.js");
9
11
  const enum_payload_js_1 = require("./enum-payload.js");
12
+ /** Trinkgeld-Position? Die eine Erkennungsstelle — niemand prueft `kind` selbst. */
13
+ function isTipItem(item) {
14
+ return item != null && item.kind === 'tip';
15
+ }
16
+ /** Rabatt-Position? Die eine Erkennungsstelle — niemand prueft `kind` selbst. */
17
+ function isDiscountItem(item) {
18
+ return item != null && item.kind === 'discount';
19
+ }
10
20
  function toReceiptItemPayload(item) {
11
21
  // Schreibpfad bleibt streng: ein unaufgeloester (unbekannter) Steuersatz
12
22
  // darf nicht unbesehen wieder hinausgehen.
13
23
  const vat = typeof item.vat === 'number' ? (0, enum_payload_js_1.requireVatRateByRate)(item.vat) : item.vat;
14
24
  pruefeMenge(item.quantity);
15
- return {
25
+ const nutzlast = {
16
26
  name: item.name,
17
27
  quantity: item.quantity,
18
28
  unitPriceCents: item.priceCents,
19
29
  vatRate: vat.rate,
20
30
  };
31
+ // Trinkgeld-Kennzeichnung reist mit — sonst wuerde ein Storno dieser
32
+ // Position am Backend als Warenzeile ankommen und aus den Trinkgeld-
33
+ // Aggregaten nicht wieder herausgerechnet.
34
+ if (item.kind === 'tip') {
35
+ nutzlast.kind = 'tip';
36
+ nutzlast.recipient = item.recipient ?? null;
37
+ if (item.paymentMethod != null)
38
+ nutzlast.paymentMethod = item.paymentMethod;
39
+ }
40
+ // Rabatt-Kennzeichnung reist mit — sonst kaeme ein Storno dieser Position
41
+ // am Bon wieder als gewoehnliche Warenzeile mit "1 x" an.
42
+ if (item.kind === 'discount')
43
+ nutzlast.kind = 'discount';
44
+ // Artikel-Verweis reist mit (Erloesgruppen im Bericht); leere Werte nicht.
45
+ if (item.articleId != null && item.articleId !== '')
46
+ nutzlast.articleId = item.articleId;
47
+ return nutzlast;
21
48
  }
22
49
  /**
23
50
  * Liest beide Nutzlast-Formen (siehe [ReceiptItemPayloadRead]). Reihenfolge
@@ -46,6 +73,19 @@ function fromReceiptItemPayload(payload) {
46
73
  // der Fall entsteht also nur bei einer kaputten Nutzlast).
47
74
  vat: satz != null ? (0, enum_payload_js_1.readVatRateByRate)(satz) : index_js_1.VatRate.vat0,
48
75
  priceCents: cents != null ? Math.round(cents) : euro != null ? Math.round(euro * 100) : 0,
76
+ // Trinkgeld-Felder nur, wenn es wirklich eine Tip-Position ist: eine
77
+ // Warenzeile bleibt so schlank wie bisher (Golden-Belege, deepEqual).
78
+ ...(payload.kind === 'tip'
79
+ ? {
80
+ kind: 'tip',
81
+ recipient: payload.recipient ?? null,
82
+ ...(payload.paymentMethod != null ? { paymentMethod: payload.paymentMethod } : {}),
83
+ }
84
+ : payload.kind === 'discount'
85
+ ? { kind: 'discount' }
86
+ : {}),
87
+ // Artikel-Verweis erhalten (Storno-Spiegelung schreibt ihn wieder mit).
88
+ ...(typeof payload.articleId === 'string' && payload.articleId !== '' ? { articleId: payload.articleId } : {}),
49
89
  };
50
90
  }
51
91
  /** Erster brauchbarer Zahlenwert der Reihe; `null`/`undefined`/NaN zaehlen nicht. */
@@ -41,6 +41,11 @@ export interface Receipt {
41
41
  legalMessage: string[];
42
42
  signatureSuccess?: boolean;
43
43
  customProjectId?: string;
44
+ /**
45
+ * Summe aller Trinkgeld-Positionen in Cent (Storno negativ) — abgeleitetes
46
+ * Lesefeld des Backends, nur vorhanden, wenn der Beleg Trinkgeld traegt.
47
+ */
48
+ tipCents?: number;
44
49
  /** Nur am Storno-Beleg: das stornierte Original. */
45
50
  cancellationOf?: CancellationOf;
46
51
  /** Nur am Storno-Beleg: Grund-Code aus [CANCELLATION_REASONS]. */
@@ -71,6 +76,8 @@ export interface ReceiptPayload {
71
76
  legalMessage: string;
72
77
  signatureSuccess: boolean | null;
73
78
  customProjectId: string | null;
79
+ /** Nur bei Belegen mit Trinkgeld (siehe [Receipt.tipCents]). */
80
+ tipCents?: number | null;
74
81
  }
75
82
  /**
76
83
  * Nutzlast-Form, die dieses Paket **liest**. Unterscheidet sich von
@@ -48,6 +48,7 @@ function toReceiptPayload(receipt) {
48
48
  legalMessage: receipt.legalMessage.join('\n'),
49
49
  signatureSuccess: receipt.signatureSuccess ?? null,
50
50
  customProjectId: receipt.customProjectId ?? null,
51
+ ...(receipt.tipCents != null ? { tipCents: receipt.tipCents } : {}),
51
52
  };
52
53
  }
53
54
  // Nullbelege haben keine Positionen -> items kann fehlen/null sein (siehe Dart-Vorbild).
@@ -74,6 +75,7 @@ function fromReceiptPayload(payload) {
74
75
  legalMessage: leereZeile(payload.legalMessage),
75
76
  signatureSuccess: payload.signatureSuccess ?? undefined,
76
77
  customProjectId: payload.customProjectId ?? undefined,
78
+ ...(typeof payload.tipCents === 'number' ? { tipCents: payload.tipCents } : {}),
77
79
  ...(payload.cancellationOf ? { cancellationOf: { receiptId: payload.cancellationOf.receiptId, fullReceiptId: payload.cancellationOf.fullReceiptId ?? null } } : {}),
78
80
  ...(payload.cancellationReason ? { cancellationReason: payload.cancellationReason } : {}),
79
81
  ...((0, receipt_summary_js_1.istZeroKind)(payload.zeroKind) ? { zeroKind: payload.zeroKind } : {}),
@@ -633,4 +633,11 @@ function escPosQrCode(doc, text, options = {}) {
633
633
  if (options.correction !== undefined)
634
634
  qrOptionen.correction = options.correction;
635
635
  anhaengen(doc, qrCodeBytes(text, qrOptionen));
636
+ // Ausrichtung SOFORT zuruecksetzen: `ESC a` gilt nur am Zeilenanfang. Die
637
+ // Spalten-Positionierung (`ESC $`) schickt ihren Reset sonst mitten in der
638
+ // Zeile — Epson ignoriert ihn, und alles nach dem QR rueckte nach rechts
639
+ // (belegt am TM-T20, 2026-08-20).
640
+ if ((options.align ?? 'center') !== 'left') {
641
+ escPosSetStyles(doc, { align: 'left' });
642
+ }
636
643
  }
@@ -20,7 +20,12 @@ function ReceiptLayoutView({ layout, className, renderQr, qrVerdeckt = false, qr
20
20
  */
21
21
  function QrVerdeckt({ data, text = 'Antippen zum Anzeigen', children }) {
22
22
  const [offen, setOffen] = (0, react_1.useState)(false);
23
- return ((0, jsx_runtime_1.jsxs)("button", { type: "button", className: offen ? 'keck-receipt-qr-toggle keck-receipt-qr-toggle--offen' : 'keck-receipt-qr-toggle', "aria-pressed": offen, "aria-label": offen ? 'QR-Code verdecken' : 'QR-Code anzeigen', "data-qr": data, onClick: () => setOffen((o) => !o), style: { position: 'relative', display: 'inline-block', border: 'none', background: 'transparent', padding: 0, cursor: 'pointer' }, children: [(0, jsx_runtime_1.jsx)("span", { style: { display: 'inline-block', filter: offen ? undefined : 'blur(6px)', transition: 'filter .15s ease' }, children: children }), !offen && ((0, jsx_runtime_1.jsx)("span", { className: "keck-receipt-qr-toggle-text", style: { position: 'absolute', inset: 0, display: 'grid', placeItems: 'center', fontSize: '0.75em', fontWeight: 700 }, children: text }))] }));
23
+ return ((0, jsx_runtime_1.jsxs)("button", { type: "button", className: offen ? 'keck-receipt-qr-toggle keck-receipt-qr-toggle--offen' : 'keck-receipt-qr-toggle', "aria-pressed": offen, "aria-label": offen ? 'QR-Code verdecken' : text, "data-qr": data, onClick: () => setOffen((o) => !o), style: { position: 'relative', display: 'inline-block', border: 'none', background: 'transparent', padding: 0, cursor: 'pointer' }, children: [(0, jsx_runtime_1.jsx)("span", { style: { display: 'inline-block', filter: offen ? undefined : 'blur(3px)', transition: 'filter .15s ease' }, children: children }), !offen && (
24
+ // Tipp-Finger statt Textzeile: versteht jeder, verdeckt fast nichts.
25
+ // Der Text bleibt als aria-label fuer Hilfsmittel. Einfarbige
26
+ // Strichzeichnung statt Emoji — Emojis rendern je Plattform anders
27
+ // und stechen farblich aus dem Beleg heraus.
28
+ (0, jsx_runtime_1.jsx)("span", { "aria-hidden": "true", className: "keck-receipt-qr-toggle-text", style: { position: 'absolute', inset: 0, display: 'grid', placeItems: 'center' }, children: (0, jsx_runtime_1.jsx)("span", { style: { display: 'grid', placeItems: 'center', width: '2.2em', height: '2.2em', borderRadius: '50%', background: 'rgba(255,255,255,.92)', boxShadow: '0 1px 4px rgba(0,0,0,.25)', color: 'rgba(0,0,0,.72)' }, children: (0, jsx_runtime_1.jsxs)("svg", { className: "keck-receipt-qr-toggle-finger", width: "1.25em", height: "1.25em", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", children: [(0, jsx_runtime_1.jsx)("path", { d: "M10 9.5V4a2 2 0 0 0-2-2a2 2 0 0 0-2 2v10" }), (0, jsx_runtime_1.jsx)("path", { d: "M14 10V9a2 2 0 0 0-2-2a2 2 0 0 0-2 2v1" }), (0, jsx_runtime_1.jsx)("path", { d: "M18 11v-1a2 2 0 0 0-2-2a2 2 0 0 0-2 2" }), (0, jsx_runtime_1.jsx)("path", { d: "M18 11a2 2 0 1 1 4 0v3a8 8 0 0 1-8 8h-2c-2.8 0-4.5-.86-5.99-2.34l-3.6-3.6a2 2 0 0 1 2.83-2.82L7 15" })] }) }) }))] }));
24
29
  }
25
30
  function Zeile({ zeile, renderQr, qrVerdeckt, qrVerdecktText }) {
26
31
  switch (zeile.kind) {
@@ -32,6 +37,11 @@ function Zeile({ zeile, renderQr, qrVerdeckt, qrVerdecktText }) {
32
37
  // so bleibt die Aufteilung auf allen Ausgabewegen dieselbe.
33
38
  flex: `0 0 ${((spalte.width / 12) * 100).toFixed(4)}%`,
34
39
  textAlign: AUSRICHTUNG[spalte.align],
40
+ // minWidth 0: Flex-Zellen haben sonst min-content-Breite --
41
+ // ein langes Wort ohne Leerzeichen (Hex-Seriennummer der
42
+ // Signaturkarte) wuerde die Zelle sprengen und abgeschnitten,
43
+ // statt per break-word umzubrechen.
44
+ minWidth: 0,
35
45
  overflowWrap: 'break-word',
36
46
  }, children: spalte.text }, index))) }));
37
47
  case 'rule':
@@ -49,6 +49,6 @@ function verteileRabatt(positionen, rabattCents, name = 'Rabatt') {
49
49
  }
50
50
  }
51
51
  return liste
52
- .map((g, i) => ({ name, quantity: 1, vat: g.vat, priceCents: -anteile[i] }))
52
+ .map((g, i) => ({ name, quantity: 1, vat: g.vat, priceCents: -anteile[i], kind: 'discount' }))
53
53
  .filter((z) => z.priceCents < 0);
54
54
  }
@@ -50,11 +50,16 @@ function escPosLayoutBytes(layout, options = {}) {
50
50
  case 'qr':
51
51
  (0, index_js_1.escPosQrCode)(doc, zeile.qr ?? '', qrOptionen);
52
52
  break;
53
- case 'banner':
54
- // Belegart/Warnung: fett, doppelte Hoehe; Warnungen zusaetzlich invers.
55
- // Der Text ist bereits zentriert aufgefuellt (Raster).
56
- (0, index_js_1.escPosText)(doc, zeile.text.trimEnd(), { styles: { align: 'left', bold: true, height: 2, reverse: zeile.ton === 'warnung' } });
53
+ case 'banner': {
54
+ // Belegart/Warnung: fett zwischen zwei Volllinien derselbe Rahmen-
55
+ // Stil wie im Beleg-Viewer und im PDF (frueher druckte der Bon invers
56
+ // weiss-auf-schwarz und sah damit anders aus als jede Anzeige).
57
+ const rahmen = '='.repeat(grid_js_1.ZEICHEN_JE_PAPIER[paperSize]);
58
+ (0, index_js_1.escPosText)(doc, rahmen, { styles: { align: 'left', bold: true } });
59
+ (0, index_js_1.escPosText)(doc, zeile.text.trimEnd(), { styles: { align: 'left', bold: true, height: 2 } });
60
+ (0, index_js_1.escPosText)(doc, rahmen, { styles: { align: 'left', bold: true } });
57
61
  break;
62
+ }
58
63
  default:
59
64
  (0, index_js_1.escPosText)(doc, zeile.text.trimEnd(), { styles: { align: 'left', bold: zeile.bold } });
60
65
  break;
@@ -21,9 +21,12 @@ import type { PosPaperSize } from '../printing/escpos.js';
21
21
  * stuende auch auf dem Bildschirm „EUR".
22
22
  *
23
23
  * Bewusst **nicht** enthalten: Firmenlogo und Rasterbilder (brauchen
24
- * Bildverarbeitung, die im Browser anders aussieht als in Node), PDF-Erzeugung,
25
- * Druckeransteuerung und die anbieterspezifischen Kartenzahlungsbloecke des
26
- * Vorbilds (GP Tom, Hobex, SumUp, myPOS, Stripe).
24
+ * Bildverarbeitung, die im Browser anders aussieht als in Node), PDF-Erzeugung
25
+ * und Druckeransteuerung. Von den anbieterspezifischen Kartenzahlungsbloecken
26
+ * des Vorbilds (GP Tom, SumUp, myPOS, Stripe) ist nur der **Hobex-Block**
27
+ * enthalten: die Browser-Kasse bedient das HPS-Terminal selbst (via Kasseneck
28
+ * Connect) und druckt den Bon aus genau diesem Modell — die uebrigen Anbieter
29
+ * drucken in ihren eigenen Apps.
27
30
  */
28
31
  /** Ausrichtung einer Zeile bzw. einer Spalte. */
29
32
  export type LayoutAlign = 'left' | 'center' | 'right';