iOS (neu, ios/):
- SwiftUI-App: Login (Server + Keychain-Token), Kamera-Scanner (EAN/UPC/Code128),
Einlagern mit mehreren Chargen und eigenem MHD, Auslagern mit Chargenauswahl
oder FEFO, Artikelsuche, Anlegen mit Open-Food-Facts-Vorbefuellung.
- Home-Screen-Shortcuts: Schnellaktionen (langer Druck) und URL-Schema
projectgood://checkin | ://checkout fuer eigene Symbole via Kurzbefehle.
- project.yml (XcodeGen) + README mit Build-Anleitung. NICHT kompiliert - auf
diesem Rechner ist kein Xcode vorhanden.
Import-Modi (wie besprochen sinnvoll):
- add (Standard, nichts loeschen), replace_listed (Bestaende der in der Datei
genannten Produkte ersetzen - fuer Inventur), replace_all (alles ersetzen).
Geleerte Bestaende werden als Korrektur-Bewegung protokolliert; die Oberflaeche
fragt bei den zerstoerenden Modi nach.
Anzeige:
- "Bald ablaufend"/"Abgelaufen" zeigen jetzt das Gebinde ("1 Glas") mit der
Basiseinheit klein darunter (ExpiringItem liefert die Einheiten mit).
- MHD als Zeitspanne mit korrektem Numerus: "in 3 Tagen", "in 1 Woche",
"vor 2 Wochen", dazu heute/morgen/gestern.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
205 lines
4.9 KiB
Swift
205 lines
4.9 KiB
Swift
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]
|
|
|
|
enum CodingKeys: String, CodingKey {
|
|
case id, barcode, name, brand, stock, kind, barcodes
|
|
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 }
|
|
}
|
|
|
|
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 locationId: Int?
|
|
|
|
enum CodingKeys: String, CodingKey {
|
|
case id, quantity
|
|
case productId = "product_id"
|
|
case bestBefore = "best_before"
|
|
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 locationId: Int?
|
|
|
|
enum CodingKeys: String, CodingKey {
|
|
case quantity
|
|
case bestBefore = "best_before"
|
|
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 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 groupId = "group_id"
|
|
}
|
|
}
|