feat(objekt): letzte abgeschlossene Kontrolle je Objekt anzeigen
CI / backend-tests (push) Failing after 1m28s
CI / frontend-build (push) Successful in 17s

Bisher nirgends vermerkt/abrufbar, wann ein Objekt zuletzt komplett
kontrolliert wurde (nur roh in kontrolle.beendet_am ableitbar). Neues Feld
ObjektRead.letzte_kontrolle_am (jüngste Kontrolle mit status=abgeschlossen),
angezeigt in Objektliste und Admin-Objektsektion ("noch nie kontrolliert"
falls keine).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KC8HYvv6UkCVYheYiTw9DD
This commit is contained in:
2026-09-05 11:00:49 +02:00
co-authored by Claude Sonnet 5
parent 69d6956d23
commit c3f141f7b0
10 changed files with 6599 additions and 14 deletions
+19 -1
View File
@@ -1,9 +1,11 @@
import re
from datetime import datetime
from decimal import Decimal
from sqlalchemy import select
from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.kontrolle import Kontrolle, KontrollStatus
from app.models.objekt import Objekt
from app.models.objektposition import Objektposition, ObjektpositionStatus
from app.models.vorlage import Beladungsvorlage, Vorlagenposition
@@ -29,6 +31,22 @@ async def naechster_freier_code(db: AsyncSession, spalte, praefix: str) -> str:
return f"{praefix}-{hoechste + 1:0{breite}d}"
async def letzte_kontrollen_map(db: AsyncSession, objekt_ids: list[int]) -> dict[int, datetime]:
"""Nutzer-Frage: "wann war die letzte komplette Kontrolle vom Objekt" - bisher
nirgends vermerkt (kein Cache-Feld am Objekt, nur ableitbar aus kontrolle.
beendet_am). Liefert je Objekt den jüngsten Abschluss-Zeitpunkt einer
Kontrolle mit status=abgeschlossen; Objekte ohne abgeschlossene Kontrolle
fehlen im Ergebnis (Aufrufer behandelt das als "noch nie kontrolliert")."""
if not objekt_ids:
return {}
result = await db.execute(
select(Kontrolle.objekt_id, func.max(Kontrolle.beendet_am))
.where(Kontrolle.objekt_id.in_(objekt_ids), Kontrolle.status == KontrollStatus.abgeschlossen)
.group_by(Kontrolle.objekt_id)
)
return {objekt_id: beendet_am for objekt_id, beendet_am in result.all()}
async def sollmenge_map(db: AsyncSession, vorlage_id: int | None) -> dict[int, Decimal]:
"""material_id -> Sollmenge laut Vorlage (Prompt 10: Basis für die dynamische
Auflösung, wenn eine Objektposition keinen Override hat)."""