@kreiseck/kasseneck-api 1.4.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 +108 -0
- package/README.md +192 -11
- package/dist/cjs/client/aufrufe.d.ts +2 -2
- package/dist/cjs/client/aufrufe.js +28 -0
- package/dist/cjs/inventory/api.d.ts +19 -3
- package/dist/cjs/inventory/api.js +18 -2
- package/dist/cjs/inventory/endpunkte.d.ts +22 -2
- package/dist/cjs/inventory/endpunkte.js +6 -1
- package/dist/cjs/inventory/fehler.d.ts +11 -1
- package/dist/cjs/inventory/fehler.js +19 -0
- package/dist/cjs/inventory/index.d.ts +6 -4
- package/dist/cjs/inventory/index.js +32 -2
- package/dist/cjs/inventory/lesen.d.ts +8 -1
- package/dist/cjs/inventory/lesen.js +131 -1
- package/dist/cjs/inventory/schreiben.d.ts +83 -0
- package/dist/cjs/inventory/schreiben.js +315 -0
- package/dist/cjs/inventory/typen.d.ts +371 -11
- package/dist/cjs/inventory/vertrag.d.ts +76 -16
- package/dist/cjs/inventory/vertrag.js +138 -13
- package/dist/cjs/inventory/webhook.d.ts +3 -1
- package/dist/cjs/inventory/webhook.js +7 -1
- 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 +28 -0
- package/dist/esm/inventory/api.d.ts +19 -3
- package/dist/esm/inventory/api.js +18 -2
- package/dist/esm/inventory/endpunkte.d.ts +22 -2
- package/dist/esm/inventory/endpunkte.js +6 -5
- package/dist/esm/inventory/fehler.d.ts +11 -1
- package/dist/esm/inventory/fehler.js +18 -1
- package/dist/esm/inventory/index.d.ts +6 -4
- package/dist/esm/inventory/index.js +5 -3
- package/dist/esm/inventory/lesen.d.ts +8 -1
- package/dist/esm/inventory/lesen.js +127 -1
- package/dist/esm/inventory/schreiben.d.ts +83 -0
- package/dist/esm/inventory/schreiben.js +298 -0
- package/dist/esm/inventory/typen.d.ts +371 -11
- package/dist/esm/inventory/vertrag.d.ts +76 -16
- package/dist/esm/inventory/vertrag.js +137 -12
- package/dist/esm/inventory/webhook.d.ts +3 -1
- package/dist/esm/inventory/webhook.js +8 -2
- 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 +152 -9
- 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 +2491 -130
- 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 +3 -3
- package/fixtures/v3/stored/rechnungen.json +174 -18
- package/fixtures/v3/v3-vokabular.json +593 -23
- package/fixtures/v3/zahlbetrag-faelle.json +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,114 @@ 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
|
+
|
|
7
115
|
## 1.4.0
|
|
8
116
|
|
|
9
117
|
Inventory API, read side and account webhooks: new subpath `./inventory`.
|
package/README.md
CHANGED
|
@@ -212,7 +212,7 @@ adapter:
|
|
|
212
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. |
|
|
213
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.** |
|
|
214
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, manage account webhooks, verify and parse incoming stock and
|
|
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.** |
|
|
216
216
|
| `…/react` | Thin React adapter that renders a receipt layout or a receipt sheet. Needs React. |
|
|
217
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. |
|
|
218
218
|
|
|
@@ -1351,6 +1351,26 @@ was classified.
|
|
|
1351
1351
|
`previewInvoice` and `recordInvoicePayment`. An intra-Community supply carries
|
|
1352
1352
|
`recapitulative_statement_due` (recapitulative statement).
|
|
1353
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
|
+
|
|
1354
1374
|
**After a timeout, retry with the same `idempotencyKey`**, never with a new one:
|
|
1355
1375
|
you then get the invoice that was already issued (`replayed: true`). The same
|
|
1356
1376
|
key with different data gives `idempotency_conflict`.
|
|
@@ -1425,10 +1445,16 @@ the old formula: `fixtures/invoice-totals.json`.
|
|
|
1425
1445
|
For online shops and other systems that show or mirror the stock of a
|
|
1426
1446
|
Kasseneck account: read articles, locations, stock per location and the stock
|
|
1427
1447
|
ledger, and get every stock change pushed by webhook within seconds, also the
|
|
1428
|
-
ones made at the register in the shop.
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
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`).
|
|
1432
1458
|
|
|
1433
1459
|
**Integers with a fixed scale:** quantities in thousandths of the base unit
|
|
1434
1460
|
(`1000` = 1 piece, `250` = 0.250 kg), money in cents, purchase prices in
|
|
@@ -1500,16 +1526,22 @@ app.post('/kasseneck-webhook', express.raw({ type: '*/*' }), async (req, res) =>
|
|
|
1500
1526
|
- **Events.** `stock.changed` carries the current state of one article at one
|
|
1501
1527
|
location (`onHand`, `reserved`, `available`, `defective`, `sequence`,
|
|
1502
1528
|
`updatedAt`) plus `cause` (`sale`, `invoice`, `goods_receipt`, `transfer`,
|
|
1503
|
-
`takeover` …) and `movementId`; changes within 10 seconds are
|
|
1504
|
-
one delivery. `stock.below_minimum` fires once when
|
|
1505
|
-
|
|
1506
|
-
|
|
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
|
|
1507
1535
|
`listStock({ belowMinimum: true })` follows the same rule. In movements,
|
|
1508
1536
|
`goods_receipt` is a goods receipt; `receipt` only ever means a sales receipt
|
|
1509
1537
|
(`source.type`).
|
|
1510
1538
|
`article.created`, `article.updated` and `article.deactivated` carry the
|
|
1511
|
-
article as `getArticle` returns it, without purchase prices.
|
|
1512
|
-
`
|
|
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.
|
|
1513
1545
|
- **Safety net without webhooks.** `listStock({ changedSince })` and
|
|
1514
1546
|
`listArticles({ updatedSince })` are sorted by `updatedAt` ascending and
|
|
1515
1547
|
include the boundary, so remembering the last `updatedAt` and asking again
|
|
@@ -1528,6 +1560,155 @@ app.post('/kasseneck-webhook', express.raw({ type: '*/*' }), async (req, res) =>
|
|
|
1528
1560
|
`invalid_webhook_url`, `event_not_subscribed` and `webhook_inactive` are
|
|
1529
1561
|
decided on the code with `isInventoryError(error, code)`.
|
|
1530
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
|
+
|
|
1531
1712
|
## Migrating from 0.x
|
|
1532
1713
|
|
|
1533
1714
|
1.0 is one breaking step: the wire, the exported names and the contract files
|
|
@@ -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", "getArticle", "listArticles", "lookupArticleByCode", "listLocations", "getStock", "listStock", "listStockMovements", "createWebhook", "updateWebhook", "deleteWebhook", "listWebhooks", "sendWebhookTest", "rotateWebhookSecret", "listWebhookDeliveries"];
|
|
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", "getArticle", "listArticles", "lookupArticleByCode", "listLocations", "getStock", "listStock", "listStockMovements", "createWebhook", "updateWebhook", "deleteWebhook", "listWebhooks", "sendWebhookTest", "rotateWebhookSecret", "listWebhookDeliveries"];
|
|
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
|
|
@@ -90,6 +90,20 @@ exports.ALL_CALLS = [
|
|
|
90
90
|
'sendWebhookTest',
|
|
91
91
|
'rotateWebhookSecret',
|
|
92
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',
|
|
93
107
|
];
|
|
94
108
|
/**
|
|
95
109
|
* Die oeffentlichen Endpunkte unter `/v3` (`https://api.kasseneck.at/v3/<name>`),
|
|
@@ -176,6 +190,20 @@ exports.PUBLIC_CALLS = [
|
|
|
176
190
|
'sendWebhookTest',
|
|
177
191
|
'rotateWebhookSecret',
|
|
178
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',
|
|
179
207
|
];
|
|
180
208
|
/**
|
|
181
209
|
* Die Endpunkte des Kassenwegs (`https://kasse.kasseneck.at/api/v3/<name>`,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Fassade ueber den Aufrufen der Lager-API
|
|
3
|
-
* rufen. Wie [createInvoiceApi] bewusst keine Klasse; die Aufrufe sind freie
|
|
2
|
+
* Fassade ueber den Aufrufen der Lager-API (lesen, Webhooks, schreiben,
|
|
3
|
+
* reservieren): Schluessel einmal binden, dann rufen. Wie [createInvoiceApi] bewusst keine Klasse; die Aufrufe sind freie
|
|
4
4
|
* Funktionen (endpunkte.ts) und bleiben einzeln importierbar.
|
|
5
5
|
*/
|
|
6
6
|
import { type FetchLike } from '../client/transport.js';
|
|
7
|
-
import type { Article, ArticleListQuery, ArticleLookup, ArticlePage, CreateInventoryWebhookOptions, InventoryWebhook, InventoryWebhookDelivery, InventoryWebhookList, InventoryWebhookPatch, InventoryWebhookTestResult, InventoryWebhookWithSecret, Location, StockLevel, StockListQuery, StockMovement, StockMovementPage, StockMovementQuery, StockPage, StockResult } from './typen.js';
|
|
7
|
+
import type { Article, ChangeStockConditionRequest, CreateArticleRequest, CreateReservationRequest, DeactivateArticleRequest, ExtendReservationRequest, GoodsReceiptPreview, GoodsReceiptPreviewRequest, ReceiveGoodsRequest, RecordStockLossRequest, ReleaseReservationRequest, Reservation, ReservationListQuery, ReservationPage, ReverseStockMovementRequest, StockOperation, TransferStockRequest, UpdateArticleRequest, ArticleListQuery, ArticleLookup, ArticlePage, CreateInventoryWebhookOptions, InventoryWebhook, InventoryWebhookDelivery, InventoryWebhookList, InventoryWebhookPatch, InventoryWebhookTestResult, InventoryWebhookWithSecret, Location, StockLevel, StockListQuery, StockMovement, StockMovementPage, StockMovementQuery, StockPage, StockResult } from './typen.js';
|
|
8
8
|
import type { InventoryWebhookEventType } from './vertrag.js';
|
|
9
9
|
export interface InventoryClientOptions {
|
|
10
10
|
/** `api_key` des Kontos (`kr_live_…` / `kr_test_…`). Gehoert auf einen Server. */
|
|
@@ -41,5 +41,21 @@ export interface InventoryClient {
|
|
|
41
41
|
webhookId?: string;
|
|
42
42
|
limit?: number;
|
|
43
43
|
}): Promise<InventoryWebhookDelivery[]>;
|
|
44
|
+
createArticle(request: CreateArticleRequest): Promise<Article>;
|
|
45
|
+
updateArticle(request: UpdateArticleRequest): Promise<Article>;
|
|
46
|
+
deactivateArticle(request: DeactivateArticleRequest): Promise<Article>;
|
|
47
|
+
receiveGoods(request: ReceiveGoodsRequest): Promise<StockOperation>;
|
|
48
|
+
/** `receiveGoods` mit `dryRun: true`: rechnet, schreibt nichts, Schluessel freigestellt. */
|
|
49
|
+
previewGoodsReceipt(request: GoodsReceiptPreviewRequest): Promise<GoodsReceiptPreview>;
|
|
50
|
+
transferStock(request: TransferStockRequest): Promise<StockOperation>;
|
|
51
|
+
recordStockLoss(request: RecordStockLossRequest): Promise<StockOperation>;
|
|
52
|
+
changeStockCondition(request: ChangeStockConditionRequest): Promise<StockOperation>;
|
|
53
|
+
reverseStockMovement(request: ReverseStockMovementRequest): Promise<StockOperation>;
|
|
54
|
+
createReservation(request: CreateReservationRequest): Promise<Reservation>;
|
|
55
|
+
extendReservation(request: ExtendReservationRequest): Promise<Reservation>;
|
|
56
|
+
releaseReservation(request: ReleaseReservationRequest): Promise<Reservation>;
|
|
57
|
+
getReservation(reservationId: string): Promise<Reservation>;
|
|
58
|
+
listReservations(query?: ReservationListQuery): Promise<ReservationPage>;
|
|
59
|
+
iterateReservations(query?: ReservationListQuery): AsyncGenerator<Reservation, void, undefined>;
|
|
44
60
|
}
|
|
45
61
|
export declare function createInventoryClient(options: InventoryClientOptions): InventoryClient;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* Fassade ueber den Aufrufen der Lager-API
|
|
4
|
-
* rufen. Wie [createInvoiceApi] bewusst keine Klasse; die Aufrufe sind freie
|
|
3
|
+
* Fassade ueber den Aufrufen der Lager-API (lesen, Webhooks, schreiben,
|
|
4
|
+
* reservieren): Schluessel einmal binden, dann rufen. Wie [createInvoiceApi] bewusst keine Klasse; die Aufrufe sind freie
|
|
5
5
|
* Funktionen (endpunkte.ts) und bleiben einzeln importierbar.
|
|
6
6
|
*/
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -9,6 +9,7 @@ exports.createInventoryClient = createInventoryClient;
|
|
|
9
9
|
const transport_js_1 = require("../client/transport.js");
|
|
10
10
|
const auth_js_1 = require("./auth.js");
|
|
11
11
|
const endpunkte_js_1 = require("./endpunkte.js");
|
|
12
|
+
const schreiben_js_1 = require("./schreiben.js");
|
|
12
13
|
function createInventoryClient(options) {
|
|
13
14
|
const rufen = (0, transport_js_1.createTransport)({
|
|
14
15
|
auth: (0, auth_js_1.inventoryKeyAuth)({ apiKey: options.apiKey }),
|
|
@@ -34,5 +35,20 @@ function createInventoryClient(options) {
|
|
|
34
35
|
sendWebhookTest: (id, event) => (0, endpunkte_js_1.sendWebhookTest)(rufen, id, event),
|
|
35
36
|
rotateWebhookSecret: (id) => (0, endpunkte_js_1.rotateWebhookSecret)(rufen, id),
|
|
36
37
|
listWebhookDeliveries: (o) => (0, endpunkte_js_1.listWebhookDeliveries)(rufen, o),
|
|
38
|
+
createArticle: (r) => (0, schreiben_js_1.createArticle)(rufen, r),
|
|
39
|
+
updateArticle: (r) => (0, schreiben_js_1.updateArticle)(rufen, r),
|
|
40
|
+
deactivateArticle: (r) => (0, schreiben_js_1.deactivateArticle)(rufen, r),
|
|
41
|
+
receiveGoods: (r) => (0, schreiben_js_1.receiveGoods)(rufen, r),
|
|
42
|
+
previewGoodsReceipt: (r) => (0, schreiben_js_1.previewGoodsReceipt)(rufen, r),
|
|
43
|
+
transferStock: (r) => (0, schreiben_js_1.transferStock)(rufen, r),
|
|
44
|
+
recordStockLoss: (r) => (0, schreiben_js_1.recordStockLoss)(rufen, r),
|
|
45
|
+
changeStockCondition: (r) => (0, schreiben_js_1.changeStockCondition)(rufen, r),
|
|
46
|
+
reverseStockMovement: (r) => (0, schreiben_js_1.reverseStockMovement)(rufen, r),
|
|
47
|
+
createReservation: (r) => (0, schreiben_js_1.createReservation)(rufen, r),
|
|
48
|
+
extendReservation: (r) => (0, schreiben_js_1.extendReservation)(rufen, r),
|
|
49
|
+
releaseReservation: (r) => (0, schreiben_js_1.releaseReservation)(rufen, r),
|
|
50
|
+
getReservation: (id) => (0, schreiben_js_1.getReservation)(rufen, id),
|
|
51
|
+
listReservations: (q) => (0, schreiben_js_1.listReservations)(rufen, q),
|
|
52
|
+
iterateReservations: (q) => (0, schreiben_js_1.iterateReservations)(rufen, q),
|
|
37
53
|
};
|
|
38
54
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Die Aufrufe der Lager-API: Artikel, Standorte, Bestand und Bewegungen
|
|
3
|
-
* lesen, Konto-Webhooks verwalten (Backend Stufe 5a).
|
|
3
|
+
* lesen, Konto-Webhooks verwalten (Backend Stufe 5a). Die schreibenden
|
|
4
|
+
* Aufrufe (Stufe 5b) stehen in `schreiben.ts` und teilen die Helfer hier.
|
|
4
5
|
*
|
|
5
6
|
* Jede Funktion nimmt den Transport als ersten Parameter und ist einzeln
|
|
6
7
|
* importierbar; die Fassade [createInventoryClient] bindet ihn nur einmal.
|
|
@@ -14,8 +15,26 @@
|
|
|
14
15
|
* wiederholt werden. Bei `rate_limited` vorher `retryAfterSec` warten
|
|
15
16
|
* ([inventoryRetryAfterSec]).
|
|
16
17
|
*/
|
|
17
|
-
import type { InternerTransport } from '../client/aufrufe.js';
|
|
18
|
+
import type { ApiCall, InternerTransport } from '../client/aufrufe.js';
|
|
19
|
+
import { KasseneckValidationError } from '../client/errors.js';
|
|
18
20
|
import type { Article, ArticleListQuery, ArticleLookup, ArticlePage, CreateInventoryWebhookOptions, InventoryWebhook, InventoryWebhookDelivery, InventoryWebhookList, InventoryWebhookPatch, InventoryWebhookTestResult, InventoryWebhookWithSecret, Location, StockLevel, StockListQuery, StockMovement, StockMovementPage, StockMovementQuery, StockPage, StockResult } from './typen.js';
|
|
21
|
+
type Params = Record<string, unknown>;
|
|
22
|
+
export declare function anfragefehler(name: ApiCall, grund: string): KasseneckValidationError;
|
|
23
|
+
/** Eine Kennung, die gesendet werden muss: Text mit mindestens einem Zeichen ausser Leerraum. */
|
|
24
|
+
export declare function kennung(name: ApiCall, feld: string, wert: unknown): string;
|
|
25
|
+
/**
|
|
26
|
+
* Die Abfrage einer Liste als Parameter. Unbekannte Felder gehen unveraendert
|
|
27
|
+
* hinaus (der Server weist sie mit `validation` ab), `undefined` faellt weg.
|
|
28
|
+
*/
|
|
29
|
+
export declare function abfrage(name: ApiCall, query: object | undefined): Params;
|
|
30
|
+
/**
|
|
31
|
+
* Blaettert ueber alle Seiten, Eintrag fuer Eintrag. Derselbe Cursor zweimal
|
|
32
|
+
* waere eine Endlosschleife: dann endet die Schleife mit einem Antwortfehler.
|
|
33
|
+
*/
|
|
34
|
+
export declare function seitenweise<T>(name: ApiCall, start: string | undefined, seite: (cursor: string | undefined) => Promise<{
|
|
35
|
+
eintraege: T[];
|
|
36
|
+
nextCursor: string | null;
|
|
37
|
+
}>): AsyncGenerator<T, void, undefined>;
|
|
19
38
|
export declare function getArticle(transport: InternerTransport, articleId: string): Promise<Article>;
|
|
20
39
|
export declare function listArticles(transport: InternerTransport, query?: ArticleListQuery): Promise<ArticlePage>;
|
|
21
40
|
/** Alle Artikel der Abfrage, Seite fuer Seite ueber `nextCursor`. */
|
|
@@ -69,3 +88,4 @@ export declare function listWebhookDeliveries(transport: InternerTransport, opti
|
|
|
69
88
|
webhookId?: string;
|
|
70
89
|
limit?: number;
|
|
71
90
|
}): Promise<InventoryWebhookDelivery[]>;
|
|
91
|
+
export {};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
3
|
* Die Aufrufe der Lager-API: Artikel, Standorte, Bestand und Bewegungen
|
|
4
|
-
* lesen, Konto-Webhooks verwalten (Backend Stufe 5a).
|
|
4
|
+
* lesen, Konto-Webhooks verwalten (Backend Stufe 5a). Die schreibenden
|
|
5
|
+
* Aufrufe (Stufe 5b) stehen in `schreiben.ts` und teilen die Helfer hier.
|
|
5
6
|
*
|
|
6
7
|
* Jede Funktion nimmt den Transport als ersten Parameter und ist einzeln
|
|
7
8
|
* importierbar; die Fassade [createInventoryClient] bindet ihn nur einmal.
|
|
@@ -16,6 +17,10 @@
|
|
|
16
17
|
* ([inventoryRetryAfterSec]).
|
|
17
18
|
*/
|
|
18
19
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.anfragefehler = anfragefehler;
|
|
21
|
+
exports.kennung = kennung;
|
|
22
|
+
exports.abfrage = abfrage;
|
|
23
|
+
exports.seitenweise = seitenweise;
|
|
19
24
|
exports.getArticle = getArticle;
|
|
20
25
|
exports.listArticles = listArticles;
|
|
21
26
|
exports.iterateArticles = iterateArticles;
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
* `KasseneckApiError.code`.
|
|
9
9
|
*/
|
|
10
10
|
import type { KasseneckApiError } from '../client/errors.js';
|
|
11
|
-
import
|
|
11
|
+
import type { InventoryShortfall } from './typen.js';
|
|
12
|
+
import { type InventoryErrorCode, type InventoryRequestErrorCode, type InventoryWarningCode } from './vertrag.js';
|
|
12
13
|
/** Ein Code, den ein Lager-Aufruf liefern kann. */
|
|
13
14
|
export type InventoryApiErrorCode = InventoryErrorCode | InventoryRequestErrorCode;
|
|
14
15
|
export declare function isInventoryErrorCode(value: unknown): value is InventoryApiErrorCode;
|
|
@@ -31,3 +32,12 @@ export declare function inventoryFieldErrors(error: unknown): InventoryFieldErro
|
|
|
31
32
|
* Konto (etwa 20 Anfragen je Sekunde, kurze Spitzen bis 40).
|
|
32
33
|
*/
|
|
33
34
|
export declare function inventoryRetryAfterSec(error: unknown): number | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* Die Positionen, fuer die beim Reservieren der verfuegbare Bestand nicht
|
|
37
|
+
* reicht (`insufficient_available`, `data.details[]`): je Artikel und Standort
|
|
38
|
+
* angefragt und verfuegbar, in Tausendstel. Leer, wenn der Fehler ein anderer
|
|
39
|
+
* ist. Es fehlen nur diese Positionen; reserviert wurde nichts.
|
|
40
|
+
*/
|
|
41
|
+
export declare function inventoryShortfalls(error: unknown): InventoryShortfall[];
|
|
42
|
+
/** `true` fuer einen Hinweis-Code einer Buchung (`warnings[].code`); Hinweise sind nie Fehler. */
|
|
43
|
+
export declare function isInventoryWarningCode(value: unknown): value is InventoryWarningCode;
|
|
@@ -14,7 +14,10 @@ exports.inventoryErrorCode = inventoryErrorCode;
|
|
|
14
14
|
exports.isInventoryError = isInventoryError;
|
|
15
15
|
exports.inventoryFieldErrors = inventoryFieldErrors;
|
|
16
16
|
exports.inventoryRetryAfterSec = inventoryRetryAfterSec;
|
|
17
|
+
exports.inventoryShortfalls = inventoryShortfalls;
|
|
18
|
+
exports.isInventoryWarningCode = isInventoryWarningCode;
|
|
17
19
|
const fehlercodes_js_1 = require("../client/fehlercodes.js");
|
|
20
|
+
const lesen_js_1 = require("./lesen.js");
|
|
18
21
|
const vertrag_js_1 = require("./vertrag.js");
|
|
19
22
|
const BEKANNT = new Set([...vertrag_js_1.INVENTORY_ERROR_CODES, ...vertrag_js_1.INVENTORY_REQUEST_ERROR_CODES]);
|
|
20
23
|
function isInventoryErrorCode(value) {
|
|
@@ -45,3 +48,19 @@ function inventoryRetryAfterSec(error) {
|
|
|
45
48
|
const wert = error.details['retryAfterSec'];
|
|
46
49
|
return typeof wert === 'number' && Number.isFinite(wert) && wert >= 0 ? wert : undefined;
|
|
47
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Die Positionen, fuer die beim Reservieren der verfuegbare Bestand nicht
|
|
53
|
+
* reicht (`insufficient_available`, `data.details[]`): je Artikel und Standort
|
|
54
|
+
* angefragt und verfuegbar, in Tausendstel. Leer, wenn der Fehler ein anderer
|
|
55
|
+
* ist. Es fehlen nur diese Positionen; reserviert wurde nichts.
|
|
56
|
+
*/
|
|
57
|
+
function inventoryShortfalls(error) {
|
|
58
|
+
if (inventoryErrorCode(error) !== 'insufficient_available')
|
|
59
|
+
return [];
|
|
60
|
+
return (0, lesen_js_1.fehlmengen)(error.details['details']);
|
|
61
|
+
}
|
|
62
|
+
const HINWEISE = new Set(vertrag_js_1.INVENTORY_WARNING_CODES);
|
|
63
|
+
/** `true` fuer einen Hinweis-Code einer Buchung (`warnings[].code`); Hinweise sind nie Fehler. */
|
|
64
|
+
function isInventoryWarningCode(value) {
|
|
65
|
+
return typeof value === 'string' && HINWEISE.has(value);
|
|
66
|
+
}
|