import Foundation /// `SubscriptionListOut` – die Vertragsliste samt Kopfzahlen. struct Aboliste: Codable, Sendable, Hashable { let subscriptions: [Abo] let summary: Abosummen } /// `SubscriptionOut` aus `backend/app/schemas.py`. struct Abo: Codable, Sendable, Identifiable, Hashable { let id: Int let name: String let icon: String let color: String /// Der Betrag je Zahlung, in der Währung des Vertrags. let amountCents: Int let currency: String /// 1 = monatlich, 12 = jährlich, 3 = vierteljährlich … let intervalMonths: Int let accountId: Int? let categoryId: Int? let nextDue: Tagesdatum? /// Woran die Erkennung den Vertrag in einer Buchung wiedererkennt. let payeePattern: String? let savingsAccountId: Int? let isActive: Bool let cancelledAt: Tagesdatum? let notes: String? let position: Int /// Auf einen Monat umgelegt, in der Vertragswährung – vom Server gerechnet. let monatlichCents: Int /// Dasselbe in der Hauptwährung. Fehlt der Kurs, fällt es still auf den /// nativen Wert zurück; welche Währungen betroffen sind, sagt /// `Abosummen.ohneKurs`. let monatlichBaseCents: Int let tageBisFaellig: Int? let hasLogo: Bool let logoVersion: Int? private enum CodingKeys: String, CodingKey { case id, name, icon, color, currency, notes, position case amountCents = "amount_cents" case intervalMonths = "interval_months" case accountId = "account_id" case categoryId = "category_id" case nextDue = "next_due" case payeePattern = "payee_pattern" case savingsAccountId = "savings_account_id" case isActive = "is_active" case cancelledAt = "cancelled_at" case monatlichCents = "monatlich_cents" case monatlichBaseCents = "monatlich_base_cents" case tageBisFaellig = "tage_bis_faellig" case hasLogo = "has_logo" case logoVersion = "logo_version" } var betrag: Geldbetrag { Geldbetrag(cents: amountCents, waehrung: currency) } var monatlich: Geldbetrag { Geldbetrag(cents: monatlichCents, waehrung: currency) } /// „monatlich", „jährlich", „alle 3 Monate". var rhythmus: String { switch intervalMonths { case 1: return String(localized: "abo.monatlich") case 3: return String(localized: "abo.vierteljaehrlich") case 6: return String(localized: "abo.halbjaehrlich") case 12: return String(localized: "abo.jaehrlich") default: return String(format: String(localized: "abo.alleNMonate"), intervalMonths) } } /// Steht die nächste Zahlung in der nächsten Woche an? var istBaldFaellig: Bool { guard isActive, let tage = tageBisFaellig else { return false } return tage <= 7 } } /// Was am Kopf der Liste steht – die Zahl, wegen der man sie öffnet. struct Abosummen: Codable, Sendable, Hashable { let currency: String let monatlichCents: Int let jaehrlichCents: Int let anzahlAktiv: Int /// Währungen, die sich mangels Kurs nicht umrechnen liessen. Lieber /// nennen als stillschweigend zu klein summieren. let ohneKurs: [String] private enum CodingKeys: String, CodingKey { case currency case monatlichCents = "monatlich_cents" case jaehrlichCents = "jaehrlich_cents" case anzahlAktiv = "anzahl_aktiv" case ohneKurs = "ohne_kurs" } var monatlich: Geldbetrag { Geldbetrag(cents: monatlichCents, waehrung: currency) } var jaehrlich: Geldbetrag { Geldbetrag(cents: jaehrlichCents, waehrung: currency) } }