@kreiseck/kasseneck-api 0.18.0 → 0.20.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.
Files changed (35) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/README.md +17 -4
  3. package/dist/cjs/payments/hobex-hps/index.d.ts +1 -1
  4. package/dist/cjs/payments/hobex-hps/index.js +3 -1
  5. package/dist/cjs/payments/hobex-hps/payments.d.ts +11 -0
  6. package/dist/cjs/payments/hobex-hps/payments.js +73 -4
  7. package/dist/cjs/payments/hobex-hps/tecs-codes.d.ts +16 -0
  8. package/dist/cjs/payments/hobex-hps/tecs-codes.js +366 -0
  9. package/dist/cjs/payments/hobex-hps/transaction-response.d.ts +56 -8
  10. package/dist/cjs/payments/hobex-hps/transaction-response.js +135 -65
  11. package/dist/cjs/rechnung/texte.d.ts +5 -4
  12. package/dist/cjs/rechnung/texte.js +16 -8
  13. package/dist/cjs/rechnung/typen.d.ts +7 -0
  14. package/dist/cjs/rechnung/vertrag.d.ts +7 -4
  15. package/dist/cjs/rechnung/vertrag.js +14 -4
  16. package/dist/esm/payments/hobex-hps/index.d.ts +1 -1
  17. package/dist/esm/payments/hobex-hps/index.js +1 -1
  18. package/dist/esm/payments/hobex-hps/payments.d.ts +11 -0
  19. package/dist/esm/payments/hobex-hps/payments.js +74 -5
  20. package/dist/esm/payments/hobex-hps/tecs-codes.d.ts +16 -0
  21. package/dist/esm/payments/hobex-hps/tecs-codes.js +363 -0
  22. package/dist/esm/payments/hobex-hps/transaction-response.d.ts +56 -8
  23. package/dist/esm/payments/hobex-hps/transaction-response.js +133 -65
  24. package/dist/esm/rechnung/texte.d.ts +5 -4
  25. package/dist/esm/rechnung/texte.js +16 -8
  26. package/dist/esm/rechnung/typen.d.ts +7 -0
  27. package/dist/esm/rechnung/vertrag.d.ts +7 -4
  28. package/dist/esm/rechnung/vertrag.js +14 -4
  29. package/fixtures/hobex-hps-codes.json +4075 -52
  30. package/fixtures/kasse-texte.json +1 -1
  31. package/fixtures/oberflaeche.json +1 -1
  32. package/fixtures/rechnung-api-beispiele/zahlung-fehler-vor-ort.json +15 -0
  33. package/fixtures/rechnung-api.schema.json +7 -1
  34. package/fixtures/rechnung-texte.json +11 -9
  35. package/package.json +1 -1
@@ -25,12 +25,22 @@
25
25
  * bis dahin unbenannter Code ([TECHNICAL_ERROR_CODE], `9900`) war darueber
26
26
  * schluessig und haette eine Zahlung, unter der tatsaechlich Geld geflossen
27
27
  * sein kann, als `declined` gemeldet.
28
+ *
29
+ * Seit 16.09.2026 kommt die TECS-Liste dazu (`tecs-codes.ts`): das HPS
30
+ * reicht die Codes der TECS-Plattform durch, auf der hobex autorisiert. Neu
31
+ * sind dabei [HpsCode.sendReversal] (Storno nachschicken, siehe
32
+ * `payments.ts`) und die Schreibweise: TECS fuehrt `0055`, das Terminal
33
+ * sendet `55` -- [normalizeHpsCode] macht beides zu einem Code.
34
+ *
35
+ * **Einen neuen Code aufnehmen:** Eintrag hier oder in `tecs-codes.ts`,
36
+ * derselbe im Dart-Zwilling, `npm run fixtures:hobex-hps-codes`; ein neuer
37
+ * Grund braucht in jeder Kasse eine Uebersetzung.
28
38
  */
29
39
  /** Wie ein Ergebniscode den Ausgang eines Vorgangs bestimmt. */
30
40
  export type HpsCodeEffect =
31
41
  /** Schreibt den Ausgang fest: `'0'` genehmigt, jeder andere abgelehnt. */
32
42
  'conclusive'
33
- /** Gemessen oder dokumentiert, aber KEINE Aussage (9027, 9900, 100011). */
43
+ /** Gemessen oder dokumentiert, aber KEINE Aussage (9027, 100011, fremde TECS-Produkte). */
34
44
  | 'noStatement'
35
45
  /**
36
46
  * Der Host war beteiligt, das Terminal storniert nicht selbst -- ob belastet
@@ -42,9 +52,12 @@ export type HpsCodeEffect =
42
52
  export type HpsCodeSource =
43
53
  /** Am Geraet gemessen (`doc/kartenzahlung.md` im Dart-Zwilling). */
44
54
  'measured'
45
- /** Aus der Antwortcodeliste von hobex (erhalten 11.09.2026). */
55
+ /**
56
+ * Von hobex dokumentiert: HPS-Liste (11.09.2026) oder TECS-Liste
57
+ * (16.09.2026, dann ist `tecsTitle` gesetzt).
58
+ */
46
59
  | 'documented'
47
- /** Beides. */
60
+ /** Beides: gemessen und von hobex bestaetigt. */
48
61
  | 'measuredAndDocumented';
49
62
  /**
50
63
  * Worauf eine Kasse reagiert -- der Grund hinter einem Ergebniscode. Mehrere
@@ -53,12 +66,12 @@ export type HpsCodeSource =
53
66
  * fuer den Bediener steht in [HPS_REASON_HINTS]; eine Kasse mit eigener
54
67
  * Uebersetzung schluesselt ueber den Grund selbst.
55
68
  */
56
- export type HpsCodeReason = 'approved' | 'aborted' | 'noCard' | 'cardReadFailed' | 'cardDeclined' | 'wrongPin' | 'amountInvalid' | 'tipNotSelected' | 'terminalBusy' | 'terminalBlocked' | 'terminalSetup' | 'terminalFault' | 'requestRejected' | 'invalidTransaction' | 'refundPassword' | 'refundDisabled' | 'hostTimeoutReversed' | 'hostFault' | 'internalError' | 'canceled' | 'notAbortable' | 'noStatement' | 'technicalError' | 'unknown';
69
+ export type HpsCodeReason = 'approved' | 'approvedWithCondition' | 'aborted' | 'noCard' | 'cardReadFailed' | 'cardDeclined' | 'issuerDeclined' | 'cardBlocked' | 'cardExpired' | 'insufficientFunds' | 'wrongPin' | 'pinTriesExceeded' | 'pinRequired' | 'amountInvalid' | 'tipNotSelected' | 'terminalBusy' | 'terminalBlocked' | 'terminalSetup' | 'acquirerSetup' | 'terminalFault' | 'requestRejected' | 'hostRejected' | 'hostUnavailable' | 'invalidTransaction' | 'refundPassword' | 'refundDisabled' | 'refundRejected' | 'hostTimeoutReversed' | 'reversedByHost' | 'voidedAfterHostFault' | 'hostFault' | 'hostTimeout' | 'internalError' | 'canceled' | 'cancelDenied' | 'originalDeclined' | 'notAbortable' | 'noStatement' | 'technicalError' | 'unknown';
57
70
  /** Der Satz fuer den Bediener je Grund, deutsch. */
58
71
  export declare const HPS_REASON_HINTS: Readonly<Record<HpsCodeReason, string>>;
59
72
  /**
60
- * Die beiden Gruende hinter einem Code mit `effect: 'hostUncertain'`: ob Geld
61
- * geflossen ist, weiss das Terminal nicht.
73
+ * Die Gruende hinter einem Code mit `effect: 'hostUncertain'`: ob (und wie
74
+ * viel) Geld geflossen ist, weiss das Terminal nicht.
62
75
  */
63
76
  export declare function isHostUncertainReason(reason: HpsCodeReason | undefined): boolean;
64
77
  /** Ein Ergebniscode, dessen Bedeutung feststeht (gemessen oder dokumentiert). */
@@ -88,6 +101,20 @@ export interface HpsCode extends HpsMeasuredCode {
88
101
  * nichts (gemessen fuer `100108`). Siehe [isConclusiveAsStatus].
89
102
  */
90
103
  readonly rejectsRequest: boolean;
104
+ /**
105
+ * Bei diesem Code wird ein Storno nachgeschickt: der Host hat nicht oder
106
+ * nicht brauchbar geantwortet, und das Terminal nimmt den Vorgang nicht
107
+ * selbst zurueck. Nur bei `effect: 'hostUncertain'`. Vorgabe von hobex
108
+ * (16.09.2026) zu `9908`: "ein Timeout wie jeder andere. Richtigerweise
109
+ * sollte in dem Fall ein Storno nachgeschickt werden."
110
+ */
111
+ readonly sendReversal: boolean;
112
+ /**
113
+ * Titel in der TECS-Liste (16.09.2026), wenn der Code dort steht -- sonst
114
+ * `null`. Bei gemessenen Codes weicht er vom [title] ab (`55`: "PIN
115
+ * falsch" gegen "Incorrect PIN").
116
+ */
117
+ readonly tecsTitle: string | null;
91
118
  }
92
119
  /**
93
120
  * Die vollstaendige Codetabelle — Vertrag mit dem Dart-Zwilling
@@ -228,7 +255,19 @@ export declare const TERMINAL_BUSY_HTTP_STATUS = 409;
228
255
  * HTTP-Status ist kein `responseCode`.
229
256
  */
230
257
  export declare const NOT_FOUND_HTTP_STATUS = 404;
231
- /** Der Eintrag zu [code] in [HPS_CODES], oder `undefined`, wenn seine Bedeutung nicht feststeht. */
258
+ /**
259
+ * Die Schreibweise, unter der [code] in [HPS_CODES] steht: ein rein
260
+ * numerischer Code ohne fuehrende Nullen (`0055` -> `55`, `0000` -> `0`),
261
+ * jeder andere unveraendert. Ohne diese Angleichung waere ein `0000` kein
262
+ * `0` -- und damit eine ABLEHNUNG einer genehmigten Zahlung.
263
+ * [parseHpsTransactionResponse] gleicht deshalb schon beim Einlesen an.
264
+ */
265
+ export declare function normalizeHpsCode(code: string): string;
266
+ /**
267
+ * Der Eintrag zu [code] in [HPS_CODES], oder `undefined`, wenn seine
268
+ * Bedeutung nicht feststeht. Findet beide Schreibweisen
269
+ * ([normalizeHpsCode]) und die Familien mit Platzhalter (`8105` -> `81xx`).
270
+ */
232
271
  export declare function hpsCodeInfo(code: string | undefined): HpsCode | undefined;
233
272
  /**
234
273
  * Der Grund zu [code] -- `'unknown'` fuer einen Code ausserhalb der Tabelle,
@@ -294,7 +333,10 @@ export declare function isNotAbortable(res: Pick<HpsTransactionResponse, 'respon
294
333
  * gemessen. `100011` ist dokumentiert, aber nie gesehen.
295
334
  */
296
335
  export declare function isNoStatement(res: Pick<HpsTransactionResponse, 'responseCode'>): boolean;
297
- /** `true`, wenn das Terminal einen technischen Fehler meldet (9900). */
336
+ /**
337
+ * `true`, wenn das Terminal einen technischen Fehler meldet (9900). Seit der
338
+ * TECS-Liste zugleich [isHostUncertain]: ob belastet wurde, ist offen.
339
+ */
298
340
  export declare function isTechnicalError(res: Pick<HpsTransactionResponse, 'responseCode'>): boolean;
299
341
  /**
300
342
  * `true`, wenn der Vorgang unter dieser Kennung aufgehoben wurde (9011).
@@ -327,6 +369,12 @@ export declare function isConclusiveAsStatus(res: Pick<HpsTransactionResponse, '
327
369
  * Speicher des Terminals, nicht den des Hosts.
328
370
  */
329
371
  export declare function isHostUncertain(res: Pick<HpsTransactionResponse, 'responseCode'>): boolean;
372
+ /**
373
+ * `true`, wenn zu diesem Code ein Storno nachzuschicken ist
374
+ * ([HpsCode.sendReversal]) -- der Host hat nicht oder nicht brauchbar
375
+ * geantwortet, etwa `9908`.
376
+ */
377
+ export declare function needsReversal(res: Pick<HpsTransactionResponse, 'responseCode'>): boolean;
330
378
  /**
331
379
  * `true`, wenn ein Ergebniscode VORHANDEN ist, dessen Bedeutung aber nicht
332
380
  * feststeht -- er fehlt in [HPS_CODES]. Ein Code, den dieses Modell schlicht
@@ -25,66 +25,84 @@
25
25
  * bis dahin unbenannter Code ([TECHNICAL_ERROR_CODE], `9900`) war darueber
26
26
  * schluessig und haette eine Zahlung, unter der tatsaechlich Geld geflossen
27
27
  * sein kann, als `declined` gemeldet.
28
+ *
29
+ * Seit 16.09.2026 kommt die TECS-Liste dazu (`tecs-codes.ts`): das HPS
30
+ * reicht die Codes der TECS-Plattform durch, auf der hobex autorisiert. Neu
31
+ * sind dabei [HpsCode.sendReversal] (Storno nachschicken, siehe
32
+ * `payments.ts`) und die Schreibweise: TECS fuehrt `0055`, das Terminal
33
+ * sendet `55` -- [normalizeHpsCode] macht beides zu einem Code.
34
+ *
35
+ * **Einen neuen Code aufnehmen:** Eintrag hier oder in `tecs-codes.ts`,
36
+ * derselbe im Dart-Zwilling, `npm run fixtures:hobex-hps-codes`; ein neuer
37
+ * Grund braucht in jeder Kasse eine Uebersetzung.
28
38
  */
39
+ import { TECS_CODES } from './tecs-codes.js';
29
40
  /** Der Satz fuer den Bediener je Grund, deutsch. */
30
41
  export const HPS_REASON_HINTS = {
31
42
  approved: 'Vom Terminal genehmigt.',
43
+ approvedWithCondition: 'Das Terminal meldet eine Genehmigung mit Vorbehalt (etwa nur über einen Teilbetrag). Ob und in welcher Höhe belastet wurde, bitte am Terminalbeleg prüfen — nicht erneut kassieren, bevor es geklärt ist.',
32
44
  aborted: 'Der Vorgang wurde abgebrochen. Es wurde kein Geld bewegt.',
33
- noCard: 'Es wurde keine Karte vorgehalten. Es wurde kein Geld bewegt — '
34
- + 'bitte erneut versuchen.',
35
- cardReadFailed: 'Die Karte konnte nicht gelesen werden. Es wurde kein Geld bewegt — '
36
- + 'bitte erneut versuchen, notfalls die Karte stecken statt auflegen.',
37
- cardDeclined: 'Die Karte wurde vom Terminal abgelehnt. Es wurde kein Geld bewegt '
38
- + '— bitte eine andere Karte oder Zahlungsart verwenden.',
39
- wrongPin: 'Die PIN war falsch. Es wurde kein Geld bewegt — bitte erneut '
40
- + 'versuchen.',
41
- amountInvalid: 'Das Terminal nimmt diesen Betrag nicht an. Es wurde kein Geld '
42
- + 'bewegt.',
43
- tipNotSelected: 'Das Trinkgeld wurde nicht rechtzeitig gewählt. Es wurde kein Geld '
44
- + 'bewegt — bitte erneut versuchen.',
45
- terminalBusy: 'Das Terminal ist noch mit einem anderen Vorgang beschäftigt. Es '
46
- + 'wurde kein Geld bewegt — kurz warten und erneut versuchen.',
47
- terminalBlocked: 'Das Terminal ist gesperrt. Es wurde kein Geld bewegt — bitte hobex '
48
- + 'kontaktieren.',
49
- terminalSetup: 'Das Terminal ist nicht richtig eingerichtet. Es wurde kein Geld '
50
- + 'bewegt — bitte die Terminal-ID in den Einstellungen prüfen, sonst '
51
- + 'hobex kontaktieren.',
52
- terminalFault: 'Das Terminal meldet eine Störung. Es wurde kein Geld bewegt — '
53
- + 'bitte das Terminal neu starten und erneut versuchen.',
54
- requestRejected: 'Das Terminal hat die Anfrage abgewiesen. Es wurde kein Geld bewegt '
55
- + '— tritt das wieder auf, bitte den Support kontaktieren.',
56
- invalidTransaction: 'Das Terminal kennt die ursprüngliche Zahlung nicht. Es wurde kein '
57
- + 'Geld bewegt.',
58
- refundPassword: 'Das Passwort für die Gutschrift war falsch oder wurde nicht '
59
- + 'eingegeben. Es wurde nichts ausgezahlt.',
60
- refundDisabled: 'Gutschriften sind an diesem Terminal abgeschaltet. Es wurde nichts '
61
- + 'ausgezahlt — bitte hobex kontaktieren.',
62
- hostTimeoutReversed: 'hobex hat nicht rechtzeitig geantwortet, das Terminal hat den '
63
- + 'Vorgang selbst storniert. Es wird kein Geld bewegt — bitte erneut '
64
- + 'versuchen.',
65
- hostFault: 'Die Verbindung zwischen Terminal und hobex ist gestört. Ob die '
66
- + 'Karte belastet wurde, weiß das Terminal nicht — bitte nicht erneut '
67
- + 'kassieren, bevor es geklärt ist.',
68
- internalError: 'Das Terminal meldet einen internen Fehler. Ob die Karte belastet '
69
- + 'wurde, ist unklar — bitte nicht erneut kassieren, bevor es geklärt '
70
- + 'ist.',
45
+ noCard: 'Es wurde keine Karte vorgehalten. Es wurde kein Geld bewegt — bitte erneut versuchen.',
46
+ cardReadFailed: 'Die Karte konnte nicht gelesen werden. Es wurde kein Geld bewegt — bitte erneut versuchen, notfalls die Karte stecken statt auflegen.',
47
+ cardDeclined: 'Die Karte wurde vom Terminal abgelehnt. Es wurde kein Geld bewegt — bitte eine andere Karte oder Zahlungsart verwenden.',
48
+ issuerDeclined: 'Die Zahlung wurde von der Bank abgelehnt. Es wurde kein Geld bewegt — bitte eine andere Karte oder Zahlungsart verwenden.',
49
+ cardBlocked: 'Die Karte ist gesperrt. Es wurde kein Geld bewegt — bitte eine andere Karte oder Zahlungsart verwenden.',
50
+ cardExpired: 'Die Karte ist abgelaufen. Es wurde kein Geld bewegt — bitte eine andere Karte oder Zahlungsart verwenden.',
51
+ insufficientFunds: 'Das Konto ist nicht gedeckt oder das Kartenlimit ist erreicht. Es wurde kein Geld bewegt — bitte eine andere Karte oder Zahlungsart verwenden.',
52
+ wrongPin: 'Die PIN war falsch. Es wurde kein Geld bewegt — bitte erneut versuchen.',
53
+ pinTriesExceeded: 'Die PIN wurde zu oft falsch eingegeben. Es wurde kein Geld bewegt — bitte eine andere Karte oder Zahlungsart verwenden.',
54
+ pinRequired: 'Die Bank verlangt die PIN. Es wurde kein Geld bewegt — bitte erneut versuchen, die Karte stecken und die PIN eingeben.',
55
+ amountInvalid: 'Das Terminal nimmt diesen Betrag nicht an. Es wurde kein Geld bewegt.',
56
+ tipNotSelected: 'Das Trinkgeld wurde nicht rechtzeitig gewählt. Es wurde kein Geld bewegt — bitte erneut versuchen.',
57
+ terminalBusy: 'Das Terminal ist noch mit einem anderen Vorgang beschäftigt. Es wurde kein Geld bewegt — kurz warten und erneut versuchen.',
58
+ terminalBlocked: 'Das Terminal ist gesperrt. Es wurde kein Geld bewegt — bitte hobex kontaktieren.',
59
+ terminalSetup: 'Das Terminal ist nicht richtig eingerichtet. Es wurde kein Geld bewegt — bitte die Terminal-ID in den Einstellungen prüfen, sonst hobex kontaktieren.',
60
+ acquirerSetup: 'hobex nimmt dieses Terminal oder diesen Händler so nicht an. Es wurde kein Geld bewegt — bitte hobex kontaktieren.',
61
+ terminalFault: 'Das Terminal meldet eine Störung. Es wurde kein Geld bewegt — bitte das Terminal neu starten und erneut versuchen.',
62
+ requestRejected: 'Das Terminal hat die Anfrage abgewiesen. Es wurde kein Geld bewegt — tritt das wieder auf, bitte den Support kontaktieren.',
63
+ hostRejected: 'hobex hat die Zahlung abgewiesen. Es wurde kein Geld bewegt — bitte erneut versuchen; tritt das wieder auf, hobex kontaktieren.',
64
+ hostUnavailable: 'Die Bank oder hobex ist gerade nicht erreichbar. Es wurde kein Geld bewegt — bitte später erneut versuchen oder eine andere Zahlungsart verwenden.',
65
+ invalidTransaction: 'Das Terminal kennt die ursprüngliche Zahlung nicht. Es wurde kein Geld bewegt.',
66
+ refundPassword: 'Das Passwort für die Gutschrift war falsch oder wurde nicht eingegeben. Es wurde nichts ausgezahlt.',
67
+ refundDisabled: 'Gutschriften sind an diesem Terminal abgeschaltet. Es wurde nichts ausgezahlt — bitte hobex kontaktieren.',
68
+ refundRejected: 'Die Gutschrift wurde abgewiesen (Betrag zu hoch, bereits erstattet oder zu viele Versuche). Es wurde nichts ausgezahlt.',
69
+ hostTimeoutReversed: 'hobex hat nicht rechtzeitig geantwortet, das Terminal hat den Vorgang selbst storniert. Es wird kein Geld bewegt — bitte erneut versuchen.',
70
+ reversedByHost: 'Die Zahlung wurde wegen einer Störung automatisch storniert. Es wurde kein Geld bewegt — bitte erneut versuchen.',
71
+ voidedAfterHostFault: 'hobex hat nicht sauber geantwortet, die Zahlung wurde deshalb sicherheitshalber storniert. Es wurde kein Geld bewegt — bitte erneut versuchen.',
72
+ hostFault: 'Die Verbindung zwischen Terminal und hobex ist gestört. Ob die Karte belastet wurde, weiß das Terminal nicht — bitte nicht erneut kassieren, bevor es geklärt ist.',
73
+ hostTimeout: 'hobex hat nicht rechtzeitig geantwortet. Ob die Karte belastet wurde, weiß das Terminal nicht — bitte nicht erneut kassieren, bevor es geklärt ist.',
74
+ internalError: 'Das Terminal meldet einen internen Fehler. Ob die Karte belastet wurde, ist unklar — bitte nicht erneut kassieren, bevor es geklärt ist.',
71
75
  canceled: 'Die Zahlung ist aufgehoben.',
72
- notAbortable: 'Der Vorgang ist bereits abgeschlossen und lässt sich nicht mehr '
73
- + 'abbrechen.',
76
+ cancelDenied: 'Die Zahlung lässt sich nicht mehr aufheben und bleibt belastet — bitte stattdessen eine Gutschrift ausführen.',
77
+ originalDeclined: 'Die ursprüngliche Zahlung war abgelehnt; es gibt nichts aufzuheben.',
78
+ notAbortable: 'Der Vorgang ist bereits abgeschlossen und lässt sich nicht mehr abbrechen.',
74
79
  noStatement: 'Das Terminal hat zu diesem Vorgang keine Auskunft.',
75
- technicalError: 'Das Terminal meldet einen technischen Fehler; über den Vorgang '
76
- + 'sagt das nichts.',
80
+ technicalError: 'Das Terminal meldet einen technischen Fehler; über den Vorgang sagt das nichts.',
77
81
  unknown: 'Das Terminal nennt einen Code, dessen Bedeutung nicht bekannt ist.',
78
82
  };
79
83
  /**
80
- * Die beiden Gruende hinter einem Code mit `effect: 'hostUncertain'`: ob Geld
81
- * geflossen ist, weiss das Terminal nicht.
84
+ * Die Gruende hinter einem Code mit `effect: 'hostUncertain'`: ob (und wie
85
+ * viel) Geld geflossen ist, weiss das Terminal nicht.
82
86
  */
83
87
  export function isHostUncertainReason(reason) {
84
- return reason === 'hostFault' || reason === 'internalError';
88
+ return reason === 'approvedWithCondition'
89
+ || reason === 'hostFault'
90
+ || reason === 'hostTimeout'
91
+ || reason === 'internalError';
85
92
  }
86
- function code(c, title, meaning, effect, reason, source, rejectsRequest = false) {
87
- return { code: c, title, meaning, conclusive: effect === 'conclusive', effect, reason, source, rejectsRequest };
93
+ function code(c, title, meaning, effect, reason, source, rejectsRequest = false, extra = {}) {
94
+ return {
95
+ code: c,
96
+ title,
97
+ meaning,
98
+ conclusive: effect === 'conclusive',
99
+ effect,
100
+ reason,
101
+ source,
102
+ rejectsRequest,
103
+ sendReversal: extra.sendReversal ?? false,
104
+ tecsTitle: extra.tecsTitle ?? null,
105
+ };
88
106
  }
89
107
  /**
90
108
  * Die vollstaendige Codetabelle — Vertrag mit dem Dart-Zwilling
@@ -109,19 +127,20 @@ function code(c, title, meaning, effect, reason, source, rejectsRequest = false)
109
127
  * dokumentierten aufsteigend -- identisch mit dem Dart-Zwilling.
110
128
  */
111
129
  export const HPS_CODES = [
112
- code('0', 'Authorized', 'genehmigt', 'conclusive', 'approved', 'measuredAndDocumented'),
130
+ code('0', 'Authorized', 'genehmigt', 'conclusive', 'approved', 'measuredAndDocumented', false, { tecsTitle: 'Approved Transaction / OK' }),
113
131
  code('9002', 'Invalid Transaction', 'ungueltiger Vorgang -- das Terminal hat den Vorgang selbst als '
114
- + 'unzulaessig verworfen, bevor irgendetwas in Bewegung kam', 'conclusive', 'invalidTransaction', 'measured', true),
132
+ + 'unzulaessig verworfen, bevor irgendetwas in Bewegung kam', 'conclusive', 'invalidTransaction', 'measuredAndDocumented', true, { tecsTitle: 'Invalid Transaction' }),
115
133
  code('9011', 'Transaction Canceled', 'aufgehoben ("Transaction Canceled") -- der Vorgang unter dieser '
116
- + 'Kennung wurde storniert', 'conclusive', 'canceled', 'measured'),
134
+ + 'Kennung wurde storniert', 'conclusive', 'canceled', 'measuredAndDocumented', false, { tecsTitle: 'Transaction cancelled' }),
117
135
  code('9027', 'Original Tx not found', 'keine Aussage -- steht gleichermassen fuer "nie gesehen", "laeuft '
118
- + 'gerade", "Karte nicht aufgelegt" und "abgebrochen"', 'noStatement', 'noStatement', 'measured'),
136
+ + 'gerade", "Karte nicht aufgelegt" und "abgebrochen"', 'noStatement', 'noStatement', 'measuredAndDocumented', false, { tecsTitle: 'Original Transaction not found' }),
119
137
  code('9900', 'Technical Error Database', '"Technical Error Database" -- gemessen im Zusammenhang mit einer '
120
- + 'nicht rein numerischen Kennung; keine Aussage ueber den Vorgang '
121
- + 'selbst', 'noStatement', 'technicalError', 'measured'),
138
+ + 'nicht rein numerischen Kennung, NACHDEM die Karte verarbeitet war; '
139
+ + 'laut TECS ein Datenbankfehler im Backend -- ob belastet wurde, ist '
140
+ + 'offen', 'hostUncertain', 'internalError', 'measuredAndDocumented', false, { tecsTitle: 'Technical Error: Database (General)' }),
122
141
  code('9003', 'Invalid Amount', '"Invalid Amount" -- der Betrag wird abgewiesen, BEVOR eine Karte '
123
142
  + 'verlangt wird (28.08.2026: 99999,99 EUR, Antwort nach 15,7 s ohne '
124
- + 'Kartenaufforderung); nichts belastet', 'conclusive', 'amountInvalid', 'measured'),
143
+ + 'Kartenaufforderung); nichts belastet', 'conclusive', 'amountInvalid', 'measuredAndDocumented', false, { tecsTitle: 'Invalid Amount' }),
125
144
  code('100002', 'Aborted', 'abgebrochen ("Aborted") -- ueber die Kasse oder am Terminal; '
126
145
  + 'nichts belastet', 'conclusive', 'aborted', 'measuredAndDocumented'),
127
146
  code('100003', 'Card not present', 'Karte nicht aufgelegt ("Card not present") -- innerhalb der Frist '
@@ -136,7 +155,7 @@ export const HPS_CODES = [
136
155
  + 'gemessene Host-Ablehnung ueberhaupt (02.09.2026 im Betrieb, TID '
137
156
  + '3556988, HPS 1.11.4, Firmware 2.3.9): die Zahlung antwortete '
138
157
  + 'direkt damit, die Statusabfrage danach elfmal in Folge ebenso -- '
139
- + 'eine Host-Ablehnung bleibt am Terminal abrufbar; nichts belastet', 'conclusive', 'wrongPin', 'measured'),
158
+ + 'eine Host-Ablehnung bleibt am Terminal abrufbar; nichts belastet', 'conclusive', 'wrongPin', 'measuredAndDocumented', false, { tecsTitle: 'Incorrect PIN' }),
140
159
  // ---- ab hier: Antwortcodeliste von hobex, erhalten 11.09.2026 ----
141
160
  code('100001', 'Bad Request', 'fehlerhafte Anfrage der Kasse ("Bad Request") -- Abweisung vor dem '
142
161
  + 'Kartenfluss; nichts belastet', 'conclusive', 'requestRejected', 'documented', true),
@@ -148,10 +167,10 @@ export const HPS_CODES = [
148
167
  + 'Betrieb danach dauerhaft 9027; nichts belastet', 'conclusive', 'cardReadFailed', 'documented'),
149
168
  code('100006', 'Communication with TecsXml failed', 'keine Verbindung zum hobex-Host ("Communication with TecsXml '
150
169
  + 'failed") -- das Terminal storniert NICHT selbst; ob beim Host '
151
- + 'etwas angekommen ist, weiss das Terminal nicht', 'hostUncertain', 'hostFault', 'documented'),
170
+ + 'etwas angekommen ist, weiss das Terminal nicht', 'hostUncertain', 'hostFault', 'documented', false, { sendReversal: true }),
152
171
  code('100007', 'Processing of TecsXml step failed', 'Schritt beim hobex-Host gescheitert ("Processing of TecsXml step '
153
172
  + 'failed") -- das Terminal storniert NICHT selbst; ob beim Host '
154
- + 'belastet wurde, weiss das Terminal nicht', 'hostUncertain', 'hostFault', 'documented'),
173
+ + 'belastet wurde, weiss das Terminal nicht', 'hostUncertain', 'hostFault', 'documented', false, { sendReversal: true }),
155
174
  code('100008', 'Invalid TID', 'Terminal-Kennung passt nicht ("Invalid TID") -- die TID der '
156
175
  + 'Anfrage ist nicht die eingerichtete; Abweisung vor dem '
157
176
  + 'Kartenfluss, nichts belastet (am Geraet gemessen wurde dafuer '
@@ -184,18 +203,18 @@ export const HPS_CODES = [
184
203
  + 'IN_OPERATION; nichts belastet', 'conclusive', 'terminalBlocked', 'documented', true),
185
204
  code('100023', 'Invalid message type', 'ungueltige Antwort des hobex-Hosts ("Invalid message type") -- '
186
205
  + 'Nachricht und Antwortcode in den UserData ungueltig; ob beim Host '
187
- + 'belastet wurde, weiss das Terminal nicht', 'hostUncertain', 'hostFault', 'documented'),
206
+ + 'belastet wurde, weiss das Terminal nicht', 'hostUncertain', 'hostFault', 'documented', false, { sendReversal: true }),
188
207
  code('100024', 'Transaction completion has failed', 'Abschluss des Online-Vorgangs gescheitert ("Transaction completion '
189
208
  + 'has failed") -- NACH der Anfrage beim Host; ob belastet bleibt, '
190
- + 'weiss das Terminal nicht', 'hostUncertain', 'hostFault', 'documented'),
209
+ + 'weiss das Terminal nicht', 'hostUncertain', 'hostFault', 'documented', false, { sendReversal: true }),
191
210
  code('100025', 'Refund transactions are disabled', 'Gutschriften abgeschaltet ("Refund transactions are disabled") -- '
192
211
  + 'in der Geraeteeinstellung; nichts ausgezahlt', 'conclusive', 'refundDisabled', 'documented'),
193
212
  code('100026', 'Transaction was declined.', 'Host-Antwort passt nicht zur Karte ("Transaction was declined.") '
194
213
  + '-- Chip-Daten fuer eine Karte ohne Chip; das Terminal lehnt ab, ob '
195
- + 'der Host zuvor belastet hat, ist offen', 'hostUncertain', 'hostFault', 'documented'),
214
+ + 'der Host zuvor belastet hat, ist offen', 'hostUncertain', 'hostFault', 'documented', false, { sendReversal: true }),
196
215
  code('100027', 'Unsupported UserData in TecsXml Response', 'unbekannte Daten in der Antwort des hobex-Hosts ("Unsupported '
197
216
  + 'UserData in TecsXml Response") -- ob beim Host belastet wurde, '
198
- + 'weiss das Terminal nicht', 'hostUncertain', 'hostFault', 'documented'),
217
+ + 'weiss das Terminal nicht', 'hostUncertain', 'hostFault', 'documented', false, { sendReversal: true }),
199
218
  code('100028', 'Tip selection process has failed.', 'Trinkgeld nicht gewaehlt ("Tip selection process has failed.") -- '
200
219
  + 'nicht innerhalb der Frist; nichts belastet', 'conclusive', 'tipNotSelected', 'documented'),
201
220
  code('100029', 'Communication with TecsXml timeout', 'Zeitueberschreitung zum hobex-Host ("Communication with TecsXml '
@@ -207,6 +226,7 @@ export const HPS_CODES = [
207
226
  code('100999', 'Internal Error', 'interner Fehler des Terminals ("Internal Error") -- Sammelcode '
208
227
  + 'fuer jeden sonst nicht benannten Fehler, an jeder Stelle des '
209
228
  + 'Ablaufs moeglich; ob belastet wurde, ist offen', 'hostUncertain', 'internalError', 'documented'),
229
+ ...TECS_CODES,
210
230
  ];
211
231
  /**
212
232
  * @deprecated Seit 0.10.0 [HPS_CODES] -- die Tabelle fuehrt nicht mehr nur
@@ -328,9 +348,38 @@ export const TERMINAL_BUSY_HTTP_STATUS = 409;
328
348
  */
329
349
  export const NOT_FOUND_HTTP_STATUS = 404;
330
350
  const CODE_BY_ID = new Map(HPS_CODES.map((c) => [c.code, c]));
331
- /** Der Eintrag zu [code] in [HPS_CODES], oder `undefined`, wenn seine Bedeutung nicht feststeht. */
351
+ const PLATZHALTER = 'xx';
352
+ /** Eintraege, die eine ganze Familie abdecken (`81xx`), nach ihrem Praefix. */
353
+ const CODE_BY_PREFIX = new Map(HPS_CODES.filter((c) => c.code.endsWith(PLATZHALTER)).map((c) => [c.code.slice(0, -PLATZHALTER.length), c]));
354
+ /**
355
+ * Die Schreibweise, unter der [code] in [HPS_CODES] steht: ein rein
356
+ * numerischer Code ohne fuehrende Nullen (`0055` -> `55`, `0000` -> `0`),
357
+ * jeder andere unveraendert. Ohne diese Angleichung waere ein `0000` kein
358
+ * `0` -- und damit eine ABLEHNUNG einer genehmigten Zahlung.
359
+ * [parseHpsTransactionResponse] gleicht deshalb schon beim Einlesen an.
360
+ */
361
+ export function normalizeHpsCode(code) {
362
+ const c = code.trim();
363
+ if (!/^\d+$/.test(c))
364
+ return c;
365
+ const ohneNullen = c.replace(/^0+/, '');
366
+ return ohneNullen === '' ? '0' : ohneNullen;
367
+ }
368
+ /**
369
+ * Der Eintrag zu [code] in [HPS_CODES], oder `undefined`, wenn seine
370
+ * Bedeutung nicht feststeht. Findet beide Schreibweisen
371
+ * ([normalizeHpsCode]) und die Familien mit Platzhalter (`8105` -> `81xx`).
372
+ */
332
373
  export function hpsCodeInfo(code) {
333
- return code === undefined ? undefined : CODE_BY_ID.get(code);
374
+ if (code === undefined)
375
+ return undefined;
376
+ const c = normalizeHpsCode(code);
377
+ const genau = CODE_BY_ID.get(c);
378
+ if (genau)
379
+ return genau;
380
+ if (c.length === 4 && /^\d+$/.test(c))
381
+ return CODE_BY_PREFIX.get(c.slice(0, 2));
382
+ return undefined;
334
383
  }
335
384
  /**
336
385
  * Der Grund zu [code] -- `'unknown'` fuer einen Code ausserhalb der Tabelle,
@@ -374,7 +423,7 @@ export function parseHpsTransactionResponse(raw) {
374
423
  // responseCode kommt teils als Zahl, teils als Zeichenkette — siehe
375
424
  // Zwilling. Ein leerer String traegt keine Aussage und wird wie ein
376
425
  // fehlendes Feld behandelt.
377
- responseCode: nonEmpty(stringify(r['responseCode'])),
426
+ responseCode: normalizedCode(stringify(r['responseCode'])),
378
427
  responseText: asString(r['responseText']),
379
428
  state: asString(r['state']),
380
429
  raw: r,
@@ -404,6 +453,14 @@ function stringify(v) {
404
453
  function nonEmpty(v) {
405
454
  return v === undefined || v === '' ? undefined : v;
406
455
  }
456
+ /**
457
+ * Der Ergebniscode in der Schreibweise der Tabelle ([normalizeHpsCode]):
458
+ * `0000` wird `0`. Der Rumpf in `raw` bleibt unveraendert.
459
+ */
460
+ function normalizedCode(v) {
461
+ const c = nonEmpty(v?.trim());
462
+ return c === undefined ? undefined : normalizeHpsCode(c);
463
+ }
407
464
  /** `true`, wenn der Vorgang genehmigt ist. */
408
465
  export function isApproved(res) {
409
466
  return res.responseCode === APPROVED_CODE;
@@ -426,7 +483,10 @@ export function isNotAbortable(res) {
426
483
  export function isNoStatement(res) {
427
484
  return res.responseCode === NO_STATEMENT_CODE;
428
485
  }
429
- /** `true`, wenn das Terminal einen technischen Fehler meldet (9900). */
486
+ /**
487
+ * `true`, wenn das Terminal einen technischen Fehler meldet (9900). Seit der
488
+ * TECS-Liste zugleich [isHostUncertain]: ob belastet wurde, ist offen.
489
+ */
430
490
  export function isTechnicalError(res) {
431
491
  return res.responseCode === TECHNICAL_ERROR_CODE;
432
492
  }
@@ -470,6 +530,14 @@ export function isConclusiveAsStatus(res) {
470
530
  export function isHostUncertain(res) {
471
531
  return hpsCodeInfo(res.responseCode)?.effect === 'hostUncertain';
472
532
  }
533
+ /**
534
+ * `true`, wenn zu diesem Code ein Storno nachzuschicken ist
535
+ * ([HpsCode.sendReversal]) -- der Host hat nicht oder nicht brauchbar
536
+ * geantwortet, etwa `9908`.
537
+ */
538
+ export function needsReversal(res) {
539
+ return hpsCodeInfo(res.responseCode)?.sendReversal ?? false;
540
+ }
473
541
  /**
474
542
  * `true`, wenn ein Ergebniscode VORHANDEN ist, dessen Bedeutung aber nicht
475
543
  * feststeht -- er fehlt in [HPS_CODES]. Ein Code, den dieses Modell schlicht
@@ -40,11 +40,12 @@ declare const DE: {
40
40
  readonly 'pdf.summe.gesamt.rechnung': "Gesamtbetrag";
41
41
  readonly 'pdf.summe.gesamt.gutschrift': "Gutschriftsbetrag";
42
42
  readonly 'steuer.kleinunternehmer': "Gemäß § 6 Abs. 1 Z 27 UStG wird keine Umsatzsteuer verrechnet (Kleinunternehmer).";
43
- readonly 'steuer.igLieferung': "Steuerfreie innergemeinschaftliche Lieferung (Art. 6 Abs. 1 UStG).";
43
+ readonly 'steuer.igLieferung.titel': "Steuerfreie innergemeinschaftliche Lieferung. ";
44
+ readonly 'steuer.igLieferung.text': "Gemäß Art. 6 Abs. 1 UStG (Art. 138 MwSt-RL 2006/112/EG). Die Erwerbsbesteuerung erfolgt durch den Abnehmer im Bestimmungsland.";
44
45
  readonly 'steuer.ausfuhr': "Steuerfreie Ausfuhrlieferung (§ 6 Abs. 1 Z 1 UStG).";
45
- readonly 'steuer.reverseCharge.titel': "Steuerfreie Leistung – Reverse Charge. ";
46
- readonly 'steuer.reverseCharge.text': "Übergang der Steuerschuldnerschaft auf den Leistungsempfänger gem. Art. 196 MwSt-RL 2006/112/EG (innergemeinschaftliche Leistung). Die Umsatzsteuer ist vom Leistungsempfänger im Bestimmungsland abzuführen.";
47
- readonly 'steuer.reverseCharge.uid': "UID Leistungserbringer: {verkaeufer} · UID Leistungsempfänger: {kaeufer}";
46
+ readonly 'steuer.reverseCharge.titel': "Steuerschuldnerschaft des Leistungsempfängers. ";
47
+ readonly 'steuer.reverseCharge.text': "Übergang der Steuerschuld gem. Art. 196 MwSt-RL 2006/112/EG (innergemeinschaftliche Leistung). Die Leistung ist nicht steuerfrei — die Umsatzsteuer schuldet und entrichtet der Leistungsempfänger in seinem Land.";
48
+ readonly 'steuer.uidZeile': "UID Leistungserbringer: {verkaeufer} · UID Leistungsempfänger: {kaeufer}";
48
49
  readonly 'pdf.zahlung.titel': "ZAHLUNGSINFORMATIONEN";
49
50
  readonly 'pdf.zahlung.empfaenger': "Empfänger";
50
51
  readonly 'pdf.zahlung.iban': "IBAN";
@@ -25,11 +25,18 @@ const DE = {
25
25
  'pdf.summe.gesamt.rechnung': 'Gesamtbetrag',
26
26
  'pdf.summe.gesamt.gutschrift': 'Gutschriftsbetrag',
27
27
  'steuer.kleinunternehmer': 'Gemäß § 6 Abs. 1 Z 27 UStG wird keine Umsatzsteuer verrechnet (Kleinunternehmer).',
28
- 'steuer.igLieferung': 'Steuerfreie innergemeinschaftliche Lieferung (Art. 6 Abs. 1 UStG).',
28
+ 'steuer.igLieferung.titel': 'Steuerfreie innergemeinschaftliche Lieferung. ',
29
+ 'steuer.igLieferung.text': 'Gemäß Art. 6 Abs. 1 UStG (Art. 138 MwSt-RL 2006/112/EG). Die Erwerbsbesteuerung erfolgt durch den Abnehmer im Bestimmungsland.',
29
30
  'steuer.ausfuhr': 'Steuerfreie Ausfuhrlieferung (§ 6 Abs. 1 Z 1 UStG).',
30
- 'steuer.reverseCharge.titel': 'Steuerfreie Leistung – Reverse Charge. ',
31
- 'steuer.reverseCharge.text': 'Übergang der Steuerschuldnerschaft auf den Leistungsempfänger gem. Art. 196 MwSt-RL 2006/112/EG (innergemeinschaftliche Leistung). Die Umsatzsteuer ist vom Leistungsempfänger im Bestimmungsland abzuführen.',
32
- 'steuer.reverseCharge.uid': 'UID Leistungserbringer: {verkaeufer} · UID Leistungsempfänger: {kaeufer}',
31
+ // Reverse Charge ist KEINE Steuerbefreiung: der Umsatz bleibt steuerpflichtig,
32
+ // nur die Steuer schuldet der Empfaenger (§ 11 Abs. 1a UStG; der Begriff steht
33
+ // wortgleich in Art. 226 Nr. 11a MwSt-RL). „Steuerfrei" waere sachlich falsch
34
+ // und erbraechte den vorgeschriebenen Hinweis nicht.
35
+ 'steuer.reverseCharge.titel': 'Steuerschuldnerschaft des Leistungsempfängers. ',
36
+ 'steuer.reverseCharge.text': 'Übergang der Steuerschuld gem. Art. 196 MwSt-RL 2006/112/EG (innergemeinschaftliche Leistung). Die Leistung ist nicht steuerfrei — die Umsatzsteuer schuldet und entrichtet der Leistungsempfänger in seinem Land.',
37
+ // Beide UID-Nummern: bei Reverse Charge § 11 Abs. 1a, bei der ig. Lieferung
38
+ // Art. 11 Abs. 2 UStG.
39
+ 'steuer.uidZeile': 'UID Leistungserbringer: {verkaeufer} · UID Leistungsempfänger: {kaeufer}',
33
40
  'pdf.zahlung.titel': 'ZAHLUNGSINFORMATIONEN',
34
41
  'pdf.zahlung.empfaenger': 'Empfänger',
35
42
  'pdf.zahlung.iban': 'IBAN',
@@ -207,11 +214,12 @@ const EN = {
207
214
  'pdf.summe.gesamt.rechnung': 'Total',
208
215
  'pdf.summe.gesamt.gutschrift': 'Credit total',
209
216
  'steuer.kleinunternehmer': 'No VAT charged pursuant to § 6 (1) no. 27 UStG (small business exemption).',
210
- 'steuer.igLieferung': 'VAT-exempt intra-Community supply (Art. 6 (1) UStG).',
217
+ 'steuer.igLieferung.titel': 'VAT-exempt intra-Community supply. ',
218
+ 'steuer.igLieferung.text': 'Pursuant to Art. 6 (1) UStG (Art. 138 of VAT Directive 2006/112/EC). The acquisition is taxed by the customer in the country of destination.',
211
219
  'steuer.ausfuhr': 'VAT-exempt export delivery (§ 6 (1) no. 1 UStG).',
212
- 'steuer.reverseCharge.titel': 'VAT-exempt service – reverse charge. ',
213
- 'steuer.reverseCharge.text': 'VAT liability transferred to the recipient pursuant to Art. 196 of VAT Directive 2006/112/EC (intra-Community service). VAT is to be accounted for by the recipient in the country of destination.',
214
- 'steuer.reverseCharge.uid': 'VAT ID supplier: {verkaeufer} · VAT ID recipient: {kaeufer}',
220
+ 'steuer.reverseCharge.titel': 'Reverse charge – VAT liability of the recipient. ',
221
+ 'steuer.reverseCharge.text': 'VAT liability transferred pursuant to Art. 196 of VAT Directive 2006/112/EC (intra-Community service). The service is not VAT-exempt — the recipient owes and accounts for the VAT in their country.',
222
+ 'steuer.uidZeile': 'VAT ID supplier: {verkaeufer} · VAT ID recipient: {kaeufer}',
215
223
  'pdf.zahlung.titel': 'PAYMENT DETAILS',
216
224
  'pdf.zahlung.empfaenger': 'Payee',
217
225
  'pdf.zahlung.iban': 'IBAN',
@@ -118,6 +118,13 @@ export interface PaymentInput {
118
118
  paidAt?: string;
119
119
  /** Zahlungskennung des Fremdsystems — gespeichert, aber nicht gedruckt. */
120
120
  reference?: string;
121
+ /**
122
+ * Die Zahlung erfolgte vor Ort beim Unternehmer (Terminal an der Kasse).
123
+ * Dann ist sie ein Barumsatz — auch mit Karte (§ 131b Abs. 1 Z 3 UStG) — und
124
+ * die Antwort traegt den Hinweis `cash_receipt_required`. Zu `transfer`
125
+ * passt das Kennzeichen nicht und wird abgewiesen.
126
+ */
127
+ onSite?: boolean;
121
128
  }
122
129
  export interface RecordPaymentRequest extends PaymentInput {
123
130
  /** Pflicht: ohne ihn bucht eine Wiederholung eine zweite Zahlung. */
@@ -52,10 +52,13 @@ export type EInvoiceFormat = (typeof EINVOICE_FORMATS)[number];
52
52
  export declare const INVOICE_LANGUAGES: readonly ["de", "en"];
53
53
  export type InvoiceLanguage = (typeof INVOICE_LANGUAGES)[number];
54
54
  /**
55
- * Wie eine Rechnung bezahlt wurde. Der Vermerk ist Buchhaltung, kein Beleg:
56
- * `cash` wird gebucht, die Antwort traegt dann zusaetzlich den Hinweis
57
- * `cash_receipt_required` — eine Barzahlung ist ein Barumsatz und braucht
58
- * einen Beleg (§ 132a BAO), bei Registrierkassenpflicht ueber die Kasse.
55
+ * Wie eine Rechnung bezahlt wurde. Der Vermerk ist Buchhaltung, kein Beleg.
56
+ *
57
+ * Ein **Barumsatz** ist nicht nur Bargeld: als Barzahlung gilt auch die Zahlung
58
+ * mit Bankomat- oder Kreditkarte **vor Ort** (§ 131b Abs. 1 Z 3 UStG), nicht
59
+ * aber dieselbe Karte im Internet. Weil `card` und `online` beides sein
60
+ * koennen, sagt das Feld `onSite` es dem Server — und nur dann traegt die
61
+ * Antwort den Hinweis `cash_receipt_required` (bei `cash` immer).
59
62
  */
60
63
  export declare const INVOICE_PAYMENT_METHODS: readonly ["transfer", "card", "online", "cash"];
61
64
  export type InvoicePaymentMethod = (typeof INVOICE_PAYMENT_METHODS)[number];
@@ -84,10 +84,13 @@ export const EINVOICE_FORMATS = ['ubl', 'cii'];
84
84
  */
85
85
  export const INVOICE_LANGUAGES = ['de', 'en'];
86
86
  /**
87
- * Wie eine Rechnung bezahlt wurde. Der Vermerk ist Buchhaltung, kein Beleg:
88
- * `cash` wird gebucht, die Antwort traegt dann zusaetzlich den Hinweis
89
- * `cash_receipt_required` — eine Barzahlung ist ein Barumsatz und braucht
90
- * einen Beleg (§ 132a BAO), bei Registrierkassenpflicht ueber die Kasse.
87
+ * Wie eine Rechnung bezahlt wurde. Der Vermerk ist Buchhaltung, kein Beleg.
88
+ *
89
+ * Ein **Barumsatz** ist nicht nur Bargeld: als Barzahlung gilt auch die Zahlung
90
+ * mit Bankomat- oder Kreditkarte **vor Ort** (§ 131b Abs. 1 Z 3 UStG), nicht
91
+ * aber dieselbe Karte im Internet. Weil `card` und `online` beides sein
92
+ * koennen, sagt das Feld `onSite` es dem Server — und nur dann traegt die
93
+ * Antwort den Hinweis `cash_receipt_required` (bei `cash` immer).
91
94
  */
92
95
  export const INVOICE_PAYMENT_METHODS = ['transfer', 'card', 'online', 'cash'];
93
96
  /**
@@ -288,6 +291,12 @@ export const PAYMENT_FELDER = Object.freeze({
288
291
  amountCents: { typ: 'integer', pflicht: false, min: 1, max: 100_000_000 },
289
292
  paidAt: datum(),
290
293
  reference: text(100),
294
+ /**
295
+ * Die Zahlung erfolgte **vor Ort** beim Unternehmer (Terminal an der Kasse,
296
+ * Zahlung per App am Tresen). Dann ist sie ein Barumsatz. Zu `transfer` passt
297
+ * das nicht — eine Ueberweisung erfolgt nie vor Ort — und wird abgewiesen.
298
+ */
299
+ onSite: { typ: 'boolean', pflicht: false },
291
300
  });
292
301
  export const RECHNUNG_ANFRAGEN = Object.freeze({
293
302
  createCustomer: {
@@ -381,6 +390,7 @@ export const RECHNUNG_ANFRAGEN = Object.freeze({
381
390
  amountCents: { typ: 'integer', pflicht: false, min: 1, max: 100_000_000 },
382
391
  paidAt: datum(),
383
392
  reference: text(100),
393
+ onSite: { typ: 'boolean', pflicht: false },
384
394
  },
385
395
  });
386
396
  /** Genau eines dieser Felder muss gesetzt sein (je Aufruf, je Gruppe). */