docs: Uebergabe, README und Fahrplan halten Stufe 3 fest

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>
This commit is contained in:
Guido
2026-10-21 21:07:06 +02:00
co-authored by Claude Opus 5
parent 82db09e6de
commit d04b6b74ad
3 changed files with 93 additions and 31 deletions
+6 -5
View File
@@ -52,10 +52,11 @@ zweispaltig, offline der letzte Stand mit Datum.
Stufe 1 ✅: Gerüst, Netzwerkschicht, Anmeldung samt zweitem Faktor und
Pflicht-Passwortwechsel, Kontenliste. Stufe 2 ✅: Übersicht, Budget-Monat,
Buchungsliste mit Suche, Vertragsliste. Übersetzt fehlerfrei, 44 Tests grün,
läuft im Simulator; das Xcode-Projekt entsteht aus `ios/project.yml`, die
Modelle werden gegen echte Serverantworten geprüft.
Stufen 3–7 (Schreiben, Arbeitsliste, Nebenräume, Erweiterungen, voller
CSV-Import) stehen aus.
Buchungsliste mit Suche, Vertragsliste. Stufe 3 ✅: Buchungen, Budget-Zeilen,
Verträge und Konten anlegen, ändern und löschen. Übersetzt fehlerfrei,
49 Tests grün, läuft im Simulator; das Xcode-Projekt entsteht aus
`ios/project.yml`, die Modelle werden gegen echte Serverantworten geprüft.
Stufen 4–7 (Arbeitsliste, Nebenräume, Erweiterungen, voller CSV-Import)
stehen aus.
**Stand, Entscheidungen und offene Punkte: `ios/UEBERGABE.md`.**
+3 -3
View File
@@ -41,13 +41,13 @@ Vollzugang. Wer eine Ausnahme braucht, trägt sie bewusst in `project.yml` ein.
| `App/` | Einstiegspunkt, Navigation, globaler Zustand |
| `Netzwerk/` | Client, Endpunkte, Fehler, Offline-Ablage |
| `Modelle/` | Codable-Typen, eins zu eins zu den Backend-Schemas |
| `Bausteine/` | Wiederverwendetes: Geld, Farben, Symbole, Zustandsansichten |
| `Bausteine/` | Wiederverwendetes: Geld, Farben, Symbole, Zustandsansichten, Formularfelder |
| `Anmeldung/` | Server-Adresse, Anmeldung, zweiter Faktor, Passwortwechsel |
| `Dashboard/` | Die Übersicht: vier feste Karten |
| `Budget/` | Soll gegen Ist, Monat für Monat |
| `Buchungen/` | Buchungsliste mit Suche und Filtern |
| `Buchungen/` | Buchungsliste mit Suche, Formular zum Anlegen und Ändern |
| `Abos/` | Verträge und Abos |
| `Konten/` | Kontenliste |
| `Konten/` | Kontenliste und Konto-Formular |
| `Texte/` | Übersetzungen, Deutsch und Englisch zeilengleich |
| `Tests/` | Logiktests ohne Netz, plus die Modellprüfung gegen `Fixtures/antworten.json` |
+84 -23
View File
@@ -6,7 +6,7 @@ haben: was entschieden ist, was fertig ist, was als Nächstes dran ist, und was
ich über das Backend herausgefunden habe. Lies sie ganz, bevor du anfängst —
die Abschnitte „Was das Backend vorgibt" und „Fallen" ersparen dir Tage.
Stand: 21.10.2026, Commit `ffe1870`. Alles committet und gepusht.
Stand: 21.10.2026, Commit `82db09e`. Alles committet und gepusht.
---
@@ -173,8 +173,8 @@ umgestellt, weil sie so funktioniert. Die App soll die Filter benutzen.
## 4. Backend-Ergänzungen
Vier geplant. **A, B und C sind fertig**, D steht aus. Jede einzeln dem Nutzer
vorlegen, bevor du sie schreibst.
Vier geplant, **alle vier sind fertig**. Falls du eine fünfte brauchst: einzeln
dem Nutzer vorlegen, bevor du sie schreibst.
### A — App-Token ✅ (Commit `8a7545b`)
@@ -225,17 +225,24 @@ zeigt eine berechnete Zeile als feste **und schreibt sie beim nächsten
Speichern auch als feste zurück**. Die Weboberfläche traf es nicht, weil sie
die Betriebsart aus der Monatsansicht nimmt.
### D — Zwei Kopier-Lücken im Budget (offen)
### D — Zwei Kopier-Lücken im Budget ✅
`copy_template_to_month` und `template_from_month` in
`backend/app/services/budget.py` übernehmen nur `planned_cents`. Ein
„berechneter" Eintrag fällt beim Kopieren still auf „fest" zurück. Das ist ein
**bestehender Fehler**, kein App-Thema — aber die App macht ihn sichtbar, weil
sie beide Richtungen anbietet. Nötig für Stufe 3.
`copy_template_to_month` und `copy_month_to_template` in
`backend/app/services/budget.py` übernahmen nur `planned_cents`. Ein
„berechneter" Eintrag fiel beim Kopieren still auf „fest" zurück — mit der
Zahl, die zufällig zuletzt in `planned_cents` stand.
`ensure_customized` nebenan machte es längst richtig; die zwei Kopierer ziehen
jetzt nach. Dabei kam eine Frage auf, die vorher niemand stellen musste: Eine
**berechnete** Zeile gehört auch mit `planned_cents == 0` in die Vorlage, ihre
Zahl steht ja in den Verträgen. Eine **feste** Null bleibt draussen, sonst
füllte sich die Vorlage mit Nullzeilen für jede je berührte Kategorie.
Drei Tests in `test_budget.py` halten beide Richtungen und die Null fest.
---
## 5. Was fertig ist — Stufen 1 und 2 ✅
## 5. Was fertig ist — Stufen 1 bis 3 ✅
### Stufe 1 (Commit `6b86a6b`)
@@ -335,20 +342,72 @@ committen:
cd backend && .venv/bin/python -m scripts.erzeuge_ios_fixture
```
Wer ein Modell für Stufe 3 ergänzt, nimmt seinen Endpunkt in `PFADE` auf und
schreibt eine Zeile im Test dazu. Das ist die einzige Prüfung, die einen
vertippten Feldnamen fängt — der übersetzt anstandslos.
Wer ein Modell ergänzt, nimmt seinen Endpunkt in `PFADE` auf und schreibt eine
Zeile im Test dazu. Das ist die einzige Prüfung, die einen vertippten
Feldnamen fängt — der übersetzt anstandslos.
### Stufe 3 — Schreiben ✅ (Commit `82db09e`)
Buchungen, Budget-Zeilen, Verträge und Konten lassen sich anlegen, ändern und
löschen.
| Datei | Inhalt |
|---|---|
| `Modelle/Eingaben.swift` | Was zum Server hinausgeht |
| `Bausteine/Formular.swift` | Speichervorgang, Betragsfeld, Auswahlfelder |
| `Buchungen/BuchungFormular.swift` | Anlegen **und** Ändern in einer Maske |
| `Budget/BudgetZeileBearbeiten.swift` | Monat oder Vorlage, dieselbe Maske |
| `Abos/AboFormular.swift`, `Konten/KontoFormular.swift` | ebenso |
**Ein Formular je Sache, nicht zwei.** Anlegen und Ändern teilen sich die
Maske; was sich unterscheidet, steht in einem `Zweck`-Typ. Zwei Masken
nebeneinander wären genau die Stelle, an der sie auseinanderlaufen.
#### Vier Regeln, die man nicht sieht, aber merkt, wenn sie fehlen
1. **Eine Teiländerung schickt nur, was gesetzt ist.** Ginge ein nicht
angefasstes Feld als `null` hinaus, löschte jedes Speichern den Empfänger,
die Notiz und die Kategorie gleich mit. Swift macht das von selbst
(`encodeIfPresent` für Optionals) — der Test dazu bleibt trotzdem stehen,
die Eigenschaft ist zu wichtig für eine ungeprüfte Spracheigenheit.
2. **Eine leere Tag-Liste geht sehr wohl mit.** Sie heisst „alle abnehmen" und
darf nicht mit „nicht angefasst" zusammenfallen.
3. **Ein Übertrag bekommt keine Kategorie.** Das Geld wechselt nur die Tasche.
4. **Bei „berechnet" bleibt `planned_cents` unberührt.** Das Backend liest es
dann nicht, und es zu überschreiben verlöre den Wert, auf den ein
Zurückschalten auf „fest" zurückfallen soll.
Die **Währung eines bestehenden Kontos** lässt sich nicht ändern — jeder
gespeicherte Betrag würde danach etwas anderes bedeuten. Das Backend erlaubt
es; die App nicht.
#### Fehlende Texte findet dieser Befehl
`String(localized:)` mit unbekanntem Schlüssel übersetzt anstandslos und zeigt
dem Nutzer den Schlüssel. Vor dem Committen:
```
cd ios && comm -23 \
<(grep -rhoE 'localized: "[a-zA-Z0-9._]+"' --include="*.swift" . | sed 's/localized: "//; s/"//' | sort -u) \
<(grep -oE '^"[a-zA-Z0-9._]+"' Texte/de.lproj/Localizable.strings | tr -d '"' | sort -u)
```
Gibt er nichts aus, ist nichts offen. (Ein Schlüssel, der nur in einem
`? :` steckt, taucht fälschlich als „fehlend" nicht auf — das ist die eine
Lücke des Befehls.) Dass beide Sprachfassungen dieselben Schlüssel tragen,
prüft ein `diff` der beiden `grep`-Ausgaben.
---
## 6. Was als Nächstes dran ist
**Stufe 3 ist dran.** Davor liegt nur noch Backend-Ergänzung **D** (unten),
die zwei Kopier-Lücken im Budget.
**Stufe 4 ist dran.** Alle vier Backend-Ergänzungen sind drin; von hier an
reicht die API, wie sie ist.
**Stufe 3 — Schreiben.** Buchungen anlegen und ändern, Budget bearbeiten
(Monat und Vorlage, beide Betriebsarten `fest`/`berechnet`), Abos verwalten
samt Logo, Konten bearbeiten. Dazu Backend-Ergänzung **D**.
Was aus Stufe 3 bewusst nicht mitkam: das **Logo** eines Vertrags (Upload und
Zuschnitt, `POST /subscriptions/{id}/logo`) und die **Reihenfolge** der Konten
(`PUT /accounts/order`). Beides ist am Rechner bequemer und hielt niemanden
auf.
**Stufe 4 — Arbeitsliste.** Eingang mit Buchen, Verwerfen, Kategorie zuordnen,
Übertragsvorschlägen und Dubletten. Fachlich der Teil, der am besten aufs
@@ -372,12 +431,14 @@ Vorlagen, Verlauf mit Rücknahme.
### Zum Umfang, ehrlich
Die Weboberfläche hat rund 8900 Zeilen JSX. Nativ wird das nicht weniger.
Stufe 2 hat rund 1800 Zeilen gebraucht; Stufen 3 bis 6 zusammen sind grob
zwei bis drei Wochen, Stufe 7 nochmal zwei bis drei.
Stufe 2 hat rund 1800 Zeilen gebraucht, Stufe 3 rund 1200; Stufen 4 bis 6
zusammen sind grob ein bis zwei Wochen, Stufe 7 nochmal zwei bis drei.
Stufe 3 wird die aufwendigste der verbliebenen: Schreiben heisst Formulare,
Fehlerbehandlung je Feld und der 409-Fall („nachfragen", oft mit
`trotzdem=true` wiederholbar). Lesen war dagegen geradeaus.
Der **409-Fall** („nachfragen", mit `trotzdem=true` wiederholbar) kam in
Stufe 3 nicht vor — er steckt nur an
`POST /transactions/{id}/transfer`, also in Stufe 4. Dort wird er gebraucht:
Die App muss den Hinweis zeigen und den Aufruf auf Wunsch wiederholen,
statt den Fehler bloss zu melden.
Stufe 7 steht bewusst am Ende. Alles davor ist für sich nutzbar. Der Ablauf
hat **keinen Server-Zustand** zwischen den Schritten: Die Datei wird bei jedem