feat: Dritte Stufe der iOS-App - alles laesst sich jetzt bearbeiten

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>
This commit is contained in:
Guido
2026-10-21 21:04:46 +02:00
co-authored by Claude Opus 5
parent 4c61a02c53
commit 82db09e6de
15 changed files with 1653 additions and 4 deletions
+76
View File
@@ -9,6 +9,8 @@
"allgemein.abbrechen" = "Abbrechen";
"allgemein.abmelden" = "Abmelden";
"allgemein.unbekannt" = "Unbekannt";
"allgemein.sichern" = "Sichern";
"allgemein.ok" = "OK";
/* --- Reiter --- */
"reiter.dashboard" = "Übersicht";
@@ -163,3 +165,77 @@
"abo.proMonat" = "%@ im Monat";
"abo.naechste" = "nächste am %@";
"abo.inTagen" = "fällig in %d Tagen";
/* --- Buchung bearbeiten --- */
"buchung.neu" = "Neue Buchung";
"buchung.aendern" = "Buchung ändern";
"buchung.datum" = "Datum";
"buchung.betrag" = "Betrag";
"buchung.konto" = "Konto";
"buchung.konto.keines" = "Kein Konto";
"buchung.zielkonto" = "Auf Konto";
"buchung.kategorie" = "Kategorie";
"buchung.empfaenger" = "Empfänger";
"buchung.verwendungszweck" = "Verwendungszweck";
"buchung.notiz" = "Notiz";
"buchung.schlagwoerter" = "Schlagwörter";
"buchung.keineSchlagwoerter" = "Noch keine Schlagwörter angelegt. Das geht am Rechner.";
"buchung.uebertragErklaerung" = "Ein Übertrag verschiebt Geld zwischen deinen Konten. Er zählt in keiner Auswertung mit und braucht deshalb keine Kategorie.";
"buchung.loeschen" = "Buchung löschen";
"buchung.loeschenFrage" = "Diese Buchung löschen?";
/* --- Budget bearbeiten --- */
"budget.geplant" = "Geplant";
"budget.zuschlag" = "Zuschlag";
"budget.ausVertraegen" = "Aus Verträgen";
"budget.ergibt" = "Ergibt";
"budget.monatSetzen" = "Nur dieser Monat";
"budget.vorlageSetzen" = "Standard-Budget";
"budget.vorlage" = "Vorlage";
"budget.ausVertraegenRechnen" = "Aus Verträgen rechnen";
"budget.berechnetErklaerung" = "Der Soll-Betrag ergibt sich aus den Verträgen dieser Kategorie plus Zuschlag. Ändert sich ein Vertrag, ändert sich das Budget mit.";
"budget.festErklaerung" = "Ein fester Betrag, den du selbst setzt.";
"budget.vorlageHolen" = "Vorlage in diesen Monat holen";
"budget.alsVorlageSetzen" = "Diesen Monat als Standard festlegen";
"budget.zuruecksetzen" = "Monat zurücksetzen";
"budget.zuruecksetzenFrage" = "Diesen Monat zurücksetzen?";
"budget.zuruecksetzenErklaerung" = "Alle Anpassungen dieses Monats fallen weg. Danach gilt wieder das Standard-Budget.";
/* --- Vertrag bearbeiten --- */
"abo.neu" = "Neuer Vertrag";
"abo.aendern" = "Vertrag ändern";
"abo.name" = "Name";
"abo.betrag" = "Betrag je Zahlung";
"abo.rhythmus" = "Rhythmus";
"abo.konto" = "Wird abgebucht von";
"abo.kategorieErklaerung" = "Die Kategorie entscheidet, in welcher Budget-Zeile dieser Vertrag mitzählt.";
"abo.faelligkeitKennen" = "Nächste Zahlung bekannt";
"abo.naechsteZahlung" = "Nächste Zahlung";
"abo.erkennungstext" = "Erkennungstext";
"abo.erkennungErklaerung" = "Woran die Erkennung diesen Vertrag in einer Buchung wiedererkennt - meist ein Teil des Empfängernamens.";
"abo.laeuftNoch" = "Läuft noch";
"abo.beendenStattLoeschen" = "Beenden statt löschen hält fest, was er gekostet hat. Löschen entfernt ihn ganz.";
"abo.loeschen" = "Vertrag löschen";
"abo.loeschenFrage" = "Diesen Vertrag löschen?";
"abo.loeschenErklaerung" = "Die zugeordneten Buchungen bleiben, sie verlieren nur das Etikett.";
/* --- Konto bearbeiten --- */
"konto.neu" = "Neues Konto";
"konto.aendern" = "Konto ändern";
"konto.name" = "Name";
"konto.art" = "Art";
"konto.waehrung" = "Währung";
"konto.anfangssaldo" = "Anfangssaldo";
"konto.stichtagKennen" = "Stichtag angeben";
"konto.stichtag" = "Stichtag";
"konto.anfangssaldoErklaerung" = "Der Stand zu Beginn. Ohne Stichtag gilt er von Anfang an; mit Stichtag zählen nur Buchungen ab diesem Tag darauf.";
"konto.kreditrahmenKennen" = "Kreditrahmen angeben";
"konto.kreditrahmen" = "Kreditrahmen";
"konto.kreditrahmenErklaerung" = "Was die Karte ausgeben darf - kein Guthaben.";
"konto.gebunden" = "Gebundenes Geld";
"konto.gebundenErklaerung" = "Geld, an das du nicht herankommst, etwa Säule 3a. Es zählt nicht als verfügbar.";
"konto.archiviert" = "Archiviert";
"konto.archivierenStattLoeschen" = "Archivieren blendet das Konto aus und behält seine Buchungen. Löschen geht nur, solange keine daran hängen.";
"konto.loeschen" = "Konto löschen";
"konto.loeschenFrage" = "Dieses Konto löschen?";
"konto.loeschenErklaerung" = "Das geht nur, wenn keine Buchung mehr daran hängt. Sonst archivieren.";
+76
View File
@@ -9,6 +9,8 @@
"allgemein.abbrechen" = "Cancel";
"allgemein.abmelden" = "Sign out";
"allgemein.unbekannt" = "Unknown";
"allgemein.sichern" = "Save";
"allgemein.ok" = "OK";
/* --- Tabs --- */
"reiter.dashboard" = "Overview";
@@ -163,3 +165,77 @@
"abo.proMonat" = "%@ per month";
"abo.naechste" = "next on %@";
"abo.inTagen" = "due in %d days";
/* --- Edit transaction --- */
"buchung.neu" = "New transaction";
"buchung.aendern" = "Edit transaction";
"buchung.datum" = "Date";
"buchung.betrag" = "Amount";
"buchung.konto" = "Account";
"buchung.konto.keines" = "No account";
"buchung.zielkonto" = "To account";
"buchung.kategorie" = "Category";
"buchung.empfaenger" = "Payee";
"buchung.verwendungszweck" = "Description";
"buchung.notiz" = "Note";
"buchung.schlagwoerter" = "Tags";
"buchung.keineSchlagwoerter" = "No tags yet. You can add them on the desktop.";
"buchung.uebertragErklaerung" = "A transfer moves money between your own accounts. It counts in no report and therefore needs no category.";
"buchung.loeschen" = "Delete transaction";
"buchung.loeschenFrage" = "Delete this transaction?";
/* --- Edit budget --- */
"budget.geplant" = "Planned";
"budget.zuschlag" = "Extra";
"budget.ausVertraegen" = "From subscriptions";
"budget.ergibt" = "Totals";
"budget.monatSetzen" = "This month only";
"budget.vorlageSetzen" = "Default budget";
"budget.vorlage" = "Template";
"budget.ausVertraegenRechnen" = "Calculate from subscriptions";
"budget.berechnetErklaerung" = "The planned amount comes from this category's subscriptions plus the extra. Change a subscription and the budget follows.";
"budget.festErklaerung" = "A fixed amount you set yourself.";
"budget.vorlageHolen" = "Load template into this month";
"budget.alsVorlageSetzen" = "Make this month the default";
"budget.zuruecksetzen" = "Reset month";
"budget.zuruecksetzenFrage" = "Reset this month?";
"budget.zuruecksetzenErklaerung" = "All adjustments to this month are dropped. The default budget applies again.";
/* --- Edit subscription --- */
"abo.neu" = "New subscription";
"abo.aendern" = "Edit subscription";
"abo.name" = "Name";
"abo.betrag" = "Amount per payment";
"abo.rhythmus" = "Billed";
"abo.konto" = "Charged to";
"abo.kategorieErklaerung" = "The category decides which budget line this subscription counts towards.";
"abo.faelligkeitKennen" = "Next payment known";
"abo.naechsteZahlung" = "Next payment";
"abo.erkennungstext" = "Match text";
"abo.erkennungErklaerung" = "How the matcher recognises this subscription in a transaction - usually part of the payee name.";
"abo.laeuftNoch" = "Still running";
"abo.beendenStattLoeschen" = "Cancelling keeps the record of what it cost. Deleting removes it entirely.";
"abo.loeschen" = "Delete subscription";
"abo.loeschenFrage" = "Delete this subscription?";
"abo.loeschenErklaerung" = "The linked transactions stay; they only lose the label.";
/* --- Edit account --- */
"konto.neu" = "New account";
"konto.aendern" = "Edit account";
"konto.name" = "Name";
"konto.art" = "Type";
"konto.waehrung" = "Currency";
"konto.anfangssaldo" = "Opening balance";
"konto.stichtagKennen" = "Set an opening date";
"konto.stichtag" = "Opening date";
"konto.anfangssaldoErklaerung" = "The balance to start from. Without a date it applies from the beginning; with one, only transactions from that day count on top.";
"konto.kreditrahmenKennen" = "Set a credit limit";
"konto.kreditrahmen" = "Credit limit";
"konto.kreditrahmenErklaerung" = "What the card may spend - not a balance.";
"konto.gebunden" = "Locked money";
"konto.gebundenErklaerung" = "Money you cannot reach, such as a pension account. It does not count as available.";
"konto.archiviert" = "Archived";
"konto.archivierenStattLoeschen" = "Archiving hides the account and keeps its transactions. Deleting only works while none are attached.";
"konto.loeschen" = "Delete account";
"konto.loeschenFrage" = "Delete this account?";
"konto.loeschenErklaerung" = "Only possible while no transaction is attached. Archive it otherwise.";