Tankstellenmarke als eigenes Feld, Belegfelder in der App richtig zugeordnet

Die Marke steht auf jedem Beleg, war aber nirgends gespeichert: der Parser
erkennt sie seit dem 16.08.2026, faltete sie aber nur in station_name.
Fuer jeden Beleg, den eine aeltere Fassung eingelesen hat, blieb dort der
Betreibername stehen und die Marke war danach nicht mehr zu holen.

- shell_beleg_parser: station_brand als eigenes Feld aus beiden Parserwegen,
  dazu marke_aus_text()/marke_aus_pdf() - sie lesen nur den Belegkopf und
  haengen nicht am vollstaendigen Einlesen.
- belege.marken_nachtragen(): traegt die Marke einmal beim Start aus der
  abgelegten Belegdatei nach. Nur wo das Feld gar nicht vorkommt und die
  Datei liegt; ein einmal eingetragenes Feld wird nie ueberschrieben.
- Panel und App: die gespeicherte Marke geht vor, geraten wird nur, wo keine
  da ist. Ein Teil, der fuer sich genommen eine Kopfmarke ist, taugt zudem
  nie als Ort.

Dazu Punkt 5 der Meldung vom 04.09.2026: ein eingelesener Beleg fuellte nur
Datum und Uhrzeit. Der Parser las ihn immer vollstaendig - die App fragte
liter/kosten/station/ersparnis/kraftstoff ab, veroeffentlicht werden aber die
Namen des Belegs (liters/fuel_total_eur/station_name/discount/fuel_type).
Uebereingestimmt hat allein ts. Die Zuordnung steht jetzt als
belegFormularwerte() in daten/belegentwurf.ts und wird gegen die echte
Nutzlast dieses Belegs geprueft.

tsc sauber, 258 Tests (die drei neuen Marken-Tests gegen den alten Stand als
scheiternd nachgewiesen), vite build sauber, Panel als Modul geparst,
Parser-Suite 11 Tests gegen elf echte Belege. Panel und App liefern fuer elf
Faelle byteweise dasselbe. Live in audi_ha_test auf 2026.9.4.21 ohne
Traceback: Uebersicht "Shell, Nuernberger Str., Ansbach", Einzelbeleg
zweizeilig mit Marke, und der echte geteilte Beleg fuellt durchs Backend das
ganze Formular.
This commit is contained in:
2026-09-04 17:00:22 +02:00
parent a99ae432fc
commit bdbdb4a010
17 changed files with 572 additions and 60 deletions
@@ -137,6 +137,7 @@ def _beleg_felder(geparst: dict, pdf_pfad: str) -> dict:
"ts_payment": geparst.get("ts_payment"),
"ts_tse": geparst.get("ts_tse"),
"station_id": geparst.get("station_id"),
"station_brand": geparst.get("station_brand"),
"station_name": geparst.get("station_name"),
"station_address": geparst.get("station_address"),
"article_no": geparst.get("article_no"),
@@ -370,3 +371,60 @@ async def tankvorgang_loeschen(k: Koordinator, tank_id: str | None) -> None:
_LOGGER.info("Tankvorgang %s gelöscht", tank_id)
else:
_LOGGER.warning("Tankvorgang %s nicht gefunden", tank_id)
async def marken_nachtragen(k: Koordinator) -> None:
"""Traegt die Tankstellenmarke bei Belegen nach, die vor dem 16.08.2026
eingelesen wurden.
WARUM ES DAS BRAUCHT
--------------------
Bis zum 16.08.2026 kannte der Parser keine Marken; `station_name` trug
deshalb die Firmierung des Betreibers ("TC Sengül GmbH"), und die Marke
war fuer diese Datensaetze nirgends gespeichert. Auf dem BELEG steht sie
aber weiterhin - jeder der geprueften Altbelege beginnt mit "Shell-Station"
bzw. "SHELL STATION". Ohne dieses Nachtragen bliebe die Einzelbeleg-Zeile
dieser Vorgaenge dauerhaft ohne Marke (Befund des Eigentuemers,
04.09.2026).
VORSICHTIG GEHALTEN
-------------------
* Angefasst wird nur, wo `station_brand` noch gar nicht vorkommt und die
Belegdatei tatsaechlich liegt. Ein einmal eingetragenes Feld - auch mit
Wert None - wird nie wieder ueberschrieben; damit ueberschreibt das
Nachtragen keine Korrektur von Hand und laeuft nicht bei jedem Start
erneut ueber dieselben Belege.
* Gelesen wird nur der Belegkopf (marke_aus_pdf), nicht der ganze Beleg.
Ein Beleg, an dem beide Parser scheitern wuerden, gibt seine Marke
trotzdem her - und ein Lesefehler bleibt folgenlos.
* Geschrieben wird nur, wenn sich wirklich etwas geaendert hat.
"""
vorgaenge = await k.ablage.tankvorgaenge_lesen()
offen = [
v
for v in vorgaenge
if "station_brand" not in v and v.get("receipt_file")
]
if not offen:
return
def _lesen() -> dict[str, str | None]:
gefunden: dict[str, str | None] = {}
for vorgang in offen:
pfad = vorgang.get("receipt_file")
if not pfad or not os.path.exists(pfad):
continue
gefunden[vorgang["tank_id"]] = shell_beleg_parser.marke_aus_pdf(pfad)
return gefunden
marken = await k.hass.async_add_executor_job(_lesen)
if not marken:
return
for vorgang in vorgaenge:
if vorgang.get("tank_id") in marken:
vorgang["station_brand"] = marken[vorgang["tank_id"]]
await k.ablage.tankvorgaenge_schreiben(vorgaenge)
benannt = sorted({m for m in marken.values() if m})
zusatz = ", ".join(benannt) if benannt else "keine erkennbar"
_LOGGER.info("Tankstellenmarke bei %d Beleg(en) nachgetragen (%s)", len(marken), zusatz)