diff --git a/backend/app/schemas/dashboard.py b/backend/app/schemas/dashboard.py
index b13b528..ad50041 100644
--- a/backend/app/schemas/dashboard.py
+++ b/backend/app/schemas/dashboard.py
@@ -34,6 +34,7 @@ class EinsatzbereitschaftRead(BaseModel):
unbekannt: int
nicht_einsatzbereit: int
gesamt: int
+ nie_kontrolliert_gesamt: int
details: list[ObjektMangelRead]
diff --git a/backend/app/services/dashboard.py b/backend/app/services/dashboard.py
index 8656abb..3e0472a 100644
--- a/backend/app/services/dashboard.py
+++ b/backend/app/services/dashboard.py
@@ -197,6 +197,7 @@ async def einsatzbereitschaft(db: AsyncSession) -> dict:
)
bereit = eingeschraenkt = unbekannt = 0
+ nie_kontrolliert_gesamt = 0
details: list[dict] = []
for objekt in objekte:
kategorie, gruende = _kategorie_und_gruende(
@@ -210,6 +211,8 @@ async def einsatzbereitschaft(db: AsyncSession) -> dict:
objekt_ids_kritischer_mangel,
objekt_ids_hu_uvv_ueberfaellig,
)
+ if "noch_nie_kontrolliert" in gruende:
+ nie_kontrolliert_gesamt += 1
if kategorie == "ready":
bereit += 1
else:
@@ -225,6 +228,10 @@ async def einsatzbereitschaft(db: AsyncSession) -> dict:
"unbekannt": unbekannt,
"nicht_einsatzbereit": len(objekte) - bereit - eingeschraenkt - unbekannt,
"gesamt": len(objekte),
+ # Bug-Fund 2026-09-06: "unbekannt" zählt nur Objekte OHNE jeden anderen
+ # Grund - diese Zahl zählt "nie kontrolliert" unabhängig von sonstigen
+ # Problemen mit, damit sie beim manuellen Nachzählen stimmt.
+ "nie_kontrolliert_gesamt": nie_kontrolliert_gesamt,
"details": details,
}
@@ -260,12 +267,23 @@ def _kategorie_und_gruende(
if objekt.id in objekt_ids_hu_uvv_ueberfaellig:
gruende.append("hu_uvv_ueberfaellig")
+ # Bug-Fund 2026-09-06 (Nutzer: "beim genauen Schauen sind es mehr"):
+ # "noch nie kontrolliert" wurde bisher nur gezählt, wenn es der EINZIGE
+ # Grund war - ein Objekt mit z.B. offenem Fehlbestand UND nie kontrolliert
+ # verlor den Hinweis komplett (fiel unter "nicht einsatzbereit" ohne
+ # diesen Grund zu nennen). Jetzt immer als Zusatzgrund angehängt, wenn
+ # zutreffend, unabhängig von anderen Gründen.
+ nie_kontrolliert = letzte_kontrolle is None
+
if not gruende and fehlbestand_ids and fehlbestand_ids <= fehlbestand_ids_genehmigt:
- return "limited", ["mindermenge_genehmigt"]
- if not gruende and letzte_kontrolle is None:
+ zusatz = ["noch_nie_kontrolliert"] if nie_kontrolliert else []
+ return "limited", ["mindermenge_genehmigt"] + zusatz
+ if not gruende and nie_kontrolliert:
return "unknown", ["noch_nie_kontrolliert"]
if not gruende:
return "ready", []
+ if nie_kontrolliert:
+ gruende = gruende + ["noch_nie_kontrolliert"]
return "not-ready", gruende
diff --git a/backend/tests/test_dashboard.py b/backend/tests/test_dashboard.py
index d62ca80..b76eee8 100644
--- a/backend/tests/test_dashboard.py
+++ b/backend/tests/test_dashboard.py
@@ -186,6 +186,7 @@ async def test_nie_kontrolliertes_objekt_ist_unbekannt(
assert body["unbekannt"] == 1
assert body["nicht_einsatzbereit"] == 0
assert body["details"][0]["gruende"] == ["noch_nie_kontrolliert"]
+ assert body["nie_kontrolliert_gesamt"] == 1
@pytest.mark.asyncio
@@ -203,7 +204,11 @@ async def test_offener_fehlbestand_macht_objekt_nicht_einsatzbereit(
body = response.json()
assert body["einsatzbereit"] == 0
assert body["nicht_einsatzbereit"] == 1
- assert body["details"][0]["gruende"] == ["fehlbestand_offen"]
+ # Bug-Fund 2026-09-06: "nie kontrolliert" darf nicht verloren gehen, nur
+ # weil das Objekt zusätzlich einen anderen Grund hat (objekt_mit_position
+ # hat nie eine abgeschlossene Kontrolle).
+ assert body["details"][0]["gruende"] == ["fehlbestand_offen", "noch_nie_kontrolliert"]
+ assert body["nie_kontrolliert_gesamt"] == 1
@pytest.mark.asyncio
@@ -230,7 +235,8 @@ async def test_genehmigte_mindermenge_macht_objekt_eingeschraenkt_einsatzbereit(
assert body["einsatzbereit"] == 0
assert body["eingeschraenkt_einsatzbereit"] == 1
assert body["nicht_einsatzbereit"] == 0
- assert body["details"][0]["gruende"] == ["mindermenge_genehmigt"]
+ assert body["details"][0]["gruende"] == ["mindermenge_genehmigt", "noch_nie_kontrolliert"]
+ assert body["nie_kontrolliert_gesamt"] == 1
@pytest.mark.asyncio
@@ -257,7 +263,8 @@ async def test_ueberfaellige_pruefung_macht_objekt_nicht_einsatzbereit(
response = await client.get("/api/v1/dashboard/einsatzbereitschaft", headers=auth_header(token))
body = response.json()
assert body["nicht_einsatzbereit"] == 1
- assert body["details"][0]["gruende"] == ["pruefung_ueberfaellig"]
+ assert body["details"][0]["gruende"] == ["pruefung_ueberfaellig", "noch_nie_kontrolliert"]
+ assert body["nie_kontrolliert_gesamt"] == 1
prueftermine = await client.get("/api/v1/dashboard/prueftermine", headers=auth_header(token))
prueftermine_body = prueftermine.json()
diff --git a/frontend/src/pages/DashboardPage.tsx b/frontend/src/pages/DashboardPage.tsx
index d378f07..4a88154 100644
--- a/frontend/src/pages/DashboardPage.tsx
+++ b/frontend/src/pages/DashboardPage.tsx
@@ -42,6 +42,7 @@ interface Einsatzbereitschaft {
unbekannt: number;
nicht_einsatzbereit: number;
gesamt: number;
+ nie_kontrolliert_gesamt: number;
details: ObjektMangel[];
}
@@ -130,8 +131,8 @@ export function DashboardPage() {
ablaufdatenAbgelaufen > 0
? { text: `${ablaufdatenAbgelaufen} Chargen abgelaufen`, ziel: null }
: null,
- bereitschaft && bereitschaft.unbekannt > 0
- ? { text: `${bereitschaft.unbekannt} Objekte noch nie kontrolliert`, ziel: "/objekte" }
+ bereitschaft && bereitschaft.nie_kontrolliert_gesamt > 0
+ ? { text: `${bereitschaft.nie_kontrolliert_gesamt} Objekte noch nie kontrolliert`, ziel: "/objekte" }
: null,
].filter((a): a is { text: string; ziel: string | null } => a !== null);
@@ -176,6 +177,12 @@ export function DashboardPage() {
+ Davon {bereitschaft.nie_kontrolliert_gesamt} insgesamt noch nie kontrolliert (auch bei Objekten mit + weiteren Problemen). +
+ )} {bereitschaft.details.length > 0 && ( <>