"""Mindestbestaende: „Ueberall" als Ort und die einmalige Datenwanderung. Mindestbestaende haengen ausschliesslich an Lagerorten. Der frueher separate Gesamt-Mindestbestand (``Product.min_stock`` / ``Group.min_stock``) ist jetzt eine Zeile mit ``location_id = NULL`` – „Ueberall": egal wo, Hauptsache die Menge ist im Haus. Damit ist Ueberall die Wurzel ueber allen Lagerorten und wird von derselben Verrechnung erfasst wie verschachtelte Orte. Alle Mengen stehen in BASISEINHEITEN. Vorher waren es drei verschiedene Einheiten nebeneinander (Gesamt in Basiseinheiten, je Ort in Artikeleinheiten, Gruppen in der Gruppen-Erfassungseinheit) – das machte jede Umrechnung zur Fehlerquelle und liess gespeicherte Werte ihre Bedeutung aendern, sobald sich eine Packungsgroesse aenderte. """ from __future__ import annotations from collections.abc import Iterable from sqlalchemy.orm import Session from ..models import ( Group, GroupLocationMinStock, Product, ProductLocationMinStock, Setting, ) from .conversion import article_unit, group_min_context #: Anzeigename des Ortes NULL – hier und in der Oberflaeche derselbe Begriff. UEBERALL_NAME = "Überall" #: Merker, dass die Datenwanderung gelaufen ist. Schritt 1 (Umrechnen der #: vorhandenen Werte) waere sonst nicht wiederholungsfest. MIGRATION_KEY = "min_stock_basis_v2" def lies_ueberall(eintraege: Iterable) -> float | None: """Der „Ueberall"-Mindestbestand (Ort NULL) aus den Ort-Eintraegen. Ersetzt die frueheren Spalten ``Product.min_stock``/``Group.min_stock``. Ergebnis in Basiseinheiten; ``None`` = kein Ueberall-Bedarf hinterlegt. """ for e in eintraege: if e.location_id is None: return e.min_stock return None def schreibe_ueberall(db: Session, besitzer, wert: float | None) -> None: """Den „Ueberall"-Mindestbestand setzen oder (bei None/0) entfernen. ``besitzer`` ist ein ``Product`` oder eine ``Group`` – beide haben eine ``location_min_stocks``-Beziehung mit derselben Form. Menge in Basiseinheiten. """ vorhanden = next( (e for e in besitzer.location_min_stocks if e.location_id is None), None ) if wert is None or wert <= 0: if vorhanden is not None: besitzer.location_min_stocks.remove(vorhanden) return if vorhanden is not None: vorhanden.min_stock = wert return if isinstance(besitzer, Product): neu = ProductLocationMinStock( product_id=besitzer.id, location_id=None, min_stock=wert ) else: neu = GroupLocationMinStock( group_id=besitzer.id, location_id=None, min_stock=wert ) besitzer.location_min_stocks.append(neu) def migriere_mindestbestaende(db: Session) -> None: """Einmalig: Gesamt-Werte zu „Ueberall" machen, alles in Basiseinheiten. Laeuft beim Start (siehe main.py) und tut beim zweiten Mal nichts mehr. """ if db.get(Setting, MIGRATION_KEY) is not None: return # 1. Bestehende Ort-Zeilen umrechnen: Artikel- bzw. Gruppeneinheit -> # Basiseinheiten. Der einzige Schritt, der bei doppelter Ausfuehrung # falsche Werte erzeugen wuerde – daher der Merker unten. for eintrag in db.query(ProductLocationMinStock).all(): product = db.get(Product, eintrag.product_id) if product is None: continue faktor, _ = article_unit(product) eintrag.min_stock = eintrag.min_stock * (faktor or 1.0) for eintrag in db.query(GroupLocationMinStock).all(): group = db.get(Group, eintrag.group_id) if group is None: continue eintrag.min_stock = eintrag.min_stock * (group_min_context(group).divisor or 1.0) # 2. Artikel-Gesamtwerte werden zur Ueberall-Zeile. ``Product.min_stock`` # stand bereits in Basiseinheiten, also keine Umrechnung. artikel = ( db.query(Product) .filter(Product.min_stock.isnot(None), Product.min_stock > 0) .all() ) for product in artikel: schon_da = ( db.query(ProductLocationMinStock) .filter( ProductLocationMinStock.product_id == product.id, ProductLocationMinStock.location_id.is_(None), ) .first() ) if schon_da is None: db.add( ProductLocationMinStock( product_id=product.id, location_id=None, min_stock=product.min_stock ) ) product.min_stock = None # 3. Gruppen-Gesamtwerte genauso – die stehen in der Erfassungseinheit der # Gruppe und muessen deshalb ueber den Divisor. gruppen = db.query(Group).filter(Group.min_stock.isnot(None), Group.min_stock > 0).all() for group in gruppen: divisor = group_min_context(group).divisor or 1.0 schon_da = ( db.query(GroupLocationMinStock) .filter( GroupLocationMinStock.group_id == group.id, GroupLocationMinStock.location_id.is_(None), ) .first() ) if schon_da is None: db.add( GroupLocationMinStock( group_id=group.id, location_id=None, min_stock=group.min_stock * divisor, ) ) group.min_stock = None db.add(Setting(key=MIGRATION_KEY, value="done")) db.commit()