Lagerorte per 10-Zeichen-Code statt fortlaufender ID; iOS-Einlagern ohne Kamerazwang

Lagerort-IDs sind jetzt ein zufaelliger 10-Zeichen-Code (wie die Einzelstueck-UIDs) statt einer fortlaufenden Zahl - so kollidiert die Stammdaten-Sicherung zwischen zwei Instanzen praktisch nie mehr, und der Code ist zugleich der Inhalt des QR /l/<code>. Alle Fremdschluessel (lots, movements, items, Mindestbestaende, parent_id) ziehen mit; die Umstellung laeuft einmalig und transaktional beim Serverstart (_migrate_locations_to_code) und rollt bei Fehlern komplett zurueck. Vor dem Deploy ein DB-Backup machen.

iOS-Einlagern oeffnet nicht mehr sofort die Kamera, sondern ein Formular mit Artikelsuche; die Kamera kommt erst per Button. Im Formular laesst sich der Lagerort zusaetzlich per /l/-QR scannen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-07-27 12:15:25 +02:00
parent 6e48cd0a7a
commit d518b4aa23
23 changed files with 507 additions and 220 deletions

View File

@@ -15,19 +15,19 @@ struct ObjectStockSection: View {
enum StockSheet: Identifiable {
case add
case relocate(from: Int?)
case remove(loc: Int?)
case relocate(from: String?)
case remove(loc: String?)
var id: String {
switch self {
case .add: return "add"
case .relocate(let f): return "relocate-\(f.map(String.init) ?? "none")"
case .remove(let l): return "remove-\(l.map(String.init) ?? "none")"
case .relocate(let f): return "relocate-\(f ?? "none")"
case .remove(let l): return "remove-\(l ?? "none")"
}
}
}
private var einheit: String { product.unitName.isEmpty ? "Stück" : product.unitName }
private func ortName(_ id: Int?) -> String {
private func ortName(_ id: String?) -> String {
guard let id else { return "Ohne Lagerort" }
return locations.first { $0.id == id }?.name ?? "Ort \(id)"
}
@@ -170,12 +170,12 @@ struct ObjectFieldRow: View {
private struct LocationPicker: View {
let title: String
let locations: [StorageLocation]
@Binding var selection: Int?
@Binding var selection: String?
var body: some View {
Picker(title, selection: $selection) {
Text(" ohne Lagerort ").tag(Int?.none)
ForEach(locations) { loc in Text(loc.name).tag(Int?.some(loc.id)) }
Text(" ohne Lagerort ").tag(String?.none)
ForEach(locations) { loc in Text(loc.name).tag(String?.some(loc.id)) }
}
}
}
@@ -188,7 +188,7 @@ struct ObjectAddSheet: View {
var perform: () async -> Void
@Environment(\.dismiss) private var dismiss
@State private var locationId: Int?
@State private var locationId: String?
@State private var quantity = ""
@State private var busy = false
@State private var error: String?
@@ -229,12 +229,12 @@ struct ObjectAddSheet: View {
struct ObjectRelocateSheet: View {
let product: Product
let locations: [StorageLocation]
let initialFrom: Int?
let initialFrom: String?
var perform: () async -> Void
@Environment(\.dismiss) private var dismiss
@State private var fromId: Int?
@State private var toId: Int?
@State private var fromId: String?
@State private var toId: String?
@State private var quantity = ""
@State private var busy = false
@State private var error: String?
@@ -277,11 +277,11 @@ struct ObjectRemoveSheet: View {
let product: Product
let locations: [StorageLocation]
let einheit: String
let initialLoc: Int?
let initialLoc: String?
var perform: () async -> Void
@Environment(\.dismiss) private var dismiss
@State private var locationId: Int?
@State private var locationId: String?
@State private var quantity = ""
@State private var reason = "broken"
@State private var note = ""