diff --git a/ios/Sources/ObjectStockView.swift b/ios/Sources/ObjectStockView.swift index 910eb94..0270f1e 100644 --- a/ios/Sources/ObjectStockView.swift +++ b/ios/Sources/ObjectStockView.swift @@ -4,27 +4,29 @@ import SwiftUI /// Lebensmittel. Erlaubt Hinzufügen, Umlagern (ohne Grund) und Entfernen (mit /// Pflicht-Grund) und zeigt eine kleine Entnahme-Statistik. Wird als Abschnitt /// in die Produkt-Detailansicht eingebettet. +/// Welches Mengen-Sheet gerade offen ist. Wird auf der stabilen Form-Ebene der +/// Detailansicht präsentiert (nicht im Abschnitt selbst), sonst schloss sich das +/// Fenster beim ersten Öffnen sofort wieder (das spät fertige Laden der +/// Entnahme-Statistik hat das frische Sheet weggerissen). +enum ObjectStockSheet: Identifiable { + case add + case relocate(from: String?) + case remove(loc: String?) + var id: String { + switch self { + case .add: return "add" + case .relocate(let f): return "relocate-\(f ?? "none")" + case .remove(let l): return "remove-\(l ?? "none")" + } + } +} + struct ObjectStockSection: View { let product: Product let locations: [StorageLocation] let lots: [Lot] - var onChanged: () async -> Void - - @State private var removals: RemovalSummary? - @State private var sheet: StockSheet? - - enum StockSheet: Identifiable { - case add - case relocate(from: String?) - case remove(loc: String?) - var id: String { - switch self { - case .add: return "add" - case .relocate(let f): return "relocate-\(f ?? "none")" - case .remove(let l): return "remove-\(l ?? "none")" - } - } - } + let removals: RemovalSummary? + @Binding var sheet: ObjectStockSheet? private var einheit: String { product.unitName.isEmpty ? "Stück" : product.unitName } private func ortName(_ id: String?) -> String { @@ -63,23 +65,6 @@ struct ObjectStockSection: View { .buttonStyle(.borderless) .font(.callout) } - // Sheet an den Abschnitt hängen (nicht an die Group) – sonst ging das - // Fenster bei manchen iOS-Versionen kurz auf und sofort wieder zu. - .sheet(item: $sheet) { welche in - NavigationStack { - switch welche { - case .add: - ObjectAddSheet(product: product, locations: locations, einheit: einheit, - perform: { await afterAction() }) - case .relocate(let from): - ObjectRelocateSheet(product: product, locations: locations, initialFrom: from, - perform: { await afterAction() }) - case .remove(let loc): - ObjectRemoveSheet(product: product, locations: locations, einheit: einheit, - initialLoc: loc, perform: { await afterAction() }) - } - } - } if let removals, !removals.stats.isEmpty { Section("Bereits entnommen") { @@ -90,17 +75,6 @@ struct ObjectStockSection: View { } } } - .task(id: product.id) { await loadRemovals() } - .onChange(of: lots) { _ in Task { await loadRemovals() } } - } - - private func loadRemovals() async { - removals = try? await APIClient.shared.productRemovals(id: product.id) - } - - private func afterAction() async { - await onChanged() - await loadRemovals() } } diff --git a/ios/Sources/ProductDetailView.swift b/ios/Sources/ProductDetailView.swift index 5b96c69..362c3c9 100644 --- a/ios/Sources/ProductDetailView.swift +++ b/ios/Sources/ProductDetailView.swift @@ -38,6 +38,10 @@ struct ProductDetailView: View { @State private var fieldValues: [String: String] = [:] @State private var editLot: Lot? + // Mengen-Bestand (Menge je Lagerort): Sheet + Entnahme-Statistik werden hier + // gehalten und auf Form-Ebene präsentiert, damit das Sheet stabil aufgeht. + @State private var objectSheet: ObjectStockSheet? + @State private var objectRemovals: RemovalSummary? private let labels = ["", "Glas", "Tüte", "Flasche", "Dose", "Tube", "Becher", "Beutel", "Karton"] @@ -92,7 +96,7 @@ struct ProductDetailView: View { } else if current.isObject && !current.isBulk { // Menge je Lagerort (Verbrauchsgegenstände laufen als Charge, s.u.). ObjectStockSection(product: current, locations: locations, lots: lots, - onChanged: { await reload() }) + removals: objectRemovals, sheet: $objectSheet) } else { // Lebensmittel und Verbrauchsgegenstände: Vorrat aus den Chargen. Section("Bestand") { @@ -287,6 +291,25 @@ struct ProductDetailView: View { } } } + // Mengen-Sheets auf der stabilen Form-Ebene (nicht im Abschnitt), sonst + // schloss sich das Fenster beim ersten Öffnen sofort wieder. + .sheet(item: $objectSheet) { welche in + NavigationStack { + switch welche { + case .add: + ObjectAddSheet(product: current, locations: locations, + einheit: current.unitName.isEmpty ? "Stück" : current.unitName, + perform: { await reload() }) + case .relocate(let from): + ObjectRelocateSheet(product: current, locations: locations, initialFrom: from, + perform: { await reload() }) + case .remove(let loc): + ObjectRemoveSheet(product: current, locations: locations, + einheit: current.unitName.isEmpty ? "Stück" : current.unitName, + initialLoc: loc, perform: { await reload() }) + } + } + } .task { fillForm() await reload() @@ -355,6 +378,12 @@ struct ProductDetailView: View { } else { fieldDefs = [] } + // Entnahme-Statistik nur für „Menge je Lagerort"-Gegenstände. + if current.isObject && !current.isBulk && !current.isIndividual { + objectRemovals = try? await APIClient.shared.productRemovals(id: current.id) + } else { + objectRemovals = nil + } } /// Verwaltungsart (individual/bulk) mitschicken, wenn sie umgestellt wurde.