fix(mobile): MOBILE-002 Scan routet zur Akte statt zur Kontrolle
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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
@@ -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<Objekt[]>([]);
|
||||
@@ -27,7 +28,7 @@ export function ObjektListPage() {
|
||||
setScannerOffen(false);
|
||||
try {
|
||||
const objekt = await apiRequest<Objekt>(`/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);
|
||||
|
||||
@@ -16,9 +16,8 @@ const TYP_LABEL: Record<LagerortTyp, string> = {
|
||||
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<Lagerort[]>([]);
|
||||
const [lagerplaetze, setLagerplaetze] = useState<Lagerplatz[]>([]);
|
||||
@@ -37,6 +36,7 @@ export function LagerSection({ materialien, onFehler }: Props) {
|
||||
const [buchungMaterial, setBuchungMaterial] = useState<number | "">("");
|
||||
const [buchungMenge, setBuchungMenge] = useState("");
|
||||
const [buchungGrund, setBuchungGrund] = useState("");
|
||||
const [umlagernZiel, setUmlagernZiel] = useState<number | "">("");
|
||||
|
||||
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 (
|
||||
<section className="section">
|
||||
<h2>Lager</h2>
|
||||
@@ -280,6 +304,23 @@ export function LagerSection({ materialien, onFehler }: Props) {
|
||||
<button className="btn btn-secondary" onClick={() => buchen("auslagern")}>
|
||||
Auslagern
|
||||
</button>
|
||||
<select
|
||||
className="input"
|
||||
value={umlagernZiel}
|
||||
onChange={(e) => setUmlagernZiel(Number(e.target.value) || "")}
|
||||
>
|
||||
<option value="">Umlagern nach…</option>
|
||||
{lagerplaetze
|
||||
.filter((p) => p.id !== ausgewaehlterPlatz)
|
||||
.map((p) => (
|
||||
<option key={p.id} value={p.id}>
|
||||
{p.code}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<button className="btn btn-secondary" onClick={umlagern}>
|
||||
Verschieben
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h4>Bestand</h4>
|
||||
|
||||
Reference in New Issue
Block a user