@kreiseck/kasseneck-api 0.7.0 → 0.7.2
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 +32 -0
- package/README.md +87 -15
- package/dist/cjs/client/aufrufe.d.ts +1 -1
- package/dist/cjs/client/aufrufe.js +1 -0
- package/dist/cjs/client/errors.d.ts +8 -4
- package/dist/cjs/client/errors.js +12 -9
- package/dist/cjs/client/receipts.d.ts +22 -0
- package/dist/cjs/client/receipts.js +16 -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/models/cancellation.d.ts +28 -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-summary.d.ts +2 -0
- package/dist/cjs/models/receipt-summary.js +4 -1
- package/dist/cjs/models/receipt.js +8 -1
- 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 +67 -0
- package/dist/cjs/payments/hobex-hps/outcome.d.ts +46 -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 +573 -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 +175 -0
- package/dist/cjs/payments/hobex-hps/transaction-response.js +266 -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/receipt/layout.js +19 -0
- package/dist/cjs/register/index.d.ts +1 -1
- package/dist/cjs/register/index.js +2 -1
- package/dist/cjs/register/pairing.d.ts +59 -0
- package/dist/cjs/register/pairing.js +51 -2
- package/dist/esm/client/aufrufe.d.ts +1 -1
- package/dist/esm/client/aufrufe.js +1 -0
- package/dist/esm/client/errors.d.ts +8 -4
- package/dist/esm/client/errors.js +12 -9
- package/dist/esm/client/receipts.d.ts +22 -0
- package/dist/esm/client/receipts.js +16 -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/models/cancellation.d.ts +28 -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-summary.d.ts +2 -0
- package/dist/esm/models/receipt-summary.js +4 -1
- package/dist/esm/models/receipt.js +8 -1
- 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 +46 -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 +570 -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 +175 -0
- package/dist/esm/payments/hobex-hps/transaction-response.js +254 -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/receipt/layout.js +19 -0
- package/dist/esm/register/index.d.ts +1 -1
- package/dist/esm/register/index.js +1 -1
- package/dist/esm/register/pairing.d.ts +59 -0
- package/dist/esm/register/pairing.js +50 -2
- package/fixtures/belege/storno-rabatt.json +3 -2
- package/fixtures/belege/storno-teil.json +2 -1
- package/fixtures/belege/storno-voll.json +2 -1
- package/fixtures/erwartet/storno-rabatt.grid32.txt +1 -0
- package/fixtures/erwartet/storno-rabatt.grid48.txt +1 -0
- package/fixtures/erwartet/storno-rabatt.lines.json +6 -0
- package/fixtures/erwartet/storno-teil.grid32.txt +1 -0
- package/fixtures/erwartet/storno-teil.grid48.txt +1 -0
- package/fixtures/erwartet/storno-teil.lines.json +6 -0
- package/fixtures/erwartet/storno-voll.grid32.txt +1 -0
- package/fixtures/erwartet/storno-voll.grid48.txt +1 -0
- package/fixtures/erwartet/storno-voll.lines.json +6 -0
- package/fixtures/hobex-hps-codes.json +83 -0
- package/fixtures/manifest.json +12 -12
- package/fixtures/oberflaeche.json +2 -1
- package/package.json +3 -2
|
@@ -27,6 +27,14 @@ export interface RegisterClientInfo {
|
|
|
27
27
|
w: number;
|
|
28
28
|
h: number;
|
|
29
29
|
};
|
|
30
|
+
/**
|
|
31
|
+
* Welcher Build der Anwendung gerade laeuft (frei, z. B. "0.6.46+3120").
|
|
32
|
+
* Bei einer Stoerungsmeldung die entscheidende Angabe: ohne sie laesst sich
|
|
33
|
+
* ein alter Build im Browser-Cache weder belegen noch ausschliessen -- und
|
|
34
|
+
* genau der steckt oft hinter "bei mir geht es aber". Das Backend kuerzt auf
|
|
35
|
+
* 80 Zeichen (register-geraetedaten.js, clientAusParams).
|
|
36
|
+
*/
|
|
37
|
+
app?: string;
|
|
30
38
|
}
|
|
31
39
|
/** Standort aus der Browser-Ortung (freiwillig); Grundlage der Standortsperre. */
|
|
32
40
|
export interface RegisterGeo {
|
|
@@ -214,6 +222,15 @@ export interface RegisterUserLoginOptions extends RegisterDeviceConnection, Regi
|
|
|
214
222
|
* es bei der Abweisung "Kasse wird gerade auf … verwendet".
|
|
215
223
|
*/
|
|
216
224
|
takeover?: boolean;
|
|
225
|
+
/**
|
|
226
|
+
* Welche Sitzung soll weichen? Kennung aus [listRegisterSessionsForDevice].
|
|
227
|
+
* Ohne Angabe verdraengt das Backend die aelteste -- so wie bisher.
|
|
228
|
+
*
|
|
229
|
+
* Eine Kennung, die nicht mehr laeuft, wird ABGEWIESEN statt still durch die
|
|
230
|
+
* aelteste ersetzt: Wer waehlt, bekommt seine Wahl oder eine Fehlermeldung,
|
|
231
|
+
* nie einen Unbeteiligten.
|
|
232
|
+
*/
|
|
233
|
+
takeoverSessionId?: string;
|
|
217
234
|
}
|
|
218
235
|
export interface RegisterPinLoginOptions extends RegisterDeviceConnection, RegisterDeviceCredentials, RegisterGeraeteAngaben {
|
|
219
236
|
/**
|
|
@@ -227,6 +244,8 @@ export interface RegisterPinLoginOptions extends RegisterDeviceConnection, Regis
|
|
|
227
244
|
cashregisterId: string;
|
|
228
245
|
/** Wie [RegisterUserLoginOptions.takeover]. */
|
|
229
246
|
takeover?: boolean;
|
|
247
|
+
/** Wie [RegisterUserLoginOptions.takeoverSessionId]. */
|
|
248
|
+
takeoverSessionId?: string;
|
|
230
249
|
}
|
|
231
250
|
/**
|
|
232
251
|
* Geraet koppeln: der Kopplungs-Code wird gegen den dauerhaften Ausweis dieses
|
|
@@ -252,6 +271,46 @@ export declare function pairRegisterDevice(options: PairRegisterDeviceOptions):
|
|
|
252
271
|
* Geraet meldet Erfolg. Danach das Geraet lokal vergessen.
|
|
253
272
|
*/
|
|
254
273
|
export declare function unpairRegisterDevice(options: ListRegisterUsersForDeviceOptions): Promise<void>;
|
|
274
|
+
/** Eine laufende Sitzung an einer Kasse -- Zeile in der Auswahl. */
|
|
275
|
+
export interface RegisterSession {
|
|
276
|
+
id: string;
|
|
277
|
+
deviceId: string | null;
|
|
278
|
+
/** Name des Geraets; „Kasse", wenn keiner gesetzt wurde. */
|
|
279
|
+
deviceLabel: string;
|
|
280
|
+
startedAt: number | null;
|
|
281
|
+
expiresAt: number | null;
|
|
282
|
+
/** Laeuft diese Sitzung auf DIESEM Geraet? */
|
|
283
|
+
selbst: boolean;
|
|
284
|
+
/**
|
|
285
|
+
* Wer angemeldet ist. Fehlt an einem nur-PIN-Geraet: Dort zeigt schon die
|
|
286
|
+
* Benutzerliste keine Namen, und was dort nicht steht, darf hier nicht
|
|
287
|
+
* durch die Hintertuer kommen. `undefined` heisst „wird nicht genannt",
|
|
288
|
+
* nicht „hat keinen Namen".
|
|
289
|
+
*/
|
|
290
|
+
userName?: string;
|
|
291
|
+
}
|
|
292
|
+
/** Was eine Kasse gerade haelt -- Lizenzzahl und laufende Sitzungen. */
|
|
293
|
+
export interface RegisterSessionsStand {
|
|
294
|
+
/** Wie viele Sitzungen gleichzeitig laufen duerfen. */
|
|
295
|
+
licenses: number;
|
|
296
|
+
/** Aelteste zuerst: oben steht, was das Backend ohne Wahl verdraengen wuerde. */
|
|
297
|
+
sessions: RegisterSession[];
|
|
298
|
+
}
|
|
299
|
+
export interface ListRegisterSessionsForDeviceOptions extends RegisterDeviceConnection, RegisterDeviceCredentials {
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Welche Sitzungen haelt diese Kasse gerade?
|
|
303
|
+
*
|
|
304
|
+
* Fuer den Anmeldebildschirm: Sind alle Lizenzplaetze belegt, muss eine
|
|
305
|
+
* weichen -- und der Kassier soll sehen, WELCHE, statt dass das Backend still
|
|
306
|
+
* die aelteste nimmt. Die Wahl geht dann als
|
|
307
|
+
* [RegisterUserLoginOptions.takeoverSessionId] mit.
|
|
308
|
+
*
|
|
309
|
+
* Ausgewiesen wird sich ueber das Geraete-Geheimnis, wie bei
|
|
310
|
+
* [listRegisterUsersForDevice]: An diesem Punkt gibt es weder Sitzung noch
|
|
311
|
+
* Token. Die Kasse bestimmt das Backend aus dem Geraet, nicht aus dem Aufruf.
|
|
312
|
+
*/
|
|
313
|
+
export declare function listRegisterSessionsForDevice(options: ListRegisterSessionsForDeviceOptions): Promise<RegisterSessionsStand>;
|
|
255
314
|
export declare function listRegisterUsersForDevice(options: ListRegisterUsersForDeviceOptions): Promise<RegisterDeviceUsers>;
|
|
256
315
|
/**
|
|
257
316
|
* Kassen-Benutzer per PIN anmelden. **Ohne Anmeldung** — dieser Aufruf
|
|
@@ -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;
|
|
@@ -119,6 +120,48 @@ async function unpairRegisterDevice(options) {
|
|
|
119
120
|
pflicht(name, 'deviceSecret', deviceSecret);
|
|
120
121
|
await transportFuer(verbindung)(name, { ownerUid, deviceId, deviceSecret }, undefined, [deviceSecret]);
|
|
121
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
|
+
}
|
|
122
165
|
async function listRegisterUsersForDevice(options) {
|
|
123
166
|
const { ownerUid, deviceId, deviceSecret, ...verbindung } = options;
|
|
124
167
|
const name = 'listRegisterUsersForDevice';
|
|
@@ -192,7 +235,7 @@ function regel(wert) {
|
|
|
192
235
|
* sperren gestaffelt (ab dem fuenften 30 Sekunden, ab dem neunten 15 Minuten).
|
|
193
236
|
*/
|
|
194
237
|
async function registerUserLogin(options) {
|
|
195
|
-
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;
|
|
196
239
|
const name = 'registerUserLogin';
|
|
197
240
|
pflicht(name, 'ownerUid', ownerUid);
|
|
198
241
|
pflicht(name, 'deviceId', deviceId);
|
|
@@ -210,6 +253,9 @@ async function registerUserLogin(options) {
|
|
|
210
253
|
// Nur die ausdrueckliche Uebernahme geht mit: das Backend prueft auf
|
|
211
254
|
// `=== true`, und ein mitgesendetes `false` waere nur Rauschen.
|
|
212
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,
|
|
213
259
|
client, geo: geo ?? undefined,
|
|
214
260
|
}, undefined, [pin, deviceSecret]);
|
|
215
261
|
return sitzungAusAntwort(name, daten);
|
|
@@ -221,7 +267,7 @@ async function registerUserLogin(options) {
|
|
|
221
267
|
* zaehlen und sperren dort am **Geraet**, nicht an einem Benutzer.
|
|
222
268
|
*/
|
|
223
269
|
async function registerPinLogin(options) {
|
|
224
|
-
const { ownerUid, deviceId, deviceSecret, pin, cashregisterId, takeover, client, geo, ...verbindung } = options;
|
|
270
|
+
const { ownerUid, deviceId, deviceSecret, pin, cashregisterId, takeover, takeoverSessionId, client, geo, ...verbindung } = options;
|
|
225
271
|
const name = 'registerPinLogin';
|
|
226
272
|
pflicht(name, 'ownerUid', ownerUid);
|
|
227
273
|
pflicht(name, 'deviceId', deviceId);
|
|
@@ -236,6 +282,9 @@ async function registerPinLogin(options) {
|
|
|
236
282
|
cashregisterId,
|
|
237
283
|
// Nur die ausdrueckliche Uebernahme geht mit — wie bei registerUserLogin.
|
|
238
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,
|
|
239
288
|
client, geo: geo ?? undefined,
|
|
240
289
|
}, undefined, [pin, deviceSecret]);
|
|
241
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 ["activateCashregister", "cancelReceipt", "createCustomerCashregister", "createPartnerCustomer", "checkPartnerCustomerEmail", "createPartnerWebhook", "createPaymentLinkStripe", "createPrintJob", "createReceipt", "deletePartnerWebhook", "downloadDailyReport", "downloadReport", "endRegisterSession", "financeWebService", "generateFullReceiptId", "getCustomerCredentials", "getCustomerSignatureStatus", "getFirstReceiptDate", "getKasseSettings", "getPartnerCustomer", "getPartnerInfo", "getPrintJob", "getReceipt", "hobexPayApi", "hobexRefundApi", "listCustomerCashregisters", "listMyArticleGroups", "listMyArticles", "listMyCashregisters", "listMyPrinters", "listMyReceipts", "listMyTipRecipients", "listPartnerCustomers", "listPartnerWebhookDeliveries", "listPartnerWebhooks", "listRegisterUsersForDevice", "pairRegisterDevice", "registerPinLogin", "registerUserLogin", "renewRegisterSession", "requestCustomerSignature", "sendPartnerCustomerFonLink", "sendPartnerWebhookTest", "setMyKasseSettings", "setMyRegisterDeviceSettings", "stripeCaptureIntent", "unpairRegisterDevice", "rotatePartnerWebhookSecret", "updatePartnerWebhook"];
|
|
16
|
+
export declare const AUFRUFE: readonly ["activateCashregister", "cancelReceipt", "createCustomerCashregister", "createPartnerCustomer", "checkPartnerCustomerEmail", "createPartnerWebhook", "createPaymentLinkStripe", "createPrintJob", "createReceipt", "deletePartnerWebhook", "downloadDailyReport", "downloadReport", "endRegisterSession", "financeWebService", "generateFullReceiptId", "getCustomerCredentials", "getCustomerSignatureStatus", "getFirstReceiptDate", "getKasseSettings", "getPartnerCustomer", "getPartnerInfo", "getPrintJob", "getReceipt", "hobexPayApi", "hobexRefundApi", "listCustomerCashregisters", "listMyArticleGroups", "listMyArticles", "listMyCashregisters", "listMyPrinters", "listMyReceipts", "listMyTipRecipients", "listPartnerCustomers", "listPartnerWebhookDeliveries", "listPartnerWebhooks", "listRegisterUsersForDevice", "listRegisterSessionsForDevice", "pairRegisterDevice", "registerPinLogin", "registerUserLogin", "renewRegisterSession", "requestCustomerSignature", "sendPartnerCustomerFonLink", "sendPartnerWebhookTest", "setMyKasseSettings", "setMyRegisterDeviceSettings", "stripeCaptureIntent", "unpairRegisterDevice", "rotatePartnerWebhookSecret", "updatePartnerWebhook"];
|
|
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>;
|
|
@@ -95,9 +95,13 @@ export declare class KasseneckApiError extends Error {
|
|
|
95
95
|
/** Meldung des Backends, unveraendert (`message` aus der Huelle). */
|
|
96
96
|
readonly serverMessage: string;
|
|
97
97
|
/**
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
98
|
+
* Stabiler, maschinenlesbarer Fehlercode des Backends, wenn der Endpunkt
|
|
99
|
+
* einen legt. Zwei Ablageorte kommen vor und beide zaehlen: das Feld `code`
|
|
100
|
+
* neben `message` (cancelReceipt, siehe CANCELLATION_ERROR_CODES) und
|
|
101
|
+
* `data.code` (Partner-API: `vertrag_offen`, `rate_limited`, …). Nur
|
|
102
|
+
* Bezeichner gelten — ein Freitext waere kein Code. Die aelteren Endpunkte
|
|
103
|
+
* antworten ohne Code — dann `undefined`. **Daran entscheiden, nie an
|
|
104
|
+
* [serverMessage]:** der Text darf sich aendern, der Code nicht.
|
|
101
105
|
*/
|
|
102
106
|
readonly code: string | undefined;
|
|
103
107
|
/**
|
|
@@ -106,7 +110,7 @@ export declare class KasseneckApiError extends Error {
|
|
|
106
110
|
* beilegt. Immer ein Objekt, notfalls ein leeres.
|
|
107
111
|
*/
|
|
108
112
|
readonly details: Record<string, unknown>;
|
|
109
|
-
constructor(functionName: string, serverMessage: string, details?: Record<string, unknown
|
|
113
|
+
constructor(functionName: string, serverMessage: string, details?: Record<string, unknown>, code?: string);
|
|
110
114
|
}
|
|
111
115
|
/** Warum die Antwort keine verwertbare Huelle war. */
|
|
112
116
|
export type HttpFailureReason = 'server-error' | 'empty-body' | 'not-json' | 'missing-status';
|
|
@@ -171,9 +171,13 @@ export class KasseneckApiError extends Error {
|
|
|
171
171
|
/** Meldung des Backends, unveraendert (`message` aus der Huelle). */
|
|
172
172
|
serverMessage;
|
|
173
173
|
/**
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
174
|
+
* Stabiler, maschinenlesbarer Fehlercode des Backends, wenn der Endpunkt
|
|
175
|
+
* einen legt. Zwei Ablageorte kommen vor und beide zaehlen: das Feld `code`
|
|
176
|
+
* neben `message` (cancelReceipt, siehe CANCELLATION_ERROR_CODES) und
|
|
177
|
+
* `data.code` (Partner-API: `vertrag_offen`, `rate_limited`, …). Nur
|
|
178
|
+
* Bezeichner gelten — ein Freitext waere kein Code. Die aelteren Endpunkte
|
|
179
|
+
* antworten ohne Code — dann `undefined`. **Daran entscheiden, nie an
|
|
180
|
+
* [serverMessage]:** der Text darf sich aendern, der Code nicht.
|
|
177
181
|
*/
|
|
178
182
|
code;
|
|
179
183
|
/**
|
|
@@ -182,16 +186,15 @@ export class KasseneckApiError extends Error {
|
|
|
182
186
|
* beilegt. Immer ein Objekt, notfalls ein leeres.
|
|
183
187
|
*/
|
|
184
188
|
details;
|
|
185
|
-
constructor(functionName, serverMessage, details = {}) {
|
|
189
|
+
constructor(functionName, serverMessage, details = {}, code) {
|
|
186
190
|
super(`${functionName} fehlgeschlagen: ${serverMessage}`);
|
|
187
191
|
this.functionName = functionName;
|
|
188
192
|
this.serverMessage = serverMessage;
|
|
189
193
|
this.details = details;
|
|
190
|
-
// Der Code
|
|
191
|
-
//
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
this.code = typeof code === 'string' && BEZEICHNER.test(code) ? code : undefined;
|
|
194
|
+
// Der Code neben `message` hat Vorrang; fehlt er, gilt `data.code` aus
|
|
195
|
+
// den Details. So bleibt die Klasse fuer beide Ablageorte dieselbe.
|
|
196
|
+
const kandidat = code !== undefined ? code : details['code'];
|
|
197
|
+
this.code = typeof kandidat === 'string' && BEZEICHNER.test(kandidat) ? kandidat : undefined;
|
|
195
198
|
}
|
|
196
199
|
}
|
|
197
200
|
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;
|
|
@@ -169,6 +185,12 @@ export declare function cancelReceipt(rufen: InternerTransport, options: CancelR
|
|
|
169
185
|
* Stornobeleg aus frei uebergebenen Positionen — fuer den Fall, dass der
|
|
170
186
|
* Originalbeleg nicht als Objekt vorliegt. Die Positionen gehen **unveraendert**
|
|
171
187
|
* hinaus; das Vorzeichen setzt der Aufrufer.
|
|
188
|
+
*
|
|
189
|
+
* @deprecated Alter Storno-Weg ohne Bezug: kein Verweis auf das Original,
|
|
190
|
+
* keine Restmengen, kein Schutz vor doppeltem Storno, Gutscheine werden nicht
|
|
191
|
+
* zurueckgenommen. Das Backend nimmt ihn weiter an und legt `deprecation` in
|
|
192
|
+
* die Antwort. Stattdessen [cancelReceipt] (Endpunkt `cancelReceipt`): Bezug,
|
|
193
|
+
* Grund, Teilstorno, Fehlercodes.
|
|
172
194
|
*/
|
|
173
195
|
export declare function createCancelReceipt(rufen: InternerTransport, options: CreateCancelReceiptOptions): Promise<Receipt>;
|
|
174
196
|
/** Nullbeleg (RKSV-Pruefbeleg) — ohne Positionen und ohne Zahlungsart. */
|
|
@@ -160,6 +160,12 @@ export async function cancelReceipt(rufen, options) {
|
|
|
160
160
|
* Stornobeleg aus frei uebergebenen Positionen — fuer den Fall, dass der
|
|
161
161
|
* Originalbeleg nicht als Objekt vorliegt. Die Positionen gehen **unveraendert**
|
|
162
162
|
* hinaus; das Vorzeichen setzt der Aufrufer.
|
|
163
|
+
*
|
|
164
|
+
* @deprecated Alter Storno-Weg ohne Bezug: kein Verweis auf das Original,
|
|
165
|
+
* keine Restmengen, kein Schutz vor doppeltem Storno, Gutscheine werden nicht
|
|
166
|
+
* zurueckgenommen. Das Backend nimmt ihn weiter an und legt `deprecation` in
|
|
167
|
+
* die Antwort. Stattdessen [cancelReceipt] (Endpunkt `cancelReceipt`): Bezug,
|
|
168
|
+
* Grund, Teilstorno, Fehlercodes.
|
|
163
169
|
*/
|
|
164
170
|
export function createCancelReceipt(rufen, options) {
|
|
165
171
|
return createReceipt(rufen, { ...options, receiptType: ReceiptType.cancellation });
|
|
@@ -385,6 +391,16 @@ function gepruefterTip(tip) {
|
|
|
385
391
|
const nutzlast = { cents: tip.cents };
|
|
386
392
|
if (tip.paymentMethod != null)
|
|
387
393
|
nutzlast['paymentMethod'] = gepruefteZahlungsart(tip.paymentMethod);
|
|
394
|
+
// Nur mitschicken, wenn gesetzt: fehlt das Feld, entscheidet die
|
|
395
|
+
// Voreinstellung des Betriebs. Ein `false` waere dort eine Aussage, kein
|
|
396
|
+
// Weglassen. Der Wortlaut des Fehlers ist der des Backends
|
|
397
|
+
// (tip-core.normalizeTip) -- wer ihn hier sieht, sieht denselben Satz.
|
|
398
|
+
if (tip.sofortErhalten != null) {
|
|
399
|
+
if (typeof tip.sofortErhalten !== 'boolean') {
|
|
400
|
+
throw eingabefehler('Trinkgeld: sofortErhalten muss true oder false sein.');
|
|
401
|
+
}
|
|
402
|
+
nutzlast['sofortErhalten'] = tip.sofortErhalten;
|
|
403
|
+
}
|
|
388
404
|
if (tip.recipients != null) {
|
|
389
405
|
if (!Array.isArray(tip.recipients) || tip.recipients.length === 0) {
|
|
390
406
|
throw eingabefehler('Trinkgeld: recipients darf nicht leer sein.');
|
|
@@ -176,7 +176,7 @@ function jsonAuswerten(text, functionName, statusCode, inhaltstyp, geheimnisse)
|
|
|
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, huelle.data, geheimnisse);
|
|
179
|
+
throw fachfehler(functionName, huelle.message, huelle.data, geheimnisse, 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, geheimnisse)
|
|
|
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, huelle.data, geheimnisse);
|
|
225
|
+
throw fachfehler(functionName, huelle.message, huelle.data, geheimnisse, 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, daten, geheimnisse) {
|
|
238
|
+
function fachfehler(functionName, message, daten, geheimnisse, 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, fehlerDetails(daten, geheimnisse), 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';
|
|
@@ -18,10 +18,29 @@ 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;
|
|
24
37
|
fullReceiptId: string | null;
|
|
38
|
+
/**
|
|
39
|
+
* Zeitstempel des Originals (Server-Format, Wiener Wanduhr) — das Layout
|
|
40
|
+
* nennt ihn im Kopfblock des Storno-Bons („vom 11.08.2026, 09:02 Uhr").
|
|
41
|
+
* Fehlt bei Altbelegen; dann bleibt die Zeile weg.
|
|
42
|
+
*/
|
|
43
|
+
timeStamp?: string;
|
|
25
44
|
}
|
|
26
45
|
/** Eine stornierte Position: Index im Original und Menge. */
|
|
27
46
|
export interface CancellationItem {
|
|
@@ -40,6 +59,15 @@ export interface Cancellation {
|
|
|
40
59
|
by: string | null;
|
|
41
60
|
note: string | null;
|
|
42
61
|
items: CancellationItem[];
|
|
62
|
+
/**
|
|
63
|
+
* Rabattgutschein-Ausgleich, den DIESER Storno gewaehrt hat — Cent je
|
|
64
|
+
* Steuertopf des Backends (`amountRateStandard`, `amountRateReduced1`, …).
|
|
65
|
+
* Ein Rabattgutschein ist am Original schon in den Umsatz eingerechnet;
|
|
66
|
+
* jeder Storno nimmt ihn anteilig der stornierten Menge zurueck (bei 3 Stueck
|
|
67
|
+
* a 10 EUR mit 6 EUR Rabatt: 2,00 je Stueck). Fehlt das Feld, hat der Eintrag
|
|
68
|
+
* nichts gewaehrt (Altbestand vor dieser Regel) — der naechste Storno holt nach.
|
|
69
|
+
*/
|
|
70
|
+
promoAdjustmentCents?: Record<string, number>;
|
|
43
71
|
}
|
|
44
72
|
/** Ab wann eine liegengebliebene Reservierung nicht mehr zaehlt (wie im Backend). */
|
|
45
73
|
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';
|
|
@@ -56,6 +56,7 @@ export interface ReceiptSummary {
|
|
|
56
56
|
/** Nur am Storno-Beleg: das Original. */
|
|
57
57
|
cancellationOf?: {
|
|
58
58
|
receiptId: string | null;
|
|
59
|
+
timeStamp?: string;
|
|
59
60
|
};
|
|
60
61
|
cancellationReason?: string;
|
|
61
62
|
/** offen | teil | voll -- Storno-Stand des Originals. */
|
|
@@ -84,6 +85,7 @@ export interface ReceiptSummaryPayload {
|
|
|
84
85
|
} | null;
|
|
85
86
|
cancellationOf?: {
|
|
86
87
|
receiptId?: string | null;
|
|
88
|
+
timeStamp?: string | null;
|
|
87
89
|
} | null;
|
|
88
90
|
cancellationReason?: string | null;
|
|
89
91
|
stornoStand?: string | null;
|
|
@@ -41,7 +41,10 @@ export function fromReceiptSummaryPayload(payload) {
|
|
|
41
41
|
signatureOk: payload.signature_ok !== false,
|
|
42
42
|
items: Array.isArray(payload.items) ? payload.items.map((i) => ({ name: i?.name ?? '', quantity: typeof i?.quantity === 'number' ? i.quantity : 0 })) : [],
|
|
43
43
|
...(payload.operator ? { operator: { uid: payload.operator.uid ?? null, name: payload.operator.name ?? '' } } : {}),
|
|
44
|
-
...(payload.cancellationOf ? { cancellationOf: {
|
|
44
|
+
...(payload.cancellationOf ? { cancellationOf: {
|
|
45
|
+
receiptId: payload.cancellationOf.receiptId ?? null,
|
|
46
|
+
...(typeof payload.cancellationOf.timeStamp === 'string' && payload.cancellationOf.timeStamp ? { timeStamp: payload.cancellationOf.timeStamp } : {}),
|
|
47
|
+
} } : {}),
|
|
45
48
|
...(payload.cancellationReason ? { cancellationReason: payload.cancellationReason } : {}),
|
|
46
49
|
stornoStand: payload.stornoStand === 'teil' || payload.stornoStand === 'voll' ? payload.stornoStand : 'offen',
|
|
47
50
|
...(istZeroKind(payload.zeroKind) ? { zeroKind: payload.zeroKind } : {}),
|
|
@@ -70,7 +70,11 @@ export function fromReceiptPayload(payload) {
|
|
|
70
70
|
signatureSuccess: payload.signatureSuccess ?? undefined,
|
|
71
71
|
customProjectId: payload.customProjectId ?? undefined,
|
|
72
72
|
...(typeof payload.tipCents === 'number' ? { tipCents: payload.tipCents } : {}),
|
|
73
|
-
...(payload.cancellationOf ? { cancellationOf: {
|
|
73
|
+
...(payload.cancellationOf ? { cancellationOf: {
|
|
74
|
+
receiptId: payload.cancellationOf.receiptId,
|
|
75
|
+
fullReceiptId: payload.cancellationOf.fullReceiptId ?? null,
|
|
76
|
+
...(typeof payload.cancellationOf.timeStamp === 'string' && payload.cancellationOf.timeStamp ? { timeStamp: payload.cancellationOf.timeStamp } : {}),
|
|
77
|
+
} } : {}),
|
|
74
78
|
...(payload.cancellationReason ? { cancellationReason: payload.cancellationReason } : {}),
|
|
75
79
|
...(istZeroKind(payload.zeroKind) ? { zeroKind: payload.zeroKind } : {}),
|
|
76
80
|
...(payload.cancellations ? { cancellations: payload.cancellations.map(leseStorno) } : {}),
|
|
@@ -84,6 +88,9 @@ function leseStorno(eintrag) {
|
|
|
84
88
|
by: eintrag.by ?? null,
|
|
85
89
|
note: eintrag.note ?? null,
|
|
86
90
|
items: (eintrag.items ?? []).map((p) => ({ index: Number(p.index), quantity: Number(p.quantity) })),
|
|
91
|
+
...(eintrag.promoAdjustmentCents && typeof eintrag.promoAdjustmentCents === 'object'
|
|
92
|
+
? { promoAdjustmentCents: Object.fromEntries(Object.entries(eintrag.promoAdjustmentCents).map(([k, v]) => [k, Number(v)])) }
|
|
93
|
+
: {}),
|
|
87
94
|
};
|
|
88
95
|
}
|
|
89
96
|
/**
|
|
@@ -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,
|