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 Stufe 1 ✅: Gerüst, Netzwerkschicht, Anmeldung samt zweitem Faktor und
Pflicht-Passwortwechsel, Kontenliste. Stufe 2 ✅: Übersicht, Budget-Monat, Pflicht-Passwortwechsel, Kontenliste. Stufe 2 ✅: Übersicht, Budget-Monat,
Buchungsliste mit Suche, Vertragsliste. Übersetzt fehlerfrei, 44 Tests grün, Buchungsliste mit Suche, Vertragsliste. Stufe 3 ✅: Buchungen, Budget-Zeilen,
läuft im Simulator; das Xcode-Projekt entsteht aus `ios/project.yml`, die Verträge und Konten anlegen, ändern und löschen. Übersetzt fehlerfrei,
Modelle werden gegen echte Serverantworten geprüft. 49 Tests grün, läuft im Simulator; das Xcode-Projekt entsteht aus
Stufen 3–7 (Schreiben, Arbeitsliste, Nebenräume, Erweiterungen, voller `ios/project.yml`, die Modelle werden gegen echte Serverantworten geprüft.
CSV-Import) stehen aus. Stufen 4–7 (Arbeitsliste, Nebenräume, Erweiterungen, voller CSV-Import)
stehen aus.
**Stand, Entscheidungen und offene Punkte: `ios/UEBERGABE.md`.** **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 | | `App/` | Einstiegspunkt, Navigation, globaler Zustand |
| `Netzwerk/` | Client, Endpunkte, Fehler, Offline-Ablage | | `Netzwerk/` | Client, Endpunkte, Fehler, Offline-Ablage |
| `Modelle/` | Codable-Typen, eins zu eins zu den Backend-Schemas | | `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 | | `Anmeldung/` | Server-Adresse, Anmeldung, zweiter Faktor, Passwortwechsel |
| `Dashboard/` | Die Übersicht: vier feste Karten | | `Dashboard/` | Die Übersicht: vier feste Karten |
| `Budget/` | Soll gegen Ist, Monat für Monat | | `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 | | `Abos/` | Verträge und Abos |
| `Konten/` | Kontenliste | | `Konten/` | Kontenliste und Konto-Formular |
| `Texte/` | Übersetzungen, Deutsch und Englisch zeilengleich | | `Texte/` | Übersetzungen, Deutsch und Englisch zeilengleich |
| `Tests/` | Logiktests ohne Netz, plus die Modellprüfung gegen `Fixtures/antworten.json` | | `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 — ich über das Backend herausgefunden habe. Lies sie ganz, bevor du anfängst —
die Abschnitte „Was das Backend vorgibt" und „Fallen" ersparen dir Tage. 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 ## 4. Backend-Ergänzungen
Vier geplant. **A, B und C sind fertig**, D steht aus. Jede einzeln dem Nutzer Vier geplant, **alle vier sind fertig**. Falls du eine fünfte brauchst: einzeln
vorlegen, bevor du sie schreibst. dem Nutzer vorlegen, bevor du sie schreibst.
### A — App-Token ✅ (Commit `8a7545b`) ### 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 Speichern auch als feste zurück**. Die Weboberfläche traf es nicht, weil sie
die Betriebsart aus der Monatsansicht nimmt. 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 `copy_template_to_month` und `copy_month_to_template` in
`backend/app/services/budget.py` übernehmen nur `planned_cents`. Ein `backend/app/services/budget.py` übernahmen nur `planned_cents`. Ein
„berechneter" Eintrag fällt beim Kopieren still auf „fest" zurück. Das ist ein „berechneter" Eintrag fiel beim Kopieren still auf „fest" zurück — mit der
**bestehender Fehler**, kein App-Thema — aber die App macht ihn sichtbar, weil Zahl, die zufällig zuletzt in `planned_cents` stand.
sie beide Richtungen anbietet. Nötig für Stufe 3.
`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`) ### Stufe 1 (Commit `6b86a6b`)
@@ -335,20 +342,72 @@ committen:
cd backend && .venv/bin/python -m scripts.erzeuge_ios_fixture 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 Wer ein Modell ergänzt, nimmt seinen Endpunkt in `PFADE` auf und schreibt eine
schreibt eine Zeile im Test dazu. Das ist die einzige Prüfung, die einen Zeile im Test dazu. Das ist die einzige Prüfung, die einen vertippten
vertippten Feldnamen fängt — der übersetzt anstandslos. 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 ## 6. Was als Nächstes dran ist
**Stufe 3 ist dran.** Davor liegt nur noch Backend-Ergänzung **D** (unten), **Stufe 4 ist dran.** Alle vier Backend-Ergänzungen sind drin; von hier an
die zwei Kopier-Lücken im Budget. reicht die API, wie sie ist.
**Stufe 3 — Schreiben.** Buchungen anlegen und ändern, Budget bearbeiten Was aus Stufe 3 bewusst nicht mitkam: das **Logo** eines Vertrags (Upload und
(Monat und Vorlage, beide Betriebsarten `fest`/`berechnet`), Abos verwalten Zuschnitt, `POST /subscriptions/{id}/logo`) und die **Reihenfolge** der Konten
samt Logo, Konten bearbeiten. Dazu Backend-Ergänzung **D**. (`PUT /accounts/order`). Beides ist am Rechner bequemer und hielt niemanden
auf.
**Stufe 4 — Arbeitsliste.** Eingang mit Buchen, Verwerfen, Kategorie zuordnen, **Stufe 4 — Arbeitsliste.** Eingang mit Buchen, Verwerfen, Kategorie zuordnen,
Übertragsvorschlägen und Dubletten. Fachlich der Teil, der am besten aufs Übertragsvorschlägen und Dubletten. Fachlich der Teil, der am besten aufs
@@ -372,12 +431,14 @@ Vorlagen, Verlauf mit Rücknahme.
### Zum Umfang, ehrlich ### Zum Umfang, ehrlich
Die Weboberfläche hat rund 8900 Zeilen JSX. Nativ wird das nicht weniger. 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 Stufe 2 hat rund 1800 Zeilen gebraucht, Stufe 3 rund 1200; Stufen 4 bis 6
zwei bis drei Wochen, Stufe 7 nochmal zwei bis drei. zusammen sind grob ein bis zwei Wochen, Stufe 7 nochmal zwei bis drei.
Stufe 3 wird die aufwendigste der verbliebenen: Schreiben heisst Formulare, Der **409-Fall** („nachfragen", mit `trotzdem=true` wiederholbar) kam in
Fehlerbehandlung je Feld und der 409-Fall („nachfragen", oft mit Stufe 3 nicht vor — er steckt nur an
`trotzdem=true` wiederholbar). Lesen war dagegen geradeaus. `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 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 hat **keinen Server-Zustand** zwischen den Schritten: Die Datei wird bei jedem