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:
@@ -181,7 +181,7 @@ struct TreeMasterView<Item: TreeItem, Header: View, Editor: View>: View {
|
||||
let title: String
|
||||
let singular: String
|
||||
let load: () async throws -> [Item]
|
||||
let delete: (Int) async throws -> Void
|
||||
let delete: (Item.ID) async throws -> Void
|
||||
/// Optionaler kleiner Hinweis am Zeilenende (z. B. der Kategorie-Typ).
|
||||
let badge: (Item) -> String?
|
||||
/// Nur Einträge, die das erfüllen, werden gezeigt (z. B. der Typ-Filter).
|
||||
@@ -190,7 +190,7 @@ struct TreeMasterView<Item: TreeItem, Header: View, Editor: View>: View {
|
||||
@ViewBuilder let editor: (_ editing: Item?, _ all: [Item], _ done: @escaping () -> Void) -> Editor
|
||||
|
||||
@State private var items: [Item] = []
|
||||
@State private var collapsed: Set<Int> = []
|
||||
@State private var collapsed: Set<Item.ID> = []
|
||||
@State private var busy = true
|
||||
@State private var error: String?
|
||||
@State private var editing: Item?
|
||||
@@ -200,7 +200,7 @@ struct TreeMasterView<Item: TreeItem, Header: View, Editor: View>: View {
|
||||
private struct Node: Identifiable {
|
||||
let item: Item
|
||||
let depth: Int
|
||||
var id: Int { item.id }
|
||||
var id: Item.ID { item.id }
|
||||
}
|
||||
|
||||
private var shown: [Item] { items.filter(include) }
|
||||
@@ -221,11 +221,11 @@ struct TreeMasterView<Item: TreeItem, Header: View, Editor: View>: View {
|
||||
return result
|
||||
}
|
||||
|
||||
private func childCount(_ id: Int) -> Int { shown.filter { $0.parentId == id }.count }
|
||||
private func childCount(_ id: Item.ID) -> Int { shown.filter { $0.parentId == id }.count }
|
||||
|
||||
private var visible: [Node] {
|
||||
let parent = Dictionary(uniqueKeysWithValues: shown.map { ($0.id, $0.parentId) })
|
||||
func hidden(_ id: Int) -> Bool {
|
||||
func hidden(_ id: Item.ID) -> Bool {
|
||||
var p = parent[id] ?? nil
|
||||
while let cur = p {
|
||||
if collapsed.contains(cur) { return true }
|
||||
@@ -324,7 +324,7 @@ struct TreeMasterView<Item: TreeItem, Header: View, Editor: View>: View {
|
||||
.padding(.leading, CGFloat(node.depth) * 16)
|
||||
}
|
||||
|
||||
private func toggle(_ id: Int) {
|
||||
private func toggle(_ id: Item.ID) {
|
||||
if collapsed.contains(id) { collapsed.remove(id) } else { collapsed.insert(id) }
|
||||
}
|
||||
|
||||
@@ -441,7 +441,7 @@ struct LocationEditor: View {
|
||||
let done: () -> Void
|
||||
|
||||
@State private var name: String
|
||||
@State private var parentId: Int?
|
||||
@State private var parentId: String?
|
||||
@State private var error: String?
|
||||
@State private var busy = false
|
||||
|
||||
@@ -467,9 +467,9 @@ struct LocationEditor: View {
|
||||
}
|
||||
Section {
|
||||
Picker("Übergeordnet", selection: $parentId) {
|
||||
Text("– oberste Ebene –").tag(Int?.none)
|
||||
Text("– oberste Ebene –").tag(String?.none)
|
||||
ForEach(parentOptions) { loc in
|
||||
Text(LocationEditor.pfad(loc, in: all)).tag(Int?.some(loc.id))
|
||||
Text(LocationEditor.pfad(loc, in: all)).tag(String?.some(loc.id))
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
@@ -513,8 +513,8 @@ struct LocationEditor: View {
|
||||
}
|
||||
|
||||
/// Alle Unterorte (rekursiv) – als Ziel beim Umhängen ausgeschlossen.
|
||||
private static func descendants(of id: Int, in all: [StorageLocation]) -> Set<Int> {
|
||||
var result: Set<Int> = []
|
||||
private static func descendants(of id: String, in all: [StorageLocation]) -> Set<String> {
|
||||
var result: Set<String> = []
|
||||
var stack = [id]
|
||||
while let cur = stack.popLast() {
|
||||
for kid in all where kid.parentId == cur {
|
||||
|
||||
Reference in New Issue
Block a user