@kreiseck/kasseneck-api 0.6.45 → 0.6.49
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/README.md +95 -15
- package/dist/cjs/client/aufrufe.d.ts +1 -1
- package/dist/cjs/client/aufrufe.js +2 -0
- package/dist/cjs/client/errors.d.ts +7 -1
- package/dist/cjs/client/errors.js +8 -1
- package/dist/cjs/client/receipts.d.ts +16 -0
- package/dist/cjs/client/receipts.js +10 -0
- package/dist/cjs/client/transport.js +5 -4
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/index.js +4 -2
- package/dist/cjs/kasse/index.d.ts +1 -0
- package/dist/cjs/kasse/index.js +3 -0
- package/dist/cjs/kasse/trinkgeld.d.ts +10 -0
- package/dist/cjs/kasse/trinkgeld.js +27 -0
- package/dist/cjs/models/cancellation.d.ts +22 -0
- package/dist/cjs/models/cancellation.js +31 -1
- package/dist/cjs/models/hobex-receipt.js +9 -2
- package/dist/cjs/models/index.d.ts +1 -1
- package/dist/cjs/models/index.js +3 -1
- package/dist/cjs/models/receipt.js +3 -0
- package/dist/cjs/models/voucher.js +18 -2
- package/dist/cjs/payments/hobex-hps/connect-client.d.ts +104 -0
- package/dist/cjs/payments/hobex-hps/connect-client.js +159 -0
- package/dist/cjs/payments/hobex-hps/errors.d.ts +122 -0
- package/dist/cjs/payments/hobex-hps/errors.js +158 -0
- package/dist/cjs/payments/hobex-hps/events.d.ts +22 -0
- package/dist/cjs/payments/hobex-hps/events.js +2 -0
- package/dist/cjs/payments/hobex-hps/index.d.ts +27 -0
- package/dist/cjs/payments/hobex-hps/index.js +66 -0
- package/dist/cjs/payments/hobex-hps/outcome.d.ts +31 -0
- package/dist/cjs/payments/hobex-hps/outcome.js +7 -0
- package/dist/cjs/payments/hobex-hps/payments.d.ts +149 -0
- package/dist/cjs/payments/hobex-hps/payments.js +550 -0
- package/dist/cjs/payments/hobex-hps/receipt.d.ts +28 -0
- package/dist/cjs/payments/hobex-hps/receipt.js +60 -0
- package/dist/cjs/payments/hobex-hps/transaction-id.d.ts +65 -0
- package/dist/cjs/payments/hobex-hps/transaction-id.js +91 -0
- package/dist/cjs/payments/hobex-hps/transaction-response.d.ts +163 -0
- package/dist/cjs/payments/hobex-hps/transaction-response.js +245 -0
- package/dist/cjs/payments/hobex.js +9 -6
- package/dist/cjs/payments/index.d.ts +26 -14
- package/dist/cjs/payments/index.js +59 -15
- package/dist/cjs/printing/index.d.ts +1 -1
- package/dist/cjs/printing/index.js +2 -1
- package/dist/cjs/printing/webusb.d.ts +37 -7
- package/dist/cjs/printing/webusb.js +76 -18
- package/dist/cjs/receipt/epos.d.ts +18 -0
- package/dist/cjs/receipt/epos.js +78 -6
- package/dist/cjs/receipt/index.d.ts +1 -1
- package/dist/cjs/receipt/index.js +2 -1
- package/dist/cjs/register/index.d.ts +1 -1
- package/dist/cjs/register/index.js +2 -1
- package/dist/cjs/register/pairing.d.ts +67 -1
- package/dist/cjs/register/pairing.js +59 -3
- package/dist/esm/client/aufrufe.d.ts +1 -1
- package/dist/esm/client/aufrufe.js +2 -0
- package/dist/esm/client/errors.d.ts +7 -1
- package/dist/esm/client/errors.js +8 -1
- package/dist/esm/client/receipts.d.ts +16 -0
- package/dist/esm/client/receipts.js +10 -0
- package/dist/esm/client/transport.js +5 -4
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/kasse/index.d.ts +1 -0
- package/dist/esm/kasse/index.js +1 -0
- package/dist/esm/kasse/trinkgeld.d.ts +10 -0
- package/dist/esm/kasse/trinkgeld.js +24 -0
- package/dist/esm/models/cancellation.d.ts +22 -0
- package/dist/esm/models/cancellation.js +29 -0
- package/dist/esm/models/hobex-receipt.js +10 -3
- package/dist/esm/models/index.d.ts +1 -1
- package/dist/esm/models/index.js +1 -1
- package/dist/esm/models/receipt.js +3 -0
- package/dist/esm/models/voucher.js +18 -2
- package/dist/esm/payments/hobex-hps/connect-client.d.ts +104 -0
- package/dist/esm/payments/hobex-hps/connect-client.js +156 -0
- package/dist/esm/payments/hobex-hps/errors.d.ts +122 -0
- package/dist/esm/payments/hobex-hps/errors.js +149 -0
- package/dist/esm/payments/hobex-hps/events.d.ts +22 -0
- package/dist/esm/payments/hobex-hps/events.js +1 -0
- package/dist/esm/payments/hobex-hps/index.d.ts +27 -0
- package/dist/esm/payments/hobex-hps/index.js +26 -0
- package/dist/esm/payments/hobex-hps/outcome.d.ts +31 -0
- package/dist/esm/payments/hobex-hps/outcome.js +4 -0
- package/dist/esm/payments/hobex-hps/payments.d.ts +149 -0
- package/dist/esm/payments/hobex-hps/payments.js +547 -0
- package/dist/esm/payments/hobex-hps/receipt.d.ts +28 -0
- package/dist/esm/payments/hobex-hps/receipt.js +57 -0
- package/dist/esm/payments/hobex-hps/transaction-id.d.ts +65 -0
- package/dist/esm/payments/hobex-hps/transaction-id.js +86 -0
- package/dist/esm/payments/hobex-hps/transaction-response.d.ts +163 -0
- package/dist/esm/payments/hobex-hps/transaction-response.js +233 -0
- package/dist/esm/payments/hobex.js +9 -6
- package/dist/esm/payments/index.d.ts +26 -14
- package/dist/esm/payments/index.js +26 -14
- package/dist/esm/printing/index.d.ts +1 -1
- package/dist/esm/printing/index.js +1 -1
- package/dist/esm/printing/webusb.d.ts +37 -7
- package/dist/esm/printing/webusb.js +74 -17
- package/dist/esm/receipt/epos.d.ts +18 -0
- package/dist/esm/receipt/epos.js +76 -6
- package/dist/esm/receipt/index.d.ts +1 -1
- package/dist/esm/receipt/index.js +1 -1
- package/dist/esm/register/index.d.ts +1 -1
- package/dist/esm/register/index.js +1 -1
- package/dist/esm/register/pairing.d.ts +67 -1
- package/dist/esm/register/pairing.js +58 -3
- package/fixtures/hobex-hps-codes.json +67 -0
- package/fixtures/oberflaeche.json +3 -1
- package/package.json +4 -2
|
@@ -5,6 +5,7 @@ exports.cancelScopeOf = cancelScopeOf;
|
|
|
5
5
|
exports.receiptsScopeOf = receiptsScopeOf;
|
|
6
6
|
exports.pairRegisterDevice = pairRegisterDevice;
|
|
7
7
|
exports.unpairRegisterDevice = unpairRegisterDevice;
|
|
8
|
+
exports.listRegisterSessionsForDevice = listRegisterSessionsForDevice;
|
|
8
9
|
exports.listRegisterUsersForDevice = listRegisterUsersForDevice;
|
|
9
10
|
exports.registerUserLogin = registerUserLogin;
|
|
10
11
|
exports.registerPinLogin = registerPinLogin;
|
|
@@ -48,7 +49,14 @@ const settings_js_1 = require("../kasse/settings.js");
|
|
|
48
49
|
* es auch halten.
|
|
49
50
|
*/
|
|
50
51
|
const ohneAnmeldung = () => ({ headers: {}, params: {} });
|
|
51
|
-
/**
|
|
52
|
+
/**
|
|
53
|
+
* Alle Rechte-Schluessel, die dieses Paket kennt — die Zwillinge pruefen
|
|
54
|
+
* dagegen, und ueber `fixtures/oberflaeche.json` steht die Liste im Vertrag.
|
|
55
|
+
*
|
|
56
|
+
* `satisfies` schliesst die eine Richtung: ein Name, den [RegisterUserPerms]
|
|
57
|
+
* nicht fuehrt (Tippfehler, umbenanntes Recht), kommt hier nicht durch.
|
|
58
|
+
* Die Gegenrichtung schliesst [_RechteVollstaendig] direkt darunter.
|
|
59
|
+
*/
|
|
52
60
|
exports.REGISTER_PERMS = [
|
|
53
61
|
'sell', 'cancel', 'articles', 'layout', 'reports', 'takeover',
|
|
54
62
|
'cancelScope', 'receiptsScope', 'drawer', 'discount', 'tipAssign',
|
|
@@ -112,6 +120,48 @@ async function unpairRegisterDevice(options) {
|
|
|
112
120
|
pflicht(name, 'deviceSecret', deviceSecret);
|
|
113
121
|
await transportFuer(verbindung)(name, { ownerUid, deviceId, deviceSecret }, undefined, [deviceSecret]);
|
|
114
122
|
}
|
|
123
|
+
/**
|
|
124
|
+
* Welche Sitzungen haelt diese Kasse gerade?
|
|
125
|
+
*
|
|
126
|
+
* Fuer den Anmeldebildschirm: Sind alle Lizenzplaetze belegt, muss eine
|
|
127
|
+
* weichen -- und der Kassier soll sehen, WELCHE, statt dass das Backend still
|
|
128
|
+
* die aelteste nimmt. Die Wahl geht dann als
|
|
129
|
+
* [RegisterUserLoginOptions.takeoverSessionId] mit.
|
|
130
|
+
*
|
|
131
|
+
* Ausgewiesen wird sich ueber das Geraete-Geheimnis, wie bei
|
|
132
|
+
* [listRegisterUsersForDevice]: An diesem Punkt gibt es weder Sitzung noch
|
|
133
|
+
* Token. Die Kasse bestimmt das Backend aus dem Geraet, nicht aus dem Aufruf.
|
|
134
|
+
*/
|
|
135
|
+
async function listRegisterSessionsForDevice(options) {
|
|
136
|
+
const { ownerUid, deviceId, deviceSecret, ...verbindung } = options;
|
|
137
|
+
const name = 'listRegisterSessionsForDevice';
|
|
138
|
+
pflicht(name, 'ownerUid', ownerUid);
|
|
139
|
+
pflicht(name, 'deviceId', deviceId);
|
|
140
|
+
pflicht(name, 'deviceSecret', deviceSecret);
|
|
141
|
+
const daten = await transportFuer(verbindung)(name, { ownerUid, deviceId, deviceSecret }, undefined, [deviceSecret]);
|
|
142
|
+
const liste = daten?.sessions;
|
|
143
|
+
if (!Array.isArray(liste)) {
|
|
144
|
+
throw antwortfehler(name, 'Antwort enthaelt keine Sitzungsliste (data.sessions fehlt)');
|
|
145
|
+
}
|
|
146
|
+
const sessions = liste.map((eintrag) => {
|
|
147
|
+
const roh = (typeof eintrag === 'object' && eintrag !== null ? eintrag : {});
|
|
148
|
+
return {
|
|
149
|
+
// Ohne Kennung waere die Zeile nicht waehlbar -- eine Schaltflaeche, die
|
|
150
|
+
// nichts tun kann, ist schlimmer als keine.
|
|
151
|
+
id: pflichtfeld(name, roh, 'id'),
|
|
152
|
+
deviceId: typeof roh['deviceId'] === 'string' && roh['deviceId'] ? roh['deviceId'] : null,
|
|
153
|
+
deviceLabel: text(roh['deviceLabel']) || 'Kasse',
|
|
154
|
+
startedAt: typeof roh['startedAt'] === 'number' ? roh['startedAt'] : null,
|
|
155
|
+
expiresAt: typeof roh['expiresAt'] === 'number' ? roh['expiresAt'] : null,
|
|
156
|
+
selbst: roh['selbst'] === true,
|
|
157
|
+
// Nur uebernehmen, wenn wirklich einer kam: ein leerer String stuende
|
|
158
|
+
// in der Oberflaeche als namenlose Zeile, statt die Spalte wegzulassen.
|
|
159
|
+
...(typeof roh['userName'] === 'string' && roh['userName'] ? { userName: roh['userName'] } : {}),
|
|
160
|
+
};
|
|
161
|
+
});
|
|
162
|
+
const licenses = typeof daten?.licenses === 'number' && daten.licenses > 0 ? daten.licenses : 1;
|
|
163
|
+
return { licenses, sessions };
|
|
164
|
+
}
|
|
115
165
|
async function listRegisterUsersForDevice(options) {
|
|
116
166
|
const { ownerUid, deviceId, deviceSecret, ...verbindung } = options;
|
|
117
167
|
const name = 'listRegisterUsersForDevice';
|
|
@@ -185,7 +235,7 @@ function regel(wert) {
|
|
|
185
235
|
* sperren gestaffelt (ab dem fuenften 30 Sekunden, ab dem neunten 15 Minuten).
|
|
186
236
|
*/
|
|
187
237
|
async function registerUserLogin(options) {
|
|
188
|
-
const { ownerUid, deviceId, deviceSecret, userId, pin, cashregisterId, takeover, client, geo, ...verbindung } = options;
|
|
238
|
+
const { ownerUid, deviceId, deviceSecret, userId, pin, cashregisterId, takeover, takeoverSessionId, client, geo, ...verbindung } = options;
|
|
189
239
|
const name = 'registerUserLogin';
|
|
190
240
|
pflicht(name, 'ownerUid', ownerUid);
|
|
191
241
|
pflicht(name, 'deviceId', deviceId);
|
|
@@ -203,6 +253,9 @@ async function registerUserLogin(options) {
|
|
|
203
253
|
// Nur die ausdrueckliche Uebernahme geht mit: das Backend prueft auf
|
|
204
254
|
// `=== true`, und ein mitgesendetes `false` waere nur Rauschen.
|
|
205
255
|
takeover: takeover === true ? true : undefined,
|
|
256
|
+
// Leerer String heisst „nichts gewaehlt" und gehoert nicht in die
|
|
257
|
+
// Nutzlast -- das Backend wiese ihn sonst als unbekannte Sitzung ab.
|
|
258
|
+
takeoverSessionId: takeoverSessionId ? takeoverSessionId : undefined,
|
|
206
259
|
client, geo: geo ?? undefined,
|
|
207
260
|
}, undefined, [pin, deviceSecret]);
|
|
208
261
|
return sitzungAusAntwort(name, daten);
|
|
@@ -214,7 +267,7 @@ async function registerUserLogin(options) {
|
|
|
214
267
|
* zaehlen und sperren dort am **Geraet**, nicht an einem Benutzer.
|
|
215
268
|
*/
|
|
216
269
|
async function registerPinLogin(options) {
|
|
217
|
-
const { ownerUid, deviceId, deviceSecret, pin, cashregisterId, takeover, client, geo, ...verbindung } = options;
|
|
270
|
+
const { ownerUid, deviceId, deviceSecret, pin, cashregisterId, takeover, takeoverSessionId, client, geo, ...verbindung } = options;
|
|
218
271
|
const name = 'registerPinLogin';
|
|
219
272
|
pflicht(name, 'ownerUid', ownerUid);
|
|
220
273
|
pflicht(name, 'deviceId', deviceId);
|
|
@@ -229,6 +282,9 @@ async function registerPinLogin(options) {
|
|
|
229
282
|
cashregisterId,
|
|
230
283
|
// Nur die ausdrueckliche Uebernahme geht mit — wie bei registerUserLogin.
|
|
231
284
|
takeover: takeover === true ? true : undefined,
|
|
285
|
+
// Leerer String heisst „nichts gewaehlt" und gehoert nicht in die
|
|
286
|
+
// Nutzlast -- das Backend wiese ihn sonst als unbekannte Sitzung ab.
|
|
287
|
+
takeoverSessionId: takeoverSessionId ? takeoverSessionId : undefined,
|
|
232
288
|
client, geo: geo ?? undefined,
|
|
233
289
|
}, undefined, [pin, deviceSecret]);
|
|
234
290
|
return sitzungAusAntwort(name, daten);
|
|
@@ -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 AUFRUFE: readonly ["cancelReceipt", "createPaymentLinkStripe", "createPrintJob", "createReceipt", "downloadDailyReport", "downloadReport", "endRegisterSession", "financeWebService", "generateFullReceiptId", "getFirstReceiptDate", "getKasseSettings", "getPrintJob", "getReceipt", "hobexPayApi", "hobexRefundApi", "listMyArticleGroups", "listMyArticles", "listMyCashregisters", "listMyPrinters", "listMyReceipts", "listRegisterUsersForDevice", "pairRegisterDevice", "registerPinLogin", "registerUserLogin", "renewRegisterSession", "setMyKasseSettings", "setMyRegisterDeviceSettings", "stripeCaptureIntent", "unpairRegisterDevice"];
|
|
16
|
+
export declare const AUFRUFE: readonly ["cancelReceipt", "createPaymentLinkStripe", "createPrintJob", "createReceipt", "downloadDailyReport", "downloadReport", "endRegisterSession", "financeWebService", "generateFullReceiptId", "getFirstReceiptDate", "getKasseSettings", "getPrintJob", "getReceipt", "hobexPayApi", "hobexRefundApi", "listMyArticleGroups", "listMyArticles", "listMyCashregisters", "listMyPrinters", "listMyReceipts", "listMyTipRecipients", "listRegisterUsersForDevice", "listRegisterSessionsForDevice", "pairRegisterDevice", "registerPinLogin", "registerUserLogin", "renewRegisterSession", "setMyKasseSettings", "setMyRegisterDeviceSettings", "stripeCaptureIntent", "unpairRegisterDevice"];
|
|
17
17
|
export type Aufruf = typeof AUFRUFE[number];
|
|
18
18
|
/** Wie [KasseneckTransport], nur mit bekanntem Aufrufnamen. Nicht exportiert nach aussen. */
|
|
19
19
|
export type InternerTransport = <T = unknown>(functionName: Aufruf, params?: Record<string, unknown>, extraBodyFields?: TransportBodyFields, secretParams?: readonly string[]) => Promise<T>;
|
|
@@ -19,7 +19,9 @@ export const AUFRUFE = [
|
|
|
19
19
|
'listMyCashregisters',
|
|
20
20
|
'listMyPrinters',
|
|
21
21
|
'listMyReceipts',
|
|
22
|
+
'listMyTipRecipients',
|
|
22
23
|
'listRegisterUsersForDevice',
|
|
24
|
+
'listRegisterSessionsForDevice',
|
|
23
25
|
'pairRegisterDevice',
|
|
24
26
|
'registerPinLogin',
|
|
25
27
|
'registerUserLogin',
|
|
@@ -76,7 +76,13 @@ export declare class KasseneckApiError extends Error {
|
|
|
76
76
|
readonly functionName: string;
|
|
77
77
|
/** Meldung des Backends, unveraendert (`message` aus der Huelle). */
|
|
78
78
|
readonly serverMessage: string;
|
|
79
|
-
|
|
79
|
+
/**
|
|
80
|
+
* Stabiler Fehlercode des Backends (`code` aus der Huelle), wenn der
|
|
81
|
+
* Endpunkt einen legt — heute `cancelReceipt` (siehe CANCELLATION_ERROR_CODES).
|
|
82
|
+
* Daran entscheiden, nie an [serverMessage]: der Text darf sich aendern.
|
|
83
|
+
*/
|
|
84
|
+
readonly code: string | undefined;
|
|
85
|
+
constructor(functionName: string, serverMessage: string, code?: string);
|
|
80
86
|
}
|
|
81
87
|
/** Warum die Antwort keine verwertbare Huelle war. */
|
|
82
88
|
export type HttpFailureReason = 'server-error' | 'empty-body' | 'not-json' | 'missing-status';
|
|
@@ -92,10 +92,17 @@ export class KasseneckApiError extends Error {
|
|
|
92
92
|
functionName;
|
|
93
93
|
/** Meldung des Backends, unveraendert (`message` aus der Huelle). */
|
|
94
94
|
serverMessage;
|
|
95
|
-
|
|
95
|
+
/**
|
|
96
|
+
* Stabiler Fehlercode des Backends (`code` aus der Huelle), wenn der
|
|
97
|
+
* Endpunkt einen legt — heute `cancelReceipt` (siehe CANCELLATION_ERROR_CODES).
|
|
98
|
+
* Daran entscheiden, nie an [serverMessage]: der Text darf sich aendern.
|
|
99
|
+
*/
|
|
100
|
+
code;
|
|
101
|
+
constructor(functionName, serverMessage, code) {
|
|
96
102
|
super(`${functionName} fehlgeschlagen: ${serverMessage}`);
|
|
97
103
|
this.functionName = functionName;
|
|
98
104
|
this.serverMessage = serverMessage;
|
|
105
|
+
this.code = code;
|
|
99
106
|
}
|
|
100
107
|
}
|
|
101
108
|
const GRUND_TEXT = {
|
|
@@ -54,6 +54,22 @@ export interface TipOptions {
|
|
|
54
54
|
paymentMethod?: KeckPaymentMethod | KeckPaymentMethodKey;
|
|
55
55
|
/** Empfaenger; ohne Angabe der angemeldete Kassen-Benutzer. Summe der cents = cents. */
|
|
56
56
|
recipients?: TipRecipientShare[];
|
|
57
|
+
/**
|
|
58
|
+
* Hat der Empfaenger das Geld schon? `true` = ja (Bargeld mitgenommen,
|
|
59
|
+
* Kartentrinkgeld sofort aus der Lade ausgezahlt), `false` = der Betrieb
|
|
60
|
+
* behaelt es und schuldet es.
|
|
61
|
+
*
|
|
62
|
+
* Entscheidend ist NICHT die Zahlart, sondern der Besitz -- § 2j Abs 2 AVRAG
|
|
63
|
+
* kennt beide Faelle. Ohne Angabe gilt die Voreinstellung des Betriebs (bar:
|
|
64
|
+
* schon erhalten, bargeldlos: einbehalten); diese Angabe braucht es nur fuer
|
|
65
|
+
* die Ausnahme.
|
|
66
|
+
*
|
|
67
|
+
* NUR mit dem Recht `tipAssign` bei persoenlicher Anmeldung: das Merkmal
|
|
68
|
+
* entscheidet, ob der Betrieb Geld schuldet, und gewoehnliches Personal soll
|
|
69
|
+
* das nicht am Geraet umstellen koennen. Ueber einen Geraete-API-Schluessel
|
|
70
|
+
* (ohne angemeldeten Kassen-Benutzer) gilt die Einschraenkung nicht.
|
|
71
|
+
*/
|
|
72
|
+
sofortErhalten?: boolean;
|
|
57
73
|
}
|
|
58
74
|
export interface TipRecipientShare {
|
|
59
75
|
registerUserId: string;
|
|
@@ -385,6 +385,16 @@ function gepruefterTip(tip) {
|
|
|
385
385
|
const nutzlast = { cents: tip.cents };
|
|
386
386
|
if (tip.paymentMethod != null)
|
|
387
387
|
nutzlast['paymentMethod'] = gepruefteZahlungsart(tip.paymentMethod);
|
|
388
|
+
// Nur mitschicken, wenn gesetzt: fehlt das Feld, entscheidet die
|
|
389
|
+
// Voreinstellung des Betriebs. Ein `false` waere dort eine Aussage, kein
|
|
390
|
+
// Weglassen. Der Wortlaut des Fehlers ist der des Backends
|
|
391
|
+
// (tip-core.normalizeTip) -- wer ihn hier sieht, sieht denselben Satz.
|
|
392
|
+
if (tip.sofortErhalten != null) {
|
|
393
|
+
if (typeof tip.sofortErhalten !== 'boolean') {
|
|
394
|
+
throw eingabefehler('Trinkgeld: sofortErhalten muss true oder false sein.');
|
|
395
|
+
}
|
|
396
|
+
nutzlast['sofortErhalten'] = tip.sofortErhalten;
|
|
397
|
+
}
|
|
388
398
|
if (tip.recipients != null) {
|
|
389
399
|
if (!Array.isArray(tip.recipients) || tip.recipients.length === 0) {
|
|
390
400
|
throw eingabefehler('Trinkgeld: recipients darf nicht leer sein.');
|
|
@@ -176,7 +176,7 @@ function jsonAuswerten(text, functionName, statusCode, inhaltstyp) {
|
|
|
176
176
|
}
|
|
177
177
|
// Alles, was nicht ausdruecklich Erfolg ist, gilt als fachlicher Fehler —
|
|
178
178
|
// ein unbekannter Statuswert darf nie stillschweigend als Erfolg durchgehen.
|
|
179
|
-
throw fachfehler(functionName, huelle.message);
|
|
179
|
+
throw fachfehler(functionName, huelle.message, huelle.code);
|
|
180
180
|
}
|
|
181
181
|
/**
|
|
182
182
|
* Auswertung des Binaerwegs. Der Kern der Zusage: **ein Aufrufer bekommt nie
|
|
@@ -222,7 +222,7 @@ function pdfAuswerten(bytes, functionName, statusCode, inhaltstyp) {
|
|
|
222
222
|
}
|
|
223
223
|
// Derselbe fachliche Fehler wie auf dem JSON-Weg — fuer den Aufrufer macht
|
|
224
224
|
// es keinen Unterschied, ob er ein PDF oder eine Nutzlast erwartet hat.
|
|
225
|
-
throw fachfehler(functionName, huelle.message);
|
|
225
|
+
throw fachfehler(functionName, huelle.message, huelle.code);
|
|
226
226
|
}
|
|
227
227
|
/** `%PDF` am Anfang — die Kennung jeder PDF-Datei. */
|
|
228
228
|
function istPdf(bytes) {
|
|
@@ -235,9 +235,10 @@ function alsHuelle(wert) {
|
|
|
235
235
|
}
|
|
236
236
|
return wert;
|
|
237
237
|
}
|
|
238
|
-
function fachfehler(functionName, message) {
|
|
238
|
+
function fachfehler(functionName, message, code) {
|
|
239
239
|
const meldung = typeof message === 'string' && message.trim() ? message : 'Unbekannter Fehler';
|
|
240
|
-
|
|
240
|
+
// Nur ein Text zaehlt als Code -- alles andere waere ein geratener Vertrag.
|
|
241
|
+
return new KasseneckApiError(functionName, meldung, typeof code === 'string' && code ? code : undefined);
|
|
241
242
|
}
|
|
242
243
|
/**
|
|
243
244
|
* Nutzlast aus Auth- und Aufruferparametern. Auth-Parameter bilden die
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { ReceiptType, type ReceiptTypeKey, VatRate, type VatRateKey, KeckPaymentMethod, type KeckPaymentMethodKey, CreditCardProvider, type CreditCardProviderKey, VoucherType, type VoucherTypeKey, VoucherAction, type VoucherActionKey, StripeLinkMode, type StripeLinkModeKey, } from './enums/index.js';
|
|
2
|
-
export { type ReceiptItem, type ReceiptItemPayload, type ReceiptItemPayloadRead, toReceiptItemPayload, fromReceiptItemPayload, receiptItemTotalCents, receiptItemIsValid, negateReceiptItem, type TipRecipient, isTipItem, type Voucher, type VoucherPayload, toVoucherPayload, fromVoucherPayload, voucherIsValid, type Receipt, type ReceiptPayload, type ReceiptPayloadRead, toReceiptPayload, fromReceiptPayload, receiptSubSumCents, receiptSumCents, type CancellationOf, type CancellationItem, type Cancellation, type CancellationReason, CANCELLATION_REASONS, CANCELLATION_RESERVATION_MS, isCancellationReason, remainingQuantities, type ReceiptCompany, type ReceiptCompanyPayload, fromReceiptCompanyPayload, receiptCompanyTaxInfo, type Cashregister, type CashregisterOnboarding, type CashregisterPayload, type CashregisterOnboardingPayload, fromCashregisterPayload, type ReceiptSummary, type ReceiptSummaryPayload, fromReceiptSummaryPayload, type ZeroKind, ZERO_KINDS, istZeroKind, type ReportMonth, reportMonthFromDate, previousReportMonth, nextReportMonth, reportMonthKey, reportMonthReadable, type StripeUrlSession, type StripeUrlSessionPayload, toStripeUrlSessionPayload, fromStripeUrlSessionPayload, type HobexReceipt, type HobexReceiptPayload, toHobexReceiptPayload, fromHobexReceiptPayload, hobexReceiptToCardPaymentData, hobexReceiptNeedsSignature, } from './models/index.js';
|
|
2
|
+
export { type ReceiptItem, type ReceiptItemPayload, type ReceiptItemPayloadRead, toReceiptItemPayload, fromReceiptItemPayload, receiptItemTotalCents, receiptItemIsValid, negateReceiptItem, type TipRecipient, isTipItem, type Voucher, type VoucherPayload, toVoucherPayload, fromVoucherPayload, voucherIsValid, type Receipt, type ReceiptPayload, type ReceiptPayloadRead, toReceiptPayload, fromReceiptPayload, receiptSubSumCents, receiptSumCents, type CancellationOf, type CancellationItem, type Cancellation, type CancellationReason, type CancellationErrorCode, CANCELLATION_REASONS, CANCELLATION_ERROR_CODES, CANCELLATION_RESERVATION_MS, isCancellationReason, isCancellationErrorCode, remainingQuantities, type ReceiptCompany, type ReceiptCompanyPayload, fromReceiptCompanyPayload, receiptCompanyTaxInfo, type Cashregister, type CashregisterOnboarding, type CashregisterPayload, type CashregisterOnboardingPayload, fromCashregisterPayload, type ReceiptSummary, type ReceiptSummaryPayload, fromReceiptSummaryPayload, type ZeroKind, ZERO_KINDS, istZeroKind, type ReportMonth, reportMonthFromDate, previousReportMonth, nextReportMonth, reportMonthKey, reportMonthReadable, type StripeUrlSession, type StripeUrlSessionPayload, toStripeUrlSessionPayload, fromStripeUrlSessionPayload, type HobexReceipt, type HobexReceiptPayload, toHobexReceiptPayload, fromHobexReceiptPayload, hobexReceiptToCardPaymentData, hobexReceiptNeedsSignature, } from './models/index.js';
|
|
3
3
|
export { type AuthCredentials, type KasseneckAuth, type ApiKeyAuthOptions, type RegisterUserAuthOptions, apiKeyAuth, registerUserAuth, DEFAULT_BASE_URL, DEFAULT_TIMEOUT_MS, type HttpResponseLike, type HttpRequestInit, type FetchLike, type TransportOptions, type TransportBodyFields, type KasseneckTransport, type KasseneckBinaryTransport, AUFRUFE, type Aufruf, createTransport, createBinaryTransport, KasseneckApiError, KasseneckAuthError, KasseneckHttpError, KasseneckNetworkError, KasseneckValidationError, type ValidationScope, type KasseneckError, type HttpFailureReason, type CauseDigest, isKasseneckApiError, isKasseneckAuthError, isKasseneckHttpError, isKasseneckNetworkError, isKasseneckValidationError, type ReceiptCommonOptions, type SellReceiptOptions, type TipOptions, type TipRecipientShare, type CancelReceiptOptions, type CancelReceiptResult, type CreateCancelReceiptOptions, type ReceiptWithCompany, sellReceipt, sellReceiptWithCompany, cancelReceipt, createCancelReceipt, zeroReceipt, getReceipt, getReceiptWithCompany, generateFullReceiptId, getFirstReceiptDate, listMyReceipts, type ListMyReceiptsOptions, type ReceiptList, type ReceiptListStats, listMyCashregisters, checkVoucherCombinationError, downloadDailyReport, downloadMonthlyReport, type CashboxStatus, type SignatureStatus, getCashboxStatus, getSignatureStatus, type KasseneckApi, createKasseneckApi, } from './client/index.js';
|
|
4
4
|
export { parseServerTimeStamp, toViennaWallClock, type ViennaWallClock } from './vienna-time.js';
|
|
5
5
|
export { type KasseSettingsBetrieb, type KasseSettingsGeraet, type KasseSettings, type ArticleGroup, type KasseArtikel, KASSE_BETRIEB_STANDARD, KASSE_GERAET_STANDARD, mergeKasseSettings, fromArticleGroupPayload, fromKasseArtikelPayload, listMyArticleGroups, listMyArticles, getKasseSettings, setMyKasseSettings, setMyRegisterDeviceSettings, verteileRabatt, cancelScopeOf, receiptsScopeOf, } from './kasse/index.js';
|
package/dist/esm/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { ReceiptType, VatRate, KeckPaymentMethod, CreditCardProvider, VoucherType, VoucherAction, StripeLinkMode, } from './enums/index.js';
|
|
2
|
-
export { toReceiptItemPayload, fromReceiptItemPayload, receiptItemTotalCents, receiptItemIsValid, negateReceiptItem, isTipItem, toVoucherPayload, fromVoucherPayload, voucherIsValid, toReceiptPayload, fromReceiptPayload, receiptSubSumCents, receiptSumCents, CANCELLATION_REASONS, CANCELLATION_RESERVATION_MS, isCancellationReason, remainingQuantities, fromReceiptCompanyPayload, receiptCompanyTaxInfo, fromCashregisterPayload, fromReceiptSummaryPayload, ZERO_KINDS, istZeroKind, reportMonthFromDate, previousReportMonth, nextReportMonth, reportMonthKey, reportMonthReadable, toStripeUrlSessionPayload, fromStripeUrlSessionPayload, toHobexReceiptPayload, fromHobexReceiptPayload, hobexReceiptToCardPaymentData, hobexReceiptNeedsSignature, } from './models/index.js';
|
|
2
|
+
export { toReceiptItemPayload, fromReceiptItemPayload, receiptItemTotalCents, receiptItemIsValid, negateReceiptItem, isTipItem, toVoucherPayload, fromVoucherPayload, voucherIsValid, toReceiptPayload, fromReceiptPayload, receiptSubSumCents, receiptSumCents, CANCELLATION_REASONS, CANCELLATION_ERROR_CODES, CANCELLATION_RESERVATION_MS, isCancellationReason, isCancellationErrorCode, remainingQuantities, fromReceiptCompanyPayload, receiptCompanyTaxInfo, fromCashregisterPayload, fromReceiptSummaryPayload, ZERO_KINDS, istZeroKind, reportMonthFromDate, previousReportMonth, nextReportMonth, reportMonthKey, reportMonthReadable, toStripeUrlSessionPayload, fromStripeUrlSessionPayload, toHobexReceiptPayload, fromHobexReceiptPayload, hobexReceiptToCardPaymentData, hobexReceiptNeedsSignature, } from './models/index.js';
|
|
3
3
|
export { apiKeyAuth, registerUserAuth, DEFAULT_BASE_URL, DEFAULT_TIMEOUT_MS, AUFRUFE, createTransport, createBinaryTransport, KasseneckApiError, KasseneckAuthError, KasseneckHttpError, KasseneckNetworkError, KasseneckValidationError, isKasseneckApiError, isKasseneckAuthError, isKasseneckHttpError, isKasseneckNetworkError, isKasseneckValidationError, sellReceipt, sellReceiptWithCompany, cancelReceipt, createCancelReceipt, zeroReceipt, getReceipt, getReceiptWithCompany, generateFullReceiptId, getFirstReceiptDate, listMyReceipts, listMyCashregisters, checkVoucherCombinationError, downloadDailyReport, downloadMonthlyReport, getCashboxStatus, getSignatureStatus, createKasseneckApi, } from './client/index.js';
|
|
4
4
|
export { parseServerTimeStamp, toViennaWallClock } from './vienna-time.js';
|
|
5
5
|
export { KASSE_BETRIEB_STANDARD, KASSE_GERAET_STANDARD, mergeKasseSettings, fromArticleGroupPayload, fromKasseArtikelPayload, listMyArticleGroups, listMyArticles, getKasseSettings, setMyKasseSettings, setMyRegisterDeviceSettings, verteileRabatt, cancelScopeOf, receiptsScopeOf, } from './kasse/index.js';
|
|
@@ -9,3 +9,4 @@ export { getKasseSettings, setMyKasseSettings, setMyRegisterDeviceSettings } fro
|
|
|
9
9
|
export { verteileRabatt } from '../receipt/discount.js';
|
|
10
10
|
export { cancelScopeOf, receiptsScopeOf, type RegisterScope, type RegisterUserPerms } from '../register/pairing.js';
|
|
11
11
|
export { type NetzDrucker, type DruckJob, type DruckJobStatus, type CreatePrintJobOptions, listMyPrinters, createPrintJob, getPrintJob } from './drucker.js';
|
|
12
|
+
export { listMyTipRecipients } from './trinkgeld.js';
|
package/dist/esm/kasse/index.js
CHANGED
|
@@ -13,3 +13,4 @@ export { verteileRabatt } from '../receipt/discount.js';
|
|
|
13
13
|
// Register-Unterpfad: dessen Exportnamen sind 1:1 Function-Namen (Rewrites).
|
|
14
14
|
export { cancelScopeOf, receiptsScopeOf } from '../register/pairing.js';
|
|
15
15
|
export { listMyPrinters, createPrintJob, getPrintJob } from './drucker.js';
|
|
16
|
+
export { listMyTipRecipients } from './trinkgeld.js';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { InternerTransport } from '../client/aufrufe.js';
|
|
2
|
+
import type { TipRecipient } from '../models/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Die Personen, denen dieser Aufrufer Trinkgeld zuweisen kann.
|
|
5
|
+
*
|
|
6
|
+
* Es ist **dieselbe Menge**, die `createReceipt` akzeptiert — wer hier steht,
|
|
7
|
+
* wird beim Verkauf nicht zurueckgewiesen. Ist ein Kassen-Benutzer ohne das
|
|
8
|
+
* Recht `tipAssign` angemeldet, steht nur er selbst darin.
|
|
9
|
+
*/
|
|
10
|
+
export declare function listMyTipRecipients(rufen: InternerTransport): Promise<TipRecipient[]>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { KasseneckValidationError } from '../client/errors.js';
|
|
2
|
+
/**
|
|
3
|
+
* Die Personen, denen dieser Aufrufer Trinkgeld zuweisen kann.
|
|
4
|
+
*
|
|
5
|
+
* Es ist **dieselbe Menge**, die `createReceipt` akzeptiert — wer hier steht,
|
|
6
|
+
* wird beim Verkauf nicht zurueckgewiesen. Ist ein Kassen-Benutzer ohne das
|
|
7
|
+
* Recht `tipAssign` angemeldet, steht nur er selbst darin.
|
|
8
|
+
*/
|
|
9
|
+
export async function listMyTipRecipients(rufen) {
|
|
10
|
+
const daten = await rufen('listMyTipRecipients');
|
|
11
|
+
const roh = daten?.recipients;
|
|
12
|
+
if (!Array.isArray(roh)) {
|
|
13
|
+
// Keine Liste ist etwas anderes als eine leere Liste: „noch niemand
|
|
14
|
+
// angelegt" darf nicht aussehen wie „Antwort kaputt".
|
|
15
|
+
throw new KasseneckValidationError('listMyTipRecipients', 'Antwort enthaelt keine Liste (data.recipients fehlt)', 'response');
|
|
16
|
+
}
|
|
17
|
+
return roh.map((e) => {
|
|
18
|
+
const d = (e ?? {});
|
|
19
|
+
// Dieselbe Gestalt wie der Empfaenger am Beleg-Item — `TipRecipient` aus
|
|
20
|
+
// src/models/receipt-item.ts. Kein neuer Typ: „wer kann Trinkgeld
|
|
21
|
+
// bekommen" und „wer hat es bekommen" sind dieselbe Sache.
|
|
22
|
+
return { registerUserId: String(d.registerUserId ?? ''), name: String(d.name ?? ''), owner: d.owner === true };
|
|
23
|
+
});
|
|
24
|
+
}
|
|
@@ -18,6 +18,19 @@ export declare const CANCELLATION_REASONS: Readonly<{
|
|
|
18
18
|
}>;
|
|
19
19
|
export type CancellationReason = keyof typeof CANCELLATION_REASONS;
|
|
20
20
|
export declare function isCancellationReason(value: unknown): value is CancellationReason;
|
|
21
|
+
/**
|
|
22
|
+
* Stabile Fehlercodes von `cancelReceipt` — Zwilling von
|
|
23
|
+
* `functions/storno-core.js` STORNO_FEHLERCODES. Das Backend legt sie bei
|
|
24
|
+
* jedem fachlichen Fehler als `code` neben die Meldung; das Paket reicht sie
|
|
25
|
+
* als [KasseneckApiError.code] durch. **Entscheide am Code, nie am Text** —
|
|
26
|
+
* die deutsche Meldung darf sich aendern, der Code nicht.
|
|
27
|
+
*
|
|
28
|
+
* Nur Auth-/Parameterfehler (Sitzung abgelaufen, Pflichtfeld fehlt) kommen
|
|
29
|
+
* ohne Code; dort bleibt `code` undefined.
|
|
30
|
+
*/
|
|
31
|
+
export declare const CANCELLATION_ERROR_CODES: readonly ["beleg_nicht_gefunden", "belegart_nicht_stornierbar", "trainingsbeleg", "bereits_storniert", "position_ungueltig", "menge_ueber_rest", "grund_unbekannt", "anmerkung_zu_lang", "items_ungueltig", "kasse_nicht_zugewiesen", "keine_berechtigung", "nur_eigene_belege", "kasse_unvollstaendig", "storno_fehlgeschlagen"];
|
|
32
|
+
export type CancellationErrorCode = (typeof CANCELLATION_ERROR_CODES)[number];
|
|
33
|
+
export declare function isCancellationErrorCode(value: unknown): value is CancellationErrorCode;
|
|
21
34
|
/** Bezug eines Storno-Belegs auf sein Original. */
|
|
22
35
|
export interface CancellationOf {
|
|
23
36
|
receiptId: string;
|
|
@@ -40,6 +53,15 @@ export interface Cancellation {
|
|
|
40
53
|
by: string | null;
|
|
41
54
|
note: string | null;
|
|
42
55
|
items: CancellationItem[];
|
|
56
|
+
/**
|
|
57
|
+
* Rabattgutschein-Ausgleich, den DIESER Storno gewaehrt hat — Cent je
|
|
58
|
+
* Steuertopf des Backends (`amountRateStandard`, `amountRateReduced1`, …).
|
|
59
|
+
* Ein Rabattgutschein ist am Original schon in den Umsatz eingerechnet;
|
|
60
|
+
* jeder Storno nimmt ihn anteilig der stornierten Menge zurueck (bei 3 Stueck
|
|
61
|
+
* a 10 EUR mit 6 EUR Rabatt: 2,00 je Stueck). Fehlt das Feld, hat der Eintrag
|
|
62
|
+
* nichts gewaehrt (Altbestand vor dieser Regel) — der naechste Storno holt nach.
|
|
63
|
+
*/
|
|
64
|
+
promoAdjustmentCents?: Record<string, number>;
|
|
43
65
|
}
|
|
44
66
|
/** Ab wann eine liegengebliebene Reservierung nicht mehr zaehlt (wie im Backend). */
|
|
45
67
|
export declare const CANCELLATION_RESERVATION_MS = 120000;
|
|
@@ -18,6 +18,35 @@ export const CANCELLATION_REASONS = Object.freeze({
|
|
|
18
18
|
export function isCancellationReason(value) {
|
|
19
19
|
return typeof value === 'string' && Object.prototype.hasOwnProperty.call(CANCELLATION_REASONS, value);
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Stabile Fehlercodes von `cancelReceipt` — Zwilling von
|
|
23
|
+
* `functions/storno-core.js` STORNO_FEHLERCODES. Das Backend legt sie bei
|
|
24
|
+
* jedem fachlichen Fehler als `code` neben die Meldung; das Paket reicht sie
|
|
25
|
+
* als [KasseneckApiError.code] durch. **Entscheide am Code, nie am Text** —
|
|
26
|
+
* die deutsche Meldung darf sich aendern, der Code nicht.
|
|
27
|
+
*
|
|
28
|
+
* Nur Auth-/Parameterfehler (Sitzung abgelaufen, Pflichtfeld fehlt) kommen
|
|
29
|
+
* ohne Code; dort bleibt `code` undefined.
|
|
30
|
+
*/
|
|
31
|
+
export const CANCELLATION_ERROR_CODES = [
|
|
32
|
+
'beleg_nicht_gefunden', // Original fehlt oder gehoert nicht zu dieser Kasse
|
|
33
|
+
'belegart_nicht_stornierbar', // Original ist selbst Storno-, Null- oder Startbeleg
|
|
34
|
+
'trainingsbeleg', // Trainingsbelege werden nicht storniert
|
|
35
|
+
'bereits_storniert', // keine Restmenge mehr (auch beim positionslosen Beleg)
|
|
36
|
+
'position_ungueltig', // Index unbekannt oder doppelt
|
|
37
|
+
'menge_ueber_rest', // Menge nicht ganzzahlig >= 1 oder groesser als der Rest
|
|
38
|
+
'grund_unbekannt', // reason fehlt oder nicht im Katalog
|
|
39
|
+
'anmerkung_zu_lang', // note laenger als 200 Zeichen
|
|
40
|
+
'items_ungueltig', // items ist keine Liste
|
|
41
|
+
'kasse_nicht_zugewiesen', // Register-Benutzer darf diese Kasse nicht
|
|
42
|
+
'keine_berechtigung', // Register-Benutzer ohne Storno-Recht
|
|
43
|
+
'nur_eigene_belege', // Recht "eigene", fremder Beleg
|
|
44
|
+
'kasse_unvollstaendig', // api_key/token fehlen am Konto bzw. an der Kasse
|
|
45
|
+
'storno_fehlgeschlagen', // der Storno-Beleg selbst wurde abgelehnt (z. B. Signatur)
|
|
46
|
+
];
|
|
47
|
+
export function isCancellationErrorCode(value) {
|
|
48
|
+
return typeof value === 'string' && CANCELLATION_ERROR_CODES.includes(value);
|
|
49
|
+
}
|
|
21
50
|
/** Ab wann eine liegengebliebene Reservierung nicht mehr zaehlt (wie im Backend). */
|
|
22
51
|
export const CANCELLATION_RESERVATION_MS = 120_000;
|
|
23
52
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CreditCardProvider } from '../enums/index.js';
|
|
2
|
-
import { centsToEuro } from '../money.js';
|
|
2
|
+
import { centsToEuro, euroToCents } from '../money.js';
|
|
3
3
|
export function toHobexReceiptPayload(beleg) {
|
|
4
4
|
return {
|
|
5
5
|
transactionId: beleg.transactionId,
|
|
@@ -38,8 +38,15 @@ export function fromHobexReceiptPayload(payload) {
|
|
|
38
38
|
responseCode: payload.responseCode,
|
|
39
39
|
transactionType: payload.transactionType,
|
|
40
40
|
currency: payload.currency,
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
// Euro->Cent ueber die eine gehaertete Stelle im Paket ([euroToCents],
|
|
42
|
+
// money.ts) statt einer eigenen `Math.round(x * 100)` -- die Hobex-Antwort
|
|
43
|
+
// ist eine fremde Antwort und wird hier nur auf transactionId/
|
|
44
|
+
// transactionDate geprueft (siehe payments/hobex.ts, belegAusNutzlast);
|
|
45
|
+
// amount/tip bleiben ungeprueft. Ein nicht-numerischer oder
|
|
46
|
+
// nicht-endlicher Wert ergab bis zu dieser Fassung `NaN` -- lautlos, ohne
|
|
47
|
+
// dass irgendwo geworfen wurde. [euroToCents] liefert dafuer `0`.
|
|
48
|
+
amountCents: euroToCents(payload.amount),
|
|
49
|
+
tipCents: euroToCents(payload.tip),
|
|
43
50
|
cvm: String(payload.cvm),
|
|
44
51
|
// Die Cloud-API-Nutzlast traegt keinen eigenen Provider-Schluessel — der
|
|
45
52
|
// Provider ergibt sich aus dem Transportweg (Cloud vs. HPS), nicht aus
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { type ReceiptItem, type ReceiptItemPayload, type ReceiptItemPayloadRead, toReceiptItemPayload, fromReceiptItemPayload, receiptItemTotalCents, receiptItemIsValid, negateReceiptItem, type TipRecipient, isTipItem, isDiscountItem, } from './receipt-item.js';
|
|
2
2
|
export { type Voucher, type VoucherPayload, toVoucherPayload, fromVoucherPayload, voucherIsValid } from './voucher.js';
|
|
3
3
|
export { type Receipt, type ReceiptPayload, type ReceiptPayloadRead, toReceiptPayload, fromReceiptPayload, receiptSubSumCents, receiptSumCents, } from './receipt.js';
|
|
4
|
-
export { type CancellationOf, type CancellationItem, type Cancellation, type CancellationReason, CANCELLATION_REASONS, CANCELLATION_RESERVATION_MS, isCancellationReason, remainingQuantities, } from './cancellation.js';
|
|
4
|
+
export { type CancellationOf, type CancellationItem, type Cancellation, type CancellationReason, type CancellationErrorCode, CANCELLATION_REASONS, CANCELLATION_ERROR_CODES, CANCELLATION_RESERVATION_MS, isCancellationReason, isCancellationErrorCode, remainingQuantities, } from './cancellation.js';
|
|
5
5
|
export { type ReceiptCompany, type ReceiptCompanyPayload, fromReceiptCompanyPayload, receiptCompanyTaxInfo, } from './receipt-company.js';
|
|
6
6
|
export { type Cashregister, type CashregisterOnboarding, type CashregisterPayload, type CashregisterOnboardingPayload, fromCashregisterPayload, } from './cashregister.js';
|
|
7
7
|
export { type ReceiptSummary, type ReceiptSummaryPayload, fromReceiptSummaryPayload, type ZeroKind, ZERO_KINDS, istZeroKind } from './receipt-summary.js';
|
package/dist/esm/models/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { toReceiptItemPayload, fromReceiptItemPayload, receiptItemTotalCents, receiptItemIsValid, negateReceiptItem, isTipItem, isDiscountItem, } from './receipt-item.js';
|
|
2
2
|
export { toVoucherPayload, fromVoucherPayload, voucherIsValid } from './voucher.js';
|
|
3
3
|
export { toReceiptPayload, fromReceiptPayload, receiptSubSumCents, receiptSumCents, } from './receipt.js';
|
|
4
|
-
export { CANCELLATION_REASONS, CANCELLATION_RESERVATION_MS, isCancellationReason, remainingQuantities, } from './cancellation.js';
|
|
4
|
+
export { CANCELLATION_REASONS, CANCELLATION_ERROR_CODES, CANCELLATION_RESERVATION_MS, isCancellationReason, isCancellationErrorCode, remainingQuantities, } from './cancellation.js';
|
|
5
5
|
export { fromReceiptCompanyPayload, receiptCompanyTaxInfo, } from './receipt-company.js';
|
|
6
6
|
export { fromCashregisterPayload, } from './cashregister.js';
|
|
7
7
|
export { fromReceiptSummaryPayload, ZERO_KINDS, istZeroKind } from './receipt-summary.js';
|
|
@@ -84,6 +84,9 @@ function leseStorno(eintrag) {
|
|
|
84
84
|
by: eintrag.by ?? null,
|
|
85
85
|
note: eintrag.note ?? null,
|
|
86
86
|
items: (eintrag.items ?? []).map((p) => ({ index: Number(p.index), quantity: Number(p.quantity) })),
|
|
87
|
+
...(eintrag.promoAdjustmentCents && typeof eintrag.promoAdjustmentCents === 'object'
|
|
88
|
+
? { promoAdjustmentCents: Object.fromEntries(Object.entries(eintrag.promoAdjustmentCents).map(([k, v]) => [k, Number(v)])) }
|
|
89
|
+
: {}),
|
|
87
90
|
};
|
|
88
91
|
}
|
|
89
92
|
/**
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { VoucherAction, VoucherType } from '../enums/index.js';
|
|
2
|
+
import { euroToCents } from '../money.js';
|
|
2
3
|
import { readEnumKey, requireEnumKey } from './enum-payload.js';
|
|
3
4
|
export function toVoucherPayload(voucher) {
|
|
4
5
|
return {
|
|
@@ -30,16 +31,31 @@ export function voucherIsValid(voucher) {
|
|
|
30
31
|
if (voucher.type === VoucherType.promo && voucher.valueCents == null) {
|
|
31
32
|
return false;
|
|
32
33
|
}
|
|
33
|
-
|
|
34
|
+
// `!Number.isFinite` VOR den Vergleich, nicht dahinter: `NaN <= 0` und
|
|
35
|
+
// `Infinity <= 0` sind beide `false`, ein Vergleich mit einem
|
|
36
|
+
// nicht-endlichen Wert ist also IMMER falsch und liesse ihn hier
|
|
37
|
+
// durchrutschen. Gemessen (28.08.2026): ein Gutschein mit `valueCents: NaN`
|
|
38
|
+
// (aus einem kaputten `value` in der Nutzlast, siehe [fromVoucherPayload])
|
|
39
|
+
// oder `valueCents: Infinity` galt bis zu dieser Fassung als GUELTIG und
|
|
40
|
+
// waere still in die Belegsummen gewandert. Diese Pruefung greift
|
|
41
|
+
// unabhaengig davon, ob `valueCents` ueber [fromVoucherPayload] oder direkt
|
|
42
|
+
// gesetzt wurde -- die gehaertete Umrechnung dort allein reicht nicht.
|
|
43
|
+
if (voucher.valueCents != null && (!Number.isFinite(voucher.valueCents) || voucher.valueCents <= 0)) {
|
|
34
44
|
return false;
|
|
35
45
|
}
|
|
36
46
|
return true;
|
|
37
47
|
}
|
|
38
48
|
export function fromVoucherPayload(payload) {
|
|
49
|
+
// Euro->Cent ueber die eine gehaertete Stelle im Paket ([euroToCents],
|
|
50
|
+
// money.ts) statt einer eigenen `Math.round(payload.value * 100)` --
|
|
51
|
+
// [euroToCents] prueft `Number.isFinite` und liefert fuer einen
|
|
52
|
+
// nicht-numerischen oder nicht-endlichen Wert `0` statt eines lautlosen
|
|
53
|
+
// `NaN`. Siehe auch [voucherIsValid]: `0` faellt dort ueber die
|
|
54
|
+
// "<= 0"-Pruefung, ein `NaN` waere durchgerutscht.
|
|
39
55
|
const valueCents = payload.valueCents != null
|
|
40
56
|
? payload.valueCents
|
|
41
57
|
: payload.value != null
|
|
42
|
-
?
|
|
58
|
+
? euroToCents(payload.value)
|
|
43
59
|
: undefined;
|
|
44
60
|
return {
|
|
45
61
|
name: payload.name ?? undefined,
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { type HpsTransactionResponse } from './transaction-response.js';
|
|
2
|
+
/**
|
|
3
|
+
* Client fuer die Terminal-Endpunkte von **Kasseneck Connect** — dem lokalen
|
|
4
|
+
* Geraete-Agenten, der ueber HTTP erreichbar ist und mit dem hobex-HPS
|
|
5
|
+
* spricht (`kasseneck-connect/lib/src/api/routes_terminal.dart`). Connect
|
|
6
|
+
* reicht die Antwort des Terminals ROH durch und ordnet nichts ein; die
|
|
7
|
+
* Einordnung passiert in `transaction-response.ts` und `payments.ts`.
|
|
8
|
+
*
|
|
9
|
+
* **Kein direkter Terminal-Kontakt.** Ein Browser hat keine rohen
|
|
10
|
+
* TCP-Sockets — das ist der Unterschied zum Dart-Zwilling `HpsClient`, der
|
|
11
|
+
* das HPS-Terminal direkt anspricht. Dieser Client spricht ausschliesslich
|
|
12
|
+
* mit Connect, niemals mit dem Terminal selbst.
|
|
13
|
+
*
|
|
14
|
+
* **`payment`/`status`/`abort`/`refund`/`cancel`.** Seit
|
|
15
|
+
* `kasseneck-connect` Commit `1c8a003` traegt Connect auch die Gutschrift
|
|
16
|
+
* (`POST /v1/terminal/refund`) und die Aufhebung (`POST /v1/terminal/cancel`)
|
|
17
|
+
* — beide erben denselben Fehlerpfad wie `payment` (`error.detail.terminalHttpStatus`,
|
|
18
|
+
* siehe `HpsConnectTerminalError.isTerminalBusy`).
|
|
19
|
+
*/
|
|
20
|
+
/** Adresse des Ziel-Terminals — bei jedem Aufruf mitgegeben, Connect selbst ist zustandslos. */
|
|
21
|
+
export interface HpsConnectTarget {
|
|
22
|
+
/** IP oder Hostname des Terminals im Kassen-Netz. */
|
|
23
|
+
host: string;
|
|
24
|
+
/** Vorgabe: `8080` (hobex-HPS-Standardport), siehe Connect. */
|
|
25
|
+
port?: number;
|
|
26
|
+
/** Terminal-ID, wie am Geraet angezeigt (fuehrende Nullen werden toleriert). */
|
|
27
|
+
tid: string;
|
|
28
|
+
}
|
|
29
|
+
export interface HpsConnectPaymentOptions extends HpsConnectTarget {
|
|
30
|
+
/** Kennung dieser Zahlung — MUSS vor diesem Aufruf feststehen, siehe `transaction-id.ts`. */
|
|
31
|
+
transactionId: string;
|
|
32
|
+
/** Zu belastender Betrag in **Cent** (ohne Trinkgeld). */
|
|
33
|
+
amountCents: number;
|
|
34
|
+
/** Trinkgeld in **Cent**; das Terminal belastet Betrag + Trinkgeld in einem. */
|
|
35
|
+
tipCents?: number;
|
|
36
|
+
reference?: string;
|
|
37
|
+
currency?: string;
|
|
38
|
+
language?: string;
|
|
39
|
+
}
|
|
40
|
+
export interface HpsConnectTransactionOptions extends HpsConnectTarget {
|
|
41
|
+
transactionId: string;
|
|
42
|
+
}
|
|
43
|
+
export interface HpsConnectRefundOptions extends HpsConnectTarget {
|
|
44
|
+
/** Kennung der Gutschrift SELBST — eine NEUE, nicht die der erstatteten Zahlung. */
|
|
45
|
+
transactionId: string;
|
|
46
|
+
/** Kennung der erstatteten Zahlung. Verlangt Connect zwingend, siehe `_handleRefund`. */
|
|
47
|
+
originalTransactionId: string;
|
|
48
|
+
/** Zu erstattender Betrag in **Cent**. */
|
|
49
|
+
amountCents: number;
|
|
50
|
+
reference?: string;
|
|
51
|
+
currency?: string;
|
|
52
|
+
language?: string;
|
|
53
|
+
}
|
|
54
|
+
export interface HpsConnectCancelOptions extends HpsConnectTarget {
|
|
55
|
+
/** Kennung der URSPRUENGLICHEN Zahlung — keine neue. */
|
|
56
|
+
transactionId: string;
|
|
57
|
+
/** Pflicht: das Terminal weist einen Void ohne Betrag mit `400 Missing amount` ab. */
|
|
58
|
+
amountCents: number;
|
|
59
|
+
currency?: string;
|
|
60
|
+
language?: string;
|
|
61
|
+
}
|
|
62
|
+
/** Austauschbare `fetch`-Umsetzung, minimal genug fuer eine Attrappe im Test. */
|
|
63
|
+
export interface HpsConnectFetchResponse {
|
|
64
|
+
status: number;
|
|
65
|
+
text(): Promise<string>;
|
|
66
|
+
}
|
|
67
|
+
export type HpsConnectFetch = (url: string, init: {
|
|
68
|
+
method: string;
|
|
69
|
+
headers: Record<string, string>;
|
|
70
|
+
body: string;
|
|
71
|
+
signal: AbortSignal;
|
|
72
|
+
}) => Promise<HpsConnectFetchResponse>;
|
|
73
|
+
export interface HpsConnectClientOptions {
|
|
74
|
+
/** Basisadresse des Agenten. Vorgabe `http://127.0.0.1:27182` (README `kasseneck-connect`). */
|
|
75
|
+
baseUrl?: string;
|
|
76
|
+
/** Kopplungstoken, geht als `Authorization: Bearer <token>` mit. */
|
|
77
|
+
token: string;
|
|
78
|
+
/**
|
|
79
|
+
* Zeitlimit fuer `status`/`abort`/`diagnosis` in ms. Vorgabe 30 s — deutlich
|
|
80
|
+
* ueber Connects eigenem `hpsKurzTimeout` (15 s), damit dieser Client nicht
|
|
81
|
+
* VOR Connect selbst aufgibt und dessen Fehlermeldung verschluckt.
|
|
82
|
+
*/
|
|
83
|
+
shortTimeoutMs?: number;
|
|
84
|
+
/**
|
|
85
|
+
* Zeitlimit fuer `payment` in ms. Vorgabe 4,5 Minuten — ueber Connects
|
|
86
|
+
* eigenem `hpsZahlungTimeout` (4 Minuten): eine Zahlung blockiert, bis der
|
|
87
|
+
* Karteninhaber fertig ist, das darf nicht vorzeitig aufgegeben werden.
|
|
88
|
+
*/
|
|
89
|
+
paymentTimeoutMs?: number;
|
|
90
|
+
/** Eigene `fetch`-Umsetzung (Tests). Vorgabe: globales `fetch`. */
|
|
91
|
+
fetch?: HpsConnectFetch;
|
|
92
|
+
}
|
|
93
|
+
export interface HpsConnectClient {
|
|
94
|
+
payment(options: HpsConnectPaymentOptions): Promise<HpsTransactionResponse>;
|
|
95
|
+
status(options: HpsConnectTransactionOptions): Promise<HpsTransactionResponse>;
|
|
96
|
+
abort(options: HpsConnectTransactionOptions): Promise<HpsTransactionResponse>;
|
|
97
|
+
/** Gutschrift — ein Kartenfluss wie [payment], deshalb dieselbe lange Frist. */
|
|
98
|
+
refund(options: HpsConnectRefundOptions): Promise<HpsTransactionResponse>;
|
|
99
|
+
/** Aufhebung (Storno/Void) — kurzer Aufruf, keine Karteninteraktion. */
|
|
100
|
+
cancel(options: HpsConnectCancelOptions): Promise<HpsTransactionResponse>;
|
|
101
|
+
/** Erreichbarkeits-/Firmware-Check, folgenlos. Rohe Nutzlast, Connect ordnet nichts ein. */
|
|
102
|
+
diagnosis(options: HpsConnectTarget): Promise<Record<string, unknown>>;
|
|
103
|
+
}
|
|
104
|
+
export declare function createHpsConnectClient(options: HpsConnectClientOptions): HpsConnectClient;
|