Commit Graph
189 Commits
Author SHA1 Message Date
GuidoandClaude Opus 5 7fb7df3554 feat: Zahlungsdaten aus Benachrichtigungs-Mails lesen (Etappe B, Teil 1)
Kein festes Schema je Bank, sondern beschriftete Felder suchen. Die beiden
Banken bauen ihre Mails verschieden: Raiffeisen schickt Klartext mit
"Betrag: 14.90 CHF", Revolut nur HTML mit dem Wert UNTER der Beschriftung.
Beide Bauformen werden abgedeckt, dazu der Betreff als eigene Quelle.

Jeder Wert merkt sich, woher er stammt – ohne das laesst sich ein falscher
Betrag spaeter nicht erklaeren.

Was bewusst NICHT geraten wird: Stehen mehrere gleichwertige Betraege ohne
Beschriftung da (Umsatz und neuer Saldo), bleibt das Feld unklar. Ein
Zahlungsauftrag mit Status "Zur Visierung" gilt nicht als geflossen. Blosse
Zahlen ohne Waehrung sind keine Betraege, und Stilbloecke werden vor dem
Entfernen der Tags herausgeschnitten – sonst haelt das CSS "480px" fuer
Geld.

Die Muster unter tests/fixtures/mail sind echte Mails, anonymisiert.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 19:46:31 +02:00
GuidoandClaude Opus 5 bf656dc957 feat: Oberflaeche fuer die Schnellerfassung
- "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>
2026-08-22 19:39:31 +02:00
GuidoandClaude Opus 5 0d7f6a8d79 fix: Schlagwort-Test kollidierte mit anderen Tests
Die Testdatenbank lebt ueber den ganzen Lauf und Schlagwoerter sind je
Nutzer eindeutig; "coop" legt bereits ein anderer Test an. Der Test lief
allein durch und fiel im Verbund um. Jetzt mit einem zufaelligen Wort.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 19:35:46 +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 9d6a53a224 test: Duplikaterkennung ueber Monatsgrenzen abgesichert
Der uebliche Ablauf ist, Mitte Monat einzulesen und Ende Monat den ganzen
Monat noch einmal. Die Tests halten fest, dass bereits gebuchte Zeilen
nicht erneut in den Saldo laufen, neue ankommen – und dass zwei bzw. drei
echte gleich aussehende Buchungen desselben Tages erhalten bleiben.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 18:56:43 +02:00
GuidoandClaude Opus 5 18368e6e93 feat: Buchungstexte in der Offen-Liste kopierbar
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>
2026-08-21 20:59:51 +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 78835d1e40 feat: Konto-Auswahl mit Symbol und Farbe
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>
2026-08-21 20:42:14 +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 ff897c196b fix: Dropdown bleibt beim Scrollen offen, Dialog beim Markieren
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>
2026-08-21 20:31:18 +02:00
GuidoandClaude Opus 5 d8c15ca260 feat: Rueckgaengig gemachte Importe sammelweise entfernen
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>
2026-08-21 20:30:11 +02:00
GuidoandClaude Opus 5 6322b65306 fix: Verwendungszweck steht in der Buchungsliste vorn
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>
2026-08-21 20:28:39 +02:00
GuidoandClaude Opus 5 bc5349f193 feat: Zeilenfilter sichtbar, mit auswaehlbaren Werten
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>
2026-08-21 18:38:05 +02:00
GuidoandClaude Opus 5 d05f4915ad fix: Saldo-Abgleich erklaert die Abweichung verstaendlich
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>
2026-08-21 18:27:14 +02:00
GuidoandClaude Opus 5 6c64dba47d test: neutrale Kategorie gilt auch fuer Einnahmen
Seit der Import keine Konten mehr verbindet, wird aus jedem Uebertrag ein
Paar: Ausgabe beim einen Konto, Einnahme beim anderen. Zaehlte nur die
Ausgabe nicht, stuende das Geld als Einnahme in der Auswertung und die
Monatsbilanz waere um den vollen Betrag zu gut.

Der Test belegt: eine neutrale Kategorie aus einer AUSGABEN-Gruppe laesst
sich einer Einnahme zuweisen, der Kontostand steigt, die Einnahmen der
Auswertung bleiben unveraendert – und ohne die Markierung zaehlt dieselbe
Buchung wieder mit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 17:24:51 +02:00
GuidoandClaude Opus 5 9d930b3484 feat: Skript teilt bestehende Uebertraege in zwei Buchungen
Damit der Altbestand zum neuen Verhalten passt: Jeder Uebertrag wird zu
einer Ausgabe beim Quellkonto und einer Einnahme beim Zielkonto, beide
mit der neutralen Kategorie. Die Salden bleiben dabei exakt gleich, nur
die Art aendert sich.

Ohne --anwenden nur ein Bericht; vor dem Schreiben wird die Datenbank
kopiert.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 17:22:10 +02:00
GuidoandClaude Opus 5 a4ea0e46db feat: Import verbindet keine Konten mehr automatisch
Das Raten des Gegenkontos war die Quelle fast aller Saldo-Fehler: falsch
geratene Ziele, verdrehte Richtungen und doppelt gebuchte Bewegungen,
sobald beide Ausz-uege eingelesen wurden. Zeilen ohne erkennbares Ziel
blieben ausserdem ungebucht, wodurch Geld unbemerkt fehlte.

Jede Zeile ist jetzt schlicht das, was im Auszug steht: Ausgabe oder
Einnahme. Fruehere Uebertrags-Schlagwoerter haengen stattdessen die
neutrale Kategorie an - der Saldo stimmt, die Statistik bleibt sauber.
Ein Uebertrag laesst sich weiterhin von Hand buchen.

Damit entfallen Gegenbuchungs-Suche, Zielkonto-Raten und der Zustand
"wartet auf Zielkonto".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 17:19:34 +02:00
GuidoandClaude Opus 5 b4cd5b7ca3 feat: Auszugs-Vergleich nennt die fehlenden Buchungen einzeln
Die Tagessummen sagen nur, dass etwas fehlt. Jetzt werden die Betraege je
Tag als Menge verglichen: was im Auszug steht und nicht in Certain, und
was Certain zusaetzlich hat – Letzteres mit Art und Text.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 17:04:11 +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 e0955b79ee chore: Revolut-Auszug (nur Betraege) fuer den Saldo-Abgleich
Nur Produkt, Datum, Betrag, Gebuehr und Status – ohne Beschreibungen und
Empfaenger. Dient dazu, die gebuchten Daten auf dem Server Tag fuer Tag
gegen den Auszug zu vergleichen, und wird danach wieder entfernt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 16:57:26 +02:00
GuidoandClaude Opus 5 0d9f0f88b0 feat: Vergleich der Buchungen mit dem Original-Auszug
Ein falscher Saldo sagt nur, DASS etwas fehlt. Das Skript rechnet je Tag
zusammen, was im Auszug steht und was in der Datenbank gelandet ist, und
listet die Tage auf, an denen beides auseinandergeht.

Gebuehren werden wie im Auszug verrechnet (Betrag minus Gebuehr); gegen
die Saldo-Spalte von Revolut geprueft.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 16:40:53 +02:00
GuidoandClaude Opus 5 6745f5490f feat: Skript raeumt falsche Uebertraege im Altbestand auf
Zwei Fehlerbilder: Uebertraege ohne Beleg (ein zu kurzer Erkennungs-Text
schlug mitten im Wort an) werden wieder zu Ausgabe/Einnahme, und zwar auf
dem Konto, aus dessen Auszug die Zeile stammt. Spiegel-Paare (dieselbe
Bewegung steht in beiden Auszuegen, etwa eine Revolut-Aufrundung) werden
gemeldet; welche Richtung bleibt, waehlt --behalte.

Ohne --anwenden aendert das Skript nichts, und vor dem Schreiben legt es
eine Kopie der Datenbank an.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 15:34:35 +02:00
GuidoandClaude Opus 5 578439097e fix: Umlaute im Hinweis zu kurzen Erkennungstexten
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 15:32:00 +02:00
GuidoandClaude Opus 5 1e7c1ec014 fix: Erkennungstext trifft nur noch an Wortgrenzen
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>
2026-08-21 15:31:41 +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 5 d88efebfeb feat: Diagnose zeigt Stichtag und Beispieltexte je Übertrag
Der Stichtag eines Kontos wird von der Saldo-Berechnung ignoriert – das
Skript weist jetzt aus, wie viel aus Buchungen bis zum Stichtag stammt
und trotzdem mitzählt. Die Übertrags-Matrix nennt dazu Beispieltexte,
Erkennungstexte der Konten und Transfer-Schlagwörter, damit sich ein
falscher Übertrag seinem Auslöser zuordnen lässt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 15:22:59 +02:00
GuidoandClaude Opus 5 e2387102c1 feat: Diagnose-Skript für Übertrag-Flüsse
Zeigt als Matrix, welches Konto an welches überträgt, dazu Überträge
ohne Zielkonto und Währungswechsel ohne Zielbetrag – Letztere schreiben
den Fremdbetrag unkonvertiert gut und verfälschen den Saldo.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 15:02:06 +02:00
GuidoandClaude Opus 5 7fa15c2b86 feat: Diagnose-Skript schlüsselt Kontostände auf
Zeigt je Konto Anfangssaldo, Einnahmen, Ausgaben und beide Transfer-
Richtungen einzeln, dazu mehrfach gebuchte Auszüge und doppelte
Buchungen. Damit lässt sich ein falscher Saldo auf seine Ursache
zurückführen, statt sie zu erraten.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 14:58:38 +02:00
GuidoandClaude Opus 4.8 3948d98273 feat: Diagnose-Skript für fehlende Schlagwörter
Die Kategorie-Liste zeigt nur manuelle, einer bestehenden Kategorie zugeordnete
Regeln. Bleibt sie leer, obwohl Regeln in der Datenbank stehen, sagt das Skript
warum: Es schlüsselt den Bestand nach source, kind und is_transfer auf, zählt
Regeln ohne Kategorie sowie solche, deren Kategorie nicht mehr existiert, und
nennt die Zahl der tatsächlich sichtbaren.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-21 14:33:05 +02:00
GuidoandClaude Opus 4.8 ff95bc5344 feat: Backup-Prüfung nennt die neueste vollständige Sicherung
Bei 35 Sicherungen hilft eine blosse Liste wenig. Das Skript zeigt jetzt je
Sicherung Zeitpunkt, Schlagwörter, Budget-Vorlagen und wie viele Buchungen eine
Kategorie tragen – und nennt am Ende die neueste noch vollständige samt der
fertigen Befehle zum Zurückspielen. Tabellen, die es zum Sicherungszeitpunkt
noch nicht gab, erscheinen als '–' statt als Fehler.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-21 14:25:40 +02:00
GuidoandClaude Opus 4.8 33bbc535cd feat: Skript zum Prüfen der Datenbank-Sicherungen
Listet je Backup, wie viele Schlagwörter, Budget-Vorlagen und kategorisierte
Buchungen darin stecken. Nach einem Datenverlust sieht man damit auf einen Blick,
welche Sicherung noch vollständig ist – statt nach Zeitstempel zu raten.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-21 14:23:12 +02:00
GuidoandClaude Opus 4.8 35d89b2b8f fix: Migrationen löschen keine abhängigen Daten mehr
Auf SQLite baut Alembic (render_as_batch) eine Tabelle um, indem es eine Kopie
anlegt und das Original löscht. Bei DROP TABLE führt SQLite ein implizites DELETE
aus – mit aktiven Fremdschlüsseln schlägt das per ON DELETE CASCADE auf abhängige
Tabellen durch.

Migration 0013 fügte `categories` nur ein Feld hinzu und vernichtete dabei alle
Schlagwörter und Budget-Vorlagen und setzte die Kategorie sämtlicher Buchungen
auf NULL. Reproduziert und behoben: Fremdschlüssel werden für die Dauer der
Migration abgeschaltet (vor der Transaktion, sonst ignoriert SQLite den Befehl)
und danach wieder aktiviert. Lässt sich das Abschalten nicht bestätigen, bricht
die Migration ab, statt Daten zu riskieren.

Regressionstest legt Daten auf Stand 0012 an, migriert auf head und prüft, dass
Schlagwörter, Budget-Vorlagen und Kategoriezuordnungen erhalten bleiben.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-21 14:21:03 +02:00
GuidoandClaude Opus 4.8 af865a5c7b fix: neutrale Kategorien gelten für Ausgabe UND Einnahme
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>
2026-08-21 14:09:54 +02:00
GuidoandClaude Opus 4.8 7674e47f43 fix: Test für neutrale Kategorien misst relativ statt absolut
Die Testdatenbank lebt über den ganzen Lauf; im laufenden Monat liegen also
schon Buchungen anderer Tests. Absolute Erwartungswerte gingen allein durch,
im Gesamtlauf nicht. Jetzt zählt die Differenz vorher/nachher. 93 grün.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-21 10:18:15 +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 ec0aa62017 feat: Liniendiagramm mit Nulllinie, runden Schritten und wählbarem Zeitraum
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>
2026-08-21 09:59:36 +02:00
GuidoandClaude Opus 4.8 511cf1abe0 fix: Gegenbuchung nimmt den besten statt des erstbesten Treffers
Liegen mehrere Währungstausche im selben Zeitfenster, schnappte der zuerst
geprüfte Transfer der eigentlich gemeinten Zeile das Gegenstück weg – diese blieb
dann ohne Gegenkonto stehen. Jetzt gewinnt der Kandidat mit der geringsten
Datums- und Betragsabweichung.

Toleranz von 10 % auf 20 % erhöht: Wir schätzen die Gegenseite mit dem EZB-Kurs,
die Bank rechnet mit ihrem eigenen – und fehlt uns der Kurs, schätzen wir 1:1.
Ein echter Tausch lag damit schnell über 10 % daneben (CHF 150.00 -> EUR 165.60
sind 10,4 %), eine fremde Buchung trifft das Zeitfenster praktisch nie.

An den echten Auszügen: offene Zeilen im Revolut-EUR-Import von 3 auf 1 gesunken.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-21 09:56:09 +02:00
GuidoandClaude Opus 4.8 7eed5ee6c4 feat: Ausgaben-Anteile wahlweise als Kreisdiagramm
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>
2026-08-21 09:46:48 +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 ab5410ca6f fix: Saldo-Abgleich meldet Zeilen, die auf ein Gegenkonto warten
Eine Zeile mit „Gegenkonto wählen" wird nicht gebucht, der Abgleich zählte ihren
Betrag aber trotzdem mit und meldete „stimmt überein" – das Geld fehlte im Konto,
ohne dass es auffiel.

Duplikate und Gegenbuchungen zählen weiterhin mit (ihr Betrag steckt bereits in
einer früher gebuchten Buchung); wartende Zeilen zählen jetzt als 0 und tauchen
als erste Abweichung mit Datum und Empfänger auf.

Nachgestellt am echten Raiffeisen-Auszug: Zeigt die Transfer-Regel „Übertrag" auf
das importierte Konto selbst, bleibt die Gutschrift über 92.00 ungebucht; der
Abgleich nennt jetzt genau diese Zeile.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-21 09:17:22 +02:00
GuidoandClaude Opus 4.8 c438d1ec50 feat: Bank-Vorlage für N26
Spaltenzuordnung aus einem echten Export (Betrag in Spalte „Amount (EUR)",
Partner als Empfänger, Payment Reference als Zweck, Partner-IBAN für die
Transfer-Erkennung). Eine Kontostand-Spalte liefert N26 nicht, dort bleibt der
Saldo-Abgleich also aus.

Nachgerechnet: 246 Zeilen, Summe -719.51; mit Anfangssaldo 872.95 ergibt das
exakt die tatsächlichen 153.44 EUR.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-21 09:07:21 +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 187a1119a2 fix: Gegenbuchungs-Erkennung beachtet die Richtung
Die Suche prüfte nur Betrag und Zeitfenster. Ein erwarteter Transfer-EINGANG
passte damit auch auf eine gleich hohe AUSGABE: Die echte Ausgabe galt als
Gegenbuchung, wurde nicht gebucht und der Saldo lief um ihren Betrag zu hoch.

Jetzt muss das Vorzeichen zur Seite des Transfers passen (wo Geld ankam, kann
keine Ausgabe die Gegenseite sein). Test deckt genau diesen Fall ab; 87 grün.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-21 08:31:18 +02:00
GuidoandClaude Opus 4.8 62eab06bdc feat: Gegenseite eines Übertrags braucht keine eigene Transfer-Regel
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>
2026-08-21 08:20:13 +02:00
GuidoandClaude Opus 4.8 cb4f3a8909 feat: Währungstausch wird über beide Auszüge als ein Transfer erkannt
Revolut tauscht Geld zwischen Währungskonten; im CHF-Auszug steht -100.00 CHF,
im EUR-Auszug +106.42 EUR. Die Gegenbuchungs-Erkennung verglich exakte Beträge
und griff deshalb nie – die Bewegung wurde zweimal gezählt.

- _find_counterpart erkennt die Gegenseite bei unterschiedlichen Währungen über
  Konto-Paar, Zeitfenster und einen plausiblen Betrag (10 % Toleranz, weil wir
  mit dem EZB-Kurs schätzen und die Bank ihren eigenen nutzt)
- die Gegenbuchung nennt den TATSÄCHLICHEN Betrag: er ersetzt beim Buchen die
  Schätzung im bereits gebuchten Transfer, damit beide Salden exakt stimmen
- Test über beide Auszüge: ein Transfer, keine Doppelzählung, EUR-Seite exakt

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-21 08:13:59 +02:00
GuidoandClaude Opus 4.8 d97e08e913 fix: Datumsspalten mit Uhrzeit werden auch ohne passendes Format erkannt
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>
2026-08-21 08:02:18 +02:00
GuidoandClaude Opus 4.8 e69be7f3d5 fix: negative Kontostände werden nicht mehr als positiv angezeigt
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>
2026-08-21 07:01:04 +02:00