@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
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,100 @@ 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.23.0
|
|
8
|
+
|
|
9
|
+
### Rechnung: der Rechenkern in ganzen Zahlen
|
|
10
|
+
|
|
11
|
+
**Anlass:** Vier Umsetzungen (Server, dieses Paket, Dart, Panel) mussten dieselbe
|
|
12
|
+
Reihenfolge von Gleitkomma-Schritten nachbauen, damit Grenzfälle gleich runden.
|
|
13
|
+
Ein Cent Unterschied im Brutto-Modus hat das am 16.09.2026 sichtbar gemacht.
|
|
14
|
+
|
|
15
|
+
- **Neu: Unterpfad `@kreiseck/kasseneck-api/rechnung/rechnen`** — rein, ohne
|
|
16
|
+
Transport, auch für den Browser. Darin:
|
|
17
|
+
- `rechnungRechnen(positionen, { priceMode, taxScheme })` rechnet exakt in
|
|
18
|
+
ganzen Zahlen (BigInt) und rundet einmal je USt-Satz. Preise in Millionstel
|
|
19
|
+
Euro, Mengen in Tausendstel, Rabatt und Satz in Hundertstel-Prozent.
|
|
20
|
+
- `positionAusEuro(item)` wandelt eine Euro-Position verlustfrei um oder
|
|
21
|
+
nennt Feld und Grund.
|
|
22
|
+
- `anteiligerPreis`, `satzSchluessel`, `satzText`, `preisText`.
|
|
23
|
+
- `RechenFehler` mit `code` (`amount_too_large`, `kein_ganzzahlwert`,
|
|
24
|
+
`ausserhalb`), Feld und Index.
|
|
25
|
+
- **Prüffälle im Paket:** `fixtures/rechnung-rechnen.json` (20 Fälle von
|
|
26
|
+
Hand), `fixtures/rechnung-rechnen-zufall.json` (400 aus einer unabhängigen
|
|
27
|
+
Referenz in Python) und `fixtures/position-aus-euro.json`. Server,
|
|
28
|
+
Dart-Zwilling und Panel werden gegen dieselben Dateien prüfen.
|
|
29
|
+
- **Fehlerkatalog um zwei Codes erweitert:** `einvoice_unavailable` (zu dieser
|
|
30
|
+
Rechnung entsteht keine E-Rechnung, Grund im `reason`) und
|
|
31
|
+
`amount_too_large` (Betrag über der Grenze des Ganzzahlkerns) — beide hinten
|
|
32
|
+
angehängt, damit gespeicherte Reihenfolgen gültig bleiben. Der Katalog geht
|
|
33
|
+
dem Server bewusst voraus, damit Fremdsysteme beide Codes behandeln können,
|
|
34
|
+
bevor der Server sie sendet; heute sendet der Server keinen von beiden,
|
|
35
|
+
`amount_too_large` erst ab dessen Umstieg auf den Ganzzahlkern.
|
|
36
|
+
- **Unverändert:** `rechnungSummen`, `fixtures/rechnung-summen.json` und Form
|
|
37
|
+
von Anfrage und Antwort der Rechnungs-API — der Fehlerkatalog wächst nur
|
|
38
|
+
additiv (s. o.). Der Kern rundet an Halbcent-Grenzen richtig, `rechnungSummen`
|
|
39
|
+
rechnet weiterhin wie der Server heute; beides wird in 0.24.0
|
|
40
|
+
zusammengeführt. Wer schon vorab genau rechnen will, nimmt `previewInvoice`.
|
|
41
|
+
|
|
42
|
+
## 0.22.0
|
|
43
|
+
|
|
44
|
+
### Rechnung: Brutto bleibt Brutto, Hinweise kommen an, Probelauf
|
|
45
|
+
|
|
46
|
+
**Anlass:** Rückmeldung eines Shops zur Rechnungs-API (16.09.2026).
|
|
47
|
+
|
|
48
|
+
- **`issueInvoice` gab `notice` nicht weiter.** Der Typ sah die Liste vor, der
|
|
49
|
+
Aufruf übernahm sie nie — eine ig. Lieferung meldete deshalb nie, dass eine
|
|
50
|
+
Zusammenfassende Meldung fällig ist. Behoben.
|
|
51
|
+
- **`recordInvoicePayment` liefert `notice` jetzt als Liste** wie
|
|
52
|
+
`issueInvoice` (bisher ein einzelnes Objekt). **Bricht die Form:** wer
|
|
53
|
+
`result.notice.code` las, liest jetzt `result.notice?.[0]?.code`. Ein Server,
|
|
54
|
+
der noch ein Objekt schickt, wird zur Liste gemacht.
|
|
55
|
+
- **Neu: `rechnungSummen(items, priceMode, taxScheme?)`** rechnet die Summen so,
|
|
56
|
+
wie der Server sie ausstellt. Im Brutto-Modus ist das Brutto je Satz der
|
|
57
|
+
vereinbarte Preis: Netto = round(B × 100 / (100 + Satz)), USt = B − Netto.
|
|
58
|
+
Der Server rechnete bis dahin Netto und USt getrennt und setzte das Brutto neu
|
|
59
|
+
zusammen — 14,79 € + 15,00 € zu 20 % wurden 29,80 €. Die Prüffälle stehen in
|
|
60
|
+
`fixtures/rechnung-summen.json`; Server und Dart-Zwilling prüfen dagegen.
|
|
61
|
+
- **Neu: `previewInvoice(anfrage)`** — Probelauf von `issueInvoice`
|
|
62
|
+
(`dryRun: true` im Vertrag): prüft und rechnet wie das Ausstellen, schreibt
|
|
63
|
+
nichts fest und verbraucht den `idempotencyKey` nicht. Antwort `preview` mit
|
|
64
|
+
Summen, Steuerfall samt Grund, Sprache, Marke, E-Rechnung — dazu die Hinweise.
|
|
65
|
+
- **`totals.byRate[]` trägt `grossCents`.** Die Summen sind auch bei
|
|
66
|
+
Gutschriften positiv; das steht jetzt am Typ.
|
|
67
|
+
- **Texte:** `pdf.tabelle.einzelBrutto`, `pdf.tabelle.betragBrutto`,
|
|
68
|
+
`pdf.summe.darinUst` — das PDF einer Brutto-Rechnung zeigt Brutto-Spalten,
|
|
69
|
+
deren Zeilen die Summe ergeben. Dazu `pdf.position.rabatt`: ein Zeilenrabatt
|
|
70
|
+
stand bisher nirgends auf dem Blatt, „2 × 24,90 = 44,82" ging für den Leser
|
|
71
|
+
nicht auf.
|
|
72
|
+
- **Kennung als Text:** `getInvoice('…')` und `getCustomer('…')` werfen vor dem
|
|
73
|
+
Senden `KasseneckValidationError` (`scope: 'request'`). Bisher wurde der Text
|
|
74
|
+
Zeichen für Zeichen zu Feldern `0`, `1`, … und der Server konnte nur
|
|
75
|
+
„Bitte Eingaben prüfen." antworten.
|
|
76
|
+
- **Feldfehler in der Meldung:** `KasseneckApiError.message` hängt bis zu fünf
|
|
77
|
+
Einträge aus `errors[]` an (`[items[0].vatRate: …]`); `serverMessage` und
|
|
78
|
+
`details` bleiben unverändert.
|
|
79
|
+
|
|
80
|
+
## 0.21.0
|
|
81
|
+
|
|
82
|
+
### Rechnung: Kataloge für den abgeleiteten Steuerfall
|
|
83
|
+
|
|
84
|
+
**Anlass:** Der Server leitet den Steuerfall jetzt selbst ab, statt ihn zu
|
|
85
|
+
glauben (Kundenland, Kundenart, UID, Ware oder Leistung).
|
|
86
|
+
|
|
87
|
+
- `taxScheme` ist in `issueInvoice` **optional**; eine Angabe prüft der Server
|
|
88
|
+
gegen die Ableitung (`tax_scheme_mismatch`).
|
|
89
|
+
- Neue Fälle `domesticReverseCharge`, `oss` und `outsideScope`; Katalog der elf
|
|
90
|
+
Gründe für den Übergang der Steuerschuld im Inland
|
|
91
|
+
(`REVERSE_CHARGE_REASONS`, mit Fundstelle und Schwelle).
|
|
92
|
+
`oss` steht im Katalog, **kann aber noch nicht ausgestellt werden** —
|
|
93
|
+
die Ländersätze folgen.
|
|
94
|
+
- Positionen tragen `kind` (`goods`/`service`), weil ig. Lieferung und Reverse
|
|
95
|
+
Charge sich genau daran unterscheiden.
|
|
96
|
+
- `notice` ist an `issueInvoice` eine **Liste**: eine bar bezahlte ig. Lieferung
|
|
97
|
+
trägt zwei Hinweise zugleich.
|
|
98
|
+
- Die Rechnungssicht trägt `reverseChargeReason` und `taxCountry`; neuer
|
|
99
|
+
Befreiungstext für den nicht steuerbaren Umsatz (E-Rechnung, BR-O-10).
|
|
100
|
+
|
|
7
101
|
## 0.20.0
|
|
8
102
|
|
|
9
103
|
### Rechnung: richtige Steuerhinweise, Barumsatz auch mit Karte
|
package/README.md
CHANGED
|
@@ -547,6 +547,72 @@ 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
|
+
**Vorab rechnen, ohne Server.** `@kreiseck/kasseneck-api/rechnung/rechnen` ist
|
|
581
|
+
der reine Rechenkern dahinter: kein Transport, kein Zugangsschlüssel, läuft
|
|
582
|
+
auch im Browser (Panel). Er rechnet intern mit ganzen Zahlen (BigInt) statt
|
|
583
|
+
Gleitkomma und rundet genau einmal je USt-Satz — Preise in Millionstel Euro
|
|
584
|
+
(`unitPriceMicros`), Mengen in Tausendstel (`quantityMilli`), Rabatt und Satz
|
|
585
|
+
in Hundertstel-Prozent (`discountBp`, `vatRateBp`):
|
|
586
|
+
|
|
587
|
+
```ts
|
|
588
|
+
import { rechnungRechnen } from '@kreiseck/kasseneck-api/rechnung/rechnen';
|
|
589
|
+
|
|
590
|
+
rechnungRechnen(
|
|
591
|
+
[{ unitPriceMicros: 14_790_000, quantityMilli: 1000, vatRateBp: 2000 }],
|
|
592
|
+
{ priceMode: 'gross' },
|
|
593
|
+
);
|
|
594
|
+
// { netCents: 1233, vatCents: 246, grossCents: 1479, byRate: [{ rateBp: 2000, … }], lines: […] }
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
`positionAusEuro(item)` wandelt eine Euro-Position (`unitPrice`, `quantity`,
|
|
598
|
+
`vatRate`, `discountPct`) verlustfrei in diese Form um oder nennt Feld und
|
|
599
|
+
Grund, wenn das nicht geht. Die Prüffälle liegen in
|
|
600
|
+
`fixtures/rechnung-rechnen.json`, `fixtures/rechnung-rechnen-zufall.json` und
|
|
601
|
+
`fixtures/position-aus-euro.json`. **Noch nicht zusammengeführt** mit
|
|
602
|
+
`rechnungSummen`: bis 0.24.0 rechnen beide parallel und weichen an
|
|
603
|
+
Halbcent-Grenzen bewusst voneinander ab — **je USt-Satz** bis zu 1 Cent bei
|
|
604
|
+
einmal gerundeten Werten (Netto und USt im Netto-Modus, Brutto im
|
|
605
|
+
Brutto-Modus) und bis zu 2 Cent bei abgeleiteten (Summe zweier Rundungen). Bei
|
|
606
|
+
mehreren Sätzen summiert sich das: eine Rechnung über drei Sätze kann deshalb
|
|
607
|
+
3 Cent auseinanderliegen. Der Kern rundet dort richtig, `rechnungSummen`
|
|
608
|
+
rechnet weiterhin wie der Server heute. Verbindlich
|
|
609
|
+
für den ausgewiesenen Betrag bleibt bis dahin `previewInvoice`.
|
|
610
|
+
|
|
611
|
+
**Hinweise** (`notice`) sind immer eine Liste — bei `issueInvoice`,
|
|
612
|
+
`previewInvoice` und `recordInvoicePayment`. Eine ig. Lieferung trägt
|
|
613
|
+
`recapitulative_statement_due` (Zusammenfassende Meldung), eine bar bezahlte
|
|
614
|
+
Rechnung zusätzlich `cash_receipt_required`.
|
|
615
|
+
|
|
550
616
|
**Barumsatz ist nicht nur Bargeld.** Als Barzahlung gilt auch die Karte **vor
|
|
551
617
|
Ort** an der Kasse (§ 131b Abs. 1 Z 3 UStG) — dieselbe Karte im Internet
|
|
552
618
|
dagegen nicht. Weil `card` und `online` beides sein können, sagt es das
|
|
@@ -557,8 +623,8 @@ payment: { method: 'card', onSite: true } // Terminal an der Kasse
|
|
|
557
623
|
payment: { method: 'card' } // Kartenzahlung im Shop
|
|
558
624
|
```
|
|
559
625
|
|
|
560
|
-
Bei `cash` (immer) und bei `onSite: true` trägt die Antwort
|
|
561
|
-
`notice
|
|
626
|
+
Bei `cash` (immer) und bei `onSite: true` trägt die Antwort in der Liste
|
|
627
|
+
`notice` den Eintrag `cash_receipt_required`: ein Barumsatz braucht einen Beleg
|
|
562
628
|
(§ 132a BAO), bei Registrierkassenpflicht über die Registrierkasse — der
|
|
563
629
|
Vermerk an der Rechnung ersetzt ihn nicht. `transfer` mit `onSite` ist ein
|
|
564
630
|
Feldfehler, eine Überweisung erfolgt nicht vor Ort.
|
|
@@ -586,6 +652,7 @@ gegen genau diese Datei.
|
|
|
586
652
|
| `…/kasse` | Kachel-Kasse: Kassen-Einstellungen (betriebsweit / je Gerät), Artikelgruppen und Artikel für Kacheln, Rabattverteilung je Steuersatz, Reichweiten der Kassen-Rechte |
|
|
587
653
|
| `…/partner` | Partner-API: Betriebe anlegen, FinanzOnline-Link, Signatur, Kassen, Zugangsdaten, Webhooks samt Signaturprüfung. **Gehört auf einen Server.** |
|
|
588
654
|
| `…/rechnung` | Rechnungs-API: Kunden anlegen und suchen, Rechnungen festgeschrieben ausstellen, Gutschrift und Storno, PDF und E-Rechnung-XML; der Vertrag als Daten. **Gehört auf einen Server.** |
|
|
655
|
+
| `…/rechnung/rechnen` | Reiner Rechenkern für Rechnungssummen (Ganzzahlen, kein Transport, keine Abhängigkeit außer Typen) — darf auch im Browser laufen. |
|
|
589
656
|
| `…/react` | Dünner React-Adapter, der ein Beleg-Layout zeichnet. Braucht React. |
|
|
590
657
|
| `…/fixtures/*` | Golden-Belege (JSON): Eingaben `belege/<name>.json`, zugesagte Zeilenausgabe `erwartet/<name>.lines.json`, `manifest.json` mit Prüfsummen — dieselben Dateien prüfen Backend, Browser-Kasse und Flutter-Paket. |
|
|
591
658
|
|
|
@@ -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,9 @@
|
|
|
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 { rechnungSummen, STEUERFREIE_FAELLE, type SummenPosition } from './summen.js';
|
|
20
|
+
export { anteiligerPreis, BETRAG_GRENZE_CENTS, positionAusEuro, preisText, RechenFehler, rechnungRechnen, rund, satzSchluessel, satzText, type RechenErgebnis, type RechenFehlerCode, type RechenOptionen, type RechenPosition, type SatzSumme, type Umwandlung, type UmwandlungsGrund, type ZeilenBetrag, } from './rechnen.js';
|
|
19
21
|
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,8 @@
|
|
|
12
12
|
* Benutzung des Clients.
|
|
13
13
|
*/
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.
|
|
15
|
+
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.satzText = exports.satzSchluessel = exports.rund = exports.rechnungRechnen = exports.RechenFehler = exports.preisText = exports.positionAusEuro = exports.BETRAG_GRENZE_CENTS = exports.anteiligerPreis = 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
|
+
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 = void 0;
|
|
16
17
|
var api_js_1 = require("./api.js");
|
|
17
18
|
Object.defineProperty(exports, "createRechnungApi", { enumerable: true, get: function () { return api_js_1.createRechnungApi; } });
|
|
18
19
|
var auth_js_1 = require("./auth.js");
|
|
@@ -33,12 +34,26 @@ Object.defineProperty(exports, "getInvoiceXml", { enumerable: true, get: functio
|
|
|
33
34
|
Object.defineProperty(exports, "issueInvoice", { enumerable: true, get: function () { return endpunkte_js_1.issueInvoice; } });
|
|
34
35
|
Object.defineProperty(exports, "listBrands", { enumerable: true, get: function () { return endpunkte_js_1.listBrands; } });
|
|
35
36
|
Object.defineProperty(exports, "listInvoices", { enumerable: true, get: function () { return endpunkte_js_1.listInvoices; } });
|
|
37
|
+
Object.defineProperty(exports, "previewInvoice", { enumerable: true, get: function () { return endpunkte_js_1.previewInvoice; } });
|
|
36
38
|
Object.defineProperty(exports, "recordInvoicePayment", { enumerable: true, get: function () { return endpunkte_js_1.recordInvoicePayment; } });
|
|
37
39
|
Object.defineProperty(exports, "searchCustomers", { enumerable: true, get: function () { return endpunkte_js_1.searchCustomers; } });
|
|
38
40
|
Object.defineProperty(exports, "updateCustomer", { enumerable: true, get: function () { return endpunkte_js_1.updateCustomer; } });
|
|
39
41
|
var texte_js_1 = require("./texte.js");
|
|
40
42
|
Object.defineProperty(exports, "RECHNUNG_TEXTE", { enumerable: true, get: function () { return texte_js_1.RECHNUNG_TEXTE; } });
|
|
41
43
|
Object.defineProperty(exports, "rechnungText", { enumerable: true, get: function () { return texte_js_1.rechnungText; } });
|
|
44
|
+
var summen_js_1 = require("./summen.js");
|
|
45
|
+
Object.defineProperty(exports, "rechnungSummen", { enumerable: true, get: function () { return summen_js_1.rechnungSummen; } });
|
|
46
|
+
Object.defineProperty(exports, "STEUERFREIE_FAELLE", { enumerable: true, get: function () { return summen_js_1.STEUERFREIE_FAELLE; } });
|
|
47
|
+
var rechnen_js_1 = require("./rechnen.js");
|
|
48
|
+
Object.defineProperty(exports, "anteiligerPreis", { enumerable: true, get: function () { return rechnen_js_1.anteiligerPreis; } });
|
|
49
|
+
Object.defineProperty(exports, "BETRAG_GRENZE_CENTS", { enumerable: true, get: function () { return rechnen_js_1.BETRAG_GRENZE_CENTS; } });
|
|
50
|
+
Object.defineProperty(exports, "positionAusEuro", { enumerable: true, get: function () { return rechnen_js_1.positionAusEuro; } });
|
|
51
|
+
Object.defineProperty(exports, "preisText", { enumerable: true, get: function () { return rechnen_js_1.preisText; } });
|
|
52
|
+
Object.defineProperty(exports, "RechenFehler", { enumerable: true, get: function () { return rechnen_js_1.RechenFehler; } });
|
|
53
|
+
Object.defineProperty(exports, "rechnungRechnen", { enumerable: true, get: function () { return rechnen_js_1.rechnungRechnen; } });
|
|
54
|
+
Object.defineProperty(exports, "rund", { enumerable: true, get: function () { return rechnen_js_1.rund; } });
|
|
55
|
+
Object.defineProperty(exports, "satzSchluessel", { enumerable: true, get: function () { return rechnen_js_1.satzSchluessel; } });
|
|
56
|
+
Object.defineProperty(exports, "satzText", { enumerable: true, get: function () { return rechnen_js_1.satzText; } });
|
|
42
57
|
var vertrag_js_1 = require("./vertrag.js");
|
|
43
58
|
Object.defineProperty(exports, "RECHNUNG_VERTRAG_VERSION", { enumerable: true, get: function () { return vertrag_js_1.RECHNUNG_VERTRAG_VERSION; } });
|
|
44
59
|
Object.defineProperty(exports, "RECHNUNG_AUFRUFE", { enumerable: true, get: function () { return vertrag_js_1.RECHNUNG_AUFRUFE; } });
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Der Rechenkern der Rechnung — exakt, in ganzen Zahlen.
|
|
3
|
+
*
|
|
4
|
+
* Eine Zeile ist ein Bruch: Preis (µ€) × Menge (Tausendstel) × Rabattanteil
|
|
5
|
+
* (Hundertstel-Prozent). Multipliziert man die Skalen, ist die kleinste Einheit
|
|
6
|
+
* 10⁻¹⁷ Cent — das passt in keine Gleitkommazahl und in keine 53-Bit-Ganzzahl,
|
|
7
|
+
* also rechnet der Kern mit BigInt. Gerundet wird genau EINMAL je USt-Satz, auf
|
|
8
|
+
* dem Bruch, kaufmaennisch (halbe Einheit vom Nullpunkt weg).
|
|
9
|
+
*
|
|
10
|
+
* Warum das so streng ist: Vier Umsetzungen (Server, dieses Paket, Dart, Panel)
|
|
11
|
+
* mussten bisher dieselbe Reihenfolge von Gleitkomma-Schritten nachbauen, damit
|
|
12
|
+
* Grenzfaelle gleich runden. Mit Ganzzahlen gibt es keine Reihenfolge mehr, an
|
|
13
|
+
* der etwas auseinanderlaufen koennte.
|
|
14
|
+
*
|
|
15
|
+
* Dieser Unterpfad ist rein: kein Transport, kein Zugangsschluessel, keine
|
|
16
|
+
* Abhaengigkeit ausser Typen. Er wird im Browser gebuendelt (Panel) und im
|
|
17
|
+
* Server geladen.
|
|
18
|
+
*
|
|
19
|
+
* Spec: docs/specs/2026-09-17-rechnung-ganzzahlen-design.md § 5.
|
|
20
|
+
*/
|
|
21
|
+
import type { PriceMode, TaxScheme } from './vertrag.js';
|
|
22
|
+
/** Hoechster Betrag je Zeile und je Rechnung: 999.999.999,99 €. */
|
|
23
|
+
export declare const BETRAG_GRENZE_CENTS = 99999999999;
|
|
24
|
+
/** Steuerfaelle, in denen die Rechnung keine Steuer ausweist: jede Zeile zaehlt zu 0 %. */
|
|
25
|
+
export declare const STEUERFREIE_FAELLE: readonly TaxScheme[];
|
|
26
|
+
/** Eine Position in gespeicherter Form — alle Werte ganzzahlig. */
|
|
27
|
+
export interface RechenPosition {
|
|
28
|
+
/** Einzelpreis in Millionstel Euro, 0 … 10¹². */
|
|
29
|
+
unitPriceMicros: number;
|
|
30
|
+
/** Menge in Tausendstel; negativ = Abzugszeile, 0 = Textzeile. */
|
|
31
|
+
quantityMilli: number;
|
|
32
|
+
/** Rabatt in Hundertstel-Prozent, 0 … 10000 (ohne Angabe 0). */
|
|
33
|
+
discountBp?: number;
|
|
34
|
+
/** USt-Satz in Hundertstel-Prozent, 0 … 10000 (ohne Angabe 0). */
|
|
35
|
+
vatRateBp?: number;
|
|
36
|
+
}
|
|
37
|
+
export interface RechenOptionen {
|
|
38
|
+
priceMode: PriceMode;
|
|
39
|
+
/** Ohne Angabe `normal`. */
|
|
40
|
+
taxScheme?: TaxScheme;
|
|
41
|
+
}
|
|
42
|
+
export interface SatzSumme {
|
|
43
|
+
rateBp: number;
|
|
44
|
+
netCents: number;
|
|
45
|
+
vatCents: number;
|
|
46
|
+
grossCents: number;
|
|
47
|
+
}
|
|
48
|
+
export interface ZeilenBetrag {
|
|
49
|
+
netCents: number;
|
|
50
|
+
grossCents: number;
|
|
51
|
+
/** Der Satz, mit dem die Zeile gerechnet wurde (bei steuerfreiem Fall 0). */
|
|
52
|
+
rateBp: number;
|
|
53
|
+
}
|
|
54
|
+
export interface RechenErgebnis {
|
|
55
|
+
netCents: number;
|
|
56
|
+
vatCents: number;
|
|
57
|
+
grossCents: number;
|
|
58
|
+
/** Absteigend nach `rateBp`. */
|
|
59
|
+
byRate: SatzSumme[];
|
|
60
|
+
/** Je Position, in der Reihenfolge der Eingabe. */
|
|
61
|
+
lines: ZeilenBetrag[];
|
|
62
|
+
}
|
|
63
|
+
export type RechenFehlerCode = 'amount_too_large' | 'kein_ganzzahlwert' | 'ausserhalb';
|
|
64
|
+
/** Fehler des Kerns — mit Code, Feld und Index, damit die API daraus einen Feldfehler machen kann. */
|
|
65
|
+
export declare class RechenFehler extends Error {
|
|
66
|
+
readonly code: RechenFehlerCode;
|
|
67
|
+
readonly feld?: string;
|
|
68
|
+
readonly index?: number;
|
|
69
|
+
constructor(code: RechenFehlerCode, nachricht: string, feld?: string, index?: number);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* `a / b` kaufmaennisch gerundet, halbe Einheit vom Nullpunkt weg; `b` > 0.
|
|
73
|
+
*
|
|
74
|
+
* Ohne Gleitkomma: `(2·|a| + b) / (2·b)` ist genau dann eins groesser, wenn der
|
|
75
|
+
* Rest mindestens die halbe Einheit betraegt.
|
|
76
|
+
*/
|
|
77
|
+
export declare function rund(a: bigint, b: bigint): bigint;
|
|
78
|
+
export declare function rechnungRechnen(positionen: readonly RechenPosition[], optionen: RechenOptionen): RechenErgebnis;
|
|
79
|
+
export type UmwandlungsGrund = 'kein_zahlwert' | 'nachkommastellen' | 'ausserhalb';
|
|
80
|
+
export type Umwandlung = {
|
|
81
|
+
ok: true;
|
|
82
|
+
position: RechenPosition & Record<string, unknown>;
|
|
83
|
+
} | {
|
|
84
|
+
ok: false;
|
|
85
|
+
feld: 'unitPrice' | 'quantity' | 'vatRate' | 'discountPct';
|
|
86
|
+
grund: UmwandlungsGrund;
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Euro-Position → Ganzzahl-Form, verlustfrei oder mit Grund abgelehnt. Die
|
|
90
|
+
* EINE Stelle, die das tut: Panel (beim Lesen), SEPA-Lauf, Umrechnungsskript
|
|
91
|
+
* und das Festschreiben benutzen alle diese Funktion, damit niemand eine
|
|
92
|
+
* zweite Auslegung baut.
|
|
93
|
+
*
|
|
94
|
+
* Eine Position, die schon `unitPriceMicros` traegt, kommt unveraendert
|
|
95
|
+
* zurueck — die Funktion ist die Formweiche, nicht eine blinde Umrechnung.
|
|
96
|
+
*
|
|
97
|
+
* Fehlende Menge und fehlender Satz gelten als 0, weil der Server bisher so
|
|
98
|
+
* abgerechnet hat. Ein fehlender Preis dagegen ist NIE 0: sonst wuerde eine
|
|
99
|
+
* Position, der nur der Preis fehlt, still zur 0-€-Zeile.
|
|
100
|
+
*/
|
|
101
|
+
export declare function positionAusEuro(item: Record<string, unknown>): Umwandlung;
|
|
102
|
+
/**
|
|
103
|
+
* Anteiliger Preis fuer einen Teilzeitraum (erste Rechnung eines Abos).
|
|
104
|
+
* War der Ursprungspreis centgenau, bleibt auch der Anteil centgenau — sonst
|
|
105
|
+
* stuende auf der ersten Rechnung ein Preis mit sechs Stellen, wo bisher zwei
|
|
106
|
+
* standen.
|
|
107
|
+
*/
|
|
108
|
+
export declare function anteiligerPreis(unitPriceMicros: number, monate: number, intervall: number): number;
|
|
109
|
+
/**
|
|
110
|
+
* Schluessel eines USt-Satzes in den gespeicherten Maps (`creditedCents.byRate`,
|
|
111
|
+
* `invoice_stats.vatByRate`): der Prozenttext mit Punkt, genau wie
|
|
112
|
+
* `String(satz)` ihn bisher gebildet hat. NICHT fuer die Anzeige — dafuer gibt
|
|
113
|
+
* es `satzText`.
|
|
114
|
+
*/
|
|
115
|
+
export declare function satzSchluessel(rateBp: number): string;
|
|
116
|
+
/** Ein USt-Satz fuer die Anzeige: oesterreichisch mit Komma, ohne nachlaufende Nullen. */
|
|
117
|
+
export declare function satzText(rateBp: number): string;
|
|
118
|
+
/**
|
|
119
|
+
* Ein Einzelpreis fuer die Anzeige: mindestens zwei, hoechstens sechs
|
|
120
|
+
* Nachkommastellen, Tausenderpunkt, Komma als Trennzeichen — in jeder Sprache
|
|
121
|
+
* oesterreichisch, wie die Betraege auf dem Blatt.
|
|
122
|
+
*/
|
|
123
|
+
export declare function preisText(unitPriceMicros: number): string;
|