@kreiseck/kasseneck-api 1.0.0-rc.4 → 1.0.0-rc.5

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 (53) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/README.md +40 -4
  3. package/dist/cjs/index.d.ts +1 -1
  4. package/dist/cjs/index.js +4 -1
  5. package/dist/cjs/pos/index.d.ts +1 -1
  6. package/dist/cjs/pos/index.js +6 -1
  7. package/dist/cjs/pos/texte.d.ts +95 -2
  8. package/dist/cjs/pos/texte.js +111 -3
  9. package/dist/cjs/receipt/due.d.ts +33 -0
  10. package/dist/cjs/receipt/due.js +61 -15
  11. package/dist/cjs/receipt/index.d.ts +1 -1
  12. package/dist/cjs/receipt/index.js +4 -1
  13. package/dist/cjs/stored/index.d.ts +1 -0
  14. package/dist/cjs/stored/index.js +7 -0
  15. package/dist/cjs/stored/layout.d.ts +55 -0
  16. package/dist/cjs/stored/layout.js +119 -0
  17. package/dist/cjs/version.d.ts +1 -1
  18. package/dist/cjs/version.js +1 -1
  19. package/dist/esm/index.d.ts +1 -1
  20. package/dist/esm/index.js +1 -1
  21. package/dist/esm/pos/index.d.ts +1 -1
  22. package/dist/esm/pos/index.js +1 -1
  23. package/dist/esm/pos/texte.d.ts +95 -2
  24. package/dist/esm/pos/texte.js +108 -2
  25. package/dist/esm/receipt/due.d.ts +33 -0
  26. package/dist/esm/receipt/due.js +58 -15
  27. package/dist/esm/receipt/index.d.ts +1 -1
  28. package/dist/esm/receipt/index.js +1 -1
  29. package/dist/esm/stored/index.d.ts +1 -0
  30. package/dist/esm/stored/index.js +2 -0
  31. package/dist/esm/stored/layout.d.ts +55 -0
  32. package/dist/esm/stored/layout.js +113 -0
  33. package/dist/esm/version.d.ts +1 -1
  34. package/dist/esm/version.js +1 -1
  35. package/fixtures/hobex-hps-codes.json +1 -1
  36. package/fixtures/invoice-api.schema.json +1 -1
  37. package/fixtures/invoice-texts.json +1 -1
  38. package/fixtures/pos-message-cases.json +31 -1
  39. package/fixtures/pos-texts.json +68 -1
  40. package/fixtures/receipt-due-errors.json +56 -0
  41. package/fixtures/surface.json +11 -1
  42. package/fixtures/v3/antworten/belege.json +1 -1
  43. package/fixtures/v3/antworten/belegmail.json +1 -1
  44. package/fixtures/v3/antworten/kasse-belege.json +1 -1
  45. package/fixtures/v3/antworten/kasse.json +2 -2
  46. package/fixtures/v3/antworten/rechnungen.json +1 -1
  47. package/fixtures/v3/antworten/storno.json +1 -1
  48. package/fixtures/v3/stored/belege.json +1 -1
  49. package/fixtures/v3/stored/kasse.json +2 -2
  50. package/fixtures/v3/stored/rechnungen.json +1 -1
  51. package/fixtures/v3/v3-vokabular.json +1 -1
  52. package/fixtures/v3/zahlbetrag-faelle.json +1 -1
  53. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -19,6 +19,79 @@ bytes of every golden receipt.
19
19
  This entry is written in English, like the developer documentation from here
20
20
  on.
21
21
 
22
+ ### 1.0.0-rc.5
23
+
24
+ Findings from moving the web register onto 1.0.
25
+
26
+ - **Plain text for edge codes on the register screen.** `ERROR_RULES` is
27
+ unchanged (one rule per kind, as in rc.4), so a register that looks a rule
28
+ up by `kind` alone keeps showing the same sentence. New
29
+ `ERROR_CODE_RULES` refine it per code. `route_missing`, `not_found` and
30
+ `internal_translation_error` (nothing happened on the server) show
31
+ `server.connection_disturbed` ("Die Verbindung zum Kassenserver ist
32
+ gestört. Bitte kurz warten und erneut versuchen."). `dialect_mismatch`,
33
+ `response_translation_failed` and `response_unreadable` (outcome unknown)
34
+ show `server.response_unreadable` ("Die Antwort des Kassenservers war nicht
35
+ lesbar. Bitte die Kasse neu öffnen und vor einem neuen Versuch prüfen, ob
36
+ der letzte Vorgang schon gebucht ist."), which never invites a retry; a
37
+ test holds every code the package treats as outcome-unknown to that. Both
38
+ sentences fit both registers (no page, no browser). The technical
39
+ sentence of the package or the edge stays in `error.message`.
40
+ `findErrorRule(kind, { code, outcome })` applies the code rules, then the
41
+ outcome rules, then `ERROR_RULES`. `pos-texts.json` carries
42
+ `errorCodeRules`, `errorOutcomeRules` and `callsWithEffect` next to
43
+ `errorRules`. `pos-message-cases.json` is at version 2: a case may carry
44
+ `error.code` (`api`) or `error.outcome` (`timeout`, `network`); the cases
45
+ without them are byte for byte those of rc.4. Reason: until 0.31 an HTML
46
+ answer was an HTTP error and showed `server.unexpected`; under 1.0 it is an
47
+ API error, and the register showed "Route fehlt: …" to the cashier.
48
+ - **Timeout and network error on a call with an effect never invite a
49
+ retry.** New `ERROR_OUTCOME_RULES`: with outcome `unknown`, `timeout` and
50
+ `network` show `network.outcome_unknown` ("Der Server hat nicht
51
+ geantwortet, der Vorgang kann trotzdem gebucht sein. Bitte vor einem neuen
52
+ Versuch prüfen, ob der letzte Vorgang schon gebucht ist."). New
53
+ `messageOutcome(error)` gives the outcome for the sentence: `unknown` when
54
+ `isOutcomeUnknown` says so, and after a timeout or network error on any
55
+ call in `CALLS_WITH_EFFECT` (the six with an unknown outcome, plus
56
+ `createPrintJob` and `sendReceiptEmail`); fetch fails the same way before
57
+ and after the request left, so the package cannot tell. Every other call
58
+ keeps the rc.4 sentence ("… und erneut versuchen."). Reason: after a
59
+ timeout the receipt, the card payment or the print job may already exist;
60
+ a retry could book or print it twice.
61
+ - **New labels:** `register.device_unnamed` ("Kasse", a device without a
62
+ name, `deviceLabel: null`), `login.locked_seconds` (`{seconds}` left of the
63
+ PIN lock), `split.remaining_with_rounding` (`{amount}`, `{cents}` with its
64
+ sign: the last round of a split payment takes the rounding cent). Reason:
65
+ the web register had to write these words itself.
66
+ - **`receiptDueCents` throws `ReceiptDueError`, not `RangeError`.** Code
67
+ `receipt_due_unavailable`, the cause in `reason` (`RECEIPT_DUE_ERROR_REASONS`,
68
+ e.g. `tip_without_goods` for a tip with an amount but no goods, also for a
69
+ staff tip), `outcome: 'rejected'`, guard `isReceiptDueError`. The cases are
70
+ in `fixtures/receipt-due-errors.json`. Every computed amount is unchanged
71
+ (the 1206 generated cases). Reason: a `RangeError` without a code could not
72
+ be told apart from a bug, and the register must say "not sent" before it
73
+ starts the card terminal.
74
+ - **`./stored`: `fromStoredLayout`, `toStoredLayout`, `fromStoredPrintLogo`,
75
+ `toStoredPrintLogo`.** The internal form of the receipt layout (`regelwerk`,
76
+ banner `ton` `belegart`/`warnung`) and of the print logo (`stufe`,
77
+ `pxBreite`, `pxHoehe`, `breite`, `hoehe`, `zeilen`) to 1.0 and back, checked
78
+ against all 40 golden layouts as 0.31.0 wrote them, byte for byte including
79
+ the key order. `fromStoredLayout` returns `null` for anything short of a
80
+ whole layout (no or empty `lines`, a line that is not an object, no
81
+ `paperSize`), so the caller rebuilds it with the `TESTKASSE` banner.
82
+ Reason: the web register
83
+ cache, the panel and the lab each carried their own copy.
84
+ - **`fromStoredReceipt` leaves English 1.0 values as they are** (a fixture
85
+ receipt with `cancellationReason: 'customer_cancelled'`). It did so
86
+ already; now it is promised and tested.
87
+ - **`fixtures/v3` refreshed** from the backend (71af3bf). Only the input
88
+ fingerprints in `_quelle` change (the client-version check of 29.09);
89
+ no name, value or code.
90
+ - **Contract versions:** a test holds the version in `surface.json`,
91
+ `pos-texts.json`, `invoice-texts.json`, `hobex-hps-codes.json` and
92
+ `invoice-api.schema.json` (and `PACKAGE_VERSION`, `package-lock.json`, this
93
+ file) to `package.json`.
94
+
22
95
  ### Migrating from 0.x
23
96
 
24
97
  #### The 0.x line
package/README.md CHANGED
@@ -133,6 +133,12 @@ is turnover, spread over the VAT rates of the goods and discounted with them,
133
133
  while a staff tip goes untouched into the 0 % bucket. Only the register knows
134
134
  which one applies, so the helper never guesses.
135
135
 
136
+ Input the amount cannot be computed from (a tip with an amount but no goods,
137
+ a tip on a null receipt, a missing `tipRecipient` …) throws a
138
+ `ReceiptDueError` with `code: 'receipt_due_unavailable'`, the cause in
139
+ `reason` (`RECEIPT_DUE_ERROR_REASONS`) and `outcome: 'rejected'`: nothing has
140
+ been sent. Tell the cashier before the card terminal is started.
141
+
136
142
  ## What a fiscal cash register in Austria must do
137
143
 
138
144
  The obligation to use a fiscal cash register and to issue receipts is set out
@@ -803,6 +809,21 @@ the register's text catalogue (`MESSAGES`, `LABELS`, `messageText`,
803
809
  `messageText('checkout.locked', { reason })`. The rendered German texts are
804
810
  the same as in 0.x.
805
811
 
812
+ `ERROR_RULES` says which text a failed call shows, one rule per kind, the
813
+ same in both registers. `ERROR_CODE_RULES` (per `error.code`) and
814
+ `ERROR_OUTCOME_RULES` (per outcome) refine it; `findErrorRule(kind, { code,
815
+ outcome: messageOutcome(error) })` applies all three in that order. A timeout
816
+ or network error on a call with an effect (`CALLS_WITH_EFFECT`) shows
817
+ `network.outcome_unknown`, which never suggests a retry. The edge codes
818
+ show a plain German sentence instead of the package's technical sentence,
819
+ which stays in `error.message` for the log: `route_missing`, `not_found` and
820
+ `internal_translation_error` (nothing happened on the server)
821
+ `server.connection_disturbed`, which suggests trying again;
822
+ `dialect_mismatch`, `response_translation_failed` and `response_unreadable`
823
+ (outcome unknown) `server.response_unreadable`, which asks to reopen the
824
+ register and check whether the last operation was booked, and never
825
+ suggests a retry.
826
+
806
827
  ## Stored documents (`./stored`)
807
828
 
808
829
  For clients that read Firestore directly, such as the admin panel. Stored
@@ -825,7 +846,20 @@ invalidStoredPosSettings({ betrieb, geraet }); // paths the server
825
846
  ```
826
847
 
827
848
  `fromStoredReceipt`, `fromStoredCompany` and `fromStoredArticle` complete the
828
- set. A broken document throws a `KasseneckValidationError`. There is no
849
+ set. A broken document throws a `KasseneckValidationError`. `fromStoredReceipt`
850
+ also takes a receipt that already carries the English 1.0 values (for example
851
+ a fixture with `cancellationReason: 'customer_cancelled'`) and leaves them
852
+ as they are; the package keeps it that way.
853
+
854
+ The receipt layout and the print logo have an internal form as well: the
855
+ backend under `/api` and the 0.x browser cache use `regelwerk` for `ruleset`,
856
+ banner lines with `ton` (`belegart`, `warnung`) for `tone` (`receipt_type`,
857
+ `warning`), and a print logo with `stufe`, `pxBreite`, `pxHoehe`, `breite`,
858
+ `hoehe`, `zeilen`. `fromStoredLayout` reads either form and keeps the key
859
+ order (`null` for anything short of a whole layout: no or empty `lines`, a
860
+ line that is not an object, no `paperSize`; `receiptLayoutFromResult` then
861
+ rebuilds it), `toStoredLayout` writes the internal one, and
862
+ `fromStoredPrintLogo`/`toStoredPrintLogo` do the same for a `PrintLogo`. There is no
829
863
  reader for stored invoices: the server computes the invoice view, so read it
830
864
  with `getInvoice` and `listInvoices`.
831
865
 
@@ -1390,6 +1424,7 @@ in machine form what both sides agreed on, among them:
1390
1424
  | `invoice-api.schema.json` | JSON Schema of the invoice API | `npm run fixtures:rechnung` |
1391
1425
  | `pos-message-cases.json` | error cases and the message each one must show in both registers | by hand |
1392
1426
  | `receipt-due-generated.json` | 1206 amounts due computed by the backend's own code, the reference for `receiptDueCents` | `node scripts/v3-zahlbetrag-generieren.mjs` |
1427
+ | `receipt-due-errors.json` | input the amount due cannot be computed from, with the `reason` of the `ReceiptDueError` each one throws | by hand |
1393
1428
  | `v3/` | the backend's `/v3` contract (vocabulary, response cases, stored cases, amounts due), copied byte for byte | `node scripts/v3-vertrag-holen.mjs` |
1394
1429
  | `renames-1.0.json` | everything in these files that changed from 0.x to 1.0: paths (`files`), text catalogue keys (`texts`), placeholders, structural keys (`structure`), machine values (`values`), shape changes (`shapes`); rendered texts are unchanged | `npm run fixtures:umbenennung` |
1395
1430
 
@@ -1397,9 +1432,10 @@ They are generated and never edited by hand. CI regenerates the register
1397
1432
  settings and `surface.json` and fails if they differ from the committed
1398
1433
  files; the test suite checks the others against the code.
1399
1434
 
1400
- `surface.json`, `hobex-hps-codes.json`, `pos-texts.json` and
1401
- `invoice-texts.json` carry the package version. **After every `npm version`,
1402
- regenerate them and commit them along**, otherwise the tests fail.
1435
+ `surface.json`, `hobex-hps-codes.json`, `pos-texts.json`,
1436
+ `invoice-texts.json` and `invoice-api.schema.json` carry the package version.
1437
+ **After every `npm version`, regenerate them and commit them along**,
1438
+ otherwise the tests fail (`test/versionen.test.ts`).
1403
1439
 
1404
1440
  ### And the other direction
1405
1441
 
@@ -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, type Voucher, type VoucherPayload, toVoucherPayload, fromVoucherPayload, voucherIsValid, type Receipt, type RegistrationInfo, type ReceiptPayload, type ReceiptPayloadRead, toReceiptPayload, fromReceiptPayload, receiptSubSumCents, receiptSumCents, type CancellationOf, type CancellationItem, type Cancellation, type CancellationReason, type CancellationErrorCode, type CancellationStatus, CANCELLATION_REASONS, CANCELLATION_STATUSES, CANCELLATION_ERROR_CODES, CANCELLATION_RESERVATION_MS, isCancellationReason, 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, type ReceiptErrorCode, type ReceiptFieldError, RECEIPT_ERROR_CODES, isReceiptErrorCode, receiptErrorCode, isReceiptError, receiptFieldErrors, 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, 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 KasseneckTransport, type KasseneckBinaryTransport, ALL_CALLS, type ApiCall, PUBLIC_CALLS, POS_CALLS, type PublicCall, type PosCall, createTransport, createBinaryTransport, 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, downloadDailyReport, downloadMonthlyReport, getReportV2, type ReportV2, type ReportV2Metadata, type ReportV2Options, 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, receiptDueCents, receiptDueBreakdown, } 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, } 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, } from './pos/index.js';
package/dist/cjs/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.fromReceiptCompanyPayload = exports.remainingQuantities = exports.receiptFieldErrors = exports.isReceiptError = exports.receiptErrorCode = exports.isReceiptErrorCode = exports.RECEIPT_ERROR_CODES = exports.receiptEmailFieldErrors = exports.isReceiptEmailError = exports.receiptEmailErrorCode = exports.isReceiptEmailErrorCode = exports.RECEIPT_EMAIL_VIAS = exports.RECEIPT_EMAIL_SEND_ERROR_CODES = exports.RECEIPT_EMAIL_ERROR_CODES = exports.paymentFieldErrors = exports.isPaymentError = exports.paymentErrorCode = exports.isPaymentErrorCode = exports.PAYMENT_ERROR_CODES = exports.toReceiptPaymentPayload = exports.fromReceiptPaymentPayload = exports.cancellationFieldErrors = exports.isCancellationError = exports.cancellationErrorCode = exports.isCancellationErrorCode = exports.isCancellationReason = exports.CANCELLATION_RESERVATION_MS = exports.CANCELLATION_ERROR_CODES = exports.CANCELLATION_STATUSES = 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
4
  exports.listMyReceipts = exports.getFirstReceiptDate = exports.generateFullReceiptId = exports.getReceiptWithCompany = exports.getReceipt = exports.zeroReceipt = exports.cancelReceipt = exports.receiptLayoutFromResult = exports.cardRefundReference = exports.paymentsExpectedCents = exports.sellReceiptWithCompany = exports.sellReceipt = exports.isOutcomeUnknown = exports.isKasseneckValidationError = exports.isKasseneckNetworkError = exports.isKasseneckHttpError = exports.isKasseneckAuthError = exports.isKasseneckApiError = exports.CLIENT_ERROR_CODES = exports.KasseneckValidationError = exports.KasseneckNetworkError = exports.KasseneckHttpError = exports.KasseneckAuthError = exports.KasseneckApiError = exports.createBinaryTransport = exports.createTransport = exports.POS_CALLS = exports.PUBLIC_CALLS = exports.ALL_CALLS = exports.DEFAULT_TIMEOUT_MS = exports.POS_BASE_URL = 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.isZeroKind = exports.ZERO_KINDS = exports.fromReceiptSummaryPayload = exports.fromCashregisterPayload = exports.receiptCompanyTaxInfo = void 0;
5
- exports.receiptsScopeOf = exports.cancelScopeOf = exports.distributeDiscount = exports.setMyRegisterDeviceSettings = exports.setMyPosSettings = exports.getPosSettings = exports.listMyArticles = exports.listMyArticleGroups = exports.fromPosArticlePayload = exports.fromArticleGroupPayload = exports.mergePosSettings = exports.POS_DEVICE_DEFAULTS = exports.POS_BUSINESS_DEFAULTS = exports.toViennaWallClock = exports.parseServerTimeStamp = exports.receiptDueBreakdown = exports.receiptDueCents = exports.createKasseneckApi = exports.getSignatureStatus = exports.getCashboxStatus = exports.getReportV2 = exports.downloadMonthlyReport = exports.downloadDailyReport = exports.sendReceiptEmail = exports.checkVoucherCombinationError = exports.listMyCashregisters = void 0;
5
+ exports.receiptsScopeOf = exports.cancelScopeOf = exports.distributeDiscount = exports.setMyRegisterDeviceSettings = exports.setMyPosSettings = exports.getPosSettings = exports.listMyArticles = exports.listMyArticleGroups = exports.fromPosArticlePayload = exports.fromArticleGroupPayload = exports.mergePosSettings = exports.POS_DEVICE_DEFAULTS = exports.POS_BUSINESS_DEFAULTS = exports.toViennaWallClock = exports.parseServerTimeStamp = exports.isReceiptDueError = exports.RECEIPT_DUE_ERROR_REASONS = exports.ReceiptDueError = exports.receiptDueBreakdown = exports.receiptDueCents = exports.createKasseneckApi = exports.getSignatureStatus = exports.getCashboxStatus = exports.getReportV2 = exports.downloadMonthlyReport = exports.downloadDailyReport = exports.sendReceiptEmail = exports.checkVoucherCombinationError = exports.listMyCashregisters = void 0;
6
6
  var index_js_1 = require("./enums/index.js");
7
7
  Object.defineProperty(exports, "ReceiptType", { enumerable: true, get: function () { return index_js_1.ReceiptType; } });
8
8
  Object.defineProperty(exports, "VatRate", { enumerable: true, get: function () { return index_js_1.VatRate; } });
@@ -118,6 +118,9 @@ Object.defineProperty(exports, "createKasseneckApi", { enumerable: true, get: fu
118
118
  var due_js_1 = require("./receipt/due.js");
119
119
  Object.defineProperty(exports, "receiptDueCents", { enumerable: true, get: function () { return due_js_1.receiptDueCents; } });
120
120
  Object.defineProperty(exports, "receiptDueBreakdown", { enumerable: true, get: function () { return due_js_1.receiptDueBreakdown; } });
121
+ Object.defineProperty(exports, "ReceiptDueError", { enumerable: true, get: function () { return due_js_1.ReceiptDueError; } });
122
+ Object.defineProperty(exports, "RECEIPT_DUE_ERROR_REASONS", { enumerable: true, get: function () { return due_js_1.RECEIPT_DUE_ERROR_REASONS; } });
123
+ Object.defineProperty(exports, "isReceiptDueError", { enumerable: true, get: function () { return due_js_1.isReceiptDueError; } });
121
124
  var vienna_time_js_1 = require("./vienna-time.js");
122
125
  Object.defineProperty(exports, "parseServerTimeStamp", { enumerable: true, get: function () { return vienna_time_js_1.parseServerTimeStamp; } });
123
126
  Object.defineProperty(exports, "toViennaWallClock", { enumerable: true, get: function () { return vienna_time_js_1.toViennaWallClock; } });
@@ -12,4 +12,4 @@ export { distributeDiscount } from '../receipt/discount.js';
12
12
  export { cancelScopeOf, receiptsScopeOf, type RegisterScope, type RegisterUserPerms } from '../register/pairing.js';
13
13
  export { type NetworkPrinter, type PrintJob, type PrintJobStatus, PRINT_JOB_STATUSES, type PrintJobSource, PRINT_JOB_SOURCES, isPrintJobFinished, type CreatePrintJobOptions, listMyPrinters, createPrintJob, getPrintJob, } from './drucker.js';
14
14
  export { listMyTipRecipients } from './trinkgeld.js';
15
- export { MESSAGES, ERROR_RULES, messageText, messageAppliesTo, RECEIPT_EMAIL_ERROR_MESSAGES, receiptEmailErrorMessage, CANCELLATION_PAYMENT_ERROR_MESSAGES, cancellationPaymentErrorMessage, LABELS, labelText, type TextEntry, type MessageKey, type ErrorKind, type Surface, type ReceiptEmailMessageCode, type CancellationPaymentMessageCode, type LabelKey, } from './texte.js';
15
+ export { MESSAGES, ERROR_RULES, ERROR_CODE_RULES, ERROR_OUTCOME_RULES, CALLS_WITH_EFFECT, findErrorRule, messageOutcome, messageText, messageAppliesTo, RECEIPT_EMAIL_ERROR_MESSAGES, receiptEmailErrorMessage, CANCELLATION_PAYMENT_ERROR_MESSAGES, cancellationPaymentErrorMessage, LABELS, labelText, type TextEntry, type MessageKey, type ErrorKind, type ErrorRule, type ErrorRuleDetail, type Surface, type ReceiptEmailMessageCode, type CancellationPaymentMessageCode, type LabelKey, } from './texte.js';
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getPosSettings = exports.allowedQuantity = exports.quantityDefaults = exports.quantityRuleForUnit = exports.QUANTITY_RULES = exports.listMyArticles = exports.listMyArticleGroups = exports.fromPosArticlePayload = exports.fromArticleGroupPayload = exports.DONE_SCREEN_SECONDS = exports.WATERMARK_SCALE = exports.LOGO_SCALE = exports.WATERMARK_STRENGTH = exports.WATERMARK_SIDE = exports.DISCOUNT = exports.AUTO_LOGOUT_MINUTES = exports.TILE_STYLE = exports.SETTINGS_FONT_SIZE = exports.LOGO_SIZE = exports.QR_MODE = exports.DRAWER_AUTO_OPEN = exports.CUT = exports.CODE_PAGE = exports.PAPER_SIZE = exports.TERMINAL_TYPE = exports.TERMINAL_VIA = exports.PRINTER_TYPE = exports.TILE_HEIGHT = exports.CATEGORY_POSITION = exports.LAYOUT = exports.RECEIPT_OUTPUT = exports.CARD_PROVIDER = exports.CHECKOUT_MODE = exports.TIP_MODE = exports.QUANTITY = exports.WATERMARK = exports.FONT_SIZE = exports.THEME = exports.posShortcutConflict = exports.POS_SHORTCUT_SHARED_PAIRS = exports.posSettingsChanges = exports.unknownPosSettingValues = exports.sanitizePosSettings = exports.mergePosSettings = exports.POS_DEVICE_VALUES = exports.POS_BUSINESS_VALUES = exports.POS_DEVICE_DEFAULTS = exports.POS_BUSINESS_DEFAULTS = exports.POS_SHORTCUT_DEFAULTS = exports.POS_SHORTCUT_ACTIONS = void 0;
4
- exports.labelText = exports.LABELS = exports.cancellationPaymentErrorMessage = exports.CANCELLATION_PAYMENT_ERROR_MESSAGES = exports.receiptEmailErrorMessage = exports.RECEIPT_EMAIL_ERROR_MESSAGES = exports.messageAppliesTo = exports.messageText = exports.ERROR_RULES = exports.MESSAGES = exports.listMyTipRecipients = exports.getPrintJob = exports.createPrintJob = exports.listMyPrinters = exports.isPrintJobFinished = exports.PRINT_JOB_SOURCES = exports.PRINT_JOB_STATUSES = exports.receiptsScopeOf = exports.cancelScopeOf = exports.distributeDiscount = exports.posFieldErrors = exports.isPosError = exports.posErrorCode = exports.isPosErrorCode = exports.POS_ERROR_CODES = exports.posSettingsFromWire = exports.setMyPosLogo = exports.setMyRegisterDeviceSettings = exports.setMyPosSettings = void 0;
4
+ exports.labelText = exports.LABELS = exports.cancellationPaymentErrorMessage = exports.CANCELLATION_PAYMENT_ERROR_MESSAGES = exports.receiptEmailErrorMessage = exports.RECEIPT_EMAIL_ERROR_MESSAGES = exports.messageAppliesTo = exports.messageText = exports.messageOutcome = exports.findErrorRule = exports.CALLS_WITH_EFFECT = exports.ERROR_OUTCOME_RULES = exports.ERROR_CODE_RULES = exports.ERROR_RULES = exports.MESSAGES = exports.listMyTipRecipients = exports.getPrintJob = exports.createPrintJob = exports.listMyPrinters = exports.isPrintJobFinished = exports.PRINT_JOB_SOURCES = exports.PRINT_JOB_STATUSES = exports.receiptsScopeOf = exports.cancelScopeOf = exports.distributeDiscount = exports.posFieldErrors = exports.isPosError = exports.posErrorCode = exports.isPosErrorCode = exports.POS_ERROR_CODES = exports.posSettingsFromWire = exports.setMyPosLogo = exports.setMyRegisterDeviceSettings = exports.setMyPosSettings = void 0;
5
5
  /**
6
6
  * Kachel-Kasse: Einstellungen, Artikelgruppen/Artikel fuer Kacheln,
7
7
  * Rabattverteilung, Drucker, Trinkgeld-Empfaenger. Die Aufrufe heissen 1:1 wie
@@ -92,6 +92,11 @@ Object.defineProperty(exports, "listMyTipRecipients", { enumerable: true, get: f
92
92
  var texte_js_1 = require("./texte.js");
93
93
  Object.defineProperty(exports, "MESSAGES", { enumerable: true, get: function () { return texte_js_1.MESSAGES; } });
94
94
  Object.defineProperty(exports, "ERROR_RULES", { enumerable: true, get: function () { return texte_js_1.ERROR_RULES; } });
95
+ Object.defineProperty(exports, "ERROR_CODE_RULES", { enumerable: true, get: function () { return texte_js_1.ERROR_CODE_RULES; } });
96
+ Object.defineProperty(exports, "ERROR_OUTCOME_RULES", { enumerable: true, get: function () { return texte_js_1.ERROR_OUTCOME_RULES; } });
97
+ Object.defineProperty(exports, "CALLS_WITH_EFFECT", { enumerable: true, get: function () { return texte_js_1.CALLS_WITH_EFFECT; } });
98
+ Object.defineProperty(exports, "findErrorRule", { enumerable: true, get: function () { return texte_js_1.findErrorRule; } });
99
+ Object.defineProperty(exports, "messageOutcome", { enumerable: true, get: function () { return texte_js_1.messageOutcome; } });
95
100
  Object.defineProperty(exports, "messageText", { enumerable: true, get: function () { return texte_js_1.messageText; } });
96
101
  Object.defineProperty(exports, "messageAppliesTo", { enumerable: true, get: function () { return texte_js_1.messageAppliesTo; } });
97
102
  Object.defineProperty(exports, "RECEIPT_EMAIL_ERROR_MESSAGES", { enumerable: true, get: function () { return texte_js_1.RECEIPT_EMAIL_ERROR_MESSAGES; } });
@@ -20,6 +20,7 @@
20
20
  * `only`), die Platzhalter und die Arten der Fehlerregeln (Abschnitte
21
21
  * `placeholders` und `structure`).
22
22
  */
23
+ import { type ErrorOutcome } from '../client/errors.js';
23
24
  export type Surface = 'web' | 'app';
24
25
  export interface TextEntry {
25
26
  readonly text: string;
@@ -35,10 +36,19 @@ declare const MELDUNGEN_ROH: {
35
36
  readonly 'network.timeout': {
36
37
  readonly text: "Der Server antwortet nicht. Bitte die Internetverbindung prüfen und erneut versuchen.";
37
38
  };
39
+ readonly 'network.outcome_unknown': {
40
+ readonly text: "Der Server hat nicht geantwortet, der Vorgang kann trotzdem gebucht sein. Bitte vor einem neuen Versuch prüfen, ob der letzte Vorgang schon gebucht ist.";
41
+ };
38
42
  readonly 'server.unexpected': {
39
43
  readonly text: "Der Server hat unerwartet geantwortet (HTTP {status}). Bitte den Support verständigen.";
40
44
  readonly placeholders: readonly ["status"];
41
45
  };
46
+ readonly 'server.connection_disturbed': {
47
+ readonly text: "Die Verbindung zum Kassenserver ist gestört. Bitte kurz warten und erneut versuchen.";
48
+ };
49
+ readonly 'server.response_unreadable': {
50
+ readonly text: "Die Antwort des Kassenservers war nicht lesbar. Bitte die Kasse neu öffnen und vor einem neuen Versuch prüfen, ob der letzte Vorgang schon gebucht ist.";
51
+ };
42
52
  readonly 'pairing.code_missing': {
43
53
  readonly text: "Bitte den Kopplungs-Code eingeben.";
44
54
  };
@@ -543,8 +553,8 @@ export type MessageKey = keyof typeof MELDUNGEN_ROH;
543
553
  export declare const MESSAGES: Record<MessageKey, TextEntry>;
544
554
  /**
545
555
  * In welcher Reihenfolge ein Fehler eingeordnet wird, auf beiden Seiten
546
- * dieselbe. Jede Regel nennt ihre Art (`kind`) und entweder ein Verhalten
547
- * (`behavior`) oder den Schluessel des Satzes (`key`). Die Arten:
556
+ * dieselbe. Genau eine Regel je Art (`kind`), mit einem Verhalten
557
+ * (`behavior`) oder dem Schluessel des Satzes (`key`). Die Arten:
548
558
  * api - HTTP 200, `status:'error'`: der Satz des Backends, woertlich
549
559
  * (`server_text`)
550
560
  * plain_text - schon fuer den Bildschirm geschrieben: sein eigener Text
@@ -557,6 +567,11 @@ export declare const MESSAGES: Record<MessageKey, TextEntry>;
557
567
  * (`fallback`)
558
568
  * Unter 0.x hiessen sie `klartext`, `zeitablauf`, `netz`, `unerwartet`,
559
569
  * `sonst` (Tabelle in `fixtures/renames-1.0.json`).
570
+ *
571
+ * Seit 1.0.0-rc.5 verfeinern [ERROR_CODE_RULES] und [ERROR_OUTCOME_RULES]
572
+ * diese Liste; sie stehen bewusst getrennt, damit ein Leser, der nur nach
573
+ * `kind` sucht, weiter genau den Satz von rc.4 zeigt. [findErrorRule] wendet
574
+ * alle drei in der richtigen Reihenfolge an.
560
575
  */
561
576
  export declare const ERROR_RULES: readonly [{
562
577
  readonly kind: "api";
@@ -578,6 +593,73 @@ export declare const ERROR_RULES: readonly [{
578
593
  readonly behavior: "fallback";
579
594
  }];
580
595
  export type ErrorKind = (typeof ERROR_RULES)[number]['kind'];
596
+ /**
597
+ * Regeln je Code (`error.code`), vor der Regel der Art: sie ersetzen den
598
+ * technischen Satz des Pakets bzw. des Rands durch einen Menschentext. Der
599
+ * technische Satz bleibt in `error.message` fuers Protokoll.
600
+ * - `dialect_mismatch`, `response_translation_failed`, `response_unreadable`:
601
+ * Ausgang unklar, der Vorgang kann gebucht sein; der Satz raet nie zum
602
+ * Wiederholen, sondern zum Nachsehen.
603
+ * - `route_missing`, `not_found`, `internal_translation_error`: am Server
604
+ * geschah nichts, ein neuer Versuch ist sicher (auch auf den Geldwegen:
605
+ * alle drei stehen in `PAYMENT_CALL_REJECTED_CODES`).
606
+ */
607
+ export declare const ERROR_CODE_RULES: readonly [{
608
+ readonly kind: "api";
609
+ readonly codes: readonly ["dialect_mismatch", "response_translation_failed", "response_unreadable"];
610
+ readonly key: "server.response_unreadable";
611
+ }, {
612
+ readonly kind: "api";
613
+ readonly codes: readonly ["route_missing", "not_found", "internal_translation_error"];
614
+ readonly key: "server.connection_disturbed";
615
+ }];
616
+ /**
617
+ * Regeln je Ausgang, nach den Code-Regeln und vor der Regel der Art: eine
618
+ * Frist oder ein Netzfehler auf einem Aufruf mit Wirkung (Ausgang unklar,
619
+ * siehe [messageOutcome]) raet nie zum Wiederholen. Ein Netzfehler auf einem
620
+ * Aufruf ohne Wirkung behaelt den Satz von rc.4 („erneut versuchen“).
621
+ */
622
+ export declare const ERROR_OUTCOME_RULES: readonly [{
623
+ readonly kind: "timeout";
624
+ readonly outcome: "unknown";
625
+ readonly key: "network.outcome_unknown";
626
+ }, {
627
+ readonly kind: "network";
628
+ readonly outcome: "unknown";
629
+ readonly key: "network.outcome_unknown";
630
+ }];
631
+ export type ErrorRule = (typeof ERROR_RULES)[number] | (typeof ERROR_CODE_RULES)[number] | (typeof ERROR_OUTCOME_RULES)[number];
632
+ /** Was ueber einen Fehler bekannt ist, ausser seiner Art. */
633
+ export interface ErrorRuleDetail {
634
+ /** `error.code` eines `KasseneckApiError`. */
635
+ code?: string | null;
636
+ /** Ausgang fuer den Satz, siehe [messageOutcome]. */
637
+ outcome?: ErrorOutcome | null;
638
+ }
639
+ /**
640
+ * Die Regel fuer einen Fehler: zuerst eine Code-Regel mit passender Art und
641
+ * passendem `code`, dann eine Ausgangs-Regel mit passender Art und
642
+ * passendem `outcome`, sonst die Regel der Art aus [ERROR_RULES]. Beide
643
+ * Kassen ordnen so ein.
644
+ */
645
+ export declare function findErrorRule(kind: ErrorKind, detail?: ErrorRuleDetail): ErrorRule;
646
+ /**
647
+ * Kassen-Aufrufe mit Wirkung, die sich nach einem Netzfehler nicht gefahrlos
648
+ * wiederholen lassen: die sechs, deren Ausgang das Paket selbst als unklar
649
+ * fuehrt (Beleg, auch Null- und Startbeleg ueber `createReceipt`, Storno,
650
+ * FinanzOnline, die drei Geldwege), dazu der Druckjob (zweiter Bon) und die
651
+ * Belegmail (zweite Mail). Ob die Anfrage vor dem Abriss schon draussen war,
652
+ * sieht das Paket nicht (fetch wirft in beiden Faellen gleich); darum gilt
653
+ * hier immer der vorsichtige Satz.
654
+ */
655
+ export declare const CALLS_WITH_EFFECT: readonly ["createReceipt", "cancelReceipt", "financeWebService", "hobexPayApi", "hobexRefundApi", "stripeCaptureIntent", "createPrintJob", "sendReceiptEmail"];
656
+ /**
657
+ * Der Ausgang, nach dem der Satz gewaehlt wird: `'unknown'`, wenn das Paket
658
+ * ihn als unklar fuehrt ([isOutcomeUnknown]) oder wenn eine Frist bzw. ein
659
+ * Netzfehler einen Aufruf aus [CALLS_WITH_EFFECT] traf; sonst der Ausgang
660
+ * des Fehlers (`'rejected'`) bzw. `undefined` fuer fremde Fehler.
661
+ */
662
+ export declare function messageOutcome(error: unknown): ErrorOutcome | undefined;
581
663
  /**
582
664
  * Beleg per E-Mail senden: welcher `code` des Backends welchen Satz bekommt.
583
665
  *
@@ -662,6 +744,13 @@ declare const BESCHRIFTUNGEN_ROH: {
662
744
  readonly 'pairing.pair_again': {
663
745
  readonly text: "Neu koppeln";
664
746
  };
747
+ readonly 'register.device_unnamed': {
748
+ readonly text: "Kasse";
749
+ };
750
+ readonly 'login.locked_seconds': {
751
+ readonly text: "Noch {seconds} s gesperrt";
752
+ readonly placeholders: readonly ["seconds"];
753
+ };
665
754
  readonly 'logout.question': {
666
755
  readonly text: "Wirklich abmelden?";
667
756
  };
@@ -731,6 +820,10 @@ declare const BESCHRIFTUNGEN_ROH: {
731
820
  readonly 'split.remaining': {
732
821
  readonly text: "Rest";
733
822
  };
823
+ readonly 'split.remaining_with_rounding': {
824
+ readonly text: "Rest inkl. Rundung {amount} ({cents} ct)";
825
+ readonly placeholders: readonly ["amount", "cents"];
826
+ };
734
827
  readonly 'split.divide': {
735
828
  readonly text: "÷ {n}";
736
829
  readonly placeholders: readonly ["n"];
@@ -22,17 +22,32 @@
22
22
  * `placeholders` und `structure`).
23
23
  */
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
- exports.LABELS = exports.CANCELLATION_PAYMENT_ERROR_MESSAGES = exports.RECEIPT_EMAIL_ERROR_MESSAGES = exports.ERROR_RULES = exports.MESSAGES = void 0;
25
+ exports.LABELS = exports.CANCELLATION_PAYMENT_ERROR_MESSAGES = exports.RECEIPT_EMAIL_ERROR_MESSAGES = exports.CALLS_WITH_EFFECT = exports.ERROR_OUTCOME_RULES = exports.ERROR_CODE_RULES = exports.ERROR_RULES = exports.MESSAGES = void 0;
26
+ exports.findErrorRule = findErrorRule;
27
+ exports.messageOutcome = messageOutcome;
26
28
  exports.receiptEmailErrorMessage = receiptEmailErrorMessage;
27
29
  exports.cancellationPaymentErrorMessage = cancellationPaymentErrorMessage;
28
30
  exports.labelText = labelText;
29
31
  exports.messageText = messageText;
30
32
  exports.messageAppliesTo = messageAppliesTo;
33
+ const errors_js_1 = require("../client/errors.js");
31
34
  const MELDUNGEN_ROH = {
32
35
  // --- Transport -----------------------------------------------------------
33
36
  'network.no_connection': { text: 'Keine Verbindung zum Server. Bitte die Internetverbindung prüfen und erneut versuchen.' },
34
37
  'network.timeout': { text: 'Der Server antwortet nicht. Bitte die Internetverbindung prüfen und erneut versuchen.' },
38
+ // Frist oder Netzfehler auf einem Aufruf mit Wirkung (ERROR_OUTCOME_RULES):
39
+ // der Vorgang kann trotzdem gebucht sein, darum nie „erneut versuchen“.
40
+ 'network.outcome_unknown': { text: 'Der Server hat nicht geantwortet, der Vorgang kann trotzdem gebucht sein. Bitte vor einem neuen Versuch prüfen, ob der letzte Vorgang schon gebucht ist.' },
35
41
  'server.unexpected': { text: 'Der Server hat unerwartet geantwortet (HTTP {status}). Bitte den Support verständigen.', placeholders: ['status'] },
42
+ // Rand-Codes, die ein Kassier nicht deuten kann (siehe ERROR_CODE_RULES): der
43
+ // technische Satz bleibt in `error.message`, auf den Schirm kommt dieser.
44
+ // Nur fuer Codes, bei denen der Server nichts ausgefuehrt hat: dann darf
45
+ // der Satz zum neuen Versuch raten.
46
+ 'server.connection_disturbed': { text: 'Die Verbindung zum Kassenserver ist gestört. Bitte kurz warten und erneut versuchen.' },
47
+ // Codes mit unklarem Ausgang (der Vorgang kann gebucht sein): nie zum
48
+ // Wiederholen raten, erst nachsehen. „Kasse neu öffnen“ passt fuer beide
49
+ // Seiten (Web: neu laden, App: neu starten).
50
+ 'server.response_unreadable': { text: 'Die Antwort des Kassenservers war nicht lesbar. Bitte die Kasse neu öffnen und vor einem neuen Versuch prüfen, ob der letzte Vorgang schon gebucht ist.' },
36
51
  // --- Kopplung ------------------------------------------------------------
37
52
  'pairing.code_missing': { text: 'Bitte den Kopplungs-Code eingeben.' },
38
53
  'pairing.failed': { text: 'Die Kopplung ist fehlgeschlagen. Bitte erneut versuchen.' },
@@ -271,8 +286,8 @@ const MELDUNGEN_ROH = {
271
286
  exports.MESSAGES = MELDUNGEN_ROH;
272
287
  /**
273
288
  * In welcher Reihenfolge ein Fehler eingeordnet wird, auf beiden Seiten
274
- * dieselbe. Jede Regel nennt ihre Art (`kind`) und entweder ein Verhalten
275
- * (`behavior`) oder den Schluessel des Satzes (`key`). Die Arten:
289
+ * dieselbe. Genau eine Regel je Art (`kind`), mit einem Verhalten
290
+ * (`behavior`) oder dem Schluessel des Satzes (`key`). Die Arten:
276
291
  * api - HTTP 200, `status:'error'`: der Satz des Backends, woertlich
277
292
  * (`server_text`)
278
293
  * plain_text - schon fuer den Bildschirm geschrieben: sein eigener Text
@@ -285,6 +300,11 @@ exports.MESSAGES = MELDUNGEN_ROH;
285
300
  * (`fallback`)
286
301
  * Unter 0.x hiessen sie `klartext`, `zeitablauf`, `netz`, `unerwartet`,
287
302
  * `sonst` (Tabelle in `fixtures/renames-1.0.json`).
303
+ *
304
+ * Seit 1.0.0-rc.5 verfeinern [ERROR_CODE_RULES] und [ERROR_OUTCOME_RULES]
305
+ * diese Liste; sie stehen bewusst getrennt, damit ein Leser, der nur nach
306
+ * `kind` sucht, weiter genau den Satz von rc.4 zeigt. [findErrorRule] wendet
307
+ * alle drei in der richtigen Reihenfolge an.
288
308
  */
289
309
  exports.ERROR_RULES = [
290
310
  { kind: 'api', behavior: 'server_text' },
@@ -294,6 +314,87 @@ exports.ERROR_RULES = [
294
314
  { kind: 'unexpected', key: 'server.unexpected' },
295
315
  { kind: 'other', behavior: 'fallback' },
296
316
  ];
317
+ /**
318
+ * Regeln je Code (`error.code`), vor der Regel der Art: sie ersetzen den
319
+ * technischen Satz des Pakets bzw. des Rands durch einen Menschentext. Der
320
+ * technische Satz bleibt in `error.message` fuers Protokoll.
321
+ * - `dialect_mismatch`, `response_translation_failed`, `response_unreadable`:
322
+ * Ausgang unklar, der Vorgang kann gebucht sein; der Satz raet nie zum
323
+ * Wiederholen, sondern zum Nachsehen.
324
+ * - `route_missing`, `not_found`, `internal_translation_error`: am Server
325
+ * geschah nichts, ein neuer Versuch ist sicher (auch auf den Geldwegen:
326
+ * alle drei stehen in `PAYMENT_CALL_REJECTED_CODES`).
327
+ */
328
+ exports.ERROR_CODE_RULES = [
329
+ { kind: 'api', codes: ['dialect_mismatch', 'response_translation_failed', 'response_unreadable'], key: 'server.response_unreadable' },
330
+ { kind: 'api', codes: ['route_missing', 'not_found', 'internal_translation_error'], key: 'server.connection_disturbed' },
331
+ ];
332
+ /**
333
+ * Regeln je Ausgang, nach den Code-Regeln und vor der Regel der Art: eine
334
+ * Frist oder ein Netzfehler auf einem Aufruf mit Wirkung (Ausgang unklar,
335
+ * siehe [messageOutcome]) raet nie zum Wiederholen. Ein Netzfehler auf einem
336
+ * Aufruf ohne Wirkung behaelt den Satz von rc.4 („erneut versuchen“).
337
+ */
338
+ exports.ERROR_OUTCOME_RULES = [
339
+ { kind: 'timeout', outcome: 'unknown', key: 'network.outcome_unknown' },
340
+ { kind: 'network', outcome: 'unknown', key: 'network.outcome_unknown' },
341
+ ];
342
+ /**
343
+ * Die Regel fuer einen Fehler: zuerst eine Code-Regel mit passender Art und
344
+ * passendem `code`, dann eine Ausgangs-Regel mit passender Art und
345
+ * passendem `outcome`, sonst die Regel der Art aus [ERROR_RULES]. Beide
346
+ * Kassen ordnen so ein.
347
+ */
348
+ function findErrorRule(kind, detail = {}) {
349
+ const { code, outcome } = detail;
350
+ if (code !== undefined && code !== null) {
351
+ const treffer = exports.ERROR_CODE_RULES.find((r) => r.kind === kind && r.codes.includes(code));
352
+ if (treffer)
353
+ return treffer;
354
+ }
355
+ if (outcome !== undefined && outcome !== null) {
356
+ const treffer = exports.ERROR_OUTCOME_RULES.find((r) => r.kind === kind && r.outcome === outcome);
357
+ if (treffer)
358
+ return treffer;
359
+ }
360
+ // Jede Art hat genau eine Regel; hierher kommt nur eine fremde Art nicht.
361
+ return exports.ERROR_RULES.find((r) => r.kind === kind) ?? exports.ERROR_RULES[exports.ERROR_RULES.length - 1];
362
+ }
363
+ /**
364
+ * Kassen-Aufrufe mit Wirkung, die sich nach einem Netzfehler nicht gefahrlos
365
+ * wiederholen lassen: die sechs, deren Ausgang das Paket selbst als unklar
366
+ * fuehrt (Beleg, auch Null- und Startbeleg ueber `createReceipt`, Storno,
367
+ * FinanzOnline, die drei Geldwege), dazu der Druckjob (zweiter Bon) und die
368
+ * Belegmail (zweite Mail). Ob die Anfrage vor dem Abriss schon draussen war,
369
+ * sieht das Paket nicht (fetch wirft in beiden Faellen gleich); darum gilt
370
+ * hier immer der vorsichtige Satz.
371
+ */
372
+ exports.CALLS_WITH_EFFECT = Object.freeze([
373
+ 'createReceipt',
374
+ 'cancelReceipt',
375
+ 'financeWebService',
376
+ 'hobexPayApi',
377
+ 'hobexRefundApi',
378
+ 'stripeCaptureIntent',
379
+ 'createPrintJob',
380
+ 'sendReceiptEmail',
381
+ ]);
382
+ const MIT_WIRKUNG = new Set(exports.CALLS_WITH_EFFECT);
383
+ /**
384
+ * Der Ausgang, nach dem der Satz gewaehlt wird: `'unknown'`, wenn das Paket
385
+ * ihn als unklar fuehrt ([isOutcomeUnknown]) oder wenn eine Frist bzw. ein
386
+ * Netzfehler einen Aufruf aus [CALLS_WITH_EFFECT] traf; sonst der Ausgang
387
+ * des Fehlers (`'rejected'`) bzw. `undefined` fuer fremde Fehler.
388
+ */
389
+ function messageOutcome(error) {
390
+ if ((0, errors_js_1.isOutcomeUnknown)(error))
391
+ return 'unknown';
392
+ if (error instanceof errors_js_1.KasseneckNetworkError)
393
+ return MIT_WIRKUNG.has(error.functionName.split('/')[0]) ? 'unknown' : error.outcome;
394
+ if (error instanceof errors_js_1.KasseneckApiError || error instanceof errors_js_1.KasseneckHttpError)
395
+ return error.outcome;
396
+ return undefined;
397
+ }
297
398
  /**
298
399
  * Beleg per E-Mail senden: welcher `code` des Backends welchen Satz bekommt.
299
400
  *
@@ -377,6 +478,10 @@ const BESCHRIFTUNGEN_ROH = {
377
478
  'message.dismiss_warning': { text: 'Verstanden – Warnung ausblenden: {message}', placeholders: ['message'] },
378
479
  // --- Kopplung und Abmelden -----------------------------------------------
379
480
  'pairing.pair_again': { text: 'Neu koppeln' },
481
+ // Ein Geraet ohne Namen (`deviceLabel: null` unter /v3) in der Auswahl.
482
+ 'register.device_unnamed': { text: 'Kasse' },
483
+ // Restzeit der PIN-Sperre unter dem Satz des Backends.
484
+ 'login.locked_seconds': { text: 'Noch {seconds} s gesperrt', placeholders: ['seconds'] },
380
485
  'logout.question': { text: 'Wirklich abmelden?' },
381
486
  'logout.keep_working': { text: 'Weiter arbeiten' },
382
487
  'device.unpair': { text: 'Gerät entkoppeln' },
@@ -405,6 +510,9 @@ const BESCHRIFTUNGEN_ROH = {
405
510
  'split.payment': { text: 'Zahlung {n}', placeholders: ['n'] },
406
511
  'split.amount': { text: 'Betrag' },
407
512
  'split.remaining': { text: 'Rest' },
513
+ // Letzte Runde bei Getrennt zahlen: der Rest samt Rundungscent;
514
+ // `{cents}` traegt das Vorzeichen (`+1`, `−1`).
515
+ 'split.remaining_with_rounding': { text: 'Rest inkl. Rundung {amount} ({cents} ct)', placeholders: ['amount', 'cents'] },
408
516
  'split.divide': { text: '÷ {n}', placeholders: ['n'] },
409
517
  'split.tip_basis': { text: '% von diesem Betrag' },
410
518
  'split.of_which_tip': { text: 'davon Trinkgeld {amount}', placeholders: ['amount'] },
@@ -33,6 +33,10 @@ import type { Voucher } from '../models/voucher.js';
33
33
  * rabattiert). Das weiss nur die Kasse; darum ist `tipRecipient` Pflicht,
34
34
  * sobald Trinkgeld ohne `recipients` vorkommt. Ohne angemeldeten Benutzer
35
35
  * (Geraete-Schluessel) gilt `'staff'`.
36
+ *
37
+ * **Nicht rechenbar:** jede unbrauchbare Eingabe wirft [ReceiptDueError]
38
+ * (`code: 'receipt_due_unavailable'`, `reason` im Einzelnen), nie still eine
39
+ * falsche Zahl. Dann ist nichts gesendet.
36
40
  */
37
41
  /** Wer das Trinkgeld ohne `recipients` bekommt (Kennzeichen des angemeldeten Kassen-Benutzers). */
38
42
  export type ReceiptDueTipRecipient = 'owner' | 'staff';
@@ -78,6 +82,35 @@ export interface ReceiptDueBreakdown {
78
82
  /** Toepfe je auf Cent gerundet; `null` bei Start- und Nullbeleg. */
79
83
  bucketsCents: ReceiptDueBuckets | null;
80
84
  }
85
+ /**
86
+ * Warum sich der Zahlbetrag nicht rechnen laesst. Fest wie ein Backend-Code;
87
+ * dieselbe Liste im Dart-Zwilling und in `fixtures/receipt-due-errors.json`.
88
+ */
89
+ export declare const RECEIPT_DUE_ERROR_REASONS: readonly ["unknown_receipt_type", "tip_not_allowed", "invalid_item", "invalid_voucher", "invalid_tip", "unknown_payment_method", "tip_conflict", "tip_recipient_missing", "tip_without_goods"];
90
+ export type ReceiptDueErrorReason = (typeof RECEIPT_DUE_ERROR_REASONS)[number];
91
+ /**
92
+ * Der Zahlbetrag laesst sich aus dieser Eingabe nicht rechnen, etwa
93
+ * Trinkgeld mit Betrag ohne Ware (`reason: 'tip_without_goods'`).
94
+ *
95
+ * Eindeutig **nicht gesendet**: die Rechnung laeuft ganz im Paket, vor jedem
96
+ * Aufruf. `outcome` ist darum immer `'rejected'`; nichts ist geschehen, keine
97
+ * Belegnummer verbraucht, keine Karte belastet. Die Kasse sagt das dem
98
+ * Kassier, bevor sie ein Terminal anspricht. Entscheiden am `code` bzw.
99
+ * `reason`, nie an `message` (die ist fuers Protokoll).
100
+ *
101
+ * Bis 1.0.0-rc.4 warf die Rechnung hier einen `RangeError` ohne Code.
102
+ */
103
+ export declare class ReceiptDueError extends Error {
104
+ readonly name = "ReceiptDueError";
105
+ /** Stabiler Code fuer jede Ursache. */
106
+ readonly code: "receipt_due_unavailable";
107
+ /** Die Ursache im Einzelnen. */
108
+ readonly reason: ReceiptDueErrorReason;
109
+ /** Immer `'rejected'`: es ging nichts hinaus. */
110
+ readonly outcome: "rejected";
111
+ constructor(reason: ReceiptDueErrorReason, message: string);
112
+ }
113
+ export declare function isReceiptDueError(error: unknown): error is ReceiptDueError;
81
114
  type BelegArt = ReceiptType | ReceiptTypeKey | string;
82
115
  /** Zahlbetrag in Cent, siehe Modulkommentar. */
83
116
  export declare function receiptDueCents(items: readonly ReceiptItem[], vouchers: readonly Voucher[], receiptType: BelegArt, options?: ReceiptDueOptions): number;