@kreiseck/kasseneck-api 1.3.0 → 1.5.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.
- package/CHANGELOG.md +176 -0
- package/README.md +293 -1
- package/dist/cjs/client/aufrufe.d.ts +2 -2
- package/dist/cjs/client/aufrufe.js +58 -0
- package/dist/cjs/inventory/api.d.ts +61 -0
- package/dist/cjs/inventory/api.js +54 -0
- package/dist/cjs/inventory/auth.d.ts +17 -0
- package/dist/cjs/inventory/auth.js +27 -0
- package/dist/cjs/inventory/endpunkte.d.ts +91 -0
- package/dist/cjs/inventory/endpunkte.js +290 -0
- package/dist/cjs/inventory/fehler.d.ts +43 -0
- package/dist/cjs/inventory/fehler.js +66 -0
- package/dist/cjs/inventory/index.d.ts +21 -0
- package/dist/cjs/inventory/index.js +94 -0
- package/dist/cjs/inventory/lesen.d.ts +45 -0
- package/dist/cjs/inventory/lesen.js +424 -0
- package/dist/cjs/inventory/schreiben.d.ts +83 -0
- package/dist/cjs/inventory/schreiben.js +315 -0
- package/dist/cjs/inventory/typen.d.ts +679 -0
- package/dist/cjs/inventory/typen.js +14 -0
- package/dist/cjs/inventory/vertrag.d.ts +136 -0
- package/dist/cjs/inventory/vertrag.js +291 -0
- package/dist/cjs/inventory/webhook.d.ts +52 -0
- package/dist/cjs/inventory/webhook.js +116 -0
- package/dist/cjs/invoice/index.d.ts +2 -2
- package/dist/cjs/invoice/index.js +2 -1
- package/dist/cjs/invoice/typen.d.ts +46 -2
- package/dist/cjs/invoice/vertrag.d.ts +19 -5
- package/dist/cjs/invoice/vertrag.js +37 -5
- package/dist/cjs/stored/vokabular.d.ts +1 -1
- package/dist/cjs/stored/vokabular.js +5 -3
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/client/aufrufe.d.ts +2 -2
- package/dist/esm/client/aufrufe.js +58 -0
- package/dist/esm/inventory/api.d.ts +61 -0
- package/dist/esm/inventory/api.js +51 -0
- package/dist/esm/inventory/auth.d.ts +17 -0
- package/dist/esm/inventory/auth.js +24 -0
- package/dist/esm/inventory/endpunkte.d.ts +91 -0
- package/dist/esm/inventory/endpunkte.js +267 -0
- package/dist/esm/inventory/fehler.d.ts +43 -0
- package/dist/esm/inventory/fehler.js +57 -0
- package/dist/esm/inventory/index.d.ts +21 -0
- package/dist/esm/inventory/index.js +20 -0
- package/dist/esm/inventory/lesen.d.ts +45 -0
- package/dist/esm/inventory/lesen.js +404 -0
- package/dist/esm/inventory/schreiben.d.ts +83 -0
- package/dist/esm/inventory/schreiben.js +298 -0
- package/dist/esm/inventory/typen.d.ts +679 -0
- package/dist/esm/inventory/typen.js +13 -0
- package/dist/esm/inventory/vertrag.d.ts +136 -0
- package/dist/esm/inventory/vertrag.js +288 -0
- package/dist/esm/inventory/webhook.d.ts +52 -0
- package/dist/esm/inventory/webhook.js +107 -0
- package/dist/esm/invoice/index.d.ts +2 -2
- package/dist/esm/invoice/index.js +1 -1
- package/dist/esm/invoice/typen.d.ts +46 -2
- package/dist/esm/invoice/vertrag.d.ts +19 -5
- package/dist/esm/invoice/vertrag.js +36 -4
- package/dist/esm/stored/vokabular.d.ts +1 -1
- package/dist/esm/stored/vokabular.js +5 -3
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/fixtures/hobex-hps-codes.json +1 -1
- package/fixtures/invoice-api-examples/credit-error-reservation.json +26 -0
- package/fixtures/invoice-api-examples/issue-error-reservation-article.json +25 -0
- package/fixtures/invoice-api-examples/issue-error-vat-id-risk.json +26 -0
- package/fixtures/invoice-api-examples/issue-reservation.json +24 -0
- package/fixtures/invoice-api-examples/issue-vat-id-risk.json +28 -0
- package/fixtures/invoice-api.schema.json +15 -2
- package/fixtures/invoice-texts.json +1 -1
- package/fixtures/pos-texts.json +1 -1
- package/fixtures/surface.json +306 -5
- package/fixtures/v3/README.md +9 -1
- package/fixtures/v3/antworten/belege.json +2 -2
- package/fixtures/v3/antworten/belegmail.json +2 -2
- package/fixtures/v3/antworten/kasse-belege.json +2 -2
- package/fixtures/v3/antworten/kasse.json +3 -3
- package/fixtures/v3/antworten/lager.json +3881 -0
- package/fixtures/v3/antworten/rechnungen.json +267 -44
- package/fixtures/v3/antworten/storno.json +2 -2
- package/fixtures/v3/stored/belege.json +2 -2
- package/fixtures/v3/stored/kasse.json +404 -4
- package/fixtures/v3/stored/rechnungen.json +174 -18
- package/fixtures/v3/v3-vokabular.json +1007 -20
- package/fixtures/v3/zahlbetrag-faelle.json +2 -2
- package/package.json +14 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,182 @@ 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.5.0
|
|
8
|
+
|
|
9
|
+
Inventory API, write side and reservations: `./inventory` gains the 13
|
|
10
|
+
endpoints of backend stage 5b, and the invoice API learns `reservationId`.
|
|
11
|
+
Reason: a shop that only reads stock still oversells. It has to hold the goods
|
|
12
|
+
at checkout, book what arrives and leaves, keep its articles in sync, and turn
|
|
13
|
+
the reservation into a sale when the order is invoiced, all with retries that
|
|
14
|
+
never book twice. The invoice API also catches up with the VAT ID check of
|
|
15
|
+
the backend (stage 4, in the 0.x line since 0.33.0).
|
|
16
|
+
|
|
17
|
+
Additive, no breaking change; existing calls send the same bytes as in 1.4.0.
|
|
18
|
+
New response fields are added to the models.
|
|
19
|
+
|
|
20
|
+
- **Articles**: `createArticle`, `updateArticle`, `deactivateArticle`, each
|
|
21
|
+
answering with the article as `getArticle` returns it. Fields: `name`,
|
|
22
|
+
`description`, `unitPriceCents`, `vatRate`, `unit`, `number`, `ean` (with
|
|
23
|
+
`ean` a foreign article with that code, otherwise the server assigns the next
|
|
24
|
+
own code), `groupId`, `revenueGroupId`, `stockTracked`, `stockLocationIds`,
|
|
25
|
+
`minStockByLocation`, `minStock` (legacy, triggers nothing), `stockKind`,
|
|
26
|
+
`purchasePriceMicros` (only with the permission `costs`), `externalIds` and
|
|
27
|
+
`metadata`. In an update `null` clears an optional field; `externalIds` and
|
|
28
|
+
`metadata` are replaced, `minStockByLocation` is merged per location.
|
|
29
|
+
- **Booking**: `receiveGoods`, `transferStock`, `recordStockLoss`,
|
|
30
|
+
`changeStockCondition`, `reverseStockMovement`, each answering with a
|
|
31
|
+
`StockOperation` (`operationId`, `movementIds`, `lotIds`, `warnings[]`).
|
|
32
|
+
`previewGoodsReceipt` is `receiveGoods` with `dryRun: true`: it books nothing,
|
|
33
|
+
needs no key (a `null` key counts as none and is not sent) and returns the
|
|
34
|
+
values only with the permission `costs`. `receiveGoods` with `dryRun: true`
|
|
35
|
+
is refused before sending: a preview answers without an operation, so it
|
|
36
|
+
goes through `previewGoodsReceipt`.
|
|
37
|
+
Transfers, losses and condition changes never overdraw (`exceeds_stock`).
|
|
38
|
+
Catalogs `STOCK_LOSS_REASONS`, `WITHDRAWAL_TYPES`, `LANDED_COST_TYPES`,
|
|
39
|
+
`LANDED_COST_ALLOCATIONS`; warnings `INVENTORY_WARNING_CODES`
|
|
40
|
+
(`insufficient_stock`, `below_minimum`, `return_exceeds_sale`,
|
|
41
|
+
`reservation_exceeded`) with `isInventoryWarningCode`. A warning is not an
|
|
42
|
+
error: the booking took effect.
|
|
43
|
+
- **Reservations**: `createReservation` (all or nothing, measured against
|
|
44
|
+
`available`; `insufficient_available` lists the missing positions in
|
|
45
|
+
`inventoryShortfalls(error)`), `extendReservation`, `releaseReservation` (all,
|
|
46
|
+
or per position), `getReservation`, `listReservations` and
|
|
47
|
+
`iterateReservations`. `Reservation` carries `status` (`active`, `redeemed`,
|
|
48
|
+
`released`, `expired`; `RESERVATION_STATUSES`), `reference`, `items[]` with
|
|
49
|
+
`quantity`, `redeemed`, `released`, and `expiresAt`. `expiresInMinutes` is
|
|
50
|
+
5 to 43,200.
|
|
51
|
+
- **Idempotency**: every write takes `idempotencyKey` (1 to 120 characters,
|
|
52
|
+
required in the type). The client refuses to send a write without a valid
|
|
53
|
+
key, because a write without one cannot be retried safely; it never trims or
|
|
54
|
+
shortens the key. Retry after a timeout with the **same** key: the call takes
|
|
55
|
+
effect once and returns the stored answer; the same key with other content
|
|
56
|
+
gives `idempotency_conflict`.
|
|
57
|
+
- **Checked before sending**, nothing else: the key, required ids, integer
|
|
58
|
+
quantities, amounts and prices (`1.5` is never 1.5 pieces), the range of
|
|
59
|
+
`expiresInMinutes`, an update without a field, an empty release list,
|
|
60
|
+
`receiveGoods` with `dryRun: true` (use `previewGoodsReceipt`). All of
|
|
61
|
+
these throw `KasseneckValidationError` with `scope: 'request'`.
|
|
62
|
+
- **Models**: `Article` gains `description`, `stockKind` and
|
|
63
|
+
`minStockByLocation` (missing on an older server: `null`, `null`, `{}`).
|
|
64
|
+
`StockMovement` gains `reservedDelta` (0 for every movement that is not a
|
|
65
|
+
reservation) and `stockAfter.reserved` (only on reservation movements,
|
|
66
|
+
otherwise `null`); the movement type `reservation` has `quantityDelta: 0`.
|
|
67
|
+
The minimum stock (`below_minimum`, `listStock({ belowMinimum })`,
|
|
68
|
+
`stock.below_minimum`) is now measured against `available = onHand −
|
|
69
|
+
reserved`, so a reservation alone can trigger it. A fractional value in any
|
|
70
|
+
of these fields throws `KasseneckValidationError` with `scope: 'response'`.
|
|
71
|
+
- **Events**: `reservation.expired`, `reservation.released` and
|
|
72
|
+
`reservation.redeemed` in `INVENTORY_WEBHOOK_EVENTS` and
|
|
73
|
+
`parseInventoryWebhookEvent`, carrying the reservation with its status
|
|
74
|
+
afterwards. `released` and `redeemed` fire on every effective release or
|
|
75
|
+
redemption, also a partial one (status stays `active`).
|
|
76
|
+
- **Errors**: `INVENTORY_ERROR_CODES` keeps its 12 codes in front and appends
|
|
77
|
+
the codes of the write side in contract order (`idempotency_key_required`,
|
|
78
|
+
`idempotency_conflict`, `exceeds_stock`, `code_taken`,
|
|
79
|
+
`external_id_taken`, `stock_kind_locked`, `article_inactive`,
|
|
80
|
+
`insufficient_available`, `reservation_not_found`,
|
|
81
|
+
`reservation_not_active` …).
|
|
82
|
+
- **Invoice API, reservations**: `issueInvoice` items take `reservationId`
|
|
83
|
+
(`IssueInvoiceItemInput`, needs `articleId`; not on credit notes, where the
|
|
84
|
+
server rejects it). New codes at the end of `INVOICE_ERROR_CODES`:
|
|
85
|
+
`reservation_not_found`, `reservation_mismatch`, `reservation_not_active`;
|
|
86
|
+
new notice `reservation_expired` (with `reservationId`): the invoice is
|
|
87
|
+
issued anyway and sells without the reservation. Three new examples in
|
|
88
|
+
`fixtures/invoice-api-examples/`.
|
|
89
|
+
- **Invoice API, VAT ID check**: an invoice without VAT that relies on the
|
|
90
|
+
customer's VAT ID (intra-Community supply, reverse charge) is only issued
|
|
91
|
+
with a result of the VAT ID check (FinanzOnline, otherwise VIES) on the day
|
|
92
|
+
of issue. Reason: the caller has to decide by the code whether to retry
|
|
93
|
+
later or not to issue at all.
|
|
94
|
+
- Codes `vat_id_invalid` and `vat_id_check_pending` in
|
|
95
|
+
`INVOICE_ERROR_CODES`, after `amount_too_large` and before the
|
|
96
|
+
reservation codes (the backend order). `vat_id_check_pending` carries
|
|
97
|
+
`details.retryAfter` (seconds until the next sensible attempt, with the
|
|
98
|
+
same `idempotencyKey`).
|
|
99
|
+
- `IssueInvoiceRequest.acceptVatIdRisk` (optional boolean): issues despite
|
|
100
|
+
a pending check, the issuer bears the risk. An invalid VAT ID blocks even
|
|
101
|
+
with it. It does not count towards the idempotency of the request.
|
|
102
|
+
- `Invoice.vatIdProof` (`InvoiceVatIdProof`: `checkedOn`, `source`
|
|
103
|
+
`finanzonline` | `vies`, `level` 1 | 2, `code`) and `Invoice.vatIdRisk`
|
|
104
|
+
(`InvoiceVatIdRisk`: `acceptedOn`), otherwise `null`.
|
|
105
|
+
- `fixtures/invoice-api.schema.json` regenerated (`acceptVatIdRisk`, the
|
|
106
|
+
two codes), two new examples `issue-vat-id-risk.json` and
|
|
107
|
+
`issue-error-vat-id-risk.json`.
|
|
108
|
+
- **Contract**: `fixtures/v3/` pulled again from the backend (the 13 new
|
|
109
|
+
endpoints, catalogs, `warningCodes`, events and the 5b cases in
|
|
110
|
+
`antworten/lager.json`, the VAT ID cases in `antworten/rechnungen.json`);
|
|
111
|
+
`PUBLIC_CALLS` and `ALL_CALLS` gain the 13 names (87 public calls);
|
|
112
|
+
`surface.json` gains the new lists under `inventory` and the two invoice
|
|
113
|
+
codes.
|
|
114
|
+
|
|
115
|
+
## 1.4.0
|
|
116
|
+
|
|
117
|
+
Inventory API, read side and account webhooks: new subpath `./inventory`.
|
|
118
|
+
Reason: since stage 5a the backend offers articles, locations, stock and the
|
|
119
|
+
stock ledger under `/v3` with the account's `api_key`, plus webhooks that
|
|
120
|
+
report every stock change within seconds. A shop that shows stock or keeps
|
|
121
|
+
its own copy needs a typed client and, above all, a signature check it
|
|
122
|
+
cannot get wrong.
|
|
123
|
+
|
|
124
|
+
Additive, no breaking change; existing calls send and read the same bytes as
|
|
125
|
+
in 1.3.0.
|
|
126
|
+
|
|
127
|
+
- **`createInventoryClient({ apiKey })`** (`./inventory`, server only, base
|
|
128
|
+
`https://api.kasseneck.at/v3`): `getArticle`, `listArticles`,
|
|
129
|
+
`lookupArticleByCode` (code as text or `{ code }`, or
|
|
130
|
+
`{ externalSystem, externalId }`), `listLocations`, `getStock`, `listStock`,
|
|
131
|
+
`listStockMovements`, and the async iterators `iterateArticles`,
|
|
132
|
+
`iterateStock`, `iterateStockMovements` that follow `nextCursor` until
|
|
133
|
+
`null` (the same cursor twice throws instead of looping). Webhook management:
|
|
134
|
+
`createWebhook`, `updateWebhook`, `deleteWebhook`, `listWebhooks`,
|
|
135
|
+
`sendWebhookTest`, `rotateWebhookSecret`, `listWebhookDeliveries`. Every call
|
|
136
|
+
is also exported as a free function taking the transport.
|
|
137
|
+
- **Models**: `Article` (incl. `stockLocationIds`; `purchasePriceMicros` only
|
|
138
|
+
with the account permission `costs`, otherwise the field is absent, not
|
|
139
|
+
`null`), `StockLevel` (`onHand`, `reserved`, `available`, `defective`,
|
|
140
|
+
`sequence`, `updatedAt`), `StockValue`, `StockMovement`, `Location`,
|
|
141
|
+
`InventoryWebhook` (`consecutiveFailures`), `InventoryWebhookDelivery`
|
|
142
|
+
(`deliveryId`; status `delivered`, `pending`, `failed`, `dropped`); the same
|
|
143
|
+
names as for partner webhooks. Quantities are integer thousandths, money
|
|
144
|
+
integer cents, purchase prices integer micro-euros; `available` may be
|
|
145
|
+
negative. A fractional or missing quantity, amount or `sequence` in a
|
|
146
|
+
response throws `KasseneckValidationError` with `scope: 'response'`, as for
|
|
147
|
+
register stock since 1.2.0. A `Date` in `updatedSince`, `changedSince`,
|
|
148
|
+
`from` or `to` goes out as ISO 8601 UTC.
|
|
149
|
+
- **Incoming webhooks**: `verifyInventoryWebhookSignature(secret, header, rawBody,
|
|
150
|
+
{ toleranceSec = 300, now })` resolves to `true` or `false` and never
|
|
151
|
+
throws: HMAC-SHA256 over `"<t>.<raw body>"`, constant-time comparison,
|
|
152
|
+
300 seconds in both directions, several `v1=` parts allowed; checked against
|
|
153
|
+
the backend's test vector (t=1700000000). Asynchronous because it uses
|
|
154
|
+
WebCrypto, as `./partner` does. `parseInventoryWebhookEvent(rawBody)` returns a typed
|
|
155
|
+
`InventoryWebhookEvent` for `stock.changed` (`cause`, `movementId`),
|
|
156
|
+
`stock.below_minimum` (when `onHand` drops below the minimum stock set for
|
|
157
|
+
the location; the article's `minStock` alone never triggers it, the same rule
|
|
158
|
+
as `listStock({ belowMinimum: true })`) and `article.created|updated|deactivated`, with
|
|
159
|
+
`accountId` instead of `partnerId` in the envelope; an event type this
|
|
160
|
+
version does not know returns `null`, a body that is no envelope throws.
|
|
161
|
+
Both carry `Inventory` in their name on purpose: `./partner` has a
|
|
162
|
+
`verifyWebhookSignature` with an options object that resolves to
|
|
163
|
+
`{ ok, reason }`, and an object is truthy, so a mix-up in plain JavaScript
|
|
164
|
+
would accept every delivery.
|
|
165
|
+
- **Errors** through the existing classes: `INVENTORY_ERROR_CODES`
|
|
166
|
+
(`rate_limited` with `retryAfterSec`, also after 20 `sendWebhookTest` per
|
|
167
|
+
account and calendar day in Vienna, `inventory_api_not_enabled`,
|
|
168
|
+
`module_inactive`, `article_not_found`, `invalid_cursor`, `webhook_limit`
|
|
169
|
+
and the other webhook codes …), `INVENTORY_REQUEST_ERROR_CODES`, `isInventoryError`,
|
|
170
|
+
`inventoryErrorCode`, `inventoryFieldErrors`, `inventoryRetryAfterSec`.
|
|
171
|
+
- **Lists as data**: `INVENTORY_ENDPOINTS`, `INVENTORY_WEBHOOK_EVENTS`,
|
|
172
|
+
`INVENTORY_WEBHOOK_ENVELOPE_FIELDS`, `LOCATION_TYPES`,
|
|
173
|
+
`STOCK_MOVEMENT_TYPES`, `STOCK_MOVEMENT_SOURCES`, `STOCK_CONDITIONS`,
|
|
174
|
+
`STOCK_CHANGE_CAUSES`, `WEBHOOK_DELIVERY_STATUSES`, each held against the
|
|
175
|
+
backend contract. A goods receipt is `goods_receipt` (movement type and
|
|
176
|
+
cause), opening stock taken over is `takeover` (also as cause); `receipt`
|
|
177
|
+
only ever means a sales receipt (`source.type`).
|
|
178
|
+
- **Contract**: `fixtures/v3/` pulled again from the backend (the 14 new
|
|
179
|
+
endpoints, catalogs and events, and `antworten/lager.json` with real
|
|
180
|
+
responses and delivered webhook events that the tests read); `PUBLIC_CALLS` and `ALL_CALLS` gain the 14
|
|
181
|
+
names; `surface.json` gains the key `inventory` with the lists above.
|
|
182
|
+
|
|
7
183
|
## 1.3.0
|
|
8
184
|
|
|
9
185
|
Article codes on `PosArticle` and the stock words of the register in the
|
package/README.md
CHANGED
|
@@ -49,6 +49,7 @@ models, same enum values, checked against each other in tests.
|
|
|
49
49
|
- [Card payments](#card-payments)
|
|
50
50
|
- [Partner API (`./partner`)](#partner-api-partner)
|
|
51
51
|
- [Invoice API (`./invoice`)](#invoice-api-invoice)
|
|
52
|
+
- [Inventory API (`./inventory`)](#inventory-api-inventory)
|
|
52
53
|
- [Migrating from 0.x](#migrating-from-0x)
|
|
53
54
|
- [Development](#development)
|
|
54
55
|
- [Contract files for the twin packages](#contract-files-for-the-twin-packages)
|
|
@@ -211,6 +212,7 @@ adapter:
|
|
|
211
212
|
| `…/stored` | Stored Firestore documents (inner form, German) as the same English models the `/v3` wire returns: receipts with company and layout, register settings, articles. For clients that read Firestore directly, such as the admin panel. |
|
|
212
213
|
| `…/invoice` | Invoice API: create and search customers, issue finalised invoices, credit notes and cancellation, PDF and e-invoice XML, the contract as data. **Belongs on a server.** |
|
|
213
214
|
| `…/invoice/calc` | Pure calculation core for invoice totals (integers, no transport, no dependency beyond types). Safe to run in the browser. |
|
|
215
|
+
| `…/inventory` | Inventory API: read articles, locations, stock and the stock ledger; create and update articles, book goods receipts, transfers and losses, reserve stock at checkout; manage account webhooks, verify and parse incoming stock, article and reservation events. **Belongs on a server.** |
|
|
214
216
|
| `…/react` | Thin React adapter that renders a receipt layout or a receipt sheet. Needs React. |
|
|
215
217
|
| `…/fixtures/*` | Golden receipts (JSON): inputs `receipts/<name>.json`, promised line output `expected/<name>.lines.json`, `manifest.json` with checksums. The backend, the browser register and the Flutter package check against the same files. |
|
|
216
218
|
|
|
@@ -408,6 +410,7 @@ the received body.
|
|
|
408
410
|
| `POS_ERROR_CODES` | `isPosError` (`…/pos`) | settings, articles, printers, tip recipients |
|
|
409
411
|
| `PARTNER_ERROR_CODES` + `PARTNER_REQUEST_ERROR_CODES` | `isPartnerError` (`…/partner`) | partner API |
|
|
410
412
|
| `INVOICE_ERROR_CODES` + `INVOICE_REQUEST_ERROR_CODES` | `isInvoiceError` (`…/invoice`) | invoice API |
|
|
413
|
+
| `INVENTORY_ERROR_CODES` + `INVENTORY_REQUEST_ERROR_CODES` | `isInventoryError` (`…/inventory`) | inventory API |
|
|
411
414
|
|
|
412
415
|
Every group has the same helpers: `is…ErrorCode(value)`, `…ErrorCode(error)`
|
|
413
416
|
(the code if the group knows it), `is…Error(error, code?)` (a type guard; without
|
|
@@ -1348,6 +1351,26 @@ was classified.
|
|
|
1348
1351
|
`previewInvoice` and `recordInvoicePayment`. An intra-Community supply carries
|
|
1349
1352
|
`recapitulative_statement_due` (recapitulative statement).
|
|
1350
1353
|
|
|
1354
|
+
**VAT ID check.** An invoice without VAT that relies on the customer's VAT ID
|
|
1355
|
+
(intra-Community supply, reverse charge) is only issued with a result of the
|
|
1356
|
+
VAT ID check (FinanzOnline, otherwise VIES) on the day of issue.
|
|
1357
|
+
`vat_id_invalid` blocks the invoice. `vat_id_check_pending` means there is no
|
|
1358
|
+
result yet: retry after `retryAfter` seconds with the same `idempotencyKey`,
|
|
1359
|
+
or send `acceptVatIdRisk: true` to issue anyway, at the issuer's risk. The
|
|
1360
|
+
invoice then carries `vatIdRisk` (`acceptedOn`), otherwise the frozen proof
|
|
1361
|
+
`vatIdProof` (`checkedOn`, `source`, `level`, `code`); both are `null` where
|
|
1362
|
+
nothing was checked.
|
|
1363
|
+
|
|
1364
|
+
```ts
|
|
1365
|
+
try {
|
|
1366
|
+
await invoices.issueInvoice(request);
|
|
1367
|
+
} catch (error) {
|
|
1368
|
+
if (!isInvoiceError(error, 'vat_id_check_pending')) throw error;
|
|
1369
|
+
const retryAfter = error.details['retryAfter']; // seconds
|
|
1370
|
+
// later: same request and key, or with acceptVatIdRisk: true
|
|
1371
|
+
}
|
|
1372
|
+
```
|
|
1373
|
+
|
|
1351
1374
|
**After a timeout, retry with the same `idempotencyKey`**, never with a new one:
|
|
1352
1375
|
you then get the invoice that was already issued (`replayed: true`). The same
|
|
1353
1376
|
key with different data gives `idempotency_conflict`.
|
|
@@ -1417,6 +1440,275 @@ but € 23.49 with the core and on the invoice. It stays unchanged for existing
|
|
|
1417
1440
|
callers; use `calculateInvoice` or `previewInvoice` in new code. Test cases for
|
|
1418
1441
|
the old formula: `fixtures/invoice-totals.json`.
|
|
1419
1442
|
|
|
1443
|
+
## Inventory API (`./inventory`)
|
|
1444
|
+
|
|
1445
|
+
For online shops and other systems that show or mirror the stock of a
|
|
1446
|
+
Kasseneck account: read articles, locations, stock per location and the stock
|
|
1447
|
+
ledger, and get every stock change pushed by webhook within seconds, also the
|
|
1448
|
+
ones made at the register in the shop. Since 1.5.0 also the write side:
|
|
1449
|
+
create and update articles, book goods receipts, transfers, losses and
|
|
1450
|
+
condition changes, and reserve stock at checkout (see
|
|
1451
|
+
[Shop: reserve and invoice](#shop-reserve-and-invoice)). Uses the `api_key` of
|
|
1452
|
+
the account and belongs on a **server**. Reading needs the module `lager`,
|
|
1453
|
+
writing additionally the account switch *Lager-API schreiben*, which Kasseneck
|
|
1454
|
+
turns on (always on in the test environment, `kr_test_…`; otherwise
|
|
1455
|
+
`inventory_api_not_enabled`). Purchase prices and stock values appear only when
|
|
1456
|
+
the account has the permission `costs` (otherwise the fields are absent, not
|
|
1457
|
+
`null`).
|
|
1458
|
+
|
|
1459
|
+
**Integers with a fixed scale:** quantities in thousandths of the base unit
|
|
1460
|
+
(`1000` = 1 piece, `250` = 0.250 kg), money in cents, purchase prices in
|
|
1461
|
+
micro-euros. `available = onHand − reserved` and may be negative: the register
|
|
1462
|
+
never refuses a sale. A fractional quantity in a response throws
|
|
1463
|
+
`KasseneckValidationError` with `scope: 'response'`; it is never read as `0`.
|
|
1464
|
+
|
|
1465
|
+
```ts
|
|
1466
|
+
import express from 'express';
|
|
1467
|
+
import { createInventoryClient, verifyInventoryWebhookSignature, parseInventoryWebhookEvent } from '@kreiseck/kasseneck-api/inventory';
|
|
1468
|
+
|
|
1469
|
+
const inventory = createInventoryClient({ apiKey: process.env.KASSENECK_API_KEY! });
|
|
1470
|
+
|
|
1471
|
+
// 1. Read: an article by its EAN, then its stock per location.
|
|
1472
|
+
const article = await inventory.lookupArticleByCode('9001234567896');
|
|
1473
|
+
const { stock } = await inventory.getStock(article.id);
|
|
1474
|
+
// stock[0]: { articleId, locationId, onHand: 12000, reserved: 2000, available: 10000, defective: 0, sequence: 42, updatedAt }
|
|
1475
|
+
|
|
1476
|
+
// 2. Initial sync, page by page over nextCursor.
|
|
1477
|
+
for await (const row of inventory.iterateStock()) {
|
|
1478
|
+
await shop.saveStock(row.articleId, row.locationId, row.available, row.sequence);
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
// 3. Subscribe once. The secret is shown only in this response.
|
|
1482
|
+
const { secret } = await inventory.createWebhook({
|
|
1483
|
+
url: 'https://shop.example.com/kasseneck-webhook',
|
|
1484
|
+
events: ['stock.changed', 'stock.below_minimum'],
|
|
1485
|
+
description: 'Bäckerei Kornblum online shop',
|
|
1486
|
+
});
|
|
1487
|
+
|
|
1488
|
+
// 4. Receive. express.raw BEFORE any JSON parser: the signature covers the bytes as received.
|
|
1489
|
+
const app = express();
|
|
1490
|
+
app.post('/kasseneck-webhook', express.raw({ type: '*/*' }), async (req, res) => {
|
|
1491
|
+
// await is required: the check is asynchronous, and a forgotten await
|
|
1492
|
+
// yields a Promise, which is truthy, so every delivery would pass.
|
|
1493
|
+
if (!(await verifyInventoryWebhookSignature(secret, req.header('X-Kasseneck-Signature'), req.body))) {
|
|
1494
|
+
return res.sendStatus(400);
|
|
1495
|
+
}
|
|
1496
|
+
let event;
|
|
1497
|
+
try {
|
|
1498
|
+
event = parseInventoryWebhookEvent(req.body); // throws on a malformed envelope
|
|
1499
|
+
} catch {
|
|
1500
|
+
return res.sendStatus(400);
|
|
1501
|
+
}
|
|
1502
|
+
res.sendStatus(200); // answer within 10 s, work afterwards
|
|
1503
|
+
if (!event || event.test) return; // unknown type of a later version, or a test delivery
|
|
1504
|
+
if (event.type === 'stock.changed') {
|
|
1505
|
+
// State, not delta: keep it only if sequence is higher than the stored one.
|
|
1506
|
+
await shop.saveStockIfNewer(event.data.articleId, event.data.locationId, event.data.available, event.data.sequence);
|
|
1507
|
+
}
|
|
1508
|
+
});
|
|
1509
|
+
```
|
|
1510
|
+
|
|
1511
|
+
- **Signature.** `verifyInventoryWebhookSignature(secret, header, rawBody, { toleranceSec, now })`
|
|
1512
|
+
resolves to `true` or `false` and never throws. It is the same procedure as
|
|
1513
|
+
for partner webhooks: `X-Kasseneck-Signature: t=<unix seconds>,v1=<hex>`
|
|
1514
|
+
with HMAC-SHA256 over `"<t>.<raw body>"`, compared in constant time, and a
|
|
1515
|
+
window of 300 seconds in both directions against replays. It is asynchronous
|
|
1516
|
+
because it uses WebCrypto: always `await` it, a forgotten `await` leaves a
|
|
1517
|
+
Promise, which is truthy, and lets every delivery through. The name differs
|
|
1518
|
+
on purpose from `verifyWebhookSignature` in `./partner`, which takes an
|
|
1519
|
+
options object and resolves to `{ ok, reason }`. After `rotateWebhookSecret`
|
|
1520
|
+
only the new secret is valid; pass both during your own switch-over
|
|
1521
|
+
(`secret` may be a list, one match is enough).
|
|
1522
|
+
- **Parsing.** `parseInventoryWebhookEvent(rawBody)` returns `null` for an
|
|
1523
|
+
event type this version does not know (answer 2xx and skip it) and throws
|
|
1524
|
+
`KasseneckValidationError` on a body that is no envelope or carries a
|
|
1525
|
+
fractional quantity; verify first, then parse inside `try`.
|
|
1526
|
+
- **Events.** `stock.changed` carries the current state of one article at one
|
|
1527
|
+
location (`onHand`, `reserved`, `available`, `defective`, `sequence`,
|
|
1528
|
+
`updatedAt`) plus `cause` (`sale`, `invoice`, `goods_receipt`, `transfer`,
|
|
1529
|
+
`takeover`, `reservation` …) and `movementId`; changes within 10 seconds are
|
|
1530
|
+
combined into one delivery. `stock.below_minimum` fires once when
|
|
1531
|
+
`available` (`onHand − reserved`, so a reservation alone can trigger it)
|
|
1532
|
+
drops below the minimum stock set for the location (`minStockByLocation` of
|
|
1533
|
+
the article); `minStock` in the payload is that threshold. The article's own
|
|
1534
|
+
`minStock` is a legacy field and never triggers it, and
|
|
1535
|
+
`listStock({ belowMinimum: true })` follows the same rule. In movements,
|
|
1536
|
+
`goods_receipt` is a goods receipt; `receipt` only ever means a sales receipt
|
|
1537
|
+
(`source.type`).
|
|
1538
|
+
`article.created`, `article.updated` and `article.deactivated` carry the
|
|
1539
|
+
article as `getArticle` returns it, without purchase prices.
|
|
1540
|
+
`reservation.expired`, `reservation.released` and `reservation.redeemed`
|
|
1541
|
+
carry the reservation as `getReservation` returns it, with its status after
|
|
1542
|
+
the change; `released` and `redeemed` also fire for a partial release or
|
|
1543
|
+
redemption (status still `active`). Deduplicate on `event.id`; deliveries
|
|
1544
|
+
are retried after 1 min, 5 min, 30 min, 2 h and 12 h.
|
|
1545
|
+
- **Safety net without webhooks.** `listStock({ changedSince })` and
|
|
1546
|
+
`listArticles({ updatedSince })` are sorted by `updatedAt` ascending and
|
|
1547
|
+
include the boundary, so remembering the last `updatedAt` and asking again
|
|
1548
|
+
loses nothing. Lists take `limit` (1–200, default 50) and `cursor`.
|
|
1549
|
+
- **Test deliveries.** `sendWebhookTest` sends a recognisably invented payload
|
|
1550
|
+
with `test: true` in the envelope. At most 20 per account and calendar day in
|
|
1551
|
+
Vienna (live and test environment count separately; rejected calls do not
|
|
1552
|
+
count); after that `rate_limited` with the wait until midnight in Vienna.
|
|
1553
|
+
Deliveries carry `deliveryId` (the header `X-Kasseneck-Delivery`), webhooks
|
|
1554
|
+
`consecutiveFailures`, the same names as for partner webhooks.
|
|
1555
|
+
- **Errors.** `rate_limited` (about 20 requests per second per account, or the
|
|
1556
|
+
daily limit of `sendWebhookTest`) carries the wait in
|
|
1557
|
+
`inventoryRetryAfterSec(error)`. `inventory_api_not_enabled`,
|
|
1558
|
+
`module_inactive`, `article_not_found`, `invalid_cursor`,
|
|
1559
|
+
`webhook_not_found`, `webhook_limit` (5 per account),
|
|
1560
|
+
`invalid_webhook_url`, `event_not_subscribed` and `webhook_inactive` are
|
|
1561
|
+
decided on the code with `isInventoryError(error, code)`.
|
|
1562
|
+
|
|
1563
|
+
### Writing: articles and bookings
|
|
1564
|
+
|
|
1565
|
+
Every write takes an `idempotencyKey` (1 to 120 characters). The same request
|
|
1566
|
+
with the same key takes effect exactly once; repeating it returns the stored
|
|
1567
|
+
answer of the first call, the same key with different content gives
|
|
1568
|
+
`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
|
|
1570
|
+
a valid key, and checks before sending only what is certainly wrong without the
|
|
1571
|
+
network (required ids, integer quantities, amounts and prices, the range of
|
|
1572
|
+
`expiresInMinutes`); everything else the server decides and reports with its
|
|
1573
|
+
code.
|
|
1574
|
+
|
|
1575
|
+
```ts
|
|
1576
|
+
import { createInventoryClient, isInventoryError, inventoryFieldErrors } from '@kreiseck/kasseneck-api/inventory';
|
|
1577
|
+
|
|
1578
|
+
const inventory = createInventoryClient({ apiKey: process.env.KASSENECK_API_KEY! });
|
|
1579
|
+
|
|
1580
|
+
// A foreign article with its EAN; without ean the server assigns an own code.
|
|
1581
|
+
const roll = await inventory.createArticle({
|
|
1582
|
+
idempotencyKey: 'shop-article-1001',
|
|
1583
|
+
name: 'Kaisersemmel',
|
|
1584
|
+
ean: '9001234567896',
|
|
1585
|
+
unitPriceCents: 65,
|
|
1586
|
+
vatRate: 10,
|
|
1587
|
+
stockTracked: true,
|
|
1588
|
+
minStockByLocation: { haupt: 20000 }, // thousandths: warn below 20 pieces
|
|
1589
|
+
externalIds: { shop: '1001' }, // lookupArticleByCode({ externalSystem: 'shop', externalId: '1001' })
|
|
1590
|
+
});
|
|
1591
|
+
|
|
1592
|
+
// Goods receipt: 60 pieces at 0.38 EUR each, plus freight spread by value.
|
|
1593
|
+
const receipt = await inventory.receiveGoods({
|
|
1594
|
+
idempotencyKey: 'shop-goods-receipt-118',
|
|
1595
|
+
items: [{ articleId: roll.id, quantity: 60000, unitPriceMicros: 380000 }],
|
|
1596
|
+
landedCosts: [{ type: 'freight', amountCents: 450 }],
|
|
1597
|
+
reference: 'LS-2026-118',
|
|
1598
|
+
});
|
|
1599
|
+
// receipt: { operationId, movementIds, lotIds, warnings: [] }
|
|
1600
|
+
|
|
1601
|
+
try {
|
|
1602
|
+
await inventory.recordStockLoss({ idempotencyKey: 'shop-loss-7', reason: 'breakage', items: [{ articleId: roll.id, quantity: 2000 }] });
|
|
1603
|
+
} catch (error) {
|
|
1604
|
+
if (isInventoryError(error, 'exceeds_stock')) { /* losses, transfers and condition changes never overdraw */ }
|
|
1605
|
+
else if (isInventoryError(error, 'validation')) console.log(inventoryFieldErrors(error));
|
|
1606
|
+
else throw error;
|
|
1607
|
+
}
|
|
1608
|
+
```
|
|
1609
|
+
|
|
1610
|
+
- **Articles.** `updateArticle({ idempotencyKey, articleId, …fields })` changes
|
|
1611
|
+
only the fields it names; `null` clears an optional one. `externalIds` and
|
|
1612
|
+
`metadata` are replaced as a whole, `minStockByLocation` is merged per
|
|
1613
|
+
location (`{ haupt: null }` removes just that one). `stockKind` is fixed after
|
|
1614
|
+
the first movement (`stock_kind_locked`). `deactivateArticle` frees the code
|
|
1615
|
+
and the external ids. `code_taken` and `external_id_taken` carry `field` and
|
|
1616
|
+
the `articleId` that holds the code in `error.details`. `minStock` is a
|
|
1617
|
+
legacy field without effect; use `minStockByLocation`.
|
|
1618
|
+
- **Bookings.** `receiveGoods`, `transferStock`, `recordStockLoss` (`reason`
|
|
1619
|
+
from `STOCK_LOSS_REASONS`; `other` needs `note`, `withdrawal` needs
|
|
1620
|
+
`withdrawalType`), `changeStockCondition` (`sellable` ↔ `defective`) and
|
|
1621
|
+
`reverseStockMovement({ operationId, reason })` answer with a
|
|
1622
|
+
`StockOperation`. `warnings[]` are notices from `INVENTORY_WARNING_CODES`,
|
|
1623
|
+
never errors: the booking took effect. Prices may be sent without the
|
|
1624
|
+
permission `costs`; values come back only in `previewGoodsReceipt`, which is
|
|
1625
|
+
`receiveGoods` with `dryRun: true` (books nothing, needs no key; a `null`
|
|
1626
|
+
key counts as none and is not sent). `receiveGoods` itself refuses
|
|
1627
|
+
`dryRun: true` before sending (`KasseneckValidationError`, `scope:
|
|
1628
|
+
'request'`): a preview is not a booking, call `previewGoodsReceipt`.
|
|
1629
|
+
|
|
1630
|
+
### Shop: reserve and invoice
|
|
1631
|
+
|
|
1632
|
+
Hold the goods while the customer pays, turn the reservation into a sale with
|
|
1633
|
+
the invoice, and let the server release what was never paid:
|
|
1634
|
+
|
|
1635
|
+
```ts
|
|
1636
|
+
import { createInventoryClient, inventoryShortfalls, isInventoryError, parseInventoryWebhookEvent } from '@kreiseck/kasseneck-api/inventory';
|
|
1637
|
+
import { createInvoiceApi } from '@kreiseck/kasseneck-api/invoice';
|
|
1638
|
+
|
|
1639
|
+
const inventory = createInventoryClient({ apiKey: process.env.KASSENECK_API_KEY! });
|
|
1640
|
+
const invoices = createInvoiceApi({ apiKey: process.env.KASSENECK_API_KEY! });
|
|
1641
|
+
|
|
1642
|
+
// 1. Checkout: reserve everything or nothing, measured against `available`.
|
|
1643
|
+
async function checkout(order: Order) {
|
|
1644
|
+
try {
|
|
1645
|
+
return await inventory.createReservation({
|
|
1646
|
+
idempotencyKey: `checkout-${order.id}`, // the same key on every retry of this checkout
|
|
1647
|
+
reference: `Bestellung ${order.number}`,
|
|
1648
|
+
expiresInMinutes: 30,
|
|
1649
|
+
items: [{ articleId: 'kaisersemmel', quantity: 6000 }], // 6 pieces, at the default location
|
|
1650
|
+
});
|
|
1651
|
+
} catch (error) {
|
|
1652
|
+
if (!isInventoryError(error, 'insufficient_available')) throw error;
|
|
1653
|
+
// Nothing was reserved. Tell the customer what is short:
|
|
1654
|
+
// [{ articleId, locationId, requested: 6000, available: 2000 }]
|
|
1655
|
+
return showShortage(inventoryShortfalls(error));
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
// 2. Paid: issue the invoice; the position redeems the reservation.
|
|
1660
|
+
async function paid(order: Order, reservationId: string) {
|
|
1661
|
+
const { invoice, notice } = await invoices.issueInvoice({
|
|
1662
|
+
idempotencyKey: `invoice-${order.id}`,
|
|
1663
|
+
priceMode: 'gross',
|
|
1664
|
+
serviceStart: '2026-10-06',
|
|
1665
|
+
orderReference: `Bestellung ${order.number}`,
|
|
1666
|
+
items: [{
|
|
1667
|
+
description: 'Kaisersemmel', quantity: 6, unitPriceCents: 65, vatRate: 10,
|
|
1668
|
+
articleId: 'kaisersemmel', reservationId,
|
|
1669
|
+
}],
|
|
1670
|
+
payment: { method: 'online' },
|
|
1671
|
+
});
|
|
1672
|
+
// Expired before the invoice: no error, the invoice sells without it.
|
|
1673
|
+
if (notice?.some((n) => n.code === 'reservation_expired')) log('sold without reservation', invoice.number);
|
|
1674
|
+
}
|
|
1675
|
+
|
|
1676
|
+
// 3. Abandoned cart: give the goods back at once instead of waiting for expiry.
|
|
1677
|
+
async function abandoned(order: Order, reservationId: string) {
|
|
1678
|
+
await inventory.releaseReservation({ idempotencyKey: `release-${order.id}`, reservationId });
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1681
|
+
// 4. Webhooks (subscribe to reservation.* with createWebhook, verify as above).
|
|
1682
|
+
function onDelivery(rawBody: Buffer) {
|
|
1683
|
+
const event = parseInventoryWebhookEvent(rawBody);
|
|
1684
|
+
if (event && !event.test && event.type === 'reservation.expired') {
|
|
1685
|
+
return shop.cancelUnpaidOrder(event.data.reference); // the stock is available again
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
```
|
|
1689
|
+
|
|
1690
|
+
- **Redeeming.** A position with `reservationId` needs `articleId`. The server
|
|
1691
|
+
checks it when the invoice is issued: `reservation_not_found` (unknown or
|
|
1692
|
+
another account), `reservation_not_active` (already redeemed or released),
|
|
1693
|
+
`reservation_mismatch` (no open quantity of this article at the invoice's
|
|
1694
|
+
stock location, `stockLocationId` or the default location); nothing is issued
|
|
1695
|
+
then. An expired reservation is no error but the notice `reservation_expired`
|
|
1696
|
+
(with `reservationId`). The reservation is redeemed when the invoice is
|
|
1697
|
+
booked into stock: selling less than reserved releases the rest, selling
|
|
1698
|
+
more gives the warning `reservation_exceeded`. Credit notes take no
|
|
1699
|
+
`reservationId`.
|
|
1700
|
+
- **Expiry.** Without `expiresInMinutes` the account's default applies (7 days);
|
|
1701
|
+
5 to 43,200 minutes are allowed. `extendReservation` sets a new expiry from
|
|
1702
|
+
now, only for an active reservation that is not yet due
|
|
1703
|
+
(`reservation_not_active`). `releaseReservation` without `items` releases
|
|
1704
|
+
everything, with `items` per position (without `quantity` the whole rest);
|
|
1705
|
+
the status becomes `released` only when nothing is open.
|
|
1706
|
+
- **Events.** `reservation.expired`, `reservation.released` and
|
|
1707
|
+
`reservation.redeemed` carry the reservation with its status afterwards.
|
|
1708
|
+
`listReservations({ status: 'active' })` or `({ reference })` is the safety
|
|
1709
|
+
net without webhooks; the stock ledger shows reservations as movements of
|
|
1710
|
+
type `reservation` with `quantityDelta: 0` and the change in `reservedDelta`.
|
|
1711
|
+
|
|
1420
1712
|
## Migrating from 0.x
|
|
1421
1713
|
|
|
1422
1714
|
1.0 is one breaking step: the wire, the exported names and the contract files
|
|
@@ -1492,7 +1784,7 @@ in machine form what both sides agreed on, among them:
|
|
|
1492
1784
|
|---|---|---|
|
|
1493
1785
|
| `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` |
|
|
1494
1786
|
| `stored/pos-settings-defaults.json` | the same defaults in the stored (internal, German) form (`betrieb`, `geraet`) | `npm run fixtures:kasse` |
|
|
1495
|
-
| `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 and
|
|
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` |
|
|
1496
1788
|
| `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` |
|
|
1497
1789
|
| `pos-texts.json` | the register's message catalogue | `npm run fixtures:texte` |
|
|
1498
1790
|
| `invoice-texts.json` | invoice texts in both languages | `npm run fixtures:rechnungstexte` |
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* umhuellt, muss ihn weiterhin absetzen koennen.
|
|
14
14
|
*/
|
|
15
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"];
|
|
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"];
|
|
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"];
|
|
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"];
|
|
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
|
|
@@ -75,6 +75,35 @@ exports.ALL_CALLS = [
|
|
|
75
75
|
'rotatePartnerWebhookSecret',
|
|
76
76
|
'updateCustomer',
|
|
77
77
|
'updatePartnerWebhook',
|
|
78
|
+
// Lager-API (Stufe 5a, ./inventory)
|
|
79
|
+
'getArticle',
|
|
80
|
+
'listArticles',
|
|
81
|
+
'lookupArticleByCode',
|
|
82
|
+
'listLocations',
|
|
83
|
+
'getStock',
|
|
84
|
+
'listStock',
|
|
85
|
+
'listStockMovements',
|
|
86
|
+
'createWebhook',
|
|
87
|
+
'updateWebhook',
|
|
88
|
+
'deleteWebhook',
|
|
89
|
+
'listWebhooks',
|
|
90
|
+
'sendWebhookTest',
|
|
91
|
+
'rotateWebhookSecret',
|
|
92
|
+
'listWebhookDeliveries',
|
|
93
|
+
// Lager-API schreiben und Reservierung (Stufe 5b, ./inventory)
|
|
94
|
+
'createArticle',
|
|
95
|
+
'updateArticle',
|
|
96
|
+
'deactivateArticle',
|
|
97
|
+
'receiveGoods',
|
|
98
|
+
'transferStock',
|
|
99
|
+
'recordStockLoss',
|
|
100
|
+
'changeStockCondition',
|
|
101
|
+
'reverseStockMovement',
|
|
102
|
+
'createReservation',
|
|
103
|
+
'extendReservation',
|
|
104
|
+
'releaseReservation',
|
|
105
|
+
'getReservation',
|
|
106
|
+
'listReservations',
|
|
78
107
|
];
|
|
79
108
|
/**
|
|
80
109
|
* Die oeffentlichen Endpunkte unter `/v3` (`https://api.kasseneck.at/v3/<name>`),
|
|
@@ -146,6 +175,35 @@ exports.PUBLIC_CALLS = [
|
|
|
146
175
|
'listInvoiceItems',
|
|
147
176
|
'setCustomerMandate',
|
|
148
177
|
'revokeCustomerMandate',
|
|
178
|
+
// Lager-API lesen und Konto-Webhooks (Backend Stufe 5a), umhuellt in `./inventory`.
|
|
179
|
+
'getArticle',
|
|
180
|
+
'listArticles',
|
|
181
|
+
'lookupArticleByCode',
|
|
182
|
+
'listLocations',
|
|
183
|
+
'getStock',
|
|
184
|
+
'listStock',
|
|
185
|
+
'listStockMovements',
|
|
186
|
+
'createWebhook',
|
|
187
|
+
'updateWebhook',
|
|
188
|
+
'deleteWebhook',
|
|
189
|
+
'listWebhooks',
|
|
190
|
+
'sendWebhookTest',
|
|
191
|
+
'rotateWebhookSecret',
|
|
192
|
+
'listWebhookDeliveries',
|
|
193
|
+
// Lager-API schreiben und Reservierung (Backend Stufe 5b), umhuellt in `./inventory`.
|
|
194
|
+
'createArticle',
|
|
195
|
+
'updateArticle',
|
|
196
|
+
'deactivateArticle',
|
|
197
|
+
'receiveGoods',
|
|
198
|
+
'transferStock',
|
|
199
|
+
'recordStockLoss',
|
|
200
|
+
'changeStockCondition',
|
|
201
|
+
'reverseStockMovement',
|
|
202
|
+
'createReservation',
|
|
203
|
+
'extendReservation',
|
|
204
|
+
'releaseReservation',
|
|
205
|
+
'getReservation',
|
|
206
|
+
'listReservations',
|
|
149
207
|
];
|
|
150
208
|
/**
|
|
151
209
|
* Die Endpunkte des Kassenwegs (`https://kasse.kasseneck.at/api/v3/<name>`,
|