@kreiseck/kasseneck-api 1.7.0 → 1.8.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 (115) hide show
  1. package/CHANGELOG.md +122 -0
  2. package/README.md +170 -1
  3. package/dist/cjs/client/aufrufe.d.ts +9 -7
  4. package/dist/cjs/client/aufrufe.js +57 -2
  5. package/dist/cjs/client/errors.js +2 -0
  6. package/dist/cjs/client/index.d.ts +1 -1
  7. package/dist/cjs/client/index.js +2 -1
  8. package/dist/cjs/client/transport.d.ts +27 -3
  9. package/dist/cjs/client/transport.js +29 -3
  10. package/dist/cjs/index.d.ts +1 -1
  11. package/dist/cjs/index.js +3 -2
  12. package/dist/cjs/inventory/api.d.ts +21 -2
  13. package/dist/cjs/inventory/api.js +22 -3
  14. package/dist/cjs/inventory/fehler.d.ts +6 -0
  15. package/dist/cjs/inventory/fehler.js +12 -0
  16. package/dist/cjs/inventory/index.d.ts +6 -4
  17. package/dist/cjs/inventory/index.js +33 -3
  18. package/dist/cjs/inventory/inventur.d.ts +104 -0
  19. package/dist/cjs/inventory/inventur.js +245 -0
  20. package/dist/cjs/inventory/lesen.d.ts +20 -1
  21. package/dist/cjs/inventory/lesen.js +307 -0
  22. package/dist/cjs/inventory/typen.d.ts +410 -1
  23. package/dist/cjs/inventory/vertrag.d.ts +94 -8
  24. package/dist/cjs/inventory/vertrag.js +117 -5
  25. package/dist/cjs/invoice/vertrag.d.ts +1 -1
  26. package/dist/cjs/invoice/vertrag.js +2 -0
  27. package/dist/cjs/models/cancellation.d.ts +1 -1
  28. package/dist/cjs/models/cancellation.js +2 -0
  29. package/dist/cjs/models/payment-errors.d.ts +1 -1
  30. package/dist/cjs/models/payment-errors.js +2 -0
  31. package/dist/cjs/models/receipt-email.d.ts +1 -1
  32. package/dist/cjs/models/receipt-email.js +2 -0
  33. package/dist/cjs/models/receipt-errors.d.ts +1 -1
  34. package/dist/cjs/models/receipt-errors.js +2 -0
  35. package/dist/cjs/pos/errors.d.ts +4 -3
  36. package/dist/cjs/pos/errors.js +23 -2
  37. package/dist/cjs/pos/index.d.ts +4 -1
  38. package/dist/cjs/pos/index.js +9 -2
  39. package/dist/cjs/pos/inventur.d.ts +104 -0
  40. package/dist/cjs/pos/inventur.js +117 -0
  41. package/dist/cjs/pos/texte.d.ts +82 -0
  42. package/dist/cjs/pos/texte.js +35 -0
  43. package/dist/cjs/react/index.js +6 -0
  44. package/dist/cjs/register/errors.d.ts +1 -1
  45. package/dist/cjs/register/errors.js +2 -0
  46. package/dist/cjs/stored/vokabular.d.ts +1 -1
  47. package/dist/cjs/stored/vokabular.js +1 -1
  48. package/dist/cjs/version.d.ts +1 -1
  49. package/dist/cjs/version.js +1 -1
  50. package/dist/esm/client/aufrufe.d.ts +9 -7
  51. package/dist/esm/client/aufrufe.js +57 -2
  52. package/dist/esm/client/errors.js +2 -0
  53. package/dist/esm/client/index.d.ts +1 -1
  54. package/dist/esm/client/index.js +1 -1
  55. package/dist/esm/client/transport.d.ts +27 -3
  56. package/dist/esm/client/transport.js +28 -3
  57. package/dist/esm/index.d.ts +1 -1
  58. package/dist/esm/index.js +1 -1
  59. package/dist/esm/inventory/api.d.ts +21 -2
  60. package/dist/esm/inventory/api.js +23 -4
  61. package/dist/esm/inventory/fehler.d.ts +6 -0
  62. package/dist/esm/inventory/fehler.js +11 -0
  63. package/dist/esm/inventory/index.d.ts +6 -4
  64. package/dist/esm/inventory/index.js +5 -3
  65. package/dist/esm/inventory/inventur.d.ts +104 -0
  66. package/dist/esm/inventory/inventur.js +226 -0
  67. package/dist/esm/inventory/lesen.d.ts +20 -1
  68. package/dist/esm/inventory/lesen.js +301 -0
  69. package/dist/esm/inventory/typen.d.ts +410 -1
  70. package/dist/esm/inventory/vertrag.d.ts +94 -8
  71. package/dist/esm/inventory/vertrag.js +116 -4
  72. package/dist/esm/invoice/vertrag.d.ts +1 -1
  73. package/dist/esm/invoice/vertrag.js +2 -0
  74. package/dist/esm/models/cancellation.d.ts +1 -1
  75. package/dist/esm/models/cancellation.js +2 -0
  76. package/dist/esm/models/payment-errors.d.ts +1 -1
  77. package/dist/esm/models/payment-errors.js +2 -0
  78. package/dist/esm/models/receipt-email.d.ts +1 -1
  79. package/dist/esm/models/receipt-email.js +2 -0
  80. package/dist/esm/models/receipt-errors.d.ts +1 -1
  81. package/dist/esm/models/receipt-errors.js +2 -0
  82. package/dist/esm/pos/errors.d.ts +4 -3
  83. package/dist/esm/pos/errors.js +23 -2
  84. package/dist/esm/pos/index.d.ts +4 -1
  85. package/dist/esm/pos/index.js +2 -1
  86. package/dist/esm/pos/inventur.d.ts +104 -0
  87. package/dist/esm/pos/inventur.js +109 -0
  88. package/dist/esm/pos/texte.d.ts +82 -0
  89. package/dist/esm/pos/texte.js +35 -0
  90. package/dist/esm/react/index.js +6 -0
  91. package/dist/esm/register/errors.d.ts +1 -1
  92. package/dist/esm/register/errors.js +2 -0
  93. package/dist/esm/stored/vokabular.d.ts +1 -1
  94. package/dist/esm/stored/vokabular.js +1 -1
  95. package/dist/esm/version.d.ts +1 -1
  96. package/dist/esm/version.js +1 -1
  97. package/fixtures/hobex-hps-codes.json +1 -1
  98. package/fixtures/invoice-api.schema.json +1 -1
  99. package/fixtures/invoice-texts.json +1 -1
  100. package/fixtures/pos-texts.json +94 -1
  101. package/fixtures/stocktake-quantity-cases.json +55 -0
  102. package/fixtures/surface.json +151 -8
  103. package/fixtures/v3/antworten/belege.json +1 -1
  104. package/fixtures/v3/antworten/belegmail.json +1 -1
  105. package/fixtures/v3/antworten/kasse-belege.json +1 -1
  106. package/fixtures/v3/antworten/kasse.json +2958 -3
  107. package/fixtures/v3/antworten/lager.json +1727 -3
  108. package/fixtures/v3/antworten/rechnungen.json +1 -1
  109. package/fixtures/v3/antworten/storno.json +1 -1
  110. package/fixtures/v3/stored/belege.json +1 -1
  111. package/fixtures/v3/stored/kasse.json +414 -3
  112. package/fixtures/v3/stored/rechnungen.json +1 -1
  113. package/fixtures/v3/v3-vokabular.json +475 -8
  114. package/fixtures/v3/zahlbetrag-faelle.json +1 -1
  115. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,128 @@ 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.8.0
8
+
9
+ Stocktake (Lager-Kern stage 3): `./inventory` gains the twelve stocktake
10
+ endpoints of the inventory API, `./pos` the five calls for counting at the
11
+ register. Reason: a business that counts its stock had only the old
12
+ "set the stock to this number" adjustment, which knows no expected quantity
13
+ at the time of the count, no several counts per article, no blind count and
14
+ no record. The backend now runs a stocktake as its own process (count, review,
15
+ recount, close in parts, PDF record with the state of the ledger seal), and
16
+ shops, the web register and the app need it through the package.
17
+
18
+ Additive at runtime; existing calls send the same bytes as in 1.7.1. As in
19
+ 1.6.0, the unions `InventoryEndpoint`, `InventoryErrorCode`,
20
+ `InventoryWarningCode`, `PosErrorCode`, `ApiCall`, `PublicCall`, `PosCall`,
21
+ `MessageKey`, `LabelKey` and the error code unions derived from the
22
+ authentication codes (see Contract below) gain members, so an exhaustive `switch` with a
23
+ `never` check needs the new cases.
24
+
25
+ - **Inventory API**: `createStocktake`, `listStocktakes`/`iterateStocktakes`
26
+ (without `updatedSince` the most recently changed first; with `updatedSince`
27
+ sorted by `updatedAt` ascending and inclusive, like `listArticles`),
28
+ `getStocktake` (with `progress.counted`), `listStocktakeItems`/
29
+ `iterateStocktakeItems` (`openOnly`), `recordStocktakeCount`,
30
+ `voidStocktakeCount`, `listStocktakeCounts`/`iterateStocktakeCounts`,
31
+ `reviewStocktake`, `recountStocktake`, `closeStocktake` (answers
32
+ `{ stocktake, warnings }`), `cancelStocktake` and `getStocktakePdf`. Models
33
+ `Stocktake`, `StocktakeItem`, `StocktakeCount` and their parts; the result
34
+ fields of a closed stocktake (`totals`, `warnings`, `seal`, `checksum`,
35
+ `inventoryAsOf`, `pdf`) and the expected quantity of an item
36
+ (`expectedQuantity`, `differenceQuantity`, `needsCheck` …) are present only
37
+ when the server sends them: never before `review` (blind), values only with
38
+ `costs`. A missing count, quantity, round or list of counters, and an actor
39
+ that is not an object, are response errors, never `0` or "nobody".
40
+ `StocktakeItem.serialNumbers` is present only when the server sends it (the
41
+ answer of counting and voiding omits it), never invented as `[]`.
42
+ - **The record**: `getStocktakePdf` returns `{ kind: 'pdf', pdf }` (the bytes,
43
+ up to 9 MiB) or `{ kind: 'download', download: { url, expiresAt, sizeBytes,
44
+ sha256, fileName, contentType } }` (a signed link for 15 minutes above
45
+ that). It reads the answer as bytes like the report downloads and accepts
46
+ either a PDF or a success envelope. The transport for it is public:
47
+ `createPdfOrDataTransport(options)` at the package root (types
48
+ `KasseneckPdfOrDataTransport`, `PdfOrData`), so the free function
49
+ `getStocktakePdf(transport, stocktakeId)` works outside the client too, as
50
+ `getInvoicePdf` does with `createBinaryTransport`.
51
+ - **Counting at the register** (`./pos`, register path only):
52
+ `listMyStocktakes`, `listMyStocktakeItems`, `listMyStocktakeCounts`,
53
+ `recordMyStocktakeCount`, `voidMyStocktakeCount`, with the same models.
54
+ `parseQuantityMilli(text, unit, rule?)` turns the typed quantity into
55
+ thousandths without floating point (comma or point, at most three decimals;
56
+ a point with exactly three digits after a non-zero whole part, `'1.000'`,
57
+ is ambiguous and `null` for every unit; pieces, by the stored `rule` of the
58
+ article or else by the unit, whole numbers without separator only); its
59
+ cases are shared with the Dart twin in the new
60
+ `fixtures/stocktake-quantity-cases.json`.
61
+ Messages and labels `stocktake.*` for the counting screen in the catalogue
62
+ (`fixtures/pos-texts.json`).
63
+ - **Idempotency and outcome**: every write takes `idempotencyKey` (refused
64
+ before sending without a valid one). `createStocktake`, `recordStocktakeCount`,
65
+ `voidStocktakeCount`, `reviewStocktake`, `recountStocktake`, `closeStocktake`,
66
+ `cancelStocktake`, `recordMyStocktakeCount` and `voidMyStocktakeCount` are in
67
+ `UNKNOWN_OUTCOME_CALLS`: after `outcome: 'unknown'` resend with the **same**
68
+ key, a count is then counted once. The eight reads are in the list without
69
+ effect.
70
+ - **Checked before sending**, nothing else: the key, `stocktakeId`,
71
+ `articleId`, `countId`, `locationId`, `scope` as an object, an integer
72
+ `quantity`, a non-empty `reason`, a non-empty `items` list with `articleId`
73
+ for a recount, `serialNumbers` as a list, `uncountedAsZero` as a boolean.
74
+ The limits (5000 items, 200 per recount, 200 counts per item and round) are
75
+ left to the server and exported as `STOCKTAKE_ITEMS_MAX`,
76
+ `STOCKTAKE_RECOUNT_ITEMS_MAX`, `STOCKTAKE_COUNTS_PER_ITEM_MAX`.
77
+ - **Catalogues**: `STOCKTAKE_STATUSES`, `STOCKTAKE_TYPES`,
78
+ `STOCKTAKE_SCOPE_TYPES`, `STOCKTAKE_CHECK_REASONS`,
79
+ `STOCKTAKE_NOT_BOOKED_REASONS`, `STOCKTAKE_SOURCES`, `STOCKTAKE_ACTOR_TYPES`,
80
+ `STOCKTAKE_INVENTORY_AS_OF`, each the contract catalogue in its order.
81
+ - **Errors**: `INVENTORY_ERROR_CODES` appends `stocktake_not_found`,
82
+ `stocktake_not_open`, `stocktake_closed`, `stocktake_not_in_review`,
83
+ `stocktake_closing`, `stocktake_location_busy` (with `data.stocktakeId`, read
84
+ by the new `inventoryBusyStocktakeId(error)`), `stocktake_review_running`,
85
+ `stocktake_recount_open`, `article_not_in_scope`, `article_not_tracked`,
86
+ `count_not_found`, `count_already_voided`, `serial_already_counted`,
87
+ `too_many_counts` and `stocktake_not_closed`; `INVENTORY_WARNING_CODES`
88
+ appends `defect_capped`, `uncounted_items`, `not_booked` and
89
+ `recount_uncounted`. `POS_ERROR_CODES` gains the codes of the five register
90
+ calls.
91
+ - **Contract**: `fixtures/v3/` pulled from the backend at the state of the
92
+ stocktake (keck #633, #634); the vocabulary of `./stored` regenerated
93
+ (fingerprint only). The contract also lists `app_check_missing` and
94
+ `app_check_invalid` among the authentication codes (App Check of the admin
95
+ app, refused before any handler); every list derived from them gains both:
96
+ `RECEIPT_ERROR_CODES`, `CANCELLATION_ERROR_CODES`,
97
+ `RECEIPT_EMAIL_ERROR_CODES`, `PAYMENT_ERROR_CODES`, `REGISTER_ERROR_CODES`,
98
+ `POS_ERROR_CODES`, `INVOICE_REQUEST_ERROR_CODES`,
99
+ `INVENTORY_REQUEST_ERROR_CODES` (and their unions) and
100
+ `PAYMENT_CALL_REJECTED_CODES`, so they stay `rejected` on the money calls
101
+ (the Dart twin's `paymentCallRejectedCodes` follows). No call of this
102
+ package can receive them: the backend sends them only on three endpoints
103
+ of the admin app (App Check), none of which is public `/v3`, on the register
104
+ path or wrapped here; the lists follow the contract so that their guards
105
+ stay derived from it.
106
+ - **Counts**: `PUBLIC_CALLS` 92 → 104, `POS_CALLS` 28 → 33 (27 register-only
107
+ names).
108
+ - **Twin check**: the source fingerprint of `functions-kasse/kasse-settings-core.js`
109
+ in `test/fixtures/stored-zwillinge.json` follows the backend, which only added
110
+ functions after `mische` (card provider and auto-logout per device); the
111
+ behaviour comparison of `./stored` against the backend is unchanged and green.
112
+
113
+ ## 1.7.1
114
+
115
+ `ReceiptSheetLines` pins `letter-spacing` and `word-spacing` to 0. Reason:
116
+ both are inherited, and the sheet is exactly `charsPerLine` × `1ch` wide with
117
+ `overflow: hidden` on every line. A page stylesheet that spaced its text (a
118
+ `tracking-*` utility, a `letter-spacing` on `body`) made every full line wider
119
+ than the sheet, and its last character was cut off. Same fix as the Dart twin
120
+ `KeckReceiptSheetWidget` in 10.6.1, where the Material 3 theme's 0.25 letter
121
+ spacing clipped "Kartenzahlung" to "Kartenzahlun".
122
+
123
+ The sheet's HTML gains `letter-spacing:0;word-spacing:0` in the `keck-blatt`
124
+ style, so the 264 `html.blatt.*` digests in `test/fixtures/druck-goldens.json`
125
+ are re-recorded; cross-check: with the addition removed each output matches its
126
+ old digest byte for byte. ESC/POS, ePOS, grid and sheet bytes are unchanged.
127
+ No public symbol changes.
128
+
7
129
  ## 1.7.0
8
130
 
9
131
  Register auto-logout: `Cashregister` gains the optional `autoLogout`
package/README.md CHANGED
@@ -893,6 +893,48 @@ errors such as `location_not_found` and `location_inactive` are in
893
893
  `RETURN_DISPOSITION_LABELS` gives the label of each return choice
894
894
  (`labelText(RETURN_DISPOSITION_LABELS.restock)` is "Zurück ins Lager").
895
895
 
896
+ **Counting a stocktake at the register.** Five calls on the register path,
897
+ permissions `stocktakeCount` (count, void own counts) and `stocktakeClose`
898
+ (also void other people's counts; the expected quantity from `review` on).
899
+ Creating, reviewing and closing happen in the panel or through the inventory
900
+ API, not at the register.
901
+
902
+ ```ts
903
+ import { listMyStocktakes, listMyStocktakeItems, recordMyStocktakeCount, voidMyStocktakeCount, parseQuantityMilli, messageText, labelText } from '@kreiseck/kasseneck-api/pos';
904
+
905
+ const [open] = await listMyStocktakes(transport, { locationId: 'haupt' });
906
+ if (!open) showHint(messageText('stocktake.none_open'));
907
+ const { items } = await listMyStocktakeItems(transport, { stocktakeId: open!.id, openOnly: true });
908
+ const quantity = parseQuantityMilli('0,25', 'kg', article.quantityRule); // 250; null for invalid input
909
+ if (quantity === null) showHint(messageText('stocktake.quantity_invalid'));
910
+ const key = crypto.randomUUID(); // keep it for "Erneut senden"
911
+ const { count, item } = await recordMyStocktakeCount(transport, {
912
+ idempotencyKey: key, stocktakeId: open!.id, articleId: items[0]!.articleId, quantity: quantity!,
913
+ });
914
+ await voidMyStocktakeCount(transport, { idempotencyKey: `${key}-void`, stocktakeId: open!.id, countId: count.id, reason: 'Doppelt gezählt' });
915
+ ```
916
+
917
+ `parseQuantityMilli(text, unit, rule?)` turns the typed text into thousandths
918
+ without floating point: comma or point as decimal separator, at most three
919
+ decimals (trailing zeros do not count). A point followed by exactly three
920
+ digits after a non-zero whole part (`'1.000'`, `'12.500'`) is `null` for every
921
+ unit: in Austrian notation it is a thousands separator, on a keypad a decimal
922
+ point, and a factor of 1000 would be booked as a difference; with a comma it
923
+ is unambiguous (`'1,000'` is 1000, i.e. one unit). Pieces take whole numbers
924
+ without any separator only. Whether an article is counted in pieces comes
925
+ from `rule`, the stored quantity rule of the article (`PosArticle.quantityRule`,
926
+ `'piece'` or `'decimal'`), without it from the default of the unit
927
+ (`quantityRuleForUnit`: `Stk`, `g`, `ml` …, also when the unit is missing);
928
+ pass `'piece'` for a serial-number article. Anything else is `null`. Its cases are shared with the Dart twin in
929
+ `fixtures/stocktake-quantity-cases.json`. Counting works online only; after
930
+ `outcome: 'unknown'` resend the **same** count with the **same** key
931
+ (`labelText('stocktake.resend')`), it is counted exactly once. The texts of the
932
+ counting screen are in the catalogue as `stocktake.*`
933
+ (`messageText('stocktake.counted', { name, quantity })`,
934
+ `labelText('stocktake.progress', { counted, total })`); the error codes
935
+ (`stocktake_not_open`, `article_not_in_scope`, `count_already_voided` …) are in
936
+ `POS_ERROR_CODES`.
937
+
896
938
  `stockViewOf(perms)` (also exported from the package root) tells whether a
897
939
  register user may see locations and quantities: a missing `stockView` counts
898
940
  as granted, only an explicit `false` blocks it, and the other stock
@@ -1483,7 +1525,8 @@ ones made at the register in the shop. Since 1.5.0 also the write side:
1483
1525
  create and update articles, book goods receipts, transfers, losses and
1484
1526
  condition changes, and reserve stock at checkout (see
1485
1527
  [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
1528
+ groups (sizes, colours, see [Variants](#variants)); since 1.8.0 stocktakes
1529
+ (see [Stocktake](#stocktake)). Uses the `api_key` of
1487
1530
  the account and belongs on a **server**. Reading needs the module `lager`,
1488
1531
  writing additionally the account switch *Lager-API schreiben*, which Kasseneck
1489
1532
  turns on (always on in the test environment, `kr_test_…`; otherwise
@@ -1863,6 +1906,131 @@ function onDelivery(rawBody: Buffer) {
1863
1906
  `listArticles({ variantGroupId, updatedSince })` are sorted by `updatedAt`
1864
1907
  ascending and include the boundary.
1865
1908
 
1909
+ ### Stocktake
1910
+
1911
+ A stocktake counts the stock of one location: create it (scope, key date or
1912
+ perpetual, blind by default), count (several counts of the same article are
1913
+ added up, a wrong one is voided with a reason), review (only now the expected
1914
+ quantity and the difference appear), recount single items, close (books one
1915
+ movement `stocktake` per item and stores the stocktake record as PDF) or
1916
+ cancel. The same flow runs in the panel and, for counting, at the register
1917
+ (`./pos`, below). All writes, counting included, take an `idempotencyKey`
1918
+ and the account switch *Lager-API schreiben*; values (`…Cents`, `…Micros`)
1919
+ and the PDF with values need `costs`.
1920
+
1921
+ Example: Bäckerei Kornblum has 40 Kornspitz at the location `haupt`. At
1922
+ 10:00:00 the register sells 3, the stock movement is booked a few seconds
1923
+ late at 10:00:03. At 10:00:02 Livia Lindmayr counts 37 on the shelf.
1924
+
1925
+ ```ts
1926
+ import { createInventoryClient, inventoryBusyStocktakeId, isInventoryError } from '@kreiseck/kasseneck-api/inventory';
1927
+ import { parseQuantityMilli } from '@kreiseck/kasseneck-api/pos';
1928
+
1929
+ const inventory = createInventoryClient({ apiKey: process.env.KASSENECK_API_KEY! });
1930
+
1931
+ // 1. One open stocktake per location; reuse it if there is one.
1932
+ let stocktakeId: string;
1933
+ try {
1934
+ const created = await inventory.createStocktake({
1935
+ idempotencyKey: 'shop-stocktake-haupt-2026-12',
1936
+ locationId: 'haupt',
1937
+ scope: { type: 'groups', groupIds: ['gebaeck'] },
1938
+ type: 'key_date',
1939
+ keyDate: '2026-12-31', // blind: true is the default
1940
+ });
1941
+ stocktakeId = created.id; // status: 'counting'
1942
+ } catch (error) {
1943
+ const busy = inventoryBusyStocktakeId(error);
1944
+ if (!busy) throw error;
1945
+ stocktakeId = busy;
1946
+ }
1947
+
1948
+ // 2. Count. Quantities in thousandths: "37" pieces -> 37000, "0,25" kg -> 250.
1949
+ const kornspitz = await inventory.lookupArticleByCode('9001234567896');
1950
+ const { item } = await inventory.recordStocktakeCount({
1951
+ idempotencyKey: 'shelf-a-kornspitz-1', // after outcome 'unknown': resend with the SAME key
1952
+ stocktakeId,
1953
+ articleId: kornspitz.id,
1954
+ quantity: parseQuantityMilli('37', kornspitz.unit)!,
1955
+ });
1956
+ // item.quantity: 37000 (sum of the round), no expectedQuantity while counting
1957
+
1958
+ // 3. Review: computed in the background, poll until complete.
1959
+ await inventory.reviewStocktake({ idempotencyKey: 'review-1', stocktakeId });
1960
+ let stocktake = await inventory.getStocktake(stocktakeId);
1961
+ while (!stocktake.review?.complete) {
1962
+ await new Promise((r) => setTimeout(r, 2000));
1963
+ stocktake = await inventory.getStocktake(stocktakeId);
1964
+ }
1965
+ for await (const it of inventory.iterateStocktakeItems({ stocktakeId })) {
1966
+ // Kornspitz: expectedQuantity 37000, differenceQuantity 0 (the sale at
1967
+ // 10:00:00 was booked after the count, but it happened before it)
1968
+ if (it.needsCheck) console.log(it.articleId, it.checkReasons);
1969
+ }
1970
+
1971
+ // 4. Close (books in parts in the background), then fetch the record.
1972
+ const { warnings } = await inventory.closeStocktake({ idempotencyKey: 'close-1', stocktakeId });
1973
+ // … poll getStocktake until status === 'closed' …
1974
+ const pdf = await inventory.getStocktakePdf(stocktakeId);
1975
+ if (pdf.kind === 'pdf') await save('stocktake.pdf', pdf.pdf);
1976
+ else await download(pdf.download.url, pdf.download.sha256); // over 9 MiB: signed link, 15 minutes
1977
+ ```
1978
+
1979
+ - **Reference time.** The expected quantity of an item is the stock at the
1980
+ time of its last count (server time; the device clock never counts), taken
1981
+ from the stock ledger, including sales that happened before the count but
1982
+ were booked after it (within 3 days). In the example the expected quantity
1983
+ is 37, the difference 0, and closing changes nothing. Had the 3 Kornspitz
1984
+ been sold at 10:00:05, after the count, the expected quantity would be 40
1985
+ and the difference −3: closing books −3 on today's stock (37 after the
1986
+ sale), which leaves 34. Each piece is deducted exactly once.
1987
+ - **Blind.** Before `review` no response carries an expected quantity, a
1988
+ difference or `needsCheck`; the fields are absent, not `null`. With
1989
+ `blind: false` the items carry `bookStockNow` while counting: today's book
1990
+ stock, never the expected quantity at the reference time.
1991
+ - **Counting.** `quantity` in thousandths, `0` means counted empty. Pieces
1992
+ and serial-number articles take whole pieces only, serial-number articles
1993
+ exactly one serial number per piece (`serial_required`,
1994
+ `serial_already_counted`). Scope `all` adds a tracked article that was not
1995
+ listed (`addedLater`); scopes `groups` and `articles` answer
1996
+ `article_not_in_scope`. While counting or recounting, `voidStocktakeCount`
1997
+ with a reason takes a count back; the item is summed up again.
1998
+ - **Recount.** In `review`, `recountStocktake({ items, reason })` starts a new
1999
+ round for the named items (old counts stay, but no longer count); count
2000
+ them, then call `reviewStocktake` again. `closeStocktake` refuses with
2001
+ `stocktake_recount_open` while recounts are open.
2002
+ - **Closing.** Only from `review` (`stocktake_not_in_review`) and only once the
2003
+ review has finished computing: while `review.complete` is `false`,
2004
+ `closeStocktake` and `recountStocktake` answer `stocktake_review_running`
2005
+ (poll `getStocktake` and try again). Uncounted items
2006
+ are not booked and appear as "not counted" in the record; with
2007
+ `uncountedAsZero: true` they are booked as 0. The answer usually has
2008
+ `status: 'closing'` with `closing.parts` and `closing.bookedParts`; calling
2009
+ `closeStocktake` again while it is closing resumes it. After `closed`, the
2010
+ head carries `totals`, `warnings` (`uncounted_items`, `not_booked`,
2011
+ `defect_capped`), `seal` (the state of the stock ledger seal), `checksum`
2012
+ and `pdf`; `closeStocktake` itself may warn `recount_uncounted`. Each
2013
+ booking reaches webhooks as `stock.changed` with `cause: 'stocktake'`;
2014
+ there is no `stocktake.*` event.
2015
+ - **Lists.** `listStocktakes()` without `updatedSince` returns the most
2016
+ recently changed first (an open stocktake is not necessarily on top; filter
2017
+ with `status`). With `updatedSince` it is sorted by `updatedAt` ascending
2018
+ and includes the boundary, like `listArticles`: remember the largest
2019
+ `updatedAt` and ask again. `listStocktakeItems({ stocktakeId, openOnly })`
2020
+ and `listStocktakeCounts({ stocktakeId, articleId })` page with `cursor`;
2021
+ the `iterate…` variants follow `nextCursor` in the same order.
2022
+ - **The record.** `getStocktakePdf` exists only after closing
2023
+ (`stocktake_not_closed`, also for a cancelled stocktake). With `costs` it is
2024
+ the version with values, otherwise the one with quantities only. Up to
2025
+ 9 MiB the file comes in the answer (`kind: 'pdf'`), above that as a signed
2026
+ link for 15 minutes (`kind: 'download'`, with `sizeBytes` and `sha256`; check
2027
+ the downloaded file against `sha256`).
2028
+ - **Limits.** At most `STOCKTAKE_ITEMS_MAX` (5000) items per stocktake
2029
+ (`too_many_positions`; count larger stocks in several stocktakes one after
2030
+ another), `STOCKTAKE_RECOUNT_ITEMS_MAX` (200) items per recount and
2031
+ `STOCKTAKE_COUNTS_PER_ITEM_MAX` (200) counts per item and round
2032
+ (`too_many_counts`). The server checks them, not the client.
2033
+
1866
2034
  ## Migrating from 0.x
1867
2035
 
1868
2036
  1.0 is one breaking step: the wire, the exported names and the contract files
@@ -1946,6 +2114,7 @@ in machine form what both sides agreed on, among them:
1946
2114
  | `pos-message-cases.json` | error cases and the message each one must show in both registers | by hand |
1947
2115
  | `receipt-due-generated.json` | 1206 amounts due computed by the backend's own code, the reference for `receiptDueCents` | `node scripts/v3-zahlbetrag-generieren.mjs` |
1948
2116
  | `receipt-due-errors.json` | input the amount due cannot be computed from, with the `reason` of the `ReceiptDueError` each one throws | by hand |
2117
+ | `stocktake-quantity-cases.json` | typed quantities and the thousandths `parseQuantityMilli` makes of them (or `null`), per unit | by hand |
1949
2118
  | `v3/` | the backend's `/v3` contract (vocabulary, response cases, stored cases, amounts due), copied byte for byte | `node scripts/v3-vertrag-holen.mjs` |
1950
2119
  | `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` |
1951
2120
 
@@ -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, 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"];
15
+ import type { PdfOrData, 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", "createStocktake", "listStocktakes", "getStocktake", "listStocktakeItems", "recordStocktakeCount", "voidStocktakeCount", "listStocktakeCounts", "reviewStocktake", "recountStocktake", "closeStocktake", "cancelStocktake", "getStocktakePdf", "listMyStocktakes", "listMyStocktakeItems", "listMyStocktakeCounts", "recordMyStocktakeCount", "voidMyStocktakeCount"];
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,14 +21,14 @@ 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", "createVariantGroup", "updateVariantGroup", "getVariantGroup", "listVariantGroups", "addVariant"];
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", "createStocktake", "listStocktakes", "getStocktake", "listStocktakeItems", "recordStocktakeCount", "voidStocktakeCount", "listStocktakeCounts", "reviewStocktake", "recountStocktake", "closeStocktake", "cancelStocktake", "getStocktakePdf"];
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
28
28
  * `endpoints.register`. Sechs davon sind zugleich oeffentlich; die Kasse ruft
29
29
  * sie trotzdem ueber diesen Weg (ein Dialekt je Client, Nachtrag §5.2).
30
30
  */
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"];
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", "listMyStocktakes", "listMyStocktakeItems", "listMyStocktakeCounts", "recordMyStocktakeCount", "voidMyStocktakeCount"];
32
32
  export type PublicCall = typeof PUBLIC_CALLS[number];
33
33
  export type PosCall = typeof POS_CALLS[number];
34
34
  /**
@@ -49,7 +49,7 @@ export type PosCall = typeof POS_CALLS[number];
49
49
  * [ALL_CALLS], [PUBLIC_CALLS] und [POS_CALLS] steht entweder hier oder in
50
50
  * [CALLS_WITHOUT_EFFECT] (Waechter `test/ausgang-einordnung.test.ts`).
51
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"];
52
+ export declare const UNKNOWN_OUTCOME_CALLS: readonly ["activateCashregister", "addVariant", "cancelInvoice", "cancelReceipt", "cancelStocktake", "changeStockCondition", "closeStocktake", "createArticle", "createCreditNote", "createCustomer", "createCustomerCashregister", "createInvoiceItem", "createPartnerCustomer", "createPartnerWebhook", "createPrintJob", "createReceipt", "createReservation", "createStocktake", "createVariantGroup", "createWebhook", "deactivateArticle", "deletePartnerWebhook", "deleteWebhook", "extendReservation", "financeWebService", "hobexPayApi", "hobexRefundApi", "issueInvoice", "pairRegisterDevice", "receiveGoods", "recordInvoicePayment", "recordMyStocktakeCount", "recordStockLoss", "recordStocktakeCount", "recountStocktake", "releaseReservation", "reportCustomerContract", "requestCustomerSignature", "reverseStockMovement", "reviewStocktake", "revokeCustomerMandate", "rotatePartnerWebhookSecret", "rotateWebhookSecret", "sendPartnerCustomerFonLink", "sendPartnerWebhookTest", "sendReceiptEmail", "sendWebhookTest", "setCustomerMandate", "setMyCashregisterStockLocation", "setMyKasseLogo", "setMyKasseSettings", "setMyRegisterDeviceSettings", "stripeCaptureIntent", "transferStock", "unpairRegisterDevice", "updateArticle", "updateCustomer", "updateInvoiceItem", "updatePartnerWebhook", "updateVariantGroup", "updateWebhook", "voidMyStocktakeCount", "voidStocktakeCount", "withdrawInvoiceItem"];
53
53
  /** Warum ein Aufruf keine Wirkung hat, die ein Zeitlimit offen lassen koennte. */
54
54
  export type CallWithoutEffectReason = 'read' | 'repeatable';
55
55
  /**
@@ -85,10 +85,10 @@ export declare const CALLS_WITHOUT_EFFECT: Readonly<Record<string, CallWithoutEf
85
85
  * absetzt, sagt es mit `hasEffect: true`.
86
86
  */
87
87
  export declare function isUnknownOutcomeCall(name: string): boolean;
88
- /** Einer der 28 Aufrufe des Kassenwegs. */
88
+ /** Einer der 33 Aufrufe des Kassenwegs. */
89
89
  export declare function isPosCall(name: string): boolean;
90
90
  /**
91
- * Nur ueber den Kassenweg erreichbar (22 Namen): unter `api.kasseneck.at/v3`
91
+ * Nur ueber den Kassenweg erreichbar (27 Namen): unter `api.kasseneck.at/v3`
92
92
  * gibt es sie nicht. Der Transport schickt sie darum ohne eigene Basis an
93
93
  * [POS_BASE_URL] statt an die oeffentliche Basis.
94
94
  */
@@ -97,3 +97,5 @@ export declare function isPosOnlyCall(name: string): boolean;
97
97
  export type InternerTransport = <T = unknown>(functionName: ApiCall, params?: Record<string, unknown>, extraBodyFields?: TransportBodyFields, secretParams?: readonly string[], options?: TransportCallOptions) => Promise<T>;
98
98
  /** Wie [KasseneckBinaryTransport], nur mit bekanntem Aufrufnamen. */
99
99
  export type InternerBinaerTransport = (functionName: ApiCall, params?: Record<string, unknown>) => Promise<Uint8Array>;
100
+ /** Wie `createPdfOrDataTransport` (transport.ts), nur mit bekanntem Aufrufnamen. */
101
+ export type InternerPdfOderDatenTransport = (functionName: ApiCall, params?: Record<string, unknown>) => Promise<PdfOrData>;
@@ -111,6 +111,25 @@ exports.ALL_CALLS = [
111
111
  'getVariantGroup',
112
112
  'listVariantGroups',
113
113
  'addVariant',
114
+ // Lager-API Inventur (Lager-Kern Stufe 3, ./inventory)
115
+ 'createStocktake',
116
+ 'listStocktakes',
117
+ 'getStocktake',
118
+ 'listStocktakeItems',
119
+ 'recordStocktakeCount',
120
+ 'voidStocktakeCount',
121
+ 'listStocktakeCounts',
122
+ 'reviewStocktake',
123
+ 'recountStocktake',
124
+ 'closeStocktake',
125
+ 'cancelStocktake',
126
+ 'getStocktakePdf',
127
+ // Inventur zaehlen an der Kasse (Lager-Kern Stufe 3, ./pos)
128
+ 'listMyStocktakes',
129
+ 'listMyStocktakeItems',
130
+ 'listMyStocktakeCounts',
131
+ 'recordMyStocktakeCount',
132
+ 'voidMyStocktakeCount',
114
133
  ];
115
134
  /**
116
135
  * Die oeffentlichen Endpunkte unter `/v3` (`https://api.kasseneck.at/v3/<name>`),
@@ -217,6 +236,19 @@ exports.PUBLIC_CALLS = [
217
236
  'getVariantGroup',
218
237
  'listVariantGroups',
219
238
  'addVariant',
239
+ // Lager-API Inventur (Lager-Kern Stufe 3), umhuellt in `./inventory`.
240
+ 'createStocktake',
241
+ 'listStocktakes',
242
+ 'getStocktake',
243
+ 'listStocktakeItems',
244
+ 'recordStocktakeCount',
245
+ 'voidStocktakeCount',
246
+ 'listStocktakeCounts',
247
+ 'reviewStocktake',
248
+ 'recountStocktake',
249
+ 'closeStocktake',
250
+ 'cancelStocktake',
251
+ 'getStocktakePdf',
220
252
  ];
221
253
  /**
222
254
  * Die Endpunkte des Kassenwegs (`https://kasse.kasseneck.at/api/v3/<name>`,
@@ -254,6 +286,12 @@ exports.POS_CALLS = [
254
286
  'listMyStockLocations',
255
287
  'listMyStock',
256
288
  'setMyCashregisterStockLocation',
289
+ // Inventur zaehlen (Lager-Kern Stufe 3): nur ueber den Kassenweg.
290
+ 'listMyStocktakes',
291
+ 'listMyStocktakeItems',
292
+ 'listMyStocktakeCounts',
293
+ 'recordMyStocktakeCount',
294
+ 'voidMyStocktakeCount',
257
295
  ];
258
296
  /**
259
297
  * Die Aufrufe **mit Wirkung**, alphabetisch: sie legen etwas an, aendern oder
@@ -278,7 +316,9 @@ exports.UNKNOWN_OUTCOME_CALLS = [
278
316
  'addVariant',
279
317
  'cancelInvoice',
280
318
  'cancelReceipt',
319
+ 'cancelStocktake',
281
320
  'changeStockCondition',
321
+ 'closeStocktake',
282
322
  'createArticle',
283
323
  'createCreditNote',
284
324
  'createCustomer',
@@ -289,6 +329,7 @@ exports.UNKNOWN_OUTCOME_CALLS = [
289
329
  'createPrintJob',
290
330
  'createReceipt',
291
331
  'createReservation',
332
+ 'createStocktake',
292
333
  'createVariantGroup',
293
334
  'createWebhook',
294
335
  'deactivateArticle',
@@ -302,11 +343,15 @@ exports.UNKNOWN_OUTCOME_CALLS = [
302
343
  'pairRegisterDevice',
303
344
  'receiveGoods',
304
345
  'recordInvoicePayment',
346
+ 'recordMyStocktakeCount',
305
347
  'recordStockLoss',
348
+ 'recordStocktakeCount',
349
+ 'recountStocktake',
306
350
  'releaseReservation',
307
351
  'reportCustomerContract',
308
352
  'requestCustomerSignature',
309
353
  'reverseStockMovement',
354
+ 'reviewStocktake',
310
355
  'revokeCustomerMandate',
311
356
  'rotatePartnerWebhookSecret',
312
357
  'rotateWebhookSecret',
@@ -328,6 +373,8 @@ exports.UNKNOWN_OUTCOME_CALLS = [
328
373
  'updatePartnerWebhook',
329
374
  'updateVariantGroup',
330
375
  'updateWebhook',
376
+ 'voidMyStocktakeCount',
377
+ 'voidStocktakeCount',
331
378
  'withdrawInvoiceItem',
332
379
  ];
333
380
  /**
@@ -382,6 +429,8 @@ exports.CALLS_WITHOUT_EFFECT = Object.freeze({
382
429
  getReportV2: 'read',
383
430
  getReservation: 'read',
384
431
  getStock: 'read',
432
+ getStocktake: 'read',
433
+ getStocktakePdf: 'read',
385
434
  getVariantGroup: 'read',
386
435
  hobexGetStatus: 'read',
387
436
  listArticles: 'read',
@@ -397,6 +446,9 @@ exports.CALLS_WITHOUT_EFFECT = Object.freeze({
397
446
  listMyReceipts: 'read',
398
447
  listMyStock: 'read',
399
448
  listMyStockLocations: 'read',
449
+ listMyStocktakeCounts: 'read',
450
+ listMyStocktakeItems: 'read',
451
+ listMyStocktakes: 'read',
400
452
  listMyTipRecipients: 'read',
401
453
  listPartnerCustomers: 'read',
402
454
  listPartnerWebhookDeliveries: 'read',
@@ -406,6 +458,9 @@ exports.CALLS_WITHOUT_EFFECT = Object.freeze({
406
458
  listReservations: 'read',
407
459
  listStock: 'read',
408
460
  listStockMovements: 'read',
461
+ listStocktakeCounts: 'read',
462
+ listStocktakeItems: 'read',
463
+ listStocktakes: 'read',
409
464
  listVariantGroups: 'read',
410
465
  listWebhookDeliveries: 'read',
411
466
  listWebhooks: 'read',
@@ -429,12 +484,12 @@ function isUnknownOutcomeCall(name) {
429
484
  const OEFFENTLICH = new Set(exports.PUBLIC_CALLS);
430
485
  const KASSENWEG = new Set(exports.POS_CALLS);
431
486
  const NUR_KASSE = new Set(exports.POS_CALLS.filter((name) => !OEFFENTLICH.has(name)));
432
- /** Einer der 28 Aufrufe des Kassenwegs. */
487
+ /** Einer der 33 Aufrufe des Kassenwegs. */
433
488
  function isPosCall(name) {
434
489
  return KASSENWEG.has(name);
435
490
  }
436
491
  /**
437
- * Nur ueber den Kassenweg erreichbar (22 Namen): unter `api.kasseneck.at/v3`
492
+ * Nur ueber den Kassenweg erreichbar (27 Namen): unter `api.kasseneck.at/v3`
438
493
  * gibt es sie nicht. Der Transport schickt sie darum ohne eigene Basis an
439
494
  * [POS_BASE_URL] statt an die oeffentliche Basis.
440
495
  */
@@ -280,6 +280,8 @@ exports.PAYMENT_CALL_REJECTED_CODES = Object.freeze([
280
280
  'account_not_found',
281
281
  'live_not_enabled',
282
282
  'api_not_approved', // Live-API ohne Freigabe (Entwicklerbereich): vor dem Handler abgewiesen
283
+ 'app_check_missing', // Admin-App ohne bzw. mit ungueltigem App Check: vor dem Handler abgewiesen
284
+ 'app_check_invalid',
283
285
  'unauthorized',
284
286
  'mfa_required',
285
287
  'user_verification_failed',
@@ -1,5 +1,5 @@
1
1
  export { type AuthCredentials, type KasseneckAuth, type ApiKeyAuthOptions, type RegisterUserAuthOptions, apiKeyAuth, registerUserAuth, } from './auth.js';
2
- export { DEFAULT_BASE_URL, POS_BASE_URL, DEFAULT_TIMEOUT_MS, type HttpResponseLike, type HttpRequestInit, type FetchLike, type TransportOptions, type TransportBodyFields, type TransportCallOptions, type KasseneckTransport, type KasseneckBinaryTransport, createTransport, createBinaryTransport, } from './transport.js';
2
+ export { DEFAULT_BASE_URL, POS_BASE_URL, DEFAULT_TIMEOUT_MS, type HttpResponseLike, type HttpRequestInit, type FetchLike, type TransportOptions, type TransportBodyFields, type TransportCallOptions, type KasseneckTransport, type KasseneckBinaryTransport, type KasseneckPdfOrDataTransport, type PdfOrData, createTransport, createBinaryTransport, createPdfOrDataTransport, } from './transport.js';
3
3
  export { ALL_CALLS, type ApiCall, PUBLIC_CALLS, POS_CALLS, UNKNOWN_OUTCOME_CALLS, type PublicCall, type PosCall, } from './aufrufe.js';
4
4
  export { 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, } from './errors.js';
5
5
  export { type ReceiptCommonOptions, type SellReceiptOptions, type TipOptions, type TipRecipientShare, type CancelReceiptOptions, type ReceiptWithCompany, sellReceipt, sellReceiptWithCompany, paymentsExpectedCents, cardRefundReference, receiptLayoutFromResult, cancelReceipt, type CancelReceiptResult, zeroReceipt, getReceipt, getReceiptWithCompany, generateFullReceiptId, getFirstReceiptDate, listMyReceipts, type ListMyReceiptsOptions, type ReceiptList, type ReceiptListStats, checkVoucherCombinationError, type SendReceiptEmailOptions, type SendReceiptEmailResult, sendReceiptEmail, } from './receipts.js';
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createKasseneckApi = exports.getSignatureStatus = exports.getCashboxStatus = exports.getReportV2 = exports.downloadMonthlyReport = exports.downloadDailyReport = exports.listMyCashregisters = exports.sendReceiptEmail = exports.checkVoucherCombinationError = 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.UNKNOWN_OUTCOME_CALLS = exports.POS_CALLS = exports.PUBLIC_CALLS = exports.ALL_CALLS = exports.createBinaryTransport = exports.createTransport = exports.DEFAULT_TIMEOUT_MS = exports.POS_BASE_URL = exports.DEFAULT_BASE_URL = exports.registerUserAuth = exports.apiKeyAuth = void 0;
3
+ exports.createKasseneckApi = exports.getSignatureStatus = exports.getCashboxStatus = exports.getReportV2 = exports.downloadMonthlyReport = exports.downloadDailyReport = exports.listMyCashregisters = exports.sendReceiptEmail = exports.checkVoucherCombinationError = 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.UNKNOWN_OUTCOME_CALLS = exports.POS_CALLS = exports.PUBLIC_CALLS = exports.ALL_CALLS = exports.createPdfOrDataTransport = exports.createBinaryTransport = exports.createTransport = exports.DEFAULT_TIMEOUT_MS = exports.POS_BASE_URL = exports.DEFAULT_BASE_URL = exports.registerUserAuth = exports.apiKeyAuth = void 0;
4
4
  var auth_js_1 = require("./auth.js");
5
5
  Object.defineProperty(exports, "apiKeyAuth", { enumerable: true, get: function () { return auth_js_1.apiKeyAuth; } });
6
6
  Object.defineProperty(exports, "registerUserAuth", { enumerable: true, get: function () { return auth_js_1.registerUserAuth; } });
@@ -10,6 +10,7 @@ Object.defineProperty(exports, "POS_BASE_URL", { enumerable: true, get: function
10
10
  Object.defineProperty(exports, "DEFAULT_TIMEOUT_MS", { enumerable: true, get: function () { return transport_js_1.DEFAULT_TIMEOUT_MS; } });
11
11
  Object.defineProperty(exports, "createTransport", { enumerable: true, get: function () { return transport_js_1.createTransport; } });
12
12
  Object.defineProperty(exports, "createBinaryTransport", { enumerable: true, get: function () { return transport_js_1.createBinaryTransport; } });
13
+ Object.defineProperty(exports, "createPdfOrDataTransport", { enumerable: true, get: function () { return transport_js_1.createPdfOrDataTransport; } });
13
14
  var aufrufe_js_1 = require("./aufrufe.js");
14
15
  Object.defineProperty(exports, "ALL_CALLS", { enumerable: true, get: function () { return aufrufe_js_1.ALL_CALLS; } });
15
16
  Object.defineProperty(exports, "PUBLIC_CALLS", { enumerable: true, get: function () { return aufrufe_js_1.PUBLIC_CALLS; } });