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:
Scarriffle
2026-07-22 14:59:04 +02:00
parent de6c8632a1
commit 4bb9a10366
23 changed files with 1770 additions and 41 deletions

View File

@@ -1,26 +1,10 @@
import { useEffect, useState } from "react";
import { api } from "../api";
import Icon from "../components/Icon";
import { fmt, unitShort } from "../units";
import { articlePrimary, articleSecondary } from "../units";
const TYPE_LABEL = { in: "Eingelagert", out: "Ausgelagert", adjust: "Korrektur" };
// Leitangabe in Artikeleinheiten (Gebinde), sonst in der Produkteinheit.
function primaryAmount(m) {
if (m.package_size > 0) {
return `${fmt(m.quantity / m.package_size)} ${m.package_label || "Packung"}`;
}
return `${fmt(m.quantity / (m.unit_factor || 1))} ${m.unit_name || unitShort(m.base_unit)}`;
}
// Untermenge in der Basiseinheit nur wenn sie sich von der Leitangabe unterscheidet.
function secondaryAmount(m) {
if (m.package_size > 0 || (m.unit_factor || 1) !== 1) {
return `${fmt(m.quantity)} ${unitShort(m.base_unit)}`;
}
return "";
}
export default function History() {
const [movements, setMovements] = useState([]);
const [error, setError] = useState(null);
@@ -63,9 +47,9 @@ export default function History() {
</td>
<td>{m.product_name}</td>
<td className="num">
{primaryAmount(m)}
{secondaryAmount(m) && (
<div className="muted small">{secondaryAmount(m)}</div>
{articlePrimary(m)}
{articleSecondary(m) && (
<div className="muted small">{articleSecondary(m)}</div>
)}
</td>
<td className="muted">{m.username || ""}</td>