@kreiseck/kasseneck-api 0.20.0 → 0.22.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 +59 -0
- package/README.md +37 -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 -3
- package/dist/cjs/rechnung/index.js +7 -1
- package/dist/cjs/rechnung/summen.d.ts +41 -0
- package/dist/cjs/rechnung/summen.js +81 -0
- package/dist/cjs/rechnung/texte.d.ts +19 -0
- package/dist/cjs/rechnung/texte.js +40 -0
- package/dist/cjs/rechnung/typen.d.ts +71 -9
- package/dist/cjs/rechnung/vertrag.d.ts +78 -3
- package/dist/cjs/rechnung/vertrag.js +82 -4
- 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 -3
- package/dist/esm/rechnung/index.js +3 -2
- package/dist/esm/rechnung/summen.d.ts +41 -0
- package/dist/esm/rechnung/summen.js +77 -0
- package/dist/esm/rechnung/texte.d.ts +19 -0
- package/dist/esm/rechnung/texte.js +40 -0
- package/dist/esm/rechnung/typen.d.ts +71 -9
- package/dist/esm/rechnung/vertrag.d.ts +78 -3
- package/dist/esm/rechnung/vertrag.js +81 -3
- package/fixtures/hobex-hps-codes.json +1 -1
- package/fixtures/kasse-texte.json +1 -1
- package/fixtures/oberflaeche.json +27 -4
- package/fixtures/rechnung-api.schema.json +42 -4
- package/fixtures/rechnung-summen.json +154 -0
- package/fixtures/rechnung-texte.json +39 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,65 @@ Was vor 0.7.0 geschah, steht in der Commit-Historie (`git log`); ab hier wird
|
|
|
4
4
|
es hier geführt. Ein Eintrag nennt die Änderung **und ihren Grund** —
|
|
5
5
|
nur der Grund überlebt den nächsten Umbau.
|
|
6
6
|
|
|
7
|
+
## 0.22.0
|
|
8
|
+
|
|
9
|
+
### Rechnung: Brutto bleibt Brutto, Hinweise kommen an, Probelauf
|
|
10
|
+
|
|
11
|
+
**Anlass:** Rückmeldung eines Shops zur Rechnungs-API (16.09.2026).
|
|
12
|
+
|
|
13
|
+
- **`issueInvoice` gab `notice` nicht weiter.** Der Typ sah die Liste vor, der
|
|
14
|
+
Aufruf übernahm sie nie — eine ig. Lieferung meldete deshalb nie, dass eine
|
|
15
|
+
Zusammenfassende Meldung fällig ist. Behoben.
|
|
16
|
+
- **`recordInvoicePayment` liefert `notice` jetzt als Liste** wie
|
|
17
|
+
`issueInvoice` (bisher ein einzelnes Objekt). **Bricht die Form:** wer
|
|
18
|
+
`result.notice.code` las, liest jetzt `result.notice?.[0]?.code`. Ein Server,
|
|
19
|
+
der noch ein Objekt schickt, wird zur Liste gemacht.
|
|
20
|
+
- **Neu: `rechnungSummen(items, priceMode, taxScheme?)`** rechnet die Summen so,
|
|
21
|
+
wie der Server sie ausstellt. Im Brutto-Modus ist das Brutto je Satz der
|
|
22
|
+
vereinbarte Preis: Netto = round(B × 100 / (100 + Satz)), USt = B − Netto.
|
|
23
|
+
Der Server rechnete bis dahin Netto und USt getrennt und setzte das Brutto neu
|
|
24
|
+
zusammen — 14,79 € + 15,00 € zu 20 % wurden 29,80 €. Die Prüffälle stehen in
|
|
25
|
+
`fixtures/rechnung-summen.json`; Server und Dart-Zwilling prüfen dagegen.
|
|
26
|
+
- **Neu: `previewInvoice(anfrage)`** — Probelauf von `issueInvoice`
|
|
27
|
+
(`dryRun: true` im Vertrag): prüft und rechnet wie das Ausstellen, schreibt
|
|
28
|
+
nichts fest und verbraucht den `idempotencyKey` nicht. Antwort `preview` mit
|
|
29
|
+
Summen, Steuerfall samt Grund, Sprache, Marke, E-Rechnung — dazu die Hinweise.
|
|
30
|
+
- **`totals.byRate[]` trägt `grossCents`.** Die Summen sind auch bei
|
|
31
|
+
Gutschriften positiv; das steht jetzt am Typ.
|
|
32
|
+
- **Texte:** `pdf.tabelle.einzelBrutto`, `pdf.tabelle.betragBrutto`,
|
|
33
|
+
`pdf.summe.darinUst` — das PDF einer Brutto-Rechnung zeigt Brutto-Spalten,
|
|
34
|
+
deren Zeilen die Summe ergeben. Dazu `pdf.position.rabatt`: ein Zeilenrabatt
|
|
35
|
+
stand bisher nirgends auf dem Blatt, „2 × 24,90 = 44,82" ging für den Leser
|
|
36
|
+
nicht auf.
|
|
37
|
+
- **Kennung als Text:** `getInvoice('…')` und `getCustomer('…')` werfen vor dem
|
|
38
|
+
Senden `KasseneckValidationError` (`scope: 'request'`). Bisher wurde der Text
|
|
39
|
+
Zeichen für Zeichen zu Feldern `0`, `1`, … und der Server konnte nur
|
|
40
|
+
„Bitte Eingaben prüfen." antworten.
|
|
41
|
+
- **Feldfehler in der Meldung:** `KasseneckApiError.message` hängt bis zu fünf
|
|
42
|
+
Einträge aus `errors[]` an (`[items[0].vatRate: …]`); `serverMessage` und
|
|
43
|
+
`details` bleiben unverändert.
|
|
44
|
+
|
|
45
|
+
## 0.21.0
|
|
46
|
+
|
|
47
|
+
### Rechnung: Kataloge für den abgeleiteten Steuerfall
|
|
48
|
+
|
|
49
|
+
**Anlass:** Der Server leitet den Steuerfall jetzt selbst ab, statt ihn zu
|
|
50
|
+
glauben (Kundenland, Kundenart, UID, Ware oder Leistung).
|
|
51
|
+
|
|
52
|
+
- `taxScheme` ist in `issueInvoice` **optional**; eine Angabe prüft der Server
|
|
53
|
+
gegen die Ableitung (`tax_scheme_mismatch`).
|
|
54
|
+
- Neue Fälle `domesticReverseCharge`, `oss` und `outsideScope`; Katalog der elf
|
|
55
|
+
Gründe für den Übergang der Steuerschuld im Inland
|
|
56
|
+
(`REVERSE_CHARGE_REASONS`, mit Fundstelle und Schwelle).
|
|
57
|
+
`oss` steht im Katalog, **kann aber noch nicht ausgestellt werden** —
|
|
58
|
+
die Ländersätze folgen.
|
|
59
|
+
- Positionen tragen `kind` (`goods`/`service`), weil ig. Lieferung und Reverse
|
|
60
|
+
Charge sich genau daran unterscheiden.
|
|
61
|
+
- `notice` ist an `issueInvoice` eine **Liste**: eine bar bezahlte ig. Lieferung
|
|
62
|
+
trägt zwei Hinweise zugleich.
|
|
63
|
+
- Die Rechnungssicht trägt `reverseChargeReason` und `taxCountry`; neuer
|
|
64
|
+
Befreiungstext für den nicht steuerbaren Umsatz (E-Rechnung, BR-O-10).
|
|
65
|
+
|
|
7
66
|
## 0.20.0
|
|
8
67
|
|
|
9
68
|
### Rechnung: richtige Steuerhinweise, Barumsatz auch mit Karte
|
package/README.md
CHANGED
|
@@ -547,6 +547,41 @@ await rechnungen.recordInvoicePayment({
|
|
|
547
547
|
`reference` wird gespeichert, aber **nicht gedruckt**; Kartendaten gehören
|
|
548
548
|
ohnehin nicht auf eine Rechnung.
|
|
549
549
|
|
|
550
|
+
**Vorab rechnen.** Wer kassiert, bevor die Rechnung entsteht, braucht den
|
|
551
|
+
Betrag, den die Rechnung später ausweist. `rechnungSummen` rechnet ihn genau
|
|
552
|
+
wie der Server; `previewInvoice` fragt den Server selbst — ein Probelauf, der
|
|
553
|
+
prüft wie das Ausstellen, aber nichts festschreibt und den `idempotencyKey`
|
|
554
|
+
nicht verbraucht:
|
|
555
|
+
|
|
556
|
+
```ts
|
|
557
|
+
import { rechnungSummen } from '@kreiseck/kasseneck-api/rechnung';
|
|
558
|
+
|
|
559
|
+
const posten = [
|
|
560
|
+
{ description: 'Maniküre', quantity: 1, unitPriceCents: 1479, vatRate: 20 as const },
|
|
561
|
+
{ description: 'Lack', quantity: 1, unitPriceCents: 1500, vatRate: 20 as const },
|
|
562
|
+
];
|
|
563
|
+
rechnungSummen(posten, 'gross');
|
|
564
|
+
// { netCents: 2483, vatCents: 496, grossCents: 2979, byRate: [{ rate: 20, … }] }
|
|
565
|
+
|
|
566
|
+
const anfrage = { idempotencyKey: `bestellung-${bestellnummer}`, customerId: kunde.id,
|
|
567
|
+
priceMode: 'gross' as const, serviceStart: '2026-09-16', items: posten };
|
|
568
|
+
const { preview, notice } = await rechnungen.previewInvoice(anfrage);
|
|
569
|
+
// preview.totals, preview.taxScheme, preview.taxSchemeReason — dann:
|
|
570
|
+
await rechnungen.issueInvoice(anfrage);
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
Im **Brutto-Modus** ist das Brutto je Satz der vereinbarte Preis: Netto =
|
|
574
|
+
round(B × 100 / (100 + Satz)), USt = B − Netto. Im **Netto-Modus** wird die USt
|
|
575
|
+
je Satz aus der Nettosumme gerundet. Gerundet wird kaufmännisch (halber Cent
|
|
576
|
+
aufwärts), je Satz, dann summiert. Die Prüffälle liegen in
|
|
577
|
+
`fixtures/rechnung-summen.json`. Die Summen sind auch bei Gutschriften positiv —
|
|
578
|
+
das Vorzeichen steht im Belegtyp (`docType: 'GU'`).
|
|
579
|
+
|
|
580
|
+
**Hinweise** (`notice`) sind immer eine Liste — bei `issueInvoice`,
|
|
581
|
+
`previewInvoice` und `recordInvoicePayment`. Eine ig. Lieferung trägt
|
|
582
|
+
`recapitulative_statement_due` (Zusammenfassende Meldung), eine bar bezahlte
|
|
583
|
+
Rechnung zusätzlich `cash_receipt_required`.
|
|
584
|
+
|
|
550
585
|
**Barumsatz ist nicht nur Bargeld.** Als Barzahlung gilt auch die Karte **vor
|
|
551
586
|
Ort** an der Kasse (§ 131b Abs. 1 Z 3 UStG) — dieselbe Karte im Internet
|
|
552
587
|
dagegen nicht. Weil `card` und `online` beides sein können, sagt es das
|
|
@@ -557,8 +592,8 @@ payment: { method: 'card', onSite: true } // Terminal an der Kasse
|
|
|
557
592
|
payment: { method: 'card' } // Kartenzahlung im Shop
|
|
558
593
|
```
|
|
559
594
|
|
|
560
|
-
Bei `cash` (immer) und bei `onSite: true` trägt die Antwort
|
|
561
|
-
`notice
|
|
595
|
+
Bei `cash` (immer) und bei `onSite: true` trägt die Antwort in der Liste
|
|
596
|
+
`notice` den Eintrag `cash_receipt_required`: ein Barumsatz braucht einen Beleg
|
|
562
597
|
(§ 132a BAO), bei Registrierkassenpflicht über die Registrierkasse — der
|
|
563
598
|
Vermerk an der Rechnung ersetzt ihn nicht. `transfer` mit `onSite` ist ein
|
|
564
599
|
Feldfehler, eine Überweisung erfolgt nicht vor Ort.
|
|
@@ -87,7 +87,6 @@ export declare function causeDigest(ursache: unknown, geheimnisse: readonly stri
|
|
|
87
87
|
* demselben Grund **keinen** Vorgabewert.
|
|
88
88
|
*/
|
|
89
89
|
export declare function fehlerDetails(daten: unknown, geheimnisse: readonly string[]): Record<string, unknown>;
|
|
90
|
-
/** Fachlicher Fehler: HTTP 200, aber `status: 'error'` im Rumpf. */
|
|
91
90
|
export declare class KasseneckApiError extends Error {
|
|
92
91
|
readonly name = "KasseneckApiError";
|
|
93
92
|
/** Aufgerufene Backend-Funktion, z. B. `createReceipt`. */
|
|
@@ -174,6 +174,29 @@ function sieben(wert, geheimnisse, tiefe) {
|
|
|
174
174
|
return raus;
|
|
175
175
|
}
|
|
176
176
|
/** Fachlicher Fehler: HTTP 200, aber `status: 'error'` im Rumpf. */
|
|
177
|
+
/**
|
|
178
|
+
* Die Feldfehler einer Pruefantwort (`errors: [{ field, message }]`) als
|
|
179
|
+
* Anhang der Fehlermeldung — sonst stuende im Log nur „Bitte Eingaben
|
|
180
|
+
* pruefen." und niemand wuesste, welches Feld gemeint ist. Hoechstens fuenf;
|
|
181
|
+
* vollstaendig bleiben sie in `details.errors`.
|
|
182
|
+
*/
|
|
183
|
+
function feldHinweis(details) {
|
|
184
|
+
const roh = details['errors'];
|
|
185
|
+
if (!Array.isArray(roh))
|
|
186
|
+
return '';
|
|
187
|
+
const teile = [];
|
|
188
|
+
for (const e of roh) {
|
|
189
|
+
if (e === null || typeof e !== 'object')
|
|
190
|
+
continue;
|
|
191
|
+
const { field, message } = e;
|
|
192
|
+
if (typeof field === 'string' && typeof message === 'string')
|
|
193
|
+
teile.push(`${field}: ${message}`);
|
|
194
|
+
}
|
|
195
|
+
if (!teile.length)
|
|
196
|
+
return '';
|
|
197
|
+
const mehr = teile.length > 5 ? ` (+${teile.length - 5} weitere)` : '';
|
|
198
|
+
return ` [${teile.slice(0, 5).join('; ')}${mehr}]`;
|
|
199
|
+
}
|
|
177
200
|
class KasseneckApiError extends Error {
|
|
178
201
|
name = 'KasseneckApiError';
|
|
179
202
|
/** Aufgerufene Backend-Funktion, z. B. `createReceipt`. */
|
|
@@ -197,7 +220,7 @@ class KasseneckApiError extends Error {
|
|
|
197
220
|
*/
|
|
198
221
|
details;
|
|
199
222
|
constructor(functionName, serverMessage, details = {}, code) {
|
|
200
|
-
super(`${functionName} fehlgeschlagen: ${serverMessage}`);
|
|
223
|
+
super(`${functionName} fehlgeschlagen: ${serverMessage}${feldHinweis(details)}`);
|
|
201
224
|
this.functionName = functionName;
|
|
202
225
|
this.serverMessage = serverMessage;
|
|
203
226
|
this.details = details;
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { type FetchLike } from '../client/transport.js';
|
|
8
8
|
import type { EInvoiceFormat, InvoiceLanguage } from './vertrag.js';
|
|
9
|
-
import type { Brand, CancelInvoiceRequest, CancelResult, CreditNoteRequest, CreditNoteResult, Customer, CustomerInput, CustomerPage, CustomerSearch, InvoiceDetail, InvoiceListQuery, InvoicePage, InvoiceSetupStatus, IssueInvoiceRequest, IssueResult, RecordPaymentRequest, RecordPaymentResult } from './typen.js';
|
|
9
|
+
import type { Brand, CancelInvoiceRequest, CancelResult, CreditNoteRequest, CreditNoteResult, Customer, CustomerInput, CustomerPage, CustomerSearch, InvoiceDetail, InvoiceListQuery, InvoicePage, InvoiceSetupStatus, IssueInvoiceRequest, IssueResult, PreviewResult, RecordPaymentRequest, RecordPaymentResult } from './typen.js';
|
|
10
10
|
export interface RechnungApiOptions {
|
|
11
11
|
/** `api_key` des Kontos (`kr_live_…` / `kr_test_…`). Gehoert auf einen Server. */
|
|
12
12
|
apiKey: string;
|
|
@@ -29,6 +29,8 @@ export interface RechnungApi {
|
|
|
29
29
|
updateCustomer(customerId: string, patch: Partial<CustomerInput>): Promise<Customer>;
|
|
30
30
|
searchCustomers(suche: CustomerSearch): Promise<CustomerPage>;
|
|
31
31
|
issueInvoice(anfrage: IssueInvoiceRequest): Promise<IssueResult>;
|
|
32
|
+
/** Probelauf: pruefen und rechnen wie `issueInvoice`, ohne auszustellen. */
|
|
33
|
+
previewInvoice(anfrage: IssueInvoiceRequest): Promise<PreviewResult>;
|
|
32
34
|
cancelInvoice(anfrage: CancelInvoiceRequest): Promise<CancelResult>;
|
|
33
35
|
createCreditNote(anfrage: CreditNoteRequest): Promise<CreditNoteResult>;
|
|
34
36
|
getInvoice(kennung: {
|
package/dist/cjs/rechnung/api.js
CHANGED
|
@@ -25,6 +25,7 @@ function createRechnungApi(optionen) {
|
|
|
25
25
|
updateCustomer: (id, patch) => (0, endpunkte_js_1.updateCustomer)(rufen, id, patch),
|
|
26
26
|
searchCustomers: (suche) => (0, endpunkte_js_1.searchCustomers)(rufen, suche),
|
|
27
27
|
issueInvoice: (anfrage) => (0, endpunkte_js_1.issueInvoice)(rufen, anfrage),
|
|
28
|
+
previewInvoice: (anfrage) => (0, endpunkte_js_1.previewInvoice)(rufen, anfrage),
|
|
28
29
|
cancelInvoice: (anfrage) => (0, endpunkte_js_1.cancelInvoice)(rufen, anfrage),
|
|
29
30
|
createCreditNote: (anfrage) => (0, endpunkte_js_1.createCreditNote)(rufen, anfrage),
|
|
30
31
|
getInvoice: (kennung) => (0, endpunkte_js_1.getInvoice)(rufen, kennung),
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import type { InternerBinaerTransport, InternerTransport } from '../client/aufrufe.js';
|
|
20
20
|
import type { EInvoiceFormat, InvoiceLanguage } from './vertrag.js';
|
|
21
|
-
import type { Brand, CancelInvoiceRequest, CancelResult, CreditNoteRequest, CreditNoteResult, Customer, CustomerInput, CustomerPage, CustomerSearch, InvoiceDetail, InvoiceListQuery, InvoicePage, InvoiceSetupStatus, IssueInvoiceRequest, IssueResult, RecordPaymentRequest, RecordPaymentResult } from './typen.js';
|
|
21
|
+
import type { Brand, CancelInvoiceRequest, CancelResult, CreditNoteRequest, CreditNoteResult, Customer, CustomerInput, CustomerPage, CustomerSearch, InvoiceDetail, InvoiceListQuery, InvoicePage, InvoiceSetupStatus, IssueInvoiceRequest, IssueResult, PreviewResult, RecordPaymentRequest, RecordPaymentResult } from './typen.js';
|
|
22
22
|
export declare function createCustomer(rufen: InternerTransport, customer: CustomerInput, optionen?: {
|
|
23
23
|
idempotencyKey?: string;
|
|
24
24
|
}): Promise<Customer>;
|
|
@@ -30,6 +30,17 @@ export declare function getCustomer(rufen: InternerTransport, kennung: {
|
|
|
30
30
|
export declare function updateCustomer(rufen: InternerTransport, customerId: string, patch: Partial<CustomerInput>): Promise<Customer>;
|
|
31
31
|
export declare function searchCustomers(rufen: InternerTransport, suche: CustomerSearch): Promise<CustomerPage>;
|
|
32
32
|
export declare function issueInvoice(rufen: InternerTransport, anfrage: IssueInvoiceRequest): Promise<IssueResult>;
|
|
33
|
+
/**
|
|
34
|
+
* Probelauf von `issueInvoice`: dieselbe Anfrage wird geprueft und gerechnet
|
|
35
|
+
* wie beim Ausstellen, aber nichts festgeschrieben. Die Antwort nennt Summen,
|
|
36
|
+
* Steuerfall, Sprache, Marke und die Hinweise — oder scheitert mit demselben
|
|
37
|
+
* Fehlercode, mit dem das Ausstellen scheitern wuerde.
|
|
38
|
+
*
|
|
39
|
+
* Der `idempotencyKey` wird nicht verbraucht: dieselbe Anfrage laesst sich
|
|
40
|
+
* danach unveraendert ausstellen. Verbindlich ist das Ausstellen — zwischen
|
|
41
|
+
* Probelauf und Ausstellen kann sich der Kunde oder das Konto aendern.
|
|
42
|
+
*/
|
|
43
|
+
export declare function previewInvoice(rufen: InternerTransport, anfrage: IssueInvoiceRequest): Promise<PreviewResult>;
|
|
33
44
|
export declare function cancelInvoice(rufen: InternerTransport, anfrage: CancelInvoiceRequest): Promise<CancelResult>;
|
|
34
45
|
export declare function createCreditNote(rufen: InternerTransport, anfrage: CreditNoteRequest): Promise<CreditNoteResult>;
|
|
35
46
|
export declare function getInvoice(rufen: InternerTransport, kennung: {
|
|
@@ -23,6 +23,7 @@ exports.getCustomer = getCustomer;
|
|
|
23
23
|
exports.updateCustomer = updateCustomer;
|
|
24
24
|
exports.searchCustomers = searchCustomers;
|
|
25
25
|
exports.issueInvoice = issueInvoice;
|
|
26
|
+
exports.previewInvoice = previewInvoice;
|
|
26
27
|
exports.cancelInvoice = cancelInvoice;
|
|
27
28
|
exports.createCreditNote = createCreditNote;
|
|
28
29
|
exports.getInvoice = getInvoice;
|
|
@@ -55,6 +56,33 @@ function seite(aufruf, daten, feld) {
|
|
|
55
56
|
return { eintraege: o[feld], nextCursor: typeof o['nextCursor'] === 'string' ? o['nextCursor'] : null };
|
|
56
57
|
}
|
|
57
58
|
const zahl = (wert) => (typeof wert === 'number' && Number.isFinite(wert) ? wert : 0);
|
|
59
|
+
/**
|
|
60
|
+
* Die Hinweise einer Antwort (`data.notice`) — immer eine Liste, `undefined`
|
|
61
|
+
* ohne Hinweis. Ein einzelnes Objekt (Server vor der Vereinheitlichung) wird
|
|
62
|
+
* zur Liste.
|
|
63
|
+
*
|
|
64
|
+
* Ein unbrauchbarer Eintrag wird uebergangen, nicht geworfen: der Aufruf hat
|
|
65
|
+
* schon gewirkt (die Rechnung ist ausgestellt, die Zahlung gebucht). Ein
|
|
66
|
+
* Fehler an dieser Stelle liesse den Aufrufer glauben, es sei nichts
|
|
67
|
+
* entstanden — und eine Wiederholung liefert die Hinweise nicht noch einmal.
|
|
68
|
+
*/
|
|
69
|
+
function hinweise(daten) {
|
|
70
|
+
const roh = objekt(daten)['notice'];
|
|
71
|
+
if (roh === undefined || roh === null)
|
|
72
|
+
return undefined;
|
|
73
|
+
const liste = (Array.isArray(roh) ? roh : [roh]).filter((h) => typeof objekt(h)['code'] === 'string' && typeof objekt(h)['message'] === 'string');
|
|
74
|
+
return liste.length ? liste : undefined;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Eine Kennung muss ein Objekt sein (`{ invoiceId }`, `{ customerId }` …). Ein
|
|
78
|
+
* blosser Text wuerde sonst Zeichen fuer Zeichen zu Feldern `0`, `1`, …
|
|
79
|
+
* zerlegt, und der Server koennte nur „Bitte Eingaben pruefen" antworten.
|
|
80
|
+
*/
|
|
81
|
+
function kennungVerlangen(aufruf, kennung, felder) {
|
|
82
|
+
if (kennung === null || typeof kennung !== 'object' || Array.isArray(kennung)) {
|
|
83
|
+
throw new errors_js_1.KasseneckValidationError(aufruf, `Kennung als Objekt erwartet: ${felder}`, 'request');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
58
86
|
/** Anfrageobjekt als Nutzlast — flache Kopie, damit der Aufrufer sein Objekt behaelt. */
|
|
59
87
|
const nutzlast = (anfrage) => ({ ...anfrage });
|
|
60
88
|
// ---- Kunden -----------------------------------------------------------------
|
|
@@ -66,6 +94,7 @@ async function createCustomer(rufen, customer, optionen = {}) {
|
|
|
66
94
|
return pflichtObjekt('createCustomer', daten, 'customer');
|
|
67
95
|
}
|
|
68
96
|
async function getCustomer(rufen, kennung) {
|
|
97
|
+
kennungVerlangen('getCustomer', kennung, '{ customerId } oder { externalId }');
|
|
69
98
|
const daten = await rufen('getCustomer', nutzlast(kennung));
|
|
70
99
|
return pflichtObjekt('getCustomer', daten, 'customer');
|
|
71
100
|
}
|
|
@@ -81,10 +110,32 @@ async function searchCustomers(rufen, suche) {
|
|
|
81
110
|
// ---- Rechnungen -------------------------------------------------------------
|
|
82
111
|
async function issueInvoice(rufen, anfrage) {
|
|
83
112
|
const daten = await rufen('issueInvoice', nutzlast(anfrage));
|
|
84
|
-
|
|
113
|
+
const ergebnis = {
|
|
85
114
|
invoice: pflichtObjekt('issueInvoice', daten, 'invoice'),
|
|
86
115
|
replayed: objekt(daten)['replayed'] === true,
|
|
87
116
|
};
|
|
117
|
+
const notice = hinweise(daten);
|
|
118
|
+
if (notice)
|
|
119
|
+
ergebnis.notice = notice;
|
|
120
|
+
return ergebnis;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Probelauf von `issueInvoice`: dieselbe Anfrage wird geprueft und gerechnet
|
|
124
|
+
* wie beim Ausstellen, aber nichts festgeschrieben. Die Antwort nennt Summen,
|
|
125
|
+
* Steuerfall, Sprache, Marke und die Hinweise — oder scheitert mit demselben
|
|
126
|
+
* Fehlercode, mit dem das Ausstellen scheitern wuerde.
|
|
127
|
+
*
|
|
128
|
+
* Der `idempotencyKey` wird nicht verbraucht: dieselbe Anfrage laesst sich
|
|
129
|
+
* danach unveraendert ausstellen. Verbindlich ist das Ausstellen — zwischen
|
|
130
|
+
* Probelauf und Ausstellen kann sich der Kunde oder das Konto aendern.
|
|
131
|
+
*/
|
|
132
|
+
async function previewInvoice(rufen, anfrage) {
|
|
133
|
+
const daten = await rufen('issueInvoice', { ...nutzlast(anfrage), dryRun: true });
|
|
134
|
+
const ergebnis = { preview: pflichtObjekt('issueInvoice', daten, 'preview') };
|
|
135
|
+
const notice = hinweise(daten);
|
|
136
|
+
if (notice)
|
|
137
|
+
ergebnis.notice = notice;
|
|
138
|
+
return ergebnis;
|
|
88
139
|
}
|
|
89
140
|
async function cancelInvoice(rufen, anfrage) {
|
|
90
141
|
const daten = await rufen('cancelInvoice', nutzlast(anfrage));
|
|
@@ -104,6 +155,7 @@ async function createCreditNote(rufen, anfrage) {
|
|
|
104
155
|
};
|
|
105
156
|
}
|
|
106
157
|
async function getInvoice(rufen, kennung) {
|
|
158
|
+
kennungVerlangen('getInvoice', kennung, '{ invoiceId } oder { number }');
|
|
107
159
|
const daten = await rufen('getInvoice', nutzlast(kennung));
|
|
108
160
|
return pflichtObjekt('getInvoice', daten, 'invoice');
|
|
109
161
|
}
|
|
@@ -159,8 +211,9 @@ async function recordInvoicePayment(rufen, anfrage) {
|
|
|
159
211
|
payment: pflichtObjekt('recordInvoicePayment', daten, 'payment'),
|
|
160
212
|
replayed: roh['replayed'] === true,
|
|
161
213
|
};
|
|
162
|
-
|
|
163
|
-
|
|
214
|
+
const notice = hinweise(daten);
|
|
215
|
+
if (notice)
|
|
216
|
+
ergebnis.notice = notice;
|
|
164
217
|
return ergebnis;
|
|
165
218
|
}
|
|
166
219
|
/** Die Marken des Kontos — die Kennung geht als `brandId` in `issueInvoice`. */
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
export { createRechnungApi, type RechnungApi, type RechnungApiOptions } from './api.js';
|
|
14
14
|
export { rechnungKeyAuth, type RechnungKeyAuthOptions } from './auth.js';
|
|
15
15
|
export { istRechnungFehler, rechnungFehlerCode, rechnungFeldFehler, type RechnungFeldFehler, } from './fehler.js';
|
|
16
|
-
export { cancelInvoice, createCreditNote, createCustomer, getCustomer, getInvoice, getInvoicePdf, getInvoiceSetupStatus, getInvoiceXml, issueInvoice, listBrands, listInvoices, recordInvoicePayment, searchCustomers, updateCustomer, } from './endpunkte.js';
|
|
17
|
-
export type { Brand, CancelInvoiceRequest, CancelResult, CreditNoteRequest, CreditNoteResult, Customer, CustomerInput, CustomerPage, CustomerSearch, EInvoiceStatus, Invoice, InvoiceDetail, InvoiceItem, InvoiceItemInput, InvoiceListQuery, InvoicePage, InvoiceRecipient, InvoiceSetupGap, InvoiceSetupStatus, InvoiceTotals, InvoiceNotice, InvoicePayment, IssueInvoiceRequest, IssueResult, PaymentInput, RecordPaymentRequest, RecordPaymentResult, } from './typen.js';
|
|
16
|
+
export { cancelInvoice, createCreditNote, createCustomer, getCustomer, getInvoice, getInvoicePdf, getInvoiceSetupStatus, getInvoiceXml, issueInvoice, listBrands, listInvoices, previewInvoice, recordInvoicePayment, searchCustomers, updateCustomer, } from './endpunkte.js';
|
|
17
|
+
export type { Brand, CancelInvoiceRequest, CancelResult, CreditNoteRequest, CreditNoteResult, Customer, CustomerInput, CustomerPage, CustomerSearch, EInvoiceStatus, Invoice, InvoiceDetail, InvoiceItem, InvoiceItemInput, InvoiceListQuery, InvoicePage, InvoiceRecipient, InvoiceSetupGap, InvoiceSetupStatus, InvoiceTotals, InvoiceNotice, InvoicePayment, InvoicePreview, InvoiceRateTotals, IssueInvoiceRequest, IssueResult, PaymentInput, PreviewResult, RecordPaymentRequest, RecordPaymentResult, } from './typen.js';
|
|
18
18
|
export { RECHNUNG_TEXTE, rechnungText, type RechnungTextSchluessel } from './texte.js';
|
|
19
|
-
export {
|
|
19
|
+
export { rechnungSummen, STEUERFREIE_FAELLE, type SummenPosition } from './summen.js';
|
|
20
|
+
export { RECHNUNG_VERTRAG_VERSION, RECHNUNG_AUFRUFE, INVOICE_ERROR_CODES, CREDIT_NOTE_REASONS, TAX_SCHEMES, PRICE_MODES, VAT_RATES, CUSTOMER_TYPES, INVOICE_LIST_STATUS, DOC_TYPES, EINVOICE_FORMATS, INVOICE_LANGUAGES, INVOICE_PAYMENT_METHODS, ITEM_KINDS, REVERSE_CHARGE_REASONS, INVOICE_NOTICE_CODES, INVOICE_UNITS, RECHNUNG_EINHEITEN_CODES, INVOICE_SETUP_REQUIREMENTS, KUNDE_FELDER, POSITION_FELDER, RECHNUNG_ANFRAGEN, RECHNUNG_GENAU_EINS, RECHNUNG_MINDESTENS_EINS, type RechnungAufruf, type InvoiceErrorCode, type CreditNoteReason, type TaxScheme, type PriceMode, type VatRatePercent, type CustomerType, type InvoiceListStatus, type DocType, type EInvoiceFormat, type InvoiceLanguage, type InvoicePaymentMethod, type ItemKind, type ReverseChargeReason, type InvoiceNoticeCode, type InvoiceUnit, type InvoiceSetupRequirement, type Format, type Feld, } from './vertrag.js';
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* Benutzung des Clients.
|
|
13
13
|
*/
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.RECHNUNG_MINDESTENS_EINS = exports.RECHNUNG_GENAU_EINS = exports.RECHNUNG_ANFRAGEN = exports.POSITION_FELDER = exports.KUNDE_FELDER = exports.INVOICE_SETUP_REQUIREMENTS = exports.RECHNUNG_EINHEITEN_CODES = exports.INVOICE_UNITS = exports.INVOICE_NOTICE_CODES = exports.INVOICE_PAYMENT_METHODS = exports.INVOICE_LANGUAGES = exports.EINVOICE_FORMATS = exports.DOC_TYPES = exports.INVOICE_LIST_STATUS = exports.CUSTOMER_TYPES = exports.VAT_RATES = exports.PRICE_MODES = exports.TAX_SCHEMES = exports.CREDIT_NOTE_REASONS = exports.INVOICE_ERROR_CODES = exports.RECHNUNG_AUFRUFE = exports.RECHNUNG_VERTRAG_VERSION = exports.rechnungText = exports.RECHNUNG_TEXTE = exports.updateCustomer = exports.searchCustomers = exports.recordInvoicePayment = exports.listInvoices = exports.listBrands = exports.issueInvoice = exports.getInvoiceXml = exports.getInvoiceSetupStatus = exports.getInvoicePdf = exports.getInvoice = exports.getCustomer = exports.createCustomer = exports.createCreditNote = exports.cancelInvoice = exports.rechnungFeldFehler = exports.rechnungFehlerCode = exports.istRechnungFehler = exports.rechnungKeyAuth = exports.createRechnungApi = void 0;
|
|
15
|
+
exports.RECHNUNG_MINDESTENS_EINS = exports.RECHNUNG_GENAU_EINS = exports.RECHNUNG_ANFRAGEN = exports.POSITION_FELDER = exports.KUNDE_FELDER = exports.INVOICE_SETUP_REQUIREMENTS = exports.RECHNUNG_EINHEITEN_CODES = exports.INVOICE_UNITS = exports.INVOICE_NOTICE_CODES = exports.REVERSE_CHARGE_REASONS = exports.ITEM_KINDS = exports.INVOICE_PAYMENT_METHODS = exports.INVOICE_LANGUAGES = exports.EINVOICE_FORMATS = exports.DOC_TYPES = exports.INVOICE_LIST_STATUS = exports.CUSTOMER_TYPES = exports.VAT_RATES = exports.PRICE_MODES = exports.TAX_SCHEMES = exports.CREDIT_NOTE_REASONS = exports.INVOICE_ERROR_CODES = exports.RECHNUNG_AUFRUFE = exports.RECHNUNG_VERTRAG_VERSION = exports.STEUERFREIE_FAELLE = exports.rechnungSummen = exports.rechnungText = exports.RECHNUNG_TEXTE = exports.updateCustomer = exports.searchCustomers = exports.recordInvoicePayment = exports.previewInvoice = exports.listInvoices = exports.listBrands = exports.issueInvoice = exports.getInvoiceXml = exports.getInvoiceSetupStatus = exports.getInvoicePdf = exports.getInvoice = exports.getCustomer = exports.createCustomer = exports.createCreditNote = exports.cancelInvoice = exports.rechnungFeldFehler = exports.rechnungFehlerCode = exports.istRechnungFehler = exports.rechnungKeyAuth = exports.createRechnungApi = void 0;
|
|
16
16
|
var api_js_1 = require("./api.js");
|
|
17
17
|
Object.defineProperty(exports, "createRechnungApi", { enumerable: true, get: function () { return api_js_1.createRechnungApi; } });
|
|
18
18
|
var auth_js_1 = require("./auth.js");
|
|
@@ -33,12 +33,16 @@ Object.defineProperty(exports, "getInvoiceXml", { enumerable: true, get: functio
|
|
|
33
33
|
Object.defineProperty(exports, "issueInvoice", { enumerable: true, get: function () { return endpunkte_js_1.issueInvoice; } });
|
|
34
34
|
Object.defineProperty(exports, "listBrands", { enumerable: true, get: function () { return endpunkte_js_1.listBrands; } });
|
|
35
35
|
Object.defineProperty(exports, "listInvoices", { enumerable: true, get: function () { return endpunkte_js_1.listInvoices; } });
|
|
36
|
+
Object.defineProperty(exports, "previewInvoice", { enumerable: true, get: function () { return endpunkte_js_1.previewInvoice; } });
|
|
36
37
|
Object.defineProperty(exports, "recordInvoicePayment", { enumerable: true, get: function () { return endpunkte_js_1.recordInvoicePayment; } });
|
|
37
38
|
Object.defineProperty(exports, "searchCustomers", { enumerable: true, get: function () { return endpunkte_js_1.searchCustomers; } });
|
|
38
39
|
Object.defineProperty(exports, "updateCustomer", { enumerable: true, get: function () { return endpunkte_js_1.updateCustomer; } });
|
|
39
40
|
var texte_js_1 = require("./texte.js");
|
|
40
41
|
Object.defineProperty(exports, "RECHNUNG_TEXTE", { enumerable: true, get: function () { return texte_js_1.RECHNUNG_TEXTE; } });
|
|
41
42
|
Object.defineProperty(exports, "rechnungText", { enumerable: true, get: function () { return texte_js_1.rechnungText; } });
|
|
43
|
+
var summen_js_1 = require("./summen.js");
|
|
44
|
+
Object.defineProperty(exports, "rechnungSummen", { enumerable: true, get: function () { return summen_js_1.rechnungSummen; } });
|
|
45
|
+
Object.defineProperty(exports, "STEUERFREIE_FAELLE", { enumerable: true, get: function () { return summen_js_1.STEUERFREIE_FAELLE; } });
|
|
42
46
|
var vertrag_js_1 = require("./vertrag.js");
|
|
43
47
|
Object.defineProperty(exports, "RECHNUNG_VERTRAG_VERSION", { enumerable: true, get: function () { return vertrag_js_1.RECHNUNG_VERTRAG_VERSION; } });
|
|
44
48
|
Object.defineProperty(exports, "RECHNUNG_AUFRUFE", { enumerable: true, get: function () { return vertrag_js_1.RECHNUNG_AUFRUFE; } });
|
|
@@ -53,6 +57,8 @@ Object.defineProperty(exports, "DOC_TYPES", { enumerable: true, get: function ()
|
|
|
53
57
|
Object.defineProperty(exports, "EINVOICE_FORMATS", { enumerable: true, get: function () { return vertrag_js_1.EINVOICE_FORMATS; } });
|
|
54
58
|
Object.defineProperty(exports, "INVOICE_LANGUAGES", { enumerable: true, get: function () { return vertrag_js_1.INVOICE_LANGUAGES; } });
|
|
55
59
|
Object.defineProperty(exports, "INVOICE_PAYMENT_METHODS", { enumerable: true, get: function () { return vertrag_js_1.INVOICE_PAYMENT_METHODS; } });
|
|
60
|
+
Object.defineProperty(exports, "ITEM_KINDS", { enumerable: true, get: function () { return vertrag_js_1.ITEM_KINDS; } });
|
|
61
|
+
Object.defineProperty(exports, "REVERSE_CHARGE_REASONS", { enumerable: true, get: function () { return vertrag_js_1.REVERSE_CHARGE_REASONS; } });
|
|
56
62
|
Object.defineProperty(exports, "INVOICE_NOTICE_CODES", { enumerable: true, get: function () { return vertrag_js_1.INVOICE_NOTICE_CODES; } });
|
|
57
63
|
Object.defineProperty(exports, "INVOICE_UNITS", { enumerable: true, get: function () { return vertrag_js_1.INVOICE_UNITS; } });
|
|
58
64
|
Object.defineProperty(exports, "RECHNUNG_EINHEITEN_CODES", { enumerable: true, get: function () { return vertrag_js_1.RECHNUNG_EINHEITEN_CODES; } });
|
|
@@ -0,0 +1,41 @@
|
|
|
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 type { InvoiceTotals } from './typen.js';
|
|
22
|
+
import type { PriceMode, TaxScheme } from './vertrag.js';
|
|
23
|
+
/** Was fuer die Summe zaehlt — `InvoiceItemInput` passt unveraendert. */
|
|
24
|
+
export interface SummenPosition {
|
|
25
|
+
quantity: number;
|
|
26
|
+
unitPriceCents: number;
|
|
27
|
+
vatRate: number;
|
|
28
|
+
discountPct?: number;
|
|
29
|
+
}
|
|
30
|
+
/** Steuerfaelle, in denen die Rechnung keine Steuer ausweist: jede Zeile zaehlt zu 0 %. */
|
|
31
|
+
export declare const STEUERFREIE_FAELLE: readonly TaxScheme[];
|
|
32
|
+
/**
|
|
33
|
+
* Netto, USt und Brutto einer Rechnung in Cent, je Satz absteigend — dieselbe
|
|
34
|
+
* Form wie `invoice.totals` in den Antworten.
|
|
35
|
+
*
|
|
36
|
+
* `taxScheme` ist der Steuerfall der Rechnung; ohne Angabe `normal`. Leitet der
|
|
37
|
+
* Server einen steuerfreien Fall ab (etwa eine ig. Lieferung), gehoert dieser
|
|
38
|
+
* Fall hierher — sonst rechnet die Funktion Steuer, die die Rechnung nicht
|
|
39
|
+
* ausweist.
|
|
40
|
+
*/
|
|
41
|
+
export declare function rechnungSummen(items: readonly SummenPosition[], priceMode: PriceMode, taxScheme?: TaxScheme): InvoiceTotals;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Summen einer Rechnung vorab rechnen — genau so, wie der Server sie beim
|
|
4
|
+
* Ausstellen rechnet. Fuer Shops, die kassieren, bevor die Rechnung entsteht,
|
|
5
|
+
* und denselben Betrag brauchen, den die Rechnung spaeter ausweist.
|
|
6
|
+
*
|
|
7
|
+
* Die Regel (je USt-Satz, Betraege in Cent, kaufmaennisch gerundet):
|
|
8
|
+
*
|
|
9
|
+
* | Modus | Netto | USt | Brutto |
|
|
10
|
+
* |---------|--------------------------------|----------------------------|--------------------|
|
|
11
|
+
* | `net` | round(Σ Zeilen) | round(Σ Zeilen × Satz/100) | Netto + USt |
|
|
12
|
+
* | `gross` | round(B × 100 / (100 + Satz)) | B − Netto | B = round(Σ Zeilen)|
|
|
13
|
+
*
|
|
14
|
+
* Zeile = Einzelpreis × Menge × (1 − Rabatt/100), ungerundet. Im Brutto-Modus
|
|
15
|
+
* ist das Brutto der vereinbarte Preis und bleibt, wie die Zeilen es ergeben.
|
|
16
|
+
* Bei einem steuerfreien Steuerfall zaehlt jede Zeile zu 0 %.
|
|
17
|
+
*
|
|
18
|
+
* `fixtures/rechnung-summen.json` haelt die Prueffaelle; Backend und
|
|
19
|
+
* Dart-Zwilling pruefen gegen dieselbe Datei. Verbindlich bleibt, was der
|
|
20
|
+
* Server rechnet — mit `previewInvoice` laesst sich das vorab abfragen.
|
|
21
|
+
*/
|
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
+
exports.STEUERFREIE_FAELLE = void 0;
|
|
24
|
+
exports.rechnungSummen = rechnungSummen;
|
|
25
|
+
/** Steuerfaelle, in denen die Rechnung keine Steuer ausweist: jede Zeile zaehlt zu 0 %. */
|
|
26
|
+
exports.STEUERFREIE_FAELLE = Object.freeze([
|
|
27
|
+
'smallBusiness',
|
|
28
|
+
'reverseCharge',
|
|
29
|
+
'igLieferung',
|
|
30
|
+
'exportThirdCountry',
|
|
31
|
+
'domesticReverseCharge',
|
|
32
|
+
'outsideScope',
|
|
33
|
+
]);
|
|
34
|
+
/** Auf Cent gerundete Euro — dieselbe Rundung wie am Server (halber Cent vom Nullpunkt weg). */
|
|
35
|
+
function euroRund(n) {
|
|
36
|
+
return ((n < 0 ? -1 : 1) * Math.round((Math.abs(n) + Number.EPSILON) * 100)) / 100;
|
|
37
|
+
}
|
|
38
|
+
const euroZuCent = (euro) => Math.round(euroRund(euro) * 100) || 0;
|
|
39
|
+
/** Ganze Cent kaufmaennisch; ein Gleitkomma-Rest wie x,4999999… zaehlt als halber Cent. */
|
|
40
|
+
function centRund(x) {
|
|
41
|
+
const betrag = Math.round(Math.abs(x) * 1e6) / 1e6;
|
|
42
|
+
return (x < 0 ? -1 : 1) * Math.round(betrag) || 0;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Netto, USt und Brutto einer Rechnung in Cent, je Satz absteigend — dieselbe
|
|
46
|
+
* Form wie `invoice.totals` in den Antworten.
|
|
47
|
+
*
|
|
48
|
+
* `taxScheme` ist der Steuerfall der Rechnung; ohne Angabe `normal`. Leitet der
|
|
49
|
+
* Server einen steuerfreien Fall ab (etwa eine ig. Lieferung), gehoert dieser
|
|
50
|
+
* Fall hierher — sonst rechnet die Funktion Steuer, die die Rechnung nicht
|
|
51
|
+
* ausweist.
|
|
52
|
+
*/
|
|
53
|
+
function rechnungSummen(items, priceMode, taxScheme = 'normal') {
|
|
54
|
+
const steuerfrei = exports.STEUERFREIE_FAELLE.includes(taxScheme);
|
|
55
|
+
const bruttoPreise = priceMode === 'gross' && !steuerfrei;
|
|
56
|
+
// Ungerundete Zeilen je Satz, in Euro wie am Server.
|
|
57
|
+
const jeSatz = new Map();
|
|
58
|
+
for (const p of items) {
|
|
59
|
+
const satz = steuerfrei ? 0 : p.vatRate;
|
|
60
|
+
const zeile = (p.unitPriceCents / 100) * p.quantity * (1 - (p.discountPct ?? 0) / 100);
|
|
61
|
+
jeSatz.set(satz, (jeSatz.get(satz) ?? 0) + zeile);
|
|
62
|
+
}
|
|
63
|
+
const byRate = [];
|
|
64
|
+
for (const [rate, roh] of jeSatz) {
|
|
65
|
+
let netCents;
|
|
66
|
+
let vatCents;
|
|
67
|
+
if (bruttoPreise) {
|
|
68
|
+
const grossCents = euroZuCent(roh);
|
|
69
|
+
netCents = centRund((grossCents * 100) / (100 + rate));
|
|
70
|
+
vatCents = grossCents - netCents;
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
netCents = euroZuCent(roh);
|
|
74
|
+
vatCents = euroZuCent((roh * rate) / 100);
|
|
75
|
+
}
|
|
76
|
+
byRate.push({ rate, netCents, vatCents, grossCents: netCents + vatCents });
|
|
77
|
+
}
|
|
78
|
+
byRate.sort((a, b) => b.rate - a.rate);
|
|
79
|
+
const summe = (feld) => byRate.reduce((s, r) => s + r[feld], 0);
|
|
80
|
+
return { netCents: summe('netCents'), vatCents: summe('vatCents'), grossCents: summe('grossCents'), byRate };
|
|
81
|
+
}
|
|
@@ -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).";
|
|
@@ -46,6 +50,20 @@ declare const DE: {
|
|
|
46
50
|
readonly 'steuer.reverseCharge.titel': "Steuerschuldnerschaft des Leistungsempfängers. ";
|
|
47
51
|
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
52
|
readonly 'steuer.uidZeile': "UID Leistungserbringer: {verkaeufer} · UID Leistungsempfänger: {kaeufer}";
|
|
53
|
+
readonly 'steuer.rcGrund.construction': "Bauleistung gemäß § 19 Abs. 1a UStG.";
|
|
54
|
+
readonly 'steuer.rcGrund.scrap': "Schrott und Abfallstoffe gemäß Schrott-Umsatzsteuerverordnung (BGBl. II Nr. 129/2007).";
|
|
55
|
+
readonly 'steuer.rcGrund.mobile_devices': "Mobilfunkgeräte bzw. integrierte Schaltkreise gemäß § 19 Abs. 1e lit. b UStG.";
|
|
56
|
+
readonly 'steuer.rcGrund.it_devices': "Laptops, Tablets bzw. Videospielkonsolen gemäß § 2 Z 1 Umsatzsteuerbetrugsbekämpfungsverordnung (BGBl. II Nr. 369/2013).";
|
|
57
|
+
readonly 'steuer.rcGrund.metals': "Metalle gemäß § 2 Z 4 Umsatzsteuerbetrugsbekämpfungsverordnung.";
|
|
58
|
+
readonly 'steuer.rcGrund.emission_certificates': "Treibhausgasemissionszertifikate gemäß § 19 Abs. 1e lit. a UStG.";
|
|
59
|
+
readonly 'steuer.rcGrund.gas_electricity': "Lieferung von Gas oder Elektrizität an einen Wiederverkäufer gemäß § 2 Z 2 Umsatzsteuerbetrugsbekämpfungsverordnung.";
|
|
60
|
+
readonly 'steuer.rcGrund.energy_certificates': "Gas- und Elektrizitätszertifikate gemäß § 2 Z 3 Umsatzsteuerbetrugsbekämpfungsverordnung.";
|
|
61
|
+
readonly 'steuer.rcGrund.investment_gold': "Steuerpflichtige Lieferung von Anlagegold gemäß § 2 Z 5 Umsatzsteuerbetrugsbekämpfungsverordnung.";
|
|
62
|
+
readonly 'steuer.rcGrund.security_transfer': "Lieferung von Sicherungs- bzw. Vorbehaltseigentum gemäß § 19 Abs. 1b UStG.";
|
|
63
|
+
readonly 'steuer.rcGrund.foreign_supplier': "Leistung eines nicht im Inland ansässigen Unternehmers gemäß § 19 Abs. 1 zweiter Satz UStG.";
|
|
64
|
+
readonly 'steuer.domestic.text': "Die Umsatzsteuer schuldet und entrichtet der Leistungsempfänger. Die Leistung ist nicht steuerfrei.";
|
|
65
|
+
readonly 'steuer.outsideScope.titel': "Nicht steuerbarer Umsatz. ";
|
|
66
|
+
readonly 'steuer.outsideScope.text': "Der Leistungsort liegt gemäß § 3a Abs. 6 UStG im Ansässigkeitsstaat des Leistungsempfängers; in Österreich fällt keine Umsatzsteuer an.";
|
|
49
67
|
readonly 'pdf.zahlung.titel': "ZAHLUNGSINFORMATIONEN";
|
|
50
68
|
readonly 'pdf.zahlung.empfaenger': "Empfänger";
|
|
51
69
|
readonly 'pdf.zahlung.iban': "IBAN";
|
|
@@ -95,6 +113,7 @@ declare const DE: {
|
|
|
95
113
|
readonly 'einvoice.befreiung.reverseCharge': "Steuerschuldnerschaft des Leistungsempfängers (Reverse Charge)";
|
|
96
114
|
readonly 'einvoice.befreiung.kleinunternehmer': "Steuerbefreit — Kleinunternehmer gemäß §6 Abs. 1 Z 27 UStG";
|
|
97
115
|
readonly 'einvoice.befreiung.igLieferung': "Steuerfreie innergemeinschaftliche Lieferung gemäß Art. 6 Abs. 1 UStG";
|
|
116
|
+
readonly 'einvoice.befreiung.outsideScope': "Nicht steuerbarer Umsatz — Leistungsort im Ausland (§ 3a Abs. 6 UStG)";
|
|
98
117
|
readonly 'einvoice.befreiung.ausfuhr': "Steuerfreie Ausfuhrlieferung gemäß §6 Abs. 1 Z 1 UStG";
|
|
99
118
|
readonly 'einvoice.zahlbarBis': "Zahlbar bis {datum}";
|
|
100
119
|
readonly 'einheit.piece': "Stk";
|