@kreiseck/kasseneck-api 0.14.0 → 0.16.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 +47 -0
- package/README.md +75 -0
- package/dist/cjs/client/aufrufe.d.ts +1 -1
- package/dist/cjs/client/aufrufe.js +12 -0
- package/dist/cjs/rechnung/api.d.ts +45 -0
- package/dist/cjs/rechnung/api.js +36 -0
- package/dist/cjs/rechnung/auth.d.ts +21 -0
- package/dist/cjs/rechnung/auth.js +36 -0
- package/dist/cjs/rechnung/endpunkte.d.ts +54 -0
- package/dist/cjs/rechnung/endpunkte.js +147 -0
- package/dist/cjs/rechnung/fehler.d.ts +21 -0
- package/dist/cjs/rechnung/fehler.js +44 -0
- package/dist/cjs/rechnung/index.d.ts +18 -0
- package/dist/cjs/rechnung/index.js +54 -0
- package/dist/cjs/rechnung/typen.d.ts +238 -0
- package/dist/cjs/rechnung/typen.js +9 -0
- package/dist/cjs/rechnung/vertrag.d.ts +113 -0
- package/dist/cjs/rechnung/vertrag.js +218 -0
- package/dist/esm/client/aufrufe.d.ts +1 -1
- package/dist/esm/client/aufrufe.js +12 -0
- package/dist/esm/rechnung/api.d.ts +45 -0
- package/dist/esm/rechnung/api.js +33 -0
- package/dist/esm/rechnung/auth.d.ts +21 -0
- package/dist/esm/rechnung/auth.js +33 -0
- package/dist/esm/rechnung/endpunkte.d.ts +54 -0
- package/dist/esm/rechnung/endpunkte.js +133 -0
- package/dist/esm/rechnung/fehler.d.ts +21 -0
- package/dist/esm/rechnung/fehler.js +39 -0
- package/dist/esm/rechnung/index.d.ts +18 -0
- package/dist/esm/rechnung/index.js +17 -0
- package/dist/esm/rechnung/typen.d.ts +238 -0
- package/dist/esm/rechnung/typen.js +8 -0
- package/dist/esm/rechnung/vertrag.d.ts +113 -0
- package/dist/esm/rechnung/vertrag.js +215 -0
- package/fixtures/hobex-hps-codes.json +1 -1
- package/fixtures/kasse-texte.json +1 -1
- package/fixtures/oberflaeche.json +101 -1
- package/fixtures/rechnung-api-beispiele/credit-fehler-grund.json +24 -0
- package/fixtures/rechnung-api-beispiele/credit-ok.json +20 -0
- package/fixtures/rechnung-api-beispiele/customer-fehler-uid.json +25 -0
- package/fixtures/rechnung-api-beispiele/customer-get-fehler-beide.json +15 -0
- package/fixtures/rechnung-api-beispiele/customer-ok.json +20 -0
- package/fixtures/rechnung-api-beispiele/customer-search-fehler-leer.json +14 -0
- package/fixtures/rechnung-api-beispiele/issue-brutto-20.json +27 -0
- package/fixtures/rechnung-api-beispiele/issue-fehler-cent.json +26 -0
- package/fixtures/rechnung-api-beispiele/issue-fehler-menge.json +26 -0
- package/fixtures/rechnung-api-beispiele/issue-fehler-negativ.json +26 -0
- package/fixtures/rechnung-api-beispiele/issue-fehler-satz.json +26 -0
- package/fixtures/rechnung-api-beispiele/issue-fehler-unbekannt.json +27 -0
- package/fixtures/rechnung-api-beispiele/issue-gemischt.json +33 -0
- package/fixtures/rechnung-api-beispiele/issue-kleinunternehmer.json +27 -0
- package/fixtures/rechnung-api-beispiele/issue-netto-20.json +28 -0
- package/fixtures/rechnung-api-beispiele/issue-rabatt.json +28 -0
- package/fixtures/rechnung-api-beispiele/setup-status-fehler-feld.json +14 -0
- package/fixtures/rechnung-api-beispiele/setup-status-ok.json +8 -0
- package/fixtures/rechnung-api.schema.json +681 -0
- package/package.json +12 -1
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
export const AUFRUFE = [
|
|
2
2
|
'activateCashregister',
|
|
3
|
+
'cancelInvoice',
|
|
3
4
|
'cancelReceipt',
|
|
5
|
+
'createCreditNote',
|
|
6
|
+
'createCustomer',
|
|
4
7
|
'createCustomerCashregister',
|
|
5
8
|
'createPartnerCustomer',
|
|
6
9
|
'checkPartnerCustomerEmail',
|
|
@@ -14,9 +17,14 @@ export const AUFRUFE = [
|
|
|
14
17
|
'endRegisterSession',
|
|
15
18
|
'financeWebService',
|
|
16
19
|
'generateFullReceiptId',
|
|
20
|
+
'getCustomer',
|
|
17
21
|
'getCustomerCredentials',
|
|
18
22
|
'getCustomerSignatureStatus',
|
|
19
23
|
'getFirstReceiptDate',
|
|
24
|
+
'getInvoice',
|
|
25
|
+
'getInvoicePdf',
|
|
26
|
+
'getInvoiceSetupStatus',
|
|
27
|
+
'getInvoiceXml',
|
|
20
28
|
'getKasseSettings',
|
|
21
29
|
'getPartnerCustomer',
|
|
22
30
|
'getPartnerInfo',
|
|
@@ -24,7 +32,9 @@ export const AUFRUFE = [
|
|
|
24
32
|
'getReceipt',
|
|
25
33
|
'hobexPayApi',
|
|
26
34
|
'hobexRefundApi',
|
|
35
|
+
'issueInvoice',
|
|
27
36
|
'listCustomerCashregisters',
|
|
37
|
+
'listInvoices',
|
|
28
38
|
'listMyArticleGroups',
|
|
29
39
|
'listMyArticles',
|
|
30
40
|
'listMyCashregisters',
|
|
@@ -41,6 +51,7 @@ export const AUFRUFE = [
|
|
|
41
51
|
'registerUserLogin',
|
|
42
52
|
'renewRegisterSession',
|
|
43
53
|
'requestCustomerSignature',
|
|
54
|
+
'searchCustomers',
|
|
44
55
|
'sendPartnerCustomerFonLink',
|
|
45
56
|
'sendPartnerWebhookTest',
|
|
46
57
|
'sendReceiptEmail',
|
|
@@ -49,5 +60,6 @@ export const AUFRUFE = [
|
|
|
49
60
|
'stripeCaptureIntent',
|
|
50
61
|
'unpairRegisterDevice',
|
|
51
62
|
'rotatePartnerWebhookSecret',
|
|
63
|
+
'updateCustomer',
|
|
52
64
|
'updatePartnerWebhook',
|
|
53
65
|
];
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fassade ueber den Aufrufen der Rechnungs-API: Schluessel einmal binden, dann rufen.
|
|
3
|
+
*
|
|
4
|
+
* Wie [createPartnerApi] bewusst **keine** Klasse — die Aufrufe sind freie
|
|
5
|
+
* Funktionen (endpunkte.ts) und bleiben einzeln importierbar.
|
|
6
|
+
*/
|
|
7
|
+
import { type FetchLike } from '../client/transport.js';
|
|
8
|
+
import type { EInvoiceFormat } from './vertrag.js';
|
|
9
|
+
import type { CancelInvoiceRequest, CancelResult, CreditNoteRequest, CreditNoteResult, Customer, CustomerInput, CustomerPage, CustomerSearch, InvoiceDetail, InvoiceListQuery, InvoicePage, InvoiceSetupStatus, IssueInvoiceRequest, IssueResult } from './typen.js';
|
|
10
|
+
export interface RechnungApiOptions {
|
|
11
|
+
/** `api_key` des Kontos (`kr_live_…` / `kr_test_…`). Gehoert auf einen Server. */
|
|
12
|
+
apiKey: string;
|
|
13
|
+
/** Abweichende Basis-URL; Vorgabe `https://api.kasseneck.at/v1`. */
|
|
14
|
+
baseUrl?: string;
|
|
15
|
+
/** Zeitlimit je Aufruf in Millisekunden. */
|
|
16
|
+
timeoutMs?: number;
|
|
17
|
+
/** Eigene `fetch`-Umsetzung (Tests, Proxys). */
|
|
18
|
+
fetch?: FetchLike;
|
|
19
|
+
}
|
|
20
|
+
export interface RechnungApi {
|
|
21
|
+
createCustomer(customer: CustomerInput, optionen?: {
|
|
22
|
+
idempotencyKey?: string;
|
|
23
|
+
}): Promise<Customer>;
|
|
24
|
+
getCustomer(kennung: {
|
|
25
|
+
customerId: string;
|
|
26
|
+
} | {
|
|
27
|
+
externalId: string;
|
|
28
|
+
}): Promise<Customer>;
|
|
29
|
+
updateCustomer(customerId: string, patch: Partial<CustomerInput>): Promise<Customer>;
|
|
30
|
+
searchCustomers(suche: CustomerSearch): Promise<CustomerPage>;
|
|
31
|
+
issueInvoice(anfrage: IssueInvoiceRequest): Promise<IssueResult>;
|
|
32
|
+
cancelInvoice(anfrage: CancelInvoiceRequest): Promise<CancelResult>;
|
|
33
|
+
createCreditNote(anfrage: CreditNoteRequest): Promise<CreditNoteResult>;
|
|
34
|
+
getInvoice(kennung: {
|
|
35
|
+
invoiceId: string;
|
|
36
|
+
} | {
|
|
37
|
+
number: string;
|
|
38
|
+
}): Promise<InvoiceDetail>;
|
|
39
|
+
listInvoices(abfrage?: InvoiceListQuery): Promise<InvoicePage>;
|
|
40
|
+
getInvoicePdf(invoiceId: string): Promise<Uint8Array>;
|
|
41
|
+
getInvoiceXml(invoiceId: string, format?: EInvoiceFormat): Promise<string>;
|
|
42
|
+
/** Darf dieses Konto ausstellen, und was fehlt noch? Laeuft auch ohne Freigabe. */
|
|
43
|
+
getInvoiceSetupStatus(): Promise<InvoiceSetupStatus>;
|
|
44
|
+
}
|
|
45
|
+
export declare function createRechnungApi(optionen: RechnungApiOptions): RechnungApi;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fassade ueber den Aufrufen der Rechnungs-API: Schluessel einmal binden, dann rufen.
|
|
3
|
+
*
|
|
4
|
+
* Wie [createPartnerApi] bewusst **keine** Klasse — die Aufrufe sind freie
|
|
5
|
+
* Funktionen (endpunkte.ts) und bleiben einzeln importierbar.
|
|
6
|
+
*/
|
|
7
|
+
import { createBinaryTransport, createTransport } from '../client/transport.js';
|
|
8
|
+
import { rechnungKeyAuth } from './auth.js';
|
|
9
|
+
import { cancelInvoice, createCreditNote, createCustomer, getCustomer, getInvoice, getInvoicePdf, getInvoiceSetupStatus, getInvoiceXml, issueInvoice, listInvoices, searchCustomers, updateCustomer, } from './endpunkte.js';
|
|
10
|
+
export function createRechnungApi(optionen) {
|
|
11
|
+
const transportOptionen = {
|
|
12
|
+
auth: rechnungKeyAuth({ apiKey: optionen.apiKey }),
|
|
13
|
+
baseUrl: optionen.baseUrl,
|
|
14
|
+
timeoutMs: optionen.timeoutMs,
|
|
15
|
+
fetch: optionen.fetch,
|
|
16
|
+
};
|
|
17
|
+
const rufen = createTransport(transportOptionen);
|
|
18
|
+
const rufenBinaer = createBinaryTransport(transportOptionen);
|
|
19
|
+
return {
|
|
20
|
+
createCustomer: (customer, o) => createCustomer(rufen, customer, o),
|
|
21
|
+
getCustomer: (kennung) => getCustomer(rufen, kennung),
|
|
22
|
+
updateCustomer: (id, patch) => updateCustomer(rufen, id, patch),
|
|
23
|
+
searchCustomers: (suche) => searchCustomers(rufen, suche),
|
|
24
|
+
issueInvoice: (anfrage) => issueInvoice(rufen, anfrage),
|
|
25
|
+
cancelInvoice: (anfrage) => cancelInvoice(rufen, anfrage),
|
|
26
|
+
createCreditNote: (anfrage) => createCreditNote(rufen, anfrage),
|
|
27
|
+
getInvoice: (kennung) => getInvoice(rufen, kennung),
|
|
28
|
+
listInvoices: (abfrage) => listInvoices(rufen, abfrage),
|
|
29
|
+
getInvoicePdf: (id) => getInvoicePdf(rufenBinaer, id),
|
|
30
|
+
getInvoiceXml: (id, format) => getInvoiceXml(rufen, id, format),
|
|
31
|
+
getInvoiceSetupStatus: () => getInvoiceSetupStatus(rufen),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anmeldung fuer die Rechnungs-API: der `api_key` des Kontos als Bearer.
|
|
3
|
+
*
|
|
4
|
+
* Anders als `apiKeyAuth` **ohne** `cashregister-token` — eine Rechnung
|
|
5
|
+
* entsteht nicht an einer Kasse, sondern am Konto.
|
|
6
|
+
*
|
|
7
|
+
* **Der Schluessel gehoert auf einen Server.** Mit ihm entstehen festgeschriebene
|
|
8
|
+
* Rechnungen mit fortlaufender Nummer, die sich nur noch per Gutschrift
|
|
9
|
+
* korrigieren lassen.
|
|
10
|
+
*
|
|
11
|
+
* Geprueft wird nur, was ohne Netz sicher falsch ist: ein leerer Wert, ein
|
|
12
|
+
* Partner-Schluessel (`pk_…`) oder ein Kassen-Token (`cb_…`). Die Form des
|
|
13
|
+
* Kontoschluessels selbst bleibt offen, weil das Backend neben `kr_<env>_…`
|
|
14
|
+
* weiterhin aeltere Formen annimmt. Die Meldung nennt nie den Wert, nur seine Art.
|
|
15
|
+
*/
|
|
16
|
+
import type { KasseneckAuth } from '../client/auth.js';
|
|
17
|
+
export interface RechnungKeyAuthOptions {
|
|
18
|
+
/** `api_key` des Kontos, z. B. `kr_live_…` oder `kr_test_…`. */
|
|
19
|
+
apiKey: string;
|
|
20
|
+
}
|
|
21
|
+
export declare function rechnungKeyAuth(options: RechnungKeyAuthOptions): KasseneckAuth;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anmeldung fuer die Rechnungs-API: der `api_key` des Kontos als Bearer.
|
|
3
|
+
*
|
|
4
|
+
* Anders als `apiKeyAuth` **ohne** `cashregister-token` — eine Rechnung
|
|
5
|
+
* entsteht nicht an einer Kasse, sondern am Konto.
|
|
6
|
+
*
|
|
7
|
+
* **Der Schluessel gehoert auf einen Server.** Mit ihm entstehen festgeschriebene
|
|
8
|
+
* Rechnungen mit fortlaufender Nummer, die sich nur noch per Gutschrift
|
|
9
|
+
* korrigieren lassen.
|
|
10
|
+
*
|
|
11
|
+
* Geprueft wird nur, was ohne Netz sicher falsch ist: ein leerer Wert, ein
|
|
12
|
+
* Partner-Schluessel (`pk_…`) oder ein Kassen-Token (`cb_…`). Die Form des
|
|
13
|
+
* Kontoschluessels selbst bleibt offen, weil das Backend neben `kr_<env>_…`
|
|
14
|
+
* weiterhin aeltere Formen annimmt. Die Meldung nennt nie den Wert, nur seine Art.
|
|
15
|
+
*/
|
|
16
|
+
import { KasseneckAuthError } from '../client/errors.js';
|
|
17
|
+
export function rechnungKeyAuth(options) {
|
|
18
|
+
const schluessel = typeof options?.apiKey === 'string' ? options.apiKey.trim() : '';
|
|
19
|
+
if (!schluessel) {
|
|
20
|
+
throw new KasseneckAuthError('rechnungKeyAuth: apiKey fehlt');
|
|
21
|
+
}
|
|
22
|
+
if (/^pk_(live|test)_/i.test(schluessel)) {
|
|
23
|
+
throw new KasseneckAuthError('rechnungKeyAuth: das ist ein Partner-Schluessel (pk_…) — die Rechnungs-API nimmt den api_key des Kontos (kr_…)');
|
|
24
|
+
}
|
|
25
|
+
if (/^cb_(live|test)_/i.test(schluessel)) {
|
|
26
|
+
throw new KasseneckAuthError('rechnungKeyAuth: das ist ein Kassen-Token (cb_…) — die Rechnungs-API nimmt den api_key des Kontos (kr_…)');
|
|
27
|
+
}
|
|
28
|
+
// Pro Aufruf ein frisches Objekt, wie bei den anderen Anmeldungen.
|
|
29
|
+
return () => ({
|
|
30
|
+
headers: { Authorization: `Bearer ${schluessel}` },
|
|
31
|
+
params: {},
|
|
32
|
+
});
|
|
33
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Die Aufrufe der Rechnungs-API — Kunden, Rechnungen, Gutschriften, Dateien.
|
|
3
|
+
*
|
|
4
|
+
* Jede Funktion nimmt den Transport als ersten Parameter und ist einzeln
|
|
5
|
+
* importierbar; die Fassade [createRechnungApi] bindet ihn nur einmal.
|
|
6
|
+
*
|
|
7
|
+
* **Geprueft wird hier nichts Fachliches.** Die Anfrage geht unveraendert an
|
|
8
|
+
* das Backend, das sie gegen denselben Vertrag prueft (`vertrag.ts`) und einen
|
|
9
|
+
* Formfehler als `validation` mit `errors[]` zurueckgibt — zwei Pruefungen
|
|
10
|
+
* hiessen zwei Wahrheiten, von denen eine veraltet.
|
|
11
|
+
*
|
|
12
|
+
* **Nicht automatisch wiederholen.** Wer nach einem Zeitlimit erneut
|
|
13
|
+
* ausstellt, tut das mit **demselben** `idempotencyKey`: dann kommt die schon
|
|
14
|
+
* ausgestellte Rechnung zurueck (`replayed: true`) statt einer zweiten.
|
|
15
|
+
*
|
|
16
|
+
* Nach dem Senden wird nichts hart gecastet: fehlt ein zugesagtes Feld, wirft
|
|
17
|
+
* der Aufruf `KasseneckValidationError` mit `scope:'response'`.
|
|
18
|
+
*/
|
|
19
|
+
import type { InternerBinaerTransport, InternerTransport } from '../client/aufrufe.js';
|
|
20
|
+
import type { EInvoiceFormat } from './vertrag.js';
|
|
21
|
+
import type { CancelInvoiceRequest, CancelResult, CreditNoteRequest, CreditNoteResult, Customer, CustomerInput, CustomerPage, CustomerSearch, InvoiceDetail, InvoiceListQuery, InvoicePage, InvoiceSetupStatus, IssueInvoiceRequest, IssueResult } from './typen.js';
|
|
22
|
+
export declare function createCustomer(rufen: InternerTransport, customer: CustomerInput, optionen?: {
|
|
23
|
+
idempotencyKey?: string;
|
|
24
|
+
}): Promise<Customer>;
|
|
25
|
+
export declare function getCustomer(rufen: InternerTransport, kennung: {
|
|
26
|
+
customerId: string;
|
|
27
|
+
} | {
|
|
28
|
+
externalId: string;
|
|
29
|
+
}): Promise<Customer>;
|
|
30
|
+
export declare function updateCustomer(rufen: InternerTransport, customerId: string, patch: Partial<CustomerInput>): Promise<Customer>;
|
|
31
|
+
export declare function searchCustomers(rufen: InternerTransport, suche: CustomerSearch): Promise<CustomerPage>;
|
|
32
|
+
export declare function issueInvoice(rufen: InternerTransport, anfrage: IssueInvoiceRequest): Promise<IssueResult>;
|
|
33
|
+
export declare function cancelInvoice(rufen: InternerTransport, anfrage: CancelInvoiceRequest): Promise<CancelResult>;
|
|
34
|
+
export declare function createCreditNote(rufen: InternerTransport, anfrage: CreditNoteRequest): Promise<CreditNoteResult>;
|
|
35
|
+
export declare function getInvoice(rufen: InternerTransport, kennung: {
|
|
36
|
+
invoiceId: string;
|
|
37
|
+
} | {
|
|
38
|
+
number: string;
|
|
39
|
+
}): Promise<InvoiceDetail>;
|
|
40
|
+
export declare function listInvoices(rufen: InternerTransport, abfrage?: InvoiceListQuery): Promise<InvoicePage>;
|
|
41
|
+
/**
|
|
42
|
+
* Darf dieses Konto ueber die API ausstellen, und was fehlt noch? Laeuft auch
|
|
43
|
+
* ohne Freigabe und vor der Live-Freischaltung — genau dann braucht man die
|
|
44
|
+
* Antwort. Vor dem ersten `issueInvoice` aufrufen und `missing` anzeigen.
|
|
45
|
+
*/
|
|
46
|
+
export declare function getInvoiceSetupStatus(rufen: InternerTransport): Promise<InvoiceSetupStatus>;
|
|
47
|
+
/** Das PDF der Rechnung (bei Gutschriften: der Gutschrift), mit eingebetteter Factur-X-Datei. */
|
|
48
|
+
export declare function getInvoicePdf(rufenBinaer: InternerBinaerTransport, invoiceId: string): Promise<Uint8Array>;
|
|
49
|
+
/**
|
|
50
|
+
* Die E-Rechnung als XML-Text. Sie kommt im gewohnten Umschlag (`data.xml`)
|
|
51
|
+
* und nicht als rohe Datei: so bleibt ein fachlicher Fehler
|
|
52
|
+
* (`einvoice_incomplete` mit `missing[]`) ein gewoehnlicher Fehler.
|
|
53
|
+
*/
|
|
54
|
+
export declare function getInvoiceXml(rufen: InternerTransport, invoiceId: string, format?: EInvoiceFormat): Promise<string>;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Die Aufrufe der Rechnungs-API — Kunden, Rechnungen, Gutschriften, Dateien.
|
|
3
|
+
*
|
|
4
|
+
* Jede Funktion nimmt den Transport als ersten Parameter und ist einzeln
|
|
5
|
+
* importierbar; die Fassade [createRechnungApi] bindet ihn nur einmal.
|
|
6
|
+
*
|
|
7
|
+
* **Geprueft wird hier nichts Fachliches.** Die Anfrage geht unveraendert an
|
|
8
|
+
* das Backend, das sie gegen denselben Vertrag prueft (`vertrag.ts`) und einen
|
|
9
|
+
* Formfehler als `validation` mit `errors[]` zurueckgibt — zwei Pruefungen
|
|
10
|
+
* hiessen zwei Wahrheiten, von denen eine veraltet.
|
|
11
|
+
*
|
|
12
|
+
* **Nicht automatisch wiederholen.** Wer nach einem Zeitlimit erneut
|
|
13
|
+
* ausstellt, tut das mit **demselben** `idempotencyKey`: dann kommt die schon
|
|
14
|
+
* ausgestellte Rechnung zurueck (`replayed: true`) statt einer zweiten.
|
|
15
|
+
*
|
|
16
|
+
* Nach dem Senden wird nichts hart gecastet: fehlt ein zugesagtes Feld, wirft
|
|
17
|
+
* der Aufruf `KasseneckValidationError` mit `scope:'response'`.
|
|
18
|
+
*/
|
|
19
|
+
import { KasseneckValidationError } from '../client/errors.js';
|
|
20
|
+
function objekt(wert) {
|
|
21
|
+
return wert !== null && typeof wert === 'object' && !Array.isArray(wert)
|
|
22
|
+
? wert
|
|
23
|
+
: {};
|
|
24
|
+
}
|
|
25
|
+
/** Ein zugesagtes Objektfeld der Antwort — oder ein Antwortfehler. */
|
|
26
|
+
function pflichtObjekt(aufruf, daten, feld) {
|
|
27
|
+
const wert = objekt(daten)[feld];
|
|
28
|
+
if (wert === null || typeof wert !== 'object' || Array.isArray(wert)) {
|
|
29
|
+
throw new KasseneckValidationError(aufruf, `Antwort ohne ${feld}`, 'response');
|
|
30
|
+
}
|
|
31
|
+
return wert;
|
|
32
|
+
}
|
|
33
|
+
/** Eine zugesagte Liste samt Cursor — oder ein Antwortfehler. */
|
|
34
|
+
function seite(aufruf, daten, feld) {
|
|
35
|
+
const o = objekt(daten);
|
|
36
|
+
if (!Array.isArray(o[feld])) {
|
|
37
|
+
throw new KasseneckValidationError(aufruf, `Antwort ohne ${feld}`, 'response');
|
|
38
|
+
}
|
|
39
|
+
return { eintraege: o[feld], nextCursor: typeof o['nextCursor'] === 'string' ? o['nextCursor'] : null };
|
|
40
|
+
}
|
|
41
|
+
const zahl = (wert) => (typeof wert === 'number' && Number.isFinite(wert) ? wert : 0);
|
|
42
|
+
/** Anfrageobjekt als Nutzlast — flache Kopie, damit der Aufrufer sein Objekt behaelt. */
|
|
43
|
+
const nutzlast = (anfrage) => ({ ...anfrage });
|
|
44
|
+
// ---- Kunden -----------------------------------------------------------------
|
|
45
|
+
export async function createCustomer(rufen, customer, optionen = {}) {
|
|
46
|
+
const params = { customer };
|
|
47
|
+
if (optionen.idempotencyKey)
|
|
48
|
+
params['idempotencyKey'] = optionen.idempotencyKey;
|
|
49
|
+
const daten = await rufen('createCustomer', params);
|
|
50
|
+
return pflichtObjekt('createCustomer', daten, 'customer');
|
|
51
|
+
}
|
|
52
|
+
export async function getCustomer(rufen, kennung) {
|
|
53
|
+
const daten = await rufen('getCustomer', nutzlast(kennung));
|
|
54
|
+
return pflichtObjekt('getCustomer', daten, 'customer');
|
|
55
|
+
}
|
|
56
|
+
export async function updateCustomer(rufen, customerId, patch) {
|
|
57
|
+
const daten = await rufen('updateCustomer', { customerId, customer: patch });
|
|
58
|
+
return pflichtObjekt('updateCustomer', daten, 'customer');
|
|
59
|
+
}
|
|
60
|
+
export async function searchCustomers(rufen, suche) {
|
|
61
|
+
const daten = await rufen('searchCustomers', nutzlast(suche));
|
|
62
|
+
const { eintraege, nextCursor } = seite('searchCustomers', daten, 'customers');
|
|
63
|
+
return { customers: eintraege, nextCursor };
|
|
64
|
+
}
|
|
65
|
+
// ---- Rechnungen -------------------------------------------------------------
|
|
66
|
+
export async function issueInvoice(rufen, anfrage) {
|
|
67
|
+
const daten = await rufen('issueInvoice', nutzlast(anfrage));
|
|
68
|
+
return {
|
|
69
|
+
invoice: pflichtObjekt('issueInvoice', daten, 'invoice'),
|
|
70
|
+
replayed: objekt(daten)['replayed'] === true,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
export async function cancelInvoice(rufen, anfrage) {
|
|
74
|
+
const daten = await rufen('cancelInvoice', nutzlast(anfrage));
|
|
75
|
+
return {
|
|
76
|
+
creditNote: pflichtObjekt('cancelInvoice', daten, 'creditNote'),
|
|
77
|
+
original: pflichtObjekt('cancelInvoice', daten, 'original'),
|
|
78
|
+
originalPaidCents: zahl(objekt(daten)['originalPaidCents']),
|
|
79
|
+
replayed: objekt(daten)['replayed'] === true,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
export async function createCreditNote(rufen, anfrage) {
|
|
83
|
+
const daten = await rufen('createCreditNote', nutzlast(anfrage));
|
|
84
|
+
return {
|
|
85
|
+
creditNote: pflichtObjekt('createCreditNote', daten, 'creditNote'),
|
|
86
|
+
remainingCents: zahl(objekt(daten)['remainingCents']),
|
|
87
|
+
replayed: objekt(daten)['replayed'] === true,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
export async function getInvoice(rufen, kennung) {
|
|
91
|
+
const daten = await rufen('getInvoice', nutzlast(kennung));
|
|
92
|
+
return pflichtObjekt('getInvoice', daten, 'invoice');
|
|
93
|
+
}
|
|
94
|
+
export async function listInvoices(rufen, abfrage = {}) {
|
|
95
|
+
const daten = await rufen('listInvoices', nutzlast(abfrage));
|
|
96
|
+
const { eintraege, nextCursor } = seite('listInvoices', daten, 'invoices');
|
|
97
|
+
return { invoices: eintraege, nextCursor };
|
|
98
|
+
}
|
|
99
|
+
// ---- Freigabe und Einrichtung ---------------------------------------------------
|
|
100
|
+
/**
|
|
101
|
+
* Darf dieses Konto ueber die API ausstellen, und was fehlt noch? Laeuft auch
|
|
102
|
+
* ohne Freigabe und vor der Live-Freischaltung — genau dann braucht man die
|
|
103
|
+
* Antwort. Vor dem ersten `issueInvoice` aufrufen und `missing` anzeigen.
|
|
104
|
+
*/
|
|
105
|
+
export async function getInvoiceSetupStatus(rufen) {
|
|
106
|
+
const daten = objekt(await rufen('getInvoiceSetupStatus', {}));
|
|
107
|
+
if (typeof daten['ready'] !== 'boolean' || !Array.isArray(daten['missing'])) {
|
|
108
|
+
throw new KasseneckValidationError('getInvoiceSetupStatus', 'Antwort ohne ready/missing', 'response');
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
ready: daten['ready'],
|
|
112
|
+
environment: daten['environment'] === 'test' ? 'test' : 'live',
|
|
113
|
+
missing: daten['missing'],
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
// ---- Dateien ----------------------------------------------------------------
|
|
117
|
+
/** Das PDF der Rechnung (bei Gutschriften: der Gutschrift), mit eingebetteter Factur-X-Datei. */
|
|
118
|
+
export function getInvoicePdf(rufenBinaer, invoiceId) {
|
|
119
|
+
return rufenBinaer('getInvoicePdf', { invoiceId });
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Die E-Rechnung als XML-Text. Sie kommt im gewohnten Umschlag (`data.xml`)
|
|
123
|
+
* und nicht als rohe Datei: so bleibt ein fachlicher Fehler
|
|
124
|
+
* (`einvoice_incomplete` mit `missing[]`) ein gewoehnlicher Fehler.
|
|
125
|
+
*/
|
|
126
|
+
export async function getInvoiceXml(rufen, invoiceId, format = 'ubl') {
|
|
127
|
+
const daten = await rufen('getInvoiceXml', { invoiceId, format });
|
|
128
|
+
const xml = objekt(daten)['xml'];
|
|
129
|
+
if (typeof xml !== 'string' || !xml) {
|
|
130
|
+
throw new KasseneckValidationError('getInvoiceXml', 'Antwort ohne xml', 'response');
|
|
131
|
+
}
|
|
132
|
+
return xml;
|
|
133
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fehler der Rechnungs-API auswerten — am Code, nie am Text.
|
|
3
|
+
*
|
|
4
|
+
* Es zaehlen nur Codes aus dem Vertrag ([INVOICE_ERROR_CODES]). Ein fremder
|
|
5
|
+
* Code (z. B. aus einer vorgeschalteten Schicht) ist fuer diese Helfer kein
|
|
6
|
+
* Rechnungs-Fehler: wer darauf verzweigt, soll es bewusst ueber
|
|
7
|
+
* `KasseneckApiError.code` tun.
|
|
8
|
+
*/
|
|
9
|
+
import { type InvoiceErrorCode } from './vertrag.js';
|
|
10
|
+
/** Der Fehlercode eines geworfenen Fehlers — `undefined`, wenn es keiner der Rechnungs-API ist. */
|
|
11
|
+
export declare function rechnungFehlerCode(error: unknown): InvoiceErrorCode | undefined;
|
|
12
|
+
/** Kurzform fuer `catch (e) { if (istRechnungFehler(e, 'customer_exists')) … }`. */
|
|
13
|
+
export declare function istRechnungFehler(error: unknown, code: InvoiceErrorCode): boolean;
|
|
14
|
+
/** Ein Feldfehler aus `data.errors[]` einer `validation`-Antwort. */
|
|
15
|
+
export interface RechnungFeldFehler {
|
|
16
|
+
/** Feldpfad in der gesendeten Anfrage, z. B. `items[2].vatRate` oder `customer.vatId`. */
|
|
17
|
+
field: string;
|
|
18
|
+
message: string;
|
|
19
|
+
}
|
|
20
|
+
/** Die Feldfehler einer `validation`-Antwort; leer, wenn es keine sind. */
|
|
21
|
+
export declare function rechnungFeldFehler(error: unknown): RechnungFeldFehler[];
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fehler der Rechnungs-API auswerten — am Code, nie am Text.
|
|
3
|
+
*
|
|
4
|
+
* Es zaehlen nur Codes aus dem Vertrag ([INVOICE_ERROR_CODES]). Ein fremder
|
|
5
|
+
* Code (z. B. aus einer vorgeschalteten Schicht) ist fuer diese Helfer kein
|
|
6
|
+
* Rechnungs-Fehler: wer darauf verzweigt, soll es bewusst ueber
|
|
7
|
+
* `KasseneckApiError.code` tun.
|
|
8
|
+
*/
|
|
9
|
+
import { KasseneckApiError } from '../client/errors.js';
|
|
10
|
+
import { INVOICE_ERROR_CODES } from './vertrag.js';
|
|
11
|
+
const BEKANNT = new Set(INVOICE_ERROR_CODES);
|
|
12
|
+
/** Der Fehlercode eines geworfenen Fehlers — `undefined`, wenn es keiner der Rechnungs-API ist. */
|
|
13
|
+
export function rechnungFehlerCode(error) {
|
|
14
|
+
if (!(error instanceof KasseneckApiError))
|
|
15
|
+
return undefined;
|
|
16
|
+
const code = error.code;
|
|
17
|
+
return code !== undefined && BEKANNT.has(code) ? code : undefined;
|
|
18
|
+
}
|
|
19
|
+
/** Kurzform fuer `catch (e) { if (istRechnungFehler(e, 'customer_exists')) … }`. */
|
|
20
|
+
export function istRechnungFehler(error, code) {
|
|
21
|
+
return rechnungFehlerCode(error) === code;
|
|
22
|
+
}
|
|
23
|
+
/** Die Feldfehler einer `validation`-Antwort; leer, wenn es keine sind. */
|
|
24
|
+
export function rechnungFeldFehler(error) {
|
|
25
|
+
if (!(error instanceof KasseneckApiError))
|
|
26
|
+
return [];
|
|
27
|
+
const roh = error.details['errors'];
|
|
28
|
+
if (!Array.isArray(roh))
|
|
29
|
+
return [];
|
|
30
|
+
const raus = [];
|
|
31
|
+
for (const eintrag of roh) {
|
|
32
|
+
if (eintrag === null || typeof eintrag !== 'object')
|
|
33
|
+
continue;
|
|
34
|
+
const { field, message } = eintrag;
|
|
35
|
+
if (typeof field === 'string' && typeof message === 'string')
|
|
36
|
+
raus.push({ field, message });
|
|
37
|
+
}
|
|
38
|
+
return raus;
|
|
39
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@kreiseck/kasseneck-api/rechnung` — Rechnungen (§ 11 UStG, keine Belege)
|
|
3
|
+
* und Kunden ueber die Rechnungs-API.
|
|
4
|
+
*
|
|
5
|
+
* Ein eigener Unterpfad wie `partner`: der `api_key` eines Kontos, mit dem
|
|
6
|
+
* Rechnungen ausgestellt werden, gehoert auf einen **Server** und soll nicht
|
|
7
|
+
* versehentlich in ein Browser-Buendel der Kasse wandern.
|
|
8
|
+
*
|
|
9
|
+
* Die Beschreibung der Endpunkte steht in der Referenz des Backends
|
|
10
|
+
* (`docs/api/rechnungen.md`). Was hier steht, ist der Vertrag als Daten und die
|
|
11
|
+
* Benutzung des Clients.
|
|
12
|
+
*/
|
|
13
|
+
export { createRechnungApi, type RechnungApi, type RechnungApiOptions } from './api.js';
|
|
14
|
+
export { rechnungKeyAuth, type RechnungKeyAuthOptions } from './auth.js';
|
|
15
|
+
export { istRechnungFehler, rechnungFehlerCode, rechnungFeldFehler, type RechnungFeldFehler, } from './fehler.js';
|
|
16
|
+
export { cancelInvoice, createCreditNote, createCustomer, getCustomer, getInvoice, getInvoicePdf, getInvoiceSetupStatus, getInvoiceXml, issueInvoice, listInvoices, searchCustomers, updateCustomer, } from './endpunkte.js';
|
|
17
|
+
export type { CancelInvoiceRequest, CancelResult, CreditNoteRequest, CreditNoteResult, Customer, CustomerInput, CustomerPage, CustomerSearch, EInvoiceStatus, Invoice, InvoiceDetail, InvoiceItem, InvoiceItemInput, InvoiceListQuery, InvoicePage, InvoiceRecipient, InvoiceSetupGap, InvoiceSetupStatus, InvoiceTotals, IssueInvoiceRequest, IssueResult, } from './typen.js';
|
|
18
|
+
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_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 InvoiceSetupRequirement, type Format, type Feld, } from './vertrag.js';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@kreiseck/kasseneck-api/rechnung` — Rechnungen (§ 11 UStG, keine Belege)
|
|
3
|
+
* und Kunden ueber die Rechnungs-API.
|
|
4
|
+
*
|
|
5
|
+
* Ein eigener Unterpfad wie `partner`: der `api_key` eines Kontos, mit dem
|
|
6
|
+
* Rechnungen ausgestellt werden, gehoert auf einen **Server** und soll nicht
|
|
7
|
+
* versehentlich in ein Browser-Buendel der Kasse wandern.
|
|
8
|
+
*
|
|
9
|
+
* Die Beschreibung der Endpunkte steht in der Referenz des Backends
|
|
10
|
+
* (`docs/api/rechnungen.md`). Was hier steht, ist der Vertrag als Daten und die
|
|
11
|
+
* Benutzung des Clients.
|
|
12
|
+
*/
|
|
13
|
+
export { createRechnungApi } from './api.js';
|
|
14
|
+
export { rechnungKeyAuth } from './auth.js';
|
|
15
|
+
export { istRechnungFehler, rechnungFehlerCode, rechnungFeldFehler, } from './fehler.js';
|
|
16
|
+
export { cancelInvoice, createCreditNote, createCustomer, getCustomer, getInvoice, getInvoicePdf, getInvoiceSetupStatus, getInvoiceXml, issueInvoice, listInvoices, searchCustomers, updateCustomer, } from './endpunkte.js';
|
|
17
|
+
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_SETUP_REQUIREMENTS, KUNDE_FELDER, POSITION_FELDER, RECHNUNG_ANFRAGEN, RECHNUNG_GENAU_EINS, RECHNUNG_MINDESTENS_EINS, } from './vertrag.js';
|