Files
MABEA/backend/tests/test_wartung.py
T
patrickandClaude Sonnet 5 2a4d391711
CI / backend-tests (push) Failing after 2m4s
CI / frontend-build (push) Successful in 18s
feat(maintenance): MAINT-Epic (Wartungspläne/-aufträge, Werkstatt) + Akte-Redesign
Neues Wartungskonzept: Wartungsplan+Position+Intervall (zeit/km/
Betriebsstunden, kombinierbar - fällig sobald ein Kriterium erreicht
ist), Wartungsauftrag mit intern/externer Werkstatt, Status offen/
in_arbeit/erledigt, Kosten, Historie, Dokument-Anhang. Ad-hoc-Aufträge
ohne Plan möglich (z.B. kleinere Reparatur). Admin-Tab "Wartungspläne".

Objektakte-Redesign (Epic 21, Nutzer-Feedback "wirkt nicht modern"):
CSS-Grid statt Karten-Stapel, einspaltig mobil, zweispaltig ab 900px;
Fahrzeug-Objekte zeigen Wartung/Fahrzeugdaten prominent, Geräte/
Rucksäcke die Beladung. Dabei gefundene Lücke: Beladung (Soll/Ist je
Position) fehlte in der Akte komplett, nur über eine laufende
Kontrolle sichtbar - jetzt eigene Karte.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
2026-09-06 23:17:39 +02:00

215 lines
7.5 KiB
Python

from datetime import date
import pytest
from app.models.objekt import Objekt
from app.services.datumshilfen import plus_monate
from tests.conftest import auth_header, login
async def _objekt_mit_typ(db_session, hauptserver_id, *, code, name, objekttyp, standort):
objekt = Objekt(
code=code,
name=name,
objekttyp_id=objekttyp.id,
standort_id=standort.id,
zustaendiger_server_id=hauptserver_id,
)
db_session.add(objekt)
await db_session.flush()
return objekt
@pytest.mark.asyncio
async def test_wartungsplan_mit_intervall_anlegen(client, admin_user, objekttyp_factory):
token = await login(client, "admin1")
objekttyp = await objekttyp_factory("RTW-Test")
response = await client.post(
"/api/v1/wartungsplaene",
json={
"objekttyp_id": objekttyp.id,
"name": "RTW Standard",
"positionen": [
{
"wartungsart": "Ölwechsel",
"intervalle": [{"typ": "zeit", "wert": 12}, {"typ": "km", "wert": 15000}],
}
],
},
headers=auth_header(token),
)
assert response.status_code == 201
body = response.json()
assert len(body["positionen"]) == 1
assert len(body["positionen"][0]["intervalle"]) == 2
@pytest.mark.asyncio
async def test_nie_durchgefuehrte_wartung_ist_faellig(
client, db_session, hauptserver_id, admin_user, objekttyp_factory, standort_factory
):
token = await login(client, "admin1")
objekttyp = await objekttyp_factory("RTW-Test2")
standort = await standort_factory("Standort-W2")
objekt = await _objekt_mit_typ(
db_session, hauptserver_id, code="RTW-W2", name="RTW W2", objekttyp=objekttyp, standort=standort
)
objekt_id = objekt.id
await client.post(
"/api/v1/wartungsplaene",
json={"objekttyp_id": objekttyp.id, "name": "Plan", "positionen": [{"wartungsart": "Ölwechsel"}]},
headers=auth_header(token),
)
response = await client.get(f"/api/v1/objekte/{objekt_id}/faellige-wartungen", headers=auth_header(token))
assert response.status_code == 200
body = response.json()
assert len(body) == 1
assert body[0]["faellig"] is True
assert body[0]["wartungsart"] == "Ölwechsel"
@pytest.mark.asyncio
async def test_wartungsauftrag_erledigen_macht_zeit_intervall_nicht_faellig(
client, db_session, hauptserver_id, admin_user, objekttyp_factory, standort_factory
):
token = await login(client, "admin1")
objekttyp = await objekttyp_factory("RTW-Test3")
standort = await standort_factory("Standort-W3")
objekt = await _objekt_mit_typ(
db_session, hauptserver_id, code="RTW-W3", name="RTW W3", objekttyp=objekttyp, standort=standort
)
objekt_id = objekt.id
plan = await client.post(
"/api/v1/wartungsplaene",
json={
"objekttyp_id": objekttyp.id,
"name": "Plan",
"positionen": [{"wartungsart": "Ölwechsel", "intervalle": [{"typ": "zeit", "wert": 12}]}],
},
headers=auth_header(token),
)
position_id = plan.json()["positionen"][0]["id"]
auftrag = await client.post(
"/api/v1/wartungsauftraege",
json={"objekt_id": objekt_id, "wartungsplan_position_id": position_id, "wartungsart": "Ölwechsel"},
headers=auth_header(token),
)
assert auftrag.status_code == 201
auftrag_id = auftrag.json()["id"]
erledigt = await client.post(
f"/api/v1/wartungsauftraege/{auftrag_id}/erledigen",
json={"erledigt_am": date.today().isoformat()},
headers=auth_header(token),
)
assert erledigt.status_code == 200
assert erledigt.json()["status"] == "erledigt"
faellig = await client.get(f"/api/v1/objekte/{objekt_id}/faellige-wartungen", headers=auth_header(token))
assert faellig.json()[0]["faellig"] is False
assert faellig.json()[0]["faellig_am"] == plus_monate(date.today(), 12).isoformat()
@pytest.mark.asyncio
async def test_km_intervall_wird_zuerst_faellig_trotz_gueltigem_zeit_intervall(
client, db_session, hauptserver_id, admin_user, objekttyp_factory, standort_factory
):
""""je nachdem was zuerst eintritt" - km-Schwelle erreicht, obwohl das
Zeit-Intervall noch nicht abgelaufen ist."""
from app.models.fahrzeugdetails import Fahrzeugdetails
token = await login(client, "admin1")
objekttyp = await objekttyp_factory("RTW-Test4")
standort = await standort_factory("Standort-W4")
objekt = await _objekt_mit_typ(
db_session, hauptserver_id, code="RTW-W4", name="RTW W4", objekttyp=objekttyp, standort=standort
)
objekt_id = objekt.id
db_session.add(Fahrzeugdetails(objekt_id=objekt_id, kilometerstand=20000))
await db_session.flush()
plan = await client.post(
"/api/v1/wartungsplaene",
json={
"objekttyp_id": objekttyp.id,
"name": "Plan",
"positionen": [
{
"wartungsart": "Ölwechsel",
"intervalle": [{"typ": "zeit", "wert": 12}, {"typ": "km", "wert": 15000}],
}
],
},
headers=auth_header(token),
)
position_id = plan.json()["positionen"][0]["id"]
auftrag = await client.post(
"/api/v1/wartungsauftraege",
json={"objekt_id": objekt_id, "wartungsplan_position_id": position_id, "wartungsart": "Ölwechsel"},
headers=auth_header(token),
)
auftrag_id = auftrag.json()["id"]
await client.post(
f"/api/v1/wartungsauftraege/{auftrag_id}/erledigen",
json={"erledigt_am": date.today().isoformat(), "kilometerstand_bei_erledigung": 5000},
headers=auth_header(token),
)
faellig = await client.get(f"/api/v1/objekte/{objekt_id}/faellige-wartungen", headers=auth_header(token))
body = faellig.json()[0]
assert body["faellig"] is True
assert body["faellig_bei_km"] == 20000
@pytest.mark.asyncio
async def test_ad_hoc_reparatur_ohne_wartungsplan_mit_externer_werkstatt(
client, admin_user, standort_factory, objekt_factory
):
token = await login(client, "admin1")
standort = await standort_factory("Standort-W5")
await objekt_factory(name="RTW W5", code="RTW-W5", standort=standort)
objekte = await client.get("/api/v1/objekte", headers=auth_header(token))
objekt_id = [o for o in objekte.json() if o["code"] == "RTW-W5"][0]["id"]
auftrag = await client.post(
"/api/v1/wartungsauftraege",
json={
"objekt_id": objekt_id,
"wartungsart": "Bremsleuchte defekt",
"werkstatt_intern": False,
"werkstatt_name": "KFZ Meyer GmbH",
},
headers=auth_header(token),
)
assert auftrag.status_code == 201
body = auftrag.json()
assert body["wartungsplan_position_id"] is None
assert body["werkstatt_intern"] is False
assert body["werkstatt_name"] == "KFZ Meyer GmbH"
assert body["status"] == "offen"
in_arbeit = await client.patch(
f"/api/v1/wartungsauftraege/{body['id']}/status",
json={"status": "in_arbeit"},
headers=auth_header(token),
)
assert in_arbeit.json()["status"] == "in_arbeit"
erledigt = await client.post(
f"/api/v1/wartungsauftraege/{body['id']}/erledigen",
json={"kosten": "89.90"},
headers=auth_header(token),
)
assert erledigt.json()["status"] == "erledigt"
assert erledigt.json()["kosten"] == "89.90"
historie = await client.get(f"/api/v1/wartungsauftraege?objekt_id={objekt_id}", headers=auth_header(token))
assert len(historie.json()) == 1