iOS: Einzelstücke (UID/QR) + Scan öffnet das Stück; Seed ohne Modell-Kaufdatum

iOS-Parität zum Web: Item-Modell + API, Einzelstueck-Liste in der Produkt-
Detailansicht (anlegen, je-Stueck Lagerort/Kaufdatum/Garantie/Bezugsquelle/Notiz
aendern, entfernen mit Grund, QR je Stueck via CoreImage). Anlegen-Formular hat
den Schalter "Einzelstuecke". Der Scanner erkennt Einzelstueck-QR (…/i/<UID>) und
oeffnet direkt das Stueck.

Seed: "Kaufdatum"/"Garantie bis" nicht mehr als Modell-Felder auf Elektronik -
diese Angaben gehoeren je Einzelstueck ans Item, nicht ans Modell.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-07-26 07:48:25 +02:00
parent 6fb5c47de6
commit 7f7129c87a
8 changed files with 421 additions and 9 deletions

View File

@@ -74,9 +74,11 @@ struct Product: Codable, Identifiable, Hashable {
let productUrl: String?
/// Selbst definierte Feldwerte: {feld_id (als Text): Wert}.
let fieldValues: [String: String?]?
/// Gegenstand als Einzelstücke (Items mit UID/QR) statt als Menge geführt.
let individual: Bool?
enum CodingKeys: String, CodingKey {
case id, barcode, name, brand, stock, kind, barcodes, tracking
case id, barcode, name, brand, stock, kind, barcodes, tracking, individual
case datePrecision = "date_precision"
case categoryId = "category_id"
case categoryName = "category_name"
@@ -99,6 +101,8 @@ struct Product: Codable, Identifiable, Hashable {
/// Gegenstand (Menge je Lagerort) statt Lebensmittel (Chargen/MHD)?
var isObject: Bool { tracking == "object" }
/// Gegenstand, der als Einzelstücke (UID/QR) geführt wird?
var isIndividual: Bool { individual == true }
/// Bezeichnung der Artikeleinheit (Gebinde, sonst Produkteinheit).
var articleUnitLabel: String {
@@ -264,9 +268,10 @@ struct NewProductRequest: Codable {
let datePrecision: String
let groupId: Int?
let categoryId: Int?
var individual: Bool? = nil
enum CodingKeys: String, CodingKey {
case barcode, name, brand
case barcode, name, brand, individual
case imageUrl = "image_url"
case baseUnit = "base_unit"
case packageSize = "package_size"
@@ -564,6 +569,86 @@ enum RemovalReasons {
}
}
// MARK: - Einzelstücke (Items mit UID/QR)
struct Item: Codable, Identifiable, Hashable {
let id: Int
let uid: String
let productId: Int
let locationId: Int?
let locationName: String?
let shopId: Int?
let shopName: String?
let acquiredOn: String? // "yyyy-MM-dd"
let warrantyUntil: String?
let note: String?
let createdAt: String
let productName: String?
let productBrand: String?
enum CodingKeys: String, CodingKey {
case id, uid, note
case productId = "product_id"
case locationId = "location_id"
case locationName = "location_name"
case shopId = "shop_id"
case shopName = "shop_name"
case acquiredOn = "acquired_on"
case warrantyUntil = "warranty_until"
case createdAt = "created_at"
case productName = "product_name"
case productBrand = "product_brand"
}
}
struct ItemCreateRequest: Codable {
let count: Int
let locationId: Int?
let shopId: Int?
let acquiredOn: String?
let warrantyUntil: String?
let note: String?
enum CodingKeys: String, CodingKey {
case count, note
case locationId = "location_id"
case shopId = "shop_id"
case acquiredOn = "acquired_on"
case warrantyUntil = "warranty_until"
}
}
struct ItemUpdateRequest: Encodable {
var locationId: Int?
var shopId: Int?
var acquiredOn: String?
var warrantyUntil: String?
var note: String?
enum CodingKeys: String, CodingKey {
case note
case locationId = "location_id"
case shopId = "shop_id"
case acquiredOn = "acquired_on"
case warrantyUntil = "warranty_until"
}
// Immer alle Felder senden (auch null), damit sich ein Feld leeren lässt.
func encode(to encoder: Encoder) throws {
var c = encoder.container(keyedBy: CodingKeys.self)
try c.encode(locationId, forKey: .locationId)
try c.encode(shopId, forKey: .shopId)
try c.encode(acquiredOn, forKey: .acquiredOn)
try c.encode(warrantyUntil, forKey: .warrantyUntil)
try c.encode(note, forKey: .note)
}
}
struct ItemRemoveRequest: Codable {
let reason: String
let note: String?
}
// MARK: - Uebersicht und Verlauf
/// Kennzahlen der Startseite (GET /dashboard/stats).