feat: Fuenfte Stufe der iOS-App - Kategorien, Schlagwoerter, Bank, zweiter Faktor
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>
This commit is contained in:
@@ -370,4 +370,106 @@ extension CertainClient {
|
||||
try await schickeStumm(.post, "transactions/dubletten/behalten",
|
||||
koerper: ["ids": ids])
|
||||
}
|
||||
|
||||
// MARK: - Kategorien schreiben
|
||||
|
||||
func legeKategorieAn(_ eingabe: KategorieAnlegen) async throws -> Kategorie {
|
||||
try await schicke(.post, "categories", koerper: eingabe)
|
||||
}
|
||||
|
||||
func aendereKategorie(_ id: Int, _ aenderung: KategorieAendern) async throws -> Kategorie {
|
||||
try await schicke(.patch, "categories/\(id)", koerper: aenderung)
|
||||
}
|
||||
|
||||
/// Geht nur, solange keine Buchung daran hängt – sonst 400. Archivieren
|
||||
/// ist der Weg für alles, was schon benutzt wurde.
|
||||
func loescheKategorie(_ id: Int) async throws {
|
||||
try await schickeStumm(.delete, "categories/\(id)")
|
||||
}
|
||||
|
||||
func legeGruppeAn(_ eingabe: GruppeAnlegen) async throws -> Kategoriegruppe {
|
||||
try await schicke(.post, "categories/groups", koerper: eingabe)
|
||||
}
|
||||
|
||||
func aendereGruppe(_ id: Int, _ aenderung: GruppeAendern) async throws -> Kategoriegruppe {
|
||||
try await schicke(.patch, "categories/groups/\(id)", koerper: aenderung)
|
||||
}
|
||||
|
||||
func loescheGruppe(_ id: Int) async throws {
|
||||
try await schickeStumm(.delete, "categories/groups/\(id)")
|
||||
}
|
||||
|
||||
// MARK: - Schlagwörter (Tags) schreiben
|
||||
|
||||
func legeTagAn(_ eingabe: TagAnlegen) async throws -> Schlagwort {
|
||||
try await schicke(.post, "tags", koerper: eingabe)
|
||||
}
|
||||
|
||||
func aendereTag(_ id: Int, _ aenderung: TagAendern) async throws -> Schlagwort {
|
||||
try await schicke(.patch, "tags/\(id)", koerper: aenderung)
|
||||
}
|
||||
|
||||
func loescheTag(_ id: Int) async throws {
|
||||
try await schickeStumm(.delete, "tags/\(id)")
|
||||
}
|
||||
|
||||
// MARK: - Erkennungswörter
|
||||
|
||||
func erkennungswoerter(kategorie: Int) async throws -> [Erkennungswort] {
|
||||
try await hole("categories/\(kategorie)/keywords")
|
||||
}
|
||||
|
||||
func legeErkennungswortAn(kategorie: Int,
|
||||
_ eingabe: ErkennungswortAnlegen) async throws -> Erkennungswort {
|
||||
try await schicke(.post, "categories/\(kategorie)/keywords", koerper: eingabe)
|
||||
}
|
||||
|
||||
func loescheErkennungswort(_ id: Int) async throws {
|
||||
try await schickeStumm(.delete, "categories/keywords/\(id)")
|
||||
}
|
||||
|
||||
/// Wörter, die oft wieder korrigiert werden – zur Prüfung gemeldet.
|
||||
func markierteErkennungswoerter() async throws -> [MarkiertesWort] {
|
||||
try await hole("categories/keywords/flagged")
|
||||
}
|
||||
|
||||
/// „Behalten": Das Wort stimmt, die Korrekturen waren Einzelfälle.
|
||||
func bestaetigeErkennungswort(_ id: Int) async throws {
|
||||
try await schickeStumm(.post, "categories/keywords/\(id)/confirm")
|
||||
}
|
||||
|
||||
// MARK: - Zweiter Faktor
|
||||
|
||||
func zweiterFaktorStand() async throws -> ZweiterFaktorStand {
|
||||
try await hole("auth/2fa/status")
|
||||
}
|
||||
|
||||
/// Legt ein noch **nicht** aktives Geheimnis an. Gültig wird es erst mit
|
||||
/// `aktiviereZweitenFaktor`.
|
||||
func richteZweitenFaktorEin() async throws -> ZweiterFaktorEinrichtung {
|
||||
try await schicke(.post, "auth/2fa/setup")
|
||||
}
|
||||
|
||||
/// Prüft einen Code aus der Authenticator-App und schaltet scharf.
|
||||
/// Die Rettungscodes kommen dabei **einmal** – danach nie wieder.
|
||||
func aktiviereZweitenFaktor(code: String) async throws -> Rettungscodes {
|
||||
try await schicke(.post, "auth/2fa/activate",
|
||||
koerper: ZweiterFaktorCode(code: code))
|
||||
}
|
||||
|
||||
// MARK: - Bank
|
||||
|
||||
func bankverbindungen() async throws -> [Bankverbindung] {
|
||||
try await hole("bank/connections")
|
||||
}
|
||||
|
||||
/// Holt die Umsätze dieser Verbindung in den Eingang.
|
||||
func rufeBankAb(_ verbindung: Int) async throws -> Abrufergebnis {
|
||||
try await schicke(.post, "bank/connections/\(verbindung)/sync")
|
||||
}
|
||||
|
||||
/// Nur die Salden auffrischen, ohne Umsätze zu holen.
|
||||
func frischeSaldenAuf(_ verbindung: Int) async throws {
|
||||
try await schickeStumm(.post, "bank/connections/\(verbindung)/salden")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user