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
|
- **Akzeptanzkriterien:** Scan bis Akte offen in wenigen Sekunden, Fehlerfall (kein QR
|
||||||
erkannt/ungültiger Code) klar kommuniziert.
|
erkannt/ungültiger Code) klar kommuniziert.
|
||||||
- **Tests:** Scan-Erfolgs-/Fehlertest.
|
- **Tests:** Scan-Erfolgs-/Fehlertest.
|
||||||
- **DoD:** deckt sich mit MABEA (`BarcodeScanner`-Komponente + Scan-Route bereits
|
- **DoD:** deckt sich 1:1 mit MABEA — Scan routet seit 2026-09-06 zur Akte
|
||||||
vorhanden) — die Akte existiert seit 2026-09-05 (`/akte/objekt/{id}`), der Scan
|
(`/akte/objekt/{id}`), von dort Kurzweg-Button "Kontrolle starten".
|
||||||
routet aber noch zur Kontrolle statt dorthin. Kleiner Nachzug (Scan-Ziel ändern),
|
|
||||||
kein Neubau mehr.
|
|
||||||
|
|
||||||
## MOBILE-003 — Offline-Grundgerüst (P2)
|
## MOBILE-003 — Offline-Grundgerüst (P2)
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,10 @@ import { apiRequest, ApiError } from "../api/client";
|
|||||||
import type { Objekt } from "../api/types";
|
import type { Objekt } from "../api/types";
|
||||||
import { BarcodeScanner } from "../components/BarcodeScanner";
|
import { BarcodeScanner } from "../components/BarcodeScanner";
|
||||||
|
|
||||||
// Prompt 11 Screen 1: Standort/Objekt wählen. Objekt-Code (Karte 10) →
|
// Prompt 11 Screen 1: Standort/Objekt wählen. Objekt-Code (MOBILE-002) →
|
||||||
// Direktsprung ins Kontroll-Screen, sonst füllt der Scan dasselbe Textfeld wie
|
// Direktsprung zur Akte (Zielseite laut Kachel 15), sonst füllt der Scan
|
||||||
// die manuelle Suche (Kamera fällt bei Etikettschaden auf Tippen zurück).
|
// dasselbe Textfeld wie die manuelle Suche (Kamera fällt bei Etikettschaden
|
||||||
|
// auf Tippen zurück).
|
||||||
export function ObjektListPage() {
|
export function ObjektListPage() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [objekte, setObjekte] = useState<Objekt[]>([]);
|
const [objekte, setObjekte] = useState<Objekt[]>([]);
|
||||||
@@ -27,7 +28,7 @@ export function ObjektListPage() {
|
|||||||
setScannerOffen(false);
|
setScannerOffen(false);
|
||||||
try {
|
try {
|
||||||
const objekt = await apiRequest<Objekt>(`/objekte/code/${encodeURIComponent(code)}`);
|
const objekt = await apiRequest<Objekt>(`/objekte/code/${encodeURIComponent(code)}`);
|
||||||
navigate(`/objekte/${objekt.id}/kontrolle`);
|
navigate(`/akte/objekt/${objekt.id}`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof ApiError && err.status === 404) {
|
if (err instanceof ApiError && err.status === 404) {
|
||||||
setSuche(code);
|
setSuche(code);
|
||||||
|
|||||||
@@ -16,9 +16,8 @@ const TYP_LABEL: Record<LagerortTyp, string> = {
|
|||||||
lager: "Lager",
|
lager: "Lager",
|
||||||
};
|
};
|
||||||
|
|
||||||
// WH-001..004+007 (Warehouse-Kern): Lagerort-Hierarchie, Lagerplätze, Bestand
|
// WH-001..005+007: Lagerort-Hierarchie, Lagerplätze, Bestand je Lagerplatz,
|
||||||
// je Lagerplatz, Ein-/Auslagerung. Umlagerung (WH-005) und Inventur (WH-006)
|
// Ein-/Auslagerung, Umlagerung. Inventur (WH-006) noch offen (arbeitskacheln/07_warehouse.md).
|
||||||
// bewusst als Folgeschritt zurückgestellt (arbeitskacheln/07_warehouse.md).
|
|
||||||
export function LagerSection({ materialien, onFehler }: Props) {
|
export function LagerSection({ materialien, onFehler }: Props) {
|
||||||
const [lagerorte, setLagerorte] = useState<Lagerort[]>([]);
|
const [lagerorte, setLagerorte] = useState<Lagerort[]>([]);
|
||||||
const [lagerplaetze, setLagerplaetze] = useState<Lagerplatz[]>([]);
|
const [lagerplaetze, setLagerplaetze] = useState<Lagerplatz[]>([]);
|
||||||
@@ -37,6 +36,7 @@ export function LagerSection({ materialien, onFehler }: Props) {
|
|||||||
const [buchungMaterial, setBuchungMaterial] = useState<number | "">("");
|
const [buchungMaterial, setBuchungMaterial] = useState<number | "">("");
|
||||||
const [buchungMenge, setBuchungMenge] = useState("");
|
const [buchungMenge, setBuchungMenge] = useState("");
|
||||||
const [buchungGrund, setBuchungGrund] = useState("");
|
const [buchungGrund, setBuchungGrund] = useState("");
|
||||||
|
const [umlagernZiel, setUmlagernZiel] = useState<number | "">("");
|
||||||
|
|
||||||
async function laden() {
|
async function laden() {
|
||||||
try {
|
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 (
|
return (
|
||||||
<section className="section">
|
<section className="section">
|
||||||
<h2>Lager</h2>
|
<h2>Lager</h2>
|
||||||
@@ -280,6 +304,23 @@ export function LagerSection({ materialien, onFehler }: Props) {
|
|||||||
<button className="btn btn-secondary" onClick={() => buchen("auslagern")}>
|
<button className="btn btn-secondary" onClick={() => buchen("auslagern")}>
|
||||||
Auslagern
|
Auslagern
|
||||||
</button>
|
</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>
|
</div>
|
||||||
|
|
||||||
<h4>Bestand</h4>
|
<h4>Bestand</h4>
|
||||||
|
|||||||
Reference in New Issue
Block a user