Web: Chargen zeigen ihren Lagerort (und lassen ihn aendern)

Die Chargen-Tabelle hatte keine Lagerort-Spalte - dadurch wirkte es, als koenne
man Chargen keinem Ort zuordnen (geht laengst ueber den Lagerort beim Einlagern).
Neue Spalte "Lagerort" zeigt den Ort je Charge; beim Bearbeiten laesst er sich
per Dropdown aendern (z.B. 2 Glaeser Pesto in Schrank A, 2 in Schrank B).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-07-29 13:41:19 +02:00
parent d448754d2a
commit 227143d13f

View File

@@ -15,6 +15,7 @@ import LocationMinStock from "../components/LocationMinStock";
import { DynamicFields, FIELD_TYPES } from "../fields"; import { DynamicFields, FIELD_TYPES } from "../fields";
import ObjektBestand from "../components/ObjektBestand"; import ObjektBestand from "../components/ObjektBestand";
import Einzelstuecke from "../components/Einzelstuecke"; import Einzelstuecke from "../components/Einzelstuecke";
import { locationOptions, locationPathById } from "../locationPath";
import { import {
daysUntil, expiryRowClass, fmt, fromMonthInput, gebinde, isExpired, relativeExpiry, daysUntil, expiryRowClass, fmt, fromMonthInput, gebinde, isExpired, relativeExpiry,
toMonthInput, unitShort, toMonthInput, unitShort,
@@ -996,6 +997,7 @@ export default function ProductForm() {
<LotsCard <LotsCard
product={product} product={product}
lots={lots} lots={lots}
locations={locations}
baseShort={baseShort} baseShort={baseShort}
warnDays={warnDays} warnDays={warnDays}
isAdmin={isAdmin} isAdmin={isAdmin}
@@ -1063,12 +1065,12 @@ export default function ProductForm() {
} }
/** Chargen-Karte mit Bearbeiten/Löschen. Bearbeitet wird in der Produkteinheit. */ /** Chargen-Karte mit Bearbeiten/Löschen. Bearbeitet wird in der Produkteinheit. */
function LotsCard({ product, lots, baseShort, warnDays, isAdmin, onChanged, onError }) { function LotsCard({ product, lots, locations = [], baseShort, warnDays, isAdmin, onChanged, onError }) {
const confirm = useConfirm(); const confirm = useConfirm();
const toast = useToast(); const toast = useToast();
const { formatBestBefore } = useSettings(); const { formatBestBefore } = useSettings();
const [editId, setEditId] = useState(null); const [editId, setEditId] = useState(null);
const [draft, setDraft] = useState({ quantity: "", best_before: "", precision: "day" }); const [draft, setDraft] = useState({ quantity: "", best_before: "", precision: "day", location_id: "" });
// In welcher Einheit die Menge bearbeitet wird: "unit" oder "package". // In welcher Einheit die Menge bearbeitet wird: "unit" oder "package".
const [editUnit, setEditUnit] = useState("unit"); const [editUnit, setEditUnit] = useState("unit");
@@ -1107,6 +1109,7 @@ function LotsCard({ product, lots, baseShort, warnDays, isAdmin, onChanged, onEr
? toMonthInput(l.best_before) ? toMonthInput(l.best_before)
: l.best_before || "", : l.best_before || "",
precision: l.best_before_precision === "month" ? "month" : "day", precision: l.best_before_precision === "month" ? "month" : "day",
location_id: l.location_id || "",
}); });
} }
@@ -1127,6 +1130,7 @@ function LotsCard({ product, lots, baseShort, warnDays, isAdmin, onChanged, onEr
(draft.precision === "month" ? fromMonthInput(draft.best_before) : draft.best_before) || (draft.precision === "month" ? fromMonthInput(draft.best_before) : draft.best_before) ||
null, null,
best_before_precision: draft.precision || "day", best_before_precision: draft.precision || "day",
location_id: draft.location_id || null,
}); });
setEditId(null); setEditId(null);
await onChanged(); await onChanged();
@@ -1165,6 +1169,7 @@ function LotsCard({ product, lots, baseShort, warnDays, isAdmin, onChanged, onEr
<tr> <tr>
<th className="num" style={{ minWidth: 150 }}>Menge</th> <th className="num" style={{ minWidth: 150 }}>Menge</th>
<th style={{ minWidth: 160 }}>MHD</th> <th style={{ minWidth: 160 }}>MHD</th>
<th style={{ minWidth: 150 }}>Lagerort</th>
<th>Eingelagert</th> <th>Eingelagert</th>
<th></th> <th></th>
</tr> </tr>
@@ -1219,6 +1224,21 @@ function LotsCard({ product, lots, baseShort, warnDays, isAdmin, onChanged, onEr
</> </>
)} )}
</td> </td>
<td data-label="Lagerort">
{editing ? (
<select value={draft.location_id} style={{ marginTop: 0, minWidth: 140 }}
onChange={(e) => setDraft({ ...draft, location_id: e.target.value })}>
<option value=""> ohne </option>
{locationOptions(locations).map((o) => (
<option key={o.id} value={o.id}>{o.label}</option>
))}
</select>
) : (
l.location_id
? (locationPathById(l.location_id, locations) || "?")
: <span className="muted"></span>
)}
</td>
<td data-label="Eingelagert" className="muted">{new Date(l.created_at).toLocaleDateString("de-DE")}</td> <td data-label="Eingelagert" className="muted">{new Date(l.created_at).toLocaleDateString("de-DE")}</td>
<td className="num"> <td className="num">
{isAdmin && (editing ? ( {isAdmin && (editing ? (
@@ -1240,7 +1260,7 @@ function LotsCard({ product, lots, baseShort, warnDays, isAdmin, onChanged, onEr
</tr> </tr>
); );
})} })}
{lots.length === 0 && <tr><td colSpan={4} className="empty">Keine Chargen im Bestand.</td></tr>} {lots.length === 0 && <tr><td colSpan={5} className="empty">Keine Chargen im Bestand.</td></tr>}
</tbody> </tbody>
</table> </table>
</div> </div>