@kreiseck/kasseneck-api 0.22.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 CHANGED
@@ -4,6 +4,41 @@ 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
+
7
42
  ## 0.22.0
8
43
 
9
44
  ### Rechnung: Brutto bleibt Brutto, Hinweise kommen an, Probelauf
package/README.md CHANGED
@@ -577,6 +577,37 @@ aufwärts), je Satz, dann summiert. Die Prüffälle liegen in
577
577
  `fixtures/rechnung-summen.json`. Die Summen sind auch bei Gutschriften positiv —
578
578
  das Vorzeichen steht im Belegtyp (`docType: 'GU'`).
579
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
+
580
611
  **Hinweise** (`notice`) sind immer eine Liste — bei `issueInvoice`,
581
612
  `previewInvoice` und `recordInvoicePayment`. Eine ig. Lieferung trägt
582
613
  `recapitulative_statement_due` (Zusammenfassende Meldung), eine bar bezahlte
@@ -621,6 +652,7 @@ gegen genau diese Datei.
621
652
  | `…/kasse` | Kachel-Kasse: Kassen-Einstellungen (betriebsweit / je Gerät), Artikelgruppen und Artikel für Kacheln, Rabattverteilung je Steuersatz, Reichweiten der Kassen-Rechte |
622
653
  | `…/partner` | Partner-API: Betriebe anlegen, FinanzOnline-Link, Signatur, Kassen, Zugangsdaten, Webhooks samt Signaturprüfung. **Gehört auf einen Server.** |
623
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. |
624
656
  | `…/react` | Dünner React-Adapter, der ein Beleg-Layout zeichnet. Braucht React. |
625
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. |
626
658
 
@@ -17,4 +17,5 @@ export { cancelInvoice, createCreditNote, createCustomer, getCustomer, getInvoic
17
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
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';
20
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.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;
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");
@@ -43,6 +44,16 @@ Object.defineProperty(exports, "rechnungText", { enumerable: true, get: function
43
44
  var summen_js_1 = require("./summen.js");
44
45
  Object.defineProperty(exports, "rechnungSummen", { enumerable: true, get: function () { return summen_js_1.rechnungSummen; } });
45
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; } });
46
57
  var vertrag_js_1 = require("./vertrag.js");
47
58
  Object.defineProperty(exports, "RECHNUNG_VERTRAG_VERSION", { enumerable: true, get: function () { return vertrag_js_1.RECHNUNG_VERTRAG_VERSION; } });
48
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;
@@ -0,0 +1,327 @@
1
+ "use strict";
2
+ /**
3
+ * Der Rechenkern der Rechnung — exakt, in ganzen Zahlen.
4
+ *
5
+ * Eine Zeile ist ein Bruch: Preis (µ€) × Menge (Tausendstel) × Rabattanteil
6
+ * (Hundertstel-Prozent). Multipliziert man die Skalen, ist die kleinste Einheit
7
+ * 10⁻¹⁷ Cent — das passt in keine Gleitkommazahl und in keine 53-Bit-Ganzzahl,
8
+ * also rechnet der Kern mit BigInt. Gerundet wird genau EINMAL je USt-Satz, auf
9
+ * dem Bruch, kaufmaennisch (halbe Einheit vom Nullpunkt weg).
10
+ *
11
+ * Warum das so streng ist: Vier Umsetzungen (Server, dieses Paket, Dart, Panel)
12
+ * mussten bisher dieselbe Reihenfolge von Gleitkomma-Schritten nachbauen, damit
13
+ * Grenzfaelle gleich runden. Mit Ganzzahlen gibt es keine Reihenfolge mehr, an
14
+ * der etwas auseinanderlaufen koennte.
15
+ *
16
+ * Dieser Unterpfad ist rein: kein Transport, kein Zugangsschluessel, keine
17
+ * Abhaengigkeit ausser Typen. Er wird im Browser gebuendelt (Panel) und im
18
+ * Server geladen.
19
+ *
20
+ * Spec: docs/specs/2026-09-17-rechnung-ganzzahlen-design.md § 5.
21
+ */
22
+ Object.defineProperty(exports, "__esModule", { value: true });
23
+ exports.RechenFehler = exports.STEUERFREIE_FAELLE = exports.BETRAG_GRENZE_CENTS = void 0;
24
+ exports.rund = rund;
25
+ exports.rechnungRechnen = rechnungRechnen;
26
+ exports.positionAusEuro = positionAusEuro;
27
+ exports.anteiligerPreis = anteiligerPreis;
28
+ exports.satzSchluessel = satzSchluessel;
29
+ exports.satzText = satzText;
30
+ exports.preisText = preisText;
31
+ /** Skala des Zeilenbruchs: 10⁻¹⁷ Cent. */
32
+ const E = 10n ** 17n;
33
+ /** Hoechster Betrag je Zeile und je Rechnung: 999.999.999,99 €. */
34
+ exports.BETRAG_GRENZE_CENTS = 99_999_999_999;
35
+ /** Steuerfaelle, in denen die Rechnung keine Steuer ausweist: jede Zeile zaehlt zu 0 %. */
36
+ exports.STEUERFREIE_FAELLE = Object.freeze([
37
+ 'smallBusiness',
38
+ 'reverseCharge',
39
+ 'igLieferung',
40
+ 'exportThirdCountry',
41
+ 'domesticReverseCharge',
42
+ 'outsideScope',
43
+ ]);
44
+ /** Fehler des Kerns — mit Code, Feld und Index, damit die API daraus einen Feldfehler machen kann. */
45
+ class RechenFehler extends Error {
46
+ code;
47
+ feld;
48
+ index;
49
+ constructor(code, nachricht, feld, index) {
50
+ super(nachricht);
51
+ this.name = 'RechenFehler';
52
+ this.code = code;
53
+ this.feld = feld;
54
+ this.index = index;
55
+ }
56
+ }
57
+ exports.RechenFehler = RechenFehler;
58
+ /**
59
+ * `a / b` kaufmaennisch gerundet, halbe Einheit vom Nullpunkt weg; `b` > 0.
60
+ *
61
+ * Ohne Gleitkomma: `(2·|a| + b) / (2·b)` ist genau dann eins groesser, wenn der
62
+ * Rest mindestens die halbe Einheit betraegt.
63
+ */
64
+ function rund(a, b) {
65
+ const negativ = a < 0n;
66
+ const betrag = negativ ? -a : a;
67
+ const ganz = (2n * betrag + b) / (2n * b);
68
+ return negativ ? -ganz : ganz;
69
+ }
70
+ const GRENZEN = {
71
+ unitPriceMicros: [0, 1_000_000_000_000],
72
+ quantityMilli: [-1_000_000_000_000, 1_000_000_000_000],
73
+ discountBp: [0, 10_000],
74
+ vatRateBp: [0, 10_000],
75
+ };
76
+ function ganzzahl(wert, feld, index) {
77
+ if (typeof wert !== 'number' || !Number.isInteger(wert)) {
78
+ throw new RechenFehler('kein_ganzzahlwert', `items[${index}].${feld} muss eine ganze Zahl sein`, feld, index);
79
+ }
80
+ const [min, max] = GRENZEN[feld];
81
+ if (wert < min || wert > max) {
82
+ throw new RechenFehler('ausserhalb', `items[${index}].${feld} liegt ausserhalb von ${min} … ${max}`, feld, index);
83
+ }
84
+ return wert;
85
+ }
86
+ /**
87
+ * Verteilt die Zeilenbetraege eines Satzes so, dass sie genau die Satzsumme
88
+ * ergeben. Startwert ist die kaufmaennisch gerundete Zeile; der Rest geht
89
+ * einzeln an die Zeilen, bei denen das Runden am meisten weggenommen (bzw.
90
+ * zugegeben) hat. Verglichen wird auf dem Bruch, nie in Gleitkomma; bei
91
+ * Gleichstand bekommt die fruehere Zeile den Cent. Eine Zeile ueber 0 bekommt
92
+ * nie einen Cent — sonst stuende auf dem Blatt ein Betrag, den die Position
93
+ * nicht hat.
94
+ */
95
+ function verteile(gruppe, feld, faktor, nenner, ziel) {
96
+ const werte = gruppe.map((z) => {
97
+ const zaehler = z.L * faktor;
98
+ return { z, zaehler, wert: rund(zaehler, nenner) };
99
+ });
100
+ let rest = ziel - werte.reduce((s, w) => s + w.wert, 0n);
101
+ if (rest !== 0n) {
102
+ const schritt = rest > 0n ? 1n : -1n;
103
+ const kandidaten = werte
104
+ .filter((w) => w.zaehler !== 0n)
105
+ .sort((a, b) => {
106
+ const da = (a.zaehler - a.wert * nenner) * schritt;
107
+ const db = (b.zaehler - b.wert * nenner) * schritt;
108
+ if (da !== db)
109
+ return db > da ? 1 : -1;
110
+ return a.z.index - b.z.index;
111
+ });
112
+ for (let k = 0; rest !== 0n && kandidaten.length > 0; k = (k + 1) % kandidaten.length) {
113
+ kandidaten[k].wert += schritt;
114
+ rest -= schritt;
115
+ }
116
+ }
117
+ for (const w of werte)
118
+ w.z[feld] = Number(w.wert);
119
+ }
120
+ function rechnungRechnen(positionen, optionen) {
121
+ const steuerfrei = exports.STEUERFREIE_FAELLE.includes(optionen.taxScheme ?? 'normal');
122
+ const bruttoPreise = optionen.priceMode === 'gross' && !steuerfrei;
123
+ const grenze = BigInt(exports.BETRAG_GRENZE_CENTS) * E;
124
+ const zeilen = positionen.map((p, index) => {
125
+ const preis = BigInt(ganzzahl(p.unitPriceMicros, 'unitPriceMicros', index));
126
+ const menge = BigInt(ganzzahl(p.quantityMilli, 'quantityMilli', index));
127
+ const rabatt = BigInt(ganzzahl(p.discountBp ?? 0, 'discountBp', index));
128
+ const satz = ganzzahl(p.vatRateBp ?? 0, 'vatRateBp', index);
129
+ const L = preis * menge * (10000n - rabatt) * 1000000n;
130
+ if ((L < 0n ? -L : L) > grenze) {
131
+ throw new RechenFehler('amount_too_large', `items[${index}] uebersteigt ${exports.BETRAG_GRENZE_CENTS} Cent`, 'unitPriceMicros', index);
132
+ }
133
+ return { index, rateBp: steuerfrei ? 0 : satz, L, netCents: 0, grossCents: 0 };
134
+ });
135
+ const jeSatz = new Map();
136
+ for (const z of zeilen) {
137
+ const gruppe = jeSatz.get(z.rateBp);
138
+ if (gruppe)
139
+ gruppe.push(z);
140
+ else
141
+ jeSatz.set(z.rateBp, [z]);
142
+ }
143
+ const byRate = [];
144
+ for (const [rateBp, gruppe] of jeSatz) {
145
+ const r = BigInt(rateBp);
146
+ const S = gruppe.reduce((s, z) => s + z.L, 0n);
147
+ let netCents;
148
+ let vatCents;
149
+ let grossCents;
150
+ if (bruttoPreise) {
151
+ grossCents = rund(S, E);
152
+ netCents = rund(grossCents * 10000n, 10000n + r);
153
+ vatCents = grossCents - netCents;
154
+ }
155
+ else {
156
+ netCents = rund(S, E);
157
+ vatCents = rund(S * r, E * 10000n);
158
+ grossCents = netCents + vatCents;
159
+ }
160
+ if (bruttoPreise) {
161
+ verteile(gruppe, 'netCents', 10000n, E * (10000n + r), netCents);
162
+ verteile(gruppe, 'grossCents', 1n, E, grossCents);
163
+ }
164
+ else {
165
+ verteile(gruppe, 'netCents', 1n, E, netCents);
166
+ verteile(gruppe, 'grossCents', 10000n + r, E * 10000n, grossCents);
167
+ }
168
+ byRate.push({
169
+ rateBp,
170
+ netCents: Number(netCents),
171
+ vatCents: Number(vatCents),
172
+ grossCents: Number(grossCents),
173
+ });
174
+ }
175
+ byRate.sort((a, b) => b.rateBp - a.rateBp);
176
+ const summe = (feld) => byRate.reduce((s, r) => s + r[feld], 0);
177
+ const ergebnis = {
178
+ netCents: summe('netCents'),
179
+ vatCents: summe('vatCents'),
180
+ grossCents: summe('grossCents'),
181
+ byRate,
182
+ lines: zeilen.map((z) => ({ netCents: z.netCents, grossCents: z.grossCents, rateBp: z.rateBp })),
183
+ };
184
+ for (const feld of ['netCents', 'vatCents', 'grossCents']) {
185
+ if (Math.abs(ergebnis[feld]) > exports.BETRAG_GRENZE_CENTS) {
186
+ throw new RechenFehler('amount_too_large', `Die Rechnung uebersteigt ${exports.BETRAG_GRENZE_CENTS} Cent (${feld})`);
187
+ }
188
+ }
189
+ return ergebnis;
190
+ }
191
+ /**
192
+ * Wandelt eine Zahl ueber ihren kuerzesten Dezimaltext in eine Ganzzahl mit
193
+ * `stellen` Nachkommastellen. Kein Gleitkomma-Rechnen: `0.1 * 1000` ist
194
+ * 100.00000000000001, `String(0.1)` dagegen genau "0.1". Mehr Stellen als
195
+ * erlaubt ergeben `null` — auch Gleitkomma-Rauschen wie 0.30000000000000004,
196
+ * das mit 17 Stellen dasteht.
197
+ */
198
+ function ganzAusDezimaltext(wert, stellen) {
199
+ if (typeof wert !== 'number' || !Number.isFinite(wert))
200
+ return null;
201
+ const treffer = /^(-?)(\d+)(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/.exec(String(wert));
202
+ if (!treffer)
203
+ return null;
204
+ const [, vorzeichen = '', ganzTeil = '0', bruchTeil = '', hoch = '0'] = treffer;
205
+ const verschiebung = Number(hoch);
206
+ let ziffern = `${ganzTeil}${bruchTeil}`;
207
+ let nachkomma = bruchTeil.length - verschiebung;
208
+ if (nachkomma < 0) {
209
+ ziffern += '0'.repeat(-nachkomma);
210
+ nachkomma = 0;
211
+ }
212
+ if (nachkomma > stellen) {
213
+ // Nur echte Nullen am Ende duerfen wegfallen (1.20 bei 1 Stelle).
214
+ const zuviel = nachkomma - stellen;
215
+ if (!/^0+$/.test(ziffern.slice(-zuviel)))
216
+ return null;
217
+ ziffern = ziffern.slice(0, -zuviel);
218
+ nachkomma = stellen;
219
+ }
220
+ const skaliert = `${ziffern}${'0'.repeat(stellen - nachkomma)}`;
221
+ const zahl = Number(`${vorzeichen}${skaliert}`);
222
+ return Number.isSafeInteger(zahl) ? zahl : null;
223
+ }
224
+ const EURO_FELDER = ['unitPrice', 'quantity', 'vatRate', 'discountPct'];
225
+ /**
226
+ * Euro-Position → Ganzzahl-Form, verlustfrei oder mit Grund abgelehnt. Die
227
+ * EINE Stelle, die das tut: Panel (beim Lesen), SEPA-Lauf, Umrechnungsskript
228
+ * und das Festschreiben benutzen alle diese Funktion, damit niemand eine
229
+ * zweite Auslegung baut.
230
+ *
231
+ * Eine Position, die schon `unitPriceMicros` traegt, kommt unveraendert
232
+ * zurueck — die Funktion ist die Formweiche, nicht eine blinde Umrechnung.
233
+ *
234
+ * Fehlende Menge und fehlender Satz gelten als 0, weil der Server bisher so
235
+ * abgerechnet hat. Ein fehlender Preis dagegen ist NIE 0: sonst wuerde eine
236
+ * Position, der nur der Preis fehlt, still zur 0-€-Zeile.
237
+ */
238
+ function positionAusEuro(item) {
239
+ if (typeof item.unitPriceMicros === 'number') {
240
+ return { ok: true, position: item };
241
+ }
242
+ const roh = item.unitPrice;
243
+ if (typeof roh !== 'number' || !Number.isFinite(roh)) {
244
+ return { ok: false, feld: 'unitPrice', grund: 'kein_zahlwert' };
245
+ }
246
+ const micros = ganzAusDezimaltext(Math.abs(roh), 6);
247
+ if (micros === null)
248
+ return { ok: false, feld: 'unitPrice', grund: 'nachkommastellen' };
249
+ if (micros > GRENZEN.unitPriceMicros[1])
250
+ return { ok: false, feld: 'unitPrice', grund: 'ausserhalb' };
251
+ const zahl = (feld, stellen, max) => {
252
+ const w = item[feld];
253
+ if (w === undefined || w === null)
254
+ return { ok: true, wert: 0 };
255
+ if (typeof w !== 'number' || !Number.isFinite(w))
256
+ return { ok: false, grund: 'kein_zahlwert' };
257
+ const ganz = ganzAusDezimaltext(w, stellen);
258
+ if (ganz === null)
259
+ return { ok: false, grund: 'nachkommastellen' };
260
+ if (ganz < (feld === 'quantity' ? -max : 0) || ganz > max)
261
+ return { ok: false, grund: 'ausserhalb' };
262
+ return { ok: true, wert: ganz };
263
+ };
264
+ const menge = zahl('quantity', 3, GRENZEN.quantityMilli[1]);
265
+ if (!menge.ok)
266
+ return { ok: false, feld: 'quantity', grund: menge.grund };
267
+ const satz = zahl('vatRate', 2, 10_000);
268
+ if (!satz.ok)
269
+ return { ok: false, feld: 'vatRate', grund: satz.grund };
270
+ const rabatt = zahl('discountPct', 2, 10_000);
271
+ if (!rabatt.ok)
272
+ return { ok: false, feld: 'discountPct', grund: rabatt.grund };
273
+ const rest = { ...item };
274
+ for (const feld of EURO_FELDER)
275
+ delete rest[feld];
276
+ return {
277
+ ok: true,
278
+ position: {
279
+ ...rest,
280
+ unitPriceMicros: micros,
281
+ quantityMilli: roh < 0 ? -menge.wert : menge.wert,
282
+ discountBp: rabatt.wert,
283
+ vatRateBp: satz.wert,
284
+ },
285
+ };
286
+ }
287
+ /**
288
+ * Anteiliger Preis fuer einen Teilzeitraum (erste Rechnung eines Abos).
289
+ * War der Ursprungspreis centgenau, bleibt auch der Anteil centgenau — sonst
290
+ * stuende auf der ersten Rechnung ein Preis mit sechs Stellen, wo bisher zwei
291
+ * standen.
292
+ */
293
+ function anteiligerPreis(unitPriceMicros, monate, intervall) {
294
+ const micros = BigInt(unitPriceMicros);
295
+ const anteil = rund(micros * BigInt(monate), BigInt(intervall));
296
+ if (unitPriceMicros % 10_000 !== 0)
297
+ return Number(anteil);
298
+ return Number(rund(anteil, 10000n) * 10000n);
299
+ }
300
+ /**
301
+ * Schluessel eines USt-Satzes in den gespeicherten Maps (`creditedCents.byRate`,
302
+ * `invoice_stats.vatByRate`): der Prozenttext mit Punkt, genau wie
303
+ * `String(satz)` ihn bisher gebildet hat. NICHT fuer die Anzeige — dafuer gibt
304
+ * es `satzText`.
305
+ */
306
+ function satzSchluessel(rateBp) {
307
+ return String(rateBp / 100);
308
+ }
309
+ /** Ein USt-Satz fuer die Anzeige: oesterreichisch mit Komma, ohne nachlaufende Nullen. */
310
+ function satzText(rateBp) {
311
+ return satzSchluessel(rateBp).replace('.', ',');
312
+ }
313
+ /**
314
+ * Ein Einzelpreis fuer die Anzeige: mindestens zwei, hoechstens sechs
315
+ * Nachkommastellen, Tausenderpunkt, Komma als Trennzeichen — in jeder Sprache
316
+ * oesterreichisch, wie die Betraege auf dem Blatt.
317
+ */
318
+ function preisText(unitPriceMicros) {
319
+ const negativ = unitPriceMicros < 0;
320
+ const betrag = Math.abs(unitPriceMicros);
321
+ const ganz = Math.trunc(betrag / 1_000_000);
322
+ let bruch = String(betrag % 1_000_000).padStart(6, '0');
323
+ while (bruch.length > 2 && bruch.endsWith('0'))
324
+ bruch = bruch.slice(0, -1);
325
+ const mitPunkten = String(ganz).replace(/\B(?=(\d{3})+(?!\d))/g, '.');
326
+ return `${negativ ? '-' : ''}${mitPunkten},${bruch}`;
327
+ }
@@ -20,6 +20,9 @@
20
20
  */
21
21
  import type { InvoiceTotals } from './typen.js';
22
22
  import type { PriceMode, TaxScheme } from './vertrag.js';
23
+ import { STEUERFREIE_FAELLE } from './rechnen.js';
24
+ /** Aus dem Kern uebernommen, damit Kern und Huelle nie auseinanderlaufen. */
25
+ export { STEUERFREIE_FAELLE };
23
26
  /** Was fuer die Summe zaehlt — `InvoiceItemInput` passt unveraendert. */
24
27
  export interface SummenPosition {
25
28
  quantity: number;
@@ -27,8 +30,6 @@ export interface SummenPosition {
27
30
  vatRate: number;
28
31
  discountPct?: number;
29
32
  }
30
- /** Steuerfaelle, in denen die Rechnung keine Steuer ausweist: jede Zeile zaehlt zu 0 %. */
31
- export declare const STEUERFREIE_FAELLE: readonly TaxScheme[];
32
33
  /**
33
34
  * Netto, USt und Brutto einer Rechnung in Cent, je Satz absteigend — dieselbe
34
35
  * Form wie `invoice.totals` in den Antworten.
@@ -22,15 +22,8 @@
22
22
  Object.defineProperty(exports, "__esModule", { value: true });
23
23
  exports.STEUERFREIE_FAELLE = void 0;
24
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
- ]);
25
+ const rechnen_js_1 = require("./rechnen.js");
26
+ Object.defineProperty(exports, "STEUERFREIE_FAELLE", { enumerable: true, get: function () { return rechnen_js_1.STEUERFREIE_FAELLE; } });
34
27
  /** Auf Cent gerundete Euro — dieselbe Rundung wie am Server (halber Cent vom Nullpunkt weg). */
35
28
  function euroRund(n) {
36
29
  return ((n < 0 ? -1 : 1) * Math.round((Math.abs(n) + Number.EPSILON) * 100)) / 100;
@@ -51,7 +44,7 @@ function centRund(x) {
51
44
  * ausweist.
52
45
  */
53
46
  function rechnungSummen(items, priceMode, taxScheme = 'normal') {
54
- const steuerfrei = exports.STEUERFREIE_FAELLE.includes(taxScheme);
47
+ const steuerfrei = rechnen_js_1.STEUERFREIE_FAELLE.includes(taxScheme);
55
48
  const bruttoPreise = priceMode === 'gross' && !steuerfrei;
56
49
  // Ungerundete Zeilen je Satz, in Euro wie am Server.
57
50
  const jeSatz = new Map();
@@ -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"];
@@ -69,6 +69,8 @@ exports.INVOICE_ERROR_CODES = [
69
69
  'reverse_charge_threshold',
70
70
  'mixed_supply_not_allowed',
71
71
  'oss_not_enabled',
72
+ 'einvoice_unavailable', // zu dieser Rechnung entsteht keine E-Rechnung, Grund in `reason`
73
+ 'amount_too_large', // Betrag ueber der Grenze des Ganzzahlkerns; erst ab dessen Umstieg gesendet
72
74
  ];
73
75
  /** Gruende einer Gutschrift. Der Server druckt den deutschen Anzeigetext. */
74
76
  exports.CREDIT_NOTE_REASONS = [
@@ -17,4 +17,5 @@ export { cancelInvoice, createCreditNote, createCustomer, getCustomer, getInvoic
17
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
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';
20
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';