diff --git a/invoice_logic.py b/invoice_logic.py index 07b5cbb..b9b6fea 100644 --- a/invoice_logic.py +++ b/invoice_logic.py @@ -199,6 +199,28 @@ def extract_pre_position_freetext(text): return "\n".join(freetext_lines) +# Beleg-Referenzen aus den Referenzzeilen: Schluesselwort -> Feld. Zuverlaessiger als die +# LLM-Zuordnung, die z.B. ein Vergabeangebot faelschlich in BT-12 (Vertrag) statt BT-17 einsortiert. +_DOC_REF_PATTERNS = { + "order_id": re.compile(r"\bAUFTRAGSBESTÄTIGUNG\s+([A-Za-z0-9][A-Za-z0-9\-/.]*)", re.IGNORECASE), + "delivery_note_id": re.compile(r"\bLIEFERSCHEIN\s+([A-Za-z0-9][A-Za-z0-9\-/.]*)", re.IGNORECASE), + "tender_id": re.compile(r"\bVERGABEANGEBOT\s+([A-Za-z0-9][A-Za-z0-9\-/.]*)", re.IGNORECASE), +} + + +def extract_document_references(text): + """Liest Beleg-Referenzen deterministisch aus den Referenzzeilen des PDF-Texts: + Auftragsbestaetigung (BT-14), Lieferschein (BT-16), Vergabeangebot (BT-17). Gibt ein Dict nur + mit den gefundenen Feldern zurueck - so lassen sich die (teils falschen) LLM-Zuordnungen gezielt + korrigieren, ohne fehlende Felder zu ueberschreiben.""" + result = {} + for field, pattern in _DOC_REF_PATTERNS.items(): + m = pattern.search(text) + if m: + result[field] = m.group(1).strip() + return result + + def sanitize_filename_part(value): """Macht einen String dateisystem-sicher: entfernt in Dateinamen kritische Zeichen (Pfadtrenner/Sonderzeichen) und fasst Whitespace zu einem Bindestrich zusammen.""" diff --git a/main.py b/main.py index a4dc198..c716981 100644 --- a/main.py +++ b/main.py @@ -32,6 +32,7 @@ from invoice_logic import ( extract_hardware_serial_numbers, extract_angebot_reference, extract_pre_position_freetext, + extract_document_references, ) # ========================================== @@ -147,6 +148,14 @@ with col1: st.subheader("1. Dokument hochladen") uploaded_file = st.file_uploader("Wähle eine Rechnungs-PDF", type=["pdf"]) pdf_text = "" + + # Neue/andere Datei (oder Datei entfernt)? -> zuvor extrahierte Daten verwerfen, damit rechts + # nicht die vorige Rechnung stehen bleibt, bis erneut extrahiert wird. + _current_file_id = f"{uploaded_file.name}:{uploaded_file.size}" if uploaded_file is not None else None + if st.session_state.get("_last_file_id") != _current_file_id: + st.session_state._last_file_id = _current_file_id + st.session_state.extracted_data = None + if uploaded_file is not None: doc = fitz.open(stream=uploaded_file.read(), filetype="pdf") for page in doc: @@ -243,6 +252,16 @@ with col2: if _freetext is not None: final_data["invoice_note"] = _freetext + # Beleg-Referenzen deterministisch aus den Referenzzeilen lesen und die (teils falsch + # zugeordneten) LLM-Werte korrigieren: Auftragsbestaetigung -> BT-14, Lieferschein -> + # BT-16, Vergabeangebot -> BT-17. Nur gefundene Felder ueberschreiben. + _doc_refs = extract_document_references(pdf_text) + for _field, _val in _doc_refs.items(): + final_data[_field] = _val + # Falls das LLM die Vergabeangebot-Nummer faelschlich in BT-12 (Vertrag) hatte: + if _doc_refs.get("tender_id") and str(final_data.get("contract_id", "")).strip() == _doc_refs["tender_id"]: + final_data["contract_id"] = "" + # Angebotsbezug ("Angebot vom ") hat kein eigenes EN16931-Feld - als # Info fuer den Kunden in den Freitext (BT-22). Zusaetzlich: falls das LLM die # Angebotsnummer faelschlich in Vertrag (BT-12) oder Vergabeangebot (BT-17) gepackt