Commit Graph

84 Commits

Author SHA1 Message Date
dfuhrmann3 23968bb911 fix(v3): '@' -> EM auch im Leitweg-Feld; BT-49-Info nennt beide Felder
- 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>
2026-07-14 15:24:56 +02:00
dfuhrmann3 2e259589c0 ui(v3): BT-49-Ableitung sichtbar machen + Schema robuster (E-Mail=EM)
- 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>
2026-07-14 15:18:52 +02:00
dfuhrmann3 991384a362 feat(v3): Angebot -> BT-17 und BT-49 aus Leitweg-ID ableiten
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>
2026-07-14 15:13:29 +02:00
dfuhrmann3 51ed31b1e8 feat(v3): KI-freie Kopfdaten-Extraktion (ohne vLLM/LLM-Backend)
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>
2026-07-14 11:47:38 +02:00
dfuhrmann3 4675cfd2a0 fix(bt-10): block XRechnung export when Leitweg-ID empty (separate blocker from BT-49)
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>
2026-07-13 16:14:57 +02:00
dfuhrmann3 271f54ecf4 ui: remove validation note from footer (technical, not actionable for users)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 16:06:59 +02:00
dfuhrmann3 5dd33c6ff8 ui: dark-mode-safe tabs (no hardcoded bg; inactive tabs weaker red, active strong red)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 16:04:06 +02:00
dfuhrmann3 b52d2a3714 ui: use formal address (Sie) in user-facing hints
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 16:01:35 +02:00
dfuhrmann3 58e589aa7a ui: remove BT-49 required-asterisk; drop noisy serial-numbers caption
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 15:58:37 +02:00
dfuhrmann3 3dcb293ba4 ui: clearer BT-49 blocker hint for XRechnung (guide to Leitweg-ID + scheme 0204)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 15:54:00 +02:00
dfuhrmann3 e6897bd263 ui: larger, more prominent tabs + hint to check all three tabs before export
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 15:49:06 +02:00
dfuhrmann3 34251a7ad2 fix(bt-49): use 'X' placeholder + orange warning for ZUGFeRD without buyer address
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>
2026-07-13 15:45:37 +02:00
dfuhrmann3 d2d09054d2 fix(bt-49): block only for XRechnung; soft warning (not blocker) for ZUGFeRD
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>
2026-07-13 15:38:46 +02:00
dfuhrmann3 308cddedef fix(bt-10): fall back to customer number when no Leitweg-ID (BR-DE-15)
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>
2026-07-13 15:29:57 +02:00
dfuhrmann3 71bb9885c6 feat: auto-detect invoice type (BT-3) from title (Teilrechnung->326)
'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>
2026-07-13 15:08:56 +02:00
dfuhrmann3 ac233baa2f feat: BT-41 Bearbeiter, BT-34/BT-43 split, Ihr Auftrag->BT-13, Referenznummer->Freitext
- 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>
2026-07-13 15:03:51 +02:00
dfuhrmann3 f92c4ffac6 feat: clear data panel on new upload; deterministic doc-reference routing (Vergabeangebot->BT-17)
- 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>
2026-07-13 14:49:35 +02:00
dfuhrmann3 3832d5cdf2 feat(parser): deterministic pre-position free text into BT-22 (Task 3a)
- 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>
2026-07-13 13:03:51 +02:00
dfuhrmann3 df81c011d3 fix(zugferd): robust factur-x embedding for varied PDF structures
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>
2026-07-13 11:48:48 +02:00
dfuhrmann3 29034d8501 fix(parser): capture positions with split serial numbers; keep Angebot out of BT-12/BT-17
- 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>
2026-07-13 11:30:27 +02:00
dfuhrmann3 5a9929aa9e feat: BT-46 Kundennummer, BT-10 Leitweg-ID, BT-17 Vergabeangebot, Angebot->Freitext, format-abh. Pflichtfelder
- 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>
2026-07-13 10:38:56 +02:00
dfuhrmann3 9a7dca1498 feat: descriptive export filenames, Vorkasse note (BT-20), dynamic BT-49 scheme
- Export-Dateiname: <Rechnungsnummer>_<Empfaenger>_<Rechnungsdatum> (dateisicher)
- Vorkasse-Schalter schreibt Hinweis in Zahlungsbedingungen (BT-20); Zahlungsmittelcode unveraendert
- BT-49 elektronische Kaeufer-Adresse: Schema (BT-49-1) manuell waehlbar (EM/0204/0088), Default EM

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 11:03:32 +02:00
dfuhrmann3 8a7990f95f refactor(ui): move ZUGFeRD validation hint from per-export box into footer
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 16:31:55 +02:00
dfuhrmann3 19cdff820d feat(ui): rename tabs to Kopfdaten/Adressen/Positionen
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 16:24:42 +02:00
dfuhrmann3 1992c30991 feat(ui): PDF-Text back under PDF preview; one-click export+download
- 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>
2026-07-07 16:21:53 +02:00
dfuhrmann3 c07eb860fa feat(ui): group validation fields into tabs (Basis/Parteien/Positionen/PDF-Text)
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>
2026-07-07 16:14:17 +02:00
dfuhrmann3 efdee4cfd6 feat: default export format to ZUGFeRD instead of XRechnung
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 16:01:53 +02:00
dfuhrmann3 16566ef858 chore: remove unused Decimal import from main.py (final review cleanup)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 15:32:37 +02:00
dfuhrmann3 3b3a2dce90 feat: extend regex parser for IT-Systemhaus service periods, contract types, and serial numbers
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 15:16:30 +02:00
dfuhrmann3 0dd9916079 feat: add document-level discount (BT-21) and Skonto payment terms (BT-20) 2026-07-07 15:10:35 +02:00
dfuhrmann3 d7b851ae36 feat: support mixed VAT rates per line (BG-23) in both XML generators 2026-07-07 15:03:09 +02:00
dfuhrmann3 6ecd59e5ea feat: make payment means code (BT-81) dynamically selectable 2026-07-07 14:54:17 +02:00
dfuhrmann3 ae986b4f3f refactor: extract pure invoice logic into invoice_logic.py for testability
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>
2026-07-07 13:43:13 +02:00
dfuhrmann3 994ad03387 Footer Text Update 2026-07-07 11:12:06 +02:00
dfuhrmann3 a0d80c5d08 Merge branch 'test-version' Update auf ZUGFeRD mit Konformität und PDF-Ansicht 2026-07-07 10:58:46 +02:00
dfuhrmann3 7b5cd86974 Test 2026-07-07 10:52:43 +02:00
dfuhrmann3 fa977594eb Test 2026-07-07 10:23:51 +02:00
dfuhrmann3 aad83afd54 Test 2026-07-07 10:03:41 +02:00
dfuhrmann3 ebf172276a Test 2026-07-07 09:49:28 +02:00
dfuhrmann3 e80d011c88 Test 2026-07-06 14:09:15 +02:00
dfuhrmann3 076e976298 Test 2026-07-06 13:42:28 +02:00
dfuhrmann3 75c9d113bc Test 2026-07-06 12:53:48 +02:00
dfuhrmann3 3e59cd188e Test 2026-07-06 11:22:11 +02:00
dfuhrmann3 37b7986831 Test 2026-07-06 11:12:39 +02:00
dfuhrmann3 ec8352682e Test 2026-07-06 10:54:43 +02:00
dfuhrmann3 381216d619 Test 2026-07-06 10:51:36 +02:00
dfuhrmann3 00fe916c06 Test 2026-07-06 10:30:35 +02:00
dfuhrmann3 a5c27f5475 Test 2026-07-06 10:13:30 +02:00
dfuhrmann3 aae15f96db Test 2026-07-06 09:23:01 +02:00
dfuhrmann3 2384b27ae1 Test 2026-07-06 09:16:26 +02:00