@kreiseck/kasseneck-api 0.21.0 → 0.23.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 +94 -0
- package/README.md +69 -2
- package/dist/cjs/client/errors.d.ts +0 -1
- package/dist/cjs/client/errors.js +24 -1
- package/dist/cjs/rechnung/api.d.ts +3 -1
- package/dist/cjs/rechnung/api.js +1 -0
- package/dist/cjs/rechnung/endpunkte.d.ts +12 -1
- package/dist/cjs/rechnung/endpunkte.js +56 -3
- package/dist/cjs/rechnung/index.d.ts +4 -2
- package/dist/cjs/rechnung/index.js +16 -1
- package/dist/cjs/rechnung/rechnen.d.ts +123 -0
- package/dist/cjs/rechnung/rechnen.js +327 -0
- package/dist/cjs/rechnung/summen.d.ts +42 -0
- package/dist/cjs/rechnung/summen.js +74 -0
- package/dist/cjs/rechnung/texte.d.ts +4 -0
- package/dist/cjs/rechnung/texte.js +8 -0
- package/dist/cjs/rechnung/typen.d.ts +51 -7
- package/dist/cjs/rechnung/vertrag.d.ts +1 -1
- package/dist/cjs/rechnung/vertrag.js +8 -0
- package/dist/esm/client/errors.d.ts +0 -1
- package/dist/esm/client/errors.js +24 -1
- package/dist/esm/rechnung/api.d.ts +3 -1
- package/dist/esm/rechnung/api.js +2 -1
- package/dist/esm/rechnung/endpunkte.d.ts +12 -1
- package/dist/esm/rechnung/endpunkte.js +55 -3
- package/dist/esm/rechnung/index.d.ts +4 -2
- package/dist/esm/rechnung/index.js +3 -1
- package/dist/esm/rechnung/rechnen.d.ts +123 -0
- package/dist/esm/rechnung/rechnen.js +316 -0
- package/dist/esm/rechnung/summen.d.ts +42 -0
- package/dist/esm/rechnung/summen.js +71 -0
- package/dist/esm/rechnung/texte.d.ts +4 -0
- package/dist/esm/rechnung/texte.js +8 -0
- package/dist/esm/rechnung/typen.d.ts +51 -7
- package/dist/esm/rechnung/vertrag.d.ts +1 -1
- package/dist/esm/rechnung/vertrag.js +8 -0
- package/fixtures/hobex-hps-codes.json +1 -1
- package/fixtures/kasse-texte.json +1 -1
- package/fixtures/oberflaeche.json +12 -2
- package/fixtures/position-aus-euro.json +107 -0
- package/fixtures/rechnung-api.schema.json +7 -2
- package/fixtures/rechnung-rechnen-zufall.json +8451 -0
- package/fixtures/rechnung-rechnen.json +304 -0
- package/fixtures/rechnung-summen.json +154 -0
- package/fixtures/rechnung-texte.json +9 -1
- package/package.json +13 -2
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Summen einer Rechnung vorab rechnen — genau so, wie der Server sie beim
|
|
3
|
+
* Ausstellen rechnet. Fuer Shops, die kassieren, bevor die Rechnung entsteht,
|
|
4
|
+
* und denselben Betrag brauchen, den die Rechnung spaeter ausweist.
|
|
5
|
+
*
|
|
6
|
+
* Die Regel (je USt-Satz, Betraege in Cent, kaufmaennisch gerundet):
|
|
7
|
+
*
|
|
8
|
+
* | Modus | Netto | USt | Brutto |
|
|
9
|
+
* |---------|--------------------------------|----------------------------|--------------------|
|
|
10
|
+
* | `net` | round(Σ Zeilen) | round(Σ Zeilen × Satz/100) | Netto + USt |
|
|
11
|
+
* | `gross` | round(B × 100 / (100 + Satz)) | B − Netto | B = round(Σ Zeilen)|
|
|
12
|
+
*
|
|
13
|
+
* Zeile = Einzelpreis × Menge × (1 − Rabatt/100), ungerundet. Im Brutto-Modus
|
|
14
|
+
* ist das Brutto der vereinbarte Preis und bleibt, wie die Zeilen es ergeben.
|
|
15
|
+
* Bei einem steuerfreien Steuerfall zaehlt jede Zeile zu 0 %.
|
|
16
|
+
*
|
|
17
|
+
* `fixtures/rechnung-summen.json` haelt die Prueffaelle; Backend und
|
|
18
|
+
* Dart-Zwilling pruefen gegen dieselbe Datei. Verbindlich bleibt, was der
|
|
19
|
+
* Server rechnet — mit `previewInvoice` laesst sich das vorab abfragen.
|
|
20
|
+
*/
|
|
21
|
+
import { STEUERFREIE_FAELLE } from './rechnen.js';
|
|
22
|
+
/** Aus dem Kern uebernommen, damit Kern und Huelle nie auseinanderlaufen. */
|
|
23
|
+
export { STEUERFREIE_FAELLE };
|
|
24
|
+
/** Auf Cent gerundete Euro — dieselbe Rundung wie am Server (halber Cent vom Nullpunkt weg). */
|
|
25
|
+
function euroRund(n) {
|
|
26
|
+
return ((n < 0 ? -1 : 1) * Math.round((Math.abs(n) + Number.EPSILON) * 100)) / 100;
|
|
27
|
+
}
|
|
28
|
+
const euroZuCent = (euro) => Math.round(euroRund(euro) * 100) || 0;
|
|
29
|
+
/** Ganze Cent kaufmaennisch; ein Gleitkomma-Rest wie x,4999999… zaehlt als halber Cent. */
|
|
30
|
+
function centRund(x) {
|
|
31
|
+
const betrag = Math.round(Math.abs(x) * 1e6) / 1e6;
|
|
32
|
+
return (x < 0 ? -1 : 1) * Math.round(betrag) || 0;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Netto, USt und Brutto einer Rechnung in Cent, je Satz absteigend — dieselbe
|
|
36
|
+
* Form wie `invoice.totals` in den Antworten.
|
|
37
|
+
*
|
|
38
|
+
* `taxScheme` ist der Steuerfall der Rechnung; ohne Angabe `normal`. Leitet der
|
|
39
|
+
* Server einen steuerfreien Fall ab (etwa eine ig. Lieferung), gehoert dieser
|
|
40
|
+
* Fall hierher — sonst rechnet die Funktion Steuer, die die Rechnung nicht
|
|
41
|
+
* ausweist.
|
|
42
|
+
*/
|
|
43
|
+
export function rechnungSummen(items, priceMode, taxScheme = 'normal') {
|
|
44
|
+
const steuerfrei = STEUERFREIE_FAELLE.includes(taxScheme);
|
|
45
|
+
const bruttoPreise = priceMode === 'gross' && !steuerfrei;
|
|
46
|
+
// Ungerundete Zeilen je Satz, in Euro wie am Server.
|
|
47
|
+
const jeSatz = new Map();
|
|
48
|
+
for (const p of items) {
|
|
49
|
+
const satz = steuerfrei ? 0 : p.vatRate;
|
|
50
|
+
const zeile = (p.unitPriceCents / 100) * p.quantity * (1 - (p.discountPct ?? 0) / 100);
|
|
51
|
+
jeSatz.set(satz, (jeSatz.get(satz) ?? 0) + zeile);
|
|
52
|
+
}
|
|
53
|
+
const byRate = [];
|
|
54
|
+
for (const [rate, roh] of jeSatz) {
|
|
55
|
+
let netCents;
|
|
56
|
+
let vatCents;
|
|
57
|
+
if (bruttoPreise) {
|
|
58
|
+
const grossCents = euroZuCent(roh);
|
|
59
|
+
netCents = centRund((grossCents * 100) / (100 + rate));
|
|
60
|
+
vatCents = grossCents - netCents;
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
netCents = euroZuCent(roh);
|
|
64
|
+
vatCents = euroZuCent((roh * rate) / 100);
|
|
65
|
+
}
|
|
66
|
+
byRate.push({ rate, netCents, vatCents, grossCents: netCents + vatCents });
|
|
67
|
+
}
|
|
68
|
+
byRate.sort((a, b) => b.rate - a.rate);
|
|
69
|
+
const summe = (feld) => byRate.reduce((s, r) => s + r[feld], 0);
|
|
70
|
+
return { netCents: summe('netCents'), vatCents: summe('vatCents'), grossCents: summe('grossCents'), byRate };
|
|
71
|
+
}
|
|
@@ -35,8 +35,12 @@ declare const DE: {
|
|
|
35
35
|
readonly 'pdf.tabelle.ust': "UST";
|
|
36
36
|
readonly 'pdf.tabelle.einzelNetto': "EINZEL NETTO";
|
|
37
37
|
readonly 'pdf.tabelle.betragNetto': "BETRAG NETTO";
|
|
38
|
+
readonly 'pdf.tabelle.einzelBrutto': "EINZEL BRUTTO";
|
|
39
|
+
readonly 'pdf.tabelle.betragBrutto': "BETRAG BRUTTO";
|
|
40
|
+
readonly 'pdf.position.rabatt': "abzüglich {prozent} % Rabatt";
|
|
38
41
|
readonly 'pdf.summe.zwischensumme': "Zwischensumme netto";
|
|
39
42
|
readonly 'pdf.summe.umsatzsteuer': "Umsatzsteuer {satz}%";
|
|
43
|
+
readonly 'pdf.summe.darinUst': "darin {satz}% USt von {netto} netto";
|
|
40
44
|
readonly 'pdf.summe.gesamt.rechnung': "Gesamtbetrag";
|
|
41
45
|
readonly 'pdf.summe.gesamt.gutschrift': "Gutschriftsbetrag";
|
|
42
46
|
readonly 'steuer.kleinunternehmer': "Gemäß § 6 Abs. 1 Z 27 UStG wird keine Umsatzsteuer verrechnet (Kleinunternehmer).";
|
|
@@ -20,8 +20,12 @@ const DE = {
|
|
|
20
20
|
'pdf.tabelle.ust': 'UST',
|
|
21
21
|
'pdf.tabelle.einzelNetto': 'EINZEL NETTO',
|
|
22
22
|
'pdf.tabelle.betragNetto': 'BETRAG NETTO',
|
|
23
|
+
'pdf.tabelle.einzelBrutto': 'EINZEL BRUTTO',
|
|
24
|
+
'pdf.tabelle.betragBrutto': 'BETRAG BRUTTO',
|
|
25
|
+
'pdf.position.rabatt': 'abzüglich {prozent} % Rabatt',
|
|
23
26
|
'pdf.summe.zwischensumme': 'Zwischensumme netto',
|
|
24
27
|
'pdf.summe.umsatzsteuer': 'Umsatzsteuer {satz}%',
|
|
28
|
+
'pdf.summe.darinUst': 'darin {satz}% USt von {netto} netto',
|
|
25
29
|
'pdf.summe.gesamt.rechnung': 'Gesamtbetrag',
|
|
26
30
|
'pdf.summe.gesamt.gutschrift': 'Gutschriftsbetrag',
|
|
27
31
|
'steuer.kleinunternehmer': 'Gemäß § 6 Abs. 1 Z 27 UStG wird keine Umsatzsteuer verrechnet (Kleinunternehmer).',
|
|
@@ -226,8 +230,12 @@ const EN = {
|
|
|
226
230
|
'pdf.tabelle.ust': 'VAT',
|
|
227
231
|
'pdf.tabelle.einzelNetto': 'UNIT NET',
|
|
228
232
|
'pdf.tabelle.betragNetto': 'AMOUNT NET',
|
|
233
|
+
'pdf.tabelle.einzelBrutto': 'UNIT GROSS',
|
|
234
|
+
'pdf.tabelle.betragBrutto': 'AMOUNT GROSS',
|
|
235
|
+
'pdf.position.rabatt': 'less {prozent}% discount',
|
|
229
236
|
'pdf.summe.zwischensumme': 'Subtotal net',
|
|
230
237
|
'pdf.summe.umsatzsteuer': 'VAT {satz}%',
|
|
238
|
+
'pdf.summe.darinUst': 'incl. {satz}% VAT on {netto} net',
|
|
231
239
|
'pdf.summe.gesamt.rechnung': 'Total',
|
|
232
240
|
'pdf.summe.gesamt.gutschrift': 'Credit total',
|
|
233
241
|
'steuer.kleinunternehmer': 'No VAT charged pursuant to § 6 (1) no. 27 UStG (small business exemption).',
|
|
@@ -146,7 +146,7 @@ export interface InvoicePayment {
|
|
|
146
146
|
}
|
|
147
147
|
/**
|
|
148
148
|
* Ein Hinweis zu einer erfolgreichen Antwort — kein Fehler, sondern etwas,
|
|
149
|
-
* das der Aufrufer wissen sollte
|
|
149
|
+
* das der Aufrufer wissen sollte. Die Codes stehen in `INVOICE_NOTICE_CODES`.
|
|
150
150
|
*/
|
|
151
151
|
export interface InvoiceNotice {
|
|
152
152
|
code: InvoiceNoticeCode;
|
|
@@ -156,7 +156,11 @@ export interface RecordPaymentResult {
|
|
|
156
156
|
invoice: Invoice;
|
|
157
157
|
payment: InvoicePayment;
|
|
158
158
|
replayed: boolean;
|
|
159
|
-
|
|
159
|
+
/**
|
|
160
|
+
* Hinweise zu dieser Zahlung — eine **Liste** wie bei `issueInvoice`
|
|
161
|
+
* (bis 0.21.0 ein einzelnes Objekt). Fehlt, wenn es keinen gibt.
|
|
162
|
+
*/
|
|
163
|
+
notice?: InvoiceNotice[];
|
|
160
164
|
}
|
|
161
165
|
export interface CancelInvoiceRequest {
|
|
162
166
|
idempotencyKey: string;
|
|
@@ -167,15 +171,23 @@ export interface CancelInvoiceRequest {
|
|
|
167
171
|
export interface CreditNoteRequest extends CancelInvoiceRequest {
|
|
168
172
|
items: InvoiceItemInput[];
|
|
169
173
|
}
|
|
174
|
+
/**
|
|
175
|
+
* Summen einer Rechnung in Cent, **immer positiv** — auch bei einer Gutschrift
|
|
176
|
+
* (`docType: 'GU'`); das Vorzeichen steht im Belegtyp, nicht im Betrag.
|
|
177
|
+
* Gerechnet wird je Satz wie in [rechnungSummen].
|
|
178
|
+
*/
|
|
170
179
|
export interface InvoiceTotals {
|
|
171
180
|
netCents: number;
|
|
172
181
|
vatCents: number;
|
|
173
182
|
grossCents: number;
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
183
|
+
/** Je USt-Satz, absteigend. `grossCents` ist `netCents + vatCents` (seit 0.22.0 mitgeliefert). */
|
|
184
|
+
byRate: InvoiceRateTotals[];
|
|
185
|
+
}
|
|
186
|
+
export interface InvoiceRateTotals {
|
|
187
|
+
rate: number;
|
|
188
|
+
netCents: number;
|
|
189
|
+
vatCents: number;
|
|
190
|
+
grossCents: number;
|
|
179
191
|
}
|
|
180
192
|
export interface EInvoiceStatus {
|
|
181
193
|
level: string;
|
|
@@ -299,6 +311,38 @@ export interface IssueResult {
|
|
|
299
311
|
*/
|
|
300
312
|
notice?: InvoiceNotice[];
|
|
301
313
|
}
|
|
314
|
+
/**
|
|
315
|
+
* Was `issueInvoice` mit dieser Anfrage ausstellen wuerde — ohne Nummer, ohne
|
|
316
|
+
* Dokument, ohne Zahlung. Der Probelauf prueft wie das Ausstellen (Einrichtung,
|
|
317
|
+
* Kunde, Steuerfall, Pflichtangaben, Marke, Zahlung) und scheitert mit
|
|
318
|
+
* denselben Fehlercodes.
|
|
319
|
+
*/
|
|
320
|
+
export interface InvoicePreview {
|
|
321
|
+
docType: 'RE';
|
|
322
|
+
/** Heutiger Wiener Tag — der Tag, den eine sofort ausgestellte Rechnung truege. */
|
|
323
|
+
invoiceDate: string;
|
|
324
|
+
dueDate: string | null;
|
|
325
|
+
customerId: string | null;
|
|
326
|
+
/** Der abgeleitete Steuerfall. */
|
|
327
|
+
taxScheme: TaxScheme;
|
|
328
|
+
/** Warum dieser Fall gilt (z. B. `customer_country_eu_with_vat_id`). */
|
|
329
|
+
taxSchemeReason: string;
|
|
330
|
+
reverseChargeReason: ReverseChargeReason | null;
|
|
331
|
+
taxCountry: string;
|
|
332
|
+
priceMode: PriceMode;
|
|
333
|
+
totals: InvoiceTotals;
|
|
334
|
+
language: InvoiceLanguage;
|
|
335
|
+
brand: {
|
|
336
|
+
id: string | null;
|
|
337
|
+
name: string | null;
|
|
338
|
+
} | null;
|
|
339
|
+
einvoice: EInvoiceStatus;
|
|
340
|
+
}
|
|
341
|
+
export interface PreviewResult {
|
|
342
|
+
preview: InvoicePreview;
|
|
343
|
+
/** Dieselben Hinweise, die das Ausstellen liefern wuerde. */
|
|
344
|
+
notice?: InvoiceNotice[];
|
|
345
|
+
}
|
|
302
346
|
export interface CancelResult {
|
|
303
347
|
creditNote: Invoice;
|
|
304
348
|
original: {
|
|
@@ -24,7 +24,7 @@ export type RechnungAufruf = (typeof RECHNUNG_AUFRUFE)[number];
|
|
|
24
24
|
* Stabile Fehlercodes. Das Fremdsystem verzweigt am Code, der Text darf sich
|
|
25
25
|
* aendern. `validation` traegt zusaetzlich `errors: [{ field, message }]`.
|
|
26
26
|
*/
|
|
27
|
-
export declare const INVOICE_ERROR_CODES: readonly ["validation", "idempotency_conflict", "module_inactive", "customer_not_found", "customer_exists", "short_code_taken", "short_code_immutable", "recipient_required", "invoice_requirements_missing", "invoice_not_found", "invoice_ambiguous", "not_cancellable", "partial_credit_exists", "credit_exceeds_invoice", "einvoice_incomplete", "invoice_api_not_enabled", "invoice_setup_incomplete", "language_not_allowed", "brand_not_found", "not_payable", "payment_exceeds_invoice", "tax_scheme_mismatch", "vat_rate_not_in_country", "reverse_charge_reason_required", "reverse_charge_threshold", "mixed_supply_not_allowed", "oss_not_enabled"];
|
|
27
|
+
export declare const INVOICE_ERROR_CODES: readonly ["validation", "idempotency_conflict", "module_inactive", "customer_not_found", "customer_exists", "short_code_taken", "short_code_immutable", "recipient_required", "invoice_requirements_missing", "invoice_not_found", "invoice_ambiguous", "not_cancellable", "partial_credit_exists", "credit_exceeds_invoice", "einvoice_incomplete", "invoice_api_not_enabled", "invoice_setup_incomplete", "language_not_allowed", "brand_not_found", "not_payable", "payment_exceeds_invoice", "tax_scheme_mismatch", "vat_rate_not_in_country", "reverse_charge_reason_required", "reverse_charge_threshold", "mixed_supply_not_allowed", "oss_not_enabled", "einvoice_unavailable", "amount_too_large"];
|
|
28
28
|
export type InvoiceErrorCode = (typeof INVOICE_ERROR_CODES)[number];
|
|
29
29
|
/** Gruende einer Gutschrift. Der Server druckt den deutschen Anzeigetext. */
|
|
30
30
|
export declare const CREDIT_NOTE_REASONS: readonly ["cancellation", "price_reduction", "return", "incorrect_invoice", "other"];
|
|
@@ -66,6 +66,8 @@ export const INVOICE_ERROR_CODES = [
|
|
|
66
66
|
'reverse_charge_threshold',
|
|
67
67
|
'mixed_supply_not_allowed',
|
|
68
68
|
'oss_not_enabled',
|
|
69
|
+
'einvoice_unavailable', // zu dieser Rechnung entsteht keine E-Rechnung, Grund in `reason`
|
|
70
|
+
'amount_too_large', // Betrag ueber der Grenze des Ganzzahlkerns; erst ab dessen Umstieg gesendet
|
|
69
71
|
];
|
|
70
72
|
/** Gruende einer Gutschrift. Der Server druckt den deutschen Anzeigetext. */
|
|
71
73
|
export const CREDIT_NOTE_REASONS = [
|
|
@@ -417,6 +419,12 @@ export const RECHNUNG_ANFRAGEN = Object.freeze({
|
|
|
417
419
|
* und ein sofort geholtes PDF Zahlungsinformationen traegt.
|
|
418
420
|
*/
|
|
419
421
|
payment: { typ: 'object', pflicht: false, felder: PAYMENT_FELDER },
|
|
422
|
+
/**
|
|
423
|
+
* Probelauf: alles pruefen und rechnen wie beim Ausstellen, aber nichts
|
|
424
|
+
* festschreiben — keine Nummer, kein Dokument, keine Zahlung, kein
|
|
425
|
+
* Idempotenz-Eintrag. Die Antwort traegt `preview` statt `invoice`.
|
|
426
|
+
*/
|
|
427
|
+
dryRun: { typ: 'boolean', pflicht: false },
|
|
420
428
|
},
|
|
421
429
|
cancelInvoice: {
|
|
422
430
|
idempotencyKey: idempotencyKey(true),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.
|
|
2
|
+
"version": "0.23.0",
|
|
3
3
|
"aufrufe": [
|
|
4
4
|
"activateCashregister",
|
|
5
5
|
"cancelInvoice",
|
|
@@ -410,7 +410,9 @@
|
|
|
410
410
|
"reverse_charge_reason_required",
|
|
411
411
|
"reverse_charge_threshold",
|
|
412
412
|
"mixed_supply_not_allowed",
|
|
413
|
-
"oss_not_enabled"
|
|
413
|
+
"oss_not_enabled",
|
|
414
|
+
"einvoice_unavailable",
|
|
415
|
+
"amount_too_large"
|
|
414
416
|
],
|
|
415
417
|
"invoiceLanguages": [
|
|
416
418
|
"de",
|
|
@@ -519,6 +521,14 @@
|
|
|
519
521
|
"listBrands",
|
|
520
522
|
"recordInvoicePayment"
|
|
521
523
|
],
|
|
524
|
+
"steuerfreieFaelle": [
|
|
525
|
+
"smallBusiness",
|
|
526
|
+
"reverseCharge",
|
|
527
|
+
"igLieferung",
|
|
528
|
+
"exportThirdCountry",
|
|
529
|
+
"domesticReverseCharge",
|
|
530
|
+
"outsideScope"
|
|
531
|
+
],
|
|
522
532
|
"taxSchemes": [
|
|
523
533
|
"normal",
|
|
524
534
|
"smallBusiness",
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
{
|
|
2
|
+
"beschreibung": "Prüffälle des Umwandlers positionAusEuro (Euro-Position → Ganzzahl-Form). Preis in Millionstel Euro, Menge in Tausendstel, Rabatt und Satz in Hundertstel-Prozent. Umgewandelt wird über den kürzesten Dezimaltext der Zahl, nie per Gleitkomma-Multiplikation — Gleitkomma-Rauschen wie 0,30000000000000004 muss abgelehnt werden.",
|
|
3
|
+
"regel": "Fehlende Menge und fehlender Satz gelten als 0 (so rechnet der Server ab). Ein fehlender Preis ist nie 0, sondern ein Grund (kein_zahlwert). Ein Minus am Preis wandert an die Menge.",
|
|
4
|
+
"faelle": [
|
|
5
|
+
{
|
|
6
|
+
"name": "Ueblicher Fall: Preis, Menge, Satz und Rabatt zusammen",
|
|
7
|
+
"item": { "unitPrice": 9.9, "quantity": 3, "vatRate": 10, "discountPct": 5, "description": "Reinigung" },
|
|
8
|
+
"erwartet": {
|
|
9
|
+
"ok": true,
|
|
10
|
+
"position": { "unitPriceMicros": 9900000, "quantityMilli": 3000, "discountBp": 500, "vatRateBp": 1000, "description": "Reinigung" }
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"name": "Schon Ganzzahl-Form kommt unveraendert zurueck",
|
|
15
|
+
"item": { "unitPriceMicros": 12345678, "quantityMilli": -500, "vatRateBp": 2000 },
|
|
16
|
+
"erwartet": {
|
|
17
|
+
"ok": true,
|
|
18
|
+
"position": { "unitPriceMicros": 12345678, "quantityMilli": -500, "vatRateBp": 2000 }
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"name": "Fehlende Menge gilt als 0",
|
|
23
|
+
"item": { "unitPrice": 5.5, "vatRate": 10 },
|
|
24
|
+
"erwartet": {
|
|
25
|
+
"ok": true,
|
|
26
|
+
"position": { "unitPriceMicros": 5500000, "quantityMilli": 0, "discountBp": 0, "vatRateBp": 1000 }
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"name": "Fehlender Satz gilt als 0",
|
|
31
|
+
"item": { "unitPrice": 3.33, "quantity": 4 },
|
|
32
|
+
"erwartet": {
|
|
33
|
+
"ok": true,
|
|
34
|
+
"position": { "unitPriceMicros": 3330000, "quantityMilli": 4000, "discountBp": 0, "vatRateBp": 0 }
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"name": "Fehlender Preis ist nie 0",
|
|
39
|
+
"item": { "quantity": 2, "vatRate": 20 },
|
|
40
|
+
"erwartet": { "ok": false, "feld": "unitPrice", "grund": "kein_zahlwert" }
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"name": "Negativer Preis wandert an die Menge",
|
|
44
|
+
"item": { "unitPrice": -7.5, "quantity": 3, "vatRate": 10 },
|
|
45
|
+
"erwartet": {
|
|
46
|
+
"ok": true,
|
|
47
|
+
"position": { "unitPriceMicros": 7500000, "quantityMilli": -3000, "discountBp": 0, "vatRateBp": 1000 }
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"name": "Negative Menge bleibt negativ",
|
|
52
|
+
"item": { "unitPrice": 4, "quantity": -6 },
|
|
53
|
+
"erwartet": {
|
|
54
|
+
"ok": true,
|
|
55
|
+
"position": { "unitPriceMicros": 4000000, "quantityMilli": -6000, "discountBp": 0, "vatRateBp": 0 }
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"name": "Gleitkomma-Rauschen am Preis (8,333333333333334) wird abgelehnt",
|
|
60
|
+
"item": { "unitPrice": 8.333333333333334 },
|
|
61
|
+
"erwartet": { "ok": false, "feld": "unitPrice", "grund": "nachkommastellen" }
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"name": "Gleitkomma-Rauschen am Rabatt (0,30000000000000004) wird abgelehnt",
|
|
65
|
+
"item": { "unitPrice": 1, "quantity": 1, "discountPct": 0.30000000000000004 },
|
|
66
|
+
"erwartet": { "ok": false, "feld": "discountPct", "grund": "nachkommastellen" }
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"name": "Menge mit 4 Nachkommastellen wird abgelehnt",
|
|
70
|
+
"item": { "unitPrice": 2, "quantity": 1.2345 },
|
|
71
|
+
"erwartet": { "ok": false, "feld": "quantity", "grund": "nachkommastellen" }
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"name": "Rabatt 12,345 % wird abgelehnt",
|
|
75
|
+
"item": { "unitPrice": 1, "quantity": 1, "discountPct": 12.345 },
|
|
76
|
+
"erwartet": { "ok": false, "feld": "discountPct", "grund": "nachkommastellen" }
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"name": "Text statt Zahl beim Preis",
|
|
80
|
+
"item": { "unitPrice": "9,99" },
|
|
81
|
+
"erwartet": { "ok": false, "feld": "unitPrice", "grund": "kein_zahlwert" }
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"name": "Satz 120 % liegt ausserhalb",
|
|
85
|
+
"item": { "unitPrice": 1, "quantity": 1, "vatRate": 120 },
|
|
86
|
+
"erwartet": { "ok": false, "feld": "vatRate", "grund": "ausserhalb" }
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"name": "Preis ueber 10^12 Mikro-Euro liegt ausserhalb",
|
|
90
|
+
"item": { "unitPrice": 1000001, "quantity": 1 },
|
|
91
|
+
"erwartet": { "ok": false, "feld": "unitPrice", "grund": "ausserhalb" }
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"name": "Satz mit zwei Nachkommastellen (4,95 %) ist verlustfrei",
|
|
95
|
+
"item": { "unitPrice": 1, "quantity": 1, "vatRate": 4.95 },
|
|
96
|
+
"erwartet": {
|
|
97
|
+
"ok": true,
|
|
98
|
+
"position": { "unitPriceMicros": 1000000, "quantityMilli": 1000, "discountBp": 0, "vatRateBp": 495 }
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"name": "Satz mit drei Nachkommastellen (4,955 %) wird abgelehnt",
|
|
103
|
+
"item": { "unitPrice": 1, "quantity": 1, "vatRate": 4.955 },
|
|
104
|
+
"erwartet": { "ok": false, "feld": "vatRate", "grund": "nachkommastellen" }
|
|
105
|
+
}
|
|
106
|
+
]
|
|
107
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"title": "Kasseneck Rechnungs-API",
|
|
4
4
|
"version": 1,
|
|
5
|
-
"paket": "0.
|
|
5
|
+
"paket": "0.23.0",
|
|
6
6
|
"aufrufe": {
|
|
7
7
|
"createCustomer": {
|
|
8
8
|
"anfrage": {
|
|
@@ -523,6 +523,9 @@
|
|
|
523
523
|
"method"
|
|
524
524
|
],
|
|
525
525
|
"additionalProperties": false
|
|
526
|
+
},
|
|
527
|
+
"dryRun": {
|
|
528
|
+
"type": "boolean"
|
|
526
529
|
}
|
|
527
530
|
},
|
|
528
531
|
"required": [
|
|
@@ -926,7 +929,9 @@
|
|
|
926
929
|
"reverse_charge_reason_required",
|
|
927
930
|
"reverse_charge_threshold",
|
|
928
931
|
"mixed_supply_not_allowed",
|
|
929
|
-
"oss_not_enabled"
|
|
932
|
+
"oss_not_enabled",
|
|
933
|
+
"einvoice_unavailable",
|
|
934
|
+
"amount_too_large"
|
|
930
935
|
],
|
|
931
936
|
"gruende": [
|
|
932
937
|
"cancellation",
|