Zwei Luecken geschlossen:
Mail-Vorschlaege standen als "pending" im Eingang, aber es gab keinen Knopf
zum Buchen – der Weg war hinten offen. Jetzt je Eintrag "Buchen" und ein
"Alle buchen". Unklare bleiben liegen: Bei ihnen fehlt etwas oder die
Erkennung war unsicher, die soll man ansehen statt im Stapel durchzuwinken.
Der Auszug vom Telefon bucht nicht mehr sofort, sondern wird nur abgelegt.
Am Telefon prueft man keine 500 Zeilen; die Datei wartet in der
Import-Ansicht und laeuft dort durch die normale Vorschau mit
Saldo-Abgleich, Zeilenfilter und Duplikatpruefung.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Revolut und Raiffeisen lassen die CSV auch am Handy exportieren; nur N26
verlangt die Web-Version. Ein zweiter Kurzbefehl nimmt die Datei aus dem
Teilen-Menue entgegen und schickt sie mit dem gewaehlten Konto an
/quick/statement.
Am Telefon laesst sich keine Spaltenzuordnung vornehmen. Der Endpunkt
verwendet deshalb die Einstellung, mit der dieses Konto zuletzt erfolgreich
eingelesen wurde – und sagt klar Bescheid, wenn es noch keine gibt, statt
mit falschen Spalten zu raten.
Gebucht wird sofort: Ein Auszug hat hunderte Zeilen, die einzeln abzunicken
waere keine Erleichterung. Sicher ist das durch die Duplikaterkennung und
weil der Stapel im Import-Verlauf mit einem Klick zurueckzunehmen ist.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Beim Abrufen und beim Bearbeiten sprang die halbe Seite. Ursache war der
CSS-Spaltenfluss: Aendert eine Karte ihre Hoehe, verteilt der Browser ALLE
Karten neu auf die Spalten. Ich hatte das zweimal am Symptom kuriert
(Formular in den Dialog verschoben), aber die naechste wachsende Karte
brachte es zurueck.
Jetzt bekommt jede Karte fest ihre Spalte. Waechst eine, verschiebt sich
nur, was in derselben Spalte darunter steht. Die Spaltenzahl kommt aus der
tatsaechlichen Containerbreite (ResizeObserver), nicht aus der
Fensterbreite – in der schmalen Seitenleisten-Ansicht stimmte das sonst
nicht.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Revolut verschickt fuer eine Zahlung in Euro und eine in Franken dieselbe
Mail vom selben Absender. Ohne die Waehrung als Kriterium landete alles auf
demselben Konto.
Die Erkennung liest die Waehrung ohnehin aus; sie wird jetzt VOR der
Zuordnung ausgewertet und ist ein optionales Filterkriterium. Zuordnungen
mit Waehrung werden zuerst geprueft – wer neben ein allgemeines "revolut"
ein genaues "revolut + EUR" stellt, meint das genaue, unabhaengig von der
Anlagereihenfolge.
Uebergangene Mails melden ausserdem die erkannte Waehrung mit, damit beim
Einrichten sichtbar ist, welcher Wert zu filtern waere.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der Loesch-Endpunkt war da, aber ohne Knopf in der Oberflaeche, und
Bearbeiten fehlte ganz – eine falsch eingetippte Zuordnung liess sich also
nicht mehr korrigieren.
Jede Zuordnung ist jetzt eine eigene Zeile mit Erkennungstext und Konto;
der Speichern-Knopf erscheint erst bei einer Aenderung. Dazu ein PUT auf
/mail/senders/{id} im Backend.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Bei einer von Hand weitergeleiteten Bank-Mail steht die eigene Adresse im
From; "Raiffeisen" findet sich nur noch im Betreff ("WG: Raiffeisen …")
und im zitierten Kopf. Das Feld hiess "Absender enthaelt" und prueft nur die
Absender-Koepfe – die Mail wurde also uebergangen, obwohl das Suchwort
sichtbar darin stand. Eine Falle, kein Bedienfehler.
Gesucht wird jetzt in Absender-Koepfen, Betreff und den ersten 2000 Zeichen
des Textes; die Beschriftung heisst entsprechend "Erkennungstext". Ein Test
mit einer nachgebauten Weiterleitung haelt fest, dass sowohl die Zuordnung
greift als auch Betrag und Empfaenger daraus lesbar bleiben.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Auf UNSEEN zu filtern klingt sparsam, ist aber eine Falle: Wer eine Mail in
seinem Mailprogramm ansieht, bevor Certain sie geholt hat, verliert sie
dauerhaft – sie ist dann fuer immer gelesen. Genau das ist passiert, als die
Mails vor dem ersten Abruf schon in Gmail geoeffnet waren: Ergebnis "0 neu",
ohne jeden Hinweis auf die Ursache.
Gesucht wird jetzt ab dem letzten Abruf mit einem Tag Ueberlappung; dass
nichts doppelt ankommt, sichert die Message-ID ab.
Die Meldung nennt ausserdem, wie viele Mails ueberhaupt geprueft wurden.
"0 neu" allein liess offen, ob nichts da war oder nichts passte.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Wer Bank-Mails in ein Sammelpostfach weiterleitet, hat im From je nach
Methode die eigene Adresse stehen. Geprueft werden deshalb auch Reply-To,
Return-Path, Sender und X-Forwarded-For – bei automatischer Weiterleitung
bleibt der Originalabsender dort erhalten.
Ausserdem meldet der Abruf jetzt, welche Mails er uebergangen hat, mit
Absender und Betreff. Beim Einrichten war das sonst Raten: Man sah nur
"3 uebersprungen" und wusste nicht, was in den Filter gehoert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Drei zusammenhaengende Aergernisse:
Der Papierkorb wurde zum roten Balken ueber die volle Breite. Ursache ist
.btn-pair: dort bekommt jeder Knopf flex:1, und ein umgebrochener Knopf
dehnt sich damit ueber die ganze Zeile. Die Aktionen haben jetzt eine
eigene Zeile, die umbricht ohne zu dehnen.
Das Formular wuchs innerhalb der Karte und ordnete damit den Spaltenfluss
der ganzen Einstellungsseite neu – deshalb sprang beim Bearbeiten das
Layout. Es liegt jetzt in einem Dialog, wie schon bei den Konten. Das
loest zugleich die Enge: im Dialog hat das Formular Platz.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Verschluesselung: Bisher gab es nur den Schalter use_ssl – also implizites
TLS auf 993 oder gar nichts. STARTTLS auf 143 fehlte ganz, und das bieten
viele Anbieter an. Jetzt ein Feld mit drei Werten, der Port zieht bei der
Auswahl mit, und "keine" warnt, dass Zugangsdaten im Klartext gehen.
Layout: Die Postfach-Karte steht in einer schmalen Spalte; der waagerechte
Aufbau brach dort auf ein Wort je Zeile um und die Knoepfe lagen ueber dem
Titel. Jetzt gestapelt.
Die Einstellungsseite nutzte ein Raster, das die Karten zeilenweise
ausrichtet – die zweite Reihe begann erst unter der hoechsten Karte der
ersten, weshalb "Backup zurueckladen" allein weit unten stand. Jetzt
fliessen die Karten spaltenweise, und die Spalten sind breiter: vier
schmale Karten nebeneinander waren auf dem Notebook nicht lesbar.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
"Text erfragen" war der uebersetzte englische Name, nicht der, der in
Kurzbefehle steht. Die Aktion heisst "Eingabe anfordern". Da sich die
Bezeichnungen je nach iOS-Version aendern, nennt die Anleitung jetzt den
Suchbegriff und den englischen Namen dazu.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Server, Port, Benutzer und Passwort eintragen, Verbindung testen, Ordner
danach aus der echten Liste des Postfachs waehlen. Je Absender wird ein
Zielkonto zugeordnet – ohne Zuordnung uebernimmt Certain nichts, denn
dieselbe Adresse kann je nach Konto etwas anderes bedeuten.
Der Hinweis am Passwortfeld sagt, was passiert, wenn das Server-Geheimnis
getauscht wird, und empfiehlt ein App-Passwort statt des echten.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Karte steht in einer schmalen Spalte; die waagerechte Zeile quetschte
Beschriftung und Feld ineinander, sodass "Name des Geraets" ueber
"Standardkonto" lag. Jetzt gestapelt wie die uebrigen Formulare der Seite.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- "Mein Konto": Geraetemarken anlegen und widerrufen. Der Klartext ist
nur einmal zu sehen, daneben steht eine Bauanleitung fuer den
Kurzbefehl mit kopierfertiger Adresse und Kopfzeile. Ein fertiger
Download geht nicht: seit iOS 15 muessen Kurzbefehl-Dateien signiert
sein, und signieren kann nur ein Mac mit Apple-Konto.
- Neue Seite "Eingang": was unterwegs erfasst wurde, mit
Kategorie-Auswahl und Verwerfen.
- Die Import-Pruefliste unterscheidet jetzt, WARUM eine Zeile
uebersprungen wird: "schon erfasst" mit Erklaerung statt nur
"Duplikat".
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
body hat user-select:none mit einer Ausnahmeliste; die Buchungsliste steht
als Tabelle darin, die Offen-Seite mit ihren Listenzeilen aber nicht. Damit
liess sich der Empfaengertext nicht markieren – ausgerechnet dort, wo man
ihn nachschlagen will, um zu erkennen, welches Franchise dahintersteckt.
.named-text ist jetzt markierbar, und je Zeile steht ein Kopierknopf.
Ausserdem wird der Verwendungszweck mit angezeigt, wenn er vom Empfaenger
abweicht – oft steht die eigentliche Kennung dort.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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>
Das native <select> zeigte nur den Namen im Systemstil. Bei einem Dutzend
Konten erkennt man seines am Symbol deutlich schneller – und beim Import
sieht man sofort, ob man in das gemeinte Konto liest.
Neue Komponente AccountSelect nach dem Muster von CategorySelect, mit
denselben Klassen und per Portal gerendert. Eingesetzt beim Import, in
Filter und Formular der Buchungen sowie beim Zielkonto eines Uebertrags.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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>
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>
Zwei Klick-Aergernisse mit derselben Ursache – ein zu grob gefasster
Schliess-Ausloeser.
Die Kategorieliste schloss bei jedem Scroll-Ereignis, wegen capture=true
auch beim Scrollen IN der Liste. Genau das will man aber tun. Jetzt zaehlt
nur noch Scrollen ausserhalb.
Der Dialog schloss, sobald ein Klick auf dem Hintergrund endete. Markiert
man Text in einem Feld und zieht ueber den Rand hinaus, liegt das Mouseup
dort – der Dialog ging mitten im Markieren zu. Jetzt muss der Klick auch
auf dem Hintergrund begonnen haben.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Aktionsspalte im Verlauf ist fest und war zu schmal fuer den
beschrifteten Knopf. Ausserdem sammeln sich nach einer Testreihe schnell
zwanzig rueckgaengig gemachte Eintraege – die einzeln wegzuklicken ist
Fleissarbeit. Neben der Ueberschrift steht jetzt ein Knopf, der alle auf
einmal entfernt; einzeln geht weiterhin.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Gross stand die Kategorie, klein der Zweck – gesucht wird aber nach dem
Zweck. Jetzt oben "Coop Pratteln", darunter klein "Lebensmittel"; das
Symbol trug die Kategorie ohnehin schon.
Ausserdem war der Knopf zum Entfernen eines rueckgaengig gemachten
Imports ein unbeschriftetes Papierkorb-Symbol und damit unauffindbar.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der Filter steckte in einem zugeklappten Bereich und der Wert musste
blind getippt werden – entsprechend importierte man den Revolut-Auszug
mit allen Produkten in ein Konto, was den Saldo-Abgleich unmoeglich
macht.
Die Rohvorschau liefert jetzt je Spalte die wiederkehrenden Werte samt
Zeilenzahl. Hat eine Datei so eine Spalte, steht der Filter offen sichtbar
mit der Ueberschrift "Diese Datei enthaelt mehrere Konten", und der Wert
wird aus einer Liste gewaehlt ("Giro (530 Zeilen)").
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Meldung nannte nur Zahlen ("Laut Datei / errechnet / Differenz") und
sagte nicht, was zu tun ist. Sie beschreibt jetzt in Worten, was nicht
aufgeht und ab welcher Zeile beide auseinanderlaufen.
Dazu ein Hinweis auf die haeufigste Ursache, wenn kein Zeilenfilter
gesetzt ist: Revolut legt Giro, Erspartes und Anlagekonto in EINE Datei,
jedes mit eigener Kontostand-Spalte. Ohne Filter springt die Spalte
zwischen den Produkten und der Abgleich kann gar nicht aufgehen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der Erkennungs-Text eines Kontos wurde als blosser Teilstring gesucht.
Am EUR-Konto stand „Euro" – der steckt auch in „PayPal Europe",
„Europa-Park" und „EUROBUS". Jede solche Zahlung wurde dadurch zum
Uebertrag auf dieses Konto, das Geld dort zusaetzlich gutgeschrieben und
der Saldo lief um mehr als tausend Euro daneben.
Gesucht wird jetzt nur an Wortgrenzen; eine Kartenendung wie „6543"
schlaegt damit auch nicht mehr mitten in einer laengeren Kontonummer an.
Das Kontoformular warnt zusaetzlich bei sehr kurzen Erkennungstexten.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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>
Kategorien erben ihre Art von der Gruppe, und die ist entweder Ausgabe oder
Einnahme. Für einen Kontoübertrag passt beides nicht: Auf dem einen Konto geht
Geld raus, auf dem anderen kommt es rein – man musste also zwei Kategorien in
zwei Gruppen anlegen.
Als „nicht in Auswertungen einrechnen" markierte Kategorien werden jetzt in
beiden Richtungen angeboten (Buchungsformular und Import-Prüfansicht), und ihre
Schlagwörter greifen unabhängig vom Vorzeichen. Eine Kategorie „Kontoübertrag"
in einer Ausgabe-Gruppe genügt damit für beide Seiten.
Test für beide Richtungen; 94 grün.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
Die Achse richtete sich rein nach den Daten: Beschriftungen wie „-3'065.06" sind
nicht ablesbar, und ohne feste Null sah man die Bewegung, aber nicht, ob man im
Plus oder Minus steht.
- Die Skala schließt die Null immer ein und ist optisch hervorgehoben
- Runde Schritte (1, 2, 2.5, 5 x Zehnerpotenz): 0 / 2'000 / 4'000 … statt
krummer Werte; nie feiner als ein Rappen, sonst entstünden bei leerem Konto
Zwischenwerte wie 0,0025
- Vermögensverlauf: Zeitraum als Karten-Option (30 T / 90 T / 6 M / 1 J)
Gilt für jedes Liniendiagramm, nicht nur den Vermögensverlauf.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Neue Karten-Option „Darstellung": Liste (Standard) oder Kreis. Dieselben Zahlen,
zwei Lesarten – die Liste nennt jeden Anteil genau, der Kreis zeigt die
Verteilung auf einen Blick. Nutzt den vorhandenen Donut, dessen Legende Betrag
und Anteil ohnehin schon ausweist.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
- 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>
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>
Bisher musste die Regel auf BEIDEN Auszügen ein fremdes Gegenkonto liefern. Beim
Empfangs-Auszug zeigt sie aber auf das Konto, das gerade importiert wird – damit
blieb die Zeile ohne Gegenkonto und wurde still als Einnahme gebucht.
- Gegenbuchungen werden über die Konto-Seite bestehender Transfers gesucht, nicht
über das Konto-Paar. Der Empfangs-Auszug braucht damit keine Regel mehr; eine
Regel auf der abgehenden Seite genügt für beide Dateien.
- Trifft eine Transfer-Regel, ohne dass ein Gegenkonto feststeht, wird die Zeile
nicht mehr als Einnahme/Ausgabe gebucht, sondern in der Prüfansicht mit
„Gegenkonto wählen" markiert – sonst zählt die Bewegung je nach Import-
Reihenfolge doppelt.
- Tests für beide Reihenfolgen; 86 grün.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Der Revolut-Export führt "2026-01-06 23:11:14" in der Datumsspalte. Wählte man
im Dropdown JJJJ-MM-TT, scheiterte das Parsen an der Uhrzeit – jede Zeile hatte
kein Datum und galt als ungültig ("137 Zeilen erkannt, 0 werden gebucht"),
obwohl die Auswahl richtig aussah.
- parse_date schneidet eine überzählige Uhrzeit ab und kommt auch damit klar,
wenn das Format eine Uhrzeit erwartet, der Wert aber keine hat (ISO-T ebenso)
- Dropdown um die Varianten mit Uhrzeit ergänzt, damit die Bank-Vorlagen ihren
echten Eintrag zeigen statt eines leeren Feldes
- Test: reines Datumsformat auf Datei mit Uhrzeit -> alle Zeilen erkannt, 84 grün
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Die Money-Komponente gab den Betrag immer durch Math.abs() aus; das Vorzeichen
kam nur über das optionale sign-Prop. Kontostände (Konten-Seite und Dashboard-
Karte) übergeben keines – ein überzogenes Konto oder eine Kreditkartenschuld sah
dadurch positiv aus, während die Gesamtsumme korrekt negativ rechnete. Genau
dieser Widerspruch: alle Konten scheinbar im Plus, Gesamt im Minus.
Ohne sign steht der Wert für sich selbst und wird jetzt mit echtem Vorzeichen
(und rot) ausgegeben; mit sign bleibt es wie bisher, weil dort die Richtung aus
der Buchungsart kommt.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
Das Revolut-Preset kannte die Spalten Gebühr/Währung/Status laut Kommentar,
der Parser hat sie aber nie gelesen. Folgen: Gebühren fehlten im Saldo,
stornierte (REVERTED) und schwebende Zeilen wurden mitgebucht, und Zeilen
fremder Währungs-Pockets landeten 1:1 im Konto – der Saldo lief dadurch zu hoch.
- Gebührenspalte mindert den Betrag (immer als Kosten)
- nur Zeilen mit Status COMPLETED (oder ohne Statusspalte) sind buchbar
- Zeilen in anderer Währung als das Zielkonto werden nicht gebucht
- Grund wird in der Prüfansicht als Badge angezeigt (storniert / Währung)
- Gebühr/Währung/Status in der Spaltenzuordnung wählbar; Revolut-Preset ergänzt
- Test mit echtem Revolut-Format (Gebühr, Storno, Fremdwährung), 80 grün
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Enthält der Buchungstext den Erkennungs-Text (Art „Text") eines ANDEREN
Kontos, wird auch ein Eingang als Transfer VON diesem Konto gesetzt – nicht
nur die Ausgabe. Richtung ergibt sich aus dem Vorzeichen.
- Gegenbuchungs-Erkennung: existiert schon ein Transfer zwischen denselben
Konten mit passendem Betrag (±5 Tage), wird die Gegenseite als „Gegenbuchung"
markiert und nicht angehakt – so zählt dieselbe Bewegung beim Import beider
Auszüge nicht doppelt (überschreibbar in der Prüfansicht).
- Tests: Eingang übers Konto-Tag, Gegenbuchung übersprungen (78 grün)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- 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>
- Neue Seite „Mein Konto" (/konto, für alle Nutzer): eigenes Passwort ändern
(inkl. Richtlinien-Anzeige) + Zwei-Faktor verwalten; erreichbar über den
Nutzer-Chip in der Seitenleisten-Fußzeile
- Einstellungen: 2FA-Karte entfernt (liegt jetzt bei „Mein Konto"), Karten in
ein mehrspaltiges Raster gruppiert
- Menüpunkt „Einstellungen" unter die Überschrift „System" verschoben (für alle
Nutzer sichtbar; „Benutzer" bleibt Admin-only)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Bank-Vorlagen und eigene Vorlagen in EINEM Dropdown (optgroups, typ-getaggte
Werte preset:/tpl:)
- Speichern überschreibt bei gleichem Namen (Rückfrage); geladene Vorlage belegt
den Namen vor
- Spaltenvorschau wird automatisch erzeugt (kein „Datei einlesen"-Klick mehr)
- Gegen-IBAN in „Weitere Spalten (optional)" verschoben + erklärt; Grundzuordnung
zeigt nur Datum/Betrag/Empfänger/Zweck
- Beleg-Modus zeigt die hochgeladenen PDF-Seiten als Bild + extrahierten Text
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- 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>
Der Schlagwort-Knopf sah bei jeder Kategorie gleich aus - man musste jede
einzeln oeffnen, um zu sehen, wo ueberhaupt etwas hinterlegt ist. Belegte
Kategorien tragen den Knopf jetzt in Akzentfarbe und mit der Anzahl daneben,
der Tooltip nennt sie ausgeschrieben.
Die Zahl kommt als keyword_count an der Kategorie mit, ermittelt in einer
Aggregat-Abfrage fuer die ganze Liste. Ein Abruf je Kategorie waere dafuer
unverhaeltnismaessig gewesen. Gefuellt wird das Feld nur beim Auflisten;
beim Anlegen und Aendern einer Gruppe bleibt es bei 0, wo es ohnehin nichts
aussagt.
Das Popup laedt beim Schliessen die Liste neu, damit die Zahl nach dem
Bearbeiten stimmt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
In der Passwortrichtlinie stand das Haekchen mittig und der Text ganz rechts.
Ursache war die globale Regel "input, select { width: 100% }": sie erwischt
auch Ankreuzfelder und zieht sie ueber die volle Breite. Vier Stellen im
Code umgingen das bereits einzeln (.checklist, .dt-pop-opt, .checkline,
.datatable td) - eine fuenfte Umgehung waere die falsche Antwort gewesen.
Ankreuz- und Auswahlfelder sind jetzt zentral ausgenommen, die vier
Einzelfaelle entfallen. Nebeneffekt: sie tragen ueberall dieselbe
Akzentfarbe (bisher nur an zwei Stellen) und sitzen in Tabellenzellen
mittig, statt durch das geerbte margin-top um 5px nach unten gedrueckt zu
werden.
Die Karte selbst nutzt jetzt .checkline, das vorhandene Muster fuer
"Ankreuzfeld plus Text"; .field-inline richtet an der Grundlinie aus und
war hier von vornherein das falsche.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Bisher oeffnete der Doppelklick ein Eingabefeld im Chip selbst. Dort war nur
Platz fuer das Wort - der Betrag liess sich gar nicht aendern, weil die
Inline-Bearbeitung bewusst nur { word } geschickt hat.
Jetzt laedt der Doppelklick Wort und Betrag in dieselbe Zeile, in der auch
neue Schlagwoerter entstehen. Der Knopf wechselt auf ein Haekchen, daneben
steht Abbrechen (auch per Escape), und der bearbeitete Chip ist markiert.
Gespeichert wird per PATCH mit ausdruecklichem amount_cents, damit ein
geleertes Betragsfeld den Betrag auch wirklich loescht; ein geparstes 0 wird
zu null, weil das Schema gt=0 verlangt. Ueber POST ginge es nicht: das
antwortet bei vorhandenem Wort mit geaendertem Betrag mit 409.
Tests decken jetzt ab, dass Betrag mitgeaendert wird, beim Weglassen stehen
bleibt und bei ausdruecklichem null verschwindet.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Im Downloads-Ordner lagen "certain-finance-export_....json" und
"certain-finance-backup_....json" nebeneinander, ohne dass man sah, welche
Datei sich zurueckladen laesst. Alle Downloads heissen jetzt
certain-finance_<art>_<datum>_<uhrzeit>.<endung>, wobei die Art es sagt:
vollbackup, vollbackup-system, datenuebersicht, buchungen, datenbank. Das
persoenliche Vollbackup traegt zusaetzlich den Benutzernamen.
Den Namen vergibt jetzt allein der Server im Content-Disposition-Kopf; das
Frontend liest ihn von dort. Vorher erfanden beide Seiten ihn getrennt und
kamen auf unterschiedliche Ergebnisse (Bindestrich gegen Unterstrich, UTC
gegen Ortszeit). Zeitstempel ist Ortszeit: wer abends um 20 Uhr exportiert,
soll keine Datei von 18 Uhr finden.
Passend dazu heisst der Knopf "Alle Daten (JSON)" nun "Datenuebersicht
(JSON)", und der Hinweis darueber sagt ausdruecklich, dass sich diese
Dateien nicht zurueckladen lassen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Zwei-Faktor per TOTP mit zehn Einmal-Wiederherstellungscodes. Der
Zwischenzustand "Passwort stimmt, Code fehlt" haengt an der vorhandenen
Sitzungstabelle (pending_2fa, zehn Minuten Laufzeit) und gilt nirgends als
angemeldet; nach dem Code wird das Cookie getauscht statt weiterverwendet.
Der Wiederverwendungsschutz merkt sich das tatsaechlich getroffene
Zeitfenster, nicht die aktuelle Uhrzeit - sonst liesse sich ein Code aus dem
naechsten Fenster gleich darauf nochmal einloesen.
Passwortrichtlinie (Laenge, Zeichenklassen, MFA-Pflicht) in der neuen
globalen Tabelle app_settings, durchgesetzt beim Anlegen, Zuruecksetzen und
Aendern von Passwoertern. Bestehende Passwoerter lassen sich nicht gegen eine
neue Richtlinie pruefen - der Hash gibt nichts her. Beim Anmelden liegt das
Passwort aber kurz im Klartext vor: dort wird geprueft und bei Verstoss ein
Wechsel verlangt, mit passendem Grund fuer den Hinweistext.
Nebenbei geschlossen: must_change_password war bisher nur im Frontend
erzwungen, wer die API direkt ansprach kam vorbei. enforce_account_state
haengt jetzt am /api-Sammelrouter und laesst nur noch durch, was man zum
Erfuellen der Pflicht braucht.
Das JSON-Backup liess sich bisher grundsaetzlich nicht zurueckladen: keine
Version, Kategorien nur ueber mehrdeutige Namen, sieben Tabellen fehlten.
Daneben steht nun ein Format v2, das Beziehungen ueber lokale IDs abbildet,
je Benutzer oder systemweit. Zurueckgeladen wird nach einer Pruefansicht,
wahlweise ersetzend oder ergaenzend; vor dem Ersetzen legt der Server einen
Schnappschuss der Datenbank an. Der alte Ansichts-Export bleibt fuer
Tabellenkalkulationen bestehen und wird beim Hochladen ausdruecklich
abgewiesen, statt halb zu raten.
Frontend: wiederverwendbares Passwortfeld mit Augensymbol, animierte
Anforderungsliste, 2FA-Seite, Admin-Karte fuer die Richtlinie und Karte zum
Zurueckladen. Neue Bewegung ist in prefers-reduced-motion hinterlegt; Secret
und Codes sind markierbar, sonst verhinderte das globale user-select das
Kopieren.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>