Ersetzt den LLM-Aufruf (gemma431b via vLLM) durch eine vollstaendig
deterministische Kopfdaten-Extraktion (extract_header_metadata_pure_python
in invoice_logic.py). Empfaenger-Adressblock, Rechnungsnummer, Daten,
Kunden-Nr., Zahlungsziel, Leistungszeitraum und Kunden-E-Mail werden aus
den regelmaessigen Kopf-Labeln und dem Adressaufbau der scitech-Rechnungen
gelesen. Die nachgelagerte Pipeline (Positionen, Beleg-Referenzen, Freitext,
Angebots-/Referenzbezug) bleibt unveraendert.
- Kein Server/Netzwerk mehr noetig, offline-faehig, quasi instant statt
mehrere Sekunden pro Rechnung.
- LLMClient/get_cached_llm und die Imports os/requests/re/InvoiceMetadata
entfernt.
- Verifiziert gegen alle 7 Beispielrechnungen: Kopfdaten korrekt, XRechnung-
(UBL) + ZUGFeRD-(CII) XML wohlgeformt, Bruttobetraege == Endbetrag der PDFs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fuer XRechnung blockt der Export jetzt getrennt fuer BT-10 (Leitweg-ID leer) und BT-49
(elektronische Adresse leer), jeweils mit eigenem Hinweis. Die Kundennummer-Aushilfe verdeckt
bei Behoerden nicht mehr die fehlende Leitweg-ID. ZUGFeRD/B2B unveraendert (kein Block).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ZUGFeRD (B2B) ohne Kunden-E-Mail: statt leerem BT-49 wird der Platzhalter 'X' gesetzt (Feld fehlt
nicht komplett), dazu eine orange Warnung (st.warning), dass eine echte Empfaenger-E-Mail besser
waere. XRechnung bleibt hart blockierend.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
XRechnung: BT-49 bleibt Pflicht (blockiert bei leer). ZUGFeRD (B2B): leer ist erlaubt, es wird
nur ein Hinweis angezeigt, dass eine Empfaenger-E-Mail besser waere (Peppol R010).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Validatoren bemaengelten leeres BT-10 (Kaeuferreferenz, in XRechnung Pflicht). BT-10 nutzt jetzt
die Leitweg-ID (Behoerden) und faellt sonst auf die Kundennummer zurueck - so ist das Feld nie
leer, BT-46 (Kundennummer) bleibt zusaetzlich erhalten. UI-Blocker/Hilfetext angepasst.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bei Unterpositionsnummern ('N.M', z.B. 2.1) lief der Beschreibungs-Scan nach oben ueber die
Referenzzeilen hinweg -> Artikelname war faelschlich die Auftragsbestaetigungs-Zeile und die
Beschreibung enthielt Referenzen + Positionsnummer. Jetzt bricht der Scan auch bei gegliederten
Nummern ab. Seriennummer-Fragmente (reine Ziffern ohne Punkt) bleiben unberuehrt.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
'Teilrechnung' im Rechnungstitel setzt den Rechnungstyp automatisch auf 326, 'Korrekturrechnung'
auf 384; sonst 380. Das Dropdown ist mit dem erkannten Typ vorbelegt und bleibt aenderbar.
Verifiziert gegen 7 echte Rechnungen (ZenIT -> 326, alle anderen -> 380).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- BT-41: Bearbeiter wird aus der Rechnung erkannt (deterministisch) und als editierbares
Kopfdaten-Feld gefuehrt; ersetzt das fixe 'Geschaeftsfuehrung' (kein Fallback, leer bleibt leer).
- BT-34 und BT-43 als zwei getrennte, editierbare Verkaeufer-Felder (elektronische Adresse vs.
Kontakt-E-Mail), beide vorbelegt mit der office-Mail.
- 'Ihr Auftrag' deterministisch in BT-13 (statt LLM-Fehlzuordnung nach BT-14).
- 'Referenznummer' (z.B. SAP) hat kein EN16931-Feld -> in den Freitext (BT-22).
- Generatoren fallen ohne die neuen Keys auf VERKAEUFER zurueck (Golden-Verhalten unveraendert).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Rechts angezeigte Rechnungsdaten werden verworfen, sobald eine neue/andere PDF geladen wird
(nicht mehr die vorige Rechnung bis zum erneuten Extrahieren).
- extract_document_references(): Auftragsbestaetigung->BT-14, Lieferschein->BT-16,
Vergabeangebot->BT-17 deterministisch aus den Referenzzeilen; korrigiert LLM-Fehlzuordnung
(Vergabeangebot landete faelschlich in BT-12 Vertrag). Verifiziert gegen 7 echte Rechnungen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- extract_pre_position_freetext(): liest den Vorlauftext zwischen Tabellenkopf ('Pos.') und
erster Position direkt aus dem PDF-Text (ohne Referenzzeilen), statt sich auf das oft
unvollstaendige LLM-Feld zu verlassen. Ersetzt invoice_note, wenn die Struktur erkannt wird.
Verifiziert gegen 7 echte Rechnungen (vollstaendiger Freitext bei BIM/valantic/degewo/ZenIT,
korrekt leer bei Gemeinde/Stadtwerke).
- ANGEBOT-Regex: Wortgrenze ergaenzt, damit 'VERGABEANGEBOT' (BT-17) nicht mehr faelschlich als
'Angebot' erkannt und tender_id nicht mehr faelschlich geleert wird.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fehler 'unable to find page tree' beim ZUGFeRD-Export mancher PDFs (z.B. von PDF-XChange
erzeugte degewo-Rechnung): factur-x bettete die XML nicht ein und zerstoerte den Seitenbaum.
- build_zugferd_pdf versucht zuerst die Original-PDF; schlaegt das fehl (Absturz ODER XML
nicht eingebettet), wird die PDF mit pikepdf normalisiert (neu gespeichert) und erneut
versucht. Manche PDFs funktionieren nur original, andere nur normalisiert - beide Wege
abgedeckt, Erfolgskontrolle via pdf_has_embedded_facturx.
- Neue Helfer normalize_pdf_bytes() und pdf_has_embedded_facturx() in invoice_logic.
- Verifiziert auf beiden echten Rechnungen (degewo -> Fallback, Gemeinde -> Original).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Positionsscan bricht nur noch bei der ECHTEN Positionsnummer (len(items)+1) ab, nicht
bei alleinstehenden Ziffern-Fragmenten aufgesplitteter Seriennummern (Rechnung R26-224:
Position wurde sonst ganz verworfen). Seriennummern landen jetzt in der Beschreibung (BT-154).
- Angebotsnummer wird aus Vertrag (BT-12)/Vergabeangebot (BT-17) entfernt, falls das LLM sie
dort eingeordnet hat - Angebot gehoert nur in den Freitext (BT-22). Schema-Beschreibungen
entsprechend praezisiert.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rechnung R26-232 Position 3 (Anfahrt, '2,00 pauschal') wurde uebersprungen, weil
'pauschal' nicht in der Einheiten-Erkennung stand. Ergaenzt: Pauschale/Pauschal/Psch.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Kundennummer jetzt BT-46 (eigenes Feld), getrennt von Leitweg-ID (BT-10)
- BT-10 Leitweg-ID: eigenes Feld, leer per Default; nur bei XRechnung Pflicht (BR-DE-15),
bei ZUGFeRD optional (kein Blocker, kein XML-Eintrag wenn leer)
- BT-12 nur noch Vertrag; Vergabeangebot/Ausschreibung als neues Feld BT-17
(UBL OriginatorDocumentReference, CII AdditionalReferencedDocument TypeCode 50)
- 'Angebot <Nr> vom <Datum>' wird erkannt und in den Freitext (BT-22) uebernommen
- BT-49 elektronische Adresse: bei ZUGFeRD optional (kein Blocker, in CII weggelassen wenn leer),
bei XRechnung weiterhin Pflicht; Hinweis: E-Mail oder Netzwerkcode
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ganze Mengen erscheinen als 1.00 statt 1.0000; gebrochene Mengen (z.B. 1.375
Tage) bleiben vollstaendig erhalten. Vereinheitlicht mit dem UBL-Generator.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Extracted-text expander moved back to left column under PDF preview (PDF-Text tab removed, now 3 tabs)
- Export button now generates and downloads in a single click via st.download_button
- ZUGFeRD generation cached (st.cache_data) so it doesn't rerun on every keystroke
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reduces scrolling - Summen und Export bleiben unterhalb der Tabs immer sichtbar.
Reine UI-Umgruppierung, keine Aenderung an Feldern oder Berechnungslogik.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Move all dependency-free business logic (metadata schema, rounding/parsing
helpers, item extraction, and the XRechnung/ZUGFeRD XML generators plus the
PDF/A output-intent helpers) out of main.py into a new invoice_logic.py that
imports only pydantic and pikepdf. This makes the logic unit-testable without
a Streamlit runtime. Pure cut-and-paste, no behavior change; main.py now
imports these names from invoice_logic instead of defining them locally.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>