Commit Graph

125 Commits

Author SHA1 Message Date
dfuhrmann3 619159cd33 Doppelte Anzeige der Leistungsnachweise entfernt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 15:52:32 +02:00
dfuhrmann3 c3fb9dc7cb Zahlungsreferenz (BT-83) im UI änderbar und bis zu drei Leistungsnachweise.
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>
2026-07-30 15:49:33 +02:00
dfuhrmann3 df015c9f97 Zahlungsziel als Klartext (BT-20) und Zahlungsreferenz (BT-83) ergänzt.
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>
2026-07-30 12:01:10 +02:00
dfuhrmann3 4b4118fe70 Leistungsnachweis-Upload direkt unter den Rechnungs-Upload verschoben.
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>
2026-07-29 14:44:42 +02:00
dfuhrmann3 88c327c977 Erweitert um optionalen Leistungsnachweis pdf merge
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 14:39:12 +02:00
dfuhrmann3 b41554f1f1 Dateiname geändert: Kundenname auf 15 Zeichen limitiert und Datum durch Bearbeiternummer ersetzt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 11:58:58 +02:00
dfuhrmann3 393d00d631 Dateiname geändert: Kundenname auf 15 Zeichen limitiert und Datum durch Bearbeiternummer ersetzt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 11:51:25 +02:00
dfuhrmann3 d217893dcb fix(v3): Replay-Button links neben Streamlit-Menue statt darueber
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 10:21:08 +02:00
dfuhrmann3 c2b2589dd0 feat(v3): Intro auf MP4 umgestellt (video statt HTML-Bundle)
- 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>
2026-07-16 10:18:43 +02:00
dfuhrmann3 7e0866ba4b chore(v3): .claude/ (nur Vorschau-Tool) auf ignore, .streamlit bleibt (Static Serving)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 10:04:18 +02:00
dfuhrmann3 f28f090b06 feat(v3): Intro-Overlay beim ersten Besuch (zentriert, Replay oben rechts)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 10:02:02 +02:00
dfuhrmann3 35fcc5a576 feat(v3): Intro-Overlay-Baustein (Overlay-Injektion, Skip/Auto-Fade/Replay)
- Player-Leiste des Bundler-Exports ausgeblendet, Panel 16:9 (1280x720)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 10:02:01 +02:00
dfuhrmann3 d89cc0290d feat(v3): Intro-Animation als statisches Asset + Static Serving aktiviert
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 10:02:01 +02:00
dfuhrmann3 1bcc3d0f57 feat(v3): eigenes Tab-Icon (Beleg-Motiv, schwarz/orange)
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>
2026-07-15 13:49:17 +02:00
dfuhrmann3 565fe097dd chore(v3): Tab-Icon (Favicon) auf 🧾 setzen (page_icon)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 13:44:50 +02:00
dfuhrmann3 bacf1798f9 feat(v3): BG-15 Lieferadresse (nur bei abweichendem Lieferort)
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>
2026-07-15 12:54:56 +02:00
dfuhrmann3 9ee56440f5 feat(v3): BT-113 Vorauszahlung, BT-86 BIC + ZUGFeRD-Profil auf EN16931
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>
2026-07-15 12:34:39 +02:00
dfuhrmann3 555272bcf6 fix(v3): Seiten-Möblierung aus BT-22-Freitext strippen (mehrseitige Rechnungen)
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>
2026-07-14 16:06:36 +02:00
dfuhrmann3 cd187585dd feat(v3): Bundle-Parser für konfigurierte Positionen (z.B. Dell) + Summen-Check
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>
2026-07-14 15:58:59 +02:00
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 1851ed7074 fix(parser): stop description scan at sub-position numbers like '2.1' (ZenIT R26-105)
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>
2026-07-13 15:15:38 +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 3e0975102f fix(parser): recognize abbreviated unit 'pausch.' (Rechnung R26-231 Anfahrt)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 12:51:09 +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 cd2bd56377 fix(parser): recognize 'pauschal' as a billing unit (Anfahrt-Position)
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>
2026-07-13 11:06:01 +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 27f6d8bab7 fix(cii): format ZUGFeRD BilledQuantity like UBL (no trailing 1.0000)
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>
2026-07-07 16:35:19 +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