"""Gefahrenbereich: Daten in großem Stil löschen. Bewusst in drei Stufen, damit man nicht mehr wegwirft als nötig: 1. ``/reset/stock`` – Chargen und Bewegungsverlauf. Der Katalog bleibt. 2. ``/reset/products`` – zusätzlich alle Artikel samt EAN-Codes. 3. ``/reset/all`` – zusätzlich Gruppen, Kategorien, Lagerorte, Einheiten. Benutzer, API-Tokens, Einstellungen und das eigene Logo bleiben in **allen** Stufen erhalten: Sonst würde man sich aussperren und müsste externe Zugriffe (Home Assistant) neu einrichten. Nach Stufe 3 werden die eingebauten Einheiten und die Kategorie-Startliste sofort neu angelegt – ohne Einheiten ließe sich nichts mehr einlagern. """ from fastapi import APIRouter, Depends from pydantic import BaseModel from sqlalchemy.orm import Session from ..database import get_db from ..deps import require_admin from ..models import ( Barcode, Category, Group, Location, Lot, Movement, Product, Unit, User, ) from ..seed import ensure_builtin_categories, ensure_builtin_units router = APIRouter(prefix="/maintenance", tags=["maintenance"]) class ResetSummary(BaseModel): """Was gerade in der Datenbank steht – für die Rückfrage vor dem Löschen.""" lots: int = 0 movements: int = 0 products: int = 0 groups: int = 0 categories: int = 0 locations: int = 0 units: int = 0 def _summary(db: Session) -> ResetSummary: return ResetSummary( lots=db.query(Lot).count(), movements=db.query(Movement).count(), products=db.query(Product).count(), groups=db.query(Group).count(), categories=db.query(Category).count(), locations=db.query(Location).count(), units=db.query(Unit).count(), ) def _delete_stock(db: Session) -> None: """Chargen und Verlauf. Zuerst die Bewegungen, sie verweisen auf Chargen.""" db.query(Movement).delete(synchronize_session=False) db.query(Lot).delete(synchronize_session=False) def _delete_products(db: Session) -> None: _delete_stock(db) # EAN-Codes hängen an Artikeln UND an Gruppen. Hier gehen nur die der # Artikel weg; die Gruppen-Codes bleiben, solange die Gruppen bleiben. db.query(Barcode).filter(Barcode.product_id.isnot(None)).delete( synchronize_session=False ) db.query(Product).delete(synchronize_session=False) @router.get("/summary", response_model=ResetSummary) def summary(db: Session = Depends(get_db), _: User = Depends(require_admin)) -> ResetSummary: return _summary(db) @router.post("/reset/stock", response_model=ResetSummary) def reset_stock( db: Session = Depends(get_db), _: User = Depends(require_admin) ) -> ResetSummary: """Alle Bestände auf null – Artikel, Gruppen und Stammdaten bleiben.""" _delete_stock(db) db.commit() return _summary(db) @router.post("/reset/products", response_model=ResetSummary) def reset_products( db: Session = Depends(get_db), _: User = Depends(require_admin) ) -> ResetSummary: """Katalog leeren: Artikel samt Chargen, Verlauf und ihren EAN-Codes.""" _delete_products(db) db.commit() return _summary(db) @router.post("/reset/all", response_model=ResetSummary) def reset_all( db: Session = Depends(get_db), _: User = Depends(require_admin) ) -> ResetSummary: """Zusätzlich Gruppen, Kategorien, Lagerorte und Einheiten.""" _delete_products(db) # Restliche EAN-Codes (die an Gruppen hängen) mitnehmen. db.query(Barcode).delete(synchronize_session=False) db.query(Group).delete(synchronize_session=False) db.query(Category).delete(synchronize_session=False) db.query(Location).delete(synchronize_session=False) db.query(Unit).delete(synchronize_session=False) db.commit() # Ohne Einheiten wäre die Installation unbrauchbar, deshalb sofort zurück # auf den Auslieferungsstand. ensure_builtin_units(db) ensure_builtin_categories(db) return _summary(db)