Web: Mindestbestaende per Loesch-Knopf entfernbar

Loeschen ging bisher nur uebers Leeren des Zahlenfeldes - das war weder
auffindbar noch (bei Gesamt-Werten, deren Zeile bestehen bleibt) sichtbar.
Jetzt hat jede gesetzte Zeile einen Papierkorb-Knopf: Gesamt-Wert -> NULL,
je-Lagerort-Eintrag faellt raus. Mit Rueckfrage ueber den App-Dialog.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-07-29 17:12:57 +02:00
parent bfc52997dc
commit e28b6a5e38

View File

@@ -1,6 +1,7 @@
import { useEffect, useMemo, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { api } from "../api"; import { api } from "../api";
import { useAuth } from "../auth"; import { useAuth } from "../auth";
import { useConfirm } from "../confirm";
import Icon from "../components/Icon"; import Icon from "../components/Icon";
import DataTable from "../components/DataTable"; import DataTable from "../components/DataTable";
import CategoryPathLabel, { pathParts } from "../components/CategoryPathLabel"; import CategoryPathLabel, { pathParts } from "../components/CategoryPathLabel";
@@ -30,6 +31,7 @@ const EMPTY_DRAFT = { kind: "product", targetId: "", scope: "global", locId: "",
export default function MinStock() { export default function MinStock() {
const { isAdmin } = useAuth(); const { isAdmin } = useAuth();
const confirm = useConfirm();
const [products, setProducts] = useState([]); const [products, setProducts] = useState([]);
const [groups, setGroups] = useState([]); const [groups, setGroups] = useState([]);
const [locations, setLocations] = useState([]); const [locations, setLocations] = useState([]);
@@ -132,6 +134,35 @@ export default function MinStock() {
} catch (err) { setError(err.message); } } catch (err) { setError(err.message); }
} }
// Mindestbestand ganz entfernen: Gesamt-Wert auf NULL, je-Lagerort-Eintrag raus.
// Das Feld leeren tut dasselbe aber ein eigener Knopf macht das Löschen
// eindeutig (und bei Gesamt-Werten sichtbar, da die Zeile sonst bestehen bleibt).
async function deleteRow(row) {
const ziel = row.kind === "group" ? `Gruppe „${row.name}"` : `${row.name}"`;
const wo = row.scope === "loc" ? ` am Lagerort „${row.ort}"` : "";
if (!(await confirm({
title: "Mindestbestand löschen?",
message: `Mindestbestand von ${ziel}${wo} entfernen?`,
confirmLabel: "Löschen", danger: true,
}))) return;
setError(null);
try {
if (row.kind === "product" && row.scope === "global") {
await api.updateProduct(row.entity.id, {
min_stock: null, min_stock_in_packages: false,
min_stock_unit_id: row.entity.display_unit_id ?? null,
});
} else if (row.kind === "product" && row.scope === "loc") {
await api.setProductLocationMinStock(row.entity.id, mergeLoc(row.entity.location_min_stocks, row.locId, null));
} else if (row.kind === "group" && row.scope === "global") {
await api.updateGroup(row.entity.id, { min_stock: null });
} else if (row.kind === "group" && row.scope === "loc") {
await api.setGroupLocationMinStock(row.entity.id, mergeLoc(row.entity.location_min_stocks, row.locId, null));
}
await load();
} catch (err) { setError(err.message); }
}
// Ausgewähltes Ziel des Dialogs + dessen Einheit (Menge wird darin erfasst). // Ausgewähltes Ziel des Dialogs + dessen Einheit (Menge wird darin erfasst).
const draftTarget = draft.kind === "product" const draftTarget = draft.kind === "product"
? products.find((p) => String(p.id) === String(draft.targetId)) ? products.find((p) => String(p.id) === String(draft.targetId))
@@ -195,12 +226,18 @@ export default function MinStock() {
: <span className="muted"></span>) }, : <span className="muted"></span>) },
{ key: "ort", header: "Ort", width: 220, filterText: (r) => r.ort, sortValue: (r) => r.ort, { key: "ort", header: "Ort", width: 220, filterText: (r) => r.ort, sortValue: (r) => r.ort,
render: (r) => (r.scope === "global" ? <span className="muted">{r.ort}</span> : r.ort) }, render: (r) => (r.scope === "global" ? <span className="muted">{r.ort}</span> : r.ort) },
{ key: "soll", header: "Mindestbestand", width: 150, { key: "soll", header: "Mindestbestand", width: 190,
sortValue: (r) => r.soll ?? -1, sortValue: (r) => r.soll ?? -1,
render: (r) => (isAdmin ? ( render: (r) => (isAdmin ? (
<div className="field-inline" style={{ gap: "var(--sp-1)", flexWrap: "nowrap" }}>
<input type="number" step="any" min="0" style={{ marginTop: 0, minWidth: 80 }} <input type="number" step="any" min="0" style={{ marginTop: 0, minWidth: 80 }}
key={r.soll ?? "none"} defaultValue={r.soll ?? ""} key={r.soll ?? "none"} defaultValue={r.soll ?? ""}
onBlur={(e) => saveSoll(r, e.target.value)} /> onBlur={(e) => saveSoll(r, e.target.value)} />
{r.soll != null && (
<button type="button" className="btn-icon danger" title="Mindestbestand löschen"
onClick={() => deleteRow(r)}><Icon name="trash" size={15} /></button>
)}
</div>
) : (r.soll != null ? fmt(r.soll) : "")) }, ) : (r.soll != null ? fmt(r.soll) : "")) },
{ key: "unit", header: "Einheit", width: 120, filterText: (r) => r.unit || "", { key: "unit", header: "Einheit", width: 120, filterText: (r) => r.unit || "",
render: (r) => <span className="muted">{r.unit || ""}</span> }, render: (r) => <span className="muted">{r.unit || ""}</span> },