← Übersicht

Rechnungs-Download und Buchhaltung

Rechnungs-Download und Buchhaltung

Stand 2026-07-16: Offline und lokal verifiziert; noch nicht fuer einen unbeaufsichtigten externen Lauf freigegeben. Wetell-Zugangsdaten fehlen im vorgesehenen Pfad, und `api.lexware.io` ist aus dieser Laufzeit aktuell nicht erreichbar. Credentials sind gemaess Scope nicht Gegenstand dieser Umsetzung.

Orientierung

Das System holt Eingangsrechnungen aus dem Wetell-Kundenportal, prueft und archiviert die Original-PDF, liest die Rechnungsdaten aus und uebergibt den Beleg an Lexware Office. Der lokale Zustand verhindert Parallel- und Wiederholungsfehler. Zusaetzlich sucht Lexware vor jedem Create nach derselben Rechnungsnummer, damit auch ein verlorener lokaler State keinen zweiten wirtschaftlichen Beleg erzeugt.

1. Lauf vorbereiten und Anwendung zusammensetzen

`run-rechnung.php` liest nur Konfiguration, erzeugt typisierte Laufzeitobjekte und startet den Use Case. Die Factory verdrahtet Ports und Adapter. Fehlende Pflichtwerte brechen vor Netzwerkmutationen ab.

Schritte: Lauf-ID und Pfade bestimmen; Wetell-/Lexware-Konfiguration validieren; Logger, State, Storage, Extraktoren und Remote-Adapter bauen; exklusiven Ingestionslauf starten.

Symbole: [run-rechnung.php](/code/105), [InvoiceApplicationFactory::create](/code/167), [WetellRuntimeConfig::fromFile](/code/174), [IngestInvoicesUseCase::execute](/code/109).

2. Wetell anmelden und Rechnungen entdecken

Der bevorzugte lokale Node-/Playwright-Dienst startet genau einen Browser fuer seine gesamte Lebensdauer. Jeder Auftrag erhaelt einen isolierten Context. Ein Discover-Auftrag meldet sich im selben Context an und liest danach die Rechnungsseite. Der PHP-Fallback bindet den gesamten Lauf an genau den Adapter, dessen Login erfolgreich war.

Nur HTTPS-URLs des exakt konfigurierten Wetell-Hosts sind erlaubt. Fremde Hosts, Loopback-Ziele, `file:`-URLs und ungepruefte Redirects werden abgewiesen. Der Dienst verlangt immer ein Secret, akzeptiert maximal 64 KiB Request-Body und zwei parallele Jobs.

Schritte: aktiven Adapter waehlen; Loginseite laden; Formular mit derselben Identitaet absenden; Login-Erfolg pruefen; Rechnungsseite laden; PDF-Links normalisieren; `SourceInvoice`-Objekte erzeugen.

Symbole: [FallbackWetellSourceAdapter](/code/131), [HeadlessWetellSourceAdapter](/code/132), [HttpWetellSourceAdapter](/code/133), [WetellUrlPolicy](/code/175), [browser-service.js](/code/182), [server-core.js](/code/186), [policy.js](/code/184).

3. PDF herunterladen und Original sichern

Der Wetell-Port liefert PDF-Bytes, keinen fremden Dateipfad. Der Download-Use-Case verlangt `%PDF-` am Anfang und `%%EOF` am Ende. Adapter und Storage begrenzen die Datei auf 5 MB. Erst danach wird sie atomar unter `rechnungen/originale/Jahr/Monat/` gespeichert und per SHA-256 identifiziert. Eine bestehende Zieldatei wird nur bei identischem Hash wiederverwendet.

Schritte: URL erneut erlauben; Bytes laden; HTTP-/PDF-/Groessenvertrag pruefen; Zielpfad bilden; atomar persistieren; SHA-256 bilden.

Symbole: [DownloadInvoiceUseCase::execute](/code/107), [DateBasedInvoiceStorage](/code/129), [HeadlessWetellSourceAdapter::downloadInvoicePdf](/code/132), [HttpWetellSourceAdapter::downloadInvoicePdf](/code/133).

4. Text und Rechnungsdaten extrahieren

Zuerst liest `pdftotext` den vorhandenen Textlayer. Reicht dessen Konfidenz nicht, rastert der OCR-Fallback PDF-Seiten und uebergibt Bilder an Tesseract. Ein gemeinsamer Parser liest Lieferant, strukturierte Anschrift, echte Rechnungsnummer, Rechnungsdatum sowie Netto, Steuer und Brutto.

Geld wird als exakte Dezimalzeichenfolge verarbeitet, nicht als binaerer Float. Brutto muss positiv sein; Netto plus Steuer muss innerhalb der konfigurierten Toleranz Brutto ergeben. Fehlt ein expliziter Steuerbetrag, wird er exakt aus Brutto minus Netto abgeleitet.

Symbole: [ExtractInvoiceDataUseCase::execute](/code/108), [InvoiceTextParser::parse](/code/148), [PdftotextExtractor](/code/137), [TesseractInvoiceExtractor](/code/139), [Amount](/code/114).

5. Identitaet, Duplikate und State absichern

Eine Rechnung wird ueber Quelle, URL-unabhaengigen fachlichen Schluessel, echte Rechnungsnummer und PDF-Hash erkannt. Der komplette Read-Modify-Remote-Write-Zyklus laeuft unter einem exklusiven Prozesslock. Ungueltiges JSON fuehrt zum Abbruch statt zu einem leeren Zustand; Schreiben erfolgt ueber eindeutige Tempdatei, `fsync` und atomisches Rename.

Fehlerstatus duerfen spaeter erneut verarbeitet werden. Erfolgreiche Treffer mit gleichem Schluessel oder Hash werden uebersprungen. Ein Test mit geaenderter URL bleibt bei genau einem Posting.

Symbole: [SourceInvoice::rechnungsschluessel](/code/126), [IngestInvoicesUseCase](/code/109), [ProcessInvoiceUseCase](/code/164), [InvoiceState](/code/121), [InvoiceRecord](/code/120), [JsonInvoiceStateAdapter](/code/134).

6. Lieferantenkontakt abgleichen

Der Lieferant ist nicht fest kodiert. Lexware wird nach dem extrahierten Namen durchsucht. Wiederverwendung ist nur bei Vendor-Rolle, exakt normalisiertem Firmennamen und passender Rechnungsadresse erlaubt. Mehrere identische Treffer sind ein Fehler. Fehlt ein Treffer, wird ein Kontakt mit `version: 0`, `roles.vendor`, `company.name` und strukturierter Rechnungsadresse angelegt.

Symbole: [ResolveVendorUseCase::execute](/code/113), [LexofficeApiAdapter::findContactByName](/code/136), [LexofficeApiAdapter::createContact](/code/136).

7. Eingangsbeleg in Lexware anlegen

Die Integration verwendet ausschliesslich `https://api.lexware.io`. Vor dem Create fragt sie `/v1/voucherlist` mit Typ `purchaseinvoice` und Rechnungsnummer ab. Ein fachlich passender Treffer wird wiederverwendet; ein Treffer mit abweichendem Kontakt oder Betrag stoppt als Konflikt.

Ein neuer Beleg entsteht ueber `/v1/vouchers`; die PDF wird ueber `/v1/vouchers/{id}/files` angehaengt. Lexware dedupliziert diesen Upload per Checksum. Ohne fachlich belegte Buchungskategorie wird der Voucher bewusst als `unchecked` angelegt; mit konfigurierter Kategorie als `open`. Erfolg verlangt Voucher-ID und File-ID.

Symbole: [PostInvoiceUseCase::execute](/code/112), [LexofficeApiAdapter::postInvoice](/code/136), [LexwareInvoicePayloadFactory](/code/170), [CurlLexwareTransport](/code/165).

8. Status, Fehler und Audit schreiben

Jeder Schritt traegt eine Lauf-ID. Unerwartete Fehler werden nach der aktiven Phase als Download, Extraktion, Kontakt, Posting oder Persistenz klassifiziert. JSONL-Logs redigieren Passwort-, Secret-, Token-, API-Key-, Cookie- und Authorization-Werte rekursiv. Kann das Auditlog nicht geschrieben, geflusht oder synchronisiert werden, scheitert der Lauf sichtbar.

Symbole: [ProcessInvoiceUseCase::classify](/code/164), [JsonLineLogger::logStep](/code/135), [InvoiceRecord::withError](/code/120), [InvoiceState::withRecord](/code/121).

9. Recovery und Exactly-once

Nach jedem fachlichen Uebergang wird State gespeichert. Scheitert der State direkt nach einem erfolgreichen Remote-Create, findet der naechste Lauf den Lexware-Voucher ueber die echte Rechnungsnummer und laedt die Datei idempotent erneut. Der Fehlerinjektionstest belegt dabei zwei Posting-Aufrufe, aber nur einen Remote-Voucher.

Symbole: [ProcessInvoiceUseCase::execute](/code/164), [LexofficeApiAdapter::postInvoice](/code/136), [JsonInvoiceStateAdapter::withExclusiveLock](/code/134).

10. Nachweise und Freigabe

Ein fachlich erfolgreicher Real-Lauf muss gemeinsam belegen: Original-PDF samt SHA-256, korrekten lokalen State, wiederverwendeten oder neu angelegten Kontakt, Voucher-ID, File-ID und einen zweiten idempotenten Lauf ohne neue Remote-Objekte.

Aktuelle lokale Gates: PHPUnit 54 Tests/124 Assertions, PHPStan Level 8 ohne Fehler, keine Produktionsdatei ueber 150 nichtkommentierte LOC, Node 9 Tests, Composer/npm-Locks, systemd-Unit sowie frische/no-op Datenbankmigrationen. Die externe Freigabe bleibt offen, bis Wetell-Credentials im bestehenden externen Pfad verfuegbar und Wetell-/Lexware-E2E erfolgreich sind.

Symbole: [run-rechnung.php](/code/105), [IngestInvoicesUseCase::execute](/code/109), [ProcessInvoiceUseCase::execute](/code/164), [DateBasedInvoiceStorage](/code/129), [LexofficeApiAdapter](/code/136), [server.js](/code/142).

Ergaenzende Session- und Betriebssymbole

  • [WetellSessionHandle](/code/196): opake Bindung eines erfolgreichen Logins an die aktive Portalinstanz.
  • [WetellAuthenticationException](/code/197): verhindert den unzulaessigen Wechsel auf einen Fallback bei falscher Authentifizierung.
  • [InvoiceLogAlertChecker](/code/194): reduziert Betriebsalarme datensparsam auf Run-ID, Fehlercode und Phase.
  • [rechnung-alert-check.php](/code/192): ausfuehrbarer Monitoring-Einstieg fuer JSONL-Auditlogs.

Frühere Versionen

VersionZeitpunktOperation
56 2026-07-16 20:21:22.333882+02 UPDATE
46 2026-07-16 19:56:53.456726+02 UPDATE
27 2026-07-16 17:52:55.104331+02 UPDATE
26 2026-07-16 17:50:13.096008+02 UPDATE
25 2026-07-16 17:47:07.010544+02 UPDATE
24 2026-07-16 17:41:54.288802+02 UPDATE