iOS: Verbrauchsgegenstand als dritte Gegenstands-Art (wie ein Lebensmittel)
Neuer Artikel: 3-Wege-Verwaltung fuer Gegenstaende (Menge je Lagerort / Einzelstuecke / Verbrauchsgegenstand). Verbrauchsgegenstaende laufen wie Lebensmittel als Charge (Einheit, Packung, MHD, Gruppe, Chargen-Ansicht, Mindestbestand mit Einheit) - foodLike = kein Gegenstand ODER bulk. Menge- Gegenstaende koennen jetzt ebenfalls einer Gruppe zugeordnet werden. ProductDetailView entsprechend auf foodLike umgestellt; NewProductRequest und ProductUpdateRequest um bulk/individual/base_unit erweitert. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -78,11 +78,13 @@ struct Product: Codable, Identifiable, Hashable {
|
|||||||
let fieldValues: [String: String?]?
|
let fieldValues: [String: String?]?
|
||||||
/// Gegenstand als Einzelstücke (Items mit UID/QR) statt als Menge geführt.
|
/// Gegenstand als Einzelstücke (Items mit UID/QR) statt als Menge geführt.
|
||||||
let individual: Bool?
|
let individual: Bool?
|
||||||
|
/// Gegenstand als Verbrauchsgegenstand wie ein Lebensmittel (Charge) geführt.
|
||||||
|
let bulk: Bool?
|
||||||
/// Mindestbestand je Lagerort (zusaetzlich zum globalen Mindestbestand).
|
/// Mindestbestand je Lagerort (zusaetzlich zum globalen Mindestbestand).
|
||||||
let locationMinStocks: [LocationMinStock]?
|
let locationMinStocks: [LocationMinStock]?
|
||||||
|
|
||||||
enum CodingKeys: String, CodingKey {
|
enum CodingKeys: String, CodingKey {
|
||||||
case id, barcode, name, brand, stock, kind, barcodes, tracking, individual
|
case id, barcode, name, brand, stock, kind, barcodes, tracking, individual, bulk
|
||||||
case locationMinStocks = "location_min_stocks"
|
case locationMinStocks = "location_min_stocks"
|
||||||
case datePrecision = "date_precision"
|
case datePrecision = "date_precision"
|
||||||
case categoryId = "category_id"
|
case categoryId = "category_id"
|
||||||
@@ -109,6 +111,11 @@ struct Product: Codable, Identifiable, Hashable {
|
|||||||
var isObject: Bool { tracking == "object" }
|
var isObject: Bool { tracking == "object" }
|
||||||
/// Gegenstand, der als Einzelstücke (UID/QR) geführt wird?
|
/// Gegenstand, der als Einzelstücke (UID/QR) geführt wird?
|
||||||
var isIndividual: Bool { individual == true }
|
var isIndividual: Bool { individual == true }
|
||||||
|
/// Verbrauchsgegenstand: Gegenstand, der wie ein Lebensmittel als Charge läuft.
|
||||||
|
var isBulk: Bool { bulk == true }
|
||||||
|
/// Nutzt die Lebensmittel-Pfade (Chargen, Einheit, MHD, Mindestbestand):
|
||||||
|
/// echte Lebensmittel UND Verbrauchsgegenstände.
|
||||||
|
var foodLike: Bool { !isObject || isBulk }
|
||||||
|
|
||||||
/// Bezeichnung der Artikeleinheit (Gebinde, sonst Produkteinheit).
|
/// Bezeichnung der Artikeleinheit (Gebinde, sonst Produkteinheit).
|
||||||
var articleUnitLabel: String {
|
var articleUnitLabel: String {
|
||||||
@@ -300,9 +307,11 @@ struct NewProductRequest: Codable {
|
|||||||
let groupId: Int?
|
let groupId: Int?
|
||||||
let categoryId: Int?
|
let categoryId: Int?
|
||||||
var individual: Bool? = nil
|
var individual: Bool? = nil
|
||||||
|
/// Verbrauchsgegenstand: Gegenstand wie ein Lebensmittel (Charge) geführt.
|
||||||
|
var bulk: Bool? = nil
|
||||||
|
|
||||||
enum CodingKeys: String, CodingKey {
|
enum CodingKeys: String, CodingKey {
|
||||||
case barcode, name, brand, individual
|
case barcode, name, brand, individual, bulk
|
||||||
case imageUrl = "image_url"
|
case imageUrl = "image_url"
|
||||||
case baseUnit = "base_unit"
|
case baseUnit = "base_unit"
|
||||||
case packageSize = "package_size"
|
case packageSize = "package_size"
|
||||||
@@ -500,9 +509,15 @@ struct ProductUpdateRequest: Encodable {
|
|||||||
var minStockInPackages: Bool? = nil
|
var minStockInPackages: Bool? = nil
|
||||||
var minStockUnitId: Int? = nil
|
var minStockUnitId: Int? = nil
|
||||||
var sendMinStock = false
|
var sendMinStock = false
|
||||||
|
// Verwaltungsart eines Gegenstands (Einzelstück/Verbrauchsgegenstand) und die
|
||||||
|
// Basiseinheit nur senden, wenn ausdrücklich gewünscht – sonst nichts ändern.
|
||||||
|
var baseUnit: String? = nil
|
||||||
|
var individual: Bool? = nil
|
||||||
|
var bulk: Bool? = nil
|
||||||
|
var sendMode = false
|
||||||
|
|
||||||
enum CodingKeys: String, CodingKey {
|
enum CodingKeys: String, CodingKey {
|
||||||
case name, brand
|
case name, brand, individual, bulk
|
||||||
case packageSize = "package_size"
|
case packageSize = "package_size"
|
||||||
case packageLabel = "package_label"
|
case packageLabel = "package_label"
|
||||||
case datePrecision = "date_precision"
|
case datePrecision = "date_precision"
|
||||||
@@ -514,6 +529,7 @@ struct ProductUpdateRequest: Encodable {
|
|||||||
case minStock = "min_stock"
|
case minStock = "min_stock"
|
||||||
case minStockInPackages = "min_stock_in_packages"
|
case minStockInPackages = "min_stock_in_packages"
|
||||||
case minStockUnitId = "min_stock_unit_id"
|
case minStockUnitId = "min_stock_unit_id"
|
||||||
|
case baseUnit = "base_unit"
|
||||||
}
|
}
|
||||||
|
|
||||||
func encode(to encoder: Encoder) throws {
|
func encode(to encoder: Encoder) throws {
|
||||||
@@ -534,6 +550,11 @@ struct ProductUpdateRequest: Encodable {
|
|||||||
try container.encode(minStockInPackages, forKey: .minStockInPackages)
|
try container.encode(minStockInPackages, forKey: .minStockInPackages)
|
||||||
try container.encode(minStockUnitId, forKey: .minStockUnitId)
|
try container.encode(minStockUnitId, forKey: .minStockUnitId)
|
||||||
}
|
}
|
||||||
|
if sendMode {
|
||||||
|
try container.encode(individual, forKey: .individual)
|
||||||
|
try container.encode(bulk, forKey: .bulk)
|
||||||
|
if let baseUnit { try container.encode(baseUnit, forKey: .baseUnit) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,16 +50,19 @@ struct ProductDetailView: View {
|
|||||||
if name != current.name { return true }
|
if name != current.name { return true }
|
||||||
if brand != (current.brand ?? "") { return true }
|
if brand != (current.brand ?? "") { return true }
|
||||||
if categoryId != current.categoryId { return true }
|
if categoryId != current.categoryId { return true }
|
||||||
if current.isObject {
|
// Gruppe gibt es für alles außer Einzelstücke.
|
||||||
if !current.isIndividual, shopId != current.shopId { return true }
|
if !current.isIndividual, groupId != current.groupId { return true }
|
||||||
if productUrl != (current.productUrl ?? "") { return true }
|
// Packung/MHD nur bei Lebensmitteln und Verbrauchsgegenständen.
|
||||||
if minStock != (current.minStockDisplay.map { formatAmount($0) } ?? "") { return true }
|
if current.foodLike {
|
||||||
if fieldValues != Self.stringValues(current.fieldValues) { return true }
|
|
||||||
} else {
|
|
||||||
if packageSize != (current.packageSize.map { formatAmount($0) } ?? "") { return true }
|
if packageSize != (current.packageSize.map { formatAmount($0) } ?? "") { return true }
|
||||||
if packageLabel != (current.packageLabel ?? "") { return true }
|
if packageLabel != (current.packageLabel ?? "") { return true }
|
||||||
if datePrecision != (current.datePrecision == "month" ? "month" : "day") { return true }
|
if datePrecision != (current.datePrecision == "month" ? "month" : "day") { return true }
|
||||||
if groupId != current.groupId { return true }
|
}
|
||||||
|
if current.isObject {
|
||||||
|
if !current.isIndividual && !current.isBulk, shopId != current.shopId { return true }
|
||||||
|
if !current.isBulk, productUrl != (current.productUrl ?? "") { return true }
|
||||||
|
if minStock != (current.minStockDisplay.map { formatAmount($0) } ?? "") { return true }
|
||||||
|
if fieldValues != Self.stringValues(current.fieldValues) { return true }
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -82,10 +85,12 @@ struct ProductDetailView: View {
|
|||||||
if current.isObject && current.isIndividual {
|
if current.isObject && current.isIndividual {
|
||||||
ProductItemsSection(product: current, onChanged: { await reload() },
|
ProductItemsSection(product: current, onChanged: { await reload() },
|
||||||
onFinish: { dismiss() })
|
onFinish: { dismiss() })
|
||||||
} else if current.isObject {
|
} else if current.isObject && !current.isBulk {
|
||||||
|
// Menge je Lagerort (Verbrauchsgegenstände laufen als Charge, s.u.).
|
||||||
ObjectStockSection(product: current, locations: locations, lots: lots,
|
ObjectStockSection(product: current, locations: locations, lots: lots,
|
||||||
onChanged: { await reload() })
|
onChanged: { await reload() })
|
||||||
} else {
|
} else {
|
||||||
|
// Lebensmittel und Verbrauchsgegenstände: Vorrat aus den Chargen.
|
||||||
Section("Bestand") {
|
Section("Bestand") {
|
||||||
LabeledContent("Vorrat",
|
LabeledContent("Vorrat",
|
||||||
value: "\(formatAmount(current.stockInArticleUnits)) \(current.articleUnitLabel)")
|
value: "\(formatAmount(current.stockInArticleUnits)) \(current.articleUnitLabel)")
|
||||||
@@ -101,7 +106,7 @@ struct ProductDetailView: View {
|
|||||||
Section("Artikel") {
|
Section("Artikel") {
|
||||||
LabeledField(label: "Name", text: $name)
|
LabeledField(label: "Name", text: $name)
|
||||||
LabeledField(label: "Marke", text: $brand)
|
LabeledField(label: "Marke", text: $brand)
|
||||||
if !current.isObject {
|
if current.foodLike {
|
||||||
QuantityField(label: "Packungsgröße", text: $packageSize,
|
QuantityField(label: "Packungsgröße", text: $packageSize,
|
||||||
suffix: display.baseUnitLabel(current.baseUnit))
|
suffix: display.baseUnitLabel(current.baseUnit))
|
||||||
Picker("Bezeichnung", selection: $packageLabel) {
|
Picker("Bezeichnung", selection: $packageLabel) {
|
||||||
@@ -123,7 +128,9 @@ struct ProductDetailView: View {
|
|||||||
: "Nur Lebensmittel-Kategorien.")
|
: "Nur Lebensmittel-Kategorien.")
|
||||||
}
|
}
|
||||||
|
|
||||||
if current.isObject {
|
// Bezugsquelle/Link nur für Menge je Lagerort und Einzelstücke –
|
||||||
|
// Verbrauchsgegenstände laufen wie Lebensmittel und lassen das weg.
|
||||||
|
if current.isObject && !current.isBulk {
|
||||||
// „Gekauft bei" gehört bei Einzelstücken zum einzelnen Stück
|
// „Gekauft bei" gehört bei Einzelstücken zum einzelnen Stück
|
||||||
// (jedes kann woanders gekauft sein), nicht ans Modell – wie im
|
// (jedes kann woanders gekauft sein), nicht ans Modell – wie im
|
||||||
// Web wird der Shop hier dann ausgeblendet. Der Produktlink
|
// Web wird der Shop hier dann ausgeblendet. Der Produktlink
|
||||||
@@ -140,14 +147,10 @@ struct ProductDetailView: View {
|
|||||||
Link("Im Onlineshop öffnen", destination: url)
|
Link("Im Onlineshop öffnen", destination: url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !fieldDefs.isEmpty {
|
}
|
||||||
Section("Eigene Felder") {
|
// Gruppe für alles außer Einzelstücke (Lebensmittel, Verbrauchs-
|
||||||
ForEach(fieldDefs) { def in
|
// gegenstand, Menge je Lagerort).
|
||||||
ObjectFieldRow(def: def, value: fieldBinding(def))
|
if !current.isIndividual {
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Section {
|
Section {
|
||||||
Picker("Gruppe", selection: $groupId) {
|
Picker("Gruppe", selection: $groupId) {
|
||||||
Text("– keine –").tag(Int?.none)
|
Text("– keine –").tag(Int?.none)
|
||||||
@@ -156,14 +159,23 @@ struct ProductDetailView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} footer: {
|
} footer: {
|
||||||
Text("Gruppe: zählt Bestände mehrerer Marken zusammen. Der EAN-Code "
|
Text("Gruppe: zählt Bestände mehrerer Artikel zusammen. Der EAN-Code "
|
||||||
+ "wandert bei einem Wechsel mit.")
|
+ "wandert bei einem Wechsel mit.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Eigene Felder für alle Gegenstände mit Kategorie-Feldern.
|
||||||
|
if current.isObject && !fieldDefs.isEmpty {
|
||||||
|
Section("Eigene Felder") {
|
||||||
|
ForEach(fieldDefs) { def in
|
||||||
|
ObjectFieldRow(def: def, value: fieldBinding(def))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if current.isObject {
|
if current.isObject {
|
||||||
Section("Mindestbestand") {
|
Section("Mindestbestand") {
|
||||||
QuantityField(label: "Gesamt (Stück)", text: $minStock)
|
QuantityField(label: "Gesamt (\(current.isBulk ? current.articleUnitLabel : "Stück"))",
|
||||||
|
text: $minStock)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,7 +203,7 @@ struct ProductDetailView: View {
|
|||||||
.disabled(busy || name.isEmpty)
|
.disabled(busy || name.isEmpty)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !current.isObject {
|
if current.foodLike {
|
||||||
Section("Chargen") {
|
Section("Chargen") {
|
||||||
ForEach(lots) { lot in
|
ForEach(lots) { lot in
|
||||||
Button {
|
Button {
|
||||||
@@ -314,8 +326,35 @@ struct ProductDetailView: View {
|
|||||||
busy = true
|
busy = true
|
||||||
defer { busy = false }
|
defer { busy = false }
|
||||||
do {
|
do {
|
||||||
if current.isObject {
|
let minBase = Double(minStock.replacingOccurrences(of: ",", with: "."))
|
||||||
// Gegenstände: keine Lebensmittel-Felder, dafür Shop, Link und eigene Felder.
|
.map { $0 * current.articleUnitFactor }
|
||||||
|
if current.foodLike {
|
||||||
|
// Lebensmittel ODER Verbrauchsgegenstand: als Charge mit Einheit/
|
||||||
|
// Packung/MHD/Gruppe. Verbrauchsgegenstände (isObject) tragen
|
||||||
|
// zusätzlich eigene Felder und einen Mindestbestand.
|
||||||
|
var req = ProductUpdateRequest(
|
||||||
|
name: name,
|
||||||
|
brand: brand.isEmpty ? nil : brand,
|
||||||
|
packageSize: Double(packageSize.replacingOccurrences(of: ",", with: ".")),
|
||||||
|
packageLabel: packageLabel.isEmpty ? nil : packageLabel,
|
||||||
|
datePrecision: datePrecision,
|
||||||
|
groupId: groupId,
|
||||||
|
categoryId: categoryId
|
||||||
|
)
|
||||||
|
if current.isObject {
|
||||||
|
var fv: [String: String?] = [:]
|
||||||
|
for def in fieldDefs { fv[String(def.id)] = fieldValues[String(def.id)] ?? "" }
|
||||||
|
req.fieldValues = fv
|
||||||
|
req.minStock = minBase
|
||||||
|
req.minStockInPackages = (current.packageSize ?? 0) > 0
|
||||||
|
req.minStockUnitId = nil
|
||||||
|
req.sendMinStock = true
|
||||||
|
}
|
||||||
|
current = try await APIClient.shared.updateProduct(id: current.id, req)
|
||||||
|
if current.isObject { fieldValues = Self.stringValues(current.fieldValues) }
|
||||||
|
} else {
|
||||||
|
// Gegenstand: Menge je Lagerort oder Einzelstücke. Menge-
|
||||||
|
// Gegenstände dürfen jetzt ebenfalls in eine Gruppe.
|
||||||
var fv: [String: String?] = [:]
|
var fv: [String: String?] = [:]
|
||||||
for def in fieldDefs { fv[String(def.id)] = fieldValues[String(def.id)] ?? "" }
|
for def in fieldDefs { fv[String(def.id)] = fieldValues[String(def.id)] ?? "" }
|
||||||
current = try await APIClient.shared.updateProduct(
|
current = try await APIClient.shared.updateProduct(
|
||||||
@@ -324,32 +363,18 @@ struct ProductDetailView: View {
|
|||||||
name: name,
|
name: name,
|
||||||
brand: brand.isEmpty ? nil : brand,
|
brand: brand.isEmpty ? nil : brand,
|
||||||
packageSize: nil, packageLabel: nil, datePrecision: nil,
|
packageSize: nil, packageLabel: nil, datePrecision: nil,
|
||||||
groupId: nil, categoryId: categoryId,
|
groupId: current.isIndividual ? nil : groupId, categoryId: categoryId,
|
||||||
// Einzelstücke tragen den Shop je Stück – nichts ans Modell.
|
// Einzelstücke tragen den Shop je Stück – nichts ans Modell.
|
||||||
shopId: current.isIndividual ? nil : shopId,
|
shopId: current.isIndividual ? nil : shopId,
|
||||||
productUrl: productUrl.isEmpty ? nil : productUrl,
|
productUrl: productUrl.isEmpty ? nil : productUrl,
|
||||||
fieldValues: fv,
|
fieldValues: fv,
|
||||||
minStock: Double(minStock.replacingOccurrences(of: ",", with: "."))
|
minStock: minBase,
|
||||||
.map { $0 * current.articleUnitFactor },
|
|
||||||
minStockInPackages: (current.packageSize ?? 0) > 0,
|
minStockInPackages: (current.packageSize ?? 0) > 0,
|
||||||
minStockUnitId: nil,
|
minStockUnitId: nil,
|
||||||
sendMinStock: true
|
sendMinStock: true
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
fieldValues = Self.stringValues(current.fieldValues)
|
fieldValues = Self.stringValues(current.fieldValues)
|
||||||
} else {
|
|
||||||
current = try await APIClient.shared.updateProduct(
|
|
||||||
id: current.id,
|
|
||||||
ProductUpdateRequest(
|
|
||||||
name: name,
|
|
||||||
brand: brand.isEmpty ? nil : brand,
|
|
||||||
packageSize: Double(packageSize.replacingOccurrences(of: ",", with: ".")),
|
|
||||||
packageLabel: packageLabel.isEmpty ? nil : packageLabel,
|
|
||||||
datePrecision: datePrecision,
|
|
||||||
groupId: groupId,
|
|
||||||
categoryId: categoryId
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
status = "Gespeichert."
|
status = "Gespeichert."
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
@@ -72,8 +72,9 @@ struct ProductFormView: View {
|
|||||||
@State private var selectedCategoryId: Int?
|
@State private var selectedCategoryId: Int?
|
||||||
// Umschalter Lebensmittel/Gegenstand: filtert die Kategorieauswahl.
|
// Umschalter Lebensmittel/Gegenstand: filtert die Kategorieauswahl.
|
||||||
@State private var modus = "object"
|
@State private var modus = "object"
|
||||||
// Gegenstand als Einzelstücke (UID/QR je Stück) führen?
|
// Verwaltungsart eines Gegenstands: "count" (Menge je Lagerort),
|
||||||
@State private var individual = false
|
// "individual" (Einzelstücke) oder "bulk" (Verbrauchsgegenstand wie Lebensmittel).
|
||||||
|
@State private var objMode = "count"
|
||||||
@State private var groups: [GroupItem] = []
|
@State private var groups: [GroupItem] = []
|
||||||
@State private var categories: [CategoryItem] = []
|
@State private var categories: [CategoryItem] = []
|
||||||
@State private var units: [Unit] = []
|
@State private var units: [Unit] = []
|
||||||
@@ -85,6 +86,13 @@ struct ProductFormView: View {
|
|||||||
// Beschriftungen kommen sonst aus DisplaySettings.baseUnitLabel.
|
// Beschriftungen kommen sonst aus DisplaySettings.baseUnitLabel.
|
||||||
private let labels = ["", "Glas", "Tüte", "Flasche", "Dose", "Tube", "Becher", "Beutel", "Karton"]
|
private let labels = ["", "Glas", "Tüte", "Flasche", "Dose", "Tube", "Becher", "Beutel", "Karton"]
|
||||||
|
|
||||||
|
/// Nutzt die Lebensmittel-Pfade (Einheit, MHD, Charge): Lebensmittel ODER
|
||||||
|
/// Verbrauchsgegenstand.
|
||||||
|
private var foodLike: Bool { modus == "food" || objMode == "bulk" }
|
||||||
|
/// Gruppe anbieten: Lebensmittel, Verbrauchsgegenstand oder Menge je Lagerort
|
||||||
|
/// (nicht bei Einzelstücken).
|
||||||
|
private var showGroup: Bool { modus == "food" || (modus == "object" && objMode != "individual") }
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Form {
|
Form {
|
||||||
Section("Artikel") {
|
Section("Artikel") {
|
||||||
@@ -100,13 +108,19 @@ struct ProductFormView: View {
|
|||||||
.pickerStyle(.segmented)
|
.pickerStyle(.segmented)
|
||||||
CategoryPicker(categories: categories, selection: $selectedCategoryId, tracking: modus)
|
CategoryPicker(categories: categories, selection: $selectedCategoryId, tracking: modus)
|
||||||
if modus == "object" {
|
if modus == "object" {
|
||||||
Toggle("Einzelstücke (UID/QR je Stück)", isOn: $individual)
|
Picker("Verwaltung", selection: $objMode) {
|
||||||
|
Text("Menge je Lagerort").tag("count")
|
||||||
|
Text("Einzelstücke").tag("individual")
|
||||||
|
Text("Verbrauchsgegenstand").tag("bulk")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} footer: {
|
} footer: {
|
||||||
Text(modus == "object"
|
Text(modus == "object"
|
||||||
? (individual
|
? (objMode == "individual"
|
||||||
? "Einzelstücke: jedes Stück mit eigener UID/QR, Kaufdatum, Garantie."
|
? "Einzelstücke: jedes Stück mit eigener UID/QR, Kaufdatum, Garantie."
|
||||||
: "Gegenstand: Menge je Lagerort und eigene Felder.")
|
: objMode == "bulk"
|
||||||
|
? "Verbrauchsgegenstand: wie ein Lebensmittel als Charge mit Menge und Einheit (z.B. Sonnencreme in ml) – nur ohne eindeutigen Code."
|
||||||
|
: "Gegenstand: Menge je Lagerort und eigene Felder.")
|
||||||
: "Lebensmittel: Chargen mit Mindesthaltbarkeit.")
|
: "Lebensmittel: Chargen mit Mindesthaltbarkeit.")
|
||||||
}
|
}
|
||||||
.onChange(of: modus) { neu in
|
.onChange(of: modus) { neu in
|
||||||
@@ -116,9 +130,9 @@ struct ProductFormView: View {
|
|||||||
selectedCategoryId = nil
|
selectedCategoryId = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Lebensmittel-Felder (Gruppe, Einheit, MHD) nur im Lebensmittel-Modus.
|
// Gruppe: Lebensmittel, Verbrauchsgegenstand und Menge je Lagerort
|
||||||
// Fuer Gegenstaende irrelevant - sie zaehlen in Stueck je Lagerort.
|
// (nur Einzelstücke bleiben außen vor).
|
||||||
if modus == "food" {
|
if showGroup {
|
||||||
Section {
|
Section {
|
||||||
Picker("Gruppe", selection: $selectedGroupId) {
|
Picker("Gruppe", selection: $selectedGroupId) {
|
||||||
Text("– keine –").tag(Int?.none)
|
Text("– keine –").tag(Int?.none)
|
||||||
@@ -134,9 +148,12 @@ struct ProductFormView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} footer: {
|
} footer: {
|
||||||
Text("Gruppe: zählt Bestände mehrerer Marken zusammen. Der EAN-Code "
|
Text("Gruppe: zählt Bestände mehrerer Artikel zusammen. Der EAN-Code "
|
||||||
+ "dieses Artikels erscheint danach automatisch bei der Gruppe.")
|
+ "dieses Artikels erscheint danach automatisch bei der Gruppe.")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
// Einheit + MHD nur für Lebensmittel und Verbrauchsgegenstände (Charge).
|
||||||
|
if foodLike {
|
||||||
Section("Einheit") {
|
Section("Einheit") {
|
||||||
Picker("Basiseinheit", selection: $baseUnit) {
|
Picker("Basiseinheit", selection: $baseUnit) {
|
||||||
ForEach(baseUnits, id: \.0) { Text($0.1).tag($0.0) }
|
ForEach(baseUnits, id: \.0) { Text($0.1).tag($0.0) }
|
||||||
@@ -217,7 +234,8 @@ struct ProductFormView: View {
|
|||||||
datePrecision: datePrecision,
|
datePrecision: datePrecision,
|
||||||
groupId: selectedGroupId,
|
groupId: selectedGroupId,
|
||||||
categoryId: selectedCategoryId,
|
categoryId: selectedCategoryId,
|
||||||
individual: modus == "object" ? individual : nil
|
individual: modus == "object" ? (objMode == "individual") : nil,
|
||||||
|
bulk: modus == "object" ? (objMode == "bulk") : nil
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
onCreated(product)
|
onCreated(product)
|
||||||
|
|||||||
Reference in New Issue
Block a user