Commit Graph
189 Commits
Author SHA1 Message Date
ScarriffleandClaude Opus 5 26718b1301 feat: Der Zuschnitt ist ein Rahmen, den man aufzieht
Zoomen war das falsche Werkzeug. Man konnte nur naeher heran, aber nicht
sagen, WAS man behalten will – und das Ergebnis war immer quadratisch, auch
wenn das Logo breit ist.

Jetzt liegt ein Rahmen auf dem Bild: ziehen zum Verschieben, acht Griffe zum
Aufziehen. Aussen abgedunkelt, Drittel-Linien zum Ausrichten, und darunter
steht, wie gross der Ausschnitt gerade ist. "Ganzes Bild" setzt zurueck.

Das Seitenverhaeltnis des Ausschnitts bleibt. Ein breites Logo wird ein
breites Logo, kein quadratisches mit Luft an den Seiten.

Der Ausschnitt wird in BILDPUNKTEN gefuehrt, nicht in Bildschirmpunkten –
umgerechnet wird nur fuer die Anzeige. So trifft das Ergebnis genau den
Bereich im Rahmen, ohne dass sich ueber viele Zieh-Bewegungen Rundungsfehler
aufaddieren.

Am Bildrand haelt die gegenueberliegende Kante still, statt dass der Rahmen
beim Anschlag mitwandert; zieht man einen Griff ueber die eigene Kante
hinaus, bleibt sie stehen, statt sich umzustuelpen. Auf dem Telefon sind die
Griffe groesser – mit dem Finger waeren vierzehn Pixel nicht zu treffen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 19:59:11 +02:00
ScarriffleandClaude Opus 5 9b9ce77267 feat: Das Logo laesst sich zuschneiden
Ein Screenshot von einer Website ist selten schon das Logo – da ist Rand
drumherum oder drei Marken nebeneinander. Nach dem Waehlen kommt deshalb ein
Rahmen: ziehen zum Verschieben, Regler zum Zoomen.

Der Rahmen IST der Ausschnitt. Deshalb nichts abgedunkelt und keine
Hilfslinien darin – man soll das Ergebnis sehen und nicht eine Vorschau
davon, die man erst uebersetzen muss.

Zu Beginn steht das GANZE Bild im Rahmen. Wer nicht zuschneiden will, drueckt
Uebernehmen und bekommt, was er hochgeladen hat; Zuschneiden ist ein Zoom,
kein Zwang. Verschieben geht nur, solange das Bild den Rahmen ueberragt –
sonst schoebe man ein kleines Bild aus seinem eigenen Ausschnitt heraus.

Geschnitten wird im Browser, hochgeladen nur noch der Ausschnitt: Der Server
bekommt kein Bild, das er gar nicht behalten soll.

Der durchsichtige Grund bleibt durchsichtig – ein weisser Kasten fiele auf
dunklem Hintergrund sofort auf.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 19:50:16 +02:00
ScarriffleandClaude Opus 5 dd2c33725f feat: Ein Markenlogo je Vertrag
Am Logo findet man den Vertrag schneller als am Namen: Netflix erkennt man am
roten N, nicht am Wort – und erst recht nicht an einem Zahnrad aus der
Symbolauswahl. Die bleibt als Rueckfall fuer alles, wozu es kein Logo gibt.

Hochgeladen wird, was das Telefon hergibt; gespeichert ein PNG von hoechstens
128 Pixeln Kantenlaenge. Aus drei Megabyte werden ein paar Kilobyte, und das
Seitenverhaeltnis bleibt – ein verzerrtes Logo sieht falscher aus als ein
kleines.

Immer PNG, egal was hereinkam. Das erspart der Anzeige jede
Fallunterscheidung und schliesst SVG aus: Eine SVG-Datei kann Skript
enthalten, und ein Bild, das der Nutzer hochlaedt und der Browser ausfuehrt,
ist genau die Art Falle, die man sich nicht in ein Finanzprogramm legt.

Eigene Tabelle, nicht eine Spalte an den Vertraegen: Sonst zoege jede
Listenabfrage saemtliche Bilder mit heraus, obwohl die Liste sie einzeln
nachlaedt.

ZWEI FEHLER IM SICHERN, die dabei aufgefallen sind:

Binaeres liess sich gar nicht sichern – die Ausgabe scheiterte an den Bytes,
und zwar erst beim Schreiben, also nachdem der Nutzer glaubt, ein Backup zu
haben. Jetzt Base64.

Und "Ergaenzen" legte Zeilen von Tabellen mit natuerlichem Schluessel ein
zweites Mal an, statt sie wiederzuerkennen. Beim Logo kippte damit die ganze
Wiederherstellung. Betraf bisher keine Tabelle, weil noch keine solche einen
Vergleichsschluessel hatte.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 19:35:37 +02:00
ScarriffleandClaude Opus 5 0a790fd45a feat: Nur die Waehrungen zeigen, die man braucht – und auf dem Telefon lesbar
Drei Sachen, die beim Benutzen aufgefallen sind.

ANGEBOTENE WAEHRUNGEN. In den Einstellungen steht jetzt, welche Waehrungen in
den Auswahlfeldern erscheinen (Vorgabe EUR, CHF, USD). Dass dreissig
unterstuetzt werden, ist gut; dreissig Zeilen in einem Feld zu sehen, wenn man
drei davon braucht, ist es nicht.

Die Einstellung beschraenkt nur die ANZEIGE. Gueltig bleibt alles mit
EZB-Kurs, und die gerade gesetzte Waehrung steht IMMER im Feld – sonst stuende
ein Konto in Dollar vor einem Feld ohne Dollar und wechselte beim naechsten
Speichern stillschweigend die Waehrung. Der Saldo waere danach still falsch.
Die Hauptwaehrung laesst sich nicht abwaehlen.

ZEICHEN STATT KUERZEL, wo es hilft: "€ EUR", "$ USD", "£ GBP". CHF steht
bewusst ohne: "Fr." ist weniger gelaeufig als CHF selbst. Mehrfach belegte
Zeichen bleiben ebenfalls draussen – "US$" von "CA$" zu unterscheiden ist
Raetselraten.

AUF DEM TELEFON. Die Vertragszeile quetschte vier Spalten in 375 Pixel; vom
Namen blieb "Sal..." uebrig und von der Kategorie ein Punkt. Sie ist jetzt ein
Gitter: Unter 620 Pixel rutscht der Betrag unter den Namen, die Knoepfe
bleiben rechts. Der Name bekommt die volle Breite – auf dem Telefon ist er das
Einzige, woran man die Zeile erkennt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 16:16:45 +02:00
ScarriffleandClaude Opus 5 f99be47587 feat: Abos & Vertraege – was regelmaessig abgeht, an einer Stelle
Netflix, Handyvertrag, Krankenkasse, Halbtax. Bisher sah man sie nur als
einzelne Buchungen im Auszug; die Frage "welche Abos habe ich eigentlich, und
was kosten die zusammen im Monat" liess sich nicht beantworten. Genau die
steht jetzt oben auf der Seite.

Der Bereich heisst "Abos & Vertraege", nicht "Abos": Ein Handyvertrag ist kein
Abo und die Versicherung erst recht nicht – niemand soll ueberlegen muessen,
wo sie hingehoert.

Der Monatswert wird gerechnet, nicht gespeichert (Betrag durch Intervall).
Gespeichert veraltete er beim naechsten Preisaufschlag stillschweigend.

Bei laengerem Intervall klappt "Ich lege monatlich zurueck auf" auf, mit dem
gerechneten Monatsbetrag daneben. Bei monatlicher Abrechnung gibt es nichts
anzusparen, also steht es da auch nicht.

DAZU: Waehrungen sind offen.

Die Rechen-Maschinerie konnte immer schon beliebige Waehrungen – sie rechnet
ueber den Euro als Drehpunkt. Beschraenkt haben es zwei Konstanten: eine
Erlaubnisliste mit EUR und CHF, und ein Filter, der aus dem EZB-Feed alles
ausser CHF wegwarf, obwohl er dreissig Waehrungen liefert.

Massgeblich ist jetzt die Liste der EZB. Frei erfundene Waehrungen bleiben
draussen, und das ist kein Geiz: Ohne Kurs liesse sich der Betrag nie
umrechnen – er stuende in keiner Summe und in keinem Budget, und das faellt
erst spaet auf. Ein Vertrag in einer Waehrung ohne Kurs wird deshalb genannt,
statt die Summe stillschweigend zu klein anzuzeigen.

Erkannt und gebucht wird noch nichts; der Erkennungstext wird nur erfasst.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 15:45:59 +02:00
ScarriffleandClaude Opus 5 ff621984dc feat: Eine Ausgabe, die kein Verlust ist
Sparen zaehlt im Budget wie eine Ausgabe – zu Recht, das Geld ist diesen Monat
gebunden. Es stand danach aber in Rot da, und wer tausend zuruecklegte statt
achthundert, bekam eine Warnung fuer etwas Gutes.

Ein Haken an der Kategorie stellt das ab. Zwei Folgen:

Der Betrag steht neutral. NICHT gruen: Gruen heisst in Certain ueberall "Geld
kommt rein", und CHF 1'010 in Gruen liest sich als Einnahme. Grau sagt genau
das Richtige – kein Verlust, kein Gewinn.

Und mehr als geplant ist keine Ueberschreitung mehr. Das gilt auch eine Ebene
hoeher: Eine Gruppe misst sich an dem, was WIRKLICH zu viel war. Sonst faerbt
sich "Finanzen" rot, weil jemand gespart hat, und die echte Ueberschreitung
daneben faellt nicht mehr auf. Angezeigt werden weiter die vollen Summen; nur
das Urteil laesst das Sparen aus.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 15:28:36 +02:00
ScarriffleandClaude Opus 5 15c2a164ec feat: Die Summe bleibt stehen, waehrend die Liste durchlaeuft
Bei mehr als ein paar Konten rollte "Gesamt" mit aus der Karte heraus – die
eine Zahl, wegen der man auf die Karte schaut, war damit die einzige, die man
suchen musste.

Sie klebt jetzt am unteren Rand. Gilt auch fuer die Ausgaben-Anteile: dieselbe
Zeile, dasselbe Problem.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 14:48:42 +02:00
ScarriffleandClaude Opus 5 cea238149b fix: Bei einem langen Formular war "Speichern" nicht erreichbar
Der Dialog wuchs mit seinem Inhalt und lief unten aus dem Bild. Scrollen ging
nicht – damit war der Knopf schlicht weg, und das Konto liess sich nicht mehr
speichern.

Jetzt bleibt der Dialog im Fenster: Kopf und Knopfleiste stehen fest, nur der
Inhalt dazwischen rollt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 14:39:08 +02:00
ScarriffleandClaude Opus 5 b69c383a72 fix: "Nicht in Auswertungen einrechnen" galt fuer Sparen nicht
Wer den Haken an einer Kategorie setzt, sagt damit ausdruecklich: nicht ins
Budget, nicht ins Dashboard, nicht in die Statistik. Die Sparen-Auswertung
las ihn nicht – das Geld stand danach im Ringdiagramm, obwohl der Nutzer
genau das abbestellt hatte.

Beide Uebertrags-Wege halten sich jetzt daran, der am Stueck wie der aus zwei
verknuepften Buchungen. Der Haken an der Kategorie gewinnt; ein Sparziel am
Konto darf ihn nicht stillschweigend aushebeln.

Und weil beides zusammen keinen Sinn ergibt, sagt es das Konto-Formular jetzt
auch: Waehlt man dort eine Kategorie mit Haken, steht darunter, dass so nichts
im Budget erscheinen wird.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 14:13:22 +02:00
ScarriffleandClaude Opus 5 e0688ab95e fix: Die Kontenseite stuerzte nach dem Sparziel-Feld ab
Das Laden der Kategorien landete in der falschen Komponente – in
TransferKeywords statt in Accounts. Dort gibt es kein `groups`, also warf die
Seite beim Aufbau "groups is not defined" und blieb leer.

Der Bau lief trotzdem durch: Es ist gueltiges JavaScript, nur eben mit einem
Namen, den es an dieser Stelle nicht gibt. Auffallen konnte das erst beim
Oeffnen der Seite.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 14:03:01 +02:00
ScarriffleandClaude Opus 5 25392b9d08 feat: Ein Konto kann sagen, dass Ueberweisungen darauf Sparen sind
Wer auf ein Pocket spart, budgetiert dafuer eine Ausgabe – im Budget stand
davon aber nichts. Zwei Gruende:

Erstens gibt es zwei Formen von Uebertrag. Die eine ist EINE Buchung mit
Zielkonto, wie sie das Formular anlegt; nur fuer die gab es die Sparen-Logik.
Die andere sind ZWEI verknuepfte Buchungen, und genau so kommt ein Uebertrag
ueber die Bankanbindung herein. Beide Seiten uebersprang die Auswertung
ausnahmslos.

Zweitens hing das Sparen am Kontotyp. Ein Revolut-Pocket ist bei der Bank ein
ganz normales Konto und kommt als "girokonto" herein – am Typ ist nicht
abzulesen, dass man darauf spart.

Jetzt sagt man es dem Konto einmal ("Sparen gegen Kategorie X"), und es gilt
fuer beide Formen. Wer eine einzelne Bewegung anders sieht, nimmt sie an der
Buchung heraus oder gibt ihr eine eigene Kategorie – dieselben zwei Felder wie
bisher.

Der Normalfall aendert sich nicht: Ein Uebertrag auf ein Konto ohne Sparziel
ist weiterhin bloss verschobenes Geld.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 13:53:26 +02:00
ScarriffleandClaude Opus 5 991a8e4fc2 fix: Die Kennung der Bank steht jetzt an der Buchung selbst
Bisher lag sie nur am Eingangs-Eintrag. Das reicht nicht: Der Eintrag ist eine
Zwischenstation und faellt weg, wenn ein Konto geloescht wird oder ein aelteres
Backup ihn nicht kannte. Dann war die Zahlung fuer den naechsten Abruf neu,
obwohl sie laengst gebucht war.

Am schlimmsten bei einem als Uebertrag gebuchten Umsatz: Der ist von der
Aehnlichkeitspruefung ausgenommen, dort griff also gar nichts mehr.

Die Kennung gehoert deshalb an die Buchung (transactions.source_uid, mit der
Quelle davor). Dort ueberlebt sie alles, was mit dem Eingang passiert, und die
Eindeutigkeit haelt die Datenbank selbst durch – nicht erst der pruefende Code
davor. Gestempelt wird an allen drei Buchungswegen: Eingang, Kurzbefehl und
Uebertrag.

Vorhandene Buchungen bekommen die Kennung nachgetragen, solange ihr
Eingangs-Eintrag noch da ist.

Ohne Kennung – von Hand erfasst, aus einer CSV-Datei – bleibt es beim
bisherigen Schutz ueber die Aehnlichkeit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 11:59:43 +02:00
GuidoandClaude Opus 5 175d9f50e0 feat: Sagen, wenn Oberflaeche und Server auseinanderlaufen
Ein "npm run build" allein startet den Dienst nicht neu. Die Oberflaeche
ruft dann Endpunkte auf, die es serverseitig noch gar nicht gibt - und das
sieht aus wie ein Fehler in der Funktion, nicht wie ein halbes Update.
Genau daran ging gerade eine Stunde Fehlersuche drauf: Die Aufstellung
eines Ausgaben-Anteils blieb leer, weil der Server den Endpunkt nicht
kannte.

Vite setzt den Commit beim Bauen ins Bundle, die Oberflaeche vergleicht
ihn beim Start mit dem, den der Server unter /api/version meldet. Weichen
sie ab, steht der Hinweis samt Kommando ueber jeder Seite. Kennt eine der
beiden Seiten ihren Stand nicht, schweigt der Hinweis - falscher Alarm
waere schlimmer als keiner.

Beide Richtungen im Browser geprueft: bei verschiedenen Staenden erscheint
er, bei gleichen nicht.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-30 15:28:05 +02:00
GuidoandClaude Opus 5 07ff0f2f8d fix: Ein gescheiterter Abruf sah aus wie "keine Ausgaben"
Die Aufstellung eines Ausgaben-Anteils schluckte jeden Fehler und zeigte
stattdessen "Noch keine Ausgaben in diesem Monat" mit einer Summe von
0.00 - waehrend die Karte daneben einen Betrag nannte. Damit war der
Widerspruch, den das Fenster aufloesen sollte, groesser als vorher.

Jetzt steht dort, was schiefging.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-30 15:20:07 +02:00
GuidoandClaude Opus 5 ee36dae34a feat: Zeigen, welche Buchungen in einem Ausgaben-Anteil stecken
Eine Zahl, die man nicht aufschluesseln kann, muss man glauben. Genau das
war der Streitpunkt: Die Karte nannte fuer Lebensmittel einen Betrag, und
es gab keine Moeglichkeit nachzusehen, welche Zahlungen darin einbezogen
sind und welche nicht.

Jetzt oeffnet ein Klick auf einen Anteil - in der Liste wie in der
Legende des Kreises - die Buchungen dahinter: Datum, Empfaenger, Konto,
Betrag. Die Liste kommt aus derselben Stelle, die auch die Summe bildet
(`budget.expense_rows`), sie kann also gar nicht etwas anderes zeigen als
das, was gezaehlt wurde. Ein Test haelt das fest.

Steht eine Buchung darin, obwohl ihr Datum ausserhalb des Zeitraums
liegt, traegt sie die Kalendermarke: Dann wurde ihr Budget-Monat von Hand
gesetzt, und das ist der Grund.

Nebenbei: Dialoge haengen jetzt am Seitenkoerper. Kacheln der Uebersicht
werden per `transform` positioniert - damit war die Kachel der
Bezugsrahmen fuer `position: fixed`, und der Dialog sass eingeklemmt
darin statt ueber der Seite.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-30 15:10:19 +02:00
GuidoandClaude Opus 5 0b19e07ce0 feat: Zu welchem Budget-Monat eine Buchung gehoert
Bisher entschied allein das Datum. Bei einem Budget-Monat, der nicht am
Ersten beginnt, faellt das auf: Die Zahlung geht am 24. ab und gehoert
doch zum naechsten Monat, die Rechnung wird verspaetet beglichen und
gehoert zum letzten.

Neu traegt jede Buchung einen Budget-Monat. Leer heisst weiterhin "aus dem
Datum" - der Normalfall, und er bleibt es. Wird einer von Hand gesetzt,
zaehlt die Buchung nur noch dort und in ihrem eigenen Monat NICHT mehr,
sonst stuende sie zweimal da.

Das Aendern liegt bewusst hinter einem Klick und hinter Pfeilen statt
hinter einer Auswahlliste: Es ist der Ausnahmefall, nicht der Normalfall,
und soll sich auch so anfuehlen. Waehlt man genau den Monat, der ohnehin
gilt, bleibt die Buchung automatisch - verschiebt sich das Datum spaeter,
wandert sie mit. In der Liste zeigt eine Marke den Monat, aber nur, wenn
jemand eingegriffen hat.

Der Server nimmt drei Monate in jede Richtung an. Ein Vertipper wie
2019-03 wird abgelehnt: Er liesse die Buchung aus jeder Auswertung
verschwinden, ohne dass irgendwo etwas fehlt.

Geprueft im Browser gegen eine Wegwerf-Datenbank mit Stichtag 25: Die
Karte summiert 25.08.-24.09., eine verschobene Buchung verlaesst diesen
Zeitraum und kehrt beim Zuruecknehmen zurueck.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-30 14:57:16 +02:00
GuidoandClaude Opus 5 bd79b83339 feat: Zeitraum an der Karte, und eine schaerfere Suche nach Dubletten
Zwei Dinge, die zusammen die Frage beantworten "zaehlt das wirklich ab
meinem Stichtag, und sind das wirklich zwei Zahlungen?".

Die Karte Ausgaben-Anteile nennt jetzt ihren Zeitraum. Gerechnet hat sie
schon immer ueber den eingestellten Budget-Monat (25.08.-24.09. bei
Starttag 25); nachvollziehen liess sich das nur nicht, weil die Karte
nirgends sagte, worueber sie summiert.

Die Dublettensuche vergleicht nicht mehr stur ueber den Text. Ein krummer
Betrag wie 10.43 kommt an einem Tag kaum zweimal vor - auch wenn zwei
Quellen ihn verschieden benennen, und genau daran scheiterte der
Textvergleich. Bei einem runden Betrag (Vielfaches von 50 Rappen) muss der
Text weiter passen, sonst waeren zwei Zwanziger am selben Tag ein Fund.

Und was die Bank selbst als verschieden gemeldet hat, gilt als geklaert:
Stammen alle Zeilen aus demselben Bankkonto und tragen verschiedene
Kennungen, verschweigt die Karte sie. Das sind die Revolut-Aufrundungen,
die echt sind.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-30 14:39:08 +02:00
GuidoandClaude Opus 5 335a7d55c8 feat: Gleich aussehende Buchungen stehen in der Arbeitsliste
Bisher fand man Dubletten nur zufaellig beim Durchscrollen der Buchungsliste –
oder ich habe ein Skript dafuer geschrieben, und der Nutzer musste sich auf
einen Server einloggen, um seine eigenen Zahlen zu reparieren. Beides ist
falsch.

Die Arbeitsliste zeigt sie jetzt: gleiches Konto, gleicher Tag, gleicher
Betrag, gleicher Text. Je Zeile steht die Kennung der Bank dabei, und die
entscheidet die Frage – dieselbe Kennung heisst sicher dieselbe Zahlung,
verschiedene heissen, die Bank hat sie als verschieden gemeldet.

Entschieden wird bewusst NICHTS von selbst. Drei Aufrundungen ueber 1.00 am
selben Tag sind echt, drei gleiche Einkaeufe koennen es auch sein; das weiss
nur der Nutzer. Certain meldet und laesst loeschen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-30 14:32:40 +02:00
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 4d3b9a8207 feat: Auch der Balken der Ausgaben-Anteile faerbt nach Budget
Der Balken IST die Ausgabe – er soll die Aussage mittragen, damit man sie
sieht, ohne die Zahl zu lesen. Gruen im geplanten Rahmen, rot darueber.

Ohne Plan behaelt er die Kategoriefarbe: Da gibt es nichts zu ueberschreiten,
und die Karte stuende sonst fast durchgehend auf einer Farbe, die nichts
bedeutet.

Der Punkt links bleibt in jedem Fall in der Kategoriefarbe – sonst liessen
sich die Zeilen nicht mehr auseinanderhalten.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-30 14:09:14 +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 1509695760 fix: Ein Umsatz unter neuer Kennung kam ein zweites Mal herein
Der Bankabruf nahm die ERSTE Kennung, die ein Umsatz mitbringt – meist die
Referenz der Bank. Vergibt die Bank sie beim Buchen neu (aus vorgemerkt wird
gebucht), ist es fuer Certain ein anderer Umsatz, und dieselbe Zahlung steht
zweimal da. Genau so sind zwei Mietbuchungen ueber 288 Euro am selben Tag
entstanden.

Zwei Aenderungen:

- Geprueft wird gegen ALLE Kennungen des Umsatzes, nicht nur gegen eine. Damit
  treffen auch Eintraege, die frueher unter einer anderen abgelegt wurden.
- Gespeichert wird die bestaendigste: die eigene Kennung der Schnittstelle
  bleibt gleich, wenn die Bank ihre Referenz austauscht.

Das ist eine von vier Stellen, an denen Certain Dubletten erkennt – neben dem
Import-Schluessel, dem unscharfen Eingangs-Abgleich und dem
Ueberschneidungsschutz. Diese vier gehoeren zusammengefuehrt; das ist ein
eigener Umbau und nicht Teil dieser Korrektur.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-30 13:51:50 +02:00
GuidoandClaude Opus 5 67a32bdda9 feat: Aufschluesselung zeigt, was der Stichtag ausschliesst
Buchungen bis einschliesslich des Stichtags zaehlen nicht in den Kontostand –
sie stecken schon im Anfangsbestand. In der Liste stehen sie aber, und in den
Auswertungen zaehlen sie mit. Das ist richtig so, war in der Oberflaeche aber
nirgends zu sehen: Der Unterschied zwischen dem, was man sieht, und dem, was
gerechnet wird, liess sich nicht erklaeren.

Ein Konto, dessen Stichtag nach dem Beginn des eingelesenen Auszugs liegt,
zeigt deshalb einen Kontostand, der zu den sichtbaren Buchungen nicht passt –
und niemand kaeme darauf, warum.

Die Rechnung nennt jetzt Stichtag, Anzahl und Summe der ausgeschlossenen
Buchungen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-30 13:41:22 +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 7fe2678192 feat: Ein verknuepftes Paar steht als EINE Zeile in der Buchungsliste
Ein Uebertrag ist eine Bewegung, keine zwei. In der Liste standen aber beide
Seiten untereinander, jede mit der Marke des Gegenkontos – man musste sie
selbst zusammensetzen, und es sah aus wie eine Dublette.

Liegen beide Seiten in der Liste, wird die Zugangs-Seite ausgeblendet und die
Abgangs-Seite zeigt "Revolut CHF → Sparen Pocket" – so wie ein echter
Uebertrag. Ueber zwei Waehrungen stehen beide Betraege untereinander
("CHF 400.00" / "→ 425,32 €"); nebeneinander passen sie nicht in die Spalte.

In der Datenbank aendert sich nichts: Beide Buchungen bleiben stehen, mit ihren
Import-Schluesseln und ihrer Stapel-Zugehoerigkeit. Das ist die tragende
Eigenschaft der Verknuepfung, und sie bleibt.

Ist nur eine Seite geladen – etwa weil nach einem Konto gefiltert wird –,
bleibt sie einzeln stehen und traegt weiter die Marke mit dem Gegenkonto.
Sonst verschwaende dort eine Buchung aus der Liste.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-30 13:18:22 +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 7b3ed3e63c fix: Zweiter Uebertrag fuer dieselbe Bewegung wurde nicht erkannt
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>
2026-08-30 12:58:37 +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 a159546218 feat: Rueckkehr von der Bank sagt, was ankam und was liegen blieb
"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>
2026-08-27 21:12:45 +02:00
GuidoandClaude Opus 5 4c2e72cb20 fix: Bankanbindung gliedern – und ein Platzhalter, der nicht ersetzt wurde
Im Kopf jeder Verbindung stand woertlich "zuletzt {{when}}": Der Text erwartet
`when`, uebergeben wurde `datum`.

Die Gliederung lieh sich die Klassen des Postfachs – gestrichelte Trennlinien,
sonst nichts. Damit war weder zu sehen, wo eine Bank aufhoert, noch welcher
Knopf zu welchem Konto gehoert, und das Suchfeld fuer eine neue Bank
verschwamm mit den Angaben der schon verbundenen.

Jetzt: Jede Verbindung ist ein umrandeter Block mit Kopfzeile. Jedes Konto
darin hat eine eigene linke Kante – die bindet seine Knoepfe sichtbar an es.
Die Knoepfe der VERBINDUNG (alle Konten, Salden, trennen) stehen ohne
Einrueckung hinter einer Trennlinie, gehoeren also erkennbar zur Bank und
nicht zum letzten Konto. "Weitere Bank verbinden" ist mit eigener Ueberschrift
abgesetzt.

Nebenbei brach "Minuten (0 = aus)" neben dem Zahlenfeld um und las sich wie
ein Fehler; die Klammer steht ohnehin im Hinweis darunter.

Im Browser nachgemessen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-27 21:04:32 +02:00
GuidoandClaude Opus 5 385352974c fix: Abruf ueber schon eingelesene Monate buchte alles doppelt
"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>
2026-08-27 20:55:51 +02:00
GuidoandClaude Opus 5 12ea391e9f fix: Startdatum lag in der Zukunft – Saldi kamen, Umsaetze nicht
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>
2026-08-27 20:48:03 +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 1b47e5abe1 fix: Ohne Anmeldung liessen sich beliebige Dateien vom Server lesen
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>
2026-08-27 15:07:42 +02:00
GuidoandClaude Opus 5 d88967d77d fix: Budget-Monat war bei Starttag 29 bis 31 nicht schluessig
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>
2026-08-27 14:37:32 +02:00
GuidoandClaude Opus 5 e2428bbabd fix: Herkunft als Pille im vorhandenen Badge-Stil
Der Kasten in Titelgroesse war zu laut fuer eine Angabe, die nur eine
Rueckfrage beantwortet. Statt einen zweiten Stil zu erfinden, nimmt sie jetzt
".badge" – dieselbe Pille wie ueberall sonst im Projekt: abgerundet, weich
gefuellt, 11 statt 14 Punkt.

In der Datei steht nur noch, wie sie sich in der Titelzeile verhaelt: nicht
schrumpfen, nicht umbrechen. Eng wird zuerst der Titel, denn ein halbes
"Datei-Imp…" nuetzt niemandem.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-27 13:44:35 +02:00
GuidoandClaude Opus 5 d6ae963241 fix: Herkunft steht neben dem Titel, nicht kleingedruckt darunter
Die zweite Zeile gehoert dem Verwendungszweck. Alles Neue dorthin zu stapeln
macht die Zeilen hoch und den Blick eng – und das Kennzeichen war zusaetzlich
kleiner gesetzt als alles andere.

Jetzt steht es neben dem Titel und in dessen Groesse. Wird es eng, kuerzt der
Titel und das Kennzeichen bleibt lesbar; die Mindestbreite der Spalte steigt
dafuer von 150 auf 190 Punkte. Die Tabelle passt damit weiterhin ohne
waagerechtes Scrollen in ein Fenster von 870 Punkten.

Im Browser an beiden Enden nachgemessen: bei voller Breite genug Platz fuer
beides, bei Mindestbreite kuerzt der richtige Teil.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-27 13:33:55 +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 1084cf7c17 feat: Postfach zeigt Takt und Abrufzeitpunkt in der Uebersicht
Der Takt des automatischen Abrufs gab es laengst – aber nur im
Bearbeiten-Formular. In der Uebersicht war deshalb nicht zu sehen, ob
ueberhaupt automatisch abgerufen wird, und es sah aus, als gaebe es die
Einstellung nicht. Bei der Bankanbindung steht sie direkt in der Karte; jetzt
auch hier: "alle 15 Minuten" oder "nur auf Knopfdruck".

Und "zuletzt abgerufen" stand ohne Zeitpunkt da, sagte also nichts. Jetzt mit
Datum und Uhrzeit – die Uhrzeit ist noetig, weil der Abruf meist am selben Tag
lief.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-27 13:06:00 +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 ca63fef5c4 feat: Doppelte Uebertraege reparieren, nicht nur melden
Das Pruefskript liess die Arbeit beim Nutzer, obwohl die Entscheidung aus der
Datenbank folgt. Das Reparaturskript trifft sie und fuehrt sie aus:

  Beide Konten angebunden   Die Bank liefert beide Seiten selbst. Der Uebertrag
                            wird in die Buchung zurueckverwandelt, aus der er
                            entstand – WELCHE das war, sagt der Eingangs-
                            Eintrag, der auf ihn zeigt. Danach stehen beide
                            Seiten wieder da und lassen sich verbinden.

  Gegenkonto nicht abgerufen  Der Uebertrag ist richtig, die zusaetzliche
                            Buchung ueberzaehlig und wird geloescht. Ihr
                            Eingangs-Eintrag wird verworfen statt geloescht,
                            sonst braechte ihn der naechste Abruf wieder.

Ohne --wirklich passiert nichts, es wird nur gezeigt. Mit --wirklich legt es
zuerst eine Kopie der Datenbank an, ueber die SQLite-Sicherung und nicht als
Dateikopie – sonst fehlte das WAL.

Uebergangen statt geraten wird alles Uneindeutige: von Hand angelegte
Uebertraege, solche ueber zwei Waehrungen, nur einseitig angebundene Paare und
Uebertraege mit Dubletten auf BEIDEN Seiten.

An nachgestellten Daten geprueft: Aus Lohnkonto -1'575 / Revolut 2'250 / Pocket
450 wird -1'575 / 1'125 / 450 – also genau das, was tatsaechlich passiert ist.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-27 11:52:36 +02:00
GuidoandClaude Opus 5 0288b2be9f feat: Pruefskript sagt, WELCHE der beiden Zeilen weg muss
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>
2026-08-27 11:40:38 +02:00
GuidoandClaude Opus 5 0cd0aab90d feat: Doppelt gebuchte Uebertraege auffindbar machen
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>
2026-08-27 11:28:12 +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 8cb82da63c fix: Uebertrag buchen tat sichtbar nichts – useToast falsch ausgepackt
useToast() liefert die Melde-Funktion selbst, kein Objekt. In drei Dateien aus
dieser Sitzung stand "const { toast } = useToast()", also undefined. Der Aufruf
steht fast immer in einem catch – dort verschluckt der TypeError die
eigentliche Meldung, und nach aussen passiert gar nichts.

Das erklaert beide Beschwerden:

- "Als Uebertrag buchen" schien wirkungslos: Die Buchung lief durch, dann warf
  toast(), der catch warf beim Melden erneut, der Dialog blieb offen.
- "Braucht ein Reload": Im Eingang stand toast() VOR laden() – der Dialog
  schloss noch, das Neuladen kam nie. Die Cache-Kopfzeile, die ich dafuer
  gesetzt habe, war nicht die Ursache; richtig ist sie trotzdem.

Am Ende in der laufenden Oberflaeche nachgeklickt: Dialog schliesst, Meldung
erscheint, die Zeile wird ohne Neuladen zum Uebertrag, die Gegenseite liegt auf
dem Zielkonto, beide Salden stimmen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-27 11:15:43 +02:00
GuidoandClaude Opus 5 64b32cf8b8 fix: Tabelle passt wieder, und Uebertraege gehen auch aus der Buchungsliste
Die neue Kategoriespalte hatte 230 px Mindestbreite hinzugefuegt. Damit war die
Tabelle in einem schmalen Fenster breiter als ihre Karte und scrollte waagerecht
- von aussen sah das aus, als waere sie abgeschnitten. Alle Spalten sind jetzt
enger bemessen; bei 900 px Fensterbreite passt sie ohne Scrollen.

Ausserdem liess sich eine Buchung nur aus der Arbeitsliste zum Uebertrag
machen, nicht aus der Buchungsliste. Dort steht der Knopf jetzt auch - und zwar
statt des Loesen-Knopfes, denn beides zugleich gibt es nie: Eine verknuepfte
Buchung IST schon ein Paar.

Der Dialog dafuer ist eine eigene Komponente geworden. Die Nachfrage bei einem
geschaetzten Betrag - 409 abfangen, begruenden lassen, mit 'trotzdem' erneut
senden - ist zu leicht falsch nachgebaut, und jetzt gibt es sie nur einmal.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-27 11:01:47 +02:00