@kreiseck/kasseneck-api 1.7.1 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +106 -0
- package/README.md +170 -1
- package/dist/cjs/client/aufrufe.d.ts +9 -7
- package/dist/cjs/client/aufrufe.js +57 -2
- package/dist/cjs/client/errors.js +2 -0
- package/dist/cjs/client/index.d.ts +1 -1
- package/dist/cjs/client/index.js +2 -1
- package/dist/cjs/client/transport.d.ts +27 -3
- package/dist/cjs/client/transport.js +29 -3
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/index.js +3 -2
- package/dist/cjs/inventory/api.d.ts +21 -2
- package/dist/cjs/inventory/api.js +22 -3
- package/dist/cjs/inventory/fehler.d.ts +6 -0
- package/dist/cjs/inventory/fehler.js +12 -0
- package/dist/cjs/inventory/index.d.ts +6 -4
- package/dist/cjs/inventory/index.js +33 -3
- package/dist/cjs/inventory/inventur.d.ts +104 -0
- package/dist/cjs/inventory/inventur.js +245 -0
- package/dist/cjs/inventory/lesen.d.ts +20 -1
- package/dist/cjs/inventory/lesen.js +307 -0
- package/dist/cjs/inventory/typen.d.ts +410 -1
- package/dist/cjs/inventory/vertrag.d.ts +94 -8
- package/dist/cjs/inventory/vertrag.js +117 -5
- package/dist/cjs/invoice/vertrag.d.ts +1 -1
- package/dist/cjs/invoice/vertrag.js +2 -0
- package/dist/cjs/models/cancellation.d.ts +1 -1
- package/dist/cjs/models/cancellation.js +2 -0
- package/dist/cjs/models/payment-errors.d.ts +1 -1
- package/dist/cjs/models/payment-errors.js +2 -0
- package/dist/cjs/models/receipt-email.d.ts +1 -1
- package/dist/cjs/models/receipt-email.js +2 -0
- package/dist/cjs/models/receipt-errors.d.ts +1 -1
- package/dist/cjs/models/receipt-errors.js +2 -0
- package/dist/cjs/pos/errors.d.ts +4 -3
- package/dist/cjs/pos/errors.js +23 -2
- package/dist/cjs/pos/index.d.ts +4 -1
- package/dist/cjs/pos/index.js +9 -2
- package/dist/cjs/pos/inventur.d.ts +104 -0
- package/dist/cjs/pos/inventur.js +117 -0
- package/dist/cjs/pos/texte.d.ts +82 -0
- package/dist/cjs/pos/texte.js +35 -0
- package/dist/cjs/register/errors.d.ts +1 -1
- package/dist/cjs/register/errors.js +2 -0
- 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 +9 -7
- package/dist/esm/client/aufrufe.js +57 -2
- package/dist/esm/client/errors.js +2 -0
- package/dist/esm/client/index.d.ts +1 -1
- package/dist/esm/client/index.js +1 -1
- package/dist/esm/client/transport.d.ts +27 -3
- package/dist/esm/client/transport.js +28 -3
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/inventory/api.d.ts +21 -2
- package/dist/esm/inventory/api.js +23 -4
- package/dist/esm/inventory/fehler.d.ts +6 -0
- package/dist/esm/inventory/fehler.js +11 -0
- package/dist/esm/inventory/index.d.ts +6 -4
- package/dist/esm/inventory/index.js +5 -3
- package/dist/esm/inventory/inventur.d.ts +104 -0
- package/dist/esm/inventory/inventur.js +226 -0
- package/dist/esm/inventory/lesen.d.ts +20 -1
- package/dist/esm/inventory/lesen.js +301 -0
- package/dist/esm/inventory/typen.d.ts +410 -1
- package/dist/esm/inventory/vertrag.d.ts +94 -8
- package/dist/esm/inventory/vertrag.js +116 -4
- package/dist/esm/invoice/vertrag.d.ts +1 -1
- package/dist/esm/invoice/vertrag.js +2 -0
- package/dist/esm/models/cancellation.d.ts +1 -1
- package/dist/esm/models/cancellation.js +2 -0
- package/dist/esm/models/payment-errors.d.ts +1 -1
- package/dist/esm/models/payment-errors.js +2 -0
- package/dist/esm/models/receipt-email.d.ts +1 -1
- package/dist/esm/models/receipt-email.js +2 -0
- package/dist/esm/models/receipt-errors.d.ts +1 -1
- package/dist/esm/models/receipt-errors.js +2 -0
- package/dist/esm/pos/errors.d.ts +4 -3
- package/dist/esm/pos/errors.js +23 -2
- package/dist/esm/pos/index.d.ts +4 -1
- package/dist/esm/pos/index.js +2 -1
- package/dist/esm/pos/inventur.d.ts +104 -0
- package/dist/esm/pos/inventur.js +109 -0
- package/dist/esm/pos/texte.d.ts +82 -0
- package/dist/esm/pos/texte.js +35 -0
- package/dist/esm/register/errors.d.ts +1 -1
- package/dist/esm/register/errors.js +2 -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 +94 -1
- package/fixtures/stocktake-quantity-cases.json +55 -0
- package/fixtures/surface.json +151 -8
- package/fixtures/v3/antworten/belege.json +1 -1
- package/fixtures/v3/antworten/belegmail.json +1 -1
- package/fixtures/v3/antworten/kasse-belege.json +1 -1
- package/fixtures/v3/antworten/kasse.json +2958 -3
- package/fixtures/v3/antworten/lager.json +1727 -3
- package/fixtures/v3/antworten/rechnungen.json +1 -1
- package/fixtures/v3/antworten/storno.json +1 -1
- package/fixtures/v3/stored/belege.json +1 -1
- package/fixtures/v3/stored/kasse.json +414 -3
- package/fixtures/v3/stored/rechnungen.json +1 -1
- package/fixtures/v3/v3-vokabular.json +475 -8
- package/fixtures/v3/zahlbetrag-faelle.json +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Inventur der Lager-API (Lager-Kern Stufe 3): anlegen, zaehlen, Zaehlungen
|
|
4
|
+
* stornieren, pruefen, nachzaehlen, abschliessen, abbrechen, Inventurprotokoll
|
|
5
|
+
* holen; dazu die Listen der Inventuren, Positionen und Zaehlungen.
|
|
6
|
+
*
|
|
7
|
+
* Ablauf: `createStocktake` → `recordStocktakeCount` (beliebig oft, auch von
|
|
8
|
+
* mehreren Geraeten; je Position addiert) → `reviewStocktake` (erst jetzt Soll
|
|
9
|
+
* und Differenz) → bei Bedarf `recountStocktake` und erneut `reviewStocktake`
|
|
10
|
+
* → `closeStocktake` → `getStocktakePdf`. Pruefen und Abschliessen rechnet der
|
|
11
|
+
* Server im Hintergrund: die Antwort traegt den Zwischenstand
|
|
12
|
+
* (`review.complete: false` bzw. `status: 'closing'`), `getStocktake` den
|
|
13
|
+
* Fortgang.
|
|
14
|
+
*
|
|
15
|
+
* **Blind:** vor `review` traegt keine Antwort ein Soll; `blind: false` zeigt
|
|
16
|
+
* beim Zaehlen nur den heutigen Buchbestand (`bookStockNow`), nie das Soll
|
|
17
|
+
* zur Referenzzeit.
|
|
18
|
+
*
|
|
19
|
+
* Rechte wie in Stufe 5b: Lesen mit jedem Schluessel, alles Schreibende (auch
|
|
20
|
+
* Zaehlen) mit dem Konto-Schalter „Lager-API schreiben“ (Test-Schluessel
|
|
21
|
+
* immer), sonst `inventory_api_not_enabled`; Werte und das Protokoll mit
|
|
22
|
+
* Werten nur mit `lagerApi.kosten`.
|
|
23
|
+
*
|
|
24
|
+
* **Vor dem Senden geprueft wird nur, was ohne Netz sicher falsch ist:**
|
|
25
|
+
* `idempotencyKey` (Pflicht bei jedem Schreiben, 1–120 Zeichen), fehlende
|
|
26
|
+
* Kennungen (`stocktakeId`, `articleId`, `countId`, `locationId`), `scope`
|
|
27
|
+
* kein Objekt, eine Menge, die keine Ganzzahl ist (Tausendstel), ein leerer
|
|
28
|
+
* Grund, `items` beim Nachzaehlen leer oder ohne `articleId`,
|
|
29
|
+
* `serialNumbers` keine Liste, `uncountedAsZero` kein Wahrheitswert. Alles
|
|
30
|
+
* Fachliche (Umfang, Stichtag, Seriennummern, Zustand) prueft der Server.
|
|
31
|
+
*
|
|
32
|
+
* **Wiederholen** wie bei jedem Schreiben: nach `outcome: 'unknown'` denselben
|
|
33
|
+
* Aufruf mit **demselben** `idempotencyKey`. Eine Zaehlung wirkt dann genau
|
|
34
|
+
* einmal (das Zaehldokument ist selbst der Nachweis); ein neuer Schluessel
|
|
35
|
+
* zaehlte die Ware ein zweites Mal.
|
|
36
|
+
*/
|
|
37
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
+
exports.createStocktake = createStocktake;
|
|
39
|
+
exports.listStocktakes = listStocktakes;
|
|
40
|
+
exports.iterateStocktakes = iterateStocktakes;
|
|
41
|
+
exports.getStocktake = getStocktake;
|
|
42
|
+
exports.listStocktakeItems = listStocktakeItems;
|
|
43
|
+
exports.iterateStocktakeItems = iterateStocktakeItems;
|
|
44
|
+
exports.listStocktakeCounts = listStocktakeCounts;
|
|
45
|
+
exports.iterateStocktakeCounts = iterateStocktakeCounts;
|
|
46
|
+
exports.zaehlungPruefen = zaehlungPruefen;
|
|
47
|
+
exports.stornoPruefen = stornoPruefen;
|
|
48
|
+
exports.recordStocktakeCount = recordStocktakeCount;
|
|
49
|
+
exports.voidStocktakeCount = voidStocktakeCount;
|
|
50
|
+
exports.reviewStocktake = reviewStocktake;
|
|
51
|
+
exports.recountStocktake = recountStocktake;
|
|
52
|
+
exports.closeStocktake = closeStocktake;
|
|
53
|
+
exports.cancelStocktake = cancelStocktake;
|
|
54
|
+
exports.getStocktakePdf = getStocktakePdf;
|
|
55
|
+
const endpunkte_js_1 = require("./endpunkte.js");
|
|
56
|
+
const lesen_js_1 = require("./lesen.js");
|
|
57
|
+
const schreiben_js_1 = require("./schreiben.js");
|
|
58
|
+
const istObjekt = (w) => w !== null && typeof w === 'object' && !Array.isArray(w);
|
|
59
|
+
const kopfAus = (name, daten) => (0, lesen_js_1.inventur)({ name, pfad: 'stocktake' }, (0, lesen_js_1.objekt)(daten)?.stocktake);
|
|
60
|
+
/** Ein Grund (Storno, Nachzaehlen, Abbruch): nicht leer. Die Laenge prueft der Server. */
|
|
61
|
+
function grund(name, w) {
|
|
62
|
+
if (typeof w !== 'string' || w.trim() === '')
|
|
63
|
+
throw (0, endpunkte_js_1.anfragefehler)(name, 'reason fehlt (Grund mit 1 bis 500 Zeichen)');
|
|
64
|
+
}
|
|
65
|
+
/** Schreibende Anfrage mit `stocktakeId`: Schluessel und Kennung geprueft, flach wie gesendet. */
|
|
66
|
+
function schreibend(name, request) {
|
|
67
|
+
const p = (0, schreiben_js_1.anfrage)(name, request);
|
|
68
|
+
(0, schreiben_js_1.schluessel)(name, p, { pflicht: true });
|
|
69
|
+
(0, endpunkte_js_1.kennung)(name, 'stocktakeId', p.stocktakeId);
|
|
70
|
+
return p;
|
|
71
|
+
}
|
|
72
|
+
/** Eine Liste unter einer Inventur: `stocktakeId` Pflicht, sonst wie jede Abfrage. */
|
|
73
|
+
function abfrageMitInventur(name, query) {
|
|
74
|
+
if (!istObjekt(query))
|
|
75
|
+
throw (0, endpunkte_js_1.anfragefehler)(name, 'Abfrage mit stocktakeId erwartet');
|
|
76
|
+
const p = (0, endpunkte_js_1.abfrage)(name, query);
|
|
77
|
+
(0, endpunkte_js_1.kennung)(name, 'stocktakeId', p.stocktakeId);
|
|
78
|
+
return p;
|
|
79
|
+
}
|
|
80
|
+
// ---- Anlegen und Lesen -------------------------------------------------------------
|
|
81
|
+
/**
|
|
82
|
+
* Legt eine Inventur an. Je Standort hoechstens eine offene
|
|
83
|
+
* (`stocktake_location_busy`, `data.stocktakeId` nennt sie); Umfang `groups`
|
|
84
|
+
* bzw. `articles` nur mit bestandsgefuehrten Artikeln (`article_not_tracked`).
|
|
85
|
+
* Jede Inventur beginnt in `counting`.
|
|
86
|
+
*/
|
|
87
|
+
async function createStocktake(transport, request) {
|
|
88
|
+
const name = 'createStocktake';
|
|
89
|
+
const p = (0, schreiben_js_1.anfrage)(name, request);
|
|
90
|
+
(0, schreiben_js_1.schluessel)(name, p, { pflicht: true });
|
|
91
|
+
(0, endpunkte_js_1.kennung)(name, 'locationId', p.locationId);
|
|
92
|
+
if (!istObjekt(p.scope))
|
|
93
|
+
throw (0, endpunkte_js_1.anfragefehler)(name, 'scope fehlt oder ist kein Objekt { type, groupIds?, articleIds? }');
|
|
94
|
+
return kopfAus(name, await transport(name, p));
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Inventuren des Kontos. Ohne `updatedSince` zuletzt geaenderte zuerst; mit
|
|
98
|
+
* `updatedSince` aufsteigend nach `updatedAt` und inklusive (Abgleich: das
|
|
99
|
+
* groesste gesehene `updatedAt` als naechstes `updatedSince`). Filter
|
|
100
|
+
* `status` und `locationId`.
|
|
101
|
+
*/
|
|
102
|
+
async function listStocktakes(transport, query) {
|
|
103
|
+
const name = 'listStocktakes';
|
|
104
|
+
const daten = await transport(name, (0, endpunkte_js_1.abfrage)(name, query));
|
|
105
|
+
return { stocktakes: (0, lesen_js_1.liste)(name, daten, 'stocktakes', lesen_js_1.inventur), nextCursor: (0, lesen_js_1.naechsterCursor)(name, daten) };
|
|
106
|
+
}
|
|
107
|
+
/** Alle Inventuren der Abfrage, Seite fuer Seite, in der Reihenfolge von [listStocktakes]. */
|
|
108
|
+
function iterateStocktakes(transport, query) {
|
|
109
|
+
const { cursor, ...rest } = query ?? {};
|
|
110
|
+
return (0, endpunkte_js_1.seitenweise)('listStocktakes', cursor, async (c) => {
|
|
111
|
+
const s = await listStocktakes(transport, { ...rest, cursor: c });
|
|
112
|
+
return { eintraege: s.stocktakes, nextCursor: s.nextCursor };
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
/** Eine Inventur samt `progress.counted` (gezaehlte Positionen). */
|
|
116
|
+
async function getStocktake(transport, stocktakeId) {
|
|
117
|
+
const name = 'getStocktake';
|
|
118
|
+
return kopfAus(name, await transport(name, { stocktakeId: (0, endpunkte_js_1.kennung)(name, 'stocktakeId', stocktakeId) }));
|
|
119
|
+
}
|
|
120
|
+
/** Positionen einer Inventur nach Kennung; `openOnly` = ungezaehlt bzw. in `review` zum Nachzaehlen offen. */
|
|
121
|
+
async function listStocktakeItems(transport, query) {
|
|
122
|
+
const name = 'listStocktakeItems';
|
|
123
|
+
const daten = await transport(name, abfrageMitInventur(name, query));
|
|
124
|
+
return { items: (0, lesen_js_1.liste)(name, daten, 'items', lesen_js_1.inventurPosition), nextCursor: (0, lesen_js_1.naechsterCursor)(name, daten) };
|
|
125
|
+
}
|
|
126
|
+
function iterateStocktakeItems(transport, query) {
|
|
127
|
+
const { cursor, ...rest } = query;
|
|
128
|
+
return (0, endpunkte_js_1.seitenweise)('listStocktakeItems', cursor, async (c) => {
|
|
129
|
+
const s = await listStocktakeItems(transport, { ...rest, cursor: c });
|
|
130
|
+
return { eintraege: s.items, nextCursor: s.nextCursor };
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
/** Zaehlungen einer Inventur, neueste zuerst, auch stornierte; mit `articleId` nur die des Artikels. */
|
|
134
|
+
async function listStocktakeCounts(transport, query) {
|
|
135
|
+
const name = 'listStocktakeCounts';
|
|
136
|
+
const daten = await transport(name, abfrageMitInventur(name, query));
|
|
137
|
+
return { counts: (0, lesen_js_1.liste)(name, daten, 'counts', lesen_js_1.inventurZaehlung), nextCursor: (0, lesen_js_1.naechsterCursor)(name, daten) };
|
|
138
|
+
}
|
|
139
|
+
function iterateStocktakeCounts(transport, query) {
|
|
140
|
+
const { cursor, ...rest } = query;
|
|
141
|
+
return (0, endpunkte_js_1.seitenweise)('listStocktakeCounts', cursor, async (c) => {
|
|
142
|
+
const s = await listStocktakeCounts(transport, { ...rest, cursor: c });
|
|
143
|
+
return { eintraege: s.counts, nextCursor: s.nextCursor };
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
// ---- Zaehlen und Stornieren ------------------------------------------------------
|
|
147
|
+
/** Pruefung einer Zaehlung vor dem Senden, geteilt mit dem Kassenweg. Paketintern. */
|
|
148
|
+
function zaehlungPruefen(name, p) {
|
|
149
|
+
(0, endpunkte_js_1.kennung)(name, 'articleId', p.articleId);
|
|
150
|
+
(0, schreiben_js_1.ganz)(name, 'quantity', p.quantity, { pflicht: true });
|
|
151
|
+
if (p.serialNumbers !== undefined && p.serialNumbers !== null && !Array.isArray(p.serialNumbers)) {
|
|
152
|
+
throw (0, endpunkte_js_1.anfragefehler)(name, 'serialNumbers muss eine Liste sein');
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/** Pruefung eines Stornos vor dem Senden, geteilt mit dem Kassenweg. Paketintern. */
|
|
156
|
+
function stornoPruefen(name, p) {
|
|
157
|
+
(0, endpunkte_js_1.kennung)(name, 'countId', p.countId);
|
|
158
|
+
grund(name, p.reason);
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Eine Zaehlung. Antwort: die Zaehlung und ihre Position danach (Summe der
|
|
162
|
+
* Runde in `item.quantity`). Gezaehlt wird in `counting`, in `review` nur an
|
|
163
|
+
* Positionen, die zum Nachzaehlen frei sind (sonst `stocktake_not_open`).
|
|
164
|
+
*/
|
|
165
|
+
async function recordStocktakeCount(transport, request) {
|
|
166
|
+
const name = 'recordStocktakeCount';
|
|
167
|
+
const p = schreibend(name, request);
|
|
168
|
+
zaehlungPruefen(name, p);
|
|
169
|
+
return (0, lesen_js_1.zaehlungMitPosition)(name, await transport(name, p));
|
|
170
|
+
}
|
|
171
|
+
/** Storniert eine Zaehlung mit Grund; die Position wird aus den uebrigen Zaehlungen neu summiert. */
|
|
172
|
+
async function voidStocktakeCount(transport, request) {
|
|
173
|
+
const name = 'voidStocktakeCount';
|
|
174
|
+
const p = schreibend(name, request);
|
|
175
|
+
stornoPruefen(name, p);
|
|
176
|
+
return (0, lesen_js_1.zaehlungMitPosition)(name, await transport(name, p));
|
|
177
|
+
}
|
|
178
|
+
// ---- Pruefen bis Abbruch ------------------------------------------------------------
|
|
179
|
+
/**
|
|
180
|
+
* Pruefen: `counting` → `review`, in `review` neu rechnen (nach dem
|
|
181
|
+
* Nachzaehlen). Die Antwort kommt sofort mit `review.complete: false`; Soll
|
|
182
|
+
* und Differenz stehen an den Positionen, sobald `complete` `true` ist.
|
|
183
|
+
*/
|
|
184
|
+
async function reviewStocktake(transport, request) {
|
|
185
|
+
const name = 'reviewStocktake';
|
|
186
|
+
return kopfAus(name, await transport(name, schreibend(name, request)));
|
|
187
|
+
}
|
|
188
|
+
/** Nachzaehlen: je genannter Position eine neue Runde; danach zaehlen und erneut [reviewStocktake]. */
|
|
189
|
+
async function recountStocktake(transport, request) {
|
|
190
|
+
const name = 'recountStocktake';
|
|
191
|
+
const p = schreibend(name, request);
|
|
192
|
+
if (!Array.isArray(p.items) || p.items.length === 0)
|
|
193
|
+
throw (0, endpunkte_js_1.anfragefehler)(name, 'items fehlt oder ist leer');
|
|
194
|
+
p.items.forEach((x, i) => {
|
|
195
|
+
if (!istObjekt(x))
|
|
196
|
+
throw (0, endpunkte_js_1.anfragefehler)(name, `items[${i}] muss ein Objekt sein`);
|
|
197
|
+
(0, endpunkte_js_1.kennung)(name, `items[${i}].articleId`, x.articleId);
|
|
198
|
+
});
|
|
199
|
+
grund(name, p.reason);
|
|
200
|
+
return kopfAus(name, await transport(name, p));
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Abschliessen (nur aus `review`, sonst `stocktake_not_in_review`; offene
|
|
204
|
+
* Nachzaehlungen ergeben `stocktake_recount_open`). Der Server bucht in
|
|
205
|
+
* Teilen weiter; die Antwort traegt meist `status: 'closing'`. Ein erneuter
|
|
206
|
+
* Aufruf waehrend `closing` stoesst den Abschluss wieder an.
|
|
207
|
+
*/
|
|
208
|
+
async function closeStocktake(transport, request) {
|
|
209
|
+
const name = 'closeStocktake';
|
|
210
|
+
const p = schreibend(name, request);
|
|
211
|
+
if (p.uncountedAsZero !== undefined && typeof p.uncountedAsZero !== 'boolean') {
|
|
212
|
+
throw (0, endpunkte_js_1.anfragefehler)(name, 'uncountedAsZero muss true oder false sein');
|
|
213
|
+
}
|
|
214
|
+
const daten = await transport(name, p);
|
|
215
|
+
return { stocktake: kopfAus(name, daten), warnings: (0, lesen_js_1.inventurWarnungen)({ name, pfad: 'warnings' }, (0, lesen_js_1.objekt)(daten)?.warnings) };
|
|
216
|
+
}
|
|
217
|
+
/** Abbrechen mit Grund (aus `counting` oder `review`); der Standort ist danach frei. */
|
|
218
|
+
async function cancelStocktake(transport, request) {
|
|
219
|
+
const name = 'cancelStocktake';
|
|
220
|
+
const p = schreibend(name, request);
|
|
221
|
+
grund(name, p.reason);
|
|
222
|
+
return kopfAus(name, await transport(name, p));
|
|
223
|
+
}
|
|
224
|
+
// ---- Protokoll ------------------------------------------------------------------
|
|
225
|
+
/**
|
|
226
|
+
* Das Inventurprotokoll (PDF), erst nach dem Abschluss (`stocktake_not_closed`,
|
|
227
|
+
* auch fuer eine abgebrochene Inventur). Mit `lagerApi.kosten` die Fassung mit
|
|
228
|
+
* Werten, sonst die nur mit Mengen. Bis 9 MiB kommt die Datei selbst
|
|
229
|
+
* (`kind: 'pdf'`), darueber ein signierter Lese-Link fuer 15 Minuten
|
|
230
|
+
* (`kind: 'download'`); die geladene Datei an `download.sha256` pruefen.
|
|
231
|
+
*
|
|
232
|
+
* Als freie Funktion braucht sie den Transport aus `createPdfOrDataTransport`
|
|
233
|
+
* (Paketwurzel): ein JSON-Transport laese die Datei als Text, ein reiner
|
|
234
|
+
* Binaer-Transport kennt den Link nicht. [createInventoryClient] bindet ihn selbst.
|
|
235
|
+
*/
|
|
236
|
+
async function getStocktakePdf(transport, stocktakeId) {
|
|
237
|
+
const name = 'getStocktakePdf';
|
|
238
|
+
const ergebnis = await transport(name, { stocktakeId: (0, endpunkte_js_1.kennung)(name, 'stocktakeId', stocktakeId) });
|
|
239
|
+
if ('pdf' in ergebnis)
|
|
240
|
+
return { kind: 'pdf', pdf: ergebnis.pdf };
|
|
241
|
+
const link = (0, lesen_js_1.objekt)(ergebnis.data)?.download;
|
|
242
|
+
if (link === undefined || link === null)
|
|
243
|
+
throw (0, lesen_js_1.antwortfehler)(name, 'Antwort ist weder ein PDF noch ein Lese-Link (data.download fehlt)');
|
|
244
|
+
return { kind: 'download', download: (0, lesen_js_1.protokollLink)(name, link) };
|
|
245
|
+
}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* wenn der Server sie sendet: „fehlt“ heisst dort „kein Recht“, nicht „leer“.
|
|
13
13
|
*/
|
|
14
14
|
import { KasseneckValidationError } from '../client/errors.js';
|
|
15
|
-
import type { Article, GoodsReceiptPreviewLine, InventoryShortfall, InventoryWebhook, InventoryWebhookDelivery, InventoryWebhookTestDelivery, Location, Reservation, StockBelowMinimumEventData, StockChangedEventData, StockLevel, StockMovement, StockOperation, StockValue, VariantGroup } from './typen.js';
|
|
15
|
+
import type { Article, GoodsReceiptPreviewLine, InventoryShortfall, InventoryWebhook, InventoryWebhookDelivery, InventoryWebhookTestDelivery, Location, Reservation, StockBelowMinimumEventData, StockChangedEventData, StockLevel, StockMovement, StockOperation, StockValue, Stocktake, StocktakeCount, StocktakeCountResult, StocktakeItem, StocktakePdfDownload, StocktakeWarning, VariantGroup } from './typen.js';
|
|
16
16
|
type Roh = Record<string, unknown>;
|
|
17
17
|
/** Ein Leser bekommt den Vorgang (fuer die Meldung) und den Pfad im Rumpf. */
|
|
18
18
|
export interface Ort {
|
|
@@ -44,6 +44,25 @@ export declare function fehlmengen(roh: unknown): InventoryShortfall[];
|
|
|
44
44
|
* bzw. „keine Varianten“).
|
|
45
45
|
*/
|
|
46
46
|
export declare function variantengruppe(ort: Ort, w: unknown): VariantGroup;
|
|
47
|
+
/** Hinweise einer Inventur; fehlt die Liste, gibt es keine. */
|
|
48
|
+
export declare function inventurWarnungen(ort: Ort, w: unknown): StocktakeWarning[];
|
|
49
|
+
/**
|
|
50
|
+
* Kopf einer Inventur. Was erst nach dem Abschluss kommt (Summen, Hinweise,
|
|
51
|
+
* Siegel, Pruefsumme, Protokoll), steht im Modell nur, wenn der Server es
|
|
52
|
+
* sendet; Werte nur mit dem Kosten-Recht.
|
|
53
|
+
*/
|
|
54
|
+
export declare function inventur(ort: Ort, w: unknown): Stocktake;
|
|
55
|
+
/**
|
|
56
|
+
* Eine Position. Soll, Differenz und alles aus Pruefung und Abschluss stehen
|
|
57
|
+
* im Modell nur, wenn der Server sie sendet (blind: vor `review` nie).
|
|
58
|
+
*/
|
|
59
|
+
export declare function inventurPosition(ort: Ort, w: unknown): StocktakeItem;
|
|
60
|
+
/** Eine Zaehlung: Menge, Runde und Kennungen muessen da sein. */
|
|
61
|
+
export declare function inventurZaehlung(ort: Ort, w: unknown): StocktakeCount;
|
|
62
|
+
/** Antwort von Zaehlen und Stornieren: beide Teile sind zugesagt. */
|
|
63
|
+
export declare function zaehlungMitPosition(name: string, daten: unknown): StocktakeCountResult;
|
|
64
|
+
/** Lese-Link auf ein grosses Inventurprotokoll; ohne Adresse, Ablauf, Groesse oder Pruefsumme ist er unbrauchbar. */
|
|
65
|
+
export declare function protokollLink(name: string, w: unknown): StocktakePdfDownload;
|
|
47
66
|
/** Eine zugesagte Liste `data.<feld>`, Eintrag fuer Eintrag gelesen. */
|
|
48
67
|
export declare function liste<T>(name: string, daten: unknown, feld: string, lesen: (ort: Ort, e: unknown) => T): T[];
|
|
49
68
|
/** Der Cursor der naechsten Seite; `null` am Ende. Etwas anderes als Text oder `null` ist kaputt. */
|
|
@@ -30,6 +30,12 @@ exports.vorschauZeile = vorschauZeile;
|
|
|
30
30
|
exports.reservierung = reservierung;
|
|
31
31
|
exports.fehlmengen = fehlmengen;
|
|
32
32
|
exports.variantengruppe = variantengruppe;
|
|
33
|
+
exports.inventurWarnungen = inventurWarnungen;
|
|
34
|
+
exports.inventur = inventur;
|
|
35
|
+
exports.inventurPosition = inventurPosition;
|
|
36
|
+
exports.inventurZaehlung = inventurZaehlung;
|
|
37
|
+
exports.zaehlungMitPosition = zaehlungMitPosition;
|
|
38
|
+
exports.protokollLink = protokollLink;
|
|
33
39
|
exports.liste = liste;
|
|
34
40
|
exports.naechsterCursor = naechsterCursor;
|
|
35
41
|
const errors_js_1 = require("../client/errors.js");
|
|
@@ -476,6 +482,307 @@ function variantengruppe(ort, w) {
|
|
|
476
482
|
updatedAt: text(g.updatedAt),
|
|
477
483
|
};
|
|
478
484
|
}
|
|
485
|
+
// ---- Inventur (Lager-Kern Stufe 3) ---------------------------------------------------
|
|
486
|
+
const unter = (ort, feld) => ({ name: ort.name, pfad: ort.pfad ? `${ort.pfad}.${feld}` : feld });
|
|
487
|
+
/** Ein Unterobjekt, das `null` sein darf (`review`, `closing` …); etwas anderes als Objekt oder `null` ist kaputt. */
|
|
488
|
+
function objektOderNull(ort, feld, w) {
|
|
489
|
+
if (w === undefined || w === null)
|
|
490
|
+
return null;
|
|
491
|
+
const o = objekt(w);
|
|
492
|
+
if (!o)
|
|
493
|
+
throw antwortfehler(ort.name, `Antwort ist unbrauchbar (data.${ort.pfad}.${feld} ist kein Objekt)`);
|
|
494
|
+
return o;
|
|
495
|
+
}
|
|
496
|
+
/** Ein Wahrheitswert, der da sein muss: ein fehlendes „gezaehlt“ ist nicht „ungezaehlt“. */
|
|
497
|
+
function wahrheitswert(ort, feld, w) {
|
|
498
|
+
if (typeof w !== 'boolean')
|
|
499
|
+
throw antwortfehler(ort.name, `Antwort enthaelt keinen Wahrheitswert (data.${ort.pfad}.${feld})`);
|
|
500
|
+
return w;
|
|
501
|
+
}
|
|
502
|
+
/** Eine Liste von Texten, die fehlen darf (dann leer); etwas anderes ist kaputt. */
|
|
503
|
+
function textlisteOderLeer(ort, feld, w) {
|
|
504
|
+
if (w === undefined || w === null)
|
|
505
|
+
return [];
|
|
506
|
+
return textliste(ort, feld, w);
|
|
507
|
+
}
|
|
508
|
+
/** Wer etwas tat; fehlt die Angabe, `null`. Etwas anderes als ein Objekt ist kaputt, nie still „niemand“. */
|
|
509
|
+
function akteur(ort, feld, w) {
|
|
510
|
+
if (w === undefined || w === null)
|
|
511
|
+
return null;
|
|
512
|
+
const a = objekt(w);
|
|
513
|
+
if (!a)
|
|
514
|
+
throw antwortfehler(ort.name, `Antwort ist unbrauchbar (data.${ort.pfad}.${feld} ist kein Objekt)`);
|
|
515
|
+
return { type: textOderNull(a.type), id: textOderNull(a.id), name: textOderNull(a.name) };
|
|
516
|
+
}
|
|
517
|
+
function inventurWarnung(ort, w) {
|
|
518
|
+
const h = eintrag(ort, w);
|
|
519
|
+
if (typeof h.code !== 'string' || h.code === '')
|
|
520
|
+
throw antwortfehler(ort.name, `Hinweis ohne Code (data.${ort.pfad}.code)`);
|
|
521
|
+
return { code: h.code, items: ganzzahl(ort, 'items', h.items), message: typeof h.message === 'string' ? h.message : null };
|
|
522
|
+
}
|
|
523
|
+
/** Hinweise einer Inventur; fehlt die Liste, gibt es keine. */
|
|
524
|
+
function inventurWarnungen(ort, w) {
|
|
525
|
+
if (w === undefined || w === null)
|
|
526
|
+
return [];
|
|
527
|
+
if (!Array.isArray(w))
|
|
528
|
+
throw antwortfehler(ort.name, `Antwort ist unbrauchbar (data.${ort.pfad} ist keine Liste)`);
|
|
529
|
+
return w.map((h, i) => inventurWarnung({ name: ort.name, pfad: `${ort.pfad}[${i}]` }, h));
|
|
530
|
+
}
|
|
531
|
+
function siegel(ort, w) {
|
|
532
|
+
const s = eintrag(ort, w);
|
|
533
|
+
const raus = {
|
|
534
|
+
fromDay: textOderNull(s.fromDay),
|
|
535
|
+
toDay: textOderNull(s.toDay),
|
|
536
|
+
daysChecked: ganzzahlOderNull(ort, 'daysChecked', s.daysChecked),
|
|
537
|
+
verified: typeof s.verified === 'boolean' ? s.verified : null,
|
|
538
|
+
firstBreak: textOderNull(s.firstBreak),
|
|
539
|
+
gaps: textlisteOderLeer(ort, 'gaps', s.gaps),
|
|
540
|
+
gapCount: ganzzahlOderNull(ort, 'gapCount', s.gapCount),
|
|
541
|
+
};
|
|
542
|
+
if (typeof s.notChecked === 'string')
|
|
543
|
+
raus.notChecked = s.notChecked;
|
|
544
|
+
if (hat(s, 'checkedUntil'))
|
|
545
|
+
raus.checkedUntil = textOderNull(s.checkedUntil);
|
|
546
|
+
return raus;
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* Kopf einer Inventur. Was erst nach dem Abschluss kommt (Summen, Hinweise,
|
|
550
|
+
* Siegel, Pruefsumme, Protokoll), steht im Modell nur, wenn der Server es
|
|
551
|
+
* sendet; Werte nur mit dem Kosten-Recht.
|
|
552
|
+
*/
|
|
553
|
+
function inventur(ort, w) {
|
|
554
|
+
const k = eintrag(ort, w);
|
|
555
|
+
const umfang = objektOderNull(ort, 'scope', k.scope);
|
|
556
|
+
const umfangOrt = unter(ort, 'scope');
|
|
557
|
+
const fortschritt = objektOderNull(ort, 'progress', k.progress);
|
|
558
|
+
const fOrt = unter(ort, 'progress');
|
|
559
|
+
const pruefung = objektOderNull(ort, 'review', k.review);
|
|
560
|
+
const pOrt = unter(ort, 'review');
|
|
561
|
+
const abschluss = objektOderNull(ort, 'closing', k.closing);
|
|
562
|
+
const aOrt = unter(ort, 'closing');
|
|
563
|
+
const abbruch = objektOderNull(ort, 'cancellation', k.cancellation);
|
|
564
|
+
const raus = {
|
|
565
|
+
id: kennung(ort, 'id', k.id),
|
|
566
|
+
name: text(k.name),
|
|
567
|
+
locationId: textOderNull(k.locationId),
|
|
568
|
+
scope: umfang
|
|
569
|
+
? {
|
|
570
|
+
type: textOderNull(umfang.type),
|
|
571
|
+
groupIds: textlisteOderLeer(umfangOrt, 'groupIds', umfang.groupIds),
|
|
572
|
+
articleIds: textlisteOderLeer(umfangOrt, 'articleIds', umfang.articleIds),
|
|
573
|
+
}
|
|
574
|
+
: null,
|
|
575
|
+
type: textOderNull(k.type),
|
|
576
|
+
keyDate: textOderNull(k.keyDate),
|
|
577
|
+
// Blind ist die sichere Vorgabe: nur ein ausdrueckliches false zeigt Bestand.
|
|
578
|
+
blind: k.blind !== false,
|
|
579
|
+
status: textOderNull(k.status),
|
|
580
|
+
progress: fortschritt
|
|
581
|
+
? {
|
|
582
|
+
items: ganzzahl(fOrt, 'items', fortschritt.items),
|
|
583
|
+
counted: ganzzahlOderNull(fOrt, 'counted', fortschritt.counted),
|
|
584
|
+
recountOpen: fortschritt.recountOpen === true,
|
|
585
|
+
}
|
|
586
|
+
: null,
|
|
587
|
+
createdAt: text(k.createdAt),
|
|
588
|
+
createdBy: akteur(ort, 'createdBy', k.createdBy),
|
|
589
|
+
source: textOderNull(k.source),
|
|
590
|
+
updatedAt: text(k.updatedAt),
|
|
591
|
+
review: pruefung
|
|
592
|
+
? {
|
|
593
|
+
startedAt: text(pruefung.startedAt),
|
|
594
|
+
startedBy: akteur(pOrt, 'startedBy', pruefung.startedBy),
|
|
595
|
+
complete: pruefung.complete === true,
|
|
596
|
+
expectedAsOf: text(pruefung.expectedAsOf),
|
|
597
|
+
recountUncounted: ganzzahlOderNull(pOrt, 'recountUncounted', pruefung.recountUncounted),
|
|
598
|
+
}
|
|
599
|
+
: null,
|
|
600
|
+
closing: abschluss
|
|
601
|
+
? {
|
|
602
|
+
startedAt: text(abschluss.startedAt),
|
|
603
|
+
startedBy: akteur(aOrt, 'startedBy', abschluss.startedBy),
|
|
604
|
+
uncountedAsZero: abschluss.uncountedAsZero === true,
|
|
605
|
+
parts: ganzzahlOderNull(aOrt, 'parts', abschluss.parts),
|
|
606
|
+
bookedParts: ganzzahlOderNull(aOrt, 'bookedParts', abschluss.bookedParts),
|
|
607
|
+
completedAt: text(abschluss.completedAt),
|
|
608
|
+
}
|
|
609
|
+
: null,
|
|
610
|
+
cancellation: abbruch
|
|
611
|
+
? { reason: text(abbruch.reason), cancelledAt: text(abbruch.cancelledAt), cancelledBy: akteur(unter(ort, 'cancellation'), 'cancelledBy', abbruch.cancelledBy) }
|
|
612
|
+
: null,
|
|
613
|
+
};
|
|
614
|
+
const summen = objektOderNull(ort, 'totals', k.totals);
|
|
615
|
+
if (summen) {
|
|
616
|
+
const sOrt = unter(ort, 'totals');
|
|
617
|
+
raus.totals = {
|
|
618
|
+
items: ganzzahl(sOrt, 'items', summen.items),
|
|
619
|
+
counted: ganzzahl(sOrt, 'counted', summen.counted),
|
|
620
|
+
uncounted: ganzzahl(sOrt, 'uncounted', summen.uncounted),
|
|
621
|
+
recounted: ganzzahl(sOrt, 'recounted', summen.recounted),
|
|
622
|
+
withDifference: ganzzahl(sOrt, 'withDifference', summen.withDifference),
|
|
623
|
+
needsCheck: ganzzahl(sOrt, 'needsCheck', summen.needsCheck),
|
|
624
|
+
notBooked: ganzzahl(sOrt, 'notBooked', summen.notBooked),
|
|
625
|
+
};
|
|
626
|
+
for (const feld of ['differenceValueCents', 'inventoryValueCents']) {
|
|
627
|
+
if (hat(summen, feld))
|
|
628
|
+
raus.totals[feld] = ganzzahlOderNull(sOrt, feld, summen[feld]);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
if (hat(k, 'warnings'))
|
|
632
|
+
raus.warnings = inventurWarnungen(unter(ort, 'warnings'), k.warnings);
|
|
633
|
+
if (hat(k, 'seal') && k.seal !== null)
|
|
634
|
+
raus.seal = siegel(unter(ort, 'seal'), k.seal);
|
|
635
|
+
if (hat(k, 'checksum'))
|
|
636
|
+
raus.checksum = textOderNull(k.checksum);
|
|
637
|
+
if (hat(k, 'inventoryAsOf'))
|
|
638
|
+
raus.inventoryAsOf = textOderNull(k.inventoryAsOf);
|
|
639
|
+
const pdf = objektOderNull(ort, 'pdf', k.pdf);
|
|
640
|
+
if (pdf) {
|
|
641
|
+
raus.pdf = { available: pdf.available === true };
|
|
642
|
+
if (typeof pdf.valuesSha256 === 'string')
|
|
643
|
+
raus.pdf.valuesSha256 = pdf.valuesSha256;
|
|
644
|
+
if (typeof pdf.quantitiesSha256 === 'string')
|
|
645
|
+
raus.pdf.quantitiesSha256 = pdf.quantitiesSha256;
|
|
646
|
+
}
|
|
647
|
+
return raus;
|
|
648
|
+
}
|
|
649
|
+
function nichtGebucht(ort, w) {
|
|
650
|
+
const n = eintrag(ort, w);
|
|
651
|
+
if (typeof n.code !== 'string' || n.code === '')
|
|
652
|
+
throw antwortfehler(ort.name, `Antwort enthaelt keinen Grund (data.${ort.pfad}.code)`);
|
|
653
|
+
const raus = { code: n.code, quantity: ganzzahlOderNull(ort, 'quantity', n.quantity) };
|
|
654
|
+
if (n.reasons !== undefined && n.reasons !== null) {
|
|
655
|
+
if (!Array.isArray(n.reasons))
|
|
656
|
+
throw antwortfehler(ort.name, `Antwort ist unbrauchbar (data.${ort.pfad}.reasons ist keine Liste)`);
|
|
657
|
+
raus.reasons = n.reasons.map((g, i) => {
|
|
658
|
+
const gOrt = { name: ort.name, pfad: `${ort.pfad}.reasons[${i}]` };
|
|
659
|
+
const r = eintrag(gOrt, g);
|
|
660
|
+
if (typeof r.code !== 'string' || r.code === '')
|
|
661
|
+
throw antwortfehler(ort.name, `Antwort enthaelt keinen Grund (data.${gOrt.pfad}.code)`);
|
|
662
|
+
return { code: r.code, quantity: ganzzahlOderNull(gOrt, 'quantity', r.quantity) };
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
return raus;
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* Eine Position. Soll, Differenz und alles aus Pruefung und Abschluss stehen
|
|
669
|
+
* im Modell nur, wenn der Server sie sendet (blind: vor `review` nie).
|
|
670
|
+
*/
|
|
671
|
+
function inventurPosition(ort, w) {
|
|
672
|
+
const p = eintrag(ort, w);
|
|
673
|
+
// Wer gezaehlt hat, ist zugesagt: fehlt die Liste, ist die Antwort kaputt (nie „niemand“).
|
|
674
|
+
if (!Array.isArray(p.countedBy))
|
|
675
|
+
throw antwortfehler(ort.name, `Antwort ist unbrauchbar (data.${ort.pfad}.countedBy ist keine Liste)`);
|
|
676
|
+
const zaehler = p.countedBy;
|
|
677
|
+
const nachzaehlen = objektOderNull(ort, 'recount', p.recount);
|
|
678
|
+
const nOrt = unter(ort, 'recount');
|
|
679
|
+
const raus = {
|
|
680
|
+
articleId: kennung(ort, 'articleId', p.articleId),
|
|
681
|
+
condition: kennung(ort, 'condition', p.condition),
|
|
682
|
+
name: text(p.name),
|
|
683
|
+
number: text(p.number),
|
|
684
|
+
unit: text(p.unit),
|
|
685
|
+
round: ganzzahl(ort, 'round', p.round),
|
|
686
|
+
counted: wahrheitswert(ort, 'counted', p.counted),
|
|
687
|
+
quantity: ganzzahlOderNull(ort, 'quantity', p.quantity),
|
|
688
|
+
counts: ganzzahl(ort, 'counts', p.counts),
|
|
689
|
+
firstCountedAt: text(p.firstCountedAt),
|
|
690
|
+
referenceTime: text(p.referenceTime),
|
|
691
|
+
countedBy: zaehler.map((a, i) => {
|
|
692
|
+
const wer = akteur(ort, `countedBy[${i}]`, a);
|
|
693
|
+
if (!wer)
|
|
694
|
+
throw antwortfehler(ort.name, `Antwort ist unbrauchbar (data.${ort.pfad}.countedBy[${i}] ist kein Objekt)`);
|
|
695
|
+
return wer;
|
|
696
|
+
}),
|
|
697
|
+
recountRequested: p.recountRequested === true,
|
|
698
|
+
recount: nachzaehlen
|
|
699
|
+
? {
|
|
700
|
+
reason: text(nachzaehlen.reason),
|
|
701
|
+
requestedAt: text(nachzaehlen.requestedAt),
|
|
702
|
+
requestedBy: akteur(nOrt, 'requestedBy', nachzaehlen.requestedBy),
|
|
703
|
+
round: ganzzahlOderNull(nOrt, 'round', nachzaehlen.round),
|
|
704
|
+
}
|
|
705
|
+
: null,
|
|
706
|
+
addedLater: p.addedLater === true,
|
|
707
|
+
};
|
|
708
|
+
// Die Zaehl- und Storno-Antwort sendet die Position ohne Seriennummern: dann fehlt das Feld, nie [].
|
|
709
|
+
if (hat(p, 'serialNumbers'))
|
|
710
|
+
raus.serialNumbers = textlisteOderLeer(ort, 'serialNumbers', p.serialNumbers);
|
|
711
|
+
// Gezaehlt heisst: es gibt eine Menge. Beides zusammen kaputt waere „0“ oder „nichts“ geraten.
|
|
712
|
+
if (raus.counted && raus.quantity === null)
|
|
713
|
+
throw antwortfehler(ort.name, `Antwort ist unbrauchbar (data.${ort.pfad}.quantity fehlt bei counted: true)`);
|
|
714
|
+
for (const feld of ['bookStockNow', 'expectedQuantity', 'differenceQuantity', 'differenceValueCents', 'bookedQuantity']) {
|
|
715
|
+
if (hat(p, feld))
|
|
716
|
+
raus[feld] = ganzzahlOderNull(ort, feld, p[feld]);
|
|
717
|
+
}
|
|
718
|
+
if (hat(p, 'needsCheck'))
|
|
719
|
+
raus.needsCheck = p.needsCheck === true;
|
|
720
|
+
if (hat(p, 'checkReasons'))
|
|
721
|
+
raus.checkReasons = textlisteOderLeer(ort, 'checkReasons', p.checkReasons);
|
|
722
|
+
if (hat(p, 'expectedAsOf'))
|
|
723
|
+
raus.expectedAsOf = text(p.expectedAsOf);
|
|
724
|
+
if (hat(p, 'missingSerialNumbers'))
|
|
725
|
+
raus.missingSerialNumbers = textlisteOderLeer(ort, 'missingSerialNumbers', p.missingSerialNumbers);
|
|
726
|
+
if (hat(p, 'extraSerialNumbers'))
|
|
727
|
+
raus.extraSerialNumbers = textlisteOderLeer(ort, 'extraSerialNumbers', p.extraSerialNumbers);
|
|
728
|
+
if (hat(p, 'notBooked'))
|
|
729
|
+
raus.notBooked = p.notBooked === null ? null : nichtGebucht(unter(ort, 'notBooked'), p.notBooked);
|
|
730
|
+
if (hat(p, 'inventory')) {
|
|
731
|
+
const inv = objektOderNull(ort, 'inventory', p.inventory);
|
|
732
|
+
if (!inv) {
|
|
733
|
+
raus.inventory = null;
|
|
734
|
+
}
|
|
735
|
+
else {
|
|
736
|
+
const iOrt = unter(ort, 'inventory');
|
|
737
|
+
raus.inventory = { quantity: ganzzahl(iOrt, 'quantity', inv.quantity), countedOn: textOderNull(inv.countedOn) };
|
|
738
|
+
for (const feld of ['unitValueMicros', 'valueCents']) {
|
|
739
|
+
if (hat(inv, feld))
|
|
740
|
+
raus.inventory[feld] = ganzzahlOderNull(iOrt, feld, inv[feld]);
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
return raus;
|
|
745
|
+
}
|
|
746
|
+
/** Eine Zaehlung: Menge, Runde und Kennungen muessen da sein. */
|
|
747
|
+
function inventurZaehlung(ort, w) {
|
|
748
|
+
const z = eintrag(ort, w);
|
|
749
|
+
const storno = objektOderNull(ort, 'voided', z.voided);
|
|
750
|
+
return {
|
|
751
|
+
id: kennung(ort, 'id', z.id),
|
|
752
|
+
articleId: kennung(ort, 'articleId', z.articleId),
|
|
753
|
+
condition: kennung(ort, 'condition', z.condition),
|
|
754
|
+
quantity: ganzzahl(ort, 'quantity', z.quantity),
|
|
755
|
+
// Die Zaehlung traegt ihre Seriennummern immer (leer bei Mengenartikeln); fehlt die Liste, ist sie kaputt.
|
|
756
|
+
serialNumbers: textliste(ort, 'serialNumbers', z.serialNumbers),
|
|
757
|
+
round: ganzzahl(ort, 'round', z.round),
|
|
758
|
+
countedBy: akteur(ort, 'countedBy', z.countedBy),
|
|
759
|
+
source: textOderNull(z.source),
|
|
760
|
+
cashregisterId: textOderNull(z.cashregisterId),
|
|
761
|
+
countedAt: text(z.countedAt),
|
|
762
|
+
note: text(z.note),
|
|
763
|
+
voided: storno
|
|
764
|
+
? { reason: text(storno.reason), voidedAt: text(storno.voidedAt), voidedBy: akteur(unter(ort, 'voided'), 'voidedBy', storno.voidedBy) }
|
|
765
|
+
: null,
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
/** Antwort von Zaehlen und Stornieren: beide Teile sind zugesagt. */
|
|
769
|
+
function zaehlungMitPosition(name, daten) {
|
|
770
|
+
const d = objekt(daten);
|
|
771
|
+
return { count: inventurZaehlung({ name, pfad: 'count' }, d?.count), item: inventurPosition({ name, pfad: 'item' }, d?.item) };
|
|
772
|
+
}
|
|
773
|
+
/** Lese-Link auf ein grosses Inventurprotokoll; ohne Adresse, Ablauf, Groesse oder Pruefsumme ist er unbrauchbar. */
|
|
774
|
+
function protokollLink(name, w) {
|
|
775
|
+
const ort = { name, pfad: 'download' };
|
|
776
|
+
const d = eintrag(ort, w);
|
|
777
|
+
return {
|
|
778
|
+
url: kennung(ort, 'url', d.url),
|
|
779
|
+
expiresAt: kennung(ort, 'expiresAt', d.expiresAt),
|
|
780
|
+
sizeBytes: ganzzahl(ort, 'sizeBytes', d.sizeBytes),
|
|
781
|
+
sha256: kennung(ort, 'sha256', d.sha256),
|
|
782
|
+
fileName: textOderNull(d.fileName),
|
|
783
|
+
contentType: textOderNull(d.contentType),
|
|
784
|
+
};
|
|
785
|
+
}
|
|
479
786
|
// ---- Listen ------------------------------------------------------------------
|
|
480
787
|
/** Eine zugesagte Liste `data.<feld>`, Eintrag fuer Eintrag gelesen. */
|
|
481
788
|
function liste(name, daten, feld, lesen) {
|