import asyncio import logging from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.core.email import send_email from app.models.auth import Benutzer, BenutzerRolle, RolleTyp from app.models.fehlbestand import Fehlbestand from app.models.stammdaten import Material logger = logging.getLogger(__name__) async def _empfaenger_fuer_objekt(db: AsyncSession) -> list[str]: """V1-Vereinfachung: alle aktiven Materialverantwortlichen/Leitungsverantwortlichen (Karte 05 spezifiziert keine feinere, zuständigkeitsbasierte Adressierung). Feinere Filterung nach Zuständigkeit (Karte 04) wäre eine spätere Ausbaustufe.""" result = await db.execute( select(Benutzer.email) .join(BenutzerRolle, BenutzerRolle.benutzer_id == Benutzer.id) .where( BenutzerRolle.rolle.in_( [RolleTyp.materialverantwortlicher, RolleTyp.leitungsverantwortlicher] ), Benutzer.aktiv.is_(True), Benutzer.email.is_not(None), ) ) return [email for (email,) in result.all() if email] async def benachrichtige_neuer_fehlbestand(db: AsyncSession, *, fehlbestand: Fehlbestand) -> asyncio.Task: """Karte 05: asynchroner E-Mail-Versand bei neuem Fehlbestand, blockiert den Kontroll-Workflow nicht (fire-and-forget, Fehler beim Versand werden geloggt, nicht an den aufrufenden Request weitergereicht). Gibt den Task zurück, damit Tests den Versand deterministisch abwarten können - im normalen Betrieb wird der Rückgabewert bewusst nicht awaited.""" empfaenger = await _empfaenger_fuer_objekt(db) material = await db.get(Material, fehlbestand.material_id) material_name = material.name if material else str(fehlbestand.material_id) subject = f"Neuer Fehlbestand: {material_name}" body = ( f"Material: {material_name}\n" f"Objekt-ID: {fehlbestand.objekt_id}\n" f"Sollmenge: {fehlbestand.sollmenge}\n" f"Istmenge: {fehlbestand.istmenge}\n" f"Fehlmenge: {fehlbestand.fehlmenge}\n" f"Entstanden am: {fehlbestand.entstanden_am.isoformat()}\n" ) async def _senden() -> None: try: await send_email(to=empfaenger, subject=subject, body=body) except Exception: # noqa: BLE001 - Versandfehler dürfen den Workflow nie stören logger.exception("E-Mail-Versand für Fehlbestand %s fehlgeschlagen", fehlbestand.id) return asyncio.create_task(_senden())