@kreiseck/kasseneck-api 1.11.0 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/README.md +2 -1
- package/dist/cjs/client/aufrufe.d.ts +4 -4
- package/dist/cjs/client/aufrufe.js +15 -0
- package/dist/cjs/client/receipts.d.ts +5 -0
- package/dist/cjs/client/receipts.js +65 -6
- package/dist/cjs/enums/voucher-action.d.ts +2 -0
- package/dist/cjs/enums/voucher-action.js +2 -0
- package/dist/cjs/enums/voucher-type.d.ts +10 -0
- package/dist/cjs/enums/voucher-type.js +10 -0
- package/dist/cjs/index.d.ts +3 -3
- package/dist/cjs/index.js +9 -3
- package/dist/cjs/models/index.d.ts +3 -3
- package/dist/cjs/models/index.js +7 -2
- package/dist/cjs/models/receipt-errors.d.ts +1 -1
- package/dist/cjs/models/receipt-errors.js +2 -0
- package/dist/cjs/models/receipt-item.d.ts +11 -3
- package/dist/cjs/models/receipt-item.js +22 -1
- package/dist/cjs/models/receipt.d.ts +31 -1
- package/dist/cjs/models/receipt.js +45 -5
- package/dist/cjs/models/voucher.d.ts +57 -4
- package/dist/cjs/models/voucher.js +111 -3
- package/dist/cjs/partner/fehler.d.ts +1 -1
- package/dist/cjs/partner/fehler.js +2 -0
- package/dist/cjs/partner/index.d.ts +1 -1
- package/dist/cjs/partner/typen.d.ts +7 -1
- package/dist/cjs/partner/typen.js +3 -0
- package/dist/cjs/pos/client.d.ts +2 -1
- package/dist/cjs/pos/client.js +3 -1
- package/dist/cjs/pos/gutschein.d.ts +90 -0
- package/dist/cjs/pos/gutschein.js +164 -0
- package/dist/cjs/pos/index.d.ts +3 -2
- package/dist/cjs/pos/index.js +13 -2
- package/dist/cjs/pos/settings.d.ts +3 -0
- package/dist/cjs/pos/settings.js +12 -10
- package/dist/cjs/pos/texte.d.ts +156 -0
- package/dist/cjs/pos/texte.js +72 -1
- package/dist/cjs/react/index.js +8 -2
- package/dist/cjs/receipt/due.d.ts +83 -2
- package/dist/cjs/receipt/due.js +202 -36
- package/dist/cjs/receipt/grid.js +13 -1
- package/dist/cjs/receipt/gutschein-plan.d.ts +104 -0
- package/dist/cjs/receipt/gutschein-plan.js +360 -0
- package/dist/cjs/receipt/index.d.ts +1 -1
- package/dist/cjs/receipt/index.js +3 -0
- package/dist/cjs/receipt/layout.js +186 -10
- package/dist/cjs/register/pairing.d.ts +21 -1
- package/dist/cjs/register/pairing.js +17 -0
- package/dist/cjs/stored/bon-layout.d.ts +8 -0
- package/dist/cjs/stored/bon-layout.js +23 -0
- package/dist/cjs/stored/draht.d.ts +3 -1
- package/dist/cjs/stored/draht.js +53 -6
- package/dist/cjs/stored/einstellungen.js +2 -1
- package/dist/cjs/stored/gutscheine.d.ts +3 -0
- package/dist/cjs/stored/gutscheine.js +60 -0
- package/dist/cjs/stored/index.js +2 -17
- package/dist/cjs/stored/vokabular.d.ts +6 -4
- package/dist/cjs/stored/vokabular.js +48 -2
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/client/aufrufe.d.ts +4 -4
- package/dist/esm/client/aufrufe.js +15 -0
- package/dist/esm/client/receipts.d.ts +5 -0
- package/dist/esm/client/receipts.js +66 -8
- package/dist/esm/enums/voucher-action.d.ts +2 -0
- package/dist/esm/enums/voucher-action.js +2 -0
- package/dist/esm/enums/voucher-type.d.ts +10 -0
- package/dist/esm/enums/voucher-type.js +10 -0
- package/dist/esm/index.d.ts +3 -3
- package/dist/esm/index.js +3 -3
- package/dist/esm/models/index.d.ts +3 -3
- package/dist/esm/models/index.js +3 -3
- package/dist/esm/models/receipt-errors.d.ts +1 -1
- package/dist/esm/models/receipt-errors.js +2 -0
- package/dist/esm/models/receipt-item.d.ts +11 -3
- package/dist/esm/models/receipt-item.js +20 -1
- package/dist/esm/models/receipt.d.ts +31 -1
- package/dist/esm/models/receipt.js +44 -5
- package/dist/esm/models/voucher.d.ts +57 -4
- package/dist/esm/models/voucher.js +108 -3
- package/dist/esm/partner/fehler.d.ts +1 -1
- package/dist/esm/partner/fehler.js +2 -0
- package/dist/esm/partner/index.d.ts +1 -1
- package/dist/esm/partner/typen.d.ts +7 -1
- package/dist/esm/partner/typen.js +3 -0
- package/dist/esm/pos/client.d.ts +2 -1
- package/dist/esm/pos/client.js +3 -1
- package/dist/esm/pos/gutschein.d.ts +90 -0
- package/dist/esm/pos/gutschein.js +158 -0
- package/dist/esm/pos/index.d.ts +3 -2
- package/dist/esm/pos/index.js +4 -2
- package/dist/esm/pos/settings.d.ts +3 -0
- package/dist/esm/pos/settings.js +2 -0
- package/dist/esm/pos/texte.d.ts +156 -0
- package/dist/esm/pos/texte.js +70 -0
- package/dist/esm/react/index.js +8 -2
- package/dist/esm/receipt/due.d.ts +83 -2
- package/dist/esm/receipt/due.js +198 -34
- package/dist/esm/receipt/grid.js +13 -1
- package/dist/esm/receipt/gutschein-plan.d.ts +104 -0
- package/dist/esm/receipt/gutschein-plan.js +352 -0
- package/dist/esm/receipt/index.d.ts +1 -1
- package/dist/esm/receipt/index.js +1 -1
- package/dist/esm/receipt/layout.js +187 -11
- package/dist/esm/register/pairing.d.ts +21 -1
- package/dist/esm/register/pairing.js +16 -0
- package/dist/esm/stored/bon-layout.d.ts +8 -0
- package/dist/esm/stored/bon-layout.js +20 -0
- package/dist/esm/stored/draht.d.ts +3 -1
- package/dist/esm/stored/draht.js +52 -6
- package/dist/esm/stored/einstellungen.js +3 -2
- package/dist/esm/stored/gutscheine.d.ts +3 -0
- package/dist/esm/stored/gutscheine.js +56 -0
- package/dist/esm/stored/index.js +2 -17
- package/dist/esm/stored/vokabular.d.ts +6 -4
- package/dist/esm/stored/vokabular.js +48 -2
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/fixtures/expected/voucher-cancellation.grid32.txt +30 -0
- package/fixtures/expected/voucher-cancellation.grid48.txt +29 -0
- package/fixtures/expected/voucher-cancellation.lines.json +252 -0
- package/fixtures/expected/voucher-cancellation.sheet32.json +206 -0
- package/fixtures/expected/voucher-cancellation.sheet48.json +200 -0
- package/fixtures/expected/voucher-multi-purpose-redeem.grid32.txt +25 -0
- package/fixtures/expected/voucher-multi-purpose-redeem.grid48.txt +25 -0
- package/fixtures/expected/voucher-multi-purpose-redeem.lines.json +259 -0
- package/fixtures/expected/voucher-multi-purpose-redeem.sheet32.json +176 -0
- package/fixtures/expected/voucher-multi-purpose-redeem.sheet48.json +176 -0
- package/fixtures/expected/voucher-multi-purpose-sale.grid32.txt +24 -0
- package/fixtures/expected/voucher-multi-purpose-sale.grid48.txt +23 -0
- package/fixtures/expected/voucher-multi-purpose-sale.lines.json +216 -0
- package/fixtures/expected/voucher-multi-purpose-sale.sheet32.json +170 -0
- package/fixtures/expected/voucher-multi-purpose-sale.sheet48.json +164 -0
- package/fixtures/expected/voucher-payout.grid32.txt +23 -0
- package/fixtures/expected/voucher-payout.grid48.txt +22 -0
- package/fixtures/expected/voucher-payout.lines.json +216 -0
- package/fixtures/expected/voucher-payout.sheet32.json +164 -0
- package/fixtures/expected/voucher-payout.sheet48.json +158 -0
- package/fixtures/expected/voucher-service-redeem.grid32.txt +26 -0
- package/fixtures/expected/voucher-service-redeem.grid48.txt +25 -0
- package/fixtures/expected/voucher-service-redeem.lines.json +261 -0
- package/fixtures/expected/voucher-service-redeem.sheet32.json +182 -0
- package/fixtures/expected/voucher-service-redeem.sheet48.json +176 -0
- package/fixtures/expected/voucher-single-purpose-redeem-exempt.grid32.txt +30 -0
- package/fixtures/expected/voucher-single-purpose-redeem-exempt.grid48.txt +29 -0
- package/fixtures/expected/voucher-single-purpose-redeem-exempt.lines.json +331 -0
- package/fixtures/expected/voucher-single-purpose-redeem-exempt.sheet32.json +206 -0
- package/fixtures/expected/voucher-single-purpose-redeem-exempt.sheet48.json +200 -0
- package/fixtures/expected/voucher-single-purpose-redeem.grid32.txt +25 -0
- package/fixtures/expected/voucher-single-purpose-redeem.grid48.txt +24 -0
- package/fixtures/expected/voucher-single-purpose-redeem.lines.json +246 -0
- package/fixtures/expected/voucher-single-purpose-redeem.sheet32.json +176 -0
- package/fixtures/expected/voucher-single-purpose-redeem.sheet48.json +170 -0
- package/fixtures/expected/voucher-single-purpose-sale.grid32.txt +26 -0
- package/fixtures/expected/voucher-single-purpose-sale.grid48.txt +24 -0
- package/fixtures/expected/voucher-single-purpose-sale.lines.json +225 -0
- package/fixtures/expected/voucher-single-purpose-sale.sheet32.json +182 -0
- package/fixtures/expected/voucher-single-purpose-sale.sheet48.json +170 -0
- package/fixtures/hobex-hps-codes.json +1 -1
- package/fixtures/invoice-api.schema.json +1 -1
- package/fixtures/invoice-texts.json +1 -1
- package/fixtures/manifest.json +64 -0
- package/fixtures/pos-texts.json +170 -1
- package/fixtures/receipt-due-generated.json +1 -1
- package/fixtures/receipt-due-voucher-generated.json +494 -0
- package/fixtures/receipts/voucher-cancellation.json +76 -0
- package/fixtures/receipts/voucher-multi-purpose-redeem.json +62 -0
- package/fixtures/receipts/voucher-multi-purpose-sale.json +63 -0
- package/fixtures/receipts/voucher-payout.json +65 -0
- package/fixtures/receipts/voucher-service-redeem.json +82 -0
- package/fixtures/receipts/voucher-single-purpose-redeem-exempt.json +86 -0
- package/fixtures/receipts/voucher-single-purpose-redeem.json +71 -0
- package/fixtures/receipts/voucher-single-purpose-sale.json +88 -0
- package/fixtures/surface.json +35 -5
- package/fixtures/v3/antworten/belege.json +3405 -3
- package/fixtures/v3/antworten/belegmail.json +5 -2
- package/fixtures/v3/antworten/kasse-belege.json +3423 -3
- package/fixtures/v3/antworten/kasse.json +621 -14
- package/fixtures/v3/antworten/lager.json +1 -1
- package/fixtures/v3/antworten/rechnungen.json +5 -2
- package/fixtures/v3/antworten/storno.json +214 -3
- package/fixtures/v3/stored/belege.json +5 -2
- package/fixtures/v3/stored/kasse.json +225 -4
- package/fixtures/v3/stored/rechnungen.json +5 -2
- package/fixtures/v3/v3-vokabular.json +1375 -23
- package/fixtures/v3/zahlbetrag-faelle.json +293 -3
- package/package.json +4 -1
|
@@ -16,11 +16,41 @@ export interface Voucher {
|
|
|
16
16
|
action: VoucherAction | string;
|
|
17
17
|
type: VoucherType | string;
|
|
18
18
|
valueCents?: number;
|
|
19
|
+
/** Neue Form: Betrag in Cent (Verkauf, Einloesung, Auszahlung). */
|
|
20
|
+
amountCents?: number;
|
|
21
|
+
/** Neue Form, Leistungsgutschein: verkaufter Artikel. */
|
|
22
|
+
articleId?: string;
|
|
23
|
+
/** Neue Form, Leistungsgutschein: Menge (Verkauf) bzw. eingeloeste Menge. */
|
|
24
|
+
quantity?: number;
|
|
25
|
+
/** Neue Form, Leistungsgutschein einloesen: Indizes der Belegpositionen. */
|
|
26
|
+
positions?: number[];
|
|
27
|
+
/** Neue Form, Auszahlung: Grund, 1 bis 200 Zeichen. */
|
|
28
|
+
reason?: string;
|
|
29
|
+
voucherType?: string;
|
|
30
|
+
voucherId?: string;
|
|
31
|
+
/** Letzte vier Zeichen des Codes; der Klartext steht nie am Beleg. */
|
|
32
|
+
codeSuffix?: string;
|
|
33
|
+
vatRateBp?: number;
|
|
34
|
+
article?: VoucherArticle;
|
|
35
|
+
balanceAfterCents?: number;
|
|
36
|
+
remainingQuantityAfter?: number;
|
|
37
|
+
taxedWith?: string;
|
|
38
|
+
exempt?: boolean;
|
|
39
|
+
zeroRateBucket?: boolean;
|
|
40
|
+
}
|
|
41
|
+
/** Artikel eines Leistungsgutscheins, wie der Server ihn am Beleg festhaelt. */
|
|
42
|
+
export interface VoucherArticle {
|
|
43
|
+
id: string;
|
|
44
|
+
name: string;
|
|
45
|
+
vatRateBp: number;
|
|
46
|
+
priceCents: number;
|
|
19
47
|
}
|
|
20
48
|
/**
|
|
21
|
-
* Nutzlast eines Gutscheins.
|
|
22
|
-
*
|
|
23
|
-
*
|
|
49
|
+
* Nutzlast eines Gutscheins. Altform: nur `valueCents` (ganze Cent); `value`
|
|
50
|
+
* (Euro) kommt nur beim Lesen vor: der Server legt es in seinen Antworten
|
|
51
|
+
* daneben, gelesen wird es nur, wenn `valueCents` fehlt. Neue Form: roh mit
|
|
52
|
+
* `amountCents` und den weiteren Feldern; gesetzte Feldern gehen hinaus,
|
|
53
|
+
* fehlende fehlen (kein `valueCents: null` an der neuen Form).
|
|
24
54
|
* `name` und `code` gehen nur hinaus, wenn sie gesetzt sind.
|
|
25
55
|
*/
|
|
26
56
|
export interface VoucherPayload {
|
|
@@ -29,8 +59,31 @@ export interface VoucherPayload {
|
|
|
29
59
|
action: string;
|
|
30
60
|
type: string;
|
|
31
61
|
value?: number | null;
|
|
32
|
-
valueCents
|
|
62
|
+
valueCents?: number | null;
|
|
63
|
+
amountCents?: number;
|
|
64
|
+
articleId?: string;
|
|
65
|
+
quantity?: number;
|
|
66
|
+
positions?: number[];
|
|
67
|
+
reason?: string;
|
|
68
|
+
voucherType?: string;
|
|
69
|
+
voucherId?: string;
|
|
70
|
+
codeSuffix?: string;
|
|
71
|
+
vatRateBp?: number;
|
|
72
|
+
article?: VoucherArticle;
|
|
73
|
+
balanceAfterCents?: number;
|
|
74
|
+
remainingQuantityAfter?: number;
|
|
75
|
+
taxedWith?: string;
|
|
76
|
+
exempt?: boolean;
|
|
77
|
+
zeroRateBucket?: boolean;
|
|
33
78
|
}
|
|
79
|
+
/** Arten der neuen Gutscheinform (die Altwerte sind `value` und `promo`). */
|
|
80
|
+
export declare const NEUE_GUTSCHEINARTEN: readonly string[];
|
|
81
|
+
/** Gehoert der Gutschein zur neuen Form (Art oder Aktion `payout`)? */
|
|
82
|
+
export declare function isNewFormVoucher(voucher: Pick<Voucher, 'type' | 'action'>): boolean;
|
|
83
|
+
/** Felder, die nur der Server setzt; an einem Wunsch sind sie `voucher_invalid`. */
|
|
84
|
+
export declare const GUTSCHEIN_SERVERFELDER: readonly ["voucherId", "codeSuffix", "zeroRateBucket", "voucherType"];
|
|
85
|
+
/** Welche Serverfelder tragt der Gutschein (leere Liste = keines)? */
|
|
86
|
+
export declare function voucherServerFields(voucher: Voucher): string[];
|
|
34
87
|
export declare function toVoucherPayload(voucher: Voucher): VoucherPayload;
|
|
35
88
|
/**
|
|
36
89
|
* Ist der Gutschein an das Backend sendbar? Zwilling von `KeckVoucher.isValid`
|
|
@@ -1,13 +1,46 @@
|
|
|
1
1
|
import { VoucherAction, VoucherType } from '../enums/index.js';
|
|
2
2
|
import { euroToCents } from '../money.js';
|
|
3
3
|
import { readEnumKey, requireEnumKey } from './enum-payload.js';
|
|
4
|
+
/** Arten der neuen Gutscheinform (die Altwerte sind `value` und `promo`). */
|
|
5
|
+
export const NEUE_GUTSCHEINARTEN = [
|
|
6
|
+
VoucherType.multi_purpose,
|
|
7
|
+
VoucherType.single_purpose,
|
|
8
|
+
VoucherType.service,
|
|
9
|
+
VoucherType.promotion,
|
|
10
|
+
];
|
|
11
|
+
/** Gehoert der Gutschein zur neuen Form (Art oder Aktion `payout`)? */
|
|
12
|
+
export function isNewFormVoucher(voucher) {
|
|
13
|
+
return NEUE_GUTSCHEINARTEN.includes(voucher.type) || voucher.action === VoucherAction.payout;
|
|
14
|
+
}
|
|
15
|
+
/** Felder, die nur der Server setzt; an einem Wunsch sind sie `voucher_invalid`. */
|
|
16
|
+
export const GUTSCHEIN_SERVERFELDER = ['voucherId', 'codeSuffix', 'zeroRateBucket', 'voucherType'];
|
|
17
|
+
/** Welche Serverfelder tragt der Gutschein (leere Liste = keines)? */
|
|
18
|
+
export function voucherServerFields(voucher) {
|
|
19
|
+
return GUTSCHEIN_SERVERFELDER.filter((feld) => voucher[feld] != null);
|
|
20
|
+
}
|
|
21
|
+
const NEUE_FELDER = [
|
|
22
|
+
'amountCents', 'articleId', 'quantity', 'positions', 'reason', 'voucherType', 'voucherId', 'codeSuffix',
|
|
23
|
+
'vatRateBp', 'article', 'balanceAfterCents', 'remainingQuantityAfter', 'taxedWith', 'exempt', 'zeroRateBucket',
|
|
24
|
+
];
|
|
4
25
|
export function toVoucherPayload(voucher) {
|
|
26
|
+
const action = requireEnumKey(VoucherAction, voucher.action, 'Gutschein-Aktion');
|
|
27
|
+
const type = requireEnumKey(VoucherType, voucher.type, 'Gutscheinart');
|
|
28
|
+
// Felder der neuen Form gehen roh hinaus, soweit gesetzt (auch an einem
|
|
29
|
+
// gelesenen Altbeleg: dort fehlen sie, die Nutzlast bleibt byte-gleich).
|
|
30
|
+
const roh = voucher;
|
|
31
|
+
const neu = {};
|
|
32
|
+
for (const feld of NEUE_FELDER)
|
|
33
|
+
if (roh[feld] != null)
|
|
34
|
+
neu[feld] = roh[feld];
|
|
5
35
|
return {
|
|
6
|
-
action
|
|
7
|
-
type
|
|
8
|
-
|
|
36
|
+
action,
|
|
37
|
+
type,
|
|
38
|
+
// Die neue Form kennt kein `valueCents`; nur ein gelesener Eintrag (die
|
|
39
|
+
// Auszahlung steht beim Server als Wert mit Cent) traegt es weiter.
|
|
40
|
+
...(isNewFormVoucher(voucher) && voucher.valueCents == null ? {} : { valueCents: voucher.valueCents ?? null }),
|
|
9
41
|
...(voucher.code != null ? { code: voucher.code } : {}),
|
|
10
42
|
...(voucher.name != null ? { name: voucher.name } : {}),
|
|
43
|
+
...neu,
|
|
11
44
|
};
|
|
12
45
|
}
|
|
13
46
|
/**
|
|
@@ -17,6 +50,8 @@ export function toVoucherPayload(voucher) {
|
|
|
17
50
|
* gesetzter Wert muss positiv sein.
|
|
18
51
|
*/
|
|
19
52
|
export function voucherIsValid(voucher) {
|
|
53
|
+
if (isNewFormVoucher(voucher))
|
|
54
|
+
return newFormVoucherIsValid(voucher);
|
|
20
55
|
if (voucher.type === VoucherType.value && voucher.valueCents == null) {
|
|
21
56
|
return false;
|
|
22
57
|
}
|
|
@@ -58,5 +93,75 @@ export function fromVoucherPayload(payload) {
|
|
|
58
93
|
action: readEnumKey(VoucherAction, payload.action),
|
|
59
94
|
type: readEnumKey(VoucherType, payload.type),
|
|
60
95
|
valueCents,
|
|
96
|
+
...lesenNeu(payload),
|
|
61
97
|
};
|
|
62
98
|
}
|
|
99
|
+
/** Felder der neuen Form beim Lesen: nur was dasteht (Altbelege bleiben schlank). */
|
|
100
|
+
function lesenNeu(payload) {
|
|
101
|
+
const roh = payload;
|
|
102
|
+
const neu = {};
|
|
103
|
+
for (const feld of NEUE_FELDER)
|
|
104
|
+
if (roh[feld] != null)
|
|
105
|
+
neu[feld] = roh[feld];
|
|
106
|
+
return neu;
|
|
107
|
+
}
|
|
108
|
+
/** Grenzen wie der Server (gutschein-beleg): Betrag 1 bis 1.000.000 Cent, Menge 1 bis 100. */
|
|
109
|
+
const MAX_BETRAG_CENTS = 1_000_000;
|
|
110
|
+
const MAX_STUECK = 100;
|
|
111
|
+
const MAX_GRUND = 200;
|
|
112
|
+
const istCent = (n) => typeof n === 'number' && Number.isInteger(n) && n >= 1 && n <= MAX_BETRAG_CENTS;
|
|
113
|
+
const istMenge = (n) => typeof n === 'number' && Number.isInteger(n) && n >= 1 && n <= MAX_STUECK;
|
|
114
|
+
const hatText = (t) => typeof t === 'string' && t.trim().length > 0;
|
|
115
|
+
/**
|
|
116
|
+
* Wunsch der neuen Form sendbar? Bildet die Regeln des Servers ab: Serverfelder
|
|
117
|
+
* nie, Betrag/Menge ganz und positiv, Code bei Einloesung und Auszahlung,
|
|
118
|
+
* Auszahlung nur Mehrzweck/Einzweck mit Grund (1 bis 200 Zeichen), Aktion nur
|
|
119
|
+
* einloesbar, Leistung braucht Artikel und Menge (Verkauf) bzw. Positionen
|
|
120
|
+
* (Einloesung). Felder, die zu einer anderen Aktion/Art gehoeren, sind
|
|
121
|
+
* ebenfalls nicht erlaubt (der Server wiese sie ab).
|
|
122
|
+
*/
|
|
123
|
+
function newFormVoucherIsValid(v) {
|
|
124
|
+
if (voucherServerFields(v).length > 0)
|
|
125
|
+
return false;
|
|
126
|
+
const r = v;
|
|
127
|
+
for (const feld of ['vatRateBp', 'article', 'balanceAfterCents', 'remainingQuantityAfter', 'taxedWith', 'exempt']) {
|
|
128
|
+
if (r[feld] != null)
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
if (v.valueCents != null)
|
|
132
|
+
return false;
|
|
133
|
+
const allein = v.type === VoucherType.multi_purpose || v.type === VoucherType.single_purpose;
|
|
134
|
+
if (v.action === VoucherAction.payout) {
|
|
135
|
+
return allein && hatText(v.code) && istCent(v.amountCents) && hatText(v.reason) && v.reason.trim().length <= MAX_GRUND
|
|
136
|
+
&& v.articleId == null && v.quantity == null && v.positions == null;
|
|
137
|
+
}
|
|
138
|
+
if (v.reason != null)
|
|
139
|
+
return false;
|
|
140
|
+
if (allein) {
|
|
141
|
+
if (v.articleId != null || v.quantity != null || v.positions != null)
|
|
142
|
+
return false;
|
|
143
|
+
if (v.action === VoucherAction.sell)
|
|
144
|
+
return istCent(v.amountCents);
|
|
145
|
+
if (v.action === VoucherAction.redeem)
|
|
146
|
+
return hatText(v.code) && istCent(v.amountCents);
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
if (v.type === VoucherType.service) {
|
|
150
|
+
if (v.amountCents != null)
|
|
151
|
+
return false;
|
|
152
|
+
if (v.action === VoucherAction.sell) {
|
|
153
|
+
return hatText(v.articleId) && istMenge(v.quantity) && v.positions == null;
|
|
154
|
+
}
|
|
155
|
+
if (v.action === VoucherAction.redeem) {
|
|
156
|
+
return hatText(v.code) && Array.isArray(v.positions) && v.positions.length > 0
|
|
157
|
+
&& v.positions.every((i) => Number.isInteger(i) && i >= 0) && new Set(v.positions).size === v.positions.length && v.articleId == null && v.quantity == null;
|
|
158
|
+
}
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
if (v.type === VoucherType.promotion) {
|
|
162
|
+
return v.action === VoucherAction.redeem && hatText(v.code)
|
|
163
|
+
&& v.amountCents == null && v.articleId == null && v.quantity == null && v.positions == null;
|
|
164
|
+
}
|
|
165
|
+
// Altart mit neuer Aktion (payout + value/promo): nicht sendbar.
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
@@ -61,7 +61,7 @@ import { KasseneckApiError } from '../client/errors.js';
|
|
|
61
61
|
*
|
|
62
62
|
* Reihenfolge und Bestand wie im Abzug des Backends.
|
|
63
63
|
*/
|
|
64
|
-
export declare const PARTNER_ERROR_CODES: readonly ["validation", "rate_limited", "app_not_found", "app_not_accepted", "live_not_allowed", "customer_exists", "customer_conflict", "customer_limit", "access_not_allowed", "email_taken", "no_email", "tax_number_missing", "fon_missing", "signature_pending", "signature_missing", "signature_unknown", "signature_ambiguous", "signature_not_ready", "signature_limit", "signature_failed", "module_inactive", "cashregister_limit", "cashregister_not_found", "contracts_pending", "kind_not_allowed", "mode_not_allowed", "power_of_attorney_missing", "not_found", "no_version", "unknown_version", "text_changed", "already_accepted", "activation_failed", "webhook_limit", "webhook_inactive", "event_not_subscribed"];
|
|
64
|
+
export declare const PARTNER_ERROR_CODES: readonly ["validation", "rate_limited", "app_not_found", "app_not_accepted", "live_not_allowed", "customer_exists", "customer_conflict", "customer_limit", "access_not_allowed", "email_taken", "no_email", "tax_number_missing", "fon_missing", "signature_pending", "signature_missing", "signature_unknown", "signature_ambiguous", "signature_not_ready", "signature_limit", "signature_failed", "module_inactive", "cashregister_limit", "cashregister_not_found", "contracts_pending", "kind_not_allowed", "mode_not_allowed", "power_of_attorney_missing", "not_found", "no_version", "unknown_version", "text_changed", "already_accepted", "activation_failed", "activation_in_progress", "webhook_limit", "webhook_inactive", "event_not_subscribed"];
|
|
65
65
|
/**
|
|
66
66
|
* Die Codes, die nur im **Partner-Portal** entstehen — beim Pflegen der App,
|
|
67
67
|
* der Schluessel, der Mitglieder und der Signaturkarten.
|
|
@@ -101,6 +101,7 @@ export const PARTNER_ERROR_CODES = [
|
|
|
101
101
|
'text_changed',
|
|
102
102
|
'already_accepted',
|
|
103
103
|
'activation_failed',
|
|
104
|
+
'activation_in_progress',
|
|
104
105
|
// Webhooks
|
|
105
106
|
'webhook_limit',
|
|
106
107
|
'webhook_inactive',
|
|
@@ -208,6 +209,7 @@ const RAT = {
|
|
|
208
209
|
text_changed: 'Der gezeigte Vertragstext hat sich seit dem Laden geaendert (data.textHash traegt die aktuell geltende Pruefsumme). Neu laden und danach erneut bestaetigen lassen.',
|
|
209
210
|
already_accepted: 'Diese Fassung ist bereits bestaetigt (data.contractId). Nichts weiter zu tun.',
|
|
210
211
|
activation_failed: 'Die Inbetriebnahme blieb an data.step haengen (ggf. data.rc). activateCashregister erneut aufrufen — jeder Schritt ist idempotent, der Lauf setzt an der Bruchstelle an.',
|
|
212
|
+
activation_in_progress: 'Die Inbetriebnahme dieser Kasse laeuft bereits. Kurz warten und den Stand abfragen, nicht erneut anstossen.',
|
|
211
213
|
webhook_limit: 'Hoechstens 10 Webhook-Endpunkte je Partner. Einen ungenutzten loeschen.',
|
|
212
214
|
webhook_inactive: 'Der Webhook steht auf active:false. Zuerst aktivieren, dann erneut proben.',
|
|
213
215
|
event_not_subscribed: 'Der Endpunkt abonniert dieses Ereignis nicht — auch eine Probe bekommt nur, was in seiner events-Liste steht. events erweitern und erneut versuchen.',
|
|
@@ -28,6 +28,6 @@ export { getPartnerInfo, createPartnerCustomer, listPartnerCustomers, checkPartn
|
|
|
28
28
|
export { createPartnerWebhook, listPartnerWebhooks, rotatePartnerWebhookSecret, updatePartnerWebhook, deletePartnerWebhook, sendPartnerWebhookTest, listPartnerWebhookDeliveries, parseWebhookEvent, isPartnerWebhookEvent, PARTNER_WEBHOOK_EVENTS, WEBHOOK_ENVELOPE_FIELDS, WEBHOOK_DELIVERY_STATUSES, type PartnerWebhookEvent, type PartnerWebhookEventType, type PartnerWebhook, type CreateWebhookOptions, type CreateWebhookResult, type DeleteWebhookResult, type WebhookApiVersion, type WebhookDeliveryStatus, type WebhookTestDelivery, type ContractAcceptedEventData, type SignatureFailedEventData, type CashregisterFailedEventData, type WebhookPatch, type WebhookList, type WebhookDelivery, type WebhookTestResult, type WebhookEventResult, } from './webhooks.js';
|
|
29
29
|
export { verifyWebhookSignature, parseSignatureHeader, WEBHOOK_SIGNATURE_HEADER, WEBHOOK_EVENT_HEADER, WEBHOOK_DELIVERY_HEADER, WEBHOOK_TOLERANCE_SEC, WEBHOOK_RETRY_PLAN_SEC, WEBHOOK_MAX_ATTEMPTS, WEBHOOK_TIMEOUT_MS, WEBHOOK_LIMIT, type VerifyWebhookOptions, type WebhookVerifyResult, type WebhookVerifyReason, } from './webhook-signatur.js';
|
|
30
30
|
export { PARTNER_ENVS, LEGAL_FORMS, AUSTRIAN_STATES, CONTACT_ROLES, AVV_MODES, FEE_INTERVALS, CONTRACT_KINDS, CONTRACT_SOURCES, SIGNATURE_HISTORY_REASONS, SIGNATURE_ERROR_CODES, } from './typen.js';
|
|
31
|
-
export type { PartnerEnv, PartnerScope, PartnerApp, PartnerInfo, LegalForm, AustrianState, ContactRole, AvvMode, FeeInterval, PartnerFee, SignatureHistoryReason, SignatureErrorCode, RequestSignatureOptions, CustomerSignature, ContractKind, ContractSource, ReportCustomerContractOptions, ReportCustomerContractResult, BusinessAddress, BusinessTaxDetails, BusinessContact, BusinessTaxAdvisor, Business, CreateCustomerOptions, CreateCustomerResult, PartnerCustomerStatus, PartnerCustomerSummary, AvvStatus, ContractStatus, PartnerCustomerFonStatus, ListCustomersOptions, PartnerCustomerList, PartnerCustomer, FonLinkResult, SignatureRequestStatus, SignatureHistoryEntry, SignatureRequest, RequestSignatureResult, CustomerSignatureStatus, CashregisterActivationStep, CustomerCashregisterStatus, CustomerCashregister, CreateCashregisterOptions, CreateCashregisterResult, ActivateCashregisterResult, CustomerCashregisterList, CustomerCashregisterCredential, CustomerCredentials, } from './typen.js';
|
|
31
|
+
export type { PartnerEnv, PartnerScope, PartnerApp, PartnerInfo, LegalForm, AustrianState, ContactRole, AvvMode, FeeInterval, PartnerFee, SignatureHistoryReason, SignatureFulfillment, SignatureErrorCode, RequestSignatureOptions, CustomerSignature, ContractKind, ContractSource, ReportCustomerContractOptions, ReportCustomerContractResult, BusinessAddress, BusinessTaxDetails, BusinessContact, BusinessTaxAdvisor, Business, CreateCustomerOptions, CreateCustomerResult, PartnerCustomerStatus, PartnerCustomerSummary, AvvStatus, ContractStatus, PartnerCustomerFonStatus, ListCustomersOptions, PartnerCustomerList, PartnerCustomer, FonLinkResult, SignatureRequestStatus, SignatureHistoryEntry, SignatureRequest, RequestSignatureResult, CustomerSignatureStatus, CashregisterActivationStep, CustomerCashregisterStatus, CustomerCashregister, CreateCashregisterOptions, CreateCashregisterResult, ActivateCashregisterResult, CustomerCashregisterList, CustomerCashregisterCredential, CustomerCredentials, } from './typen.js';
|
|
32
32
|
/** `credentials:read` — nicht im Standardsatz, siehe typen.ts. */
|
|
33
33
|
export { SCOPE_CREDENTIALS } from './typen.js';
|
|
@@ -382,7 +382,7 @@ export type SignatureRequestStatus = 'requested' | 'assigned' | 'registered' | '
|
|
|
382
382
|
* Gruende in der Historie eines Signaturantrags. `card_entered` und
|
|
383
383
|
* `finanzonline` hiessen unter `/v1` `karte_eingetragen` und `fon`.
|
|
384
384
|
*/
|
|
385
|
-
export declare const SIGNATURE_HISTORY_REASONS: readonly ["api", "portal", "card_entered", "finanzonline", "automation_off", "test_environment", "no_stock"];
|
|
385
|
+
export declare const SIGNATURE_HISTORY_REASONS: readonly ["api", "portal", "card_entered", "finanzonline", "automation_off", "test_environment", "no_stock", "card_pool_activated", "activation_run", "finanzonline_completed"];
|
|
386
386
|
/** Offen fuer spaeter ergaenzte Gruende, siehe [SIGNATURE_HISTORY_REASONS]. */
|
|
387
387
|
export type SignatureHistoryReason = typeof SIGNATURE_HISTORY_REASONS[number] | (string & {});
|
|
388
388
|
/**
|
|
@@ -400,6 +400,8 @@ export interface SignatureHistoryEntry {
|
|
|
400
400
|
at: number;
|
|
401
401
|
reason: SignatureHistoryReason | null;
|
|
402
402
|
}
|
|
403
|
+
/** Erfuellungsweg eines Signaturantrags; offen fuer spaeter ergaenzte Werte. */
|
|
404
|
+
export type SignatureFulfillment = 'automatic' | 'manual' | (string & {});
|
|
403
405
|
export interface SignatureRequest {
|
|
404
406
|
requestId: string;
|
|
405
407
|
status: SignatureRequestStatus;
|
|
@@ -408,6 +410,8 @@ export interface SignatureRequest {
|
|
|
408
410
|
kind: string;
|
|
409
411
|
vdaId: string | null;
|
|
410
412
|
signatureId: string | null;
|
|
413
|
+
/** Wie der Antrag erfuellt wird: aus dem Kartenvorrat oder von Hand. */
|
|
414
|
+
fulfillment?: SignatureFulfillment;
|
|
411
415
|
error: {
|
|
412
416
|
code: SignatureErrorCode | null;
|
|
413
417
|
message: string | null;
|
|
@@ -442,6 +446,8 @@ export interface CustomerSignature {
|
|
|
442
446
|
status: SignatureRequestStatus | 'decommissioned';
|
|
443
447
|
statusText: string;
|
|
444
448
|
inProgress: boolean;
|
|
449
|
+
/** Wie der Antrag erfuellt wird: aus dem Kartenvorrat oder von Hand. */
|
|
450
|
+
fulfillment?: SignatureFulfillment;
|
|
445
451
|
ready: boolean;
|
|
446
452
|
kind: string;
|
|
447
453
|
vdaId: string | null;
|
|
@@ -97,6 +97,9 @@ export const SIGNATURE_HISTORY_REASONS = [
|
|
|
97
97
|
'automation_off',
|
|
98
98
|
'test_environment',
|
|
99
99
|
'no_stock',
|
|
100
|
+
'card_pool_activated',
|
|
101
|
+
'activation_run',
|
|
102
|
+
'finanzonline_completed',
|
|
100
103
|
];
|
|
101
104
|
/**
|
|
102
105
|
* `error.code` eines Signaturantrags oder einer Signatur (`SignatureRequest`,
|
package/dist/esm/pos/client.d.ts
CHANGED
|
@@ -4,10 +4,11 @@ import { type PosSettings, type PosBusinessSettings, type PosDeviceSettings } fr
|
|
|
4
4
|
export declare function getPosSettings(transport: InternerTransport, options?: {
|
|
5
5
|
deviceId?: string;
|
|
6
6
|
}): Promise<PosSettings>;
|
|
7
|
-
/** `{business, device}` einer Antwort, gemischt mit den Standardwerten. */
|
|
7
|
+
/** `{business, device, vouchers}` einer Antwort, gemischt mit den Standardwerten. */
|
|
8
8
|
export declare function posSettingsFromWire(data: {
|
|
9
9
|
business?: unknown;
|
|
10
10
|
device?: unknown;
|
|
11
|
+
vouchers?: unknown;
|
|
11
12
|
} | null | undefined): PosSettings;
|
|
12
13
|
/** Betriebsweite Einstellungen schreiben (Recht `layout`); liefert den gemischten Stand. */
|
|
13
14
|
export declare function setMyPosSettings(transport: InternerTransport, business: Partial<PosBusinessSettings>): Promise<PosBusinessSettings>;
|
package/dist/esm/pos/client.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { KasseneckValidationError } from '../client/errors.js';
|
|
2
|
+
import { posVoucherSettingsFromWire } from './gutschein.js';
|
|
2
3
|
import { POS_BUSINESS_DEFAULTS, POS_DEVICE_DEFAULTS, POS_BUSINESS_VALUES, POS_DEVICE_VALUES, POS_SHORTCUT_ACTIONS, POS_DEVICE_OPTIONAL_FIELDS, POS_BUSINESS_OPTIONAL_FIELDS, mergePosSettings, posShortcutConflict, _istAltwert0x, _optionalerWert, } from './settings.js';
|
|
3
4
|
/*
|
|
4
5
|
* Die Einstellungs-Aufrufe am Draht `/api/v3`: Parameter und Antwort heissen
|
|
@@ -27,9 +28,10 @@ export async function getPosSettings(transport, options = {}) {
|
|
|
27
28
|
const daten = await transport('getKasseSettings', params);
|
|
28
29
|
return posSettingsFromWire(daten);
|
|
29
30
|
}
|
|
30
|
-
/** `{business, device}` einer Antwort, gemischt mit den Standardwerten. */
|
|
31
|
+
/** `{business, device, vouchers}` einer Antwort, gemischt mit den Standardwerten. */
|
|
31
32
|
export function posSettingsFromWire(data) {
|
|
32
33
|
return {
|
|
34
|
+
vouchers: posVoucherSettingsFromWire(data?.vouchers),
|
|
33
35
|
business: mergePosSettings(POS_BUSINESS_DEFAULTS, objekt(data?.business)),
|
|
34
36
|
device: mergePosSettings(POS_DEVICE_DEFAULTS, objekt(data?.device)),
|
|
35
37
|
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { InternerTransport } from '../client/aufrufe.js';
|
|
2
|
+
/**
|
|
3
|
+
* Gutschein pruefen und Etikett nachdrucken an der Kasse (Backend
|
|
4
|
+
* gutschein-endpoints). Beide Aufrufe gibt es nur ueber den Kassenweg
|
|
5
|
+
* `/api/v3`.
|
|
6
|
+
*
|
|
7
|
+
* Der Klartext-Code lebt nur im Speicher des Aufrufs: er wird weder
|
|
8
|
+
* protokolliert noch in einer Fehlermeldung genannt. Die Kasse bekommt nach
|
|
9
|
+
* dem Pruefen nur die letzten vier Zeichen (`codeSuffix`) zurueck.
|
|
10
|
+
*/
|
|
11
|
+
export type VoucherKind = 'multi_purpose' | 'single_purpose' | 'service' | 'promotion';
|
|
12
|
+
export type VoucherStatus = 'active' | 'used_up' | 'expired' | 'paid_out' | 'cancelled' | 'blocked';
|
|
13
|
+
export declare const VOUCHER_KINDS: readonly VoucherKind[];
|
|
14
|
+
export declare const VOUCHER_STATUSES: readonly VoucherStatus[];
|
|
15
|
+
/** Gueltigkeit eines Gutscheins; Daten als `YYYY-MM-DD` (Wiener Tag). Was der Server nicht nennt, fehlt. */
|
|
16
|
+
export interface VoucherValidity {
|
|
17
|
+
pattern: 'unlimited' | '1+3' | 'custom';
|
|
18
|
+
redeemableUntil?: string;
|
|
19
|
+
refundableUntil?: string;
|
|
20
|
+
validUntil?: string;
|
|
21
|
+
}
|
|
22
|
+
/** Der Artikel eines Dienstleistungsgutscheins. */
|
|
23
|
+
export interface VoucherArticle {
|
|
24
|
+
id: string;
|
|
25
|
+
name: string;
|
|
26
|
+
vatRateBp: number;
|
|
27
|
+
priceCents: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Stand eines Gutscheins nach dem Code-Eingeben. Felder, die zur Art nicht
|
|
31
|
+
* passen oder die der Server nicht sendet, fehlen im Modell (nie `null`).
|
|
32
|
+
*/
|
|
33
|
+
export interface VoucherCheck {
|
|
34
|
+
type: VoucherKind;
|
|
35
|
+
status: VoucherStatus;
|
|
36
|
+
/** Die letzten vier Zeichen des Codes, fuer Anzeige und Beleg. */
|
|
37
|
+
codeSuffix: string;
|
|
38
|
+
balanceCents?: number;
|
|
39
|
+
faceValueCents?: number;
|
|
40
|
+
quantity?: number;
|
|
41
|
+
remainingQuantity?: number;
|
|
42
|
+
vatRateBp?: number;
|
|
43
|
+
article?: VoucherArticle;
|
|
44
|
+
discountCents?: number;
|
|
45
|
+
discountPercentBp?: number;
|
|
46
|
+
validity?: VoucherValidity;
|
|
47
|
+
/**
|
|
48
|
+
* Befreit verkauft (Kleinunternehmer beim Verkauf, Satz 0): eine Einloesung
|
|
49
|
+
* verteilt sich anteilig auf alle Saetze des Korbs, auch in einem
|
|
50
|
+
* inzwischen steuerpflichtigen Konto. Fehlt bei aelteren Servern.
|
|
51
|
+
*/
|
|
52
|
+
exempt?: boolean;
|
|
53
|
+
}
|
|
54
|
+
/** Gutschein-Einstellungen der Kasse (Block `vouchers` aus `getKasseSettings`). */
|
|
55
|
+
export interface PosVoucherSettings {
|
|
56
|
+
sale: boolean;
|
|
57
|
+
valueVoucherType: 'multi_purpose' | 'single_purpose' | null;
|
|
58
|
+
vatRateBp: number | null;
|
|
59
|
+
}
|
|
60
|
+
/** Ohne Block `vouchers` (alter Server, Konto ohne Schalter): Verkaufen ist aus. */
|
|
61
|
+
export declare const VOUCHER_SETTINGS_DEFAULTS: Readonly<PosVoucherSettings>;
|
|
62
|
+
/**
|
|
63
|
+
* Block `vouchers` einer Einstellungs-Antwort, geschlossen im Zweifel: ist
|
|
64
|
+
* irgendein Feld unstimmig, gilt der ganze Block als nicht gelesen und
|
|
65
|
+
* Verkaufen bleibt aus (kein Wurf, sonst ginge das Laden aller Einstellungen
|
|
66
|
+
* verloren). Stimmig heisst: `sale` ein Wahrheitswert, die Art
|
|
67
|
+
* `multi_purpose`/`single_purpose`/`null`, der Satz eine ganze Zahl >= 0 oder
|
|
68
|
+
* `null`; Mehrzweck traegt keinen Satz, Einzweck braucht einen, und ein
|
|
69
|
+
* eingeschalteter Verkauf braucht eine Art. Ein ausgeschalteter Verkauf darf
|
|
70
|
+
* eine Art behalten.
|
|
71
|
+
*/
|
|
72
|
+
export declare function posVoucherSettingsFromWire(block: unknown): PosVoucherSettings;
|
|
73
|
+
/**
|
|
74
|
+
* Gutschein am Code pruefen (Gutschein-Recht `voucherRedeem`). Der Code wird
|
|
75
|
+
* tolerant eingelesen (Gross/klein, Bindestriche, `O`/`0`, `I`/`L`/`1`) und
|
|
76
|
+
* vor dem Senden auf sein Pruefzeichen geprueft: ein Tippfehler geht nie ueber
|
|
77
|
+
* das Netz, sondern endet mit `KasseneckValidationError`. Fachliche Fehler
|
|
78
|
+
* kommen als `KasseneckApiError` mit dem Code des Servers
|
|
79
|
+
* (`voucher_not_found`, `too_many_voucher_checks`, `not_permitted` …).
|
|
80
|
+
*/
|
|
81
|
+
export declare function checkVoucher(transport: InternerTransport, code: string): Promise<VoucherCheck>;
|
|
82
|
+
/**
|
|
83
|
+
* Etikett eines Gutscheins noch einmal drucken (Gutschein-Recht
|
|
84
|
+
* `voucherManage`). Antwort ist der Klartext-Code zum Drucken; er gehoert
|
|
85
|
+
* nur in den Speicher bis zum Druck. Aufrufer duerfen ihn nicht protokollieren,
|
|
86
|
+
* speichern oder in Meldungen nennen; Lesefehler dieser Antwort nennen ihn nie.
|
|
87
|
+
*/
|
|
88
|
+
export declare function reprintVoucherLabel(transport: InternerTransport, id: string): Promise<{
|
|
89
|
+
code: string;
|
|
90
|
+
}>;
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { VoucherCode } from '@kreiseck/validator';
|
|
2
|
+
import { KasseneckApiError, KasseneckValidationError } from '../client/errors.js';
|
|
3
|
+
export const VOUCHER_KINDS = Object.freeze(['multi_purpose', 'single_purpose', 'service', 'promotion']);
|
|
4
|
+
export const VOUCHER_STATUSES = Object.freeze(['active', 'used_up', 'expired', 'paid_out', 'cancelled', 'blocked']);
|
|
5
|
+
const VOUCHER_VALIDITY_PATTERNS = ['unlimited', '1+3', 'custom'];
|
|
6
|
+
/** Ohne Block `vouchers` (alter Server, Konto ohne Schalter): Verkaufen ist aus. */
|
|
7
|
+
export const VOUCHER_SETTINGS_DEFAULTS = Object.freeze({
|
|
8
|
+
sale: false, valueVoucherType: null, vatRateBp: null,
|
|
9
|
+
});
|
|
10
|
+
/**
|
|
11
|
+
* Block `vouchers` einer Einstellungs-Antwort, geschlossen im Zweifel: ist
|
|
12
|
+
* irgendein Feld unstimmig, gilt der ganze Block als nicht gelesen und
|
|
13
|
+
* Verkaufen bleibt aus (kein Wurf, sonst ginge das Laden aller Einstellungen
|
|
14
|
+
* verloren). Stimmig heisst: `sale` ein Wahrheitswert, die Art
|
|
15
|
+
* `multi_purpose`/`single_purpose`/`null`, der Satz eine ganze Zahl >= 0 oder
|
|
16
|
+
* `null`; Mehrzweck traegt keinen Satz, Einzweck braucht einen, und ein
|
|
17
|
+
* eingeschalteter Verkauf braucht eine Art. Ein ausgeschalteter Verkauf darf
|
|
18
|
+
* eine Art behalten.
|
|
19
|
+
*/
|
|
20
|
+
export function posVoucherSettingsFromWire(block) {
|
|
21
|
+
const zu = () => ({ ...VOUCHER_SETTINGS_DEFAULTS });
|
|
22
|
+
if (block === null || typeof block !== 'object' || Array.isArray(block))
|
|
23
|
+
return zu();
|
|
24
|
+
const b = block;
|
|
25
|
+
const sale = b['sale'];
|
|
26
|
+
const art = b['valueVoucherType'] ?? null;
|
|
27
|
+
const satz = b['vatRateBp'] ?? null;
|
|
28
|
+
if (typeof sale !== 'boolean')
|
|
29
|
+
return zu();
|
|
30
|
+
if (art !== null && art !== 'multi_purpose' && art !== 'single_purpose')
|
|
31
|
+
return zu();
|
|
32
|
+
if (satz !== null && !(typeof satz === 'number' && Number.isInteger(satz) && satz >= 0))
|
|
33
|
+
return zu();
|
|
34
|
+
if (sale && art === null)
|
|
35
|
+
return zu();
|
|
36
|
+
if (art === 'single_purpose' && satz === null)
|
|
37
|
+
return zu();
|
|
38
|
+
if (art === 'multi_purpose' && satz !== null)
|
|
39
|
+
return zu();
|
|
40
|
+
return { sale, valueVoucherType: art, vatRateBp: satz };
|
|
41
|
+
}
|
|
42
|
+
function fehler(name, grund) {
|
|
43
|
+
return new KasseneckValidationError(name, `Antwort: ${grund}`, 'response');
|
|
44
|
+
}
|
|
45
|
+
function objekt(name, pfad, w) {
|
|
46
|
+
if (w === null || typeof w !== 'object' || Array.isArray(w))
|
|
47
|
+
throw fehler(name, `${pfad} ist kein Objekt`);
|
|
48
|
+
return w;
|
|
49
|
+
}
|
|
50
|
+
const fehlt = (w) => w === undefined || w === null;
|
|
51
|
+
function ganzzahlWennDa(name, pfad, ziel, o, feld) {
|
|
52
|
+
const w = o[feld];
|
|
53
|
+
if (fehlt(w))
|
|
54
|
+
return;
|
|
55
|
+
if (typeof w !== 'number' || !Number.isSafeInteger(w))
|
|
56
|
+
throw fehler(name, `${pfad}.${feld} ist keine ganze Zahl`);
|
|
57
|
+
ziel[feld] = w;
|
|
58
|
+
}
|
|
59
|
+
function textPflicht(name, pfad, o, feld) {
|
|
60
|
+
const w = o[feld];
|
|
61
|
+
if (typeof w !== 'string' || w === '')
|
|
62
|
+
throw fehler(name, `${pfad}.${feld} fehlt`);
|
|
63
|
+
return w;
|
|
64
|
+
}
|
|
65
|
+
function wahl(name, pfad, o, feld, erlaubt) {
|
|
66
|
+
const w = o[feld];
|
|
67
|
+
if (typeof w !== 'string' || !erlaubt.includes(w))
|
|
68
|
+
throw fehler(name, `${pfad}.${feld} ist unbekannt`);
|
|
69
|
+
return w;
|
|
70
|
+
}
|
|
71
|
+
function gueltigkeit(name, w) {
|
|
72
|
+
const o = objekt(name, 'validity', w);
|
|
73
|
+
const raus = { pattern: wahl(name, 'validity', o, 'pattern', VOUCHER_VALIDITY_PATTERNS) };
|
|
74
|
+
for (const feld of ['redeemableUntil', 'refundableUntil', 'validUntil']) {
|
|
75
|
+
const t = o[feld];
|
|
76
|
+
if (fehlt(t))
|
|
77
|
+
continue;
|
|
78
|
+
if (typeof t !== 'string' || t === '')
|
|
79
|
+
throw fehler(name, `validity.${feld} ist kein Datum`);
|
|
80
|
+
raus[feld] = t;
|
|
81
|
+
}
|
|
82
|
+
return raus;
|
|
83
|
+
}
|
|
84
|
+
function artikel(name, w) {
|
|
85
|
+
const o = objekt(name, 'article', w);
|
|
86
|
+
const raus = { id: textPflicht(name, 'article', o, 'id'), name: textPflicht(name, 'article', o, 'name') };
|
|
87
|
+
for (const feld of ['vatRateBp', 'priceCents']) {
|
|
88
|
+
const z = o[feld];
|
|
89
|
+
if (typeof z !== 'number' || !Number.isSafeInteger(z))
|
|
90
|
+
throw fehler(name, `article.${feld} ist keine ganze Zahl`);
|
|
91
|
+
raus[feld] = z;
|
|
92
|
+
}
|
|
93
|
+
return raus;
|
|
94
|
+
}
|
|
95
|
+
function leseCheck(name, w) {
|
|
96
|
+
const o = objekt(name, 'data', w);
|
|
97
|
+
const raus = {
|
|
98
|
+
type: wahl(name, 'data', o, 'type', VOUCHER_KINDS),
|
|
99
|
+
status: wahl(name, 'data', o, 'status', VOUCHER_STATUSES),
|
|
100
|
+
codeSuffix: textPflicht(name, 'data', o, 'codeSuffix'),
|
|
101
|
+
};
|
|
102
|
+
for (const feld of ['balanceCents', 'faceValueCents', 'quantity', 'remainingQuantity', 'vatRateBp', 'discountCents', 'discountPercentBp']) {
|
|
103
|
+
ganzzahlWennDa(name, 'data', raus, o, feld);
|
|
104
|
+
}
|
|
105
|
+
if (!fehlt(o.article))
|
|
106
|
+
raus.article = artikel(name, o.article);
|
|
107
|
+
if (!fehlt(o.validity))
|
|
108
|
+
raus.validity = gueltigkeit(name, o.validity);
|
|
109
|
+
if (!fehlt(o.exempt)) {
|
|
110
|
+
if (typeof o.exempt !== 'boolean')
|
|
111
|
+
throw fehler(name, 'data.exempt ist kein Wahrheitswert');
|
|
112
|
+
raus.exempt = o.exempt;
|
|
113
|
+
}
|
|
114
|
+
return raus;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Gutschein am Code pruefen (Gutschein-Recht `voucherRedeem`). Der Code wird
|
|
118
|
+
* tolerant eingelesen (Gross/klein, Bindestriche, `O`/`0`, `I`/`L`/`1`) und
|
|
119
|
+
* vor dem Senden auf sein Pruefzeichen geprueft: ein Tippfehler geht nie ueber
|
|
120
|
+
* das Netz, sondern endet mit `KasseneckValidationError`. Fachliche Fehler
|
|
121
|
+
* kommen als `KasseneckApiError` mit dem Code des Servers
|
|
122
|
+
* (`voucher_not_found`, `too_many_voucher_checks`, `not_permitted` …).
|
|
123
|
+
*/
|
|
124
|
+
export async function checkVoucher(transport, code) {
|
|
125
|
+
const name = 'checkVoucher';
|
|
126
|
+
const geprueft = typeof code === 'string' ? VoucherCode.validate(code) : null;
|
|
127
|
+
// Der Code steht bewusst nicht in der Meldung.
|
|
128
|
+
if (!geprueft || !geprueft.ok)
|
|
129
|
+
throw new KasseneckValidationError(name, 'code ist kein gueltiger Gutscheincode', 'request');
|
|
130
|
+
// Der Code reist im Rumpf: als Geheimnis nennen, damit er in keinem Fehler (Servertext, Ursache) auftaucht.
|
|
131
|
+
const geheim = [geprueft.normalized, VoucherCode.format(geprueft.normalized)];
|
|
132
|
+
let antwort;
|
|
133
|
+
try {
|
|
134
|
+
antwort = await transport(name, { code: geprueft.normalized }, undefined, geheim);
|
|
135
|
+
}
|
|
136
|
+
catch (fehler) {
|
|
137
|
+
// Der Transport siebt nur Ursachen; ein Servertext, der den Code wiederholt, wird hier geschwaerzt.
|
|
138
|
+
if (fehler instanceof KasseneckApiError && geheim.some((g) => fehler.serverMessage.includes(g))) {
|
|
139
|
+
const text = geheim.reduce((t, g) => t.split(g).join('****'), fehler.serverMessage);
|
|
140
|
+
throw new KasseneckApiError(name, text, fehler.details, fehler.code);
|
|
141
|
+
}
|
|
142
|
+
throw fehler;
|
|
143
|
+
}
|
|
144
|
+
return leseCheck(name, antwort);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Etikett eines Gutscheins noch einmal drucken (Gutschein-Recht
|
|
148
|
+
* `voucherManage`). Antwort ist der Klartext-Code zum Drucken; er gehoert
|
|
149
|
+
* nur in den Speicher bis zum Druck. Aufrufer duerfen ihn nicht protokollieren,
|
|
150
|
+
* speichern oder in Meldungen nennen; Lesefehler dieser Antwort nennen ihn nie.
|
|
151
|
+
*/
|
|
152
|
+
export async function reprintVoucherLabel(transport, id) {
|
|
153
|
+
const name = 'reprintVoucherLabel';
|
|
154
|
+
if (typeof id !== 'string' || id.trim() === '')
|
|
155
|
+
throw new KasseneckValidationError(name, 'id fehlt', 'request');
|
|
156
|
+
const o = objekt(name, 'data', await transport(name, { id }));
|
|
157
|
+
return { code: textPflicht(name, 'data', o, 'code') };
|
|
158
|
+
}
|
package/dist/esm/pos/index.d.ts
CHANGED
|
@@ -11,15 +11,16 @@ export { type ArticleGroup, type ArticleGroupPayload, fromArticleGroupPayload, t
|
|
|
11
11
|
export { getPosSettings, setMyPosSettings, setMyRegisterDeviceSettings, setMyPosLogo, posSettingsFromWire, type SetMyPosLogoOptions, } from './client.js';
|
|
12
12
|
export { POS_ERROR_CODES, type PosErrorCode, isPosErrorCode, posErrorCode, isPosError, type PosFieldError, posFieldErrors, } from './errors.js';
|
|
13
13
|
export { distributeDiscount } from '../receipt/discount.js';
|
|
14
|
-
export { cancelScopeOf, receiptsScopeOf, stockViewOf, type RegisterScope, type RegisterUserPerms } from '../register/pairing.js';
|
|
14
|
+
export { cancelScopeOf, receiptsScopeOf, stockViewOf, voucherPermsOf, type VoucherPerms, type RegisterScope, type RegisterUserPerms } from '../register/pairing.js';
|
|
15
15
|
export { type NetworkPrinter, type PrintJob, type PrintJobStatus, PRINT_JOB_STATUSES, type PrintJobSource, PRINT_JOB_SOURCES, isPrintJobFinished, type CreatePrintJobOptions, listMyPrinters, createPrintJob, getPrintJob, } from './drucker.js';
|
|
16
16
|
export { listMyTipRecipients } from './trinkgeld.js';
|
|
17
17
|
export { STOCK_LOCATION_TYPES, type StockLocationType, type StockLocation, type StockLocationAddress, type StockLevel, type StockValue, type StockList, type ListMyStockOptions, type SetMyCashregisterStockLocationOptions, type CashregisterStockLocation, listMyStockLocations, listMyStock, setMyCashregisterStockLocation, } from './lager.js';
|
|
18
18
|
export { type ListMyStocktakesOptions, type ListMyStocktakeItemsOptions, type ListMyStocktakeCountsOptions, type RecordMyStocktakeCountOptions, type VoidMyStocktakeCountOptions, listMyStocktakes, listMyStocktakeItems, listMyStocktakeCounts, recordMyStocktakeCount, voidMyStocktakeCount, parseQuantityMilli, } from './inventur.js';
|
|
19
19
|
export type { Stocktake, StocktakeItem, StocktakeCount, StocktakeCountResult, StocktakeItemPage, StocktakeCountPage, StocktakeActor, StocktakeProgress, StocktakeReview, StocktakeRecount, StocktakeScope, } from '../inventory/typen.js';
|
|
20
20
|
export type { StocktakeStatus } from '../inventory/vertrag.js';
|
|
21
|
+
export { VOUCHER_KINDS, VOUCHER_STATUSES, checkVoucher, reprintVoucherLabel, VOUCHER_SETTINGS_DEFAULTS, posVoucherSettingsFromWire, type VoucherKind, type VoucherStatus, type VoucherValidity, type VoucherArticle, type VoucherCheck, type PosVoucherSettings, } from './gutschein.js';
|
|
21
22
|
export { type GetMyDrawerStateOptions, type OpenMyShiftOptions, type RecordMyCashMovementOptions, type DrawerOpeningEntry, type LogMyDrawerOpeningsOptions, type GetMyShiftReportOptions, type TipPayout, type CloseMyShiftOptions, type AfterCountOption, getMyDrawerState, openMyShift, recordMyCashMovement, logMyDrawerOpenings, getMyShiftReport, closeMyShift, sumDenominations, parseCentsAllowZero, newIdempotencyKey, shiftTipLines, durationText, type ShiftTipLine, type ShiftTipLineKey, } from './schicht.js';
|
|
22
23
|
export { EUR_DENOMINATIONS, MOVEMENT_TYPES, MANUAL_MOVEMENT_TYPES, CASH_LOCATIONS, OPENING_REASONS, MANUAL_OPENING_REASONS, SHIFT_RECEIPT_KINDS, AFTER_COUNT_TYPES, type AfterCountType, type AfterCount, type EurDenomination, type MovementType, type ManualMovementType, type CashLocation, type OpeningReason, type ManualOpeningReason, type ShiftReceiptKind, type ShiftStatus, type ShiftActorRole, type Denominations, type ShiftActor, type DrawerInfo, type DrawerRights, type NonCashAmount, type ShiftTipTotals, type ShiftTip, type ShiftRunningTotals, type ShiftReconciliation, type Shift, type TipClaim, type DrawerOpening, type DrawerState, type CashMovement, type ShiftReceiptHeader, type ShiftReceiptMovement, type ShiftReceiptTip, type ShiftSummaryReceipt, type CashMovementReceipt, type ShiftReceipt, type OpenShiftResult, type CashMovementResult, type DrawerOpeningsResult, type ShiftReport, type CloseShiftResult, } from './schicht-typen.js';
|
|
23
24
|
export { type ShiftReceiptLayoutOptions, shiftReceiptLayout, cashMovementLayout } from '../receipt/shift-sheet.js';
|
|
24
25
|
export { RETURN_DISPOSITIONS, isReturnDisposition, type ReturnDisposition } from '../models/cancellation.js';
|
|
25
|
-
export { MESSAGES, ERROR_RULES, ERROR_CODE_RULES, ERROR_OUTCOME_RULES, CALLS_WITH_EFFECT, findErrorRule, messageOutcome, messageText, messageAppliesTo, RECEIPT_EMAIL_ERROR_MESSAGES, receiptEmailErrorMessage, CANCELLATION_PAYMENT_ERROR_MESSAGES, cancellationPaymentErrorMessage, LABELS, labelText, RETURN_DISPOSITION_LABELS, type TextEntry, type MessageKey, type ErrorKind, type ErrorRule, type ErrorRuleDetail, type Surface, type ReceiptEmailMessageCode, type CancellationPaymentMessageCode, type LabelKey, } from './texte.js';
|
|
26
|
+
export { MESSAGES, ERROR_RULES, ERROR_CODE_RULES, ERROR_OUTCOME_RULES, CALLS_WITH_EFFECT, findErrorRule, messageOutcome, messageText, messageAppliesTo, RECEIPT_EMAIL_ERROR_MESSAGES, receiptEmailErrorMessage, CANCELLATION_PAYMENT_ERROR_MESSAGES, cancellationPaymentErrorMessage, VOUCHER_ERROR_MESSAGES, voucherErrorMessage, LABELS, labelText, RETURN_DISPOSITION_LABELS, type TextEntry, type MessageKey, type ErrorKind, type ErrorRule, type ErrorRuleDetail, type Surface, type ReceiptEmailMessageCode, type CancellationPaymentMessageCode, type LabelKey, } from './texte.js';
|
package/dist/esm/pos/index.js
CHANGED
|
@@ -15,11 +15,13 @@ export { POS_ERROR_CODES, isPosErrorCode, posErrorCode, isPosError, posFieldErro
|
|
|
15
15
|
export { distributeDiscount } from '../receipt/discount.js';
|
|
16
16
|
// Reichweiten der Kassen-Rechte (Migration wie im Backend) -- bewusst NICHT im
|
|
17
17
|
// Register-Unterpfad: dessen Exportnamen sind 1:1 Function-Namen (Rewrites).
|
|
18
|
-
export { cancelScopeOf, receiptsScopeOf, stockViewOf } from '../register/pairing.js';
|
|
18
|
+
export { cancelScopeOf, receiptsScopeOf, stockViewOf, voucherPermsOf } from '../register/pairing.js';
|
|
19
19
|
export { PRINT_JOB_STATUSES, PRINT_JOB_SOURCES, isPrintJobFinished, listMyPrinters, createPrintJob, getPrintJob, } from './drucker.js';
|
|
20
20
|
export { listMyTipRecipients } from './trinkgeld.js';
|
|
21
21
|
export { STOCK_LOCATION_TYPES, listMyStockLocations, listMyStock, setMyCashregisterStockLocation, } from './lager.js';
|
|
22
22
|
export { listMyStocktakes, listMyStocktakeItems, listMyStocktakeCounts, recordMyStocktakeCount, voidMyStocktakeCount, parseQuantityMilli, } from './inventur.js';
|
|
23
|
+
// Gutschein pruefen und Etikett nachdrucken an der Kasse.
|
|
24
|
+
export { VOUCHER_KINDS, VOUCHER_STATUSES, checkVoucher, reprintVoucherLabel, VOUCHER_SETTINGS_DEFAULTS, posVoucherSettingsFromWire, } from './gutschein.js';
|
|
23
25
|
// Schicht, Kassenstand und Lade-Protokoll (1.9.0): Aufrufe, Kataloge, Helfer, Bons.
|
|
24
26
|
export { getMyDrawerState, openMyShift, recordMyCashMovement, logMyDrawerOpenings, getMyShiftReport, closeMyShift, sumDenominations, parseCentsAllowZero, newIdempotencyKey, shiftTipLines, durationText, } from './schicht.js';
|
|
25
27
|
export { EUR_DENOMINATIONS, MOVEMENT_TYPES, MANUAL_MOVEMENT_TYPES, CASH_LOCATIONS, OPENING_REASONS, MANUAL_OPENING_REASONS, SHIFT_RECEIPT_KINDS, AFTER_COUNT_TYPES, } from './schicht-typen.js';
|
|
@@ -27,4 +29,4 @@ export { shiftReceiptLayout, cashMovementLayout } from '../receipt/shift-sheet.j
|
|
|
27
29
|
// Rueckgabe-Wahl beim Storno (Kassen-Dialog); dieselbe Liste wie an der Wurzel.
|
|
28
30
|
export { RETURN_DISPOSITIONS, isReturnDisposition } from '../models/cancellation.js';
|
|
29
31
|
// Was die Kasse selbst sagt: ein Katalog fuer Browser-Kasse und App.
|
|
30
|
-
export { MESSAGES, ERROR_RULES, ERROR_CODE_RULES, ERROR_OUTCOME_RULES, CALLS_WITH_EFFECT, findErrorRule, messageOutcome, messageText, messageAppliesTo, RECEIPT_EMAIL_ERROR_MESSAGES, receiptEmailErrorMessage, CANCELLATION_PAYMENT_ERROR_MESSAGES, cancellationPaymentErrorMessage, LABELS, labelText, RETURN_DISPOSITION_LABELS, } from './texte.js';
|
|
32
|
+
export { MESSAGES, ERROR_RULES, ERROR_CODE_RULES, ERROR_OUTCOME_RULES, CALLS_WITH_EFFECT, findErrorRule, messageOutcome, messageText, messageAppliesTo, RECEIPT_EMAIL_ERROR_MESSAGES, receiptEmailErrorMessage, CANCELLATION_PAYMENT_ERROR_MESSAGES, cancellationPaymentErrorMessage, VOUCHER_ERROR_MESSAGES, voucherErrorMessage, LABELS, labelText, RETURN_DISPOSITION_LABELS, } from './texte.js';
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* derselben Werte liegt unter `fixtures/stored/`. Trinkgeld ist bewusst AUS,
|
|
12
12
|
* bis die Buchung geklaert ist (Spec kachel-kasse § 9).
|
|
13
13
|
*/
|
|
14
|
+
import { type PosVoucherSettings } from './gutschein.js';
|
|
14
15
|
export declare const THEME: readonly ["clear", "warm", "night", "contrast"];
|
|
15
16
|
export type PosTheme = typeof THEME[number];
|
|
16
17
|
export declare const FONT_SIZE: readonly ["S", "M", "L", "XL"];
|
|
@@ -268,6 +269,8 @@ export interface PosDeviceSettings {
|
|
|
268
269
|
export interface PosSettings {
|
|
269
270
|
business: PosBusinessSettings;
|
|
270
271
|
device: PosDeviceSettings;
|
|
272
|
+
/** Gutschein-Verkauf der Kasse (Block `vouchers`); ohne Block ist Verkaufen aus. */
|
|
273
|
+
vouchers: PosVoucherSettings;
|
|
271
274
|
}
|
|
272
275
|
export declare const POS_BUSINESS_DEFAULTS: Readonly<PosBusinessSettings>;
|
|
273
276
|
export declare const POS_DEVICE_DEFAULTS: Readonly<PosDeviceSettings>;
|