import Foundation // Entspricht den Schemas des Backends (backend/app/schemas.py). struct TokenResponse: Codable { let accessToken: String let role: String let username: String enum CodingKeys: String, CodingKey { case accessToken = "access_token" case role, username } } struct MeResponse: Codable { let id: Int let username: String let role: String var isAdmin: Bool { role == "admin" } } struct Unit: Codable, Identifiable, Hashable { let id: Int let name: String let kind: String let factor: Double } struct BarcodeEntry: Codable, Identifiable, Hashable { let id: Int let code: String let note: String? } struct Product: Codable, Identifiable, Hashable { let id: Int let barcode: String? let name: String let brand: String? let imageUrl: String? let baseUnit: String let packageSize: Double? let packageLabel: String? let groupId: Int? let minStock: Double? let stock: Double let expiredCount: Int let kind: String let unitName: String let unitFactor: Double let barcodes: [BarcodeEntry] /// Welche MHD-Genauigkeit bei diesem Produkt ueblich ist ("day"/"month"). let datePrecision: String? enum CodingKeys: String, CodingKey { case id, barcode, name, brand, stock, kind, barcodes case datePrecision = "date_precision" case imageUrl = "image_url" case baseUnit = "base_unit" case packageSize = "package_size" case packageLabel = "package_label" case groupId = "group_id" case minStock = "min_stock" case expiredCount = "expired_count" case unitName = "unit_name" case unitFactor = "unit_factor" } /// Bezeichnung der Artikeleinheit (Gebinde, sonst Produkteinheit). var articleUnitLabel: String { if let size = packageSize, size > 0 { return packageLabel ?? "Packung" } return unitName } /// Faktor, um Basiseinheiten in Artikeleinheiten umzurechnen. var articleUnitFactor: Double { if let size = packageSize, size > 0 { return size } return unitFactor == 0 ? 1 : unitFactor } var stockInArticleUnits: Double { stock / articleUnitFactor } } /// Auswahleintrag fuer Einheiten. /// /// Anzeige und uebertragener Wert sind bewusst getrennt: Das Backend kennt /// fuer das Gebinde nur das Schluesselwort "package" (siehe /// services/conversion.py). Die Bezeichnung "Glas" oder "Dose" ist reine /// Anzeige - wird sie mitgeschickt, antwortet der Server /// "Unbekannte Einheit: Glas". struct UnitOption: Hashable, Identifiable { let value: String let label: String var id: String { value } static let packageValue = "package" } struct LookupResult: Codable { let found: Bool let existingProduct: Product? let suggestion: Suggestion? let groupId: Int? let groupName: String? enum CodingKeys: String, CodingKey { case found, suggestion case existingProduct = "existing_product" case groupId = "group_id" case groupName = "group_name" } struct Suggestion: Codable { let barcode: String? let name: String let brand: String? let imageUrl: String? let baseUnit: String? let packageSize: Double? let quantityText: String? enum CodingKeys: String, CodingKey { case barcode, name, brand case imageUrl = "image_url" case baseUnit = "base_unit" case packageSize = "package_size" case quantityText = "quantity_text" } } } struct Lot: Codable, Identifiable, Hashable { let id: Int let productId: Int let quantity: Double let bestBefore: String? let bestBeforePrecision: String? let locationId: Int? enum CodingKeys: String, CodingKey { case id, quantity case productId = "product_id" case bestBefore = "best_before" case bestBeforePrecision = "best_before_precision" case locationId = "location_id" } } struct StorageLocation: Codable, Identifiable, Hashable { let id: Int let name: String let parentId: Int? enum CodingKeys: String, CodingKey { case id, name case parentId = "parent_id" } } struct CheckInLine: Codable { let quantity: Double let bestBefore: String? let bestBeforePrecision: String let locationId: Int? enum CodingKeys: String, CodingKey { case quantity case bestBefore = "best_before" case bestBeforePrecision = "best_before_precision" case locationId = "location_id" } } struct BatchCheckInRequest: Codable { let productId: Int let unit: String let lines: [CheckInLine] enum CodingKeys: String, CodingKey { case unit, lines case productId = "product_id" } } struct CheckOutRequest: Codable { let productId: Int let quantity: Double let unit: String let lotId: Int? enum CodingKeys: String, CodingKey { case quantity, unit case productId = "product_id" case lotId = "lot_id" } } struct StockResponse: Codable { let productStock: Double enum CodingKeys: String, CodingKey { case productStock = "product_stock" } } struct NewProductRequest: Codable { let barcode: String? let name: String let brand: String? let imageUrl: String? let baseUnit: String let packageSize: Double? let packageLabel: String? let datePrecision: String let groupId: Int? enum CodingKeys: String, CodingKey { case barcode, name, brand case imageUrl = "image_url" case baseUnit = "base_unit" case packageSize = "package_size" case packageLabel = "package_label" case datePrecision = "date_precision" case groupId = "group_id" } } // MARK: - Listen (Einkaufsliste, bald ablaufend) struct ShoppingItem: Codable, Identifiable { let productId: Int let name: String let baseUnit: String let packageSize: Double? let stock: Double let minStock: Double let deficit: Double var id: Int { productId } enum CodingKeys: String, CodingKey { case name, stock, deficit case productId = "product_id" case baseUnit = "base_unit" case packageSize = "package_size" case minStock = "min_stock" } } struct GroupShoppingItem: Codable, Identifiable { let groupId: Int let name: String let stock: Double let minStock: Double let deficit: Double let unitName: String let productCount: Int var id: Int { groupId } enum CodingKeys: String, CodingKey { case name, stock, deficit case groupId = "group_id" case minStock = "min_stock" case unitName = "unit_name" case productCount = "product_count" } } struct ExpiringItem: Codable, Identifiable { let lotId: Int let productId: Int let productName: String let quantity: Double let baseUnit: String let bestBefore: String let bestBeforePrecision: String? let daysLeft: Int let packageSize: Double? let packageLabel: String? let unitName: String let unitFactor: Double var id: Int { lotId } enum CodingKeys: String, CodingKey { case quantity case lotId = "lot_id" case productId = "product_id" case productName = "product_name" case baseUnit = "base_unit" case bestBefore = "best_before" case bestBeforePrecision = "best_before_precision" case daysLeft = "days_left" case packageSize = "package_size" case packageLabel = "package_label" case unitName = "unit_name" case unitFactor = "unit_factor" } } // MARK: - Aenderungen /// Die Felder, die das Produktformular der App besitzt (PATCH). /// /// Bewusst mit eigener Kodierung: Swift laesst `nil`-Optionals sonst einfach /// weg, und das Backend wertet nur mitgeschickte Felder aus. Leeren waere damit /// unmoeglich - "keine Kategorie" oder eine geloeschte Marke wuerden stumm /// verpuffen. Felder, die die App nicht bearbeitet (etwa der Mindestbestand), /// stehen deshalb gar nicht erst hier drin. struct ProductUpdateRequest: Encodable { var name: String? var brand: String? var packageSize: Double? var packageLabel: String? var datePrecision: String? /// Kategorie (heisst in der API weiterhin group_id). var groupId: Int? enum CodingKeys: String, CodingKey { case name, brand case packageSize = "package_size" case packageLabel = "package_label" case datePrecision = "date_precision" case groupId = "group_id" } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(name, forKey: .name) try container.encode(brand, forKey: .brand) try container.encode(packageSize, forKey: .packageSize) try container.encode(packageLabel, forKey: .packageLabel) try container.encode(datePrecision, forKey: .datePrecision) try container.encode(groupId, forKey: .groupId) } } struct LotUpdateRequest: Codable { var quantity: Double? var bestBefore: String? var bestBeforePrecision: String? enum CodingKeys: String, CodingKey { case quantity case bestBefore = "best_before" case bestBeforePrecision = "best_before_precision" } } /// Ein Eintrag aus /settings (Schluessel/Wert). struct SettingEntry: Codable { let key: String let value: String } /// Kategorie (heisst in der Datenbank und in der API weiterhin "group"; /// nur die Beschriftung in der Oberflaeche wurde auf "Kategorie" geaendert). struct GroupItem: Codable, Identifiable, Hashable { let id: Int let name: String }