Commit Graph
239 Commits
Author SHA1 Message Date
GuidoandClaude Opus 4.8 5a747fbef4 feat: Saldo-Abgleich beim CSV-Import gegen die Kontostand-Spalte
Bisher merkte man einen Importfehler erst am falschen Endsaldo, ohne Hinweis auf
die Ursache. Enthält der Auszug eine laufende Kontostand-Spalte, gleicht der
Import jetzt jede Zeile dagegen ab: Die Spalte sagt, wie sich der Saldo
tatsächlich verändert hat, unsere Erkennung muss dazu passen.

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

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-21 06:58:33 +02:00
GuidoandClaude Opus 4.8 4520c7318f fix: Status-Prüfung beim Import ist sprachunabhängig
Die neue Statusprüfung ließ nur „COMPLETED" durch. Deutsche Bank-Exporte
schreiben aber „ABGESCHLOSSEN", wodurch keine einzige Zeile mehr buchbar war.

Statt einer Positivliste jetzt eine Sperrliste (storniert/ausstehend/abgelehnt/
fehlgeschlagen, jeweils deutsch und englisch); unbekannte Status werden gebucht,
damit keine echten Buchungen still verloren gehen.

Test mit echtem deutschem Revolut-Export inkl. reiner Gebührenzeile
(Betrag 0.00, Gebühr 10.99): Endsaldo trifft die Kontostand-Spalte. 81 grün.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-21 06:41:32 +02:00
GuidoandClaude Opus 4.8 d725f5cf57 fix: Revolut-Import wertet Gebühr, Status und Währung aus
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>
2026-08-21 06:30:22 +02:00
GuidoandClaude Opus 4.8 01059b2bb2 fix: Buchungsliste zeigt alle Buchungen (Default-Limit 200 -> 5000)
Die Liste lud nur die 200 neuesten Buchungen, wodurch ältere Monate fehlten
(bei 254 importierten Zeilen reichte die Ansicht nur bis Juni zurück). Da die
Tabelle im Browser filtert und sortiert, werden nun standardmäßig alle Buchungen
geladen. Verifiziert am echten Auszug: 254 von 254, ab 05.01.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-21 06:02:22 +02:00
GuidoandClaude Opus 4.8 14b3e53685 fix: Duplikat-Erkennung als Multiset – echte gleich aussehende Buchungen bleiben
Bisher wurde eine zweite Buchung mit gleichem Tag/Betrag/Text (z. B. zwei
Gutschriften à 324 am selben Tag) fälschlich als Duplikat verworfen. Jetzt gilt
eine Zeile nur als Duplikat, wenn bereits GENAU SO VIELE identische Buchungen in
der DB stehen – ein erneuter Import derselben Datei bleibt weiterhin Duplikat-sicher.

- csv_import.detect_rows: existing/seen als Counter (Multiset) statt Set
- Tests angepasst + Re-Import-Test ergänzt; an echtem Raiffeisen-Auszug verifiziert
  (254 Zeilen, 0 fälschliche Duplikate, Saldo == Bank-Endsaldo)

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

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

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

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

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

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

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-20 16:58:32 +02:00
GuidoandClaude Opus 4.8 d1cab19264 feat: gleiches Schlagwort für Einnahme UND Ausgabe erlaubt (Art je Regel)
- CategorizationRule bekommt `kind` (expense|income|transfer); Unique jetzt
  (owner, match_field, pattern, kind) statt ohne kind
- Konfliktprüfung/Anlage (add/update Keyword, Transfer-Keyword, gelernte Regeln)
  arbeiten kind-bewusst: dasselbe Wort je einmal pro Art möglich
- Migration 0009 mit Backfill (kind aus Kategorie-Art bzw. is_transfer)
- Tests angepasst: quer über Arten erlaubt, gleiche Art weiterhin Konflikt (71 grün)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-20 16:37:35 +02:00
ScarriffleandClaude Opus 5 64cbbc8cf6 feat: Kategorien mit Schlagwoertern sind in der Liste erkennbar
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>
2026-08-20 14:19:42 +02:00
ScarriffleandClaude Opus 5 df5bfec331 fix: Ankreuzfelder zentral von der Textfeld-Regel ausnehmen
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>
2026-08-20 14:10:56 +02:00
ScarriffleandClaude Opus 5 cdf21474cf feat: Schlagwort per Doppelklick oben in der Zeile bearbeiten
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>
2026-08-20 13:49:43 +02:00
ScarriffleandClaude Opus 5 e503baa8a6 fix: sprechende Dateinamen fuer alle Exporte
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>
2026-08-20 13:13:39 +02:00
ScarriffleandClaude Opus 5 7fc8763d57 feat: 2FA, Passwortrichtlinie und wiederherstellbares JSON-Backup
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>
2026-08-20 10:11:22 +02:00
GuidoandClaude Opus 4.8 1d5b23c127 feat: Excel-artige DataTable (Spaltenbreite/Sortieren/Filtern) für alle Tabellen
- Portierte DataTable-Komponente (aus Vorrania): Spalten per Drag breiter ziehen
  und verschieben, Klick-Sortierung, Filterzeile je Spalte, Spalten ein-/ausblenden;
  Breiten/Reihenfolge/Filter pro Tabelle im Browser gemerkt (localStorage)
- Zu langer Zellinhalt wird mit „…" abgeschnitten (Titel bleibt sichtbar, Excel-artig)
- Umgestellt: Buchungen, Import-Prüfansicht (inkl. interaktiver Zellen), Import-
  Verlauf, Benutzer
- Neue Skeleton-Komponente für den Ladezustand

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-19 22:58:01 +02:00
GuidoandClaude Opus 4.8 e02ef7eb8f style: Dark-Theme grauer (weniger Grünanteil in den Neutraltönen)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-19 22:47:22 +02:00
GuidoandClaude Opus 4.8 468e76ef4c style: Dark-Theme in dunkles Grün (Hintergrund + Sidebar ohne Blaustich)
Neutraltöne (bg/surface/border/text/muted) hell wie dunkel leicht grün getönt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-19 22:43:31 +02:00
GuidoandClaude Opus 4.8 204a24d0df feat: Regeln (Betrag/Transfer/Konto-Texte) + Münz-Icon + pastellgrünes Theme
Regeln:
- Schlagwörter mit optionaler Betragsbedingung (z. B. „SV = 5.00 → Restaurant");
  Betrags-Regeln gewinnen vor betragslosen
- Transfer-Schlagwörter (Text → Transfer) auf der Konten-Seite
- Konto-Identifikator-Art „Text": erkennt das Zielkonto eines Transfers aus dem
  Buchungstext (längster passender Text gewinnt)
- Migration 0007, Endpunkte, Tests (Betrag/Transfer/Konflikt), gesamt 30 grün

Optik:
- App-Icon: Münzhaufen (aus Nutzer-SVG) weiß auf grünem Grund, klein gehalten
- Farbschema auf pastelliges Grün (Akzent hell/dunkel + Favicon)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-19 22:41:38 +02:00
GuidoandClaude Opus 4.8 30bc037579 feat: App-Icon auf Münzen (weg vom Aktien-Look)
BrandMark + favicon.svg zeigen jetzt zwei Münzen statt der Trendlinie.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-19 22:23:01 +02:00
GuidoandClaude Opus 4.8 a3ab03aeec fix: Kategorien-Seite als durchgehende Liste statt abgesetzter Gruppen-Karten
Gruppen erscheinen nur noch als dezente Zwischenüberschrift; die Kategorien
laufen als schlichte Zeilen mit dünnem Trenner durch (eine Karte).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-19 21:40:32 +02:00
GuidoandClaude Opus 4.8 37f64999cd feat: Schlagwörter je Kategorie + auf-/zuklappbares Kategorie-Dropdown
- Manuelle Schlagwörter je Kategorie (Popup mit Chips, Doppelklick zum Bearbeiten)
- Treffer über Empfänger ODER Verwendungszweck; längstes Schlagwort gewinnt;
  Kind-Filter (Ausgabe/Einnahme); wirken nur bei künftigen Importen
- Lernende Korrektur-Erkennung: ab 5 Anwendungen und >= 1/3 Korrekturen
  erscheint eine Prüf-Meldung in den Einstellungen (Behalten/Entfernen)
- Neue CategorySelect-Komponente (Baum + Suche, per Portal) ersetzt die
  nativen optgroup-Selects in Buchungen und Import-Prüfansicht
- Migration 0006 (source/applied_count/overridden_count/confirmed, match_field any)
- Endpunkte, i18n DE/EN, Tests (test_keywords: 7 neu, gesamt 27 grün)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-19 21:31:29 +02:00
GuidoandClaude Opus 4.8 f375fc5eaa feat: Backup/Export + Import-UX klarer + Raiffeisen-Preset-Fix
Backup & Export:
- routers/backup: /backup/db (Admin, konsistentes SQLite-Online-Backup),
  /export/transactions.csv|json und /export/all.json (owner-gescoped).
- Einstellungen: Bereich „Backup & Export" mit Download-Buttons. docs/BACKUP.md
  (Wiederherstellung + LXC-Sicherung). pytest für Export + Admin-Schutz.

Import-Verbesserungen (Nutzer-Feedback zum Ablauf):
- Klarer Hinweis, dass die Zuordnungstabelle nur Beispielzeilen zeigt und
  „Vorschau erstellen" die vollständige Prüfliste + Buchen-Button erzeugt.
- Zusammenfassung (erkannt/gebucht) und Warnung, wenn keine gültige Zeile erkannt
  wurde (Hinweis auf Format/Bank-Vorlage).
- Fix: Raiffeisen-Preset hat has_header=true (der Export hat eine Kopfzeile).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-19 20:35:02 +02:00
GuidoandClaude Opus 4.8 7461fddb7a feat: Phase 5 – PDF/Bild-Beleg-Import (lokal, Tesseract-OCR)
Backend:
- services/ocr: PDF-Text via PyMuPDF, OCR (Tesseract) für Scans/Bilder,
  heuristischer Parser (Datum + Betrag je Zeile, CH/DE/US-Beträge).
- routers/documents: /documents/preview (Upload -> Text -> Kandidaten ->
  gemeinsame Prüf-/Buchungslogik des CSV-Imports). csv_import refaktoriert:
  detect_rows() von CSV und Beleg gemeinsam genutzt.
- requirements: PyMuPDF, pytesseract, pillow; install.sh installiert tesseract-ocr
  (+ deu). pytest: Heuristik-Parser und PDF-Vorschau.

Frontend:
- Import-Seite: CSV/PDF-Umschalter; PDF-Modus mit Beleg-Auslesen, extrahiertem
  Text zur Kontrolle und Best-Effort-Hinweis; gleiche Prüfansicht + Buchen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-19 20:25:32 +02:00
GuidoandClaude Opus 4.8 ddf4f1f5ed feat: vorgefertigte Import-Presets für Raiffeisen (CH) und Revolut
- services/csv_import: PRESETS-Liste (aus echten Beispiel-Exporten des Nutzers),
  GET /imports/presets.
- Frontend: Preset-Auswahl im Import füllt Format/Spalten automatisch.
Beide Presets per TestClient gegen echte Zeilenformate verifiziert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-19 20:14:39 +02:00
GuidoandClaude Opus 4.8 1381967aae feat: Phase 4 – CSV-Import (Templates, Prüfansicht, Duplikat/Transfer-Erkennung, Undo)
Backend:
- Modelle + Migration 0005: import_templates, import_batches, categorization_rules;
  transactions um import_batch_id + import_hash erweitert.
- services/csv_import: Datei parsen (Trennzeichen, Dezimal-/Datumsformat, Kodierung,
  Header/Skip, Vorzeichen vs. Soll/Haben), Vorschau mit Duplikat-Erkennung (Hash),
  interner-Transfer-Erkennung via IBAN, lernender Auto-Kategorisierung; Buchen mit
  Batch und Undo.
- routers/imports: Templates (CRUD), Roh-Vorschau, Prüf-Vorschau, Buchen, Verlauf,
  Rückgängig. pytest: Parsing, Duplikate, Transfer, Buchen, Undo, Lernen.

Frontend:
- Import-Seite: Konto+Datei, Format/Spaltenzuordnung (mit Roh-Vorschau),
  editierbare Prüfansicht (Kategorie/Typ/Zielkonto, Duplikate markiert),
  Buchen, Import-Verlauf mit Undo; Vorlagen speichern; i18n DE/EN.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-19 20:06:50 +02:00
GuidoandClaude Opus 4.8 a31ba0c785 fix: IBAN-Wertfeld im Konto-Formular war zu schmal (Flex-Shrink)
Ein 100%-breites select drängte das danebenliegende Eingabefeld auf 0.
Select auf feste Breite, .row-Kinder dürfen schrumpfen (min-width:0).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-19 19:59:14 +02:00
GuidoandClaude Opus 4.8 b871815a46 feat: Phase 3 – konfigurierbares Karten-Dashboard
Frontend:
- Dashboard-Seite mit react-grid-layout: Karten frei verschieben/skalieren,
  hinzufügen/entfernen, Standard-Layout zurücksetzen; Layout je Nutzer gespeichert.
- Sechs Karten: Kontostände, Budget-Donut, Vermögensverlauf (Linien-Chart),
  Budget-Fortschritt (grün/rot), Einnahmen/Ausgaben, letzte Buchungen.
- SVG-Charts (Donut/Line) mit währungsbewusster Formatierung; i18n DE/EN.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-19 19:42:14 +02:00
GuidoandClaude Opus 4.8 a998f70241 fix: Dienst läuft als Repo-Eigentümer (behebt 200/CHDIR bei Klon unter /root)
Ein eigener Dienst-Benutzer kommt nicht an Code/venv unter /root (Modus 700).
install.sh setzt User/Group der Unit jetzt auf den Eigentümer des Repos – bei
Klon als root nach /root also root. Kein useradd mehr.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-19 19:40:37 +02:00
GuidoandClaude Opus 4.8 2dd8f9cd35 fix: systemd ohne ProtectHome (venv unter /root sichtbar) + Phase-3-Backend
Deployment-Fixes (Dienst startete nicht -> 203/EXEC "uvicorn not found"):
- Unit ohne ProtectHome/ProtectSystem: Repo/venv dürfen unter /root oder /home
  liegen (ProtectHome=true blendete diese Pfade für den Dienst aus).
- install.sh: Python-3.11+-Prüfung mit klarer Meldung; prüft nach der Installation,
  dass uvicorn wirklich in der venv liegt.
- EZB-Kursabruf läuft beim Start jetzt im Hintergrund (blockiert den Start nie).

Phase-3-Backend (Dashboard):
- Modell + Migration 0004: dashboard_layouts (JSON-Layout je Nutzer).
- routers/dashboard: Layout speichern/laden/zurücksetzen + Kartendaten
  (Budget-Donut, Budget-Fortschritt, Einnahmen/Ausgaben, Kontostände,
  Vermögensverlauf, letzte Buchungen).
- Frontend-Infrastruktur: Chart-Komponenten (Donut/Line), palette, API-Methoden,
  react-grid-layout. (Dashboard-Seite folgt im nächsten Commit.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-19 19:31:55 +02:00
GuidoandClaude Opus 4.8 f53c79a20c fix: update.sh meldet klar, wenn der systemd-Dienst fehlt (statt kryptischem systemctl-Fehler)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-19 19:25:19 +02:00
GuidoandClaude Opus 4.8 8ed4bf5254 feat: Phase 2 – Budget-Engine + Direktzugriff (http://IP:Port)
Budget:
- Modelle + Migration 0003: budget_template_lines, planned_income_lines,
  budget_periods, budget_period_lines.
- services/budget: Perioden-Grenzen (einstellbarer Starttag, Label nach Endmonat),
  Standard-Vorlage vs. Monats-Override (Snapshot-on-Customize), Soll/Ist inkl.
  budgetierter Sparen-Transfers, geplante Einnahmen (Soll/Ist).
- routers/budgets: Vorlage, geplante Einnahmen (CRUD), Monats-Soll/Ist,
  Monats-Override, Zurücksetzen. pytest: Perioden, Soll/Ist, Override/Reset, Einnahmen.
- Frontend: Budget-Seite (Monat + Standard-Budget), Fortschrittsbalken grün/rot,
  Monats-Navigation, editierbare Soll-Beträge; Sparen-Option im Transfer-Formular.

Deployment-Fix (Nutzer-Anforderung: http://IP:Port muss funktionieren):
- Dienst bindet an APP_BIND_HOST=0.0.0.0 (direkt im LAN erreichbar).
- APP_COOKIE_SECURE=false als Standard (sonst kein Login über reines HTTP);
  für reinen HTTPS-Betrieb umstellbar. Doku + install.sh entsprechend angepasst.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-19 19:16:27 +02:00
GuidoandClaude Opus 4.8 d692c04cc1 docs: Phase 1 als erledigt markiert
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-19 19:00:01 +02:00
GuidoandClaude Opus 4.8 1b7107b9bd feat: Phase 1 – Konten, Kategorien, Transaktionen, EZB-Währungsumrechnung
Backend:
- Modelle + Migration 0002: accounts, account_identifiers, category_groups,
  categories, transactions, exchange_rates (alle owner-gescoped).
- EZB-Kursdienst (90-Tage/Historie) + Backfill-Skript; Umrechnung EUR<->CHF zum
  Buchungsdatum mit lokalem Cache und manueller Übersteuerung; EUR-Pivot
  (amount_eur_cents) eingefroren.
- Kontostände + Netto-Vermögen (Umrechnung in Hauptwährung).
- Router: accounts (inkl. IBAN-Identifikatoren), categories (Gruppen+Kategorien),
  transactions (Ausgabe/Einnahme/Transfer, cross-currency), rates.
- Gruppierte Standard-Kategorien je Nutzer beim Anlegen.
- pytest-Suite: Währung, Salden, Cross-Currency-Transfer, Nutzer-Isolation.

Frontend:
- Seiten Konten, Kategorien, Buchungen; Icon-Chip, Geldbetrag mit Umrechnung,
  Icon-/Farbwähler, Modal; erweitertes SVG-Icon-Set (Konto-/Kategorie-Icons).
- Währungs-/Datumsformatierung (EUR de-DE, CHF de-CH), i18n DE/EN erweitert.
- Live-Umrechnungs-Vorschau im Transfer-Formular.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-19 18:59:24 +02:00
GuidoandClaude Opus 4.8 2c795a57c8 feat: Phase 0 – Gerüst (FastAPI+SQLite, Multi-User-Login, React-App-Shell, Deploy)
Grundgerüst für Certain Finance:
- Backend: FastAPI + SQLAlchemy + SQLite, Alembic-Migrationen (Batch-Mode),
  argon2-Passwort-Hashing, server-seitige Sitzungen im Cookie, Erst-Admin,
  Benutzerverwaltung mit Temporär-Passwort, Einstellungen je Benutzer.
- Ein Prozess, ein Port: uvicorn liefert API (/api) und gebautes SPA aus,
  inkl. SPA-Deeplink-Fallback.
- Frontend: React 18 + Vite + JSX, Design-Tokens (Light/Dark), i18n DE/EN,
  Inline-SVG-Icons (keine Emojis), Login, Passwort-ändern, Dashboard-Platzhalter,
  Benutzer- und Einstellungsseiten.
- Deploy: nativer LXC-Installer + Update-Skript (systemd), env-Vorlage, Doku.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-19 18:27:42 +02:00