@kreiseck/kasseneck-api 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/CHANGELOG.md +124 -0
  2. package/README.md +181 -27
  3. package/dist/cjs/client/aufrufe.d.ts +58 -4
  4. package/dist/cjs/client/aufrufe.js +185 -1
  5. package/dist/cjs/client/errors.d.ts +32 -19
  6. package/dist/cjs/client/errors.js +28 -17
  7. package/dist/cjs/client/index.d.ts +2 -2
  8. package/dist/cjs/client/index.js +2 -1
  9. package/dist/cjs/client/transport.d.ts +27 -1
  10. package/dist/cjs/client/transport.js +25 -27
  11. package/dist/cjs/index.d.ts +1 -1
  12. package/dist/cjs/index.js +3 -2
  13. package/dist/cjs/inventory/api.d.ts +10 -2
  14. package/dist/cjs/inventory/api.js +8 -1
  15. package/dist/cjs/inventory/endpunkte.d.ts +2 -1
  16. package/dist/cjs/inventory/endpunkte.js +2 -1
  17. package/dist/cjs/inventory/index.d.ts +5 -3
  18. package/dist/cjs/inventory/index.js +15 -3
  19. package/dist/cjs/inventory/lesen.d.ts +7 -1
  20. package/dist/cjs/inventory/lesen.js +75 -0
  21. package/dist/cjs/inventory/schreiben.d.ts +26 -1
  22. package/dist/cjs/inventory/schreiben.js +24 -10
  23. package/dist/cjs/inventory/typen.d.ts +156 -2
  24. package/dist/cjs/inventory/varianten.d.ts +67 -0
  25. package/dist/cjs/inventory/varianten.js +150 -0
  26. package/dist/cjs/inventory/vertrag.d.ts +40 -5
  27. package/dist/cjs/inventory/vertrag.js +51 -3
  28. package/dist/cjs/inventory/webhook.d.ts +5 -2
  29. package/dist/cjs/inventory/webhook.js +8 -2
  30. package/dist/cjs/invoice/endpunkte.d.ts +6 -0
  31. package/dist/cjs/invoice/endpunkte.js +13 -2
  32. package/dist/cjs/pos/texte.d.ts +11 -6
  33. package/dist/cjs/pos/texte.js +11 -6
  34. package/dist/cjs/stored/vokabular.d.ts +1 -1
  35. package/dist/cjs/stored/vokabular.js +1 -1
  36. package/dist/cjs/version.d.ts +1 -1
  37. package/dist/cjs/version.js +1 -1
  38. package/dist/esm/client/aufrufe.d.ts +58 -4
  39. package/dist/esm/client/aufrufe.js +183 -0
  40. package/dist/esm/client/errors.d.ts +32 -19
  41. package/dist/esm/client/errors.js +28 -17
  42. package/dist/esm/client/index.d.ts +2 -2
  43. package/dist/esm/client/index.js +1 -1
  44. package/dist/esm/client/transport.d.ts +27 -1
  45. package/dist/esm/client/transport.js +26 -28
  46. package/dist/esm/index.d.ts +1 -1
  47. package/dist/esm/index.js +1 -1
  48. package/dist/esm/inventory/api.d.ts +10 -2
  49. package/dist/esm/inventory/api.js +8 -1
  50. package/dist/esm/inventory/endpunkte.d.ts +2 -1
  51. package/dist/esm/inventory/endpunkte.js +2 -1
  52. package/dist/esm/inventory/index.d.ts +5 -3
  53. package/dist/esm/inventory/index.js +4 -2
  54. package/dist/esm/inventory/lesen.d.ts +7 -1
  55. package/dist/esm/inventory/lesen.js +74 -0
  56. package/dist/esm/inventory/schreiben.d.ts +26 -1
  57. package/dist/esm/inventory/schreiben.js +19 -11
  58. package/dist/esm/inventory/typen.d.ts +156 -2
  59. package/dist/esm/inventory/varianten.d.ts +67 -0
  60. package/dist/esm/inventory/varianten.js +142 -0
  61. package/dist/esm/inventory/vertrag.d.ts +40 -5
  62. package/dist/esm/inventory/vertrag.js +50 -2
  63. package/dist/esm/inventory/webhook.d.ts +5 -2
  64. package/dist/esm/inventory/webhook.js +9 -3
  65. package/dist/esm/invoice/endpunkte.d.ts +6 -0
  66. package/dist/esm/invoice/endpunkte.js +13 -2
  67. package/dist/esm/pos/texte.d.ts +11 -6
  68. package/dist/esm/pos/texte.js +11 -6
  69. package/dist/esm/stored/vokabular.d.ts +1 -1
  70. package/dist/esm/stored/vokabular.js +1 -1
  71. package/dist/esm/version.d.ts +1 -1
  72. package/dist/esm/version.js +1 -1
  73. package/fixtures/hobex-hps-codes.json +1 -1
  74. package/fixtures/invoice-api.schema.json +1 -1
  75. package/fixtures/invoice-texts.json +1 -1
  76. package/fixtures/pos-texts.json +1 -1
  77. package/fixtures/surface.json +85 -6
  78. package/fixtures/v3/antworten/belege.json +1 -1
  79. package/fixtures/v3/antworten/belegmail.json +1 -1
  80. package/fixtures/v3/antworten/kasse-belege.json +1 -1
  81. package/fixtures/v3/antworten/kasse.json +2 -2
  82. package/fixtures/v3/antworten/lager.json +2396 -4
  83. package/fixtures/v3/antworten/rechnungen.json +1 -1
  84. package/fixtures/v3/antworten/storno.json +1 -1
  85. package/fixtures/v3/stored/belege.json +1 -1
  86. package/fixtures/v3/stored/kasse.json +2 -2
  87. package/fixtures/v3/stored/rechnungen.json +1 -1
  88. package/fixtures/v3/v3-vokabular.json +151 -6
  89. package/fixtures/v3/zahlbetrag-faelle.json +1 -1
  90. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,130 @@ Was vor 0.7.0 geschah, steht in der Commit-Historie (`git log`); ab hier wird
4
4
  es hier geführt. Ein Eintrag nennt die Änderung **und ihren Grund** —
5
5
  nur der Grund überlebt den nächsten Umbau.
6
6
 
7
+ ## 1.6.0
8
+
9
+ Inventory API, variants: `./inventory` gains the five endpoints of backend
10
+ stage 5c. Reason: a shop sells the same apron in three sizes and two colours.
11
+ Until now it had to create six unrelated articles and keep their names, prices
12
+ and codes in step by hand; nothing told the shop or the register that they
13
+ belong together, and nothing stopped a second "M red". A variant group holds
14
+ what they share and guarantees that each combination exists once.
15
+
16
+ Additive at runtime; existing calls send the same bytes as in 1.5.1. Two
17
+ things change for code that relies on the old shape: the unions
18
+ `InventoryWebhookEvent`/`InventoryWebhookEventType`, `InventoryErrorCode`,
19
+ `InventoryEndpoint` and `ApiCall` gain members, so an exhaustive `switch` with a
20
+ `never` check (or a `Record<…>` over them) needs the new cases; and
21
+ `parseInventoryWebhookEvent` now returns an event for `variant_group.*` where
22
+ 1.5.x returned `null`.
23
+
24
+ - **Variant groups**: `createVariantGroup` (with `createMatrix: true` every
25
+ combination of the values, at most 100, or the listed `variants[]`, at most
26
+ 100; never both), `updateVariantGroup` (name, defaults as a partial update,
27
+ `addAttributeValues`, or `active: false` to deactivate the group and all its
28
+ variants for good), `getVariantGroup`, `listVariantGroups` and
29
+ `iterateVariantGroups` (sorted by `updatedAt` ascending, filters `active`
30
+ and `updatedSince`). `VariantGroup` carries `attributes` (1 to 3, each with
31
+ `key`, `label` and 1 to 30 `values`, in group order), `defaults`
32
+ (`unitPriceCents`, `vatRate`, `unit`, `groupId`, `stockTracked`), `active`,
33
+ `variants[]` (`articleId` and `variantAttributes` of every active variant)
34
+ and the times. The group answers carry no articles: read them with
35
+ `listArticles({ variantGroupId })`.
36
+ - **Variants are articles**: `addVariant` creates one more variant and answers
37
+ with the article as `createArticle` does. Each variant takes the article
38
+ fields of `createArticle`; fields it does not name are filled from the group
39
+ defaults at creation, the name defaults to "<group> <value1> <value2>" in
40
+ attribute order. `Article.variantGroupId` and `Article.variantAttributes`
41
+ are set only on variants; the keys of `variantAttributes` come sorted by code
42
+ point, the attribute order is in `VariantGroup.attributes`.
43
+ - **Idempotency**: the three writes take `idempotencyKey` (required in the
44
+ type and refused before sending without a valid one) and are in
45
+ `UNKNOWN_OUTCOME_CALLS`: after `outcome: 'unknown'` resend with the same key.
46
+ `getVariantGroup` and `listVariantGroups` are reads.
47
+ - **Checked before sending**, nothing else: the key, `variantGroupId`,
48
+ `createMatrix: true` together with `variants`, `variants` that is no list or
49
+ holds an entry without a `variantAttributes` object, integer prices,
50
+ quantities and purchase prices (also in `defaults` and in every variant), an
51
+ update without a change, `active` other than `false` or not alone,
52
+ `addAttributeValues` that is no object. The limits (3 attributes, 30 values,
53
+ matrix 100, 250 active variants per group) are left to the server, so it can
54
+ raise them; they are exported as `VARIANT_ATTRIBUTES_MAX`,
55
+ `VARIANT_VALUES_MAX`, `VARIANT_MATRIX_MAX`, `VARIANT_GROUP_ACTIVE_MAX`.
56
+ - **Events**: `variant_group.created` and `variant_group.updated` in
57
+ `INVENTORY_WEBHOOK_EVENTS` and `parseInventoryWebhookEvent`, carrying the
58
+ group as `getVariantGroup` returns it. `updated` fires only on a visible
59
+ change (new value, new or deactivated variant, name, defaults, deactivating
60
+ the group). Two deliveries may overtake each other: keep the one with the
61
+ later `updatedAt`.
62
+ - **Errors**: `INVENTORY_ERROR_CODES` appends `variant_group_not_found`,
63
+ `variant_already_exists` (with `field`, and `articleId` of the existing
64
+ variant), `invalid_variant_attributes` (with `field` and `errors[]`),
65
+ `variant_group_inactive` and `variant_limit`. `too_many_positions` now also
66
+ answers a variant request that would need more writes than fit into one
67
+ operation, with `field` (`variants`, `createMatrix`, or `externalIds` for
68
+ `addVariant`); nothing is written then.
69
+ - **Contract**: `fixtures/v3/` pulled from the backend (stage 5c); the
70
+ vocabulary of `./stored` regenerated from it (fingerprint only, no schema
71
+ change for stored documents).
72
+
73
+ ## 1.5.1
74
+
75
+ Every call with an effect reports `outcome: 'unknown'` when it fails after
76
+ sending began: network error, timeout, HTTP 5xx, HTTP 200 with the `/v3`
77
+ marker but an empty, non-JSON or status-less body, and HTML with the marker.
78
+ Reason: until 1.5.0 only the six signing and money calls did; a goods receipt,
79
+ a reservation or `issueInvoice` came back as `'rejected'` ("did not happen")
80
+ although the server may have booked it. Whoever believed that and resent with
81
+ a **new** `idempotencyKey` booked twice.
82
+
83
+ A change to the safe side, no change on the wire; requests send the same body
84
+ as in 1.5.0 (only the `Kasseneck-Client` header carries the new version).
85
+
86
+ - **Which calls**: `UNKNOWN_OUTCOME_CALLS` (exported, sorted), also in
87
+ `fixtures/surface.json` as `unknownOutcomeCalls` for the Dart twin. New in it:
88
+ inventory writes (`createArticle`, `updateArticle`, `deactivateArticle`,
89
+ `receiveGoods`, `transferStock`, `recordStockLoss`, `changeStockCondition`,
90
+ `reverseStockMovement`), reservations (`createReservation`,
91
+ `extendReservation`, `releaseReservation`), inventory and partner webhooks
92
+ (create, update, delete, rotate the secret, test delivery), the invoice API
93
+ (`issueInvoice`, `cancelInvoice`, `createCreditNote`, `recordInvoicePayment`,
94
+ `createCustomer`, `updateCustomer`), invoice items and SEPA mandates
95
+ (`createInvoiceItem`, `updateInvoiceItem`, `withdrawInvoiceItem`,
96
+ `setCustomerMandate`, `revokeCustomerMandate`), the partner calls with an
97
+ effect (`createPartnerCustomer`, `sendPartnerCustomerFonLink`,
98
+ `requestCustomerSignature`, `createCustomerCashregister`,
99
+ `activateCashregister`, `reportCustomerContract`), and on the register
100
+ `pairRegisterDevice`, `unpairRegisterDevice`, `setMyKasseSettings`,
101
+ `setMyKasseLogo`, `setMyRegisterDeviceSettings`,
102
+ `setMyCashregisterStockLocation`, `createPrintJob`, `sendReceiptEmail`.
103
+ - **Which stay `'rejected'`**: reading calls, dry runs (`previewGoodsReceipt`,
104
+ `previewInvoice`, `issueInvoice` with `dryRun: true`), the register sign-in
105
+ sessions (`registerUserLogin`, `registerPinLogin`, `renewRegisterSession`,
106
+ `endRegisterSession`) and `createPaymentLinkStripe`: a repeat books nothing.
107
+ A timeout already during sign-in stays `'rejected'` as before: nothing was
108
+ sent.
109
+ - **What to do**: with an `idempotencyKey`, send the same request with the
110
+ **same** key; it takes effect once and returns the stored answer.
111
+ `createInvoiceItem` is recognised by `source` + `reference`: the same values
112
+ again are safe. Without a key (receipts, cancellations, money calls,
113
+ settings, webhooks, changing or withdrawing invoice items, mandates), read
114
+ the state first. The rule for receipts and payments is unchanged: look it up,
115
+ never resend.
116
+ - **Per call**: the transport takes a fifth argument `{ hasEffect }`
117
+ (`TransportCallOptions`). `true` marks a call with an effect that this
118
+ package does not know. `false` only counts together with
119
+ `params.dryRun === true` (a dry run under the name of the real call; the
120
+ package's own previews use it); without `dryRun: true` a call from the list
121
+ stays `'unknown'`, so no option can turn a real booking into `'rejected'`.
122
+ Without the option the list decides, and an unknown name counts as having no
123
+ effect.
124
+ - **Guard**: every call the package knows (`ALL_CALLS`, `PUBLIC_CALLS`,
125
+ `POS_CALLS`) is classified as with or without effect; a new call without a
126
+ classification fails the tests.
127
+ - `CALLS_WITH_EFFECT` of the register texts and `fixtures/pos-texts.json` are
128
+ unchanged; `messageOutcome` follows the transport first, so the register
129
+ shows the careful sentence for every call above.
130
+
7
131
  ## 1.5.0
8
132
 
9
133
  Inventory API, write side and reservations: `./inventory` gains the 13
package/README.md CHANGED
@@ -433,30 +433,42 @@ receipt email, and values outside a settings field's value list throw a
433
433
  `KasseneckValidationError` before anything is sent, instead of being dropped
434
434
  silently. For a layout option of 0.x the message names its English successor.
435
435
 
436
- ### `outcome: 'unknown'`: never retry, look it up
436
+ ### `outcome: 'unknown'`: never resend blindly
437
437
 
438
438
  `KasseneckApiError`, `KasseneckHttpError` and `KasseneckNetworkError` carry
439
439
  `outcome`. `'rejected'` means the server turned the request down; whether
440
440
  anything else is safe to do depends on the code, and on the money calls it
441
441
  is only set for the codes listed below. `'unknown'` means the operation **may have been carried out**, for
442
- `createReceipt` a signed receipt in the chain. Then never send it again: read
443
- the result back (`getReceipt`, `listMyReceipts`, the original of a
444
- cancellation) and continue from there. `isOutcomeUnknown(error)` covers all
445
- three classes. The outcome is unknown for:
442
+ `createReceipt` a signed receipt in the chain. Then never send it again blindly:
443
+ read the result back (`getReceipt`, `listMyReceipts`, the original of a
444
+ cancellation) and continue from there. A call with an `idempotencyKey` (or,
445
+ for invoice items, the same `source` and `reference`) is the exception: send
446
+ it again unchanged (see below).
447
+ `isOutcomeUnknown(error)` covers all three classes. The outcome is unknown for:
446
448
 
447
449
  - `dialect_mismatch`, `receipt_outcome_unknown`, `cancellation_outcome_unknown`;
448
450
  - `response_translation_failed`, unless `details.handled === false`;
449
451
  - `response_unreadable`: a signing or money-moving call reported success,
450
452
  but the response lacks what the call promises (no receipt, no reference, no
451
453
  remaining quantities, no Hobex receipt, no captured payment intent);
452
- - on the calls with an effect: the signing calls `createReceipt`,
453
- `cancelReceipt` and `financeWebService`, and the money calls `hobexPay`
454
- (`hobexPayApi`, charges a card), `hobexRefund` (`hobexRefundApi`) and
455
- `stripeCaptureIntent`: a network error or timeout after sending began, HTTP
456
- 5xx, and HTTP 200 with the `Kasseneck-Api-Version: v3` marker but an empty,
454
+ - on every call with an effect (`UNKNOWN_OUTCOME_CALLS`, also in
455
+ `fixtures/surface.json` as `unknownOutcomeCalls`): the signing calls
456
+ `createReceipt`, `cancelReceipt` and `financeWebService`, the money calls
457
+ `hobexPay` (`hobexPayApi`, charges a card), `hobexRefund`
458
+ (`hobexRefundApi`) and `stripeCaptureIntent`, and since 1.5.1 every other
459
+ call that books, issues, creates, changes, deletes or sends something:
460
+ inventory writes and reservations, webhooks, invoices, credit notes,
461
+ recorded payments, customers, invoice items and SEPA mandates, partner
462
+ businesses, register settings, pairing, print jobs and receipt emails.
463
+ There it is a network error or timeout after sending began, HTTP 5xx, and
464
+ HTTP 200 with the `Kasseneck-Api-Version: v3` marker but an empty,
457
465
  non-JSON (also `text/html`) or status-less body (`KasseneckHttpError`,
458
466
  `reason` `empty-body`, `not-json` or `missing-status`). HTML without the
459
- marker stays `route_missing` with `'rejected'`: no function saw the call;
467
+ marker stays `route_missing` with `'rejected'`: no function saw the call.
468
+ Reading calls and dry runs (`previewGoodsReceipt`, `previewInvoice`) stay
469
+ `'rejected'`, and so do the register sign-in sessions and
470
+ `createPaymentLinkStripe`: a repeat books nothing (a second short-lived
471
+ session, a second link nobody else has seen);
460
472
  - on the money calls: every error envelope **without a code**, and every
461
473
  code that is not one of the rejection codes below. The Hobex and Stripe
462
474
  handlers also answer with a plain error message after the provider was
@@ -490,17 +502,33 @@ failed `hobexRefund` throws a `KasseneckApiError`; it never returns `false`,
490
502
  because a `false` on an unclear outcome invites a second refund. Even a
491
503
  `'rejected'` is no invitation to resend blindly: fix the cause first.
492
504
 
493
- **Never retry these six calls, and never put a retrying layer under the
494
- package.** A `fetch` passed in `options.fetch` (or a proxy or service worker in
495
- front of it) must not resend a request by itself after a network error, a
496
- timeout or a 5xx: a silent second send is a second signed receipt, a second
497
- charge or a second refund, and the package cannot see it. Look the result up
498
- instead (`getReceipt`, `listMyReceipts`, the Hobex transaction by its
499
- `transactionId`, the Stripe session).
500
-
501
- `outcome` only covers signing and the money calls. After a network error on
502
- `issueInvoice` an invoice may still have been issued; retry it with the same
503
- `idempotencyKey`.
505
+ **Never retry a call with an effect blindly, and never put a retrying layer
506
+ under the package.** A `fetch` passed in `options.fetch` (or a proxy or service
507
+ worker in front of it) must not resend a request by itself after a network
508
+ error, a timeout or a 5xx: a silent second send is a second signed receipt, a
509
+ second charge, a second refund or a second booking, and the package cannot
510
+ see it. Look the result up instead (`getReceipt`, `listMyReceipts`, the Hobex
511
+ transaction by its `transactionId`, the Stripe session).
512
+
513
+ **With an `idempotencyKey`** (inventory writes and reservations, `issueInvoice`,
514
+ `cancelInvoice`, `createCreditNote`, `recordInvoicePayment`, `createCustomer`
515
+ and `createPartnerCustomer` with a key) the safe retry after `'unknown'` is the
516
+ same request with the **same** key: it takes effect exactly once and returns
517
+ the stored answer (invoices and partner customers mark it with
518
+ `replayed: true`; inventory writes return the stored answer unchanged). Never a
519
+ new key: that books a second time. `createInvoiceItem` has no key; the server recognises a repeat by
520
+ `source` + `reference`, so the same request with the same values is safe
521
+ (`replayed: true`; other data gives `item_reference_conflict`). Without a key
522
+ (receipts, cancellations, money calls, settings, webhooks, `updateCustomer`,
523
+ `createCustomer` and `createPartnerCustomer` without one, `updateInvoiceItem`,
524
+ `withdrawInvoiceItem`, `setCustomerMandate`, `revokeCustomerMandate`) read the
525
+ state first (for invoice items `getInvoiceItem`) and only then decide.
526
+
527
+ A call through the open transport that this package does not know counts as
528
+ having no effect; say so with `transport(name, params, undefined, undefined,
529
+ { hasEffect: true })`. `hasEffect: false` only counts together with
530
+ `params.dryRun === true` (a dry run under the name of the real call); without
531
+ it a call with an effect stays `'unknown'`.
504
532
 
505
533
  ```ts
506
534
  import { isOutcomeUnknown, paymentsExpectedCents } from '@kreiseck/kasseneck-api';
@@ -944,7 +972,7 @@ process:
944
972
  `hobexPay`, `hobexRefund` and `stripeCaptureIntent` move money. They are never
945
973
  retried, neither by the app nor by a retrying `fetch` under the package: on
946
974
  `isOutcomeUnknown(error)` look the payment up (see
947
- [`outcome: 'unknown'`](#outcome-unknown-never-retry-look-it-up)).
975
+ [`outcome: 'unknown'`](#outcome-unknown-never-resend-blindly)).
948
976
  - **Hobex HPS via Kasseneck Connect**, described below.
949
977
 
950
978
  ### Hobex HPS via Kasseneck Connect
@@ -1373,7 +1401,13 @@ try {
1373
1401
 
1374
1402
  **After a timeout, retry with the same `idempotencyKey`**, never with a new one:
1375
1403
  you then get the invoice that was already issued (`replayed: true`). The same
1376
- key with different data gives `idempotency_conflict`.
1404
+ key with different data gives `idempotency_conflict`. Since 1.5.1 every invoice
1405
+ call with an effect (`issueInvoice`, `cancelInvoice`, `createCreditNote`,
1406
+ `recordInvoicePayment`, `createCustomer`, `updateCustomer`) reports
1407
+ `outcome: 'unknown'` after a timeout, a network error, HTTP 5xx or an
1408
+ unreadable answer (before: `'rejected'`); `previewInvoice` and the reading
1409
+ calls stay `'rejected'`. See
1410
+ [`outcome: 'unknown'`](#outcome-unknown-never-resend-blindly).
1377
1411
 
1378
1412
  Validation errors arrive as `validation` with field paths
1379
1413
  (`invoiceFieldErrors(error)` → `[{ field: 'items[0].vatRate', message }]`).
@@ -1448,7 +1482,8 @@ ledger, and get every stock change pushed by webhook within seconds, also the
1448
1482
  ones made at the register in the shop. Since 1.5.0 also the write side:
1449
1483
  create and update articles, book goods receipts, transfers, losses and
1450
1484
  condition changes, and reserve stock at checkout (see
1451
- [Shop: reserve and invoice](#shop-reserve-and-invoice)). Uses the `api_key` of
1485
+ [Shop: reserve and invoice](#shop-reserve-and-invoice)); since 1.6.0 variant
1486
+ groups (sizes, colours, see [Variants](#variants)). Uses the `api_key` of
1452
1487
  the account and belongs on a **server**. Reading needs the module `lager`,
1453
1488
  writing additionally the account switch *Lager-API schreiben*, which Kasseneck
1454
1489
  turns on (always on in the test environment, `kr_test_…`; otherwise
@@ -1566,7 +1601,16 @@ Every write takes an `idempotencyKey` (1 to 120 characters). The same request
1566
1601
  with the same key takes effect exactly once; repeating it returns the stored
1567
1602
  answer of the first call, the same key with different content gives
1568
1603
  `idempotency_conflict`. After a timeout or a network error, repeat with the
1569
- **same** key, never with a new one. The client refuses to send a write without
1604
+ **same** key, never with a new one. Since 1.5.1 every write, reservation and
1605
+ webhook call reports `outcome: 'unknown'` in that case (also after HTTP 5xx or
1606
+ an unreadable answer; before: `'rejected'`), so `isOutcomeUnknown(error)` is
1607
+ the signal to resend writes and reservations with the same key;
1608
+ `previewGoodsReceipt` and the reading calls stay `'rejected'`. The webhook
1609
+ calls (`createWebhook`, `updateWebhook`, `deleteWebhook`,
1610
+ `rotateWebhookSecret`, `sendWebhookTest`) have no key: after `'unknown'` look
1611
+ the state up first (`listWebhooks`, `listWebhookDeliveries`) instead of
1612
+ resending, or you get a second webhook, a second new secret or a used-up test
1613
+ delivery. The client refuses to send a write without
1570
1614
  a valid key, and checks before sending only what is certainly wrong without the
1571
1615
  network (required ids, integer quantities, amounts and prices, the range of
1572
1616
  `expiresInMinutes`); everything else the server decides and reports with its
@@ -1709,6 +1753,116 @@ function onDelivery(rawBody: Buffer) {
1709
1753
  net without webhooks; the stock ledger shows reservations as movements of
1710
1754
  type `reservation` with `quantityDelta: 0` and the change in `reservedDelta`.
1711
1755
 
1756
+ ### Variants
1757
+
1758
+ A variant is an ordinary article with `variantGroupId` and
1759
+ `variantAttributes`: its own id, code, stock and tile at the register; it is
1760
+ read, booked, reserved and invoiced like any other article. The variant group
1761
+ holds what the variants share (name, attributes with their values, defaults
1762
+ for new variants) and guarantees that each combination exists only once. The
1763
+ writes take an `idempotencyKey` and the account switch *Lager-API schreiben*,
1764
+ like every other write.
1765
+
1766
+ ```ts
1767
+ import { createInventoryClient, isInventoryError, parseInventoryWebhookEvent } from '@kreiseck/kasseneck-api/inventory';
1768
+
1769
+ const inventory = createInventoryClient({ apiKey: process.env.KASSENECK_API_KEY! });
1770
+
1771
+ // 1. Create the group with every combination (3 sizes × 2 colours = 6 variants).
1772
+ const apron = await inventory.createVariantGroup({
1773
+ idempotencyKey: 'shop-group-3001',
1774
+ name: 'Schürze',
1775
+ attributes: [
1776
+ { key: 'size', label: 'Größe', values: ['S', 'M', 'L'] },
1777
+ { key: 'colour', label: 'Farbe', values: ['rot', 'blau'] },
1778
+ ],
1779
+ defaults: { unitPriceCents: 2490, vatRate: 20, stockTracked: true },
1780
+ createMatrix: true,
1781
+ });
1782
+ // apron.variants: [{ articleId, variantAttributes: { colour: 'rot', size: 'S' } }, …]
1783
+
1784
+ // 2. The answer carries ids only. Read the articles of the group (names
1785
+ // "Schürze S rot" …, own codes) and map them to the shop's products.
1786
+ for await (const article of inventory.iterateArticles({ variantGroupId: apron.id })) {
1787
+ await shop.linkVariant(article.variantAttributes!, article.id, article.internalCode);
1788
+ }
1789
+
1790
+ // 3. A new size later: add the value, then the variants you want.
1791
+ await inventory.updateVariantGroup({
1792
+ idempotencyKey: 'shop-group-3001-xl',
1793
+ variantGroupId: apron.id,
1794
+ addAttributeValues: { size: ['S', 'M', 'L', 'XL'] }, // known values are skipped
1795
+ });
1796
+ try {
1797
+ await inventory.addVariant({
1798
+ idempotencyKey: 'shop-variant-3001-xl-rot',
1799
+ variantGroupId: apron.id,
1800
+ variantAttributes: { size: 'XL', colour: 'rot' },
1801
+ ean: '9001234567834', // optional: a foreign article with this code
1802
+ });
1803
+ } catch (error) {
1804
+ if (!isInventoryError(error, 'variant_already_exists')) throw error;
1805
+ // Exists already (error.details.articleId): link that one instead.
1806
+ }
1807
+
1808
+ // 4. Webhooks: keep the group state with the latest updatedAt.
1809
+ function onDelivery(rawBody: Buffer) {
1810
+ const event = parseInventoryWebhookEvent(rawBody);
1811
+ if (event && !event.test && (event.type === 'variant_group.created' || event.type === 'variant_group.updated')) {
1812
+ return shop.saveGroupIfNewer(event.data.id, event.data, event.data.updatedAt);
1813
+ }
1814
+ }
1815
+ ```
1816
+
1817
+ - **Limits.** At most 3 attributes per group (keys `^[a-z0-9_]{1,32}$`,
1818
+ `__…__` is reserved), at most 30 values per attribute (1 to 30 characters,
1819
+ unique ignoring case), at most 100 combinations with `createMatrix` and at
1820
+ most 100 entries in `variants[]` per request, at most 250 active variants
1821
+ per group (`variant_limit`). The client does not check these before sending
1822
+ (the server may raise them); they are exported as
1823
+ `VARIANT_ATTRIBUTES_MAX`, `VARIANT_VALUES_MAX`, `VARIANT_MATRIX_MAX` and
1824
+ `VARIANT_GROUP_ACTIVE_MAX`. A request that would need more writes than fit
1825
+ into one operation (about 187 external ids with 100 variants) is refused as
1826
+ a whole with `too_many_positions` and `field` (`variants`, `createMatrix`,
1827
+ or `externalIds` for `addVariant`); nothing is written. Send fewer variants
1828
+ and add the rest with `addVariant`.
1829
+ - **Order.** `attributes` keeps the order of the group: it decides the default
1830
+ name "<group> <value1> <value2>" and the matrix (the first attribute runs
1831
+ outermost). `variantAttributes` on articles, in `variants[]` and in webhooks
1832
+ comes with its keys **sorted by code point**, not in attribute order; compare
1833
+ by key, never by position. Values are stored trimmed and in Unicode NFC; a
1834
+ variant's value must match a listed value exactly (case and spaces are not
1835
+ adjusted), otherwise `invalid_variant_attributes` names the `field`.
1836
+ - **Variants are articles.** `addVariant` answers with the article as
1837
+ `createArticle` does. Each variant takes the fields of `createArticle`;
1838
+ fields it does not name are filled from the group defaults **at creation
1839
+ only**: changing the group name or defaults later changes no existing
1840
+ variant (use `updateArticle`). `createArticle` and `updateArticle` refuse
1841
+ `variantGroupId` and `variantAttributes`; a variant is never moved to another
1842
+ group, and an existing article never becomes a variant.
1843
+ - **Deactivating.** `deactivateArticle` on one variant of an active group
1844
+ takes it out of the group's `variants[]` and frees its combination for a new
1845
+ variant.
1846
+ `updateVariantGroup({ active: false })` deactivates the group and every
1847
+ variant (codes and external ids become free), stands alone and is final; a
1848
+ deactivated group answers every other change and `addVariant` with
1849
+ `variant_group_inactive`. The answer comes when all variants are
1850
+ deactivated; after `outcome: 'unknown'` simply repeat it, also with a new
1851
+ key, it completes an interrupted run.
1852
+ - **After `unknown`.** As with every write: resend with the **same**
1853
+ `idempotencyKey`. A new key would create the group a second time (the
1854
+ combination check is per group). `variant_already_exists` on `addVariant`
1855
+ carries `articleId` of the existing variant.
1856
+ - **Events.** `variant_group.created` and `variant_group.updated` carry the
1857
+ group as `getVariantGroup` returns it (with `variants[]`, without articles).
1858
+ `updated` fires only on a visible change; creating a variant also sends
1859
+ `article.created`, deactivating the group one `variant_group.updated` and
1860
+ one `article.deactivated` per variant. Deliveries can overtake each other:
1861
+ keep a group state only if its `updatedAt` is later than the stored one.
1862
+ Without webhooks, `listVariantGroups({ updatedSince })` and
1863
+ `listArticles({ variantGroupId, updatedSince })` are sorted by `updatedAt`
1864
+ ascending and include the boundary.
1865
+
1712
1866
  ## Migrating from 0.x
1713
1867
 
1714
1868
  1.0 is one breaking step: the wire, the exported names and the contract files
@@ -1784,7 +1938,7 @@ in machine form what both sides agreed on, among them:
1784
1938
  |---|---|---|
1785
1939
  | `pos-settings-defaults.json` | field names and defaults of the register settings as sent on `/api/v3` (`business`, `device`), derived from the backend contract in `fixtures/v3/` | `npm run fixtures:kasse` |
1786
1940
  | `stored/pos-settings-defaults.json` | the same defaults in the stored (internal, German) form (`betrieb`, `geraet`) | `npm run fixtures:kasse` |
1787
- | `surface.json` | base URLs (`baseUrls.public`, `baseUrls.pos`), the calls per path (`calls`, and the backend's `routes`), settings value lists (`enums`, keyed by field), other register lists (`pos`), register error codes, permission keys, shortcut actions, partner, invoice and inventory lists | `npm run fixtures:oberflaeche` |
1941
+ | `surface.json` | base URLs (`baseUrls.public`, `baseUrls.pos`), the calls per path (`calls`, and the backend's `routes`), the calls with an effect whose outcome is `unknown` after a timeout, network error, 5xx or unreadable answer (`unknownOutcomeCalls`, sorted), settings value lists (`enums`, keyed by field), other register lists (`pos`), register error codes, permission keys, shortcut actions, partner, invoice and inventory lists | `npm run fixtures:oberflaeche` |
1788
1942
  | `hobex-hps-codes.json` | measured HPS result codes, their meaning and whether they settle an outcome (the contract behind `isConclusive`) | `npm run fixtures:hobex-hps-codes` |
1789
1943
  | `pos-texts.json` | the register's message catalogue | `npm run fixtures:texte` |
1790
1944
  | `invoice-texts.json` | invoice texts in both languages | `npm run fixtures:rechnungstexte` |
@@ -12,8 +12,8 @@
12
12
  * weiterhin jeden `string`: wer einen Aufruf braucht, den dieses Paket nicht
13
13
  * umhuellt, muss ihn weiterhin absetzen koennen.
14
14
  */
15
- import type { TransportBodyFields } from './transport.js';
16
- export declare const ALL_CALLS: readonly ["activateCashregister", "cancelInvoice", "cancelReceipt", "createCreditNote", "createCustomer", "createCustomerCashregister", "createPartnerCustomer", "checkPartnerCustomerEmail", "createPartnerWebhook", "createPaymentLinkStripe", "createPrintJob", "createReceipt", "deletePartnerWebhook", "downloadDailyReport", "downloadReport", "endRegisterSession", "financeWebService", "generateFullReceiptId", "getCustomer", "getCustomerCredentials", "getCustomerSignatureStatus", "getFirstReceiptDate", "getInvoice", "getInvoicePdf", "getInvoiceSetupStatus", "getInvoiceXml", "getKasseSettings", "getPartnerCustomer", "getPartnerInfo", "getPrintJob", "getReceipt", "getReportV2", "hobexPayApi", "hobexRefundApi", "issueInvoice", "listCustomerCashregisters", "listInvoices", "listBrands", "listMyArticleGroups", "listMyArticles", "listMyCashregisters", "listMyPrinters", "listMyReceipts", "listMyStock", "listMyStockLocations", "listMyTipRecipients", "listPartnerCustomers", "listPartnerWebhookDeliveries", "listPartnerWebhooks", "listRegisterUsersForDevice", "listRegisterSessionsForDevice", "pairRegisterDevice", "recordInvoicePayment", "registerPinLogin", "registerUserLogin", "renewRegisterSession", "reportCustomerContract", "requestCustomerSignature", "searchCustomers", "sendPartnerCustomerFonLink", "sendPartnerWebhookTest", "sendReceiptEmail", "setMyCashregisterStockLocation", "setMyKasseLogo", "setMyKasseSettings", "setMyRegisterDeviceSettings", "stripeCaptureIntent", "unpairRegisterDevice", "rotatePartnerWebhookSecret", "updateCustomer", "updatePartnerWebhook", "getArticle", "listArticles", "lookupArticleByCode", "listLocations", "getStock", "listStock", "listStockMovements", "createWebhook", "updateWebhook", "deleteWebhook", "listWebhooks", "sendWebhookTest", "rotateWebhookSecret", "listWebhookDeliveries", "createArticle", "updateArticle", "deactivateArticle", "receiveGoods", "transferStock", "recordStockLoss", "changeStockCondition", "reverseStockMovement", "createReservation", "extendReservation", "releaseReservation", "getReservation", "listReservations"];
15
+ import type { TransportBodyFields, TransportCallOptions } from './transport.js';
16
+ export declare const ALL_CALLS: readonly ["activateCashregister", "cancelInvoice", "cancelReceipt", "createCreditNote", "createCustomer", "createCustomerCashregister", "createPartnerCustomer", "checkPartnerCustomerEmail", "createPartnerWebhook", "createPaymentLinkStripe", "createPrintJob", "createReceipt", "deletePartnerWebhook", "downloadDailyReport", "downloadReport", "endRegisterSession", "financeWebService", "generateFullReceiptId", "getCustomer", "getCustomerCredentials", "getCustomerSignatureStatus", "getFirstReceiptDate", "getInvoice", "getInvoicePdf", "getInvoiceSetupStatus", "getInvoiceXml", "getKasseSettings", "getPartnerCustomer", "getPartnerInfo", "getPrintJob", "getReceipt", "getReportV2", "hobexPayApi", "hobexRefundApi", "issueInvoice", "listCustomerCashregisters", "listInvoices", "listBrands", "listMyArticleGroups", "listMyArticles", "listMyCashregisters", "listMyPrinters", "listMyReceipts", "listMyStock", "listMyStockLocations", "listMyTipRecipients", "listPartnerCustomers", "listPartnerWebhookDeliveries", "listPartnerWebhooks", "listRegisterUsersForDevice", "listRegisterSessionsForDevice", "pairRegisterDevice", "recordInvoicePayment", "registerPinLogin", "registerUserLogin", "renewRegisterSession", "reportCustomerContract", "requestCustomerSignature", "searchCustomers", "sendPartnerCustomerFonLink", "sendPartnerWebhookTest", "sendReceiptEmail", "setMyCashregisterStockLocation", "setMyKasseLogo", "setMyKasseSettings", "setMyRegisterDeviceSettings", "stripeCaptureIntent", "unpairRegisterDevice", "rotatePartnerWebhookSecret", "updateCustomer", "updatePartnerWebhook", "getArticle", "listArticles", "lookupArticleByCode", "listLocations", "getStock", "listStock", "listStockMovements", "createWebhook", "updateWebhook", "deleteWebhook", "listWebhooks", "sendWebhookTest", "rotateWebhookSecret", "listWebhookDeliveries", "createArticle", "updateArticle", "deactivateArticle", "receiveGoods", "transferStock", "recordStockLoss", "changeStockCondition", "reverseStockMovement", "createReservation", "extendReservation", "releaseReservation", "getReservation", "listReservations", "createVariantGroup", "updateVariantGroup", "getVariantGroup", "listVariantGroups", "addVariant"];
17
17
  export type ApiCall = typeof ALL_CALLS[number];
18
18
  /**
19
19
  * Die oeffentlichen Endpunkte unter `/v3` (`https://api.kasseneck.at/v3/<name>`),
@@ -21,7 +21,7 @@ export type ApiCall = typeof ALL_CALLS[number];
21
21
  * (`fixtures/v3/v3-vokabular.json`, `endpoints.public`, umbenannt nach
22
22
  * `names`). Ein Test haelt beide Listen deckungsgleich.
23
23
  */
24
- export declare const PUBLIC_CALLS: readonly ["createReceipt", "getReceipt", "cancelReceipt", "sendReceiptEmail", "generateFullReceiptId", "downloadReceipt", "getFirstReceiptDate", "getReportV2", "downloadReport", "downloadDailyReport", "createPaymentLinkStripe", "stripeCaptureIntent", "hobexPayApi", "hobexRefundApi", "hobexGetStatus", "financeWebService", "listMyTipRecipients", "getPartnerInfo", "createPartnerCustomer", "checkPartnerCustomerEmail", "listPartnerCustomers", "getPartnerCustomer", "sendPartnerCustomerFonLink", "createPartnerWebhook", "updatePartnerWebhook", "deletePartnerWebhook", "listPartnerWebhooks", "sendPartnerWebhookTest", "rotatePartnerWebhookSecret", "listPartnerWebhookDeliveries", "requestCustomerSignature", "getCustomerSignatureStatus", "createCustomerCashregister", "activateCashregister", "listCustomerCashregisters", "getCustomerCredentials", "reportCustomerContract", "getPartnerBilling", "getPartnerBillingMonth", "createCustomer", "getCustomer", "updateCustomer", "searchCustomers", "issueInvoice", "cancelInvoice", "createCreditNote", "getInvoice", "listInvoices", "getInvoicePdf", "getInvoiceXml", "getInvoiceSetupStatus", "listBrands", "recordInvoicePayment", "createInvoiceItem", "updateInvoiceItem", "withdrawInvoiceItem", "getInvoiceItem", "listInvoiceItems", "setCustomerMandate", "revokeCustomerMandate", "getArticle", "listArticles", "lookupArticleByCode", "listLocations", "getStock", "listStock", "listStockMovements", "createWebhook", "updateWebhook", "deleteWebhook", "listWebhooks", "sendWebhookTest", "rotateWebhookSecret", "listWebhookDeliveries", "createArticle", "updateArticle", "deactivateArticle", "receiveGoods", "transferStock", "recordStockLoss", "changeStockCondition", "reverseStockMovement", "createReservation", "extendReservation", "releaseReservation", "getReservation", "listReservations"];
24
+ export declare const PUBLIC_CALLS: readonly ["createReceipt", "getReceipt", "cancelReceipt", "sendReceiptEmail", "generateFullReceiptId", "downloadReceipt", "getFirstReceiptDate", "getReportV2", "downloadReport", "downloadDailyReport", "createPaymentLinkStripe", "stripeCaptureIntent", "hobexPayApi", "hobexRefundApi", "hobexGetStatus", "financeWebService", "listMyTipRecipients", "getPartnerInfo", "createPartnerCustomer", "checkPartnerCustomerEmail", "listPartnerCustomers", "getPartnerCustomer", "sendPartnerCustomerFonLink", "createPartnerWebhook", "updatePartnerWebhook", "deletePartnerWebhook", "listPartnerWebhooks", "sendPartnerWebhookTest", "rotatePartnerWebhookSecret", "listPartnerWebhookDeliveries", "requestCustomerSignature", "getCustomerSignatureStatus", "createCustomerCashregister", "activateCashregister", "listCustomerCashregisters", "getCustomerCredentials", "reportCustomerContract", "getPartnerBilling", "getPartnerBillingMonth", "createCustomer", "getCustomer", "updateCustomer", "searchCustomers", "issueInvoice", "cancelInvoice", "createCreditNote", "getInvoice", "listInvoices", "getInvoicePdf", "getInvoiceXml", "getInvoiceSetupStatus", "listBrands", "recordInvoicePayment", "createInvoiceItem", "updateInvoiceItem", "withdrawInvoiceItem", "getInvoiceItem", "listInvoiceItems", "setCustomerMandate", "revokeCustomerMandate", "getArticle", "listArticles", "lookupArticleByCode", "listLocations", "getStock", "listStock", "listStockMovements", "createWebhook", "updateWebhook", "deleteWebhook", "listWebhooks", "sendWebhookTest", "rotateWebhookSecret", "listWebhookDeliveries", "createArticle", "updateArticle", "deactivateArticle", "receiveGoods", "transferStock", "recordStockLoss", "changeStockCondition", "reverseStockMovement", "createReservation", "extendReservation", "releaseReservation", "getReservation", "listReservations", "createVariantGroup", "updateVariantGroup", "getVariantGroup", "listVariantGroups", "addVariant"];
25
25
  /**
26
26
  * Die Endpunkte des Kassenwegs (`https://kasse.kasseneck.at/api/v3/<name>`,
27
27
  * Web-Kasse: gleicher Ursprung `/api/v3`), in der Reihenfolge von
@@ -31,6 +31,60 @@ export declare const PUBLIC_CALLS: readonly ["createReceipt", "getReceipt", "can
31
31
  export declare const POS_CALLS: readonly ["pairRegisterDevice", "listRegisterUsersForDevice", "listRegisterSessionsForDevice", "registerUserLogin", "registerPinLogin", "renewRegisterSession", "endRegisterSession", "unpairRegisterDevice", "listMyCashregisters", "listMyReceipts", "getReceipt", "createReceipt", "generateFullReceiptId", "cancelReceipt", "sendReceiptEmail", "listMyArticleGroups", "listMyArticles", "getKasseSettings", "setMyKasseSettings", "setMyKasseLogo", "setMyRegisterDeviceSettings", "listMyPrinters", "createPrintJob", "getPrintJob", "listMyTipRecipients", "listMyStockLocations", "listMyStock", "setMyCashregisterStockLocation"];
32
32
  export type PublicCall = typeof PUBLIC_CALLS[number];
33
33
  export type PosCall = typeof POS_CALLS[number];
34
+ /**
35
+ * Die Aufrufe **mit Wirkung**, alphabetisch: sie legen etwas an, aendern oder
36
+ * loeschen es, buchen, signieren, bewegen Geld oder senden etwas hinaus
37
+ * (Mail, Druckjob, Probesendung). Scheitert einer, nachdem die Anfrage
38
+ * unterwegs war (Netzfehler, Zeitlimit, HTTP 5xx, unlesbare Erfolgsantwort,
39
+ * HTML mit Kennzeichen), meldet der Transport `outcome: 'unknown'`: der
40
+ * Vorgang kann ausgefuehrt sein.
41
+ *
42
+ * Bis 1.5.0 galt das nur fuer die sechs signierenden und geldbewegenden
43
+ * Aufrufe; ein Wareneingang oder eine Rechnung nach einem Zeitlimit kam als
44
+ * `'rejected'` an, und wer das glaubte und mit NEUEM Idempotenzschluessel
45
+ * neu sendete, buchte doppelt.
46
+ *
47
+ * Geht als `unknownOutcomeCalls` in den Vertrag (`fixtures/surface.json`);
48
+ * der Dart-Zwilling prueft seine Liste dagegen. Jeder Aufruf aus
49
+ * [ALL_CALLS], [PUBLIC_CALLS] und [POS_CALLS] steht entweder hier oder in
50
+ * [CALLS_WITHOUT_EFFECT] (Waechter `test/ausgang-einordnung.test.ts`).
51
+ */
52
+ export declare const UNKNOWN_OUTCOME_CALLS: readonly ["activateCashregister", "addVariant", "cancelInvoice", "cancelReceipt", "changeStockCondition", "createArticle", "createCreditNote", "createCustomer", "createCustomerCashregister", "createInvoiceItem", "createPartnerCustomer", "createPartnerWebhook", "createPrintJob", "createReceipt", "createReservation", "createVariantGroup", "createWebhook", "deactivateArticle", "deletePartnerWebhook", "deleteWebhook", "extendReservation", "financeWebService", "hobexPayApi", "hobexRefundApi", "issueInvoice", "pairRegisterDevice", "receiveGoods", "recordInvoicePayment", "recordStockLoss", "releaseReservation", "reportCustomerContract", "requestCustomerSignature", "reverseStockMovement", "revokeCustomerMandate", "rotatePartnerWebhookSecret", "rotateWebhookSecret", "sendPartnerCustomerFonLink", "sendPartnerWebhookTest", "sendReceiptEmail", "sendWebhookTest", "setCustomerMandate", "setMyCashregisterStockLocation", "setMyKasseLogo", "setMyKasseSettings", "setMyRegisterDeviceSettings", "stripeCaptureIntent", "transferStock", "unpairRegisterDevice", "updateArticle", "updateCustomer", "updateInvoiceItem", "updatePartnerWebhook", "updateVariantGroup", "updateWebhook", "withdrawInvoiceItem"];
53
+ /** Warum ein Aufruf keine Wirkung hat, die ein Zeitlimit offen lassen koennte. */
54
+ export type CallWithoutEffectReason = 'read' | 'repeatable';
55
+ /**
56
+ * Die Aufrufe **ohne Wirkung**, je mit Grund. Bei ihnen bleibt es bei
57
+ * `outcome: 'rejected'`, auch nach Zeitlimit, Netzfehler oder HTTP 5xx; eine
58
+ * Wiederholung bucht nichts doppelt:
59
+ *
60
+ * - `read`: reines Lesen (auch Berichte, Dateien, `generateFullReceiptId`,
61
+ * das nur einen Link-Schluessel ableitet, und `getCustomerCredentials`,
62
+ * dessen Abruf das Backend nur mitschreibt).
63
+ * - `repeatable`: legt etwas an, das ohne die verlorene Antwort niemand
64
+ * erreicht und das von selbst verfaellt, und bucht nichts. Die Sitzung der
65
+ * Kassen-Anmeldung (anmelden, verlaengern, abmelden: eine Wiederholung legt
66
+ * hoechstens eine weitere kurzlebige Sitzung an bzw. verlaengert oder
67
+ * beendet dieselbe noch einmal) und der Stripe-Zahlungslink
68
+ * (`createPaymentLinkStripe`: den Link kennt nur die Antwort, ein nie
69
+ * zugestellter Link wird nie bezahlt; Geld bewegt erst die Zahlung bzw.
70
+ * `stripeCaptureIntent`).
71
+ *
72
+ * Probelaeufe (`previewGoodsReceipt`, `previewInvoice`) teilen den Namen mit
73
+ * dem echten Aufruf und stehen darum nicht hier; die Huellen setzen fuer sie
74
+ * `hasEffect: false` beim Aufruf, das nur zusammen mit `dryRun: true` gilt
75
+ * (siehe `TransportCallOptions`).
76
+ *
77
+ * Paketintern; nicht Teil der Paketoberflaeche.
78
+ */
79
+ export declare const CALLS_WITHOUT_EFFECT: Readonly<Record<string, CallWithoutEffectReason>>;
80
+ /**
81
+ * Hat dieser Aufruf Wirkung, ist sein Ausgang nach Zeitlimit, Netzfehler,
82
+ * HTTP 5xx oder unlesbarer Erfolgsantwort also unklar? `financeWebService`
83
+ * zaehlt mit jedem Vorgang. Ein Name, den dieses Paket nicht kennt (der
84
+ * offene Transport nimmt jeden), ist `false`; wer so einen Aufruf mit Wirkung
85
+ * absetzt, sagt es mit `hasEffect: true`.
86
+ */
87
+ export declare function isUnknownOutcomeCall(name: string): boolean;
34
88
  /** Einer der 28 Aufrufe des Kassenwegs. */
35
89
  export declare function isPosCall(name: string): boolean;
36
90
  /**
@@ -40,6 +94,6 @@ export declare function isPosCall(name: string): boolean;
40
94
  */
41
95
  export declare function isPosOnlyCall(name: string): boolean;
42
96
  /** Wie [KasseneckTransport], nur mit bekanntem Aufrufnamen. Nicht exportiert nach aussen. */
43
- export type InternerTransport = <T = unknown>(functionName: ApiCall, params?: Record<string, unknown>, extraBodyFields?: TransportBodyFields, secretParams?: readonly string[]) => Promise<T>;
97
+ export type InternerTransport = <T = unknown>(functionName: ApiCall, params?: Record<string, unknown>, extraBodyFields?: TransportBodyFields, secretParams?: readonly string[], options?: TransportCallOptions) => Promise<T>;
44
98
  /** Wie [KasseneckBinaryTransport], nur mit bekanntem Aufrufnamen. */
45
99
  export type InternerBinaerTransport = (functionName: ApiCall, params?: Record<string, unknown>) => Promise<Uint8Array>;