From 01263753bf3f9179dad8fcd136db5a30d8e28c86 Mon Sep 17 00:00:00 2001 From: patrick Date: Sun, 6 Sep 2026 09:42:26 +0200 Subject: [PATCH] fix(mobile): MOBILE-002 Scan routet zur Akte statt zur Kontrolle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Scan-Ziel war noch auf den alten Kontroll-Direktsprung eingestellt, obwohl die Akte (FILE-006) seit 2026-09-05 existiert. Kurzweg-Button "Kontrolle starten" auf der Akte deckt den alten Ablauf weiter ab. Frontend-Anzeigen-Audit über alle Epics ergab keine weiteren echten Backend-fertig/Anzeige-fehlt-Lücken. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV --- arbeitskacheln/15_mobile.md | 6 +-- frontend/src/pages/ObjektListPage.tsx | 9 +++-- frontend/src/pages/admin/LagerSection.tsx | 47 +++++++++++++++++++++-- 3 files changed, 51 insertions(+), 11 deletions(-) diff --git a/arbeitskacheln/15_mobile.md b/arbeitskacheln/15_mobile.md index d3de9e9..93b2809 100644 --- a/arbeitskacheln/15_mobile.md +++ b/arbeitskacheln/15_mobile.md @@ -48,10 +48,8 @@ Details zu MOBILE-001 … MOBILE-005 (vollständig). - **Akzeptanzkriterien:** Scan bis Akte offen in wenigen Sekunden, Fehlerfall (kein QR erkannt/ungültiger Code) klar kommuniziert. - **Tests:** Scan-Erfolgs-/Fehlertest. -- **DoD:** deckt sich mit MABEA (`BarcodeScanner`-Komponente + Scan-Route bereits - vorhanden) — die Akte existiert seit 2026-09-05 (`/akte/objekt/{id}`), der Scan - routet aber noch zur Kontrolle statt dorthin. Kleiner Nachzug (Scan-Ziel ändern), - kein Neubau mehr. +- **DoD:** deckt sich 1:1 mit MABEA — Scan routet seit 2026-09-06 zur Akte + (`/akte/objekt/{id}`), von dort Kurzweg-Button "Kontrolle starten". ## MOBILE-003 — Offline-Grundgerüst (P2) diff --git a/frontend/src/pages/ObjektListPage.tsx b/frontend/src/pages/ObjektListPage.tsx index 4c6b7d0..bf6d4b5 100644 --- a/frontend/src/pages/ObjektListPage.tsx +++ b/frontend/src/pages/ObjektListPage.tsx @@ -5,9 +5,10 @@ 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 (Karte 10) → -// Direktsprung ins Kontroll-Screen, sonst füllt der Scan dasselbe Textfeld wie -// die manuelle Suche (Kamera fällt bei Etikettschaden auf Tippen zurück). +// 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). export function ObjektListPage() { const navigate = useNavigate(); const [objekte, setObjekte] = useState([]); @@ -27,7 +28,7 @@ export function ObjektListPage() { setScannerOffen(false); try { const objekt = await apiRequest(`/objekte/code/${encodeURIComponent(code)}`); - navigate(`/objekte/${objekt.id}/kontrolle`); + navigate(`/akte/objekt/${objekt.id}`); } catch (err) { if (err instanceof ApiError && err.status === 404) { setSuche(code); diff --git a/frontend/src/pages/admin/LagerSection.tsx b/frontend/src/pages/admin/LagerSection.tsx index cf7fa00..9c4e64c 100644 --- a/frontend/src/pages/admin/LagerSection.tsx +++ b/frontend/src/pages/admin/LagerSection.tsx @@ -16,9 +16,8 @@ const TYP_LABEL: Record = { lager: "Lager", }; -// WH-001..004+007 (Warehouse-Kern): Lagerort-Hierarchie, Lagerplätze, Bestand -// je Lagerplatz, Ein-/Auslagerung. Umlagerung (WH-005) und Inventur (WH-006) -// bewusst als Folgeschritt zurückgestellt (arbeitskacheln/07_warehouse.md). +// WH-001..005+007: Lagerort-Hierarchie, Lagerplätze, Bestand je Lagerplatz, +// Ein-/Auslagerung, Umlagerung. Inventur (WH-006) noch offen (arbeitskacheln/07_warehouse.md). export function LagerSection({ materialien, onFehler }: Props) { const [lagerorte, setLagerorte] = useState([]); const [lagerplaetze, setLagerplaetze] = useState([]); @@ -37,6 +36,7 @@ export function LagerSection({ materialien, onFehler }: Props) { const [buchungMaterial, setBuchungMaterial] = useState(""); const [buchungMenge, setBuchungMenge] = useState(""); const [buchungGrund, setBuchungGrund] = useState(""); + const [umlagernZiel, setUmlagernZiel] = useState(""); async function laden() { try { @@ -144,6 +144,30 @@ export function LagerSection({ materialien, onFehler }: Props) { } } + async function umlagern() { + if (ausgewaehlterPlatz === "" || umlagernZiel === "" || buchungMaterial === "" || !buchungMenge) { + onFehler("Ziel-Lagerplatz, Material und Menge angeben."); + return; + } + try { + await apiRequest(`/lagerplaetze/${ausgewaehlterPlatz}/umlagern`, { + method: "POST", + body: { + nach_lagerplatz_id: umlagernZiel, + material_id: buchungMaterial, + menge: buchungMenge, + grund: buchungGrund || null, + }, + }); + setBuchungMenge(""); + setBuchungGrund(""); + setUmlagernZiel(""); + await ladeBestandUndBewegungen(ausgewaehlterPlatz); + } catch { + onFehler("Umlagerung fehlgeschlagen (nicht genug Bestand oder Ziel = Quelle)."); + } + } + return (

Lager

@@ -280,6 +304,23 @@ export function LagerSection({ materialien, onFehler }: Props) { + +

Bestand