feat(fahrzeug): Fahrzeug-Spezialfelder + 5-stufiger Status (Roadmap Phase 3)
Neue Tabelle fahrzeugdetails (1:1 zu Objekt, additiv/optional): Kennzeichen, Funkrufname, Baujahr, Kilometerstand, Betriebsstunden, nächste HU/UVV. objekt_status um 'in_wartung' erweitert (Nutzer-Vorgabe: einsatzbereit/ eingeschränkt/nicht einsatzbereit/in Wartung/außer Dienst - die ersten drei bleiben weiterhin rein rechnerisch über dashboard.einsatzbereitschaft(), in_wartung/ausser_dienst sind die beiden manuell gesetzten Zustände). dashboard.einsatzbereitschaft(): ausser_dienst-Objekte zählen nicht mehr mit, in_wartung und überfällige HU/UVV sind neue Gründe für "nicht einsatzbereit". dashboard.bevorstehende_prueftermine() (Kachel 2) zeigt jetzt Geräte- UND Fahrzeugprüfungen (HU/UVV) in einer Liste (Nutzer-Vorgabe: ein Konzept für "welche Prüfungen bald fällig sind") - Schema dafür verallgemeinert (typ/bezeichnung/faelligkeitsdatum statt geräte-spezifischer Felder). Frontend: Status-Dropdown + Fahrzeugdetails-Formular in ObjektSection (nur für Objekttypen mit ist_zugfahrzeug=True), Dashboard-Kachel 2 zeigt gemischte Liste. 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,49 @@
|
||||
"""Fahrzeug-Spezialfelder (Roadmap Phase 3: BOS-Ressourcenplattform-Anforderung
|
||||
Modul 2 "Fahrzeuge"). Kennzeichen/Funkrufname/Kilometerstand/Betriebsstunden/
|
||||
Baujahr/HU/UVV als optionale 1:1-Erweiterung zu Objekt (nicht jeder Objekttyp
|
||||
braucht das, nur Zugfahrzeuge - additiv, kein eigener Fahrzeug-Objekttyp
|
||||
erzwungen, Karte 11 bleibt gültig).
|
||||
|
||||
Zusätzlich objekt_status um 'in_wartung' erweitert (Nutzer-Vorgabe: 5-stufiger
|
||||
Fahrzeug-Status). ALTER TYPE ... ADD VALUE muss außerhalb der laufenden
|
||||
Transaktion committen (Postgres-Einschränkung), daher AUTOCOMMIT-Block.
|
||||
|
||||
Revision ID: 0014_fahrzeugdetails
|
||||
Revises: 0013_mangel
|
||||
Create Date: 2026-09-05
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision: str = "0014_fahrzeugdetails"
|
||||
down_revision: Union[str, None] = "0013_mangel"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
with op.get_context().autocommit_block():
|
||||
op.execute("ALTER TYPE objekt_status ADD VALUE IF NOT EXISTS 'in_wartung'")
|
||||
|
||||
op.execute(
|
||||
"""
|
||||
CREATE TABLE fahrzeugdetails (
|
||||
objekt_id INTEGER PRIMARY KEY REFERENCES objekt(id),
|
||||
kennzeichen TEXT,
|
||||
funkrufname TEXT,
|
||||
baujahr INTEGER,
|
||||
kilometerstand INTEGER,
|
||||
betriebsstunden NUMERIC,
|
||||
naechste_hu DATE,
|
||||
naechste_uvv DATE
|
||||
)
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.execute("DROP TABLE fahrzeugdetails")
|
||||
# 'in_wartung' aus dem ENUM zu entfernen ist in Postgres nicht direkt möglich
|
||||
# (kein ALTER TYPE ... DROP VALUE) - bewusst nicht rückgängig gemacht, siehe
|
||||
# Muster in 0001_initial_schema (dort ebenfalls nur DROP TYPE als Ganzes).
|
||||
@@ -8,12 +8,15 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from app.api.deps import get_current_user, require_roles
|
||||
from app.db.session import get_db
|
||||
from app.models.auth import KnotenTyp, RolleTyp, Systemknoten
|
||||
from app.models.fahrzeugdetails import Fahrzeugdetails
|
||||
from app.models.objekt import Objekt
|
||||
from app.models.objektposition import Objektposition
|
||||
from app.models.stammdaten import Material, Objekttyp
|
||||
from app.models.vorlage import Beladungsvorlage
|
||||
from app.services.label import generiere_code128_label_pdf
|
||||
from app.schemas.objekt import (
|
||||
FahrzeugdetailsRead,
|
||||
FahrzeugdetailsUpsert,
|
||||
ObjektCreate,
|
||||
ObjektDuplizierenRequest,
|
||||
ObjektpositionCreate,
|
||||
@@ -391,3 +394,40 @@ async def fuege_zusatzposition_hinzu(
|
||||
code=position.code,
|
||||
pruefintervall_monate=position.pruefintervall_monate,
|
||||
)
|
||||
|
||||
|
||||
@router.get("/objekte/{objekt_id}/fahrzeugdetails", response_model=FahrzeugdetailsRead)
|
||||
async def hole_fahrzeugdetails(
|
||||
objekt_id: int, db: AsyncSession = Depends(get_db), _=Depends(get_current_user)
|
||||
) -> Fahrzeugdetails:
|
||||
"""Roadmap Phase 3 (Modul Fahrzeuge): 404 statt leerem Default, wenn noch
|
||||
keine Fahrzeugdetails erfasst sind - Frontend zeigt dann ein leeres
|
||||
Formular statt scheinbar vorhandener Nullwerte."""
|
||||
details = await db.get(Fahrzeugdetails, objekt_id)
|
||||
if details is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Keine Fahrzeugdetails erfasst")
|
||||
return details
|
||||
|
||||
|
||||
@router.put("/objekte/{objekt_id}/fahrzeugdetails", response_model=FahrzeugdetailsRead)
|
||||
async def setze_fahrzeugdetails(
|
||||
objekt_id: int,
|
||||
payload: FahrzeugdetailsUpsert,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
_=Depends(_materialverantwortliche),
|
||||
) -> Fahrzeugdetails:
|
||||
"""Upsert (PUT statt PATCH): Fahrzeugdetails existieren typischerweise
|
||||
genau einmal je Objekt, ein Formular ersetzt komplett statt Teil-Update -
|
||||
einfacher als Anlegen/Ändern zu unterscheiden."""
|
||||
objekt = await db.get(Objekt, objekt_id)
|
||||
if objekt is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Objekt nicht gefunden")
|
||||
|
||||
details = await db.get(Fahrzeugdetails, objekt_id)
|
||||
if details is None:
|
||||
details = Fahrzeugdetails(objekt_id=objekt_id)
|
||||
db.add(details)
|
||||
for feld, wert in payload.model_dump().items():
|
||||
setattr(details, feld, wert)
|
||||
await db.flush()
|
||||
return details
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
from datetime import date
|
||||
from decimal import Decimal
|
||||
|
||||
from sqlalchemy import Date, ForeignKey, Integer, Numeric, String
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.db.base import Base
|
||||
|
||||
|
||||
class Fahrzeugdetails(Base):
|
||||
"""Roadmap Phase 3 (Modul "Fahrzeuge"): optionale 1:1-Erweiterung zu Objekt
|
||||
für Fahrzeug-Spezialfelder. Objekt bleibt generisch (Karte 11) - nicht jedes
|
||||
Objekt braucht das, daher eigene Tabelle statt Spalten direkt an Objekt."""
|
||||
|
||||
__tablename__ = "fahrzeugdetails"
|
||||
|
||||
objekt_id: Mapped[int] = mapped_column(ForeignKey("objekt.id"), primary_key=True)
|
||||
kennzeichen: Mapped[str | None] = mapped_column(String)
|
||||
funkrufname: Mapped[str | None] = mapped_column(String)
|
||||
baujahr: Mapped[int | None] = mapped_column(Integer)
|
||||
kilometerstand: Mapped[int | None] = mapped_column(Integer)
|
||||
betriebsstunden: Mapped[Decimal | None] = mapped_column(Numeric)
|
||||
naechste_hu: Mapped[date | None] = mapped_column(Date)
|
||||
naechste_uvv: Mapped[date | None] = mapped_column(Date)
|
||||
@@ -10,6 +10,7 @@ from app.db.base import Base
|
||||
class ObjektStatus(str, enum.Enum):
|
||||
aktiv = "aktiv"
|
||||
ausser_dienst = "ausser_dienst"
|
||||
in_wartung = "in_wartung"
|
||||
|
||||
|
||||
objekt_status_pg = PgEnum(ObjektStatus, name="objekt_status", create_type=False)
|
||||
|
||||
@@ -3,8 +3,6 @@ from datetime import date
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from app.models.geraet_instanz import GeraetStatus
|
||||
|
||||
|
||||
class KennzahlenRead(BaseModel):
|
||||
anzahl_offener_fehlbestaende: int
|
||||
@@ -39,13 +37,18 @@ class EinsatzbereitschaftRead(BaseModel):
|
||||
|
||||
|
||||
class PrueftermineintragRead(BaseModel):
|
||||
geraet_instanz_id: uuid.UUID
|
||||
"""typ='geraet' (Karte 14) oder 'fahrzeug_hu'/'fahrzeug_uvv' (Roadmap Phase
|
||||
3, Modul Fahrzeuge) - eine Kachel für beide (Nutzer-Vorgabe: "welche
|
||||
Prüfungen bald fällig sind" ist ein Konzept). Die geraet-spezifischen
|
||||
Felder sind bei Fahrzeug-Einträgen None."""
|
||||
|
||||
typ: str
|
||||
objekt_id: int
|
||||
objektposition_id: uuid.UUID
|
||||
material_id: int
|
||||
material_name: str
|
||||
seriennummer: str
|
||||
geraet_status: GeraetStatus
|
||||
naechste_pruefung: date
|
||||
bezeichnung: str
|
||||
faelligkeitsdatum: date
|
||||
verbleibende_tage: int
|
||||
status: str
|
||||
geraet_instanz_id: uuid.UUID | None = None
|
||||
objektposition_id: uuid.UUID | None = None
|
||||
material_id: int | None = None
|
||||
seriennummer: str | None = None
|
||||
|
||||
@@ -32,6 +32,29 @@ class ObjektRead(BaseModel):
|
||||
|
||||
class ObjektUpdate(BaseModel):
|
||||
fahrzeug_id: int | None = None
|
||||
status: ObjektStatus | None = None
|
||||
|
||||
|
||||
class FahrzeugdetailsUpsert(BaseModel):
|
||||
kennzeichen: str | None = None
|
||||
funkrufname: str | None = None
|
||||
baujahr: int | None = None
|
||||
kilometerstand: int | None = None
|
||||
betriebsstunden: Decimal | None = None
|
||||
naechste_hu: date | None = None
|
||||
naechste_uvv: date | None = None
|
||||
|
||||
|
||||
class FahrzeugdetailsRead(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
objekt_id: int
|
||||
kennzeichen: str | None
|
||||
funkrufname: str | None
|
||||
baujahr: int | None
|
||||
kilometerstand: int | None
|
||||
betriebsstunden: Decimal | None
|
||||
naechste_hu: date | None
|
||||
naechste_uvv: date | None
|
||||
|
||||
|
||||
class ObjektDuplizierenRequest(BaseModel):
|
||||
|
||||
@@ -3,11 +3,12 @@ from datetime import date, datetime, timedelta, timezone
|
||||
from sqlalchemy import func, select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.models.fahrzeugdetails import Fahrzeugdetails
|
||||
from app.models.fehlbestand import Fehlbestand, FehlbestandStatus
|
||||
from app.models.geraet_instanz import GeraetInstanz
|
||||
from app.models.kontrolle import Kontrolle, KontrollStatus
|
||||
from app.models.mindermenge import MindermengeStatus, MindermengenGenehmigung
|
||||
from app.models.objekt import Objekt
|
||||
from app.models.objekt import Objekt, ObjektStatus
|
||||
from app.models.objektposition import Objektposition
|
||||
from app.models.stammdaten import Material
|
||||
from app.services.mangel import objekt_ids_mit_kritischem_offenem_mangel
|
||||
@@ -104,7 +105,12 @@ async def einsatzbereitschaft(db: AsyncSession) -> dict:
|
||||
(Leitplanke U2/Karte 08) - das Objekt bleibt daher "eingeschränkt
|
||||
einsatzbereit", nicht "einsatzbereit"."""
|
||||
heute = datetime.now(timezone.utc).date()
|
||||
objekte = (await db.execute(select(Objekt))).scalars().all()
|
||||
# ausser_dienst-Objekte zählen fachlich gar nicht mehr mit (Roadmap Phase 3,
|
||||
# Modul Fahrzeuge: 5-stufiger Status) - anders als in_wartung, das als
|
||||
# aktiver Grund weiter unten erscheint statt das Objekt komplett auszublenden.
|
||||
objekte = (
|
||||
(await db.execute(select(Objekt).where(Objekt.status != ObjektStatus.ausser_dienst))).scalars().all()
|
||||
)
|
||||
|
||||
offene_fehlbestaende = (
|
||||
await db.execute(
|
||||
@@ -163,6 +169,25 @@ async def einsatzbereitschaft(db: AsyncSession) -> dict:
|
||||
|
||||
objekt_ids_kritischer_mangel = await objekt_ids_mit_kritischem_offenem_mangel(db)
|
||||
|
||||
objekt_ids_hu_uvv_ueberfaellig = set(
|
||||
(
|
||||
await db.execute(
|
||||
select(func.distinct(Fahrzeugdetails.objekt_id)).where(
|
||||
(
|
||||
(Fahrzeugdetails.naechste_hu.is_not(None))
|
||||
& (Fahrzeugdetails.naechste_hu < heute)
|
||||
)
|
||||
| (
|
||||
(Fahrzeugdetails.naechste_uvv.is_not(None))
|
||||
& (Fahrzeugdetails.naechste_uvv < heute)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
.scalars()
|
||||
.all()
|
||||
)
|
||||
|
||||
bereit = eingeschraenkt = 0
|
||||
details: list[dict] = []
|
||||
for objekt in objekte:
|
||||
@@ -179,6 +204,10 @@ async def einsatzbereitschaft(db: AsyncSession) -> dict:
|
||||
gruende.append("kontrolle_laeuft")
|
||||
if objekt.id in objekt_ids_kritischer_mangel:
|
||||
gruende.append("mangel_kritisch_offen")
|
||||
if objekt.status == ObjektStatus.in_wartung:
|
||||
gruende.append("in_wartung")
|
||||
if objekt.id in objekt_ids_hu_uvv_ueberfaellig:
|
||||
gruende.append("hu_uvv_ueberfaellig")
|
||||
|
||||
if not gruende and fehlbestand_ids and fehlbestand_ids <= fehlbestand_ids_genehmigt:
|
||||
eingeschraenkt += 1
|
||||
@@ -205,30 +234,67 @@ async def bevorstehende_prueftermine(db: AsyncSession, warnzeitraum_tage: int =
|
||||
Endpoint /dashboard/ablaufdaten) - Prüfung ist Wartungsintervall, Ablauf ist
|
||||
Verbrauchsgut-Verfall, fachlich unterschiedlich (Nutzer-Entscheidung)."""
|
||||
heute = datetime.now(timezone.utc).date()
|
||||
eintraege = []
|
||||
|
||||
result = await db.execute(
|
||||
select(GeraetInstanz, Objektposition, Material)
|
||||
.join(Objektposition, GeraetInstanz.objektposition_id == Objektposition.id)
|
||||
.join(Material, Objektposition.material_id == Material.id)
|
||||
.where(GeraetInstanz.naechste_pruefung.is_not(None))
|
||||
)
|
||||
eintraege = []
|
||||
for geraet, objektposition, material in result.all():
|
||||
status_wert, verbleibende_tage = _ablauf_status(geraet.naechste_pruefung, warnzeitraum_tage, heute)
|
||||
if status_wert == "gueltig":
|
||||
continue
|
||||
eintraege.append(
|
||||
{
|
||||
"geraet_instanz_id": geraet.id,
|
||||
"typ": "geraet",
|
||||
"objekt_id": objektposition.objekt_id,
|
||||
"objektposition_id": objektposition.id,
|
||||
"material_id": material.id,
|
||||
"material_name": material.name,
|
||||
"seriennummer": geraet.seriennummer,
|
||||
"geraet_status": geraet.status,
|
||||
"naechste_pruefung": geraet.naechste_pruefung,
|
||||
"bezeichnung": f"{material.name} (SN {geraet.seriennummer})",
|
||||
"faelligkeitsdatum": geraet.naechste_pruefung,
|
||||
"verbleibende_tage": verbleibende_tage,
|
||||
"status": status_wert,
|
||||
"geraet_instanz_id": geraet.id,
|
||||
"objektposition_id": objektposition.id,
|
||||
"material_id": material.id,
|
||||
"seriennummer": geraet.seriennummer,
|
||||
}
|
||||
)
|
||||
|
||||
# Fahrzeug-HU/UVV (Roadmap Phase 3, Modul Fahrzeuge) - gleiche Kachel wie
|
||||
# Geräteprüfungen (Nutzer-Vorgabe: "welche Prüfungen bald fällig sind" ist
|
||||
# EIN Konzept, egal ob Gerät oder Fahrzeug).
|
||||
result = await db.execute(
|
||||
select(Fahrzeugdetails, Objekt)
|
||||
.join(Objekt, Fahrzeugdetails.objekt_id == Objekt.id)
|
||||
.where(
|
||||
(Fahrzeugdetails.naechste_hu.is_not(None)) | (Fahrzeugdetails.naechste_uvv.is_not(None))
|
||||
)
|
||||
)
|
||||
for details, objekt in result.all():
|
||||
for feld, typ, label in (
|
||||
(details.naechste_hu, "fahrzeug_hu", "HU"),
|
||||
(details.naechste_uvv, "fahrzeug_uvv", "UVV"),
|
||||
):
|
||||
if feld is None:
|
||||
continue
|
||||
status_wert, verbleibende_tage = _ablauf_status(feld, warnzeitraum_tage, heute)
|
||||
if status_wert == "gueltig":
|
||||
continue
|
||||
eintraege.append(
|
||||
{
|
||||
"typ": typ,
|
||||
"objekt_id": objekt.id,
|
||||
"bezeichnung": f"{label} {objekt.name} ({details.kennzeichen or objekt.code})",
|
||||
"faelligkeitsdatum": feld,
|
||||
"verbleibende_tage": verbleibende_tage,
|
||||
"status": status_wert,
|
||||
"geraet_instanz_id": None,
|
||||
"objektposition_id": None,
|
||||
"material_id": None,
|
||||
"seriennummer": None,
|
||||
}
|
||||
)
|
||||
|
||||
eintraege.sort(key=lambda e: e["verbleibende_tage"])
|
||||
return eintraege
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
from datetime import date, timedelta
|
||||
|
||||
import pytest
|
||||
|
||||
from tests.conftest import auth_header, login
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_fahrzeugdetails_upsert(client, objekt_mit_position, admin_user):
|
||||
objekt, _material = objekt_mit_position
|
||||
token = await login(client, "admin1")
|
||||
|
||||
fehlend = await client.get(f"/api/v1/objekte/{objekt.id}/fahrzeugdetails", headers=auth_header(token))
|
||||
assert fehlend.status_code == 404
|
||||
|
||||
gesetzt = await client.put(
|
||||
f"/api/v1/objekte/{objekt.id}/fahrzeugdetails",
|
||||
json={"kennzeichen": "XX-YY 123", "kilometerstand": 42315, "naechste_hu": "2027-05-01"},
|
||||
headers=auth_header(token),
|
||||
)
|
||||
assert gesetzt.status_code == 200
|
||||
assert gesetzt.json()["kennzeichen"] == "XX-YY 123"
|
||||
assert gesetzt.json()["kilometerstand"] == 42315
|
||||
|
||||
aktualisiert = await client.put(
|
||||
f"/api/v1/objekte/{objekt.id}/fahrzeugdetails",
|
||||
json={"kilometerstand": 42500},
|
||||
headers=auth_header(token),
|
||||
)
|
||||
assert aktualisiert.json()["kilometerstand"] == 42500
|
||||
assert aktualisiert.json()["kennzeichen"] is None # PUT ersetzt komplett, kein Merge
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mitarbeiter_darf_fahrzeugdetails_nicht_setzen(client, objekt_mit_position, mitarbeiter_user):
|
||||
objekt, _material = objekt_mit_position
|
||||
token = await login(client, "mitarbeiter1")
|
||||
response = await client.put(
|
||||
f"/api/v1/objekte/{objekt.id}/fahrzeugdetails", json={"kennzeichen": "X"}, headers=auth_header(token)
|
||||
)
|
||||
assert response.status_code == 403
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ausser_dienst_objekt_zaehlt_nicht_in_einsatzbereitschaft(
|
||||
client, objekt_mit_position, admin_user, materialverantwortlicher_user
|
||||
):
|
||||
objekt, _material = objekt_mit_position
|
||||
token = await login(client, "admin1")
|
||||
await client.patch(f"/api/v1/objekte/{objekt.id}", json={"status": "ausser_dienst"}, headers=auth_header(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["gesamt"] == 0
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_in_wartung_macht_objekt_nicht_einsatzbereit(
|
||||
client, objekt_mit_position, admin_user, materialverantwortlicher_user
|
||||
):
|
||||
objekt, _material = objekt_mit_position
|
||||
token = await login(client, "admin1")
|
||||
await client.patch(f"/api/v1/objekte/{objekt.id}", json={"status": "in_wartung"}, headers=auth_header(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"] == ["in_wartung"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ueberfaellige_hu_macht_objekt_nicht_einsatzbereit_und_erscheint_in_prueftermine(
|
||||
client, objekt_mit_position, admin_user, materialverantwortlicher_user
|
||||
):
|
||||
objekt, _material = objekt_mit_position
|
||||
token = await login(client, "admin1")
|
||||
await client.put(
|
||||
f"/api/v1/objekte/{objekt.id}/fahrzeugdetails",
|
||||
json={"naechste_hu": (date.today() - timedelta(days=10)).isoformat()},
|
||||
headers=auth_header(token),
|
||||
)
|
||||
|
||||
verantwortlicher_token = await login(client, "materialverantwortlicher1")
|
||||
bereitschaft = await client.get(
|
||||
"/api/v1/dashboard/einsatzbereitschaft", headers=auth_header(verantwortlicher_token)
|
||||
)
|
||||
assert bereitschaft.json()["details"][0]["gruende"] == ["hu_uvv_ueberfaellig"]
|
||||
|
||||
prueftermine = await client.get("/api/v1/dashboard/prueftermine", headers=auth_header(verantwortlicher_token))
|
||||
body = prueftermine.json()
|
||||
assert len(body) == 1
|
||||
assert body[0]["typ"] == "fahrzeug_hu"
|
||||
assert body[0]["status"] == "abgelaufen"
|
||||
Reference in New Issue
Block a user