#
nullInvoice
**nullInvoice** ist ein Spring Boot Mikroservice für **automatisierte Rechnungserstellung und -verwaltung** mit **vollständig anpassbaren HTML-Vorlagen**, entwickelt zur Integration in Webshops und SaaS-Plattformen.
## Überblick
Unternehmen nutzen nullInvoice für die Rechnungserstellung, nachdem Verkäufe abgeschlossen sind. Lieferanten werden einmalig über die Web-UI konfiguriert, danach ruft Ihre Anwendung die REST API auf, um konforme Rechnungen bei Bedarf zu erstellen.
**So funktioniert es:**
1. **Setup**: Konfigurieren Sie Lieferanten in der UI mit Firmendaten, Locale, Währung, Steuersätzen, individuellem Branding und Rechnungsvorlagen
2. **Authentifizierung**: Generieren Sie API-Schlüssel im Admin-Dashboard für sicheren REST-API-Zugriff
3. **Integration**: Ihr Webshop/SaaS sendet authentifizierte API-Anfragen an `/api/v1/invoices/generate` unter Verwendung der Lieferanten-ID
4. **Erstellung**: Rechnungen werden aus vollständig anpassbaren HTML-Vorlagen erstellt und als JSON oder PDF mit Metadaten-Headern zurückgegeben
5. **Zustellung**: Ihre Anwendung erhält die Rechnung und kann sie an Kunden weiterleiten oder für die Ablage speichern
### Typischer Integrationsablauf
```
┌─────────────────┐
│ Kunde │
│ schließt │
│ Kauf ab │
└────────┬────────┘
│
▼
┌─────────────────────────────────────────┐
│ Ihr Webshop/SaaS-Plattform │
│ ───────────────────────────────────── │
│ 1. Zahlung verarbeiten │
│ 2. Digitale Quittung ausstellen │
│ (erforderlich) │
│ 3. Kunde fordert Rechnung? ─────────┐ │
└──────────────────────────────────────┼──┘
│
│ API-Aufruf
▼
┌──────────────────────────────┐
│ nullInvoice-Service │
│ ────────────────────────── │
│ POST /api/v1/invoices/ │
│ generate │
│ │
│ - Prüft Lieferanten-ID │
│ - Wendet Vorlage an │
│ - Speichert HTML-Snapshot │
│ - Generiert PDF │
│ - Gibt Rechnung zurück │
└──────────────┬───────────────┘
│
│ Antwort (JSON oder PDF)
▼
┌──────────────────────────────────────────┐
│ Ihr Webshop/SaaS-Plattform │
│ ────────────────────────────────────── │
│ - Empfängt JSON-Metadaten ODER PDF-Datei│
│ - Speichert Rechnungsnummer für Ablage │
│ - Sendet PDF per E-Mail an den Kunden │
└──────────────────────────────────────────┘
```
### Hauptfunktionen
**Vollständig anpassbare Vorlagen**
- Rechnungen basieren auf benutzerdefinierten HTML-Vorlagen mit Inline-CSS
- Vorlagen unterstützen 30+ Platzhalter für Lieferanten-, Kunden- und Finanzdaten
- Standardvorlagen pro Lieferant ermöglichen unterschiedliches Branding je Geschäftseinheit
**Unveränderlichkeit von Dokumenten**
- Jede Rechnung speichert einen **Snapshot der geparsten HTML-Vorlage** zum Zeitpunkt der Erstellung
- Vorlagenänderungen beeinflussen bereits erstellte Rechnungen nicht
- Rechnungen können konsistent aus dem gespeicherten Snapshot neu erstellt werden
- Datenbankberechtigungen verhindern das Löschen von Rechnungsdatensätzen (finanzielle Compliance)
**Multi-Tenant-fähig**
- Konfigurieren Sie mehrere Lieferanten mit unabhängigen Einstellungen (Locale, Währung, Nummerierung, Branding)
- API-Aufrufer geben die Lieferanten-ID an, um Rechnungen unter verschiedenen Geschäftseinheiten zu erstellen
**Flexible Bereitstellung**
- Rückgabe von Rechnungsmetadaten als JSON für die Ablage (`response_type: number`)
- PDF direkt mit Metadaten in den Headern für sofortige Zustellung (`response_type: pdf`)
- PDF später abrufen über `/api/v1/invoices/{invoiceNumber}/pdf`
**Asynchrone Generierungs-Warteschlange**
- Alternativer Pfad für Integrationen, die nicht synchron auf die Generierung warten möchten
- Die Anfrage wird in eine Warteschlange geschrieben, ein Hintergrund-Worker erzeugt die Rechnung, Clients fragen den Status per Polling ab
- Gleiche Nummerierungs-Garantien wie der synchrone Pfad (gemeinsame Lieferanten-Sperre)
- Der Worker lässt sich über `QUEUE_ENABLED=false` deaktivieren (Standard `true`)
**OpenAPI-Dokumentation**
- Interaktive API-Dokumentation unter `/swagger`
- OpenAPI-JSON-Spezifikation unter `/openapi`
- Vollständige Request/Response-Beispiele und Try-it-out-Funktionalität
## Wichtige Hinweise
**⚠️ Sicherheitshinweis**
nullInvoice enthält **integrierte Authentifizierung** (sitzungsbasiertes UI-Login + API-Schlüssel-Authentifizierung für REST-Endpunkte). Dies bietet standardmäßig Sicherheit, ist jedoch weiterhin für den Einsatz in internen/privaten Netzwerken gedacht.
**Empfohlene Bereitstellung:**
- Hinter einer Firewall oder einem VPN
- In einem privaten Netzwerk, das nur für Ihre vertrauenswürdigen Anwendungen erreichbar ist
- Mit aktiviertem HTTPS/TLS für alle Verbindungen
- Hinter einem Reverse Proxy mit konfiguriertem Rate Limiting
**Für den Produktivbetrieb sind zusätzliche Sicherheitsmaßnahmen erforderlich:**
- HTTPS/TLS aktivieren
- Rate Limiting auf Reverse-Proxy-Ebene konfigurieren
- Starke Admin-Passwörter verwenden
- API-Schlüssel regelmäßig rotieren
- API-Schlüssel in sicherem Secret Management speichern (HashiCorp Vault, AWS Secrets Manager usw.)
Siehe Abschnitt [Sicherheit & Best Practices](#sicherheit--best-practices) für eine vollständige Checkliste.
**⚠️ Diese Anwendung ist KEIN Buchhaltungsinstrument.**
nullInvoice ist eine **Pipeline zur Rechnungserstellung**, die Rechnungsdokumente erstellt, speichert und bereitstellt. Es:
- Verfolgt keine Zahlungen oder Zahlungsstatus über einfache "unbezahlt/ausgestellt"-Flags hinaus
- Verwaltet keine Forderungen oder Verbindlichkeiten
- Erstellt keine Finanzberichte oder Bilanzen
- Integriert sich nicht in Buchhaltungssysteme (Hauptbücher, Journale usw.)
- Übernimmt keine Buchhaltung, Abstimmung oder Steuererklärungen
Für umfassendes Finanzmanagement integrieren Sie nullInvoice mit einem dedizierten Buchhaltungssystem. Nutzen Sie diesen Dienst zur Rechnungserstellung und importieren Sie die Rechnungen anschließend in Ihre Buchhaltungssoftware für Nachverfolgung und Compliance.
## Stack
- Java 21, Spring Boot 3.5.3
- MariaDB + JPA
- Thymeleaf (UI)
- OpenHTMLToPDF (PDFBox)
- OpenAPI unter `/openapi`, Swagger UI unter `/swagger`
## Voraussetzungen
**Für Docker-Bereitstellung (empfohlen):**
- Docker
- Docker Compose
**Für lokale Entwicklung:**
- Java 21 (JDK - Eclipse Temurin oder OpenJDK)
- Maven 3.9+
- MariaDB 10.5+ (oder MySQL 8.0+)
- Tailwind CSS Standalone-Binary (für den CSS-Build - von [GitHub releases](https://github.com/tailwindlabs/tailwindcss/releases))
## Datenbank einrichten
**Datenbank erstellen:**
```sql
CREATE DATABASE nullinvoice CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
```
**Dedizierten Anwendungsnutzer mit eingeschränkten Rechten erstellen:**
```sql
CREATE USER 'nullinvoice'@'localhost' IDENTIFIED BY 'your_secure_password';
-- Berechtigungen für normale Operationen und Schema-Migrationen
GRANT SELECT, INSERT, UPDATE ON nullinvoice.* TO 'nullinvoice'@'localhost';
GRANT CREATE, ALTER, INDEX, REFERENCES ON nullinvoice.* TO 'nullinvoice'@'localhost';
FLUSH PRIVILEGES;
```
**Für Remote-Zugriff den Host anpassen:**
```sql
CREATE USER 'nullinvoice'@'%' IDENTIFIED BY 'your_secure_password';
GRANT SELECT, INSERT, UPDATE ON nullinvoice.* TO 'nullinvoice'@'%';
GRANT CREATE, ALTER, INDEX, REFERENCES ON nullinvoice.* TO 'nullinvoice'@'%';
FLUSH PRIVILEGES;
```
**Wichtig: Warum diese eingeschränkten Rechte?**
Rechnungen sind **unveränderliche Finanzdokumente** und dürfen nach dem Erstellen niemals gelöscht werden. Der Anwendungsnutzer ist bewusst eingeschränkt und darf nicht:
- `DELETE` - kann keine Datensätze löschen
- `DROP` - kann keine Tabellen oder Datenbank löschen
- `TRUNCATE` - kann keine Tabellen leeren
- `GRANT` - kann keine Berechtigungen vergeben
Das stellt Datenintegrität und Compliance mit Anforderungen zur Finanzarchivierung sicher. Das `UPDATE`-Recht wird für Statusänderungen (z. B. als bezahlt markieren) und Soft Deletes für Parteien-Datensätze benötigt.
### Schema-Verwaltung
- Das Datenbankschema wird durch **Flyway**-Migrationen verwaltet
- Initiales Schema: `nullInvoice/src/main/resources/db/migration/V1__initial_schema.sql`
- Das Schema wird beim Start automatisch erstellt/aktualisiert
- Hibernate DDL-Modus ist auf `none` gesetzt (Flyway verwaltet alle Schemaänderungen)
## Konfiguration
Eine Beispiel-Konfigurationsdatei liegt unter `.env.example`. Kopieren Sie diese nach `.env` und passen Sie die Werte an Ihre Umgebung an.
Umgebungsvariablen (Standardwerte in Klammern):
- `TZ` - **ERFORDERLICH** System-Zeitzone (Europe/Sofia)
- `APP_PORT` (8080)
- `DB_HOST` (localhost)
- `DB_PORT` (3306)
- `DB_USER` (nullinvoice)
- `DB_PASSWORD` (leer)
- `DB_NAME` (nullinvoice)
- `DB_PARAMS` - **ERFORDERLICH** JDBC-Parameter, die an die Verbindungs-URL angehängt werden
### **WICHTIG: Zeitzonen-Konfiguration**
**Sie MÜSSEN die Zeitzone an ZWEI Stellen setzen:**
1. **`TZ`** Umgebungsvariable - setzt die System-/Anwendungszeitzone
2. **`serverTimezone`** Parameter in `DB_PARAMS` - setzt die Zeitzone der Datenbankverbindung
**Beide Werte MÜSSEN mit der Zeitzone des Datenbankservers übereinstimmen**, damit Datums-/Zeitwerte korrekt interpretiert und gespeichert werden.
Beispielkonfiguration:
```bash
TZ=Europe/Sofia
DB_PARAMS=?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Europe/Sofia
```
Häufige Zeitzonenwerte: `UTC`, `Europe/London`, `America/New_York`, `Asia/Tokyo`. Siehe [vollständige Liste](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones).
**Nicht übereinstimmende Zeitzonen führen zu falschen Rechnungsdaten und Zeitstempeln.**
### Erste Admin-Einrichtung
Beim ersten Start wird die Anwendung auf `/setup` umleiten, um das initiale Admin-Konto zu erstellen. Dies ist erforderlich, bevor andere Funktionen genutzt werden können.
**Zurücksetzen und neues Admin-Konto erstellen (Notfall):**
1. Anwendung stoppen
2. Tabellen `users` und `api_keys` in der Datenbank leeren
3. Anwendung neu starten
4. Den `/setup`-Flow erneut abschließen
**Sicherheitsempfehlungen:**
- Starke Passwörter für das Admin-Konto verwenden
- Passwort-Hinweis setzen (optional, aber empfohlen)
- Separate API-Schlüssel für unterschiedliche Umgebungen generieren (dev, staging, prod)
- Nicht verwendete API-Schlüssel widerrufen
## Sicherheit & Best Practices
**Authentifizierungsarchitektur:**
- **UI-Zugriff:** Sitzungsbasierte Authentifizierung mit Form-Login
- **API-Zugriff:** Stateless Bearer-Token-Authentifizierung (API-Schlüssel)
- **Passwörter:** BCrypt-Hash mit 10 Runden
- **API-Schlüssel:** UUID-Format, BCrypt-gehasht, werden nur einmal angezeigt
**Sitzungsdauer:**
Standardmäßig laufen UI-Sitzungen nach **30 Minuten Inaktivität** ab (Spring Boot/Tomcat Standard). Benutzer werden automatisch abgemeldet und zur Anmeldeseite weitergeleitet.
Um die Sitzungsdauer anzupassen, fügen Sie Folgendes zu `nullInvoice/src/main/resources/application.yml` hinzu:
```yaml
server:
servlet:
session:
timeout: 60m # Optionen: 15m, 30m, 1h, 2h, usw.
```
Gängige Timeout-Werte:
- `15m` - 15 Minuten (strengere Sicherheit)
- `30m` - 30 Minuten (Standard)
- `1h` - 1 Stunde (Komfort für aktive Benutzer)
- `8h` - 8 Stunden (erweitert für lange Sitzungen)
**Checkliste für den Produktivbetrieb:**
- HTTPS/TLS für alle Verbindungen aktivieren
- Starke Admin-Passwörter verwenden
- Separate API-Schlüssel pro Anwendung/Umgebung erstellen
- Hinter Firewall oder VPN betreiben
- Rate Limiting auf Reverse-Proxy-Ebene konfigurieren
- Logging und Monitoring einrichten
- API-Schlüssel-Nutzung regelmäßig prüfen (Zeitstempel der letzten Nutzung)
- Nicht verwendete oder kompromittierte API-Schlüssel sofort widerrufen
- API-Schlüssel in Umgebungsvariablen halten, niemals im Code
- Sicheres Secret Management verwenden (HashiCorp Vault, AWS Secrets Manager usw.)
**CSRF-Schutz:**
- Aktiviert für alle UI-Formulare
- Deaktiviert für API-Endpunkte (stateless Bearer-Token-Auth)
**Sicherheit beim ersten Start:**
- Die Anwendung ist unbenutzbar, bis ein Admin-Konto über `/setup` erstellt wurde
- Die Setup-Seite ist nur erreichbar, wenn noch kein Admin existiert
- Nach dem Setup ist Login für alle Funktionen erforderlich
## Docker Compose
**Offizielle Docker-Images:**
Vorgefertigte Images sind auf [Docker Hub](https://hub.docker.com) verfügbar. Suchen Sie nach dem offiziellen nullInvoice-Image, um den Build-Schritt zu überspringen.
Build ohne Cache:
```bash
docker compose build --no-cache
```
Stack starten:
```bash
docker compose up -d
```
Stack stoppen:
```bash
docker compose down
```
## Lokale Entwicklung (ohne Docker)
### Setup
1. Stellen Sie sicher, dass eine MariaDB-Instanz läuft und erstellen Sie die Datenbank. Siehe [Datenbank einrichten](#datenbank-einrichten)
2. Tailwind CSS bauen (vor dem ersten Start erforderlich):
```bash
./build-tailwind.sh
```
3. Projekt mit Maven bauen:
```bash
cd nullInvoice
mvn clean package
```
4. Anwendung starten:
```bash
java -jar target/nullinvoice-0.0.1-SNAPSHOT.jar
```
### Verwendung von NetBeans IDE
Das Projekt wurde mit NetBeans erstellt und kann als Maven-Projekt mit dem Spring-Boot-Plugin geöffnet werden.
**Umgebungsvariablen in NetBeans setzen:**
Option 1 - Über die IDE:
1. Rechtsklick auf das Projekt >> Properties
2. Zu Actions >> Run navigieren
3. Umgebungsvariablen setzen: `APP_PORT`, `DB_NAME`, `DB_USER`, `DB_PASSWORD`
Option 2 - Direkt bearbeiten:
- `nbactions.xml` im Projekt-Root anpassen
## Erste Schritte
### Ersteinrichtung beim ersten Start
Beim ersten Zugriff werden Sie zu `/setup` weitergeleitet, um das initiale Admin-Konto zu erstellen:
1. `http://localhost:8080` öffnen (oder Ihre konfigurierte URL)
2. Sie werden automatisch zu `/setup` weitergeleitet
3. Admin-Konto mit Benutzername, Passwort und optionalem Passwort-Hinweis erstellen
4. Nach dem Setup werden Sie zu `/login` weitergeleitet
**Anmeldung:**
- Anmeldeseite unter `/login` aufrufen
- Die erstellten Admin-Zugangsdaten verwenden
- Passwort-Hinweis ist über das Info-Icon verfügbar (falls konfiguriert)
**Admin-Dashboard:**
Nach der Anmeldung finden Sie das Admin-Dashboard im Benutzermenü:
- Admin-Passwort ändern
- API-Schlüssel für REST-API-Zugriff generieren
- API-Schlüssel widerrufen
- API-Schlüssel-Nutzung anzeigen (Zeitstempel der letzten Nutzung)
### Ersten Lieferanten einrichten
**Voraussetzungen:**
- Ersteinrichtung abgeschlossen (Admin-Konto erstellt)
- Mit Admin-Zugangsdaten angemeldet
Bevor Sie Rechnungen über die API erstellen können, müssen Sie mindestens einen Lieferanten über die Web-UI konfigurieren. Lieferanten definieren Firmendaten, Locale, Währung, Steuersätze, individuelles Branding (über Vorlagen) und die Rechnungsnummerierung für die Rechnungserstellung.
1. In die Web-UI unter `http://localhost:8080` einloggen (oder Ihren konfigurierten Port)
2. Zu Lieferanten navigieren
3. Neuen Lieferanten mit Firmendaten erstellen
4. Locale, Währung und Steuereinstellungen konfigurieren
5. Einstellungen für die Rechnungsnummerierung setzen (Präfix, Stellen)
6. Lieferanten-ID für die API-Integration notieren
7. API-Schlüssel unter Admin > API-Schlüssel für REST-API-Zugriff generieren
**Beispiele für die Lieferanteneinrichtung finden Sie unter `docs/example-images` - `en-us` für ein US- oder Nicht-EU-Beispiel und `eu-de` für ein EU-Beispiel.**
Nach der Konfiguration können Sie eine XHTML-Vorlage einrichten und die Lieferanten-ID (oben links in der Lieferantenbearbeitung) verwenden, um API-Aufrufe an `/api/v1/invoices/generate` zu senden.
## Funktionen
- Lieferanten und Kunden mit Soft Delete und Eindeutigkeitsprüfungen verwalten.
- Rechnungsvorlagen mit individuellem Branding erstellen und verwalten, mit globalem Standard und Lieferanten-Standard.
- Rechnungen erstellen, wobei HTML-Snapshots im Rechnungseintrag gespeichert werden.
- Rechnungen bei Bedarf als PDF rendern.
- Rechnungen nach Nummer, Datum, Kunde, Lieferant und Status suchen und sortieren.
- Rechnungsstatus als `unpaid` oder `issued` (bezahlt/endgültig) verwalten.
## Rechnungslebenszyklus und Status
- Statuswerte sind `unpaid` und `issued`. `issued` gilt als bezahlt und endgültig.
- Die Erstellung einer Rechnung über die API führt immer zu `issued`, und die API akzeptiert keine Status-Overrides.
- Die Erstellung über die UI kann eine Rechnung nur dann als `unpaid` markieren, wenn ein Fälligkeitsdatum gesetzt ist.
- Unbezahlte Rechnungen können auf der Rechnungsdetailseite als `issued` markiert werden.
- Ausgestellte Rechnungen können nicht wieder auf unbezahlt zurückgesetzt werden.
## UI-Verhalten
- `/invoices/new` erstellt Rechnungen mit dem im Cookie ausgewählten Lieferanten (falls vorhanden).
- Der Schalter für `unpaid` ist deaktiviert, bis ein Fälligkeitsdatum gesetzt ist.
- `/invoices` unterstützt Filterung nach Lieferant (Dropdown) und Suche nach Nummer, Datum oder Kunde. Die Datumssuche akzeptiert ISO (`YYYY-MM-DD`) oder `dd.MM.yyyy`.
- Die Rechnungsliste kann nach Status sortiert werden; die Sortierung nach Status wechselt die Reihenfolge von `unpaid` und `issued`.
- `/invoices/{invoiceNumber}` zeigt Status, Summen, gespeicherte HTML-Vorschau und bietet eine Einweg-Aktion „Als bezahlt markieren“ für unbezahlte Rechnungen.
## UI-Workflow
1) Lieferanten: Zuerst Lieferantendaten einrichten. Das Lieferantenprofil bestimmt Locale, Währung, Rechnungsnummerierung und Standardsteuersatz.
2) Vorlagen: Vorlage für individuelles Branding erstellen und als Standard festlegen. Globalen Standard verwenden oder einen Lieferanten-Standard setzen, um den globalen Standard zu überschreiben.
3) Kunden (optional): Kunden können manuell hinzugefügt werden, aber die Rechnungserstellung legt Kunden auch automatisch an/aktualisiert sie.
4) Aktiven Lieferanten auswählen: Standardlieferant in der UI auswählen, wodurch ein Cookie gesetzt wird, der bei der Rechnungserstellung verwendet wird.
5) Lieferanten-ID für die API: Lieferanten im Bearbeitungsmodus öffnen und die Lieferanten-ID oben links verwenden.
6) Rechnungen: Rechnungen auflisten, suchen und filtern; Rechnung öffnen, um Details zu prüfen und unbezahlte Rechnungen als ausgestellt/bezahlt zu markieren.
7) Rechnung erstellen: Kundendaten eingeben oder einen vorhandenen Kunden suchen, Positionen hinzufügen und den Steuersatz pro Position setzen. Fehlt bei einer Position der Steuersatz, gilt der Standardsteuersatz des Lieferanten.
8) Rabatte und Notizen: Einen pauschalen Rabatt eingeben oder den Rabatt-%-Rechner verwenden; Notizen hinzufügen und die Rechnung erstellen, um die Übersichtsseite zu sehen.
Die Rechnungserstellung verwendet eine pessimistische Schreibsperre auf dem Lieferanten-Datensatz, um Race Conditions bei der Berechnung der nächsten Rechnungsnummer zu vermeiden. Dies blockiert konkurrierende Anfragen für denselben Lieferanten, bis die Nummer vergeben ist.
## Authentifizierung
**Authentifizierung erforderlich:** Alle API-Endpunkte erfordern entweder:
- Bearer-Token im `Authorization`-Header (empfohlen für Integrationen)
- Aktive Sitzung (wenn Sie über die Web-UI eingeloggt sind)
### API-Schlüssel-Authentifizierung (empfohlen für externe Integrationen)
Generieren Sie einen API-Schlüssel im Admin-Dashboard und fügen Sie ihn in den `Authorization`-Header ein:
```bash
curl -H "Authorization: Bearer YOUR_API_KEY_HERE" \
http://localhost:8080/api/v1/invoices
```
### Sitzungsbasierte Authentifizierung (für UI-initierte Anfragen)
Wenn Sie über die Web-UI eingeloggt sind, wird Ihre Sitzung automatisch für API-Anfragen genutzt.
**API-Schlüssel generieren:**
1. In die Web-UI einloggen
2. Zu Admin (Benutzermenü) navigieren
3. Zum Abschnitt "API-Schlüssel" scrollen
4. Optional eine Beschreibung eingeben und auf „Schlüssel generieren“ klicken
5. **Schlüssel sofort kopieren** - er wird nicht erneut angezeigt
6. Der Schlüssel wird im Format `Authorization: Bearer {key}` angezeigt
**Sicherheitshinweise:**
- API-Schlüssel sind in der Datenbank gehasht (BCrypt)
- Schlüssel können jederzeit im Admin-Dashboard widerrufen werden
- Zeitstempel der letzten Nutzung wird pro Schlüssel erfasst
- Separate Schlüssel für verschiedene Anwendungen/Umgebungen generieren
## REST API (Basis: `/api/v1`)
### Rechnungserstellung
`POST /api/v1/invoices/generate`
**Authentifizierung erforderlich** - Bearer-Token im `Authorization`-Header angeben.
- Erfordert `supplier_id` und `client`.
- `response_type` unterstützt `number` (Standard) oder `pdf`.
- `number`: gibt JSON nur mit Rechnungsmetadaten zurück
- `pdf`: liefert das PDF direkt mit Metadaten in den Response-Headern
- Status ist immer `issued` für API-erstellte Rechnungen.
Beispielanfrage (bestehender Kunde per ID):
```bash
curl -X POST http://localhost:8080/api/v1/invoices/generate \
-H "Authorization: Bearer YOUR_API_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{
"response_type": "number",
"supplier_id": 1,
"client": { "id": 42 },
"items": [
{ "description": "Consulting", "quantity": 1, "unit_price": 1000, "tax_rate": 0.2 }
],
"issue_date": "2026-01-16",
"due_date": "2026-01-30",
"currency_code": "EUR",
"notes": "Thank you"
}'
```
Beispielanfrage (neue Kundendaten):
```bash
curl -X POST http://localhost:8080/api/v1/invoices/generate \
-H "Authorization: Bearer YOUR_API_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{
"response_type": "number",
"supplier_id": 1,
"client": {
"name": "Client Co",
"addressLine1": "2 Side St",
"city": "Burgas",
"country": "BG",
"taxId": "123",
"vatId": "BG123"
},
"items": [
{ "description": "Consulting", "quantity": 1, "unit_price": 1000, "tax_rate": 0.2 }
]
}'
```
Beispielantwort (response_type: number):
```json
{
"status": "issued",
"message": "invoice generated",
"invoiceNumber": "INV-000001",
"issueDate": "2026-01-16"
}
```
Beispielanfrage (response_type: pdf für direkten PDF-Download):
```bash
curl -X POST http://localhost:8080/api/v1/invoices/generate \
-H "Authorization: Bearer YOUR_API_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{
"response_type": "pdf",
"supplier_id": 1,
"client": {
"name": "Client Co",
"addressLine1": "2 Side St",
"city": "Plovdiv",
"country": "BG",
"taxId": "123",
"vatId": "BG123"
},
"items": [
{ "description": "Consulting", "quantity": 1, "unit_price": 1000, "tax_rate": 0.2 }
]
}' \
-o invoice.pdf -i
```
Beispielantwort (response_type: pdf):
```
HTTP/1.1 200 OK
Content-Type: application/pdf
Content-Disposition: attachment; filename="INV-000001.pdf"
X-Invoice-Number: INV-000001
X-Invoice-Status: issued
X-Invoice-Issue-Date: 2026-01-16
[PDF binary data]
```
Die PDF-Antwort enthält Rechnungsmetadaten in benutzerdefinierten Response-Headern (`X-Invoice-Number`, `X-Invoice-Status`, `X-Invoice-Issue-Date`), sodass Ihre Anwendung die Rechnungsdetails speichern kann, während sie die PDF-Datei direkt erhält.
### Rechnungen auflisten und filtern
`GET /api/v1/invoices`
**Authentifizierung erforderlich** - Bearer-Token im `Authorization`-Header angeben.
- Optionaler Filter: `status=unpaid` oder `status=issued`
Beispielanfrage:
```bash
curl -H "Authorization: Bearer YOUR_API_KEY_HERE" \
http://localhost:8080/api/v1/invoices?status=unpaid
```
Beispielantwort (gefiltert):
```json
[
{ "invoiceNumber": "INV-000002", "status": "unpaid" }
]
```
### Rechnung abrufen
**Authentifizierung erforderlich** - Bearer-Token im `Authorization`-Header angeben.
Rechnungsmetadaten abrufen:
```bash
curl -H "Authorization: Bearer YOUR_API_KEY_HERE" \
http://localhost:8080/api/v1/invoices/INV-000001
```
Rechnung als PDF herunterladen:
```bash
curl -H "Authorization: Bearer YOUR_API_KEY_HERE" \
http://localhost:8080/api/v1/invoices/INV-000001/pdf \
-o invoice.pdf
```
### Asynchrone Rechnungserstellungs-Warteschlange
Alternative zu `POST /api/v1/invoices/generate` für Aufrufer, die nicht synchron auf die Generierung warten möchten. Die Anfrage wird in einer Warteschlange persistiert, ein Hintergrund-Worker erzeugt die Rechnung, und Clients fragen den Status per Polling ab.
Beide Pfade durchlaufen dieselbe Lieferanten-Zeilensperre, sodass Rechnungsnummern über den synchronen und asynchronen Pfad hinweg fortlaufend bleiben.
#### Generierungsanfrage einreichen
`POST /api/v1/invoice-requests`
**Authentifizierung erforderlich** - Bearer-Token im `Authorization`-Header angeben.
Der Request-Body ist identisch mit `POST /api/v1/invoices/generate`. Das Feld `response_type` wird ignoriert - asynchrone Anfragen persistieren nur die Warteschlangenzeile; nach Abschluss wird die Rechnung über die Standard-Abrufendpunkte mit der `invoiceNumber` aus der Statusantwort geladen.
```bash
curl -X POST http://localhost:8080/api/v1/invoice-requests \
-H "Authorization: Bearer YOUR_API_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{
"supplier_id": 1,
"client": { "id": 42 },
"items": [
{ "description": "Consulting", "quantity": 1, "unit_price": 1000, "tax_rate": 0.2 }
]
}'
```
Antwort (201 Created):
```json
{
"requestId": 42,
"status": "PENDING",
"message": "invoice generation request queued",
"createdAt": "2026-05-17T10:00:00"
}
```
#### Status abfragen
`GET /api/v1/invoice-requests/{requestId}`
```bash
curl -H "Authorization: Bearer YOUR_API_KEY_HERE" \
http://localhost:8080/api/v1/invoice-requests/42
```
Antwort bei `COMPLETED`:
```json
{
"requestId": 42,
"status": "COMPLETED",
"invoiceId": 107,
"invoiceNumber": "INV-000042",
"attempts": 1,
"createdAt": "2026-05-17T10:00:00",
"completedAt": "2026-05-17T10:00:02"
}
```
#### Statuszyklus
```
neue Anfrage -> PENDING
Worker erfolgreich -> COMPLETED
Worker wirft Fehler -> PENDING (wird erneut versucht)
attempts >= max_attempts -> FAILED (terminal)
```
`PENDING` ist der einzige wiederholbare Status. Abstürze während der Verarbeitung werden vollständig zurückgerollt und zählen nicht zum Wiederholungsbudget. Nach `COMPLETED` die Rechnung über `GET /api/v1/invoices/{invoiceNumber}` (JSON) oder `/api/v1/invoices/{invoiceNumber}/pdf` (PDF) abrufen.
Der Worker lässt sich über die Umgebungsvariable `QUEUE_ENABLED=false` deaktivieren.
### Parteien
**Authentifizierung erforderlich** - Bearer-Token im `Authorization`-Header angeben.
- `GET /api/v1/parties/client?taxId=...&vatId=...` (erfordert taxId oder vatId)
- `GET /api/v1/parties/clients/search?q=...` (mindestens 2 Zeichen)
- `GET /api/v1/parties/suppliers`
Beispiel:
```bash
curl -H "Authorization: Bearer YOUR_API_KEY_HERE" \
http://localhost:8080/api/v1/parties/suppliers
```
### Health
- `GET /api/v1/health` (keine Authentifizierung erforderlich)
## Vorlagen und PDFs
- Vorlagen befinden sich in `invoice_templates` und müssen HTML-Inhalt enthalten.
- Die Rechnungserstellung erfordert eine wirksame Standardvorlage (lieferantenspezifisch oder global).
- Lieferanten können den globalen Standard mit einer lieferantenspezifischen Vorlage überschreiben.
- Erstellte Rechnungen speichern einen HTML-Snapshot für konsistentes Re-Rendering.
- PDFs werden aus dem gespeicherten HTML-Snapshot gerendert, sofern vorhanden.
### Formatanforderungen für Vorlagen
Vorlagen müssen im **XHTML**-Format vorliegen, damit PDFs korrekt gerendert werden:
- XML-Deklaration hinzufügen: ``
- XHTML-Namespace verwenden: ``
- Sämtliches CSS muss **inline** in einem `