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:
@@ -0,0 +1,115 @@
|
||||
import pytest
|
||||
|
||||
from tests.conftest import auth_header, login
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mangel_melden_und_lesen(client, objekt_mit_position, mitarbeiter_user):
|
||||
objekt, _material = objekt_mit_position
|
||||
token = await login(client, "mitarbeiter1")
|
||||
|
||||
gemeldet = await client.post(
|
||||
"/api/v1/maengel",
|
||||
json={"objekt_id": objekt.id, "beschreibung": "Blaulicht vorne rechts defekt", "prioritaet": "hoch"},
|
||||
headers=auth_header(token),
|
||||
)
|
||||
assert gemeldet.status_code == 201
|
||||
assert gemeldet.json()["status"] == "neu"
|
||||
|
||||
liste = await client.get(f"/api/v1/maengel?objekt_id={objekt.id}", headers=auth_header(token))
|
||||
assert len(liste.json()) == 1
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mitarbeiter_darf_status_nicht_aendern(client, objekt_mit_position, mitarbeiter_user):
|
||||
objekt, _material = objekt_mit_position
|
||||
token = await login(client, "mitarbeiter1")
|
||||
gemeldet = await client.post(
|
||||
"/api/v1/maengel",
|
||||
json={"objekt_id": objekt.id, "beschreibung": "Reifen platt"},
|
||||
headers=auth_header(token),
|
||||
)
|
||||
mangel_id = gemeldet.json()["id"]
|
||||
|
||||
response = await client.patch(
|
||||
f"/api/v1/maengel/{mangel_id}", json={"status": "in_bearbeitung"}, headers=auth_header(token)
|
||||
)
|
||||
assert response.status_code == 403
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_materialverantwortlicher_kann_status_aendern_und_erledigen(
|
||||
client, objekt_mit_position, mitarbeiter_user, materialverantwortlicher_user
|
||||
):
|
||||
objekt, _material = objekt_mit_position
|
||||
mitarbeiter_token = await login(client, "mitarbeiter1")
|
||||
gemeldet = await client.post(
|
||||
"/api/v1/maengel",
|
||||
json={"objekt_id": objekt.id, "beschreibung": "Sirene ausgefallen"},
|
||||
headers=auth_header(mitarbeiter_token),
|
||||
)
|
||||
mangel_id = gemeldet.json()["id"]
|
||||
|
||||
verantwortlicher_token = await login(client, "materialverantwortlicher1")
|
||||
bearbeitung = await client.patch(
|
||||
f"/api/v1/maengel/{mangel_id}", json={"status": "ersatzteil_bestellt"}, headers=auth_header(verantwortlicher_token)
|
||||
)
|
||||
assert bearbeitung.json()["status"] == "ersatzteil_bestellt"
|
||||
|
||||
erledigt = await client.patch(
|
||||
f"/api/v1/maengel/{mangel_id}", json={"status": "erledigt"}, headers=auth_header(verantwortlicher_token)
|
||||
)
|
||||
assert erledigt.json()["status"] == "erledigt"
|
||||
assert erledigt.json()["erledigt_am"] is not None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_entitaet_typ_und_id_muessen_zusammen_gesetzt_sein(client, objekt_mit_position, mitarbeiter_user):
|
||||
objekt, _material = objekt_mit_position
|
||||
token = await login(client, "mitarbeiter1")
|
||||
response = await client.post(
|
||||
"/api/v1/maengel",
|
||||
json={"objekt_id": objekt.id, "beschreibung": "X", "entitaet_typ": "geraet_instanz"},
|
||||
headers=auth_header(token),
|
||||
)
|
||||
assert response.status_code == 422
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_kritischer_offener_mangel_macht_objekt_nicht_einsatzbereit(
|
||||
client, objekt_mit_position, mitarbeiter_user, materialverantwortlicher_user
|
||||
):
|
||||
objekt, _material = objekt_mit_position
|
||||
mitarbeiter_token = await login(client, "mitarbeiter1")
|
||||
await client.post(
|
||||
"/api/v1/maengel",
|
||||
json={"objekt_id": objekt.id, "beschreibung": "Bremsen defekt", "prioritaet": "hoch"},
|
||||
headers=auth_header(mitarbeiter_token),
|
||||
)
|
||||
|
||||
verantwortlicher_token = await login(client, "materialverantwortlicher1")
|
||||
response = await client.get(
|
||||
"/api/v1/dashboard/einsatzbereitschaft", headers=auth_header(verantwortlicher_token)
|
||||
)
|
||||
body = response.json()
|
||||
assert body["nicht_einsatzbereit"] == 1
|
||||
assert body["details"][0]["gruende"] == ["mangel_kritisch_offen"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_niedrige_prioritaet_blockiert_einsatzbereitschaft_nicht(
|
||||
client, objekt_mit_position, mitarbeiter_user, materialverantwortlicher_user
|
||||
):
|
||||
objekt, _material = objekt_mit_position
|
||||
mitarbeiter_token = await login(client, "mitarbeiter1")
|
||||
await client.post(
|
||||
"/api/v1/maengel",
|
||||
json={"objekt_id": objekt.id, "beschreibung": "Kratzer im Lack", "prioritaet": "niedrig"},
|
||||
headers=auth_header(mitarbeiter_token),
|
||||
)
|
||||
|
||||
verantwortlicher_token = await login(client, "materialverantwortlicher1")
|
||||
response = await client.get(
|
||||
"/api/v1/dashboard/einsatzbereitschaft", headers=auth_header(verantwortlicher_token)
|
||||
)
|
||||
assert response.json()["einsatzbereit"] == 1
|
||||
Reference in New Issue
Block a user