App: Zweiteinheit erfassen und anzeigen

Eigener Abschnitt im Artikel mit "Menge [Stk] entspricht [Gramm] das sind
[250]" und der ausgerechneten Kontrolle in der Fusszeile. Nur Charge-Artikel,
und nur die anderen Arten stehen zur Wahl - eine Bruecke auf die eigene Art
haette nichts umzurechnen.

ProductUpdateRequest bekommt sendSecondary nach dem Vorbild von sendMinStock:
ein PATCH aus einer anderen Maske soll die Bruecke nicht unbeabsichtigt
loeschen. Die Einheitenauswahl beim Ein- und Auslagern nimmt bei hinterlegter
Bruecke die Gegenart mit auf, der Bestand steht in beiden Lesarten
("6 Stueck · 500 g").

Alle neuen Product-Felder sind Optionals, damit die App auch gegen einen
aelteren Server laeuft.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-08-16 00:42:52 +02:00
parent 788ef2caf2
commit 71d52917e4
4 changed files with 138 additions and 4 deletions

View File

@@ -50,6 +50,14 @@ struct Product: Codable, Identifiable, Hashable {
let baseUnit: String
let packageSize: Double?
let packageLabel: String?
/// Zweiteinheit: secondaryCount Basiseinheiten secondaryAmount
/// secondaryBase". Bruecke zwischen den sonst strikt getrennten Arten.
/// Optionals, damit die App auch gegen einen aelteren Server laeuft.
let secondaryBase: String?
let secondaryCount: Double?
let secondaryAmount: Double?
/// Vom Server ausgerechnet: Zweit-Basiseinheiten je EINER Basiseinheit.
let secondaryFactor: Double?
let groupId: Int?
let minStock: Double?
let stock: Double
@@ -98,6 +106,10 @@ struct Product: Codable, Identifiable, Hashable {
case baseUnit = "base_unit"
case packageSize = "package_size"
case packageLabel = "package_label"
case secondaryBase = "secondary_base"
case secondaryCount = "secondary_count"
case secondaryAmount = "secondary_amount"
case secondaryFactor = "secondary_factor"
case groupId = "group_id"
case minStock = "min_stock"
case expiredCount = "expired_count"
@@ -134,6 +146,38 @@ struct Product: Codable, Identifiable, Hashable {
var stockInArticleUnits: Double { stock / articleUnitFactor }
/// Zweit-Basiseinheiten je EINER Basiseinheit nil ohne Bruecke.
var zweitFaktor: Double? {
guard let basis = secondaryBase, !basis.isEmpty, basis != baseUnit else { return nil }
if let f = secondaryFactor, f > 0 { return f }
// Rueckfall, falls das abgeleitete Feld fehlt (aelterer Server).
guard let n = secondaryCount, let m = secondaryAmount, n > 0, m > 0 else { return nil }
return m / n
}
/// Art der Zweiteinheit ("count"/"weight"/"volume") fuer die Einheitenwahl.
var zweitKind: String? {
switch secondaryBase {
case "piece": return "count"
case "gram": return "weight"
case "milliliter": return "volume"
default: return nil
}
}
/// Kuerzel der Zweit-Basiseinheit fuer die Anzeige.
var zweitKurz: String {
switch secondaryBase {
case "gram": return "g"
case "milliliter": return "ml"
case "piece": return "Stück"
default: return ""
}
}
/// Bestand in der Zweiteinheit, z.B. 500 g bei 6 Stueck.
var stockInZweit: Double? { zweitFaktor.map { stock * $0 } }
/// Wie es um den Mindestbestand steht. Beides in Basiseinheiten verglichen.
enum StockLevel { case none, ok, close, below }
@@ -585,6 +629,12 @@ struct ProductUpdateRequest: Encodable {
var individual: Bool? = nil
var bulk: Bool? = nil
var sendMode = false
// Zweiteinheit ebenfalls nur auf Wunsch ein PATCH aus einer anderen Maske
// soll die Bruecke nicht unbeabsichtigt loeschen.
var secondaryBase: String? = nil
var secondaryCount: Double? = nil
var secondaryAmount: Double? = nil
var sendSecondary = false
enum CodingKeys: String, CodingKey {
case name, brand, individual, bulk
@@ -600,6 +650,9 @@ struct ProductUpdateRequest: Encodable {
case minStockInPackages = "min_stock_in_packages"
case minStockUnitId = "min_stock_unit_id"
case baseUnit = "base_unit"
case secondaryBase = "secondary_base"
case secondaryCount = "secondary_count"
case secondaryAmount = "secondary_amount"
}
func encode(to encoder: Encoder) throws {
@@ -625,6 +678,11 @@ struct ProductUpdateRequest: Encodable {
try container.encode(bulk, forKey: .bulk)
if let baseUnit { try container.encode(baseUnit, forKey: .baseUnit) }
}
if sendSecondary {
try container.encode(secondaryBase, forKey: .secondaryBase)
try container.encode(secondaryCount, forKey: .secondaryCount)
try container.encode(secondaryAmount, forKey: .secondaryAmount)
}
}
}