from sqlalchemy import func, select, update from sqlalchemy.ext.asyncio import AsyncSession from app.models.ausgabe import Ausgabe from app.models.fehlbestand import Fehlbestand from app.models.kontrolle import Kontrollposition from app.models.lager import Bestand, Materialbewegung from app.models.nachfuellung import Nachfuellung from app.models.objektposition import Objektposition from app.models.vorlage import Vorlagenposition # (Modell, Gruppierungs-Spalte für den Konflikt-Check, Anzeige-Name) _KONFLIKT_TABELLEN = [ (Objektposition, Objektposition.objekt_id, "Objektposition"), (Vorlagenposition, Vorlagenposition.vorlage_id, "Vorlagenposition"), (Bestand, Bestand.lagerplatz_id, "Bestand"), (Kontrollposition, Kontrollposition.kontrolle_id, "Kontrollposition"), ] # Reine Protokoll-/Bewegungstabellen ohne Eindeutigkeits-Constraint auf # material_id - können immer gefahrlos umgehängt werden. _SICHERE_TABELLEN = [Fehlbestand, Ausgabe, Nachfuellung, Materialbewegung] async def hole_verwendung(db: AsyncSession, material_id: int) -> dict[str, int]: verwendung = {} for model, _, name in _KONFLIKT_TABELLEN: result = await db.execute(select(func.count()).select_from(model).where(model.material_id == material_id)) verwendung[name] = result.scalar_one() for model in _SICHERE_TABELLEN: result = await db.execute(select(func.count()).select_from(model).where(model.material_id == material_id)) verwendung[model.__tablename__] = result.scalar_one() return verwendung async def finde_konflikte(db: AsyncSession, *, quelle_id: int, ziel_id: int) -> list[str]: """Für Tabellen mit UNIQUE(gruppen_spalte, material_id) darf nach dem Umhängen nicht dieselbe Kombination doppelt entstehen - z. B. wenn ein Objekt schon eine Position mit dem Ziel-Material hat UND eine mit dem Quell-Material.""" konflikte: list[str] = [] for model, gruppen_spalte, name in _KONFLIKT_TABELLEN: quelle_gruppen = ( (await db.execute(select(gruppen_spalte).where(model.material_id == quelle_id))).scalars().all() ) if not quelle_gruppen: continue ziel_gruppen = set( (await db.execute(select(gruppen_spalte).where(model.material_id == ziel_id))).scalars().all() ) ueberschneidung = set(quelle_gruppen) & ziel_gruppen if ueberschneidung: konflikte.append(f"{name}: {len(ueberschneidung)} Konflikt(e) - beide Materialien dort gleichzeitig vorhanden") return konflikte async def fuehre_zusammen(db: AsyncSession, *, quelle_id: int, ziel_id: int) -> None: """Voraussetzung: finde_konflikte() wurde vorher aufgerufen und war leer - der Aufrufer (Endpunkt) verweigert den Merge sonst.""" for model, _, _ in _KONFLIKT_TABELLEN: await db.execute(update(model).where(model.material_id == quelle_id).values(material_id=ziel_id)) for model in _SICHERE_TABELLEN: await db.execute(update(model).where(model.material_id == quelle_id).values(material_id=ziel_id)) await db.flush()