feat(akte): Ablaufdatum in der Beladung-Tabelle
CI / backend-tests (push) Failing after 2m6s
CI / frontend-build (push) Successful in 18s

Bevorstehende Ablaufdaten waren nur im Dashboard und als
Einsatzbereitschafts-Grund sichtbar, nicht direkt an der jeweiligen
Beladungsposition in der Akte. Neue Spalte "Ablauf", rot bei
überfälligem, gelb bei <30 Tagen verbleibender Zeit.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
This commit is contained in:
2026-09-06 23:48:31 +02:00
co-authored by Claude Sonnet 5
parent a18ec5bbce
commit cc016f09d3
5 changed files with 16 additions and 1 deletions
+2 -1
View File
@@ -1,5 +1,5 @@
import uuid import uuid
from datetime import datetime from datetime import date, datetime
from decimal import Decimal from decimal import Decimal
from pydantic import BaseModel, ConfigDict from pydantic import BaseModel, ConfigDict
@@ -27,6 +27,7 @@ class BeladungsPosition(BaseModel):
fach: str | None fach: str | None
sollmenge: Decimal sollmenge: Decimal
istmenge: Decimal istmenge: Decimal
ablaufdatum: date | None
class AkteRead(BaseModel): class AkteRead(BaseModel):
+1
View File
@@ -123,6 +123,7 @@ async def akte_fuer_objekt(db: AsyncSession, objekt_id: int) -> AkteRead | None:
if material.materialtyp == MaterialTyp.geraet_sn if material.materialtyp == MaterialTyp.geraet_sn
else position.istmenge else position.istmenge
), ),
ablaufdatum=position.ablaufdatum,
) )
for position, material in beladung_result.all() for position, material in beladung_result.all()
] ]
+1
View File
@@ -54,6 +54,7 @@ async def test_akte_buendelt_teilbereiche(client, db_session, objekt_mit_positio
assert body["beladung"][0]["material_id"] == _material.id assert body["beladung"][0]["material_id"] == _material.id
assert body["beladung"][0]["sollmenge"] == "10" assert body["beladung"][0]["sollmenge"] == "10"
assert body["beladung"][0]["istmenge"] == "0" assert body["beladung"][0]["istmenge"] == "0"
assert body["beladung"][0]["ablaufdatum"] is None
@pytest.mark.asyncio @pytest.mark.asyncio
+1
View File
@@ -319,6 +319,7 @@ export interface BeladungsPosition {
fach: string | null; fach: string | null;
sollmenge: string; sollmenge: string;
istmenge: string; istmenge: string;
ablaufdatum: string | null;
} }
export interface Akte { export interface Akte {
+11
View File
@@ -10,6 +10,13 @@ import { ReadinessBadge } from "../components/status/ReadinessBadge";
import { GRUND_TEXT } from "../components/status/gruendeText"; import { GRUND_TEXT } from "../components/status/gruendeText";
import { EREIGNIS_LABEL } from "../components/status/ereignisText"; import { EREIGNIS_LABEL } from "../components/status/ereignisText";
function ablaufFarbe(ablaufdatum: string): string | undefined {
const tage = (new Date(ablaufdatum).getTime() - Date.now()) / (1000 * 60 * 60 * 24);
if (tage < 0) return "var(--color-danger)";
if (tage < 30) return "var(--color-warning)";
return undefined;
}
const MANGEL_PRIORITAET_LABEL: Record<string, string> = { const MANGEL_PRIORITAET_LABEL: Record<string, string> = {
niedrig: "Niedrig", niedrig: "Niedrig",
normal: "Normal", normal: "Normal",
@@ -120,6 +127,7 @@ export function AktePage() {
<th>Fach</th> <th>Fach</th>
<th>Soll</th> <th>Soll</th>
<th>Ist</th> <th>Ist</th>
<th>Ablauf</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -133,6 +141,9 @@ export function AktePage() {
<td className={Number(b.istmenge) < Number(b.sollmenge) ? "text-danger" : undefined}> <td className={Number(b.istmenge) < Number(b.sollmenge) ? "text-danger" : undefined}>
{b.istmenge} {b.einheit} {b.istmenge} {b.einheit}
</td> </td>
<td style={{ color: b.ablaufdatum ? ablaufFarbe(b.ablaufdatum) : undefined }}>
{b.ablaufdatum ? new Date(b.ablaufdatum).toLocaleDateString("de-DE") : ""}
</td>
</tr> </tr>
))} ))}
</tbody> </tbody>