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>
88 lines
3.1 KiB
Swift
88 lines
3.1 KiB
Swift
import Foundation
|
||
|
||
/// `AccountOut` aus `backend/app/schemas.py`.
|
||
struct Konto: Codable, Sendable, Identifiable, Hashable {
|
||
let id: Int
|
||
let name: String
|
||
let type: Kontoart
|
||
let currency: String
|
||
let icon: String
|
||
let color: String
|
||
let openingBalanceCents: Int
|
||
let openingDate: Tagesdatum?
|
||
let creditLimitCents: Int?
|
||
let isLocked: Bool
|
||
let isArchived: Bool
|
||
let position: Int
|
||
let savingsCategoryId: Int?
|
||
let identifiers: [Kontokennung]
|
||
/// Saldo in der Währung des Kontos.
|
||
let balanceCents: Int
|
||
/// Derselbe Saldo in der Hauptwährung – vom Server gerechnet.
|
||
let balanceBaseCents: Int
|
||
/// Was die Bank selbst meldet. Nur bei angebundenen Konten gesetzt.
|
||
let bankBalanceCents: Int?
|
||
let bankBalanceAt: Zeitpunkt?
|
||
/// Nur ein gebuchter Banksaldo lässt sich mit unserer Rechnung
|
||
/// vergleichen; ein verfügbarer enthält Vorgemerktes.
|
||
let bankBalanceVergleichbar: Bool
|
||
|
||
private enum CodingKeys: String, CodingKey {
|
||
case id, name, type, currency, icon, color, position, identifiers
|
||
case openingBalanceCents = "opening_balance_cents"
|
||
case openingDate = "opening_date"
|
||
case creditLimitCents = "credit_limit_cents"
|
||
case isLocked = "is_locked"
|
||
case isArchived = "is_archived"
|
||
case savingsCategoryId = "savings_category_id"
|
||
case balanceCents = "balance_cents"
|
||
case balanceBaseCents = "balance_base_cents"
|
||
case bankBalanceCents = "bank_balance_cents"
|
||
case bankBalanceAt = "bank_balance_at"
|
||
case bankBalanceVergleichbar = "bank_balance_vergleichbar"
|
||
}
|
||
|
||
var saldo: Geldbetrag { Geldbetrag(cents: balanceCents, waehrung: currency) }
|
||
|
||
/// Ob der gemeldete Banksaldo von unserer Rechnung abweicht – nur dann ist
|
||
/// ein Hinweis angebracht.
|
||
var weichtVonDerBankAb: Bool {
|
||
guard bankBalanceVergleichbar, let bank = bankBalanceCents else { return false }
|
||
return bank != balanceCents
|
||
}
|
||
}
|
||
|
||
/// `AccountType`
|
||
enum Kontoart: String, Codable, Sendable, CaseIterable {
|
||
case girokonto, sparkonto, bargeld, kreditkarte, depot, sonstiges
|
||
|
||
var name: String {
|
||
switch self {
|
||
case .girokonto: return String(localized: "kontoart.girokonto")
|
||
case .sparkonto: return String(localized: "kontoart.sparkonto")
|
||
case .bargeld: return String(localized: "kontoart.bargeld")
|
||
case .kreditkarte: return String(localized: "kontoart.kreditkarte")
|
||
case .depot: return String(localized: "kontoart.depot")
|
||
case .sonstiges: return String(localized: "kontoart.sonstiges")
|
||
}
|
||
}
|
||
|
||
/// Ein Übertrag auf diese Konten zählt im Budget als Sparen, nicht als
|
||
/// neutrale Umbuchung (`SAVINGS_ACCOUNT_TYPES` im Backend).
|
||
var giltAlsSparen: Bool { self == .sparkonto || self == .depot }
|
||
}
|
||
|
||
/// `AccountIdentifierOut`
|
||
struct Kontokennung: Codable, Sendable, Identifiable, Hashable {
|
||
let id: Int
|
||
let kind: Art
|
||
let value: String
|
||
|
||
enum Art: String, Codable, Sendable {
|
||
case iban
|
||
case accountNo = "account_no"
|
||
case cardNo = "card_no"
|
||
case text
|
||
}
|
||
}
|