Commit Graph
65 Commits
Author SHA1 Message Date
GuidoandClaude Opus 5 63056ae76a feat: Ringdiagramm faerbt Legende und Mitte nach Budget
Eingefaerbt war bisher nur die Listen-Darstellung der Ausgaben-Anteile. In der
Kreis-Darstellung blieben die Betraege der Legende grau, und die Summe in der
Mitte sagte gar nichts ueber den Stand.

Jetzt tragen beide die Aussage: gruen im geplanten Rahmen, rot darueber. Faehrt
man ein Segment an, zeigt die Mitte dessen Betrag – dann gilt dort das Budget
dieser Kategorie, sonst das des Ganzen.

Fuer die Mitte liefert der Server jetzt auch das Geplante ueber ALLE
Ausgaben-Einheiten, nicht nur ueber die mit Ausgaben: Der Monatsplan umfasst
auch, wofuer noch nichts ausgegeben wurde.

Ohne Plan bleibt alles, wie es war. Der Donut faerbt nur, wenn ein Segment die
Angabe mitbringt – die Budget-Karte, die dieselbe Komponente benutzt, bleibt
also unberuehrt.

Der Test hat dabei gleich einen Fehler gefunden: ``_units`` ist ein Generator,
und ich lief zweimal darueber – beim zweiten Mal war er leer und das Geplante
immer 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-30 14:13:49 +02:00
GuidoandClaude Opus 5 5e70644f73 feat: Ausgaben-Anteile faerben nach Budget
Die Karte zeigte nur, wie sich die Ausgaben verteilen – nicht, ob sie im
Rahmen liegen. Der Betrag ist jetzt gruen, solange er unter dem Geplanten
bleibt, und rot darueber. Dieselben Farben wie ueberall sonst fuer Einnahme
und Ausgabe.

Ohne Plan bleibt der Betrag gedeckt. "Ueber Budget" waere sonst die Aussage
ueber jede Kategorie, fuer die nie eine Vorgabe gemacht wurde – die Karte
stuende dann fast durchgehend auf Rot und sagte nichts mehr.

Das Geplante kommt aus derselben Quelle wie im Budget-Fortschritt, also
einschliesslich eines Monats-Werts, der die Vorlage uebersteuert.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-30 14:05:28 +02:00
GuidoandClaude Opus 5 7c0d13cc8a feat: Kontostand aufschluesseln – und der Angleich-Knopf bleibt
Zwei Sachen aus derselben Wurzel: Wenn ein Saldo nicht stimmt, sieht man nicht,
warum, und kann auch nichts dagegen tun.

Ein Klick auf den Kontostand zeigt jetzt die Rechnung: Anfangsbestand,
Einnahmen, Ausgaben, Uebertraege raus und rein – jeweils mit der Anzahl der
Buchungen – und was daraus wird. Darunter der Saldo der Bank und, einzeln
aufgefuehrt, alle Uebertraege des Kontos. Genau die sind die haeufigste
Ursache und in einer langen Buchungsliste am schwersten zu ueberblicken.

Rechnung und Kontenliste kommen aus EINER Quelle: ``account_balance_cents``
liest jetzt aus der Aufschluesselung. Sonst koennte die Anzeige etwas anderes
behaupten als der Saldo, und man suchte den Fehler im Falschen.

Ausserdem war der Knopf "Saldo der Bank uebernehmen" verschwunden, sobald die
Bank nur einen verfuegbaren Saldo meldet. Das war zu weit gedacht: Nicht
vergleichbar heisst nicht, dass man nicht angleichen duerfte. Der Knopf ist
zurueck, und die Nachfrage sagt in dem Fall, worauf man sich einlaesst – der
verfuegbare Saldo enthaelt Vorgemerktes, das spaeter noch als Buchung kommt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-30 13:33:27 +02:00
GuidoandClaude Opus 5 e0d8882851 fix: Automatischer Abruf lief gegen die Abfragegrenze der Banken
"Weniger als 15 Minuten lassen die Banken nicht zu" stand als Hinweis im
Formular. Das war erfunden. PSD2 verpflichtet die Banken zu VIER
unbeaufsichtigten Abfragen je Konto und Tag – haeufiger duerfen sie ablehnen,
und N26 tut es: "Too many requests", und der Abruf scheitert, bis das
Zeitfenster weiterrueckt.

Bei zehn angebundenen Konten und einem Takt von 15 Minuten waren es tausende
Anfragen am Tag. Der Takt betraegt jetzt mindestens sechs Stunden, also genau
die zugesicherten vier je Tag. Bestehende, zu knappe Einstellungen werden beim
Faelligkeitspruefen mitgebremst – sonst liefe eine gespeicherte 15 weiter
dagegen.

Von Hand abrufen bleibt unbegrenzt: Dabei sitzt der Nutzer davor, und fuer
diesen Fall gilt die Grenze nicht.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-30 13:11:35 +02:00
GuidoandClaude Opus 5 6ed91ddd58 fix: Verknuepfte Buchungen brauchen keine Kategorie mehr
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>
2026-08-30 13:07:29 +02:00
GuidoandClaude Opus 5 bb748cc092 feat: Certain fragt in der Buchungsliste nach, ob es ein Uebertrag ist
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>
2026-08-30 13:03:25 +02:00
GuidoandClaude Opus 5 c1561acdce fix: Vier Ursachen, warum Saldo und Umsaetze auseinanderliefen
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>
2026-08-27 21:44:01 +02:00
GuidoandClaude Opus 5 4000082df4 fix: Dieselbe Zahlung ueber zwei Bankzugaenge kam doppelt herein
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>
2026-08-27 21:39:57 +02:00
GuidoandClaude Opus 5 b4c90f5221 fix: Bankabruf verschluckte Umsaetze mit wiederkehrendem Betrag
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>
2026-08-27 16:45:38 +02:00
GuidoandClaude Opus 5 f2c1ab7dec feat: Regeln nachtraeglich auf Buchungen ohne Kategorie anwenden
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>
2026-08-27 16:02:04 +02:00
GuidoandClaude Opus 5 61dc6c7f0b feat: Mail-Abruf sagt, was mit den Treffern passiert ist – und jede Buchung,
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>
2026-08-27 13:11:18 +02:00
GuidoandClaude Opus 5 60b8178c57 fix: Mails vor der Einrichtung waren fuer immer ausserhalb des Suchfensters
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>
2026-08-27 12:59:08 +02:00
GuidoandClaude Opus 5 6cd1c100c0 fix: Uebertrag pruefen, ob die Gegenseite schon gebucht ist
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>
2026-08-27 11:22:13 +02:00
GuidoandClaude Opus 5 b303c87801 feat: Eine Arbeitsliste, und Kategorien direkt in der Buchungsliste
'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>
2026-08-27 10:25:20 +02:00
GuidoandClaude Opus 5 8309f58a67 feat: Kurs der Bank statt EZB-Naeherung, und Saldo uebernehmen
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>
2026-08-27 10:18:33 +02:00
GuidoandClaude Opus 5 8ceee5c5b1 feat: Ein Uebertrag faengt seine Gegenseite ab
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>
2026-08-27 10:13:52 +02:00
GuidoandClaude Opus 5 90bda6b161 fix: Ein Uebertrag im Eingang fragt nicht mehr nach der Kategorie
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>
2026-08-27 10:08:09 +02:00
GuidoandClaude Opus 5 8084ba511a feat: Umsaetze je Konto holen, Takt fuer den automatischen Abruf
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>
2026-08-27 10:04:29 +02:00
GuidoandClaude Opus 5 a471eba617 feat: Kontouebertraege lassen sich im Eingang buchen
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>
2026-08-27 06:31:49 +02:00
GuidoandClaude Opus 5 b9abb912e3 feat: Angebundene Konten buchen selbst – der Saldo kann nicht abweichen
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>
2026-08-27 06:21:10 +02:00
GuidoandClaude Opus 5 2379cf4973 feat: Uebertraege lassen sich endlich bedienen
Der Uebertrags-Dienst und seine fuenf Endpunkte gab es seit Wochen – ohne
einen einzigen Aufrufer in der Oberflaeche. Man konnte also nirgends sagen,
dass zwei Buchungen derselbe Uebertrag sind, und eine gefundene Verknuepfung
auch nicht wieder loesen.

Jetzt: eine Karte "Moegliche Uebertraege" im Eingang mit beiden Seiten
nebeneinander, Verbinden und Ignorieren; in der Buchungsliste eine Marke mit
dem Gegenkonto und ein Knopf zum Loesen. Das Gegenkonto kommt vom Server mit –
die andere Buchung steht oft gar nicht in der gefilterten Liste, und
"Uebertrag" ohne Angabe wohin sagt nichts.

Ausserdem wird nach dem Buchen aus dem Eingang nach Uebertraegen gesucht, so
wie bisher nur nach einem Import. Seit die Bankanbindung ihre Umsaetze in den
Eingang legt, entsteht die Gegenseite naemlich erst dort; ohne diesen Aufruf
wuerde sie nie gefunden. Ein Fehler dabei kippt das Buchen nicht.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 23:13:40 +02:00
GuidoandClaude Opus 5 63de8368f0 feat: Kontostand von der Bank – die eigene Rechnung wird geprueft
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>
2026-08-26 23:08:56 +02:00
GuidoandClaude Opus 5 9a0c607e8c fix: "Keine Treffer" sagt jetzt, woran es liegt
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>
2026-08-26 21:48:59 +02:00
GuidoandClaude Opus 5 71e8419634 feat: Bank ueber alle Laender suchen
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>
2026-08-26 21:42:08 +02:00
GuidoandClaude Opus 5 7a3bd200b1 feat: Bankanbindung ueber PSD2 (Backend)
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>
2026-08-26 21:25:20 +02:00
GuidoandClaude Opus 5 c9a598a056 feat: Ueberträge als Verknuepfung erkennen, ohne Buchungen anzufassen
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>
2026-08-23 22:41:34 +02:00
GuidoandClaude Opus 5 e8a862bd63 feat: Konto wird aus der Datei erkannt – ein Kurzbefehl genuegt
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>
2026-08-22 23:54:27 +02:00
GuidoandClaude Opus 5 e6801b0474 feat: CSV-Format automatisch erkennen
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>
2026-08-22 21:17:37 +02:00
GuidoandClaude Opus 5 c47525ff7e feat: Vorschlaege buchen und Telefon-Dateien nur ablegen
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>
2026-08-22 21:12:58 +02:00
GuidoandClaude Opus 5 34be575eb3 feat: Kontoauszug vom Telefon per Teilen-Menue einlesen
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>
2026-08-22 21:06:20 +02:00
GuidoandClaude Opus 5 7b087247a5 feat: Absender-Zuordnungen bearbeiten und loeschen
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>
2026-08-22 20:30:24 +02:00
GuidoandClaude Opus 5 fc6ee2f345 feat: Postfaecher einrichten und Zahlungs-Mails abholen (Etappe B, Teil 2)
- 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>
2026-08-22 19:58:06 +02:00
GuidoandClaude Opus 5 02260d7cd2 feat: Schnellerfassung per Kurzbefehl (Etappe A)
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>
2026-08-22 19:34:14 +02:00
GuidoandClaude Opus 5 3a44509e77 feat: Import-Vorlage richtet sich nach dem gewaehlten Konto
Beim Kontowechsel musste man die Vorlage jedes Mal neu waehlen – und las
im schlimmsten Fall den Revolut-Auszug mit der Raiffeisen-Vorlage ein
(genau das steht im Verlauf).

Eigene Vorlagen standen schon als template_id im Verlauf; Bank-Vorlagen
sind fest im Code und hinterliessen nichts. Der Verlauf merkt sich deshalb
jetzt auch deren Namen (Migration 0014). Die Oberflaeche waehlt beim
Kontowechsel, womit zuletzt in dieses Konto importiert wurde.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 20:44:50 +02:00
GuidoandClaude Opus 5 6390f7c325 feat: Seite "Buchungen ohne Kategorie" zum Abarbeiten
Bisher musste man in der Gesamtliste nach Luecken suchen. Der Endpunkt
kennt jetzt ?uncategorized=true; Uebertraege bleiben aussen vor, denn sie
ordnen kein Geld ein, sie verschieben es nur.

Die Seite zeigt die offenen Buchungen aelteste zuerst; nach dem Zuordnen
verschwindet die Zeile, sodass der Stapel sichtbar kleiner wird. Auf Wunsch
wird der Zweck gleich als Schlagwort gemerkt, damit die naechste gleiche
Buchung schon beim Import zugeordnet wird.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 20:40:52 +02:00
GuidoandClaude Opus 5 9642901e4e feat: Kontenreihenfolge selbst bestimmen
Die Liste stand in der Anlegereihenfolge, die selten die gewuenschte ist.
Neuer Endpunkt PUT /accounts/order nimmt die IDs in der gewuenschten Folge
und schreibt die Position; er muss vor /{account_id} stehen, sonst faengt
die ID-Route den Pfad ab. Fremde IDs werden abgewiesen.

In der Kontenliste zwei Pfeile je Zeile, optimistisch sortiert, damit die
Liste beim Klicken nicht springt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 20:36:59 +02:00
GuidoandClaude Opus 5 51af9eacff fix: Konto loeschen veraendert keine fremden Salden mehr
Beide Konto-Spalten einer Buchung hingen an ON DELETE CASCADE. Wurde ein
Zielkonto geloescht, verschwand die ganze Buchung – auch die Abgangsseite,
die einem anderen Konto gehoert. Dessen Saldo stieg dadurch stillschweigend
um den Uebertrag; beim Loeschen von 'Revolut investments' waren es 1'020.99.

Vor dem Loeschen werden solche Uebertraege jetzt zu gewoehnlicher Ausgabe
bzw. Einnahme auf dem verbleibenden Konto. Nur was allein am geloeschten
Konto haengt, geht mit ihm.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 17:01:40 +02:00
GuidoandClaude Opus 5 5dbfd6abc4 fix: Anfangssaldo gilt ab dem Stichtag
Der Stichtag eines Kontos wurde gespeichert und angezeigt, von der
Saldo-Berechnung aber ignoriert: sie addierte jede Buchung des Kontos,
auch die aus der Zeit vor dem Stichtag. Damit lief jeder Saldo um die
gesamte Vorgeschichte daneben.

Der Anfangssaldo beschreibt jetzt den Stand am ENDE des Stichtags – so
steht er auf dem Auszug. Buchungen bis einschliesslich dieses Tages
zaehlen nicht mehr zum Kontostand, bleiben aber in Liste und
Auswertungen. Beim Uebertrag wird jede Seite an ihrem eigenen Stichtag
gemessen; der Vermoegensverlauf folgt derselben Regel. Ohne Stichtag
zaehlt weiterhin alles.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 15:26:21 +02:00
GuidoandClaude Opus 4.8 5c8b53a995 feat: Kategorien aus Auswertungen ausnehmen (z. B. „Kontoübertrag")
Umbuchungen zwischen eigenen Konten sind keine Ausgabe – das Geld ist nicht weg,
es liegt woanders. Bisher blieb nur die Wahl zwischen einer echten Transfer-
Buchung (bildet die Wirklichkeit ab, braucht aber auf beiden Seiten passende
Regeln und schätzt bei Fremdwährung den Gegenwert) oder einer gewöhnlichen
Ausgabe, die Budget und Statistik verfälscht.

Kategorien haben jetzt den Schalter „Nicht in Auswertungen einrechnen": Die
Buchung wirkt weiter auf den Kontostand, bleibt aber aus Budget, Dashboard und
Auswertungen heraus. Gefiltert wird zentral in actual_expense_by_category und
income_by_category – damit greift es zugleich für Budget-Donut, Budget-
Fortschritt, Einnahmen/Ausgaben, Ausgaben-Anteile und die Budget-Seite.

Das ersetzt die Transfer-Paarung nicht, es ist der einfachere Weg: kein
Gegenkonto, keine Kursschätzung, dafür ohne Verbindung zwischen den beiden Seiten.

Migration 0013, Schalter im Kategorie-Dialog, Kennzeichnung in der Liste, Test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-21 10:17:20 +02:00
GuidoandClaude Opus 4.8 e10ccdf185 feat: Dashboard-Karte „Ausgaben-Anteile"
Zeigt für den laufenden Budget-Monat, wie sich die tatsächlichen Ausgaben
prozentual auf die Kategorien verteilen – absteigend sortiert, mit Balken in der
Kategoriefarbe, Prozentwert und Betrag.

Anders als der Budget-Donut hängt sie nicht am geplanten Budget: Sie zeigt reine
Ist-Ausgaben, auch wenn für den Monat nichts geplant wurde. Umschaltbar zwischen
Kategorien (Standard) und Gruppen.

Die Anteile rechnet die Oberfläche aus Betrag und Summe – gerundete Prozente vom
Server addierten sich sonst nicht auf 100 %. Wird die Karte schmal, gibt zuerst
der Betrag nach; ohne Kategorienamen sagt die Zeile nichts mehr aus.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-21 09:30:14 +02:00
GuidoandClaude Opus 4.8 8d767e728a feat: Buchen-Knopf auch unter der Prüfliste + Verlauf aufräumen
- Bei mehreren hundert Zeilen ist der Knopf oben längst aus dem Bild gescrollt;
  unter der Liste steht jetzt derselbe.
- Rückgängig gemachte Importe lassen sich aus dem Verlauf entfernen. Nur diese:
  solange ein Import steht, ist sein Eintrag die einzige Möglichkeit, seine
  Buchungen wieder loszuwerden – der Server lehnt das sonst ab.

Test für beide Fälle des neuen Endpunkts; 92 grün.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-21 09:20:29 +02:00
GuidoandClaude Opus 4.8 4a6f7a0814 feat: Zeilenfilter für Auszüge mit mehreren Pockets + Gebühren-Richtung
Der Revolut-Auszug enthält ALLE Produkte in einer Datei – im CHF-Export stehen
533 Giro-Zeilen und 255 Zeilen des Ersparten, jedes mit eigenem Kontostand-
Verlauf. Ohne Trennung landeten beide auf demselben Konto und die Pocket-
Bewegungen zählten doppelt.

- Import-Vorlage kennt einen Zeilenfilter („nur Zeilen aus Spalte X mit Wert Y"),
  Migration 0012; damit bekommt jedes Pocket sein eigenes Konto
- Gebührenzeilen (Betrag 0.00) können Belastung ODER Erstattung sein – beide
  sehen identisch aus. Die Richtung kommt jetzt aus der Kontostand-Spalte;
  ohne sie bleibt die Belastung der Normalfall
- Tests für beides; 90 grün

Nachgerechnet an den echten Auszügen: Raiffeisen 223.37, Revolut CHF 61.88,
Erspartes 6.71, Revolut EUR 0.68 – alle vier stimmen zeilengenau mit der
Kontostand-Spalte überein.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-21 09:01:07 +02:00
GuidoandClaude Opus 4.8 595662e515 feat: ein Schlagwort darf mehrere Beträge haben
Ein Wort war je Art nur einmal erlaubt, also ließ sich genau ein Preis
hinterlegen. Im Personalrestaurant zahlt man aber mal 5.00 und mal 7.50 – der
zweite Preis ließ sich nicht erfassen.

Der Betrag gehört jetzt in den Unique-Schlüssel: „sv gruppe = 5.00" und
„sv gruppe = 7.50" sind zwei Regeln derselben Kategorie, dasselbe Wort OHNE
Betrag bleibt der Auffang für alle übrigen Beträge (und darf in einer anderen
Kategorie liegen). Betrags-Regeln greifen vor dem Auffang.

- Migration 0011 (Unique-Schlüssel um amount_cents erweitert)
- Konflikt nur noch bei gleichem Wort UND gleichem Betrag; gilt auch beim
  Umbenennen und für Transfer-Schlagwörter
- Test: zwei Preise treffen die Kategorie, ein dritter Betrag den Auffang; 88 grün

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-21 08:34:24 +02:00
GuidoandClaude Opus 4.8 5a747fbef4 feat: Saldo-Abgleich beim CSV-Import gegen die Kontostand-Spalte
Bisher merkte man einen Importfehler erst am falschen Endsaldo, ohne Hinweis auf
die Ursache. Enthält der Auszug eine laufende Kontostand-Spalte, gleicht der
Import jetzt jede Zeile dagegen ab: Die Spalte sagt, wie sich der Saldo
tatsächlich verändert hat, unsere Erkennung muss dazu passen.

- neue Spaltenzuordnung "Kontostand (laufend)"; Presets Raiffeisen/Revolut ergänzt
- Prüfansicht zeigt: stimmt überein ODER Differenz + erste abweichende Zeile
  (Datum, Empfänger, Betrag laut Bank vs. erkannt)
- meldet zusätzlich den Anfangssaldo laut Datei – häufigste Fehlerquelle
- rechnet mit dem tatsächlich gebuchten Betrag, damit auch nicht gebuchte Zeilen
  auffallen (z. B. nicht zugeordnete Gebührenspalte); Fremdwährungs-Zeilen
  gehören zu einem anderen Saldo-Verlauf und bleiben außen vor
- erkennt absteigend sortierte Auszüge
- Tests: stimmiger Auszug (ok) und fehlende Gebührenspalte (zeigt genau die
  Zeile). 83 grün.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-21 06:58:33 +02:00
GuidoandClaude Opus 4.8 01059b2bb2 fix: Buchungsliste zeigt alle Buchungen (Default-Limit 200 -> 5000)
Die Liste lud nur die 200 neuesten Buchungen, wodurch ältere Monate fehlten
(bei 254 importierten Zeilen reichte die Ansicht nur bis Juni zurück). Da die
Tabelle im Browser filtert und sortiert, werden nun standardmäßig alle Buchungen
geladen. Verifiziert am echten Auszug: 254 von 254, ab 05.01.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-21 06:02:22 +02:00
GuidoandClaude Opus 4.8 1f03483e5d fix: Konto umbenennen scheitert nicht mehr an Identifikator-UNIQUE
_set_identifiers glich die Liste per clear()+neu-Anlegen ab; SQLAlchemy fügte
dabei die (unveränderten) Identifikatoren vor dem Löschen ein und verletzte so
UNIQUE(account_id, kind, value_norm) -> Internal Server Error beim Speichern.
Jetzt als Diff: unveränderte behalten, nur fehlende hinzufügen / überzählige
entfernen. Betrifft alle Identifikator-Arten (IBAN/Nummer/Text).

Regressionstest: Umbenennen mit unverändertem Identifikator + Entfernen (76 grün).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-20 20:00:09 +02:00
GuidoandClaude Opus 4.8 c44892ff72 feat: Transfer-Schlagwort mit festem Zielkonto (z. B. REVOLUT -> Konto Revolut)
- Transfer-Regel kann direkt ein Zielkonto tragen (target_account_id); ohne ->
  weiterhin Auto-Erkennung aus dem Buchungstext (Konto-Texte)
- Migration 0010; Endpunkt aktualisiert bestehendes Wort statt Konflikt
- Import bevorzugt das feste Zielkonto, sonst _guess_target_account
- Konten-Seite: Transfer-Erkennung mit Zielkonto-Auswahl + Anzeige am Chip
- Test: festes Zielkonto greift ohne Konto-Texte (75 grün)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-20 19:06:24 +02:00
GuidoandClaude Opus 4.8 5f88660bcc feat: PDF-Beleg-Parser blockbasiert (mehrzeilig) + Seiten als Bild
- Neuer Parser erkennt mehrzeilige Auszüge (N26 & Co.): Beschreibung → Value
  Date → Buchungsdatum → Betrag stehen auf eigenen Zeilen; Betrags-Zeile schließt
  eine Buchung ab, Kopf-/Fußzeilen (Name/Adresse/eigene IBAN/Seitenzahl/
  Spaltenköpfe) werden gefiltert, Gegen-IBAN und Zweck werden übernommen
- Alter Ein-Zeilen-Heuristik-Parser bleibt als Fallback
- /documents/preview liefert zusätzlich die Seiten als PNG-Data-URLs
  (PyMuPDF get_pixmap, kein poppler nötig)
- Tests: N26-artige mehrzeilige Fixture + Seiten-Rendering (74 grün);
  am echten 34-Seiten-Auszug 207 Buchungen erkannt

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-20 17:14:12 +02:00
GuidoandClaude Opus 4.8 49b927e2eb feat: Budget – mehrere Monate nebeneinander + Kopieren Monat<->Vorlage
- Umschalter 1·2·3: Monate nebeneinander (responsives Raster, auf schmalem
  Schirm gestapelt); prev/next verschiebt das ganze Fenster
- Je Monat: „Als Standard festlegen" (Monat -> Vorlage) und „Standard
  übernehmen" (Vorlage -> Monat, überschreibt), plus Zurücksetzen
- Backend: copy_month_to_template / copy_template_to_month + Endpunkte
  POST /budget/template/from-month/{label} und /budget/{label}/from-template
- Test fürs Kopieren; globale .grow-Utility ergänzt

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-20 17:07:49 +02:00
GuidoandClaude Opus 4.8 8d0fbdfe2b feat: Dashboard – Donut ohne Überlauf, Achse lesbar, Karten-Optionen
- Budget-Donut: Mittelbetrag passt sich der Länge an (kein Hineinlaufen in den
  Ring bei 4-stelligen Beträgen)
- Vermögensverlauf: Y-Achse ohne Währungssymbol (formatAmount) + etwas mehr
  linker Rand → „CHF …" wird nicht mehr abgeschnitten
- Karten-Optionen: im Anordnen-Modus je Karte einstellbar; Budget-Donut und
  Budget-Fortschritt können nach Gruppen ODER Kategorien aufschlüsseln
  (?group_by=), Auswahl wird im Layout (props) mitgespeichert

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-20 16:58:32 +02:00