fix(kontrolle): doppelten Fehlbestand bei Folge-Kontrolle vermieden
_synchronisiere_fehlbestand suchte nur nach Fehlbeständen der eigenen
Kontroll-Sitzung. Ein bereits offener Fehlbestand aus einer früheren
Kontrolle für dasselbe Objekt+Material wurde nicht wiedergefunden -
es entstand ein zweiter, paralleler Fehlbestand mit eigener (neuerer)
Sollmenge, der alte blieb mit veralteter Sollmenge offen liegen.
Lookup jetzt über objekt_id+material_id+status=offen statt
kontrolle_id; kontrolle_id am gefundenen Datensatz bleibt unverändert,
damit ein Abbruch der aktuellen Kontrolle ihn nicht fälschlich
mitlöscht. Löst sich die Abweichung über eine andere Kontrolle als
die erzeugende auf, wird der Fehlbestand jetzt sauber als erledigt
markiert (inkl. Mindermenge-Abschluss) statt stillschweigend gelöscht.
Nebenbei: Kommentare mit Kachel-/Datums-Referenzen ("Bug-Fund
2026-09-06", "UI-00X (Epic 20)") aus dem Code dieser Session entfernt.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
This commit is contained in:
@@ -106,7 +106,7 @@ export async function ladeDokumentHerunter(dokumentId: string, dateiname: string
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
// UI-005 (Epic 20): Dokumente waren bisher nur per Zwangs-Download einsehbar,
|
||||
// Dokumente waren bisher nur per Zwangs-Download einsehbar,
|
||||
// nicht direkt im Browser anschaubar (PDF/Bild in neuem Tab statt Datei
|
||||
// speichern). Auch hier: Auth-Header nötig, daher Blob statt einfachem
|
||||
// <a href> auf den Download-Endpunkt. Object-URL wird bewusst NICHT sofort
|
||||
|
||||
@@ -13,7 +13,7 @@ interface Eintrag {
|
||||
}
|
||||
|
||||
/**
|
||||
* UI-002 (Epic 20): globale Suche/Quick Actions per Ctrl+K bzw. ⌘+K.
|
||||
* globale Suche/Quick Actions per Ctrl+K bzw. ⌘+K.
|
||||
* Öffnet sich über der ganzen App (AppShell), navigiert nur - keine eigene
|
||||
* Fachlogik, nur Zusammenfassung bereits vorhandener Routen/Aktionen.
|
||||
*/
|
||||
|
||||
@@ -25,7 +25,7 @@ function gespeichertesTheme(): Theme {
|
||||
const REIHENFOLGE: Theme[] = ["system", "light", "dark"];
|
||||
const LABEL: Record<Theme, string> = { system: "System", light: "Hell", dark: "Dunkel" };
|
||||
|
||||
/** UI-001 (Epic 20): manueller Dark-Mode-Schalter, Präferenz überlebt Reload. */
|
||||
/** manueller Dark-Mode-Schalter, Präferenz überlebt Reload. */
|
||||
export function ThemeToggle() {
|
||||
const [theme, setTheme] = useState<Theme>(() => gespeichertesTheme());
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ interface Props {
|
||||
tage?: number;
|
||||
}
|
||||
|
||||
/** UI-003 (Epic 20): gemeinsame Basis für InspectionStatus/InventoryStatus -
|
||||
/** gemeinsame Basis für InspectionStatus/InventoryStatus -
|
||||
* beide Backend-Endpunkte (Prüftermine/Ablaufdaten) liefern dieselben zwei
|
||||
* Fristzustände (siehe app/services/dashboard.py `_ablauf_status`). */
|
||||
export function FristBadge({ status, tage }: Props) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { FristBadge } from "./FristBadge";
|
||||
|
||||
/** UI-003 (Epic 20): Geräteprüfung-Fristanzeige (Dashboard "Nächste Prüftermine"). */
|
||||
/** Geräteprüfung-Fristanzeige (Dashboard "Nächste Prüftermine"). */
|
||||
export function InspectionStatus({ status, tage }: { status: string; tage?: number }) {
|
||||
return <FristBadge status={status} tage={tage} />;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { FristBadge } from "./FristBadge";
|
||||
|
||||
/** UI-003 (Epic 20): Chargen-/Ablaufdatum-Fristanzeige (Dashboard "Bevorstehende Ablaufdaten"). */
|
||||
/** Chargen-/Ablaufdatum-Fristanzeige (Dashboard "Bevorstehende Ablaufdaten"). */
|
||||
export function InventoryStatus({ status, tage }: { status: string; tage?: number }) {
|
||||
return <FristBadge status={status} tage={tage} />;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ interface Props {
|
||||
}
|
||||
|
||||
/**
|
||||
* UI-003 (Epic 20): einzige Stelle, die Einsatzbereitschaft farblich/textlich
|
||||
* einzige Stelle, die Einsatzbereitschaft farblich/textlich
|
||||
* darstellt - "unbekannt" (noch nie kontrolliert) sieht bewusst NIE wie
|
||||
* "einsatzbereit" aus (eigene Farbe/Icon, siehe global.css badge-unknown).
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,7 @@ import { alleEintraege } from "../../offline/queue";
|
||||
import { useOnlineStatus } from "../../offline/useOnlineStatus";
|
||||
|
||||
/**
|
||||
* UI-003 (Epic 20): Online-/Offline-/Sync-Status war bisher nirgends sichtbar
|
||||
* Online-/Offline-/Sync-Status war bisher nirgends sichtbar
|
||||
* (useOnlineStatus/Queue existierten nur intern für den Kontroll-Workflow) -
|
||||
* echte Lücke laut Redesign-Vorgabe, kein reiner Umbau.
|
||||
*/
|
||||
|
||||
@@ -104,7 +104,7 @@ export function AdminPage() {
|
||||
const [qualifikationstypen, setQualifikationstypen] = useState<Qualifikationstyp[]>([]);
|
||||
const [fehler, setFehler] = useState<string | null>(null);
|
||||
const [laedt, setLaedt] = useState(true);
|
||||
// UI-002 (Epic 20): Tab im URL-Query statt nur lokalem State - erlaubt
|
||||
// Tab im URL-Query statt nur lokalem State - erlaubt
|
||||
// Deep-Links von der Command Palette (z.B. /admin?tab=material).
|
||||
const [suchparameter, setSuchparameter] = useSearchParams();
|
||||
const tab = suchparameter.get("tab") as Tab | null;
|
||||
|
||||
@@ -58,7 +58,7 @@ export function AktePage() {
|
||||
</ul>
|
||||
)}
|
||||
|
||||
{/* UI-007 (Epic 20): nach dem QR-Scan (MOBILE-002) landet man hier - die
|
||||
{/* nach dem QR-Scan (MOBILE-002) landet man hier - die
|
||||
Kernaktionen sollen direkt sichtbar sein, nicht erst am Seitenende. */}
|
||||
<div className="row" style={{ flexWrap: "wrap", marginTop: "1rem" }}>
|
||||
<Link to={`/objekte/${objekt.id}/kontrolle`} className="btn btn-primary">
|
||||
|
||||
@@ -118,10 +118,6 @@ export function DashboardPage() {
|
||||
const pruefungenUeberfaellig = prueftermine.filter((p) => p.status === "abgelaufen").length;
|
||||
const ablaufdatenAbgelaufen = ablaufdaten.filter((a) => a.status === "abgelaufen").length;
|
||||
|
||||
// UI-004 (Epic 20): "Was ist einsatzbereit und was muss ich jetzt tun?" in
|
||||
// 5 Sekunden beantwortbar - eine Aufgaben-Karte statt Zahlen über sechs
|
||||
// gleichrangige Karten verteilt suchen zu müssen. Keine neuen Daten, nur
|
||||
// Zusammenfassung der bereits geladenen Kennzahlen mit Direktlinks.
|
||||
const aufgaben = [
|
||||
kennzahlen && kennzahlen.anzahl_offener_fehlbestaende > 0
|
||||
? { text: `${kennzahlen.anzahl_offener_fehlbestaende} offene Fehlbestände`, ziel: "/fehlbestaende" }
|
||||
|
||||
@@ -4,7 +4,7 @@ import { apiRequest } from "../api/client";
|
||||
import { useAuth } from "../auth/AuthContext";
|
||||
import type { Fehlbestand, Material, Objekt } from "../api/types";
|
||||
|
||||
// UI-008 (Epic 20): Status war bisher immer badge-neutral, unabhängig vom
|
||||
// Status war bisher immer badge-neutral, unabhängig vom
|
||||
// tatsächlichen Zustand - andere Listen (Mängel, Ausgabe) unterscheiden längst
|
||||
// visuell zwischen offen/erledigt.
|
||||
const STATUS_BADGE: Record<string, string> = {
|
||||
|
||||
@@ -14,7 +14,7 @@ const STATUS_LABEL: Record<MangelStatus, string> = {
|
||||
erledigt: "Erledigt",
|
||||
};
|
||||
|
||||
// UI-008 (Epic 20): Status-Badge war immer neutral, unabhängig vom Zustand.
|
||||
// Status-Badge war immer neutral, unabhängig vom Zustand.
|
||||
const STATUS_BADGE: Record<MangelStatus, string> = {
|
||||
neu: "badge-warning",
|
||||
in_bearbeitung: "badge-warning",
|
||||
@@ -45,7 +45,7 @@ export function MangelListePage() {
|
||||
const [nurOffene, setNurOffene] = useState(true);
|
||||
|
||||
const [suchparameter] = useSearchParams();
|
||||
// UI-007 (Epic 20): Quick Action "Mangel melden" aus der Objektakte springt
|
||||
// Quick Action "Mangel melden" aus der Objektakte springt
|
||||
// hierher mit ?objekt_id=X und wählt das Objekt direkt vor.
|
||||
const [objektId, setObjektId] = useState<number | "">(() => {
|
||||
const wert = Number(suchparameter.get("objekt_id"));
|
||||
|
||||
@@ -5,10 +5,8 @@ import { apiRequest, ApiError } from "../api/client";
|
||||
import type { Objekt } from "../api/types";
|
||||
import { BarcodeScanner } from "../components/BarcodeScanner";
|
||||
|
||||
// Prompt 11 Screen 1: Standort/Objekt wählen. Objekt-Code (MOBILE-002) →
|
||||
// Direktsprung zur Akte (Zielseite laut Kachel 15), sonst füllt der Scan
|
||||
// dasselbe Textfeld wie die manuelle Suche (Kamera fällt bei Etikettschaden
|
||||
// auf Tippen zurück).
|
||||
// Objekt-Code-Scan springt direkt zur Akte, sonst füllt er dasselbe Textfeld
|
||||
// wie die manuelle Suche (Kamera fällt bei Etikettschaden auf Tippen zurück).
|
||||
export function ObjektListPage() {
|
||||
const navigate = useNavigate();
|
||||
const [objekte, setObjekte] = useState<Objekt[]>([]);
|
||||
@@ -18,7 +16,7 @@ export function ObjektListPage() {
|
||||
const [scannerOffen, setScannerOffen] = useState(false);
|
||||
const [suchparameter, setSuchparameter] = useSearchParams();
|
||||
|
||||
// UI-002 (Epic 20): Quick Action "QR scannen" aus der Command Palette springt
|
||||
// Quick Action "QR scannen" aus der Command Palette springt
|
||||
// hierher mit ?scan=1 und öffnet den Scanner direkt, statt nur die Liste zu zeigen.
|
||||
useEffect(() => {
|
||||
if (suchparameter.get("scan") === "1") {
|
||||
|
||||
@@ -15,7 +15,7 @@ const STATUS_LABEL: Record<GeraetInstanz["status"], string> = {
|
||||
in_reparatur: "In Reparatur",
|
||||
};
|
||||
|
||||
// UI-008 (Epic 20): Status-Badge war immer neutral, unabhängig vom Zustand.
|
||||
// Status-Badge war immer neutral, unabhängig vom Zustand.
|
||||
const STATUS_BADGE: Record<GeraetInstanz["status"], string> = {
|
||||
einsatzbereit: "badge-success",
|
||||
defekt: "badge-danger",
|
||||
|
||||
@@ -21,7 +21,7 @@ function ablaufFarbe(ablaufdatum: string): string | undefined {
|
||||
return "var(--color-success)";
|
||||
}
|
||||
|
||||
// UI-006 (Epic 20): "Fehlende oder problematische Positionen klar hervorheben" -
|
||||
// "Fehlende oder problematische Positionen klar hervorheben" -
|
||||
// Rand-Farbe macht eine Abweichung auch nach dem Speichern noch auf einen Blick
|
||||
// sichtbar, nicht nur kurz während des Nachfüll-Dialogs.
|
||||
function rahmenFarbe(status: PositionZustand["status"], passt: boolean): string {
|
||||
|
||||
Reference in New Issue
Block a user