Beim ersten Start fuehrt kein Weg an einer sechsstelligen PIN vorbei. Danach
sperrt die App beim Kaltstart und beim Wegschalten; kommt man binnen einer
Minute zurueck, geht sie ohne Nachfrage wieder auf. Face ID zuerst, die PIN
als Rueckfallweg und als einziger Weg ohne Biometrie. Nach fuenf falschen
PINs meldet die App ab.
Gesperrt wird schon bei inactive, nicht erst im Hintergrund - sonst stuenden
die Kontostaende im App-Umschalter.
Von der PIN liegt nur ein gesalzener SHA-256 im Schluesselbund. Die
Gerätemarke selbst bleibt wie bisher abgelegt: Die Sperre verdeckt die
Oberflaeche, sie verschluesselt nichts.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Aus Xcode uebernommen: eigener Verlauf fuer den Dunkelmodus im Icon,
Kategorie Finanzen, nur iPhone, Hochformat.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Farbauswahl der App lag dreimal gleich im Code; sie steht jetzt einmal
in Farbwahl, mit dem Markengruen an erster Stelle.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Neu drin: warum der zweite Faktor sich hier einrichten, aber nicht
abschalten laesst (das verlangt das Kontopasswort, und die App speichert
keines - ein Vollzugang auf dem Geraet soll den Schutz nicht aufheben
koennen), warum auf dem Telefon die otpauth-Adresse statt eines QR-Codes
genommen wird, und dass die Rettungscodes genau einmal erscheinen.
Dazu der Grund fuer Einstellungen ohne Sichern-Knopf und der Hinweis, dass
sie dem Konto gehoeren, nicht dem Geraet.
Die Liste dessen, was bewusst am Rechner bleibt, ist jetzt vollstaendig:
Vertragslogo, Kontoreihenfolge, Loesen einer Verknuepfung,
Transfer-Schlagwoerter und das Anwenden von Regeln auf Bestehendes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Nebenraeume haengen unter Einstellungen, nicht als eigene Reiter: Man
braucht sie selten, und eine Leiste mit zehn Reitern findet niemand mehr.
- Kategorien: anlegen, aendern, archivieren, loeschen - samt Gruppen. Eine
Kategorie oeffnet ihre Erkennungswoerter, die sich dort hinzufuegen und
entfernen lassen. Darunter steht, was sonst niemand merkt: Sie wirken nur
auf kuenftige Importe.
- Woerter, die oft korrigiert werden, stehen als Pruefmeldung in den
Einstellungen - behalten oder entfernen.
- Schlagwoerter: anlegen, umbenennen, umfaerben, loeschen. Neue bekommen
reihum eine Farbe, damit nicht alle grau sind.
- Einstellungen: Erscheinungsbild, Hauptwaehrung und Budget-Starttag lassen
sich jetzt aendern. Ohne Sichern-Knopf - es ist je Zeile genau ein Wert,
und das Backend nimmt ihn einzeln entgegen. Dazu der Hinweis, dass sie dem
Konto gehoeren und nicht dem Geraet.
- Bank: abrufen und Salden auffrischen. Verbinden bleibt am Rechner, die
Bank leitet fest dorthin zurueck. Der letzte Fehler der Verbindung steht
dabei, sonst ruft man vergeblich ab.
- Zweiter Faktor: einrichten in drei Schritten. Auf dem Telefon fuehrt der
Weg ueber die otpauth-Adresse direkt in die Authenticator-App statt ueber
einen QR-Code, den man sich selbst zeigen muesste. Die Rettungscodes
erscheinen einmal; das Blatt laesst sich nicht wegwischen und erst
schliessen, wenn man bestaetigt hat, sie gesichert zu haben.
Abschalten des zweiten Faktors bleibt bewusst draussen: Es verlangt das
Kontopasswort, und die App speichert keines. Ein Vollzugang auf diesem Geraet
soll den Schutz des Kontos nicht aufheben koennen.
59 Tests gruen. Texte in Deutsch und Englisch, zeilengleich.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Erkennungswoerter einer Kategorie, markierte Woerter, der Stand des zweiten
Faktors und die Bankverbindungen kommen jetzt mit in
ios/Tests/Fixtures/antworten.json. Das Skript gibt die IDs aus der Saat
zurueck, damit ein Pfad wie /categories/{id}/keywords ueberhaupt gebaut
werden kann.
Zwei davon bleiben leer, und das steht auch dort: Eine Markierung entsteht
erst nach fuenf Anwendungen mit Korrekturen, eine Bankanbindung ohne echten
Anbieter gar nicht. Die Felder von BankConnectionOut deckt der Test also
NICHT ab - wer sie aendert, prueft von Hand.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Neu drin: wo der einzige 409-Fall sitzt und wie er behandelt wird (eine
Frage, kein Fehler), woran man einen Uebertrag im Eingang erkennt, warum die
Dublettenliste die Kennung der Bank zeigt, und der Backend-Fehler, den die
App ans Licht brachte.
Ausserdem der Hinweis, der den naechsten sonst eine Stunde kostet: Die
Pruefdatei-Tests zaehlen keine Mengen mehr. Wer die Saat erweitert, macht
sonst die aelteren Pruefungen rot, ohne dass etwas kaputt waere.
Festgehalten, was weiter bewusst fehlt: Vertragslogo, Kontoreihenfolge und
das Loesen einer Uebertrags-Verknuepfung.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ein neuer Reiter "Eingang" mit allem, worueber jemand entscheiden muss. Vier
Abschnitte, jeweils mit Anzahl im Kopf:
- Zu buchen: offene Eingangseintraege aus Kurzbefehl, Postfach oder Bank.
Buchen, als Uebertrag buchen, verwerfen - oder alle auf einmal, bei denen
das Konto feststeht. Unklare tragen einen Hinweis ueber den Knoepfen, nicht
darunter: Man soll sie ansehen, nicht wegbuchen.
- Koennten Uebertraege sein: zwei getrennte Buchungen, die zusammengehoeren
koennten. Verbinden oder ablehnen. Hat eine der beiden eine Kategorie,
steht dabei, dass sie durchs Verbinden aus dem Budget faellt.
- Ohne Kategorie: gebuchtes Geld, das in keiner Budget-Zeile mitzaehlt. Ein
Tipp, eine Kategorie - kein Formular mit zwoelf Feldern davor.
- Sieht doppelt aus: gleiches Konto, gleicher Tag, gleicher Betrag. Mit der
Kennung der Bank je Zeile, denn verschiedene Kennungen heissen, dass die
Bank sie als verschiedene Zahlungen gemeldet hat.
Alles in einem Abruf. Vier getrennte Zwischenspeicher koennten offline aus
vier verschiedenen Momenten stammen und sich widersprechen.
Hier sitzt der einzige 409-Fall der App: Beim Buchen als Uebertrag kann das
Backend Bedenken haben. Das ist eine Frage, kein Fehler - die App zeigt den
Hinweis und wiederholt den Aufruf auf Wunsch mit trotzdem=true. Ihn zu
verschlucken waere falsch, ihn als blossen Fehler zu zeigen auch.
Zwei Dinge, die die Tests festhalten: Ohne Konto laesst sich nichts buchen
(das Backend lehnt mit 400 ab, der Knopf bleibt also grau - und die Zeile sagt
auch warum). Und ein Uebertrag braucht keine Kategorie, sonst fragte die Liste
ewig nach einer, die es nie geben wird.
Die Pruefdatei deckt jetzt auch Eingang, Dubletten und Uebertragsvorschlaege
ab. Die aelteren Pruefungen zaehlten exakt und wurden dadurch rot - sie
pruefen jetzt Eigenschaften statt Mengen, damit sie das naechste Wachstum
ueberleben. 54 Tests gruen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Jedes Paar steht in der Kandidatenliste zweimal, einmal je Richtung. Was
verknuepft wird, faellt ueber "verbraucht" heraus - ein blosser Vorschlag aber
nicht. Also bot /uebertraege/vorschlaege dieselbe Zahlung zweimal zum
Verbinden an, und wer das zweite anklickte, bekam "bereits verknuepft" fuer
etwas, das er gerade selbst verknuepft hatte.
Betrifft auch die Weboberflaeche: Uebertragsvorschlaege.jsx zeigt die Liste
unveraendert an und hatte denselben Doppeleintrag.
Gefunden beim Bauen der Arbeitsliste fuer die iOS-App - die Pruefdatei brachte
den Vorschlag doppelt zurueck.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Neu drin: die vier Regeln, die man nicht sieht, aber merkt, wenn sie fehlen
(Teilaenderung schickt nur Gesetztes, leere Tag-Liste bedeutet etwas, kein
Kategorie beim Uebertrag, planned_cents bleibt bei "berechnet" unberuehrt),
und warum die Waehrung eines bestehenden Kontos nicht aenderbar ist.
Dazu der Befehl, mit dem man fehlende Uebersetzungen findet - ein unbekannter
Schluessel uebersetzt anstandslos und zeigt dem Nutzer den Schluessel.
Festgehalten, was aus Stufe 3 bewusst nicht mitkam: Vertragslogo und
Kontoreihenfolge. Und wo der 409-Fall wartet: an POST
/transactions/{id}/transfer, also in Stufe 4.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Buchungen anlegen, aendern und loeschen; Budget-Zeilen setzen, fuer den Monat
oder fuer die Vorlage; Vertraege und Konten anlegen und aendern. Damit kann
die App alles, wofuer man bisher an den Rechner musste - ausser Import,
Arbeitsliste und Verwaltung.
Ein Formular je Sache, nicht zwei: Anlegen und Aendern teilen sich die Maske,
weil die Felder dieselben sind und zwei Kopien genau die Stelle waeren, an der
sie auseinanderlaufen. Was sich unterscheidet, steht in einem Zweck-Typ.
Geteilt in Bausteine/Formular.swift: ein Speichervorgang, der den Knopf sperrt
und den Fehler festhaelt (das Formular bleibt offen - zugeklappt waere die
Eingabe weg und die Meldung nutzlos), ein Betragsfeld, das an Cent gebunden
ist statt an Text, und die Auswahlfelder fuer Kategorie, Konto und
Schlagwoerter.
Drei Dinge, die stillschweigend falsch waeren:
Eine Teilaenderung schickt nur, was gesetzt ist. Ginge ein nicht angefasstes
Feld als null hinaus, loeschte jedes Speichern den Empfaenger, die Notiz und
die Kategorie gleich mit. Eine leere Tag-Liste geht dagegen sehr wohl mit -
sie bedeutet "alle abnehmen" und darf nicht mit "nicht angefasst"
zusammenfallen.
Bei einem Uebertrag wird keine Kategorie mitgeschickt: Das Geld wechselt nur
die Tasche, es wird nicht eingeordnet.
Bei Betriebsart "berechnet" bleibt planned_cents unberuehrt - das Backend
liest es dann nicht, und es zu ueberschreiben verloere den Wert, auf den ein
Zurueckschalten auf "fest" zurueckfallen soll.
Die Waehrung eines bestehenden Kontos laesst sich nicht aendern: Jeder
gespeicherte Betrag wuerde danach etwas anderes bedeuten.
49 Tests gruen. Texte in Deutsch und Englisch, zeilengleich.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Beide Kopierwege uebernahmen nur planned_cents. Ein Eintrag der Betriebsart
"berechnet" fiel dabei still auf "fest" zurueck - mit der Zahl, die zufaellig
zuletzt in planned_cents stand. Der Bezug zu den Vertraegen war danach weg,
ohne dass irgendwo stand, dass er weg ist.
Vorlage -> Monat (copy_template_to_month) zieht jetzt nach, was
ensure_customized nebenan laengst richtig macht. Monat -> Vorlage
(copy_month_to_template) nimmt die Betriebsart mit, statt die gerade
herauskommende Zahl einzufrieren: Eine berechnete Zeile soll auch in der
Vorlage berechnet bleiben.
Dabei aufgefallen: Eine berechnete Zeile gehoert auch dann in die Vorlage,
wenn planned_cents null ist - ihre Zahl steht in den Vertraegen. Eine feste
Null bleibt dagegen draussen, sonst fuellte sich die Vorlage mit Nullzeilen
fuer jede je beruehrte Kategorie.
Das war Backend-Ergaenzung D und damit die letzte offene.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Was neu drinsteht, damit der naechste Agent es nicht selbst herausfinden muss:
die drei Entwurfsentscheidungen der Stufe 2 (Dashboard in einem Abruf,
Nachschlagewerk im AppZustand, Suche erst beim Absenden), die zwei Fallen der
Uebertrags-Paarung, und dass die Modellpruefung jetzt ein Test ist statt
Handarbeit - samt dem Befehl, die Pruefdatei neu zu erzeugen.
Naechster Schritt steht oben: Stufe 3, davor nur noch Backend-Ergaenzung D.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die App kann jetzt lesen, wofuer man sie oeffnet. Vier neue Reiter:
- Uebersicht: Einnahmen/Ausgaben des Monats, Budget-Fortschritt je Gruppe,
Kontostaende und die letzten Buchungen. Feste Auswahl statt des
verschiebbaren Rasters der Weboberflaeche - auf Telefonbreite waere das
Arbeit ohne Gewinn. Vier Endpunkte nebeneinander in einem Abruf, damit der
Offline-Stand nicht aus vier verschiedenen Momenten stammt.
- Budget: Soll gegen Ist je Gruppe und Kategorie, blaetterbar ueber prev/next
aus der Antwort. Rot nur bei echter Ueberschreitung - wer mehr zuruecklegt
als geplant, hat nichts ueberzogen. Vertraege ohne Kategorie und fehlende
Wechselkurse stehen als Hinweis darunter, statt die Summen still zu
verfaelschen.
- Buchungen: Suche und Filter laufen im Server (Backend-Ergaenzung B), nicht
im Telefon. Gesucht wird beim Absenden, nicht je Tastendruck.
- Vertraege: Monats- und Jahressumme, Rhythmus, naechste Faelligkeit.
Zwei Dinge, die das Backend bewusst dem Client ueberlaesst:
Ein Uebertrag kommt in zwei Gestalten - am Stueck (eine Buchung mit
to_account_id) oder als zwei verknuepfte Buchungen, wie die Bank sie liefert.
Beide Beine als zwei Zeilen zu zeigen hiesse, dasselbe Geld zweimal zu zeigen.
Buchungszeile.zeilen fasst sie zusammen, aber nur wenn beide in der Liste
stehen - sonst liesse ein Kontofilter Zeilen verschwinden, die sehr wohl zu
diesem Konto gehoeren. Die Richtung kommt aus der Verknuepfung, nicht aus dem
Vorzeichen: Manche Banken liefern beide Beine positiv.
Kategorien, Schlagwoerter und Konten liegen als Nachschlagewerk im AppZustand,
einmal geladen. Je Ansicht neu zu holen waere genau das N+1, das das Backend
sich selbst verbietet.
Texte in Deutsch und Englisch, zeilengleich. 44 Tests gruen, darunter die
Uebertrags-Paarung und jedes Modell gegen eine echte Serverantwort.
Nicht geprueft: der Weg gegen ein echtes Backend. Die App laesst nur https zu,
ein lokaler Server haette eine ATS-Ausnahme gebraucht.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
AppZustand faellt beim Start ohne Verbindung auf den gemerkten Benutzer
zurueck - nur schrieb diesen Schluessel niemand je in den Zwischenspeicher.
Der Zweig konnte also nie greifen, und die App verlangte im Flugmodus eine
neue Anmeldung, obwohl die Geraetemarke laengst da war.
Gefunden beim Durchsehen fuer Stufe 2: Nur Lader und das Nachschlagewerk
schreiben in den Speicher, und auth/me geht an beiden vorbei.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Swift-Modelle sind aus den Pydantic-Schemas abgeschrieben. Ein vertippter
Feldname uebersetzt anstandslos und faellt erst dem Nutzer auf, wenn eine
Liste leer bleibt - das ist beim blinden Schreiben die einzige Luecke, die
kein Uebersetzer schliesst.
backend/scripts/erzeuge_ios_fixture.py legt eine Wegwerfdatenbank an, befuellt
sie so, dass jedes Feld einmal belegt ist (Tags, Uebertrag am Stueck,
verknuepfter Uebertrag, Vertragszuordnung, gebundenes Konto, jaehrliches Abo)
und schreibt 15 echte Antworten nach ios/Tests/Fixtures/antworten.json. Der
Swift-Test "Serverantworten" entschluesselt daraus jedes Modell.
Nach jeder Schema-Aenderung neu laufen lassen und das Ergebnis mit committen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Backend-Ergaenzungen B und C sind gebaut; Stufe 2 hat damit alles, was sie
braucht. Offen bleibt D, die zwei Kopier-Luecken im Budget.
Dazu berichtigt: Der Testbefehl zeigte auf einen Windows-Pfad, und die zwei
Tests, die als "vorbekannt rot" galten, laufen auf diesem Rechner durch - das
war ein Umgebungsproblem der alten Maschine. Erwartung ist jetzt: 438 Tests,
alle gruen. Wer einen roten sieht, hat ihn verursacht.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
GET /api/budget/template gab je Zeile nur planned_cents heraus, obwohl das
Modell modus und zuschlag_cents fuehrt und PUT sie zurueckgibt. Wer die
Vorlage so liest, haelt eine berechnete Zeile fuer eine feste - und schreibt
sie beim naechsten Speichern auch als feste zurueck.
Die Weboberflaeche traf es nicht, weil sie die Betriebsart aus der
Monatsansicht nimmt. Die iOS-App bietet beide Richtungen an und haette den
Fehler sichtbar gemacht.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Drei neue Abfrageparameter an GET /api/transactions: q sucht in Empfaenger,
Verwendungszweck und Notiz, tag_id filtert auf ein Etikett, subscription_id
auf einen Vertrag. Bisher gab es das nur im Browser - die Weboberflaeche laedt
alle Buchungen und siebt sie dort. Am Telefon ist das der Unterschied
zwischen "laedt" und "benutzbar"; das Limit steht nicht umsonst auf 5000.
Zwei Dinge, die die Tests festhalten: Prozent- und Unterstrich-Zeichen im
Suchtext sind Zeichen, keine Platzhalter - sonst faende die Eingabe "50%"
alles. Und ein fremder tag_id liefert nichts statt alles, weil der Filter auf
TransactionTag.owner_id anschlaegt.
Der Tag-Filter nimmt eine Unterabfrage wie der uncategorized-Zweig daneben,
keinen Join: Ein Join gaebe jede Buchung so oft zurueck, wie sie Tags traegt.
Die lokale Abfragevariable hiess q und heisst jetzt abfrage, damit der
Parameter den Namen bekommen konnte.
Bekannte Grenze: SQLite gleicht bei LIKE nur ASCII gross/klein ab, "Mueller"
mit Umlaut findet also nicht jede Schreibweise.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die wichtigste Aussage der Uebergabe war ueberholt: auf dieser Maschine liegt
Xcode 27, Stufe 1 ist gebaut, getestet und im Simulator gestartet. Wer das
nicht weiss, schreibt weiter blind und hinterlaesst dem Nutzer Fehler.
Dazu neu festgehalten: der Befehl nach jeder neuen Datei (xcodegen generate),
was beim Bauen auffiel (nur Swift 5, nicht Swift 6), und der naechste
Stolperstein - App Transport Security laesst nur https zu, auch kein
http://localhost. Ueber die Verbindung geht die Geraetemarke, ein Vollzugang;
die Ausnahme ist deshalb eine Entscheidung des Nutzers, keine Nebensache.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Bisher musste das Xcode-Projekt von Hand angelegt werden, und der Swift-Code
wurde blind geschrieben, ohne ihn je zu uebersetzen. ios/project.yml
beschreibt das Projekt jetzt in 50 Zeilen; xcodegen generate macht daraus
Certain.xcodeproj. Das erzeugte Projekt liegt mit im Repository, damit man es
ohne xcodegen einfach oeffnen kann - geaendert wird aber die project.yml.
Festgelegt nach dem Muster von Vorrania: Buendelkennung com.scarriffle.certain,
Team PP34X97WS3, iPhone und iPad, Entwicklungssprache Deutsch, iOS 26 als
Mindestversion. Sprachfassung Swift 5, weil der Code nicht auf die strikte
Nebenlaeufigkeitspruefung von Swift 6 hin geschrieben ist.
Stufe 1 uebersetzt damit fehlerfrei, die 28 Tests laufen durch, und die App
startet im Simulator bei der Serverabfrage.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der Vorgabewert von weitereFelder in schickeDatei stand auf [], was Swift als
leeres Array liest. Der einzige Uebersetzungsfehler in den 2600 blind
geschriebenen Zeilen der Stufe 1.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der Plan lag bisher ausserhalb des Repositories und waere beim Geraetewechsel
verloren gewesen. ios/UEBERGABE.md haelt jetzt alles fest: die Entscheidungen
des Nutzers, die Eigenheiten des Backends (Cookie-Anmeldung, Zeitstempel ohne
Zone, Uebertraege in zwei Mechanismen), was Stufe 1 enthaelt, was die drei
offenen Backend-Ergaenzungen sind und was die Stufen 2 bis 7 bringen.
Dazu die Konventionen des Projekts und die Fallen, die mich Zeit gekostet
haben - damit sie niemand zweimal findet.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Neuer Ordner ios/ mit dem Geruest: Netzwerkschicht, Modelle, Offline-Ablage,
Texte in Deutsch und Englisch, Navigation, der komplette Anmeldeweg (Server
waehlen, anmelden, zweiter Faktor, Pflicht-Passwortwechsel) und als erste
Fachansicht die Kontenliste.
Die Anmeldung laeuft einmal ueber das Sitzungs-Cookie, holt sich dabei eine
App-Marke und legt sie in den Schluesselbund. Danach traegt nur noch der
Header. Kein Passwort auf dem Geraet, kein Ablauf nach 30 Tagen.
Der Code ist nie uebersetzt worden - hier gibt es kein Xcode. Geprueft ist
dafuer jedes Modellfeld gegen echte Antworten des laufenden Backends; dabei
kam heraus, dass created_at ohne Zeitzone und mit Mikrosekunden kommt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die API kannte zwei Zustaende: angemeldet per Sitzungs-Cookie, oder eine enge
Kurzbefehl-Marke, die nur erfassen darf. Eine native App passt in keinen davon
- sie kann kein Cookie-Formular ausfuellen und soll das Passwort nicht auf dem
Geraet halten.
Neu ist die Reichweite "app": Sie gilt im Header X-Certain-Token wie eine
Anmeldung, laeuft nicht nach 30 Tagen ab und laesst sich einzeln widerrufen.
Die enge Marke bleibt eng - das ist der Punkt, an dem die Tests am genauesten
hinsehen. Und eine App-Marke darf keine weitere ausstellen: Sonst stellte sich
ein Dieb Ersatzschluessel aus und das Widerrufen liefe ins Leere.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Mit Notiz und Tags ist die Buchungsliste breiter als der Bildschirm geworden.
Die Knopfspalte scrollte damit einfach aus dem Bild - und weil sie in einer
gespeicherten Spaltenreihenfolge auch noch mittendrin stand, war sie gar
nicht mehr zu finden. "fixed" hiess bisher nur "nicht verschiebbar", nicht
"bleibt stehen".
Angeheftete Spalten (neu: pin) kleben jetzt am rechten Rand und ruecken
immer ans Ende, egal was gespeichert ist.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Bisher musste man dafuer die ganze Buchung aufmachen. Jetzt steht in der
Notiz-Spalte ein Feld: hineinschreiben, Enter oder wegklicken - fertig.
Escape nimmt die Aenderung zurueck, leeren loescht die Notiz.
Und sie steht nicht mehr doppelt da. Die dritte Zeile unter der Buchung
zeigte dieselbe Notiz wie die neue Spalte; sie faellt weg, der
Verwendungszweck darueber bleibt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
source-map-js 1.2.1 -> 1.2.2 (GHSA-68fv-2mgg-jv7q). Eine reine
Build-Abhaengigkeit ueber Vite und PostCSS, sie landet nie im Browser.
Die beiden verbleibenden Meldungen betreffen react-router und waeren nur mit
dem Sprung auf Version 7 zu beheben. Beide greifen hier nicht: Der offene
Redirect braucht ein Navigationsziel aus Nutzereingaben, es gibt aber nur
feste Pfade; die zweite betrifft SSR-Hydration, und die App ist ein reiner
SPA ohne SSR.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Notiz stand nur im Bearbeiten-Dialog, obwohl man sie schreibt, um sie
spaeter wiederzufinden. Jetzt hat sie eine eigene Spalte mit eigenem Filter;
der ganze Text steht im Tooltip. Das Feld im Dialog ist wieder einzeilig und
laesst sich bei Bedarf aufziehen.
Statt einer Einfachauswahl gibt es drei Haken: Ausgaben, Einnahmen,
Uebertraege. Man will oft zwei Arten gleichzeitig sehen und eine dritte
weglassen.
Und "nur Einnahmen" zeigt keine Uebertraege mehr. Eine verknuepfte Buchung ist
weder Ausgabe noch Einnahme, sie verschiebt nur Geld - nach ihrem rohen Typ
gefiltert stand sie trotzdem unter den Einnahmen, und weil der Server nur eine
Seite des Paares lieferte, sogar einzeln statt als eine Zeile.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Kategorie beantwortet "wofuer war das Geld" und gilt genau einmal. Alles,
was quer dazu liegt – "Urlaub Island", "absetzbar", "zurueckfordern" –, hatte
bisher keinen Platz, weil eine Buchung davon mehrere auf einmal sein kann.
Tags werden auf einer eigenen Seite angelegt, jeder mit seiner Farbe. Gesetzt
werden sie an der Buchung: direkt in der Liste oder beim Bearbeiten. Die
Spalte laesst sich filtern, auch nach mehreren Tags gleichzeitig.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Das feste Zielkonto eines Transfer-Schlagworts behielt beim Wiederherstellen
die alte Konto-Nummer. Die Nummern werden dabei aber neu vergeben, also lief
es ins Leere – und zwar so, dass die GANZE Wiederherstellung mit einem
Fremdschluessel-Fehler abbrach, nicht nur diese eine Zeile. Wer je ein
Zielkonto gewaehlt hatte, kam an seine Sicherung nicht mehr heran.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Zwischen zwei Konten liess sich kein zweiter Uebertrag ueber denselben
Betrag buchen. Die Begruendung stimmte fuer den haeufigen Fall: Beide
Seiten kommen oft getrennt herein, einmal von der Bank, einmal aus der
Datei – wer beide einzeln verknuepft, verschiebt das Geld zweimal.
Nur ist sie nicht immer richtig. Revolut-Aufrundungen sind reihenweise
CHF 1.00 zwischen demselben Paar in denselben Tagen, und die sind alle
echt. Als einziger Ausweg blieb, den vorhandenen Uebertrag zu loeschen.
Jetzt wird gefragt statt verboten: dieselbe Warnung, aber ueber
"trotzdem" zu uebergehen – der Weg, den alle anderen Pruefungen hier
schon nehmen. Entscheiden muss das der Nutzer; die Regel kann es nicht
wissen.
Dabei fiel auf, dass die Rueckfrage HINTER dem Fenster lag, das sie
aufgeworfen hat: Modal rendert per Portal an den Koerper und liegt damit
spaeter im Baum, bei gleicher Ebene gewinnt der spaetere. Eine
Rueckfrage gehoert ueber das, wonach sie fragt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Liste sagt ausdruecklich, dass sie nichts entscheidet: Drei
Aufrundungen ueber 1.00 am selben Tag sind echt, drei gleiche Einkaeufe
koennen es auch sein – das weiss nur der Nutzer. Dann darf sie aber auch
nicht zu einer Entscheidung draengen, und genau das tat sie: Der einzige
Knopf war der Papierkorb. Wer die Meldung loswerden wollte, musste eine
richtige Buchung wegwerfen.
Jetzt steht neben jeder Gruppe "Alle behalten". An den Buchungen aendert
sich dabei nichts; es ist ein Haken an der Frage.
Der Haken gilt dem, was man gesehen hat – nicht allem, was noch kommt:
Eine Gruppe faellt erst weg, wenn ALLE ihre Zeilen ihn tragen. Kommt
spaeter eine dritte gleich aussehende Buchung dazu, steht die Frage
wieder da, und zwar fuer alle drei. Ein Test haelt das fest.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Das native Dropdown fiel neben der Kategorie-Auswahl auseinander: anderer
Stil, kein Suchfeld, kein Symbol, und die Vertraege standen in
Anlagereihenfolge. Bei zwanzig Abos sucht man einen bestimmten, und die
Reihenfolge der Eingabe kennt niemand.
Jetzt dieselbe Auswahl wie bei der Kategorie. CategorySelect bekommt dafuer
zwei kleine Zugaenge statt eines zweiten Dropdowns: ein eigenes Symbol je
Eintrag (renderMarke) und eine Gruppe ohne Namen, die ohne Kopfzeile
auskommt – die Vertraege haben keine Gruppen. Alles andere, von der
Suche bis zum Schliessen beim Scrollen, bleibt eine Mechanik.
Das Logo schlaegt dabei das Symbol: Netflix erkennt man am roten N, nicht
am Wort. Die Fallunterscheidung dafuer stand schon in der Abo-Liste und
ist jetzt eine Komponente, die sich beide teilen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Erkennung lief seit Stufe 3, aber nur automatisch: Was sie nicht
sicher traf, blieb liegen, und sehen konnte man davon nichts. Die
Endpunkte gab es, die Oberflaeche kannte keinen davon.
Jetzt steht in der Buchungsliste eine Spalte "Abo" – dieselbe Form wie
die Kategorie daneben: Die Auswahl zeigt, zu welchem Vertrag die Zahlung
gehoert, aendert es, und leer loest die Zuordnung. Uebertraege bleiben
aussen vor; sie verschieben nur Geld.
Und sie lernt. Musste man von Hand ran, hat der Erkennungstext nicht
gepasst – also wird der Empfaenger dieser Buchung der neue. Beim
naechsten Mal faellt dieselbe Zahlung von selbst zu, statt dass man
jeden Monat dasselbe klickt. Der Vertragsname bleibt als zweite Chance:
_passt_text prueft beide, nicht entweder/oder, sonst verlaere man beim
Lernen, was vorher schon getroffen hat.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Kontostands-Karte zeigte die Saeule 3a wie jedes andere Konto: ein
Name, ein Betrag. Dass man an das Geld nicht herankommt, stand nur in
der Summe darunter – und wer den Betrag liest, rechnet ihn im Kopf doch
mit. Die Zeile traegt jetzt dieselbe Marke wie auf der Kontenseite.
Dabei ist ein Fehler aufgefallen, den ich selbst eingebaut hatte: Die
Zeile "zusaetzlich gebunden" stand im Markup HINTER der Summe. Die
klebt per position:sticky am unteren Rand des Rollbereichs – alles
dahinter liegt darunter und ist praktisch nie zu sehen. Summe und
gebundener Betrag stehen jetzt gemeinsam im angehefteten Fuss.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Drei Dinge, die zusammengehoeren.
1. Uebertraege sind als solche lesbar.
Stand nur eine Seite in der Liste – weil nach einem Konto gefiltert wird
oder die andere nicht mitgeladen war – trug die Zeile den Rohtext der
Bank als Ueberschrift. "To CHF MB:8f33c0aa-9c92-456b-a6fe-b64a858a019c"
sah aus wie eine Zahlung an jemanden; die Verknuepfung stand klein
darunter. Jetzt steht oben, zwischen WELCHEN Konten das Geld lief, in
beiden Faellen gleich, und daneben eine Marke "Transfer". Ein Uebertrag
ist weder Ausgabe noch Einnahme – das gehoert in die erste Zeile, nicht
in eine Fussnote.
2. Notiz je Buchung.
Das Feld gab es im Modell seit jeher, die Oberflaeche bot es nie an. Die
Kategorie sagt, in welche Schublade etwas gehoert – nicht, WAS es war.
"Amazon" kann Einrichtung sein, Werkzeug oder ein Geschenk, und in drei
Monaten weiss das niemand mehr. Die Notiz steht in der Liste und ist
mitdurchsuchbar: Wer sie schreibt, will spaeter danach suchen.
3. "Ignorieren" ist keine Einbahnstrasse mehr.
Ein abgelehntes Paar blieb bei jedem weiteren Suchlauf stumm, und nichts
verriet, dass es je einen Vorschlag gab. Wer einmal falsch klickte,
suchte danach einen Fehler in der Erkennung, den es nicht gab. Die
abgelehnten Paare lassen sich jetzt ansehen und zurueckholen.
Und wenn die Vorschlaege nicht geladen werden koennen, wird das gesagt
statt verschluckt – sonst sieht die Liste aus, als gaebe es nichts zu
verbinden.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Eine Saeule 3a ist gesperrt – bis zur Rente, zur Selbstaendigkeit oder
zur Auswanderung. Bisher zaehlte ihr Stand in dieselbe Summe wie das
Girokonto, und die Antwort auf "wie viel habe ich" war um genau diesen
Betrag zu hoch. Das Geld steht da und ist doch nicht da.
Konten lassen sich jetzt als gebunden kennzeichnen. "Gesamt" zeigt dann
nur noch, was wirklich ausgegeben werden kann; darunter steht
"zusaetzlich CHF 2'004.61 gebunden" – aufklappbar, mit jedem Konto
einzeln. Eine Summe, die man nicht nachrechnen kann, muss man glauben.
Verschwiegen wird nichts: Der Saldo bleibt am Konto stehen, und
Vermoegensverlauf wie Nettovermoegen rechnen weiter damit. Es nicht
ausgeben zu koennen heisst nicht, es nicht zu besitzen – das ist der
ganze Unterschied, und ein Test haelt ihn fest.
Ein eigenes Kennzeichen, keine Ableitung aus dem Kontotyp: Ein frei
verkaeufliches Depot und eine gesperrte Saeule 3a kommen beide als
"depot" herein. Am Typ laesst sich das nicht ablesen – am
ausdruecklichen Eintrag schon.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der Abgleich wird einmal serverseitig gerechnet, beim Erzeugen der
Vorschau. Wer danach eine Zeile von Hand umstellt, erreicht ihn nicht –
das gruene "stimmt" blieb stehen, obwohl es sich auf eine andere
Leserichtung bezog.
Mit der neuen Sammelaktion "Richtung umdrehen" waere das leicht
passiert: ein Klick, und die Bestaetigung darueber stimmt nicht mehr.
Nachgerechnet wird nicht – das waere eine zweite Wahrheit neben der
ersten. Stattdessen sagt der Hinweis, dass seine Aussage veraltet ist.
Beim Einlesen kommt die Art aus dem Vorzeichen; jede Abweichung ist
also nachweislich eine Handaenderung.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Bisher standen alle Einstellungen offen nebeneinander – auch die, die
Certain laengst erkannt hatte. Rund zwanzig Bedienelemente fuer einen
Vorgang, bei dem im Normalfall keine einzige Entscheidung ansteht.
Jetzt: Datei waehlen, Pruefansicht. Dazwischen nichts.
- Format und Spaltenzuordnung klappen ein, solange die Erkennung sicher
ist. Ein Klick auf "Alle Einstellungen anzeigen" holt sie zurueck –
samt der Begruendung, WAS erkannt wurde und warum die Vorschau so
aussieht, wie sie aussieht. Wer nachsehen will, kann; muessen soll
niemand.
- Die Erkennungs-Meldung steht jetzt unter der Datei, auf die sie sich
bezieht, statt weit unten zwischen den Spaltenfeldern.
- "Vorschau erstellen" entfaellt. Der Knopf tat genau das, was ohnehin
als naechstes kommen musste; die Vorschau laeuft jetzt von selbst,
entprellt, sobald Datum und Betrag zugeordnet sind. Fehlt etwas, steht
da, was fehlt.
- "Als Vorlage speichern" ist keine primaere Aktion mehr. Das ist Pflege
und sah aus wie eine Entscheidung, die jetzt ansteht.
Der Zeilenfilter klappt ausdruecklich NICHT mit ein. Revolut legt alle
Pockets in eine Datei; ohne Filter zaehlen die Bewegungen doppelt. Das
ist kein Detail fuer Fortgeschrittene, sondern die Bedingung dafuer,
dass die Zahlen stimmen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Eine Kreditkarte bekommt Geld nur aus einem Grund: weil ihre Rechnung
bezahlt wird. Das genuegt als Hinweis – und es wird gebraucht, denn im
Buchungstext steht bei so einer Zahlung oft weder ein Schlagwort noch
der Name des Gegenkontos. Die Erkennung fand das Paar deshalb, liess es
aber als Vorschlag liegen.
Entscheidend ist die RICHTUNG, nicht dass eine Karte beteiligt ist. Die
naheliegende Fassung "Kartenkonto auf einer Seite" haette einen
Karteneinkauf ueber 50,00 mit einer zufaelligen Gutschrift ueber 50,00
am selben Tag verknuepft – eine echte Ausgabe UND eine echte Einnahme
waeren still aus der Auswertung gefallen. Genau davor schuetzt die
Huerde, die hier gelockert wird. Ein Test haelt die Gegenprobe fest.
Die Schlagwortliste bleibt unberuehrt: "visa" und "mastercard" stehen in
gewoehnlichen Buchungstexten jedes Kontos.
Und der Import sagt jetzt, was er getan hat. Bisher lief die Suche nach
dem Buchen still; wer wissen wollte, ob die Kartenrechnung erkannt
wurde, musste auf "Zu erledigen" wechseln – und ahnte nicht, dass es da
etwas zu sehen gab. Was nicht automatisch geht, erscheint als Vorschlag
direkt auf der Import-Seite.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Auf einem Kartenauszug ist +6,00 eine AUSGABE und -10,00 die Zahlung der
Rechnung. Certain kannte diese Leserichtung nicht. Der scheinbare Ausweg
"Ein Betrag (immer Ausgabe)" machte auch die Rechnungszahlung und jede
Gutschrift zur Ausgabe.
Neuer Betragsmodus "signed_inverted". Er dreht Betrag UND Saldospalte:
Ein Auszug, der einen Einkauf als +6 fuehrt, schreibt auch den Saldo so –
die Schuld steigt. Wer nur den Betrag dreht, bekommt bei jeder einzelnen
Zeile eine Abweichung gemeldet, und die einzige Kontrollinstanz des
Imports wird wertlos. Dazu gibt es jetzt einen Test.
Waehlen muss den Modus niemand: /imports/detect erkennt jetzt auch das
KONTO – erkenne_konto konnte das laengst, benutzt hat es nur der
Telefon-Kurzbefehl. Ist das erkannte Konto eine Kreditkarte, wird die
Leserichtung mitgesetzt. Eng gefasst: nur bei einer Betragsspalte, nur
aus "signed" heraus, nur bei sicher erkanntem Konto. Und nie still – in
der Pruefansicht steht, dass umgekehrt gelesen wird, und wo man es
aendert.
Drei Dinge, die dabei auffielen:
- Der Abgleich rechnete mit dem Vorzeichen, gebucht wird aber nach Art
(abs(Betrag) + Ausgabe/Einnahme). Wer eine Zeile umstellte, pruefte
eine Buchung, die so nie entsteht.
- Dieselbe Luecke in der Vorschau: Die Betragsspalte faerbte nach dem
Rohvorzeichen. Ein Umstellen aenderte sichtbar nichts.
- Auf Kartenauszuegen heisst die Empfaengerspalte "Haendler", und viele
fuehren die eigene Kontonummer in jeder Zeile mit. Die gewann die
Spaltenwahl ueber die Textlaenge – heraus kam eine Liste, in der bei
jeder Buchung dasselbe stand.
Neu ausserdem eine Summenzeile je Richtung und eine Sammelaktion: Ist
eine Datei durchgehend verkehrt herum gelesen, sind alle Zeilen
betroffen, und bei 150 Positionen ist Einzelkorrektur keine Moeglichkeit.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>