feat(mangel): Mangel-Ticket-System (Roadmap Phase 2)
Eigener Workflow für Schäden/Defekte, getrennt von Fehlbestand (Mengen- Abweichung Soll/Ist). Status: neu → in_bearbeitung → ersatzteil_bestellt/ reparatur_geplant → erledigt. entitaet_typ/entitaet_id optional (Objektposition oder Geräteinstanz), analog zum bestehenden historie.entitaet_typ-Muster, objekt_id immer gesetzt für Dashboard-Abfragen. Ein offener Mangel mit prioritaet=hoch macht das Objekt "nicht einsatzbereit" (neuer Grund mangel_kritisch_offen in dashboard.einsatzbereitschaft) - niedrige/normale Mängel blockieren nicht. Melden/Lesen: alle Mitarbeiter+. Status ändern: Materialverantwortliche+. Neue Seite /maengel für alle eingeloggten Nutzer (Nav-Link "Mängel"). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KC8HYvv6UkCVYheYiTw9DD
This commit is contained in:
@@ -10,6 +10,7 @@ from app.models.mindermenge import MindermengeStatus, MindermengenGenehmigung
|
||||
from app.models.objekt import Objekt
|
||||
from app.models.objektposition import Objektposition
|
||||
from app.models.stammdaten import Material
|
||||
from app.services.mangel import objekt_ids_mit_kritischem_offenem_mangel
|
||||
|
||||
# Prompt 12.2: offen/in_bearbeitung/nachgefuellt_teilweise zählen gemeinsam als "offen"
|
||||
# (technischer Substatus ist keine Erledigung, Prompt 03/20).
|
||||
@@ -160,6 +161,8 @@ async def einsatzbereitschaft(db: AsyncSession) -> dict:
|
||||
.all()
|
||||
)
|
||||
|
||||
objekt_ids_kritischer_mangel = await objekt_ids_mit_kritischem_offenem_mangel(db)
|
||||
|
||||
bereit = eingeschraenkt = 0
|
||||
details: list[dict] = []
|
||||
for objekt in objekte:
|
||||
@@ -174,6 +177,8 @@ async def einsatzbereitschaft(db: AsyncSession) -> dict:
|
||||
gruende.append("pruefung_ueberfaellig")
|
||||
if objekt.id in objekt_ids_gesperrt:
|
||||
gruende.append("kontrolle_laeuft")
|
||||
if objekt.id in objekt_ids_kritischer_mangel:
|
||||
gruende.append("mangel_kritisch_offen")
|
||||
|
||||
if not gruende and fehlbestand_ids and fehlbestand_ids <= fehlbestand_ids_genehmigt:
|
||||
eingeschraenkt += 1
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from sqlalchemy import func, select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.models.mangel import Mangel, MangelPrioritaet, MangelStatus
|
||||
|
||||
OFFENE_MANGEL_STATI = (
|
||||
MangelStatus.neu,
|
||||
MangelStatus.in_bearbeitung,
|
||||
MangelStatus.ersatzteil_bestellt,
|
||||
MangelStatus.reparatur_geplant,
|
||||
)
|
||||
|
||||
|
||||
async def objekt_ids_mit_kritischem_offenem_mangel(db: AsyncSession) -> set[int]:
|
||||
"""Dashboard/Einsatzbereitschaft (Nutzer-Vorgabe): nur ein offener Mangel mit
|
||||
prioritaet=hoch macht ein Objekt "nicht einsatzbereit" - niedrige/normale
|
||||
Mängel sind Hinweise, aber kein Einsatzbereitschafts-Blocker."""
|
||||
result = await db.execute(
|
||||
select(func.distinct(Mangel.objekt_id)).where(
|
||||
Mangel.status.in_(OFFENE_MANGEL_STATI), Mangel.prioritaet == MangelPrioritaet.hoch
|
||||
)
|
||||
)
|
||||
return set(result.scalars().all())
|
||||
|
||||
|
||||
async def erledige_mangel(db: AsyncSession, *, mangel: Mangel, benutzer_id: int) -> Mangel:
|
||||
mangel.status = MangelStatus.erledigt
|
||||
mangel.erledigt_von = benutzer_id
|
||||
mangel.erledigt_am = datetime.now(timezone.utc)
|
||||
await db.flush()
|
||||
return mangel
|
||||
Reference in New Issue
Block a user