iOS-App angefangen; Import-Modi; MHD-Formatierung; Gebinde in Ablauf-Ansichten
iOS (neu, ios/):
- SwiftUI-App: Login (Server + Keychain-Token), Kamera-Scanner (EAN/UPC/Code128),
Einlagern mit mehreren Chargen und eigenem MHD, Auslagern mit Chargenauswahl
oder FEFO, Artikelsuche, Anlegen mit Open-Food-Facts-Vorbefuellung.
- Home-Screen-Shortcuts: Schnellaktionen (langer Druck) und URL-Schema
projectgood://checkin | ://checkout fuer eigene Symbole via Kurzbefehle.
- project.yml (XcodeGen) + README mit Build-Anleitung. NICHT kompiliert - auf
diesem Rechner ist kein Xcode vorhanden.
Import-Modi (wie besprochen sinnvoll):
- add (Standard, nichts loeschen), replace_listed (Bestaende der in der Datei
genannten Produkte ersetzen - fuer Inventur), replace_all (alles ersetzen).
Geleerte Bestaende werden als Korrektur-Bewegung protokolliert; die Oberflaeche
fragt bei den zerstoerenden Modi nach.
Anzeige:
- "Bald ablaufend"/"Abgelaufen" zeigen jetzt das Gebinde ("1 Glas") mit der
Basiseinheit klein darunter (ExpiringItem liefert die Einheiten mit).
- MHD als Zeitspanne mit korrektem Numerus: "in 3 Tagen", "in 1 Woche",
"vor 2 Wochen", dazu heute/morgen/gestern.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -5,10 +5,19 @@ import Icon from "../components/Icon";
|
||||
export default function Transfer() {
|
||||
const fileRef = useRef(null);
|
||||
const [file, setFile] = useState(null);
|
||||
const [mode, setMode] = useState("add");
|
||||
const [result, setResult] = useState(null);
|
||||
const [error, setError] = useState(null);
|
||||
const [busy, setBusy] = useState(false);
|
||||
|
||||
const MODES = {
|
||||
add: "Nur hinzufügen – es wird nichts gelöscht.",
|
||||
replace_listed:
|
||||
"Bestand der Produkte ersetzen, die in der Datei vorkommen. Ideal für eine Inventur: exportieren, korrigieren, zurückspielen.",
|
||||
replace_all:
|
||||
"Alle Bestände vorher leeren und komplett neu aufbauen. Für die vollständige Wiederherstellung aus einem Backup.",
|
||||
};
|
||||
|
||||
async function download(kind) {
|
||||
setError(null);
|
||||
try {
|
||||
@@ -22,11 +31,18 @@ export default function Transfer() {
|
||||
async function doImport(e) {
|
||||
e.preventDefault();
|
||||
if (!file) return;
|
||||
if (mode !== "add") {
|
||||
const warning =
|
||||
mode === "replace_all"
|
||||
? "ALLE Bestände werden vorher gelöscht und aus der Datei neu aufgebaut. Fortfahren?"
|
||||
: "Für alle Produkte in der Datei werden die vorhandenen Chargen gelöscht und ersetzt. Fortfahren?";
|
||||
if (!confirm(warning)) return;
|
||||
}
|
||||
setError(null);
|
||||
setResult(null);
|
||||
setBusy(true);
|
||||
try {
|
||||
setResult(await api.importStock(file));
|
||||
setResult(await api.importStock(file, mode));
|
||||
setFile(null);
|
||||
if (fileRef.current) fileRef.current.value = "";
|
||||
} catch (err) {
|
||||
@@ -68,9 +84,8 @@ export default function Transfer() {
|
||||
<section className="card">
|
||||
<div className="card-head"><Icon name="checkin" /><h2>Import</h2></div>
|
||||
<p className="muted small mt-0">
|
||||
CSV oder JSON auswählen. Der Import <strong>ergänzt nur</strong>: unbekannte Produkte,
|
||||
Gruppen und Lagerorte werden angelegt, Chargen hinzugefügt. Es wird nichts gelöscht
|
||||
oder überschrieben.
|
||||
CSV oder JSON auswählen. Unbekannte Produkte, Gruppen und Lagerorte werden immer
|
||||
angelegt – was mit vorhandenen Beständen passiert, bestimmt der Modus.
|
||||
</p>
|
||||
<form onSubmit={doImport}>
|
||||
<label>
|
||||
@@ -78,6 +93,17 @@ export default function Transfer() {
|
||||
<input ref={fileRef} type="file" accept=".csv,.json,text/csv,application/json"
|
||||
onChange={(e) => setFile(e.target.files?.[0] || null)} />
|
||||
</label>
|
||||
<label>
|
||||
Modus
|
||||
<select value={mode} onChange={(e) => setMode(e.target.value)}>
|
||||
<option value="add">Nur hinzufügen</option>
|
||||
<option value="replace_listed">Bestände der enthaltenen Produkte ersetzen</option>
|
||||
<option value="replace_all">Alle Bestände ersetzen</option>
|
||||
</select>
|
||||
</label>
|
||||
<p className={`muted small mt-0 ${mode === "replace_all" ? "hint-danger" : ""}`}>
|
||||
{MODES[mode]}
|
||||
</p>
|
||||
<button className="btn primary" disabled={!file || busy}>
|
||||
{busy ? "Importiere…" : "Importieren"}
|
||||
</button>
|
||||
@@ -89,6 +115,7 @@ export default function Transfer() {
|
||||
<span>
|
||||
Import abgeschlossen ({result.format?.toUpperCase()}):{" "}
|
||||
{result.products_created} Produkt(e) angelegt, {result.lots_added} Charge(n) ergänzt
|
||||
{result.products_cleared ? `, ${result.products_cleared} Produkt(e) vorher geleert` : ""}
|
||||
{result.units_created ? `, ${result.units_created} Einheit(en) angelegt` : ""}.
|
||||
</span>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user