@kreiseck/kasseneck-api 1.2.2 → 1.4.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 +99 -0
- package/README.md +119 -3
- package/dist/cjs/client/aufrufe.d.ts +2 -2
- package/dist/cjs/client/aufrufe.js +30 -0
- package/dist/cjs/inventory/api.d.ts +45 -0
- package/dist/cjs/inventory/api.js +38 -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 +71 -0
- package/dist/cjs/inventory/endpunkte.js +285 -0
- package/dist/cjs/inventory/fehler.d.ts +33 -0
- package/dist/cjs/inventory/fehler.js +47 -0
- package/dist/cjs/inventory/index.d.ts +19 -0
- package/dist/cjs/inventory/index.js +64 -0
- package/dist/cjs/inventory/lesen.d.ts +38 -0
- package/dist/cjs/inventory/lesen.js +294 -0
- package/dist/cjs/inventory/typen.d.ts +319 -0
- package/dist/cjs/inventory/typen.js +14 -0
- package/dist/cjs/inventory/vertrag.d.ts +76 -0
- package/dist/cjs/inventory/vertrag.js +166 -0
- package/dist/cjs/inventory/webhook.d.ts +50 -0
- package/dist/cjs/inventory/webhook.js +110 -0
- package/dist/cjs/pos/artikel.d.ts +20 -0
- package/dist/cjs/pos/artikel.js +8 -0
- package/dist/cjs/pos/index.d.ts +1 -1
- package/dist/cjs/pos/index.js +2 -1
- package/dist/cjs/pos/texte.d.ts +41 -0
- package/dist/cjs/pos/texte.js +29 -1
- package/dist/cjs/stored/vokabular.d.ts +1 -1
- package/dist/cjs/stored/vokabular.js +1 -1
- 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 +30 -0
- package/dist/esm/inventory/api.d.ts +45 -0
- package/dist/esm/inventory/api.js +35 -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 +71 -0
- package/dist/esm/inventory/endpunkte.js +266 -0
- package/dist/esm/inventory/fehler.d.ts +33 -0
- package/dist/esm/inventory/fehler.js +40 -0
- package/dist/esm/inventory/index.d.ts +19 -0
- package/dist/esm/inventory/index.js +18 -0
- package/dist/esm/inventory/lesen.d.ts +38 -0
- package/dist/esm/inventory/lesen.js +278 -0
- package/dist/esm/inventory/typen.d.ts +319 -0
- package/dist/esm/inventory/typen.js +13 -0
- package/dist/esm/inventory/vertrag.d.ts +76 -0
- package/dist/esm/inventory/vertrag.js +163 -0
- package/dist/esm/inventory/webhook.d.ts +50 -0
- package/dist/esm/inventory/webhook.js +101 -0
- package/dist/esm/pos/artikel.d.ts +20 -0
- package/dist/esm/pos/artikel.js +8 -0
- package/dist/esm/pos/index.d.ts +1 -1
- package/dist/esm/pos/index.js +1 -1
- package/dist/esm/pos/texte.d.ts +41 -0
- package/dist/esm/pos/texte.js +28 -0
- package/dist/esm/stored/vokabular.d.ts +1 -1
- package/dist/esm/stored/vokabular.js +1 -1
- 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.schema.json +1 -1
- package/fixtures/invoice-texts.json +1 -1
- package/fixtures/pos-texts.json +36 -1
- package/fixtures/surface.json +161 -3
- 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 +1520 -0
- package/fixtures/v3/antworten/rechnungen.json +2 -2
- 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 +2 -2
- package/fixtures/v3/v3-vokabular.json +423 -6
- package/fixtures/v3/zahlbetrag-faelle.json +2 -2
- package/package.json +14 -2
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@kreiseck/kasseneck-api/inventory` – die Lager-API: Artikel, Standorte,
|
|
3
|
+
* Bestand und Lagerprotokoll lesen, Konto-Webhooks verwalten und eingehende
|
|
4
|
+
* Zustellungen pruefen (Backend Stufe 5a).
|
|
5
|
+
*
|
|
6
|
+
* Ein eigener Unterpfad wie `invoice`: der `api_key` eines Kontos gehoert auf
|
|
7
|
+
* einen **Server** (etwa das Backend eines Online-Shops) und soll nicht
|
|
8
|
+
* versehentlich in ein Browser-Buendel wandern.
|
|
9
|
+
*
|
|
10
|
+
* Mengen in Tausendstel der Basiseinheit, Geld in Cent, Einkaufspreise in
|
|
11
|
+
* Mikro-Euro; alles Ganzzahlen.
|
|
12
|
+
*/
|
|
13
|
+
export { createInventoryClient } from './api.js';
|
|
14
|
+
export { inventoryKeyAuth } from './auth.js';
|
|
15
|
+
export { isInventoryError, isInventoryErrorCode, inventoryErrorCode, inventoryFieldErrors, inventoryRetryAfterSec, } from './fehler.js';
|
|
16
|
+
export { createWebhook, deleteWebhook, getArticle, getStock, iterateArticles, iterateStock, iterateStockMovements, listArticles, listLocations, listStock, listStockMovements, listWebhookDeliveries, listWebhooks, lookupArticleByCode, rotateWebhookSecret, sendWebhookTest, updateWebhook, } from './endpunkte.js';
|
|
17
|
+
export { verifyInventoryWebhookSignature, parseInventoryWebhookEvent, WEBHOOK_SIGNATURE_HEADER, WEBHOOK_EVENT_HEADER, WEBHOOK_DELIVERY_HEADER, WEBHOOK_TOLERANCE_SEC, } from './webhook.js';
|
|
18
|
+
export { INVENTORY_ENDPOINTS, INVENTORY_ERROR_CODES, INVENTORY_REQUEST_ERROR_CODES, INVENTORY_WEBHOOK_EVENTS, INVENTORY_WEBHOOK_ENVELOPE_FIELDS, INVENTORY_WEBHOOK_LIMIT, INVENTORY_LIST_LIMIT_MAX, LOCATION_TYPES, STOCK_MOVEMENT_TYPES, STOCK_MOVEMENT_SOURCES, STOCK_CONDITIONS, STOCK_CHANGE_CAUSES, WEBHOOK_DELIVERY_STATUSES, } from './vertrag.js';
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Leser der Lager-Antworten: aus dem Draht `/v3` werden die Modelle in
|
|
3
|
+
* `typen.ts`. Paketintern, nicht Teil der Oberflaeche.
|
|
4
|
+
*
|
|
5
|
+
* Regel wie beim Lager an der Kasse (`pos/lager.ts`): eine Menge, ein Betrag
|
|
6
|
+
* oder eine Folgenummer ist eine Ganzzahl, sonst endet der Aufruf mit
|
|
7
|
+
* `KasseneckValidationError` (`scope: 'response'`). Ein Ersatzwert wie `0`
|
|
8
|
+
* waere eine falsche Aussage („kein Bestand“ statt „Antwort kaputt“). Texte
|
|
9
|
+
* sind nachsichtiger: ein fehlender oder fremder Wert wird `null`.
|
|
10
|
+
*
|
|
11
|
+
* Optionale Felder (Einkaufswerte, `externalIds` …) erscheinen im Modell nur,
|
|
12
|
+
* wenn der Server sie sendet: „fehlt“ heisst dort „kein Recht“, nicht „leer“.
|
|
13
|
+
*/
|
|
14
|
+
import { KasseneckValidationError } from '../client/errors.js';
|
|
15
|
+
import type { Article, InventoryWebhook, InventoryWebhookDelivery, InventoryWebhookTestDelivery, Location, StockBelowMinimumEventData, StockChangedEventData, StockLevel, StockMovement, StockValue } from './typen.js';
|
|
16
|
+
type Roh = Record<string, unknown>;
|
|
17
|
+
/** Ein Leser bekommt den Vorgang (fuer die Meldung) und den Pfad im Rumpf. */
|
|
18
|
+
export interface Ort {
|
|
19
|
+
name: string;
|
|
20
|
+
pfad: string;
|
|
21
|
+
}
|
|
22
|
+
export declare function antwortfehler(name: string, grund: string): KasseneckValidationError;
|
|
23
|
+
export declare function objekt(w: unknown): Roh | null;
|
|
24
|
+
export declare function artikel(ort: Ort, w: unknown): Article;
|
|
25
|
+
export declare function standort(ort: Ort, w: unknown): Location;
|
|
26
|
+
export declare function bestand(ort: Ort, w: unknown): StockLevel;
|
|
27
|
+
export declare function wert(ort: Ort, w: unknown): StockValue;
|
|
28
|
+
export declare function bewegung(ort: Ort, w: unknown): StockMovement;
|
|
29
|
+
export declare function webhook(ort: Ort, w: unknown): InventoryWebhook;
|
|
30
|
+
export declare function zustellung(ort: Ort, w: unknown): InventoryWebhookDelivery;
|
|
31
|
+
export declare function probeZustellung(ort: Ort, w: unknown, webhookId: string): InventoryWebhookTestDelivery;
|
|
32
|
+
export declare function bestandGeaendert(ort: Ort, w: unknown): StockChangedEventData;
|
|
33
|
+
export declare function unterMindestbestand(ort: Ort, w: unknown): StockBelowMinimumEventData;
|
|
34
|
+
/** Eine zugesagte Liste `data.<feld>`, Eintrag fuer Eintrag gelesen. */
|
|
35
|
+
export declare function liste<T>(name: string, daten: unknown, feld: string, lesen: (ort: Ort, e: unknown) => T): T[];
|
|
36
|
+
/** Der Cursor der naechsten Seite; `null` am Ende. Etwas anderes als Text oder `null` ist kaputt. */
|
|
37
|
+
export declare function naechsterCursor(name: string, daten: unknown): string | null;
|
|
38
|
+
export {};
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Leser der Lager-Antworten: aus dem Draht `/v3` werden die Modelle in
|
|
3
|
+
* `typen.ts`. Paketintern, nicht Teil der Oberflaeche.
|
|
4
|
+
*
|
|
5
|
+
* Regel wie beim Lager an der Kasse (`pos/lager.ts`): eine Menge, ein Betrag
|
|
6
|
+
* oder eine Folgenummer ist eine Ganzzahl, sonst endet der Aufruf mit
|
|
7
|
+
* `KasseneckValidationError` (`scope: 'response'`). Ein Ersatzwert wie `0`
|
|
8
|
+
* waere eine falsche Aussage („kein Bestand“ statt „Antwort kaputt“). Texte
|
|
9
|
+
* sind nachsichtiger: ein fehlender oder fremder Wert wird `null`.
|
|
10
|
+
*
|
|
11
|
+
* Optionale Felder (Einkaufswerte, `externalIds` …) erscheinen im Modell nur,
|
|
12
|
+
* wenn der Server sie sendet: „fehlt“ heisst dort „kein Recht“, nicht „leer“.
|
|
13
|
+
*/
|
|
14
|
+
import { KasseneckValidationError } from '../client/errors.js';
|
|
15
|
+
import { LOCATION_TYPES } from './vertrag.js';
|
|
16
|
+
const hat = (o, k) => Object.prototype.hasOwnProperty.call(o, k);
|
|
17
|
+
export function antwortfehler(name, grund) {
|
|
18
|
+
return new KasseneckValidationError(name, grund, 'response');
|
|
19
|
+
}
|
|
20
|
+
export function objekt(w) {
|
|
21
|
+
return w !== null && typeof w === 'object' && !Array.isArray(w) ? w : null;
|
|
22
|
+
}
|
|
23
|
+
/** Ein Eintrag, der ein Objekt sein muss. */
|
|
24
|
+
function eintrag(ort, w) {
|
|
25
|
+
const o = objekt(w);
|
|
26
|
+
if (!o)
|
|
27
|
+
throw antwortfehler(ort.name, `Antwort ist unbrauchbar (data.${ort.pfad} ist kein Objekt)`);
|
|
28
|
+
return o;
|
|
29
|
+
}
|
|
30
|
+
const text = (w) => (typeof w === 'string' ? w : null);
|
|
31
|
+
/** Eine Kennung, die da sein muss; ohne sie ist der Eintrag nicht zuzuordnen. */
|
|
32
|
+
function kennung(ort, feld, w) {
|
|
33
|
+
if (typeof w !== 'string' || w === '') {
|
|
34
|
+
throw antwortfehler(ort.name, `Antwort enthaelt keine Kennung (data.${ort.pfad}.${feld} fehlt)`);
|
|
35
|
+
}
|
|
36
|
+
return w;
|
|
37
|
+
}
|
|
38
|
+
/** Eine Ganzzahl, die da sein muss. */
|
|
39
|
+
function ganzzahl(ort, feld, w) {
|
|
40
|
+
if (typeof w !== 'number' || !Number.isSafeInteger(w)) {
|
|
41
|
+
throw antwortfehler(ort.name, `Antwort enthaelt keine ganze Zahl (data.${ort.pfad}.${feld})`);
|
|
42
|
+
}
|
|
43
|
+
return w;
|
|
44
|
+
}
|
|
45
|
+
/** Eine Ganzzahl oder `null`; fehlt der Wert, gilt `null`. */
|
|
46
|
+
function ganzzahlOderNull(ort, feld, w) {
|
|
47
|
+
if (w === undefined || w === null)
|
|
48
|
+
return null;
|
|
49
|
+
return ganzzahl(ort, feld, w);
|
|
50
|
+
}
|
|
51
|
+
/** Eine Textabbildung (`externalIds`, `metadata`, `variantAttributes`): nur Texteintraege. */
|
|
52
|
+
function textAbbildung(w) {
|
|
53
|
+
const o = objekt(w);
|
|
54
|
+
if (!o)
|
|
55
|
+
return undefined;
|
|
56
|
+
const raus = {};
|
|
57
|
+
for (const [k, v] of Object.entries(o))
|
|
58
|
+
if (typeof v === 'string')
|
|
59
|
+
raus[k] = v;
|
|
60
|
+
return raus;
|
|
61
|
+
}
|
|
62
|
+
// ---- Artikel ------------------------------------------------------------------
|
|
63
|
+
export function artikel(ort, w) {
|
|
64
|
+
const a = eintrag(ort, w);
|
|
65
|
+
let standorte = [];
|
|
66
|
+
if (a.stockLocationIds !== undefined && a.stockLocationIds !== null) {
|
|
67
|
+
if (!Array.isArray(a.stockLocationIds) || !a.stockLocationIds.every((s) => typeof s === 'string')) {
|
|
68
|
+
throw antwortfehler(ort.name, `Antwort ist unbrauchbar (data.${ort.pfad}.stockLocationIds ist keine Liste von Kennungen)`);
|
|
69
|
+
}
|
|
70
|
+
standorte = [...a.stockLocationIds];
|
|
71
|
+
}
|
|
72
|
+
const vatRate = a.vatRate;
|
|
73
|
+
if (vatRate !== undefined && vatRate !== null && (typeof vatRate !== 'number' || !Number.isFinite(vatRate))) {
|
|
74
|
+
throw antwortfehler(ort.name, `Antwort enthaelt keinen USt-Satz (data.${ort.pfad}.vatRate)`);
|
|
75
|
+
}
|
|
76
|
+
const raus = {
|
|
77
|
+
id: kennung(ort, 'id', a.id),
|
|
78
|
+
name: text(a.name),
|
|
79
|
+
unitPriceCents: ganzzahlOderNull(ort, 'unitPriceCents', a.unitPriceCents),
|
|
80
|
+
vatRate: typeof vatRate === 'number' ? vatRate : null,
|
|
81
|
+
unit: text(a.unit),
|
|
82
|
+
number: text(a.number),
|
|
83
|
+
ean: text(a.ean),
|
|
84
|
+
internalCode: text(a.internalCode),
|
|
85
|
+
groupId: text(a.groupId),
|
|
86
|
+
revenueGroupId: text(a.revenueGroupId),
|
|
87
|
+
stockTracked: a.stockTracked === true,
|
|
88
|
+
stockLocationIds: standorte,
|
|
89
|
+
minStock: ganzzahlOderNull(ort, 'minStock', a.minStock),
|
|
90
|
+
active: a.active !== false,
|
|
91
|
+
createdAt: text(a.createdAt),
|
|
92
|
+
updatedAt: text(a.updatedAt),
|
|
93
|
+
};
|
|
94
|
+
for (const feld of ['externalIds', 'metadata', 'variantAttributes']) {
|
|
95
|
+
const abb = textAbbildung(a[feld]);
|
|
96
|
+
if (abb)
|
|
97
|
+
raus[feld] = abb;
|
|
98
|
+
}
|
|
99
|
+
if (typeof a.variantGroupId === 'string')
|
|
100
|
+
raus.variantGroupId = a.variantGroupId;
|
|
101
|
+
// Nur mit dem Recht `costs`; ohne fehlt das Feld ganz.
|
|
102
|
+
if (hat(a, 'purchasePriceMicros'))
|
|
103
|
+
raus.purchasePriceMicros = ganzzahlOderNull(ort, 'purchasePriceMicros', a.purchasePriceMicros);
|
|
104
|
+
return raus;
|
|
105
|
+
}
|
|
106
|
+
// ---- Standorte ------------------------------------------------------------------
|
|
107
|
+
const TYPEN = new Set(LOCATION_TYPES);
|
|
108
|
+
const textOderNull = (w) => (typeof w === 'string' && w !== '' ? w : null);
|
|
109
|
+
export function standort(ort, w) {
|
|
110
|
+
const s = eintrag(ort, w);
|
|
111
|
+
const a = objekt(s.address);
|
|
112
|
+
const teile = a ? { street: textOderNull(a.street), zip: textOderNull(a.zip), city: textOderNull(a.city), country: textOderNull(a.country) } : null;
|
|
113
|
+
return {
|
|
114
|
+
id: kennung(ort, 'id', s.id),
|
|
115
|
+
name: typeof s.name === 'string' ? s.name : '',
|
|
116
|
+
type: typeof s.type === 'string' && TYPEN.has(s.type) ? s.type : null,
|
|
117
|
+
// Eine Adresse ohne einen einzigen Teil ist keine Adresse.
|
|
118
|
+
address: teile && Object.values(teile).some((t) => t !== null) ? teile : null,
|
|
119
|
+
licensePlate: textOderNull(s.licensePlate),
|
|
120
|
+
active: s.active !== false,
|
|
121
|
+
virtual: s.virtual === true,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
// ---- Bestand ------------------------------------------------------------------
|
|
125
|
+
export function bestand(ort, w) {
|
|
126
|
+
const b = eintrag(ort, w);
|
|
127
|
+
return {
|
|
128
|
+
articleId: kennung(ort, 'articleId', b.articleId),
|
|
129
|
+
locationId: kennung(ort, 'locationId', b.locationId),
|
|
130
|
+
onHand: ganzzahl(ort, 'onHand', b.onHand),
|
|
131
|
+
reserved: ganzzahl(ort, 'reserved', b.reserved),
|
|
132
|
+
available: ganzzahl(ort, 'available', b.available),
|
|
133
|
+
defective: ganzzahl(ort, 'defective', b.defective),
|
|
134
|
+
sequence: ganzzahl(ort, 'sequence', b.sequence),
|
|
135
|
+
updatedAt: text(b.updatedAt),
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
export function wert(ort, w) {
|
|
139
|
+
const v = eintrag(ort, w);
|
|
140
|
+
return {
|
|
141
|
+
articleId: kennung(ort, 'articleId', v.articleId),
|
|
142
|
+
stockValueCents: ganzzahl(ort, 'stockValueCents', v.stockValueCents),
|
|
143
|
+
averageCostMicros: ganzzahlOderNull(ort, 'averageCostMicros', v.averageCostMicros),
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
// ---- Bewegungen ------------------------------------------------------------------
|
|
147
|
+
function los(ort, w) {
|
|
148
|
+
const l = eintrag(ort, w);
|
|
149
|
+
const raus = {
|
|
150
|
+
lotId: text(l.lotId),
|
|
151
|
+
quantity: ganzzahl(ort, 'quantity', l.quantity),
|
|
152
|
+
expiresOn: text(l.expiresOn),
|
|
153
|
+
batch: text(l.batch),
|
|
154
|
+
serialNumber: text(l.serialNumber),
|
|
155
|
+
receivedAt: text(l.receivedAt),
|
|
156
|
+
};
|
|
157
|
+
if (hat(l, 'valueCents'))
|
|
158
|
+
raus.valueCents = ganzzahlOderNull(ort, 'valueCents', l.valueCents);
|
|
159
|
+
return raus;
|
|
160
|
+
}
|
|
161
|
+
export function bewegung(ort, w) {
|
|
162
|
+
const b = eintrag(ort, w);
|
|
163
|
+
const nachher = objekt(b.stockAfter);
|
|
164
|
+
const quelle = objekt(b.source);
|
|
165
|
+
const lose = b.lots === undefined || b.lots === null ? [] : b.lots;
|
|
166
|
+
if (!Array.isArray(lose))
|
|
167
|
+
throw antwortfehler(ort.name, `Antwort ist unbrauchbar (data.${ort.pfad}.lots ist keine Liste)`);
|
|
168
|
+
const nachherOrt = { name: ort.name, pfad: `${ort.pfad}.stockAfter` };
|
|
169
|
+
const raus = {
|
|
170
|
+
id: kennung(ort, 'id', b.id),
|
|
171
|
+
type: text(b.type),
|
|
172
|
+
articleId: text(b.articleId),
|
|
173
|
+
locationId: text(b.locationId),
|
|
174
|
+
condition: text(b.condition),
|
|
175
|
+
quantityDelta: ganzzahl(ort, 'quantityDelta', b.quantityDelta),
|
|
176
|
+
stockAfter: nachher
|
|
177
|
+
? { sellable: ganzzahl(nachherOrt, 'sellable', nachher.sellable), defective: ganzzahl(nachherOrt, 'defective', nachher.defective) }
|
|
178
|
+
: null,
|
|
179
|
+
operationId: text(b.operationId),
|
|
180
|
+
source: quelle
|
|
181
|
+
? {
|
|
182
|
+
type: text(quelle.type),
|
|
183
|
+
id: text(quelle.id),
|
|
184
|
+
register: text(quelle.register),
|
|
185
|
+
position: typeof quelle.position === 'number' && Number.isSafeInteger(quelle.position) ? quelle.position : null,
|
|
186
|
+
}
|
|
187
|
+
: null,
|
|
188
|
+
viennaDay: text(b.viennaDay),
|
|
189
|
+
time: text(b.time),
|
|
190
|
+
lots: lose.map((l, i) => los({ name: ort.name, pfad: `${ort.pfad}.lots[${i}]` }, l)),
|
|
191
|
+
};
|
|
192
|
+
if (hat(b, 'valueDeltaCents'))
|
|
193
|
+
raus.valueDeltaCents = ganzzahlOderNull(ort, 'valueDeltaCents', b.valueDeltaCents);
|
|
194
|
+
if (hat(b, 'consumedValueCents'))
|
|
195
|
+
raus.consumedValueCents = ganzzahlOderNull(ort, 'consumedValueCents', b.consumedValueCents);
|
|
196
|
+
return raus;
|
|
197
|
+
}
|
|
198
|
+
// ---- Webhooks ------------------------------------------------------------------
|
|
199
|
+
const zahlOderNull = (w) => (typeof w === 'number' && Number.isFinite(w) ? w : null);
|
|
200
|
+
export function webhook(ort, w) {
|
|
201
|
+
const h = eintrag(ort, w);
|
|
202
|
+
const z = objekt(h.lastDelivery);
|
|
203
|
+
return {
|
|
204
|
+
id: kennung(ort, 'id', h.id),
|
|
205
|
+
url: typeof h.url === 'string' ? h.url : '',
|
|
206
|
+
events: Array.isArray(h.events) ? h.events.filter((e) => typeof e === 'string') : [],
|
|
207
|
+
active: h.active !== false,
|
|
208
|
+
description: text(h.description),
|
|
209
|
+
createdAt: text(h.createdAt),
|
|
210
|
+
lastDelivery: z ? { at: text(z.at), status: text(z.status), statusCode: zahlOderNull(z.statusCode) } : null,
|
|
211
|
+
consecutiveFailures: typeof h.consecutiveFailures === 'number' && Number.isSafeInteger(h.consecutiveFailures) ? h.consecutiveFailures : 0,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
export function zustellung(ort, w) {
|
|
215
|
+
const z = eintrag(ort, w);
|
|
216
|
+
return {
|
|
217
|
+
deliveryId: kennung(ort, 'deliveryId', z.deliveryId),
|
|
218
|
+
webhookId: text(z.webhookId),
|
|
219
|
+
event: text(z.event),
|
|
220
|
+
eventId: text(z.eventId),
|
|
221
|
+
status: text(z.status),
|
|
222
|
+
attempts: typeof z.attempts === 'number' && Number.isSafeInteger(z.attempts) ? z.attempts : 0,
|
|
223
|
+
statusCode: zahlOderNull(z.statusCode),
|
|
224
|
+
response: text(z.response),
|
|
225
|
+
error: text(z.error),
|
|
226
|
+
createdAt: text(z.createdAt),
|
|
227
|
+
lastAttemptAt: text(z.lastAttemptAt),
|
|
228
|
+
nextAttemptAt: text(z.nextAttemptAt),
|
|
229
|
+
test: z.test === true,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
export function probeZustellung(ort, w, webhookId) {
|
|
233
|
+
const z = eintrag(ort, w);
|
|
234
|
+
return {
|
|
235
|
+
deliveryId: kennung(ort, 'deliveryId', z.deliveryId),
|
|
236
|
+
webhookId: typeof z.webhookId === 'string' && z.webhookId ? z.webhookId : webhookId,
|
|
237
|
+
status: text(z.status),
|
|
238
|
+
statusCode: zahlOderNull(z.statusCode),
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
// ---- Ereignisse ------------------------------------------------------------------
|
|
242
|
+
export function bestandGeaendert(ort, w) {
|
|
243
|
+
const d = eintrag(ort, w);
|
|
244
|
+
const ursache = d.cause;
|
|
245
|
+
if (typeof ursache !== 'string' || ursache === '')
|
|
246
|
+
throw antwortfehler(ort.name, `Ereignis ohne Ursache (${ort.pfad}.cause)`);
|
|
247
|
+
return { ...bestand(ort, d), cause: ursache, movementId: textOderNull(d.movementId) };
|
|
248
|
+
}
|
|
249
|
+
export function unterMindestbestand(ort, w) {
|
|
250
|
+
const d = eintrag(ort, w);
|
|
251
|
+
return {
|
|
252
|
+
articleId: kennung(ort, 'articleId', d.articleId),
|
|
253
|
+
locationId: kennung(ort, 'locationId', d.locationId),
|
|
254
|
+
available: ganzzahl(ort, 'available', d.available),
|
|
255
|
+
minStock: ganzzahl(ort, 'minStock', d.minStock),
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
// ---- Listen ------------------------------------------------------------------
|
|
259
|
+
/** Eine zugesagte Liste `data.<feld>`, Eintrag fuer Eintrag gelesen. */
|
|
260
|
+
export function liste(name, daten, feld, lesen) {
|
|
261
|
+
const roh = objekt(daten)?.[feld];
|
|
262
|
+
if (!Array.isArray(roh)) {
|
|
263
|
+
const grund = roh === undefined || roh === null
|
|
264
|
+
? `Antwort enthaelt keine Liste (data.${feld} fehlt)`
|
|
265
|
+
: `Antwort ist unbrauchbar (data.${feld} ist keine Liste)`;
|
|
266
|
+
throw antwortfehler(name, grund);
|
|
267
|
+
}
|
|
268
|
+
return roh.map((e, i) => lesen({ name, pfad: `${feld}[${i}]` }, e));
|
|
269
|
+
}
|
|
270
|
+
/** Der Cursor der naechsten Seite; `null` am Ende. Etwas anderes als Text oder `null` ist kaputt. */
|
|
271
|
+
export function naechsterCursor(name, daten) {
|
|
272
|
+
const c = objekt(daten)?.nextCursor;
|
|
273
|
+
if (c === undefined || c === null)
|
|
274
|
+
return null;
|
|
275
|
+
if (typeof c !== 'string' || c === '')
|
|
276
|
+
throw antwortfehler(name, 'Antwort ist unbrauchbar (data.nextCursor)');
|
|
277
|
+
return c;
|
|
278
|
+
}
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Die Modelle der Lager-API, so wie sie am Draht `/v3` stehen.
|
|
3
|
+
*
|
|
4
|
+
* **Ganzzahlen mit fester Skala:** Mengen in Tausendstel der Basiseinheit
|
|
5
|
+
* (`1000` = 1 Stueck, `250` = 0,250 kg), Geld in Cent, Einkaufspreise in
|
|
6
|
+
* Mikro-Euro (`1_000_000` = 1 €). Nichts davon wird geteilt oder gerundet;
|
|
7
|
+
* eine Bruchzahl in einer Antwort ist ein Antwortfehler
|
|
8
|
+
* (`KasseneckValidationError`, `scope: 'response'`), nie ein Ersatzwert.
|
|
9
|
+
*
|
|
10
|
+
* Zeitpunkte sind ISO 8601 in UTC (`2026-10-06T08:15:00.000Z`), Tage (Wiener
|
|
11
|
+
* Tag) `YYYY-MM-DD`.
|
|
12
|
+
*/
|
|
13
|
+
import type { InventoryWebhookEventType, LocationType, StockChangeCause, StockCondition, StockMovementSource, StockMovementType, WebhookDeliveryStatus } from './vertrag.js';
|
|
14
|
+
/**
|
|
15
|
+
* Ein Artikel, wie `getArticle`, `listArticles`, `lookupArticleByCode` und die
|
|
16
|
+
* Ereignisse `article.*` ihn senden.
|
|
17
|
+
*
|
|
18
|
+
* Optionale Felder fehlen ganz, wenn der Server sie nicht sendet:
|
|
19
|
+
* `purchasePriceMicros` nur mit dem Recht `costs` (Konto-Schalter
|
|
20
|
+
* `lagerApi.kosten`, sonst fehlt das Feld, nicht `null`), `externalIds`,
|
|
21
|
+
* `metadata`, `variantGroupId` und `variantAttributes` nur, wenn der Artikel
|
|
22
|
+
* sie traegt.
|
|
23
|
+
*/
|
|
24
|
+
export interface Article {
|
|
25
|
+
id: string;
|
|
26
|
+
name: string | null;
|
|
27
|
+
unitPriceCents: number | null;
|
|
28
|
+
/** USt-Satz in Prozent, z. B. `20`, `10`, `4.9`. */
|
|
29
|
+
vatRate: number | null;
|
|
30
|
+
unit: string | null;
|
|
31
|
+
number: string | null;
|
|
32
|
+
ean: string | null;
|
|
33
|
+
internalCode: string | null;
|
|
34
|
+
groupId: string | null;
|
|
35
|
+
revenueGroupId: string | null;
|
|
36
|
+
stockTracked: boolean;
|
|
37
|
+
/** Standorte, an denen der Artikel gefuehrt wird; leer = nur der Standard-Standort. */
|
|
38
|
+
stockLocationIds: string[];
|
|
39
|
+
/**
|
|
40
|
+
* Mindestbestand des Artikels in Tausendstel; `null` = keiner. Warnungen,
|
|
41
|
+
* `belowMinimum` und `stock.below_minimum` richten sich nach dem Mindestbestand je
|
|
42
|
+
* Standort, nicht nach diesem Wert.
|
|
43
|
+
*/
|
|
44
|
+
minStock: number | null;
|
|
45
|
+
active: boolean;
|
|
46
|
+
externalIds?: Record<string, string>;
|
|
47
|
+
metadata?: Record<string, string>;
|
|
48
|
+
variantGroupId?: string;
|
|
49
|
+
variantAttributes?: Record<string, string>;
|
|
50
|
+
createdAt: string | null;
|
|
51
|
+
updatedAt: string | null;
|
|
52
|
+
/**
|
|
53
|
+
* Einkaufspreis je Basiseinheit in Mikro-Euro (Wiederbeschaffungs- vor
|
|
54
|
+
* letztem vor Standard-Einkaufspreis); `null` = keiner hinterlegt. Fehlt
|
|
55
|
+
* ganz ohne das Recht `costs`.
|
|
56
|
+
*/
|
|
57
|
+
purchasePriceMicros?: number | null;
|
|
58
|
+
}
|
|
59
|
+
export interface ArticleListQuery {
|
|
60
|
+
/**
|
|
61
|
+
* Nur Artikel, die seitdem geaendert wurden (inklusive). Die Liste ist nach
|
|
62
|
+
* `updatedAt` aufsteigend sortiert: wer sich das `updatedAt` des letzten
|
|
63
|
+
* Treffers merkt und damit wieder fragt, verliert nichts.
|
|
64
|
+
*/
|
|
65
|
+
updatedSince?: string | Date;
|
|
66
|
+
groupId?: string;
|
|
67
|
+
variantGroupId?: string;
|
|
68
|
+
active?: boolean;
|
|
69
|
+
stockTracked?: boolean;
|
|
70
|
+
/** 1–200, Vorgabe des Servers 50. */
|
|
71
|
+
limit?: number;
|
|
72
|
+
/** `nextCursor` der vorigen Seite. */
|
|
73
|
+
cursor?: string;
|
|
74
|
+
}
|
|
75
|
+
export interface ArticlePage {
|
|
76
|
+
articles: Article[];
|
|
77
|
+
/** `null` = letzte Seite. */
|
|
78
|
+
nextCursor: string | null;
|
|
79
|
+
}
|
|
80
|
+
/** Ein Artikel per Code (`number`, `ean`, `internalCode`) oder per eigener Kennung. */
|
|
81
|
+
export type ArticleLookup = string | {
|
|
82
|
+
code: string;
|
|
83
|
+
} | {
|
|
84
|
+
externalSystem: string;
|
|
85
|
+
externalId: string;
|
|
86
|
+
};
|
|
87
|
+
export interface LocationAddress {
|
|
88
|
+
street: string | null;
|
|
89
|
+
zip: string | null;
|
|
90
|
+
city: string | null;
|
|
91
|
+
country: string | null;
|
|
92
|
+
}
|
|
93
|
+
export interface Location {
|
|
94
|
+
id: string;
|
|
95
|
+
name: string;
|
|
96
|
+
/** `null`: ein Typ, den diese Paketversion nicht kennt. */
|
|
97
|
+
type: LocationType | null;
|
|
98
|
+
address: LocationAddress | null;
|
|
99
|
+
/** Kennzeichen, nur bei `vehicle`. */
|
|
100
|
+
licensePlate: string | null;
|
|
101
|
+
/** `false` = aufgeloest. */
|
|
102
|
+
active: boolean;
|
|
103
|
+
/** `true` = Hauptstandort, den der Server ohne eigenes Dokument ergaenzt. */
|
|
104
|
+
virtual: boolean;
|
|
105
|
+
}
|
|
106
|
+
/** Bestand eines Artikels an einem Standort, Mengen in Tausendstel. */
|
|
107
|
+
export interface StockLevel {
|
|
108
|
+
articleId: string;
|
|
109
|
+
locationId: string;
|
|
110
|
+
/** Verkaufbare Menge am Standort. */
|
|
111
|
+
onHand: number;
|
|
112
|
+
/** Reserviert (Checkout im Shop). */
|
|
113
|
+
reserved: number;
|
|
114
|
+
/** `onHand - reserved`, vom Server gerechnet; darf negativ sein (die Kasse verkauft trotzdem). */
|
|
115
|
+
available: number;
|
|
116
|
+
defective: number;
|
|
117
|
+
/**
|
|
118
|
+
* Steigt bei jedem Schreiben dieses Bestands um 1. Einen gespeicherten Stand
|
|
119
|
+
* nur ueberschreiben, wenn `sequence` groesser ist: so richten doppelte oder
|
|
120
|
+
* verspaetete Ereignisse nichts an.
|
|
121
|
+
*/
|
|
122
|
+
sequence: number;
|
|
123
|
+
updatedAt: string | null;
|
|
124
|
+
}
|
|
125
|
+
/** Lagerwert eines Artikels, nur mit dem Recht `costs`. */
|
|
126
|
+
export interface StockValue {
|
|
127
|
+
articleId: string;
|
|
128
|
+
stockValueCents: number;
|
|
129
|
+
/** Durchschnittlicher Einstandspreis je Basiseinheit in Mikro-Euro; `null` bei Menge 0. */
|
|
130
|
+
averageCostMicros: number | null;
|
|
131
|
+
}
|
|
132
|
+
export interface StockResult {
|
|
133
|
+
stock: StockLevel[];
|
|
134
|
+
/** `null` ohne das Recht `costs` (der Server laesst das Feld weg), sonst die Werte. */
|
|
135
|
+
values: StockValue[] | null;
|
|
136
|
+
}
|
|
137
|
+
export interface StockListQuery {
|
|
138
|
+
locationId?: string;
|
|
139
|
+
articleId?: string;
|
|
140
|
+
/**
|
|
141
|
+
* `true`: nur Zeilen, deren `onHand` unter dem Mindestbestand ihres Standorts liegt
|
|
142
|
+
* (dieselbe Regel wie `stock.below_minimum`; `minStock` des Artikels zaehlt nicht).
|
|
143
|
+
*/
|
|
144
|
+
belowMinimum?: boolean;
|
|
145
|
+
/** Nur Zeilen, die sich seitdem geaendert haben (inklusive), nach `updatedAt` aufsteigend. */
|
|
146
|
+
changedSince?: string | Date;
|
|
147
|
+
limit?: number;
|
|
148
|
+
cursor?: string;
|
|
149
|
+
}
|
|
150
|
+
export interface StockPage extends StockResult {
|
|
151
|
+
nextCursor: string | null;
|
|
152
|
+
}
|
|
153
|
+
export interface StockMovementLot {
|
|
154
|
+
lotId: string | null;
|
|
155
|
+
quantity: number;
|
|
156
|
+
expiresOn: string | null;
|
|
157
|
+
batch: string | null;
|
|
158
|
+
serialNumber: string | null;
|
|
159
|
+
receivedAt: string | null;
|
|
160
|
+
/** Nur mit dem Recht `costs`. */
|
|
161
|
+
valueCents?: number | null;
|
|
162
|
+
}
|
|
163
|
+
export interface StockMovementSourceRef {
|
|
164
|
+
type: StockMovementSource | (string & {}) | null;
|
|
165
|
+
/** Kennung des Belegs, der Rechnung o. Ae. */
|
|
166
|
+
id: string | null;
|
|
167
|
+
/** Kasse, an der verkauft wurde. */
|
|
168
|
+
register: string | null;
|
|
169
|
+
/** Position im Beleg bzw. in der Rechnung. */
|
|
170
|
+
position: number | null;
|
|
171
|
+
}
|
|
172
|
+
/** Eine Zeile des Lagerprotokolls; neueste zuerst. */
|
|
173
|
+
export interface StockMovement {
|
|
174
|
+
id: string;
|
|
175
|
+
type: StockMovementType | (string & {}) | null;
|
|
176
|
+
articleId: string | null;
|
|
177
|
+
locationId: string | null;
|
|
178
|
+
condition: StockCondition | (string & {}) | null;
|
|
179
|
+
/** Mengenaenderung in Tausendstel (Abgang negativ). */
|
|
180
|
+
quantityDelta: number;
|
|
181
|
+
/** Bestand am Standort nach der Bewegung. */
|
|
182
|
+
stockAfter: {
|
|
183
|
+
sellable: number;
|
|
184
|
+
defective: number;
|
|
185
|
+
} | null;
|
|
186
|
+
operationId: string | null;
|
|
187
|
+
source: StockMovementSourceRef | null;
|
|
188
|
+
/** Wiener Tag `YYYY-MM-DD`. */
|
|
189
|
+
viennaDay: string | null;
|
|
190
|
+
time: string | null;
|
|
191
|
+
lots: StockMovementLot[];
|
|
192
|
+
/** Nur mit dem Recht `costs`. */
|
|
193
|
+
valueDeltaCents?: number | null;
|
|
194
|
+
/** Nur mit dem Recht `costs`. */
|
|
195
|
+
consumedValueCents?: number | null;
|
|
196
|
+
}
|
|
197
|
+
export interface StockMovementQuery {
|
|
198
|
+
/** Nur nach `articleId` **oder** `locationId` filtern, nicht beides. */
|
|
199
|
+
articleId?: string;
|
|
200
|
+
locationId?: string;
|
|
201
|
+
from?: string | Date;
|
|
202
|
+
to?: string | Date;
|
|
203
|
+
type?: StockMovementType;
|
|
204
|
+
source?: StockMovementSource;
|
|
205
|
+
limit?: number;
|
|
206
|
+
cursor?: string;
|
|
207
|
+
}
|
|
208
|
+
export interface StockMovementPage {
|
|
209
|
+
movements: StockMovement[];
|
|
210
|
+
nextCursor: string | null;
|
|
211
|
+
}
|
|
212
|
+
export interface InventoryWebhook {
|
|
213
|
+
id: string;
|
|
214
|
+
url: string;
|
|
215
|
+
events: (InventoryWebhookEventType | (string & {}))[];
|
|
216
|
+
active: boolean;
|
|
217
|
+
description: string | null;
|
|
218
|
+
createdAt: string | null;
|
|
219
|
+
/** Die letzte Zustellung; `null`, solange keine versucht wurde. */
|
|
220
|
+
lastDelivery: {
|
|
221
|
+
at: string | null;
|
|
222
|
+
status: WebhookDeliveryStatus | (string & {}) | null;
|
|
223
|
+
statusCode: number | null;
|
|
224
|
+
} | null;
|
|
225
|
+
/** Fehlversuche in Folge (wie bei Partner-Webhooks); steigt der Wert, stimmt beim Empfaenger etwas nicht. */
|
|
226
|
+
consecutiveFailures: number;
|
|
227
|
+
}
|
|
228
|
+
export interface CreateInventoryWebhookOptions {
|
|
229
|
+
/** Vollstaendige `https://`-Adresse, hoechstens 500 Zeichen. */
|
|
230
|
+
url: string;
|
|
231
|
+
/** Mindestens eines. */
|
|
232
|
+
events: (InventoryWebhookEventType | (string & {}))[];
|
|
233
|
+
/** Hoechstens 120 Zeichen. */
|
|
234
|
+
description?: string;
|
|
235
|
+
}
|
|
236
|
+
export interface InventoryWebhookPatch {
|
|
237
|
+
url?: string;
|
|
238
|
+
events?: (InventoryWebhookEventType | (string & {}))[];
|
|
239
|
+
active?: boolean;
|
|
240
|
+
/** `null` loescht die Beschreibung. */
|
|
241
|
+
description?: string | null;
|
|
242
|
+
}
|
|
243
|
+
export interface InventoryWebhookWithSecret {
|
|
244
|
+
webhook: InventoryWebhook;
|
|
245
|
+
/**
|
|
246
|
+
* Das Secret fuer [verifyInventoryWebhookSignature]. **Es kommt genau einmal**, bei
|
|
247
|
+
* `createWebhook` bzw. `rotateWebhookSecret`; danach gibt der Server es nie
|
|
248
|
+
* wieder aus. Nach einem Wechsel gilt sofort nur das neue.
|
|
249
|
+
*/
|
|
250
|
+
secret: string;
|
|
251
|
+
}
|
|
252
|
+
export interface InventoryWebhookList {
|
|
253
|
+
webhooks: InventoryWebhook[];
|
|
254
|
+
/** Die Ereignisse, die dieses Konto abonnieren kann. */
|
|
255
|
+
events: string[];
|
|
256
|
+
}
|
|
257
|
+
export interface InventoryWebhookTestDelivery {
|
|
258
|
+
/** Kennung der Zustellung (Kopfzeile `X-Kasseneck-Delivery`), wie bei Partner-Webhooks. */
|
|
259
|
+
deliveryId: string;
|
|
260
|
+
webhookId: string;
|
|
261
|
+
status: WebhookDeliveryStatus | (string & {}) | null;
|
|
262
|
+
statusCode: number | null;
|
|
263
|
+
}
|
|
264
|
+
export interface InventoryWebhookTestResult {
|
|
265
|
+
eventId: string;
|
|
266
|
+
event: string;
|
|
267
|
+
deliveries: InventoryWebhookTestDelivery[];
|
|
268
|
+
}
|
|
269
|
+
export interface InventoryWebhookDelivery {
|
|
270
|
+
/** Kennung der Zustellung (Kopfzeile `X-Kasseneck-Delivery`), wie bei Partner-Webhooks. */
|
|
271
|
+
deliveryId: string;
|
|
272
|
+
webhookId: string | null;
|
|
273
|
+
event: string | null;
|
|
274
|
+
eventId: string | null;
|
|
275
|
+
/** `dropped`: der Webhook wurde vor der Faelligkeit deaktiviert oder geloescht. */
|
|
276
|
+
status: WebhookDeliveryStatus | (string & {}) | null;
|
|
277
|
+
attempts: number;
|
|
278
|
+
statusCode: number | null;
|
|
279
|
+
/** Auszug der Antwort des Empfaengers, hoechstens 500 Zeichen. */
|
|
280
|
+
response: string | null;
|
|
281
|
+
error: string | null;
|
|
282
|
+
createdAt: string | null;
|
|
283
|
+
lastAttemptAt: string | null;
|
|
284
|
+
nextAttemptAt: string | null;
|
|
285
|
+
test: boolean;
|
|
286
|
+
}
|
|
287
|
+
/** Nutzlast von `stock.changed`: der aktuelle Stand, nicht die Aenderung. */
|
|
288
|
+
export interface StockChangedEventData extends StockLevel {
|
|
289
|
+
cause: StockChangeCause | (string & {});
|
|
290
|
+
/** Die Bewegung, aus der `cause` stammt; `null` bei `other` ohne Bewegung. */
|
|
291
|
+
movementId: string | null;
|
|
292
|
+
}
|
|
293
|
+
/** Nutzlast von `stock.below_minimum`: nur beim Unterschreiten, nicht bei jedem weiteren Sinken. */
|
|
294
|
+
export interface StockBelowMinimumEventData {
|
|
295
|
+
articleId: string;
|
|
296
|
+
locationId: string;
|
|
297
|
+
available: number;
|
|
298
|
+
/**
|
|
299
|
+
* Der Mindestbestand des Standorts in Tausendstel – die unterschrittene Schwelle,
|
|
300
|
+
* gemessen am Bestand `onHand`. Der `minStock` des Artikels allein loest nie aus.
|
|
301
|
+
*/
|
|
302
|
+
minStock: number;
|
|
303
|
+
}
|
|
304
|
+
export interface InventoryWebhookEnvelope<T extends InventoryWebhookEventType, D> {
|
|
305
|
+
/** `evt_…`: darauf entdoppeln. */
|
|
306
|
+
id: string;
|
|
307
|
+
type: T;
|
|
308
|
+
/** Millisekunden seit 1970 (UTC). */
|
|
309
|
+
createdAt: number;
|
|
310
|
+
/** Das Konto, dessen Lager das Ereignis betrifft. */
|
|
311
|
+
accountId: string;
|
|
312
|
+
/**
|
|
313
|
+
* `true` nur bei einer Probe aus `sendWebhookTest` (erfundene Nutzlast).
|
|
314
|
+
* An den Anfang jedes Handlers: `if (event.test) return;`
|
|
315
|
+
*/
|
|
316
|
+
test: boolean;
|
|
317
|
+
data: D;
|
|
318
|
+
}
|
|
319
|
+
export type InventoryWebhookEvent = InventoryWebhookEnvelope<'stock.changed', StockChangedEventData> | InventoryWebhookEnvelope<'stock.below_minimum', StockBelowMinimumEventData> | InventoryWebhookEnvelope<'article.created', Article> | InventoryWebhookEnvelope<'article.updated', Article> | InventoryWebhookEnvelope<'article.deactivated', Article>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Die Modelle der Lager-API, so wie sie am Draht `/v3` stehen.
|
|
3
|
+
*
|
|
4
|
+
* **Ganzzahlen mit fester Skala:** Mengen in Tausendstel der Basiseinheit
|
|
5
|
+
* (`1000` = 1 Stueck, `250` = 0,250 kg), Geld in Cent, Einkaufspreise in
|
|
6
|
+
* Mikro-Euro (`1_000_000` = 1 €). Nichts davon wird geteilt oder gerundet;
|
|
7
|
+
* eine Bruchzahl in einer Antwort ist ein Antwortfehler
|
|
8
|
+
* (`KasseneckValidationError`, `scope: 'response'`), nie ein Ersatzwert.
|
|
9
|
+
*
|
|
10
|
+
* Zeitpunkte sind ISO 8601 in UTC (`2026-10-06T08:15:00.000Z`), Tage (Wiener
|
|
11
|
+
* Tag) `YYYY-MM-DD`.
|
|
12
|
+
*/
|
|
13
|
+
export {};
|