BT-83: Neues Feld "Verwendungszweck / Zahlungsreferenz" im Reiter Basis, mit
der Rechnungsnummer vorbelegt, aber frei überschreibbar - manche Empfänger
verlangen zur Zuordnung eine eigene Referenz. Ein leeres Feld bedeutet "Standard
nehmen", nicht "weglassen". BT-1 bleibt davon unberührt.
Leistungsnachweise: statt einer PDF jetzt bis zu drei (MAX_ATTACHMENT_PDFS),
nebeneinander angezeigt und in Upload-Reihenfolge angehängt. Bei mehr als drei
Dateien wird der Export gesperrt statt still abzuschneiden, damit kein bewusst
hochgeladener Nachweis unbemerkt fehlt.
Verifiziert: 162 Tests, XSD-Validierung mit 0/1/2/3 Anhängen sowie mit
abweichender Zahlungsreferenz.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
BT-20: Das Zahlungsziel erscheint zusätzlich zum strukturierten Fälligkeits-
datum im Klartext ("Zahlbar innerhalb von 14 Tagen ohne Abzug."). Bei Vorkasse
bewusst unterdrückt, da sich beide Angaben widersprechen würden.
BT-83: Die Rechnungsnummer als Verwendungszweck für den automatischen
Zahlungsabgleich - cbc:PaymentID (UBL) bzw. ram:PaymentReference (CII), jeweils
an der schema-korrekten Position der Sequenz.
Beides ergab der Vergleich gegen fünf Eingangs-E-Rechnungen: BT-20 lieferten
vier von fünf Lieferanten, BT-83 zwei von fünf.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Zuvor stand das Feld hinter dem Vorschau-Block und rutschte dadurch nach dem
Hochladen einer Rechnung weit nach unten.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- static/intro.html entfernt, static/intro.mp4 als Quelle
- Overlay nutzt <video> autoplay/muted, Ausblenden exakt per 'ended'-Event
- Replay-Button z-index-Fix (lag hinter Streamlit-Header)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Custom-Favicon assets/favicon.png (256px, schwarzer abgerundeter Hintergrund,
oranger Beleg mit Sägezahn-Unterkante) statt Emoji. page_icon lädt die Datei
über absoluten Pfad, mit Fallback auf 🧾, falls sie fehlt.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Die Lieferadresse (BG-15) wird jetzt erfasst und exportiert - aber nur, wenn sie
von der Rechnungsadresse ABWEICHT (sonst wäre sie reine Dopplung; die
Rechnungsadresse ist meist auch der Lieferort).
- extract_delivery_address: parst den 'Lieferadresse:'-Block (Name/Straße/PLZ/Ort),
E-Mail-Zeilen ignoriert. Block-Zerlegung mit extract_recipient_address geteilt.
- delivery_address_differs: umbruch-/schreibweisenrobuster Vergleich
(Str.=Straße, ß=ss) - so gilt z.B. ein mehrzeilig umbrochener Straßenname nicht
fälschlich als Abweichung.
- Header-Extraktor: delivery_* nur vorbelegt, wenn abweichend; sonst leer.
- Generatoren: UBL cac:Delivery/DeliveryLocation/Address + DeliveryParty (BT-70/
75/77/78/80), CII ShipToTradeParty (vor dem Lieferdatum). Nur wenn Name+Ort
gesetzt sind.
- UI: eingeklappter Bereich 'Abweichende Lieferadresse (BG-15)' im Reiter Adressen -
klappt automatisch auf und ist vorbefüllt, wenn eine Abweichung erkannt wurde;
sonst unsichtbar. Editierbar/leerbar.
Verifiziert: Stadtwerke (Neuendorfstr. abweichend) und Gemeinde (EDV) -> BG-15;
BIM/degewo/ZenIT (gleiche Adresse) -> keine BG-15. ZUGFeRD-PDF mit check_xsd=True
(ShipToTradeParty-Reihenfolge korrekt). Alle 7 Beispielrechnungen unverändert.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Aus dem Vergleich mit eingegangenen Lieferanten-Rechnungen (ZUGFeRD/Factur-X)
übernommen:
1) BT-113 TotalPrepaidAmount/PrepaidAmount = 0,00 immer ausgeben (UBL + CII) -
macht die Summensektion vollständig (4/4 Lieferanten hatten es).
2) BT-86 BIC der Verkäuferbank (DEUTDEDB160) ins XML: UBL
FinancialInstitutionBranch/ID, CII PayeeSpecifiedCreditorFinancialInstitution/
BICID. BIC in VERKAEUFER-Stammdaten ergänzt.
3) ZUGFeRD-Profil-Konsistenz: die CII deklariert jetzt das EN16931-Profil
(urn:cen.eu:en16931:2017, B2B) statt des strengen KoSIT-XRechnung-Profils -
das passt zum factur-x-Level 'en16931'. Damit entfällt der bisherige XMP-
Override (fix_xrechnung_xmp_conformance): factur-x schreibt nativ
'EN 16931'/'1.0', konsistent zum eingebetteten XML. Die reine XRechnung (UBL,
B2G) bleibt beim strengen xrechnung_3.0-Profil.
Verifiziert: ZUGFeRD-PDF mit check_xsd=True erzeugt (Element-Reihenfolge korrekt),
XMP 'EN 16931' == eingebettetes Profil. Alle 7 Beispielrechnungen unverändert.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bei mehrseitigen Rechnungen steht die erste 'Pos.'-Zeile auf Seite 1, die erste
echte Position aber erst nach einem Seitenumbruch. Dadurch rutschte die dazwischen
liegende Seiten-Möblierung (Seitenkopf RECHNUNG..Betrag €, Bank-/Registerzeilen)
in den globalen Freitext BT-22. extract_pre_position_freetext strippt jetzt diese
Möblierung (via _strip_bundle_furniture), bevor die Referenzzeilen entfernt werden.
Verifiziert: R26-252 hat nun leeren BT-22; die 7 bekannten Rechnungen behalten
ihren Freitext unverändert.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rechnungen mit konfigurierten Bündel-Positionen (Signal: 'Menge Komponenten')
haben einen völlig anderen Aufbau: Produktname + Dutzende Komponentenzeilen über
viele Seiten, Preis als 3-Zeilen-Block mittendrin, Service-Tags erst danach. Der
bisherige Parser scheitert daran (falsche Namen, Fehltreffer).
Neu in invoice_logic.py:
- is_bundle_invoice / extract_bundle_items: eigener Parser, verankert am
3-Zeilen-Preisblock (Menge+Einheit / Einzelpreis / Zeilensumme), segmentiert an
den Positions-Headern; entfernt Seitenmöblierung (Kopf RECHNUNG..Betrag €,
Bank-/Registerzeilen, Übertrag/Vortrag). Beschreibung (BT-154) = volle
Komponentenliste + Service-Tags.
- extract_invoice_items: Dispatcher (Bundle vs. Standardparser) - die bekannten
Service-Rechnungen bleiben unverändert.
- extract_stated_tax_basis: liest die ausgewiesene Bemessungsgrundlage aus der
Steuerzeile.
main.py: nutzt den Dispatcher; Summen-Abgleich als Sicherheitsnetz - weicht die
Summe der erkannten Positionen von der ausgewiesenen Bemessungsgrundlage ab,
erscheint eine deutliche Warnung.
Verifiziert an R26-252 (9 Seiten): 4 Positionen korrekt, Summe 217.500,55 =
Bemessungsgrundlage, Brutto 258.825,65 = Endbetrag, valides XRechnung/ZUGFeRD mit
Service-Tags in BT-154. Alle 7 bekannten Rechnungen unverändert.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- resolve_buyer_endpoint: das Schema wird jetzt am finalen Wert entschieden,
egal aus welchem Feld er stammt. Ein '@' (auch versehentlich ins
Leitweg-ID-Feld getippt) fuehrt immer zu Schema EM statt 0204.
- Der sichtbare Hinweis unter dem Schema-Feld benennt jetzt ausdruecklich
beide Felder (Elektronische Adresse BT-49 UND Adress-Schema BT-49-1) mit
ihren effektiven Werten, damit das leere BT-49-Feld nicht als "offen"
missverstanden wird.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- resolve_buyer_endpoint: eine Adresse mit '@' wird immer als Schema EM
gewertet (unabhaengig vom Dropdown), damit ein falsch gewaehltes Schema
nicht durchschlaegt. Ohne '@' entscheidet weiter das gewaehlte Schema
(z.B. GLN 0088); leeres Feld + Leitweg-ID -> 0204 wie bisher.
- main.py: unter dem BT-49-Feld wird jetzt sichtbar angezeigt, was
tatsaechlich exportiert wird - bei Ableitung aus der Leitweg-ID ein
gruener Hinweis, sonst eine kurze Bestaetigung mit Wert + Schema. So
wirkt das (bewusst leere) Feld fuer den Sachbearbeiter nicht mehr offen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1) BT-17 (tender_id): extract_document_references sortiert jetzt nicht nur
ein Vergabeangebot, sondern auch ein einfaches 'Angebot <Nr> vom <Datum>'
in BT-17 ein (Vergabeangebot hat Vorrang; pro Rechnung kommt nur eines
von beiden vor). Der bisherige Angebot->Freitext-Block in main.py entfaellt.
2) BT-49 (elektronische Adresse) aus BT-10 ableiten: neue Funktion
resolve_buyer_endpoint - ist das E-Mail-Feld leer, aber eine Leitweg-ID
(BT-10) vorhanden, wird die Leitweg-ID mit Schema 0204 als BT-49 verwendet.
So muss die Leitweg-ID nur einmal eingegeben werden. Das Schema bleibt an
den Wert gekoppelt (E-Mail -> EM, Leitweg-ID -> 0204), Default bleibt EM.
Blocker + ZUGFeRD-Platzhalter nutzen die abgeleitete Adresse; UI-Hilfen und
das BT-17-Label ('Angebot / Vergabeangebot') entsprechend angepasst.
Verifiziert gegen alle 7 Beispielrechnungen: die 3 Angebot-Rechnungen landen
in BT-17 (im XML), die 2 Vergabeangebote bleiben, Freitext ohne Dopplung.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>