Eine verknuepfte Buchung gehoert zu einem Uebertrag und zaehlt in keiner
Auswertung. Trotzdem stand sie unter "Zu erledigen" und wurde in der
Buchungsliste nach einer Kategorie gefragt – Arbeit, die nichts bewirkt, und
ein falsches Signal: Sie sah aus, als fehle noch etwas.
Der Filter fuer offene Buchungen laesst verknuepfte jetzt aus, und in der
Buchungsliste steht bei ihnen ein Strich statt der Auswahl – so wie bei einem
echten Uebertrag.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Erkennung gab es laengst – aber nur auf der Seite "Zu erledigen", in einer
Karte, die man erst finden muss. Wer in der Buchungsliste steht und die zweite
Haelfte eines Uebertrags sieht, greift stattdessen zum ↔-Knopf und legt einen
zweiten Uebertrag an. Genau so ist heute eine Zahlung doppelt verschoben
worden.
Jetzt steht die Frage in der Zeile: "Uebertrag von/nach Revolut CHF?" mit
Verbinden und Ignorieren. Ignorieren wird gemerkt, sonst fragte Certain bei
jedem Laden erneut.
Dafuer gibt es die Suche jetzt auch als reines Nachschlagen: ``finde`` mit
``nur_vorschlaege`` verknuepft nichts, und dahinter liegt GET
/uebertraege/vorschlaege. Eine Ansicht, die bloss fragt, darf beim Hinsehen
nichts veraendern – sonst entschiede das Anschauen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Sperre sah auf dem Zielkonto nur nach Ausgaben und Einnahmen. Steht dort
schon ein UEBERTRAG, der dasselbe Geld zwischen denselben beiden Konten
bewegt, fand sie nichts – und der zweite ging durch.
Genau das passiert im Alltag: Die eine Seite kommt von der Bank, die andere
aus der CSV-Datei, und beide sehen einzeln nach einem Uebertrag aus. Wer beide
umwidmet, hat das Quellkonto doppelt belastet und das Ziel doppelt
gutgeschrieben – bei 1'575 macht das 3'150 Unterschied auf zwei Konten.
Jetzt wird zuerst geprueft, ob zwischen denselben beiden Konten schon ein
Uebertrag ueber denselben Betrag in derselben Woche steht. Die Richtung ist
dabei egal, ein Uebertrag hat kein Vorzeichen. Dieser Fall laesst sich auch
nicht mit "trotzdem" uebergehen: Dasselbe Geld zweimal zu verschieben ist
keine Ansichtssache.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die staendige Meldung "weicht ab" hatte mehrere Quellen, und eine davon war
ueberhaupt keine Abweichung.
1. Ein VERFUEGBARER Saldo enthaelt Vorgemerktes. Certain bucht Vorgemerktes
bewusst nicht – es kaeme spaeter mit anderer Kennung ein zweites Mal. Beides
zu vergleichen musste also dauerhaft danebengehen. Verglichen wird jetzt nur
noch ein GEBUCHTER Saldo (CLBD, ITBD, PRCD); sonst steht dort "verfuegbarer
Saldo, nicht vergleichbar" und keine Abweichung.
2. Die Saldi wurden VOR den Umsaetzen geholt und sofort geschrieben. Brach der
Umsatz-Abruf danach ab, stand ein aktueller Saldo neben altem Bestand – und
die Anzeige meldete eine Abweichung, die nur aus der Reihenfolge kam. Sie
kommen jetzt zuletzt. Ohne Kontozuordnung weiterhin zuerst: Dort gibt es
keine Umsaetze zu holen, und bei mehreren gleich aussehenden Pockets ist der
Betrag das Einzige, woran man sie unterscheidet.
3. Jeder weitere Abruf griff nur drei Tage hinter den letzten zurueck. Banken
buchen mitunter spaeter zurueck; was einmal ausserhalb lag, kam nie wieder.
Jetzt sieben Tage – doppelt holen kostet nichts, die Kennung des Umsatzes
faengt es exakt ab.
Damit ist die Anzeige nicht mehr nur ehrlicher, sondern zeigt auch wieder auf
etwas: Steht dort eine Abweichung, ist sie echt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Wer dieselbe Bank zweimal verbindet, bekommt fuer dieselbe Zahlung zwei
verschiedene Kennungen – Enable Banking fuehrt eine Bank je Land getrennt.
Genau das passiert bei Revolut: Ueber den DE-Eintrag kommen drei Konten, ueber
den LT-Eintrag zusaetzlich die Pockets, also verbindet man beide. Zeigen dann
zwei Bankkonten auf dasselbe Konto in Certain, holt jeder Zugang dieselben
Umsaetze, und der Duplikatschutz kann sie nicht als dieselben erkennen.
Drei Aenderungen:
- Ein Konto in Certain wird nur noch von EINEM Bankkonto gefuellt. Ordnet man
ein zweites zu, wird die alte Zuordnung geloest. Sie ist ohnehin nie richtig,
und stillschweigend doppelt zu buchen ist schlimmer als eine geloeste
Zuordnung.
- Die Ausnahme im Ueberschneidungsschutz gilt nur noch fuer DASSELBE Bankkonto.
Dort entscheidet die Kennung der Bank, und die ist exakt – zwei Aufrundungen
ueber 1.00 duerfen sich nicht verschlucken. Buchungen aus einem ANDEREN
Zugang zaehlen jetzt mit.
- Das Aufraeumskript kennt den Fall: Findet es in einer Gruppe (Konto, Art,
Tag, Betrag) Buchungen aus zwei Zugaengen, bleibt die Fassung EINES Zugangs
stehen. Aufrundungen aus einem einzigen Zugang bleiben unangetastet, auch
wenn sie gleich aussehen.
An nachgestellten Daten geprueft: Aus -579.50 wird -290.50; die drei gleichen
Aufrundungen aus einem Zugang bleiben alle stehen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
"3 Konten gefunden" beantwortet die entscheidende Frage nicht: Hat die Bank nur
drei geschickt, oder hat Certain die uebrigen verworfen? Der Server rechnet
beides laengst aus, die Seite zeigte es nur, wenn ueberhaupt kein Konto ankam.
Jetzt steht immer dabei, wie viele die Bank geschickt hat. Und Konten, die
Certain nicht uebernehmen konnte, werden nicht mehr bloss gezaehlt, sondern
mitsamt dem gezeigt, was von ihnen ankam – uebersprungen werden sie, weil die
Kennung fehlt, mit der sich Salden und Umsaetze holen lassen.
Anlass: Fuenf Revolut-Pockets sind im Control Panel freigeschaltet und wurden
bei der Autorisierung ausdruecklich mitgewaehlt, kommen in Certain aber nicht
an. Ohne diese Zahlen laesst sich nicht sagen, wo sie verloren gehen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
"Aelteres nachholen" greift 90 Tage zurueck – mitten in Monate, die schon per
CSV eingelesen sind. Genau davor schuetzt sonst das Startdatum, und der Knopf
haengt es aus. Der Duplikatschutz faengt das nicht: Sein Schluessel enthaelt
den Empfaengertext, und der lautet bei der Bank anders als im Auszug. Ergebnis:
jede Zahlung des Zeitraums zweimal.
Das war mein Fehler – ich habe den Knopf gebaut UND empfohlen, ohne die
Sicherung zu ersetzen, die er aushebelt.
Der Abruf prueft jetzt selbst: Gibt es auf dem Konto schon eine Buchung
gleicher Richtung und gleichen Betrags im Umkreis von drei Tagen, die NICHT
von der Bank kam, ist der Umsatz dieselbe Bewegung und wird uebersprungen.
Jede vorhandene Buchung erklaert dabei hoechstens einen Umsatz – sonst
verschluckte eine Kaffeezahlung alle weiteren desselben Betrags. Die Zahl der
uebersprungenen steht in der Rueckmeldung.
Fuer den bereits angerichteten Schaden gibt es
scripts/raeume_doppelte_bankumsaetze.py: Es findet Bank-Umsaetze, die eine
vorhandene Buchung wiederholen, und entfernt IMMER die von der Bank – die
eigene traegt die Kategorie des Nutzers. Sie wird verworfen, nicht geloescht,
damit kein spaeterer Abruf sie wiederholt. Ohne --wirklich passiert nichts;
mit --wirklich wird zuerst eine Kopie der Datenbank angelegt.
An nachgestellten Daten geprueft: Aus -95.80 wird -48.40, die eigenen
Buchungen samt Kategorie bleiben, der echte neue Umsatz bleibt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Beim ersten Abruf beginnt Certain am Tag NACH der neuesten vorhandenen Buchung
des Kontos, damit es nicht in schon eingelesene Monate zurueckgreift. Liegt
diese Buchung auf heute, ist der Tag danach morgen – und die Schnittstelle
lehnt das rundweg ab: "date_from can not be in the future".
Der Fehler faellt erst beim Umsatz-Abruf an. Die Saldi sind da schon gelesen
und geschrieben, der Abruf bricht danach ab. Von aussen sieht es genau so aus,
wie der Nutzer es beschrieben hat: Der Kontostand aktualisiert sich, die
Buchungen kommen nie – und die Abweichung bleibt bestehen, obwohl man den
Saldo gerade erst von der Bank uebernommen hat. Genau dieses Uebernehmen legt
naemlich keine Buchung an, aber es gibt fast immer eine von heute.
Das Startdatum wird jetzt auf heute begrenzt. Gegenprobe: Ohne die Begrenzung
verlangt der Abruf den 28.08. an einem 27.08.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der Eingangs-Abgleich vergleicht Betrag und Datum mit ein paar Tagen
Spielraum – bewusst unscharf, damit eine per Kurzbefehl erfasste Zahlung nicht
ein zweites Mal ankommt. Er merkt sich dabei, welchen Eintrag er schon
verbraucht hat.
Nur ruft der Bankabruf ihn JE ZEILE einzeln auf, und damit begann diese Menge
jedes Mal von vorn: Derselbe Eintrag liess sich beliebig oft verbrauchen. Bei
Revolut-Aufrundungen – dreimal 1.00 in drei Tagen – kam so nur die erste an,
und der Saldo wich ab, ohne dass irgendwo etwas stand.
Zwei Aenderungen:
- Der Aufrufer kann die Menge jetzt ueber mehrere Aufrufe mitfuehren; der
Bankabruf haelt eine je Konto.
- Gegen andere BANK-Eintraege wird gar nicht mehr unscharf verglichen. Zwei
Umsaetze derselben Quelle sind nie Dubletten voneinander – dort entscheidet
die Kennung der Bank, und die ist exakt.
Dazu ein Weg zurueck: Ein verschluckter Umsatz hinterliess keinen Eintrag, der
naechste Abruf haette ihn also wieder geholt – nur liegt er dann vor dem
Fenster des letzten Abrufs. "Aelteres nachholen" gibt es jetzt auch je Konto,
wie beim Postfach.
Nachgestellt und als Test festgehalten: dreimal 1.00 in drei Tagen, alle drei
kommen an, Saldo -3.00.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Regeln greifen beim Import. Alles, was vorher hereinkam – oder hereinkam, bevor
es die Regel gab –, bleibt liegen. Bei einer Bankanbindung passiert genau das
dauernd: Die Umsaetze kommen von selbst, die Regel entsteht spaeter. Die
Revolut-Aufrundungen aufs Sparen-Pocket sind der Fall, an dem es aufgefallen
ist.
Der Knopf steht bei der Transfer-Erkennung und zeigt erst, was passieren
wuerde. Uebertrags-Regeln laufen durch denselben Dienst wie jeder andere
Uebertrag – er lehnt ab, wo die Gegenseite schon gebucht ist, und solche
Buchungen bleiben unangetastet und werden mit Grund gemeldet.
Ohne festes Zielkonto wird es aus dem Buchungstext erraten, aber nur bei GENAU
einem Treffer und nur an Wortgrenzen: "Euro" darf nicht "PayPal Europe"
treffen. Zwei Moeglichkeiten heisst raten, und dann passiert nichts.
Angefasst werden nur Buchungen OHNE Kategorie – sonst ueberschriebe der Knopf
stillschweigend, was von Hand zugeordnet wurde. Ein Uebertrag ist kein Kandidat
mehr, ein zweiter Klick verschiebt das Geld also nicht erneut.
Zwei Dinge fielen beim Testen auf und sind mit behoben:
- Ein fehlgeschlagener Uebertrag verwarf per rollback() auch alles, was im
selben Durchlauf schon gelungen war. Jetzt haelt ein Rettungspunkt je
Uebertrag den Schaden bei dem einen.
- test_neutral_category nahm VORHANDENE Kategorien und stellte eine davon auf
"nicht einrechnen". Benutzten andere Buchungen sie, fielen die mit heraus und
die Rechnung ging um deren Summe daneben – ohne Bezug zum Gepruefften. Der
Test legt sich seine Kategorien jetzt selbst an.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Auslieferung des Frontends nahm den Pfad roh aus der Adresse: "Path /
'../../x'" verlaesst den Ordner klaglos, und die Route liegt VOR jeder
Anmeldepruefung. Gegen die eigene Instanz nachgestellt – roh und
prozentkodiert kam beides Mal die Datei heraus:
GET /../../GEHEIM.txt -> "geheim"
GET /../../backend/app/config.py -> der Quelltext
Damit war alles lesbar, was der Dienst lesen darf: Quelltext, Konfiguration,
und mit der passenden Zahl von Ebenen die Datenbank samt verschluesseltem
Postfach-Passwort und Bank-Schluessel. Der Pfad wird jetzt aufgeloest und
muss im Auslieferungs-Ordner liegen.
Zweitens prueft der Postfach-Abruf jetzt das Zertifikat. Ohne eigenen
SSL-Kontext tut imaplib das NICHT – die Verschluesselung schuetzt dann vor
niemandem, der sich dazwischenhaengt, und das Postfach-Passwort geht mit.
Gilt fuer beide Wege, SSL und STARTTLS.
Drittens warnt der Start, wenn das Sitzungs-Cookie ohne "Secure" ausgeliefert
wird. Die Vorgabe bleibt bewusst aus: Sie umzustellen sperrt jeden aus, der
Certain im Heimnetz ueber HTTP aufruft – und ein Ausgesperrter saehe die
Ursache nirgends.
Die Befunde stammen aus dem /security-review dieser Sitzung und waren seither
offen. Sie wiegen schwerer, seit die Anwendung fuer die Bankanbindung unter
einer oeffentlichen HTTPS-Adresse erreichbar sein muss.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der Starttag wird auf den letzten Tag des Monats gekappt – einen 31. Februar
gibt es nicht. period_bounds tat das, period_of nicht: Es verglich stur den
Tag des Monats. Damit fiel der 28. Februar bei Starttag 31 in einen Monat, der
vom 31. Januar bis zum 27. Februar reicht – der Tag lag also in keiner
Auswertung.
Betroffen waren elf Tage in vier Jahren, alle am Ende kurzer Monate, und nur
bei Starttag 29, 30 und 31. Bei allen anderen stimmte es.
Zwei Tests halten es fest: Jeder Tag von 2024 bis 2027 muss bei JEDEM Starttag
in dem Monat liegen, den period_of ihm zuweist – und die Monate muessen
lueckenlos aneinanderstossen, keiner doppelt, keiner dazwischen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
woher sie kommt
Die Meldung nach einem Abruf zaehlte geprueft, neu und unklar – aber nicht
"schon bekannt". Wurden alle Treffer beim vorigen Abruf schon uebernommen,
stand da "0 neu", und es sah aus, als waere nichts passiert, obwohl die
Eintraege im Eingang liegen. Die Zahl steht jetzt dabei.
Und die uebergangenen Mails wurden aufgelistet, die unklaren nicht: nur eine
Zahl, ohne zu sagen, welche Mail gemeint ist oder was ihr fehlt. Jetzt stehen
sie mit Grund da – kein Konto zugeordnet, kein Betrag erkannt, Richtung unklar,
Zahlung noch nicht ausgefuehrt.
Ausserdem traegt jede Buchung jetzt ihre Herkunft: Bank, Mail, Kurzbefehl,
Datei-Import oder von Hand. In der Buchungsliste steht sie als kleines
Kennzeichen unter dem Text. Ermittelt wird sie mit einer Sammelabfrage, nicht
je Zeile.
Bei einem Uebertrag zeigen zwei Eingangs-Eintraege auf dieselbe Buchung; die
Gegenseite hat Certain selbst angelegt, gemeint ist die echte Quelle. Genau
dafuer gibt es einen Test.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der Zeitstempel des letzten Abrufs wird auch dann gesetzt, wenn ein Abruf
nichts fand – und danach sucht Certain nur noch ab gestern. Wer das Postfach
einrichtet, nachdem die Mails schon da waren, findet sie nie: Der erste,
ergebnislose Abruf schiebt das Fenster an ihnen vorbei.
Jetzt gibt es "Aelteres nachholen": Es sucht die letzten 90 Tage und uebergeht
den Zeitstempel. Dass dabei nichts doppelt ankommt, sichert wie bisher die
Message-ID.
Ausserdem sagt die Meldung jetzt, AB WANN gesucht wurde. Bisher stand dort
"Stimmt der Ordner?" – die Frage war fast immer die falsche.
Und ein latenter Fehler, der im August nicht auffaellt: Das Suchdatum wurde mit
strftime("%b") gebaut, also mit dem Monatskuerzel in der Sprache des Servers.
IMAP verlangt die englischen; auf einem deutschen System haette die Suche im
Maerz, Mai, Oktober und Dezember nichts gefunden. Die Kuerzel stehen jetzt fest
im Code.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Es liess die Entscheidung beim Nutzer, obwohl die Datenbank sie hergibt: In
bank_account_links steht, welche Konten Certain selbst abholt. Sind beide
angebunden, liefert die Bank beide Seiten und der Uebertrag ist falsch. Wird
das Gegenkonto nicht abgerufen, ist der Uebertrag richtig und die zusaetzliche
Buchung ueberzaehlig. Das steht jetzt je Fall dabei.
Ausserdem war die Zusammenfassung irrefuehrend: Sie addierte Betraege in beide
Richtungen. Eine zusaetzliche Gutschrift macht das Konto zu hoch, eine
zusaetzliche Belastung zu niedrig – aus '2'026 zu viel' werden so '1'125 zu
hoch'.
Dazu zwei Tests zu der Frage, ob der naechste Abruf alles wieder anlegt: Nein.
Der Umsatz wird an der Kennung der Bank wiedererkannt, unabhaengig davon, was
aus der Buchung geworden ist, und ein spaeterer CSV-Auszug faellt ueber den
Eingangs-Abgleich. Aufraeumen haelt also.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Solange beim Anlegen eines Uebertrags nicht geprueft wurde, ob die Gegenseite
schon gebucht ist, konnte dieselbe Bewegung zweimal auf einem Konto landen.
Das ist seit dem vorigen Commit unterbunden – die Daten von vorher bleiben
aber, wie sie sind.
Das Skript findet sie: Zu jedem Uebertrag sieht es auf BEIDEN Konten nach, ob
dort zusaetzlich eine passende Buchung steht (gleiche Tage, passender Betrag,
ueber Waehrungen hinweg mit Spielraum), und rechnet zusammen, wieviel dann zu
viel dasteht.
Es aendert NICHTS und oeffnet die Datenbank nur lesend. Aufraeumen bleibt Sache
des Nutzers, denn welche der beiden Zeilen falsch ist, haengt daran, ob Certain
das Gegenkonto selbst abholt – das kann das Skript nicht wissen, und ein
Aufraeumskript hat hier schon einmal Richtiges mit weggeraeumt. Beide Faelle
stehen als Anleitung dabei.
Dazu ein Regressionstest fuer genau den gemeldeten Fall: Gutschrift steht schon
auf dem Zielkonto, die Belastung soll zum Uebertrag werden – das wird jetzt
abgefangen, und der Saldo des Zielkontos bleibt unveraendert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ein Franken-in-Euro-Tausch stand zweimal da: die CHF-Seite als Uebertrag nach
Revolut Euro, die EUR-Seite weiterhin als eigene Einnahme. Revolut Euro war
damit doppelt gutgeschrieben. Drei Fehler auf einmal.
Erstens prueft "zum Uebertrag machen" jetzt, ob die Buchung bereits mit ihrer
Gegenseite VERBUNDEN ist. Dann sind beide Seiten gebucht und zaehlen schon in
keiner Auswertung; ein Uebertrag bewegt das Geld auf dem Zielkonto ein zweites
Mal. Das laesst sich auch nicht mit "trotzdem" wegdruecken – es ist nicht
Ansichtssache.
Zweitens wird auf dem Zielkonto nach einer passenden gebuchten Gegenseite
gesehen, auch ohne Verknuepfung: gleiche Tage, passender Betrag, ueber
Waehrungen hinweg mit Spielraum. Findet sich eine, kommt die Warnung samt dem
richtigen Weg – die beiden verbinden statt einen Uebertrag anzulegen. Der
Treffer ist unscharf, deshalb hier mit "trotzdem" uebergehbar.
Drittens der beobachtete Punkt: Der nachtraegliche Weg reichte den
Eingangs-Eintrag nicht durch und rechnete deshalb IMMER mit dem EZB-Kurs, auch
wenn die Bank ihren eigenen genannt hatte. Jetzt wird er nachgeschlagen, und
der Kurs der Bank landet an der Buchung.
Und Loeschen raeumt die Eingangs-Eintraege mit. Der Fremdschluessel setzt sonst
nur ihre Verweisspalte auf NULL, und sie blieben als "gebucht" ohne Buchung
stehen – bei einem Uebertrag sind das zwei.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
'Eingang' und 'Offene Buchungen' zeigten fast dasselbe. Seit angebundene Konten
selbst buchen, ist eine Bank-Buchung sofort gebucht und ohne Kategorie – sie
stand damit in BEIDEN Listen. Die Trennung stammte aus der Zeit, als der
Eingang nur Vorschlaege enthielt.
Jetzt eine Seite 'Zu erledigen' mit allem, was noch eine Hand braucht: offene
Vorschlaege zum Buchen oder Uebertragen und Buchungen ohne Kategorie, egal ob
von Bank, Mail, Kurzbefehl oder Datei. Aelteste zuerst, mit der Herkunft in der
Zeile. Erledigtes verschwindet – die Eingangs-Eintraege bleiben in der
Datenbank, weil der spaetere Import gegen sie abgleicht. /offen leitet um.
Das Haekchen 'Schlagwort merken' aus der alten Seite bleibt: Es ist der Grund,
warum der Stapel mit der Zeit kleiner wird, und der Eingang hatte es nie. Der
Uebertrags-Knopf gilt jetzt auch fuer schon gebuchte Buchungen – sonst haette
in einer gemeinsamen Liste die eine Haelfte der Zeilen ihn gehabt und die
andere nicht.
In der Buchungsliste steht die Kategorie jetzt als Auswahl in der Zeile, dort
wo bisher nur ihr Name stand. Vorher musste man jede Buchung einzeln zum
Bearbeiten oeffnen. Die Zeile aendert sich an Ort und Stelle, die
Bildlaufposition bleibt.
Ausserdem tragen API-Antworten jetzt 'Cache-Control: no-store'. Ohne das darf
ein Browser selbst entscheiden, und der Zurueck-Knopf zeigt Salden von vorhin –
bei Geld ist eine veraltete Anzeige schlimmer als eine langsame.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Bei einer Fremdwaehrungs-Zahlung nennt die Bank oft ihren echten Kurs und den
Betrag in der Ausgangswaehrung. Certain warf das weg und naeherte mit dem
EZB-Kurs. Das faellt genau dort auf, wo es teuer ist: Die spaeter gelieferte
Gegenseite eines Uebertrags laesst sich mit einem geschaetzten Betrag nicht als
dieselbe Bewegung wiedererkennen.
Der Uebertrag waehlt jetzt in drei Stufen: der von der Bank genannte
Gegenbetrag (exakt), sonst ihr Kurs, sonst die EZB. Nur die erste Stufe gilt
als bekannt – daran haengt, ob bei einem angebundenen Zielkonto nachgefragt
wird.
Der Parser klopft mehrere Formen ab, weil die Feldnamen je Anbindung
verschieden sind, und setzt still nichts, wenn keine passt. Keine dieser Formen
ist gegen echte Daten geprueft; ein falscher Kurs saehe aus wie ein richtiger,
deshalb wird lieber gar keiner gesetzt.
Ausserdem ein Knopf auf der Kontenseite: den Saldo der Bank als richtig
uebernehmen. Die Differenz wandert in den ANFANGSBESTAND – sie steht fuer
Bewegungen, die Certain nie gesehen hat, und genau dafuer ist er da. Eine
Korrekturbuchung stuende dagegen fuer immer in der Liste und liefe durch jede
Auswertung. Die Buchungen bleiben unangetastet.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ein Uebertrag legte auf dem Zielkonto nichts an. Der Duplikatschutz konnte die
Gegenseite deshalb nicht sehen – er vergleicht import_hash, den eine
Uebertrags-Buchung nicht hat, und Eingangs-Eintraege, die es dort nicht gab.
Die schon vorliegende Gegenseite liess sich ein zweites Mal buchen, die
spaeter von der Bank gelieferte kam ungeprueft an: das Geld zweimal auf dem
Zielkonto.
Jetzt entsteht auf dem Zielkonto ein Eingangs-Eintrag, der auf DIESELBE Buchung
zeigt. Liegt dort schon ein passender offener Eintrag, wird dieser verbraucht
statt einen zweiten anzulegen – ueber denselben Abgleich wie beim Import, nicht
ueber eine zweite Vergleichslogik. Damit greift auch der vorhandene Schutz
gegen die spaeter eintreffende Gegenseite, ohne dass etwas Neues dafuer gebaut
werden musste.
Die Uebertrags-Logik liegt dafuer in einem eigenen Dienst. Zwei Wege fuehren
hinein – ein offener Vorschlag und eine schon gebuchte Buchung – und beide
brauchen dieselben vier Regeln. Der PATCH-Weg des Buchungs-Routers taete es
nicht: Er dreht die Richtung bei einer Einnahme nicht um und legt keine
Gegenseite an.
Die Sperre richtet sich nicht mehr nach der Waehrung, sondern danach, ob der
Betrag der Gegenseite BEKANNT oder geschaetzt ist. Bei gleicher Waehrung ist er
exakt, die Gegenseite wird sicher wiedererkannt – dann geht ein Uebertrag auf
ein angebundenes Konto ohne Nachfrage durch. Ueber Waehrungen hinweg bleibt die
Warnung, weil der EZB-Kurs nicht der Kurs der Bank ist.
Und Verwerfen raeumt beide Seiten: Bei einem Uebertrag zeigen zwei Eintraege
auf dieselbe Buchung, und der Fremdschluessel haette die andere als 'gebucht'
ohne Buchung zurueckgelassen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Zeile im Eingang zeigte die Kategorie-Auswahl bedingungslos – auch bei
einem Kontouebertrag, der gar keine bekommt. Die Oberflaeche konnte es auch
nicht wissen: InboxItemOut hatte kein Feld, an dem sich ein Uebertrag
erkennen laesst.
Jetzt liefert der Eintrag transfer_account_id mit – das jeweils ANDERE
Konto, aus seiner Sicht. Steht es, zeigt die Zeile '↔ Kontoname' statt der
Auswahl. Ermittelt wird es mit einer Sammelabfrage, nicht je Zeile: Die Liste
geht bis 200 Eintraege.
Dazu die Luecke dahinter: set_category schrieb die Kategorie auch auf die
Buchung und konnte sie so auf einem TxnType.transfer ablegen – genau das,
was das Buchungsformular beim Anlegen verhindert. Bei einem Uebertrag lehnt
der Endpunkt jetzt ab; bei allem anderen bleibt es, wie es war.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Drei Sachen an der Bankanbindung.
'Umsaetze holen' lief ueber alle Konten einer Verbindung. Seit angebundene
Konten selbst buchen, ist ein Klick damit folgenreich: Er bucht auf zwei
Konten. Der Knopf sitzt jetzt in der Kontozeile; an der Verbindung bleibt
'Alle Konten holen'.
Dabei musste der Zeitstempel mitwandern. last_sync_at hing an der
Verbindung – ein Einzelabruf haette damit das Startdatum der uebrigen Konten
mit vorgeschoben, und deren Umsaetze aus dem uebersprungenen Zeitraum waeren
nie angekommen. Der Stempel gehoert an das Konto, das abgerufen wurde.
Neu ist ein Takt je Verbindung, nach demselben Muster wie beim Postfach: ein
Hintergrund-Thread, der jede Minute nachsieht, ob etwas faellig ist. Bewusst
ein eigener Thread – ein haengender Bankzugang darf den Mail-Abruf nicht
mitreissen. Die Vorgabe ist 0, also aus: Anders als beim Mail-Abruf entstehen
hier Buchungen, das laeuft nicht ungefragt los. Schlaegt ein Abruf fehl, wird
der Stempel trotzdem gesetzt, sonst liefe die Verbindung jede Minute in
denselben Fehler.
Und die Abschnitte heissen jetzt 'Bankkonten Anbindung' und 'Mail Import'.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Bisher blieb im Eingang nur, eine Umbuchung als Ausgabe oder Einnahme zu
kategorisieren. Sie zaehlte dann als Ausgabe, obwohl kein Geld weg ist – nur
verschoben.
Jetzt gibt es je Vorschlag den Weg 'Als Uebertrag buchen': Zielkonto waehlen,
fertig. Es entsteht EINE Buchung, die beide Salden bewegt und in keiner
Auswertung zaehlt. Die Richtung kommt aus dem Vorzeichen – ein Abgang geht von
hier weg, ein Zugang kommt vom Gegenkonto her; entsprechend fragt der Dialog
'wohin' oder 'woher'. Bei verschiedenen Waehrungen wird die Seite exakt
gesetzt, die wir wirklich kennen, und die andere umgerechnet.
Der teuerste Fehler an dieser Stelle waere ein Zielkonto, das selbst an einer
Bank haengt: Es meldet den Zugang von allein, eine Gegenbuchung hier haette das
Geld zweimal. Der Server lehnt das mit einer Begruendung ab; wer es besser
weiss, bestaetigt und kommt durch. Das Wissen bleibt an einer Stelle – die
Oberflaeche fragt nicht selbst nach, sie zeigt die Antwort des Servers.
Die Betragslogik ist dafuer aus dem Buchungs-Router in einen Dienst gewandert.
Zwei Kopien derselben Umrechnung waeren genau die Stelle, an der Salden
auseinanderlaufen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Bisher legte der Bankabruf Vorschlaege in den Eingang, die man von Hand buchen
musste. Ein angebundenes Konto ist aber die Quelle der Wahrheit: Was die Bank
gebucht hat, IST gebucht. Jetzt wird es sofort gebucht; uebrig bleibt das
Zuordnen der Kategorie.
Drei Dinge mussten dafuer stimmen, sonst haette es Salden verfaelscht:
- Der erste Abruf greift nicht mehr hinter die neueste vorhandene Buchung des
Kontos zurueck. Vorher holte er neunzig Tage – mitten in Monate, die schon
per CSV eingelesen sind. Der Duplikatschutz haette die nicht gefunden: Sein
Schluessel enthaelt den Empfaengertext, und der lautet bei der Bank anders
als im Kontoauszug. Das Startdatum steht in der Rueckmeldung.
- Vorgemerkte Umsaetze werden nicht gebucht. Sie kommen spaeter noch einmal,
dann mit anderer Kennung, weil die Bank beim Buchen eine neue vergibt.
Beides zu nehmen hiesse doppelt buchen. Ihre Zahl wird gemeldet, nicht
verschwiegen.
- Nach dem Buchen wird nach Uebertraegen gesucht. Ein Uebertrag zwischen zwei
angebundenen Konten kommt in EINEM Abruf an – beide Seiten.
Nebenbei schliesst das eine bestehende Luecke: Der Eingangs-Abgleich beim
CSV-Import sieht nur gebuchte Eintraege. Solange Bank-Vorschlaege unbeachtet
liegen blieben, haette ein spaeterer Import derselben Wochen sie verdoppelt.
Das Buchen aus dem Eingang ist dafuer vom Router in einen Dienst gewandert;
zwei Kopien derselben Umrechnung waeren genau die Stelle, an der Salden
auseinanderlaufen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Certain rechnet den Saldo bisher ausschliesslich selbst aus: Anfangsbestand
plus alle Buchungen. Stimmt eine Buchung nicht, faellt das erst auf, wenn man
von Hand mit dem Auszug vergleicht.
Der von der Bank gemeldete Saldo wird jetzt mitgefuehrt und danebengestellt.
Weicht er ab, fehlt oder doppelt eine Buchung – und man sieht es auf der
Kontenseite und in den Einstellungen sofort, statt es zu suchen.
Banken melden mehrere Saldi nebeneinander; ausgewaehlt wird der gebuchte, denn
nur der ist mit Certains Rechnung vergleichbar. Ein verfuegbarer Saldo enthaelt
Vorgemerktes oder einen Ueberziehungsrahmen.
Die Saldi kommen auch fuer noch nicht zugeordnete Konten: Bei mehreren
Revolut-Pockets ist der Betrag oft das Einzige, woran man sie unterscheidet.
Ein Fehler bei einem Konto beendet den Durchlauf nicht.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ein Revolut-Pocket bringt weder eigene IBAN noch eigenen Namen mit. Certain
hielt bisher nur uid, IBAN, Name und Waehrung fest – mehrere Konten derselben
Bank sahen damit gleich aus, und in der Zeile stand die rohe Kennung.
Jetzt werden Produktbezeichnung und die Kennung fuer Konten ohne IBAN
mitgefuehrt, der angezeigte Name faellt der Reihe nach auf Name, Details,
Produkt, Kennung, IBAN zurueck statt auf die uid, und der uebermittelte
Datensatz laesst sich aufklappen – nachsehen statt raten, wenn zwei Konten
gleich aussehen. Beim Neuverbinden werden die Merkmale aufgefrischt, ohne die
Zuordnung anzutasten.
Fehlt ein Konto in der Waehrung des Bankkontos, steht das jetzt dabei; vorher
war die Auswahl einfach leer.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Um eine Verbindung aufzufrischen – etwa nachdem man im Control Panel weitere
Konten verknuepft hat – haette man sie bisher erst loeschen muessen. Dabei
gehen die Kontozuordnungen mit verloren, also genau das, was man behalten
will; ein zweiter Verbindungsversuch haette sonst einen doppelten Eintrag
erzeugt.
Jetzt frischt das Verbinden derselben Bank den bestehenden Eintrag auf: neue
Sitzung, neue Konten, bestehende Zuordnungen bleiben. Der Warntext beim
Trennen sagt ausserdem, dass Trennen dafuer gar nicht noetig ist.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Drei verschiedene Faelle sahen alle gleich aus: Die Bank schickte nichts, sie
schickte Eintraege ohne Kennung (die still verworfen wurden), oder alle Konten
waren schon bekannt – Letzteres meldete faelschlich "0 Konten", obwohl alles
in Ordnung war.
Die Antwort unterscheidet jetzt uebermittelt / neu / bekannt / ohne_kennung.
Die Rueckkehr-Seite zeigt bei null nutzbaren Konten den passenden Hinweis: Im
eingeschraenkten Betrieb gibt eine Bank nur heraus, was zuvor im Control Panel
unter "Activate by linking accounts" verknuepft wurde.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der Fehler, den der Nutzer gefunden hat: Die Suche nach "n" lieferte
"genau 200" Treffer und darunter nie N26. Ursache war die Reihenfolge –
alphabetisch sortiert und ERST DANN bei 200 gekuerzt. "N26" liegt hinter
"Aachener Bank", fiel also hinter zweihundert Banken heraus, die irgendwo
ein n im Namen tragen. Die Bank war die ganze Zeit da.
Jetzt wird zuerst bewertet (genauer Name > Namensanfang > Wortanfang >
Teiltreffer > nur BIC/Gruppe) und erst danach gekuerzt. Ausserdem wird die
ECHTE Trefferzahl gemeldet statt der gekuerzten – "genau 200" sah aus wie ein
Suchergebnis, war aber die Obergrenze.
Ein Regressionstest stellt den Fall nach: 300 Banken mit "n" im Namen,
alphabetisch vor N26. Ohne den Fix faellt er um.
Nebenbei geprueft und ausgeschlossen: /aspsps blaettert nicht, die Liste ist
also vollstaendig; Sandbox und alphabetische Kuerzung waren es auch nicht.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Bei der Bank-Suche kam nur "Keine Treffer" – das hiess zweierlei: Die
Schnittstelle lieferte gar keine Liste, oder sie lieferte eine und der
Suchbegriff passte nicht. Beides sieht gleich aus, fuehrt aber an ganz
verschiedene Stellen. Genau diese Verwechslung hat heute schon zweimal Zeit
gekostet.
Die Antwort traegt jetzt die Gesamtzahl mit. Kam nichts, steht der
wahrscheinliche Grund dabei: Die Anwendung bei Enable Banking wird erst
aktiv, wenn man dort die eigenen Konten verknuepft hat.
Ausserdem wird breiter gesucht – auch in BIC und Gruppenname, weil Banken in
so einer Liste nicht immer so heissen wie im Alltag – und die Antwort wird
defensiv gelesen, falls die Liste ohne Schluessel kommt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Unter "Deutschland" liessen sich N26 und Revolut nicht finden – und bei
Revolut kann das gar nicht klappen: Die Bank vergibt deutsche IBANs, ist aber
die Revolut Bank UAB in Litauen. Das Sitzland einer Bank steht nicht in der
IBAN, also ist die Laenderauswahl der falsche Einstieg.
Jetzt ein Suchfeld, das ohne Land ueber alle sucht; das Land ist nur noch ein
optionaler Filter. Der Treffer zeigt sein Land mit an, und verbunden wird mit
dem Land der BANK statt dem des Filters – sonst schluege die Autorisierung
fehl, sobald ohne Land gesucht wurde.
Ohne Land UND ohne Begriff wird abgelehnt: Sonst kaemen alle Banken Europas
ins Formular.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Seit PSD2 muessen EU-Banken eine Schnittstelle anbieten, aber nur fuer
lizenzierte Dienste mit eIDAS-Zertifikat. Enable Banking haelt die Lizenz und
reicht sie weiter: eigene Anwendung registrieren, eigene Konten verknuepfen,
danach genau diese lesen. Deckt N26 und Revolut ab; die Schweiz faellt nicht
unter PSD2, Raiffeisen bleibt beim CSV-Weg.
Es gibt keinen API-Schluessel. Jede Anfrage traegt ein selbst gebautes JWT,
signiert mit dem privaten Schluessel der Anwendung (RS256). Signiert wird mit
cryptography, das ohnehin schon fuer die Verschluesselung im Projekt ist – der
Test prueft die Signatur echt gegen den oeffentlichen Schluessel, denn genau
daran scheitert so eine Anbindung sonst als Erstes.
Abgerufene Umsaetze werden NICHT gebucht. Sie laufen durch detect_rows und
landen als Vorschlag im Eingang – dieselbe Strecke wie Mail und Kurzbefehl,
also greifen Duplikatschutz und Schlagwortregeln ohne Sonderbehandlung. Eine
dritte Quelle, kein dritter Sonderweg.
Bewusst eng gehalten: nur Lesen; Zuordnung Bankkonto -> Konto trifft der
Nutzer; die Zuordnung verlangt dieselbe Waehrung; die Rueckleitungs-Adresse
wird eingetragen statt geraten (hinter einem Proxy sieht der Dienst "http",
obwohl der Browser HTTPS spricht) und muss HTTPS sein.
Der Ablauf der Zustimmung (bis 180 Tage) wird sichtbar gemeldet, damit der
Abruf nicht eines Tages stumm aufhoert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sieben nutzereigene Tabellen aus den Migrationen 0015-0020 standen in keiner
Backup-Liste. Am schwersten wiegt inbox_items: Der Eingang haelt fest, welche
Zahlungen von aussen schon erfasst wurden. Fehlt er nach dem Wiedereinspielen,
erkennt der naechste Kontoauszug sie nicht wieder und bucht sie ein zweites
Mal – genau der Fehler, den die Tabelle verhindern soll.
Aufgenommen: inbox_items, transfer_links, link_ignored, api_tokens.
Bewusst nicht: mail_sources/mail_senders (tragen das verschluesselte
IMAP-Passwort; ein Backup ist eine Datei, die man herumreicht) und
pending_uploads (fluechtige Rohdateien). Beides steht begruendet im Code.
Ein Test laeuft jetzt ueber Base.registry und fordert fuer jedes Modell mit
owner_id eine bewusste Entscheidung – sichern oder begruendet ausnehmen.
Genau diese Pruefung fehlte, weshalb die sieben unbemerkt durchrutschten.
Dazu der tote Code aus dem Transfer-Ausbau: is_counterpart und braucht_ziel
waren feste False, _apply_counterpart_amount damit unerreichbar. Die Tests
sichern dieselbe Garantie jetzt ueber das tatsaechliche Verhalten.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ein Uebertrag steht in zwei Auszuegen und wird zweimal gebucht – richtig fuer
die Salden, aber er zaehlt als Ausgabe UND als Einnahme in der Auswertung.
Statt beide zu einer Uebertrags-Buchung zusammenzufuehren (der urspruengliche
Plan) legt Certain nur eine Schicht darueber: transfer_links haelt fest, dass
zwei Buchungen zusammengehoeren. An den Buchungen selbst aendert sich nichts –
Betrag, Konto, Kategorie, Import-Schluessel und Stapel bleiben.
Das haelt alles heraus, was am Zusammenfuehren haette brechen koennen: der
Duplikatschutz verliert keinen Schluessel, undo_batch reisst keine Gegenseite
mit, und eine falsche Paarung ist ein Etikett, das man abzieht.
Die Auswertung ueberspringt verknuepfte Buchungen – nicht umkategorisiert:
Loest man die Verknuepfung, zaehlt die Buchung mit ihrer alten Kategorie
sofort wieder mit.
Automatisch nur, wenn die Zuordnung zwingend ist UND keine Seite eine
Kategorie traegt UND der Text auf einen Uebertrag hindeutet. Sonst Vorschlag.
Waehrungswechsel werden ueber den EZB-Kurs des Tages geprueft (2,5 % Toleranz
fuer den Bankaufschlag) – der Kurs dient nur als Pruefung, gespeichert wird
nichts davon.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Bisher brauchte es je Konto einen eigenen Kurzbefehl, weil account_id
Pflicht war. Jetzt ist sie optional: Raiffeisen fuehrt die eigene IBAN in der
Datei mit (Treffer gegen die Erkennungs-Texte der Konten), Revolut die
Waehrung – zusammen mit der erkannten Bank und dem Import-Verlauf reicht das.
Bleibt es mehrdeutig, wird NICHT geraten: Ein Auszug im falschen Konto
verfaelscht zwei Salden auf einmal. Stattdessen nennt die Fehlermeldung die
Konten samt Nummern, sodass man sie im Kurzbefehl nachtragen kann. Hat der
Schluessel ein Standardkonto, gilt das.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der Kurzbefehl zeigt das Feld "message" an. Fehlerantworten liefern aber
"detail" – die Mitteilung auf dem Telefon blieb also leer. Man sah nicht
einmal, DASS etwas schiefging: Die Marke wurde als benutzt vermerkt, auf dem
Server kam trotzdem nichts an, und die Ursache war von aussen unsichtbar.
Antworten unter /api/quick liefern jetzt zusaetzlich "message" mit einem
lesbaren Text. Bei fehlenden Pflichtfeldern nennt er sie beim Namen und
erinnert daran, dass der Anfragetext beim Datei-Kurzbefehl 'Formular' sein
muss.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Bisher musste man bei jedem Import Trennzeichen, Datumsformat und
Spaltenzuordnung von Hand eintragen – die haeufigste Fehlerquelle ueberhaupt:
Ein falsches Datumsformat liess alle Zeilen durchfallen, ein falscher
Dezimaltrenner verzehnfachte Betraege.
Zuerst wird gegen die Bank-Vorlagen geprueft (Kopfzeilen-Signatur); alle vier
echten Ausaege des Nutzers werden so sicher erkannt. Sonst inhaltsbasiert:
welche Spalte enthaelt Daten, welche Betraege, welche den laengsten Text.
Zwei Fallen, die dabei aufgefallen sind und getestet werden:
- Deutsche Datumsangaben stecken voller Punkte, und viele Auszuege fuehren
Buchungs- UND Valutadatum. Ohne sie herauszurechnen kaeme "." als
Dezimalzeichen heraus und aus 1.204,70 wuerden 1.20.
- parse_date ist absichtlich nachsichtig; beim Erkennen darf es das nicht
sein, sonst gewinnt ein Format mit Uhrzeit gegen das richtige ohne.
Ganzzahlen ohne Vorzeichen zaehlen nur halb, damit Referenznummern und Kurse
nicht als Betragsspalte durchgehen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Zwei Luecken geschlossen:
Mail-Vorschlaege standen als "pending" im Eingang, aber es gab keinen Knopf
zum Buchen – der Weg war hinten offen. Jetzt je Eintrag "Buchen" und ein
"Alle buchen". Unklare bleiben liegen: Bei ihnen fehlt etwas oder die
Erkennung war unsicher, die soll man ansehen statt im Stapel durchzuwinken.
Der Auszug vom Telefon bucht nicht mehr sofort, sondern wird nur abgelegt.
Am Telefon prueft man keine 500 Zeilen; die Datei wartet in der
Import-Ansicht und laeuft dort durch die normale Vorschau mit
Saldo-Abgleich, Zeilenfilter und Duplikatpruefung.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Revolut und Raiffeisen lassen die CSV auch am Handy exportieren; nur N26
verlangt die Web-Version. Ein zweiter Kurzbefehl nimmt die Datei aus dem
Teilen-Menue entgegen und schickt sie mit dem gewaehlten Konto an
/quick/statement.
Am Telefon laesst sich keine Spaltenzuordnung vornehmen. Der Endpunkt
verwendet deshalb die Einstellung, mit der dieses Konto zuletzt erfolgreich
eingelesen wurde – und sagt klar Bescheid, wenn es noch keine gibt, statt
mit falschen Spalten zu raten.
Gebucht wird sofort: Ein Auszug hat hunderte Zeilen, die einzeln abzunicken
waere keine Erleichterung. Sicher ist das durch die Duplikaterkennung und
weil der Stapel im Import-Verlauf mit einem Klick zurueckzunehmen ist.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Revolut verschickt fuer eine Zahlung in Euro und eine in Franken dieselbe
Mail vom selben Absender. Ohne die Waehrung als Kriterium landete alles auf
demselben Konto.
Die Erkennung liest die Waehrung ohnehin aus; sie wird jetzt VOR der
Zuordnung ausgewertet und ist ein optionales Filterkriterium. Zuordnungen
mit Waehrung werden zuerst geprueft – wer neben ein allgemeines "revolut"
ein genaues "revolut + EUR" stellt, meint das genaue, unabhaengig von der
Anlagereihenfolge.
Uebergangene Mails melden ausserdem die erkannte Waehrung mit, damit beim
Einrichten sichtbar ist, welcher Wert zu filtern waere.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ohne den Beguenstigten ist ein Eintrag im Eingang wertlos – man sieht einen
Betrag und weiss nicht, wofuer. Genau das passierte bei weitergeleiteten
Mails: Gmail zitiert den Text mit "> " davor, die Beschriftung stand also
nicht mehr am Zeilenanfang, und je nach Kodierung kam "Beguenstigter" statt
"Beguenstigter" mit Umlaut an.
Beschriftungen werden jetzt normalisiert verglichen (Umlaute verschriftlicht,
alles ausser Buchstaben und Ziffern verworfen), und Zitatzeichen fallen vor
der Feldsuche weg. Die Suchbegriffe laufen durch dieselbe Normalisierung,
damit sie lesbar bleiben statt doppelt gepflegt zu werden.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der Loesch-Endpunkt war da, aber ohne Knopf in der Oberflaeche, und
Bearbeiten fehlte ganz – eine falsch eingetippte Zuordnung liess sich also
nicht mehr korrigieren.
Jede Zuordnung ist jetzt eine eigene Zeile mit Erkennungstext und Konto;
der Speichern-Knopf erscheint erst bei einer Aenderung. Dazu ein PUT auf
/mail/senders/{id} im Backend.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Bei einer von Hand weitergeleiteten Bank-Mail steht die eigene Adresse im
From; "Raiffeisen" findet sich nur noch im Betreff ("WG: Raiffeisen …")
und im zitierten Kopf. Das Feld hiess "Absender enthaelt" und prueft nur die
Absender-Koepfe – die Mail wurde also uebergangen, obwohl das Suchwort
sichtbar darin stand. Eine Falle, kein Bedienfehler.
Gesucht wird jetzt in Absender-Koepfen, Betreff und den ersten 2000 Zeichen
des Textes; die Beschriftung heisst entsprechend "Erkennungstext". Ein Test
mit einer nachgebauten Weiterleitung haelt fest, dass sowohl die Zuordnung
greift als auch Betrag und Empfaenger daraus lesbar bleiben.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Auf UNSEEN zu filtern klingt sparsam, ist aber eine Falle: Wer eine Mail in
seinem Mailprogramm ansieht, bevor Certain sie geholt hat, verliert sie
dauerhaft – sie ist dann fuer immer gelesen. Genau das ist passiert, als die
Mails vor dem ersten Abruf schon in Gmail geoeffnet waren: Ergebnis "0 neu",
ohne jeden Hinweis auf die Ursache.
Gesucht wird jetzt ab dem letzten Abruf mit einem Tag Ueberlappung; dass
nichts doppelt ankommt, sichert die Message-ID ab.
Die Meldung nennt ausserdem, wie viele Mails ueberhaupt geprueft wurden.
"0 neu" allein liess offen, ob nichts da war oder nichts passte.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Wer Bank-Mails in ein Sammelpostfach weiterleitet, hat im From je nach
Methode die eigene Adresse stehen. Geprueft werden deshalb auch Reply-To,
Return-Path, Sender und X-Forwarded-For – bei automatischer Weiterleitung
bleibt der Originalabsender dort erhalten.
Ausserdem meldet der Abruf jetzt, welche Mails er uebergangen hat, mit
Absender und Betreff. Beim Einrichten war das sonst Raten: Man sah nur
"3 uebersprungen" und wusste nicht, was in den Filter gehoert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Verschluesselung fuer Zugangsdaten: Ein IMAP-Passwort laesst sich nicht
hashen, Certain muss es dem Mailserver vorlegen. Es liegt deshalb mit
Fernet verschluesselt in der Datenbank, der Schluessel wird per HKDF aus
session_secret abgeleitet und nirgends gespeichert. Wird das Geheimnis
getauscht, meldet Certain das verstaendlich statt mit einem Stacktrace.
- IMAP-Abruf ueber imaplib/email (Standardbibliothek). Ordnerliste,
Verbindungstest mit klarer Fehlermeldung, Absender-Zuordnung auf Konten,
danach als gelesen markieren oder verschieben.
- Wiedererkennung ueber die Message-ID statt der UID – die vergibt der
Server beim Verschieben neu.
- Nichts wird stillschweigend gebucht: Eine Mail wird zum Eingangs-Eintrag.
Fehlt das Zielkonto oder ist ein Feld unsicher, gilt sie als unklar.
- Hintergrund-Thread beim Start, nach dem Muster des EZB-Kursabrufs: ein
langsames Postfach darf den Dienst nie blockieren.
Getestet gegen einen IMAP-Doppelgaenger, damit die Suite ohne Netz laeuft.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Kein festes Schema je Bank, sondern beschriftete Felder suchen. Die beiden
Banken bauen ihre Mails verschieden: Raiffeisen schickt Klartext mit
"Betrag: 14.90 CHF", Revolut nur HTML mit dem Wert UNTER der Beschriftung.
Beide Bauformen werden abgedeckt, dazu der Betreff als eigene Quelle.
Jeder Wert merkt sich, woher er stammt – ohne das laesst sich ein falscher
Betrag spaeter nicht erklaeren.
Was bewusst NICHT geraten wird: Stehen mehrere gleichwertige Betraege ohne
Beschriftung da (Umsatz und neuer Saldo), bleibt das Feld unklar. Ein
Zahlungsauftrag mit Status "Zur Visierung" gilt nicht als geflossen. Blosse
Zahlen ohne Waehrung sind keine Betraege, und Stilbloecke werden vor dem
Entfernen der Tags herausgeschnitten – sonst haelt das CSS "480px" fuer
Geld.
Die Muster unter tests/fixtures/mail sind echte Mails, anonymisiert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Testdatenbank lebt ueber den ganzen Lauf und Schlagwoerter sind je
Nutzer eindeutig; "coop" legt bereits ein anderer Test an. Der Test lief
allein durch und fiel im Verbund um. Jetzt mit einem zufaelligen Wort.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der Kontoauszug kommt erst am Monatsende; wer im Laden bezahlt hat, weiss
in dem Moment noch, wofuer. iOS laesst keine App fremde Mitteilungen
mitlesen, also fuehrt der Weg ueber einen Kurzbefehl, der die Zahlung an
Certain schickt.
Gebaut:
- Geraetemarken mit eigener Abhaengigkeit statt Sonderfall in
get_current_user. Eine Marke gilt NUR an /quick – auf allen anderen
Wegen ist sie keine Anmeldung. Geht das Telefon verloren, ist kein
Datenabfluss moeglich; Tests halten das fuer Buchungen, Konten,
Kategorien, Export und Datenbanksicherung fest.
- POST /quick/booking laeuft durch detect_rows, damit Schlagwort- und
Betragsregeln die Kategorie schon vorschlagen. external_id verhindert,
dass ein doppelt ausgeloester Kurzbefehl zweimal bucht.
- inbox_items merkt sich die Zahlung ueber das Buchen hinaus. Beim
spaeteren CSV-Import greift der Import-Schluessel nicht: Der Text vom
Telefon ist selten wortgleich mit dem des Auszugs und die Bank bucht ein
bis zwei Tage spaeter. detect_rows vergleicht deshalb zusaetzlich
unscharf – exakter Betrag, Datum +/- 3 Tage, je Eintrag nur einmal.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der uebliche Ablauf ist, Mitte Monat einzulesen und Ende Monat den ganzen
Monat noch einmal. Die Tests halten fest, dass bereits gebuchte Zeilen
nicht erneut in den Saldo laufen, neue ankommen – und dass zwei bzw. drei
echte gleich aussehende Buchungen desselben Tages erhalten bleiben.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>