import Foundation // Was die App zum Server schickt. Getrennt von den Lesemodellen, weil das // Backend es ebenso trennt: `TransactionCreate` kennt Felder, die // `TransactionOut` nicht hat, und umgekehrt (`subscription_id` etwa steht nur // in der Ausgabe – beim Anlegen gibt man es nicht mit). // // Alle Änderungstypen sind **teilweise**: Nur was gesetzt ist, geht mit. Ein // `nil` darf nicht als `null` hinausgehen, sonst löschte jedes Speichern die // Felder, die das Formular gar nicht anfasst. // // Das macht Swift von selbst – die erzeugte `encode(to:)` ruft für Optionals // `encodeIfPresent`. Hier stand das eine Weile von Hand ausgeschrieben, bis // ein Test zeigte, dass es dasselbe tut. Der Test bleibt: Die Eigenschaft ist // zu wichtig, um sie ungeprüft einer Spracheigenheit zu überlassen. /// `TransactionCreate` struct BuchungAnlegen: Encodable, Sendable { var type: Buchungsart var date: Tagesdatum var accountId: Int var toAccountId: Int? /// Immer positiv – die Richtung steckt in `type`, nicht im Vorzeichen. var amountCents: Int var toAmountCents: Int? var categoryId: Int? var payee: String? var description: String? var notes: String? var tagIds: [Int] = [] private enum CodingKeys: String, CodingKey { case type, date, payee, description, notes case accountId = "account_id" case toAccountId = "to_account_id" case amountCents = "amount_cents" case toAmountCents = "to_amount_cents" case categoryId = "category_id" case tagIds = "tag_ids" } } /// `TransactionUpdate` – nur die gesetzten Felder gehen mit. struct BuchungAendern: Encodable, Sendable { var type: Buchungsart? var date: Tagesdatum? var accountId: Int? var toAccountId: Int? var amountCents: Int? var categoryId: Int? var payee: String? var description: String? var notes: String? /// Mitgeschickt heisst **ersetzen**; eine leere Liste nimmt alle Tags ab. /// Weggelassen lässt sie unberührt. var tagIds: [Int]? private enum CodingKeys: String, CodingKey { case type, date, payee, description, notes case accountId = "account_id" case toAccountId = "to_account_id" case amountCents = "amount_cents" case categoryId = "category_id" case tagIds = "tag_ids" } } /// `AccountCreate` struct KontoAnlegen: Encodable, Sendable { var name: String var type: Kontoart = .girokonto var currency: String = "EUR" var icon: String = "wallet" var color: String = "#3f51b5" var openingBalanceCents: Int = 0 var openingDate: Tagesdatum? var creditLimitCents: Int? var isLocked: Bool = false private enum CodingKeys: String, CodingKey { case name, type, currency, icon, color case openingBalanceCents = "opening_balance_cents" case openingDate = "opening_date" case creditLimitCents = "credit_limit_cents" case isLocked = "is_locked" } } /// `AccountUpdate` – nur die gesetzten Felder gehen mit. /// /// Die Währung fehlt mit Absicht: Sie nachträglich zu wechseln liesse jeden /// gespeicherten Betrag etwas anderes bedeuten. Wer das will, legt ein neues /// Konto an. struct KontoAendern: Encodable, Sendable { var name: String? var type: Kontoart? var icon: String? var color: String? var openingBalanceCents: Int? var openingDate: Tagesdatum? var creditLimitCents: Int? var isLocked: Bool? var isArchived: Bool? private enum CodingKeys: String, CodingKey { case name, type, icon, color case openingBalanceCents = "opening_balance_cents" case openingDate = "opening_date" case creditLimitCents = "credit_limit_cents" case isLocked = "is_locked" case isArchived = "is_archived" } } /// `SubscriptionIn` struct AboAnlegen: Encodable, Sendable { var name: String var icon: String = "refresh-ccw" var color: String = "#6b7480" var amountCents: Int var currency: String = "EUR" /// Nur 1, 2, 3, 6 oder 12 – das Backend weist alles andere ab. var intervalMonths: Int = 1 var categoryId: Int? var accountId: Int? var nextDue: Tagesdatum? var payeePattern: String? var notes: String? private enum CodingKeys: String, CodingKey { case name, icon, color, currency, notes case amountCents = "amount_cents" case intervalMonths = "interval_months" case categoryId = "category_id" case accountId = "account_id" case nextDue = "next_due" case payeePattern = "payee_pattern" } } /// `SubscriptionUpdate` – nur die gesetzten Felder gehen mit. struct AboAendern: Encodable, Sendable { var name: String? var icon: String? var color: String? var amountCents: Int? var intervalMonths: Int? var categoryId: Int? var accountId: Int? var nextDue: Tagesdatum? var payeePattern: String? var isActive: Bool? var notes: String? private enum CodingKeys: String, CodingKey { case name, icon, color, notes case amountCents = "amount_cents" case intervalMonths = "interval_months" case categoryId = "category_id" case accountId = "account_id" case nextDue = "next_due" case payeePattern = "payee_pattern" case isActive = "is_active" } } /// Die möglichen Zahlungsrhythmen. Mehr nimmt das Backend nicht an. enum Zahlungsrhythmus: Int, CaseIterable, Identifiable, Sendable { case monatlich = 1 case zweimonatlich = 2 case vierteljaehrlich = 3 case halbjaehrlich = 6 case jaehrlich = 12 var id: Int { rawValue } var name: String { switch self { case .monatlich: return String(localized: "abo.monatlich") case .zweimonatlich: return String(format: String(localized: "abo.alleNMonate"), 2) case .vierteljaehrlich: return String(localized: "abo.vierteljaehrlich") case .halbjaehrlich: return String(localized: "abo.halbjaehrlich") case .jaehrlich: return String(localized: "abo.jaehrlich") } } }