Verleihen und Zuruecknehmen in der Einzelstueck-Tabelle eines Artikels und in der Liste aller Einzelstuecke, dort als Spalten "Verliehen an" und "Zurueck bis" (filter- und sortierbar). Ueberfaellig ist rot, demnaechst faellig gelb - dieselben Zustandsfarben wie beim MHD. Die Scan-Zielseite /i/<uid> leitete vorher nur zum Artikel weiter. Fuer "scannen und verleihen" braucht die Aktion aber genau dort ihren Platz: Die Seite zeigt jetzt, was man in der Hand haelt - Stueck, Artikel, Lagerort, Verleihzustand - und bietet Verleihen bzw. Zuruecknehmen an. Gegenstueck zur Lagerort-Seite /l/<code>, die es fuers Einlagern genauso macht. Anzeige und Aktionen liegen einmal in components/Verleih.jsx, weil alle drei Stellen dasselbe brauchen. Der Knopf haengt nicht an isAdmin: Verleihen ist eine Buchung wie Ein- und Auslagern. Dazu zwei Dashboard-Karten: alles Verliehene und, getrennt, nur das Ueberfaellige. Stuecke ohne Rueckgabedatum sind nie faellig und tauchen in der zweiten Karte nicht auf. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
138 lines
8.9 KiB
Markdown
138 lines
8.9 KiB
Markdown
# Vorrania – Roadmap
|
||
|
||
Selfhostbare Lebensmittel-Lagerverwaltung. Aufbau in mehreren Schritten.
|
||
|
||
## Schritt 1 – Fundament ✅
|
||
- Backend (FastAPI + PostgreSQL): Datenmodell, Auth mit Rollen (Admin/Benutzer),
|
||
Chargen mit MHD, FEFO-Auslagern, Open-Food-Facts-Lookup (v0-API) mit lokalem Fallback.
|
||
- Web-UI (React/Vite): Login, Dashboard, Ein-/Auslagern, Produkte, Lagerorte,
|
||
Benutzerverwaltung, Einkaufsliste.
|
||
- Deployment: Docker Compose + `install.sh` (Docker-/curl-Autoinstall, Secrets,
|
||
erster Admin, Health-Check), README.
|
||
- Tests: pytest für FEFO, Einheiten-Umrechnung und OFF-Mengenerkennung.
|
||
|
||
## Schritt 1b – Ausbau Web-UI ✅
|
||
- **Design:** professionelles Layout mit Seitenleiste, eigenes SVG-Icon-Set
|
||
(bewusst keine Emojis), Light-/Dark-Mode, konsistente Farbcodierung
|
||
(gelb = Warnfrist, rot = abgelaufen).
|
||
- **Gruppen:** Verwaltung, Zuordnung im Produkt, Gruppen-Mindestbestand **mit Einheit**,
|
||
Auto-Zuordnungsvorschlag aus der OFF-Kategorie.
|
||
- **Einheiten (verwaltbar):** eigene Tabelle mit Art (Anzahl/Gewicht/Volumen) und
|
||
Umrechnungsfaktor. Eingebaut: Stück, Gramm, Kilogramm, Milliliter, Liter.
|
||
Admins können weitere anlegen (z.B. Pfund = 500 g). Gilt für Produkte **und**
|
||
Gruppen-Mindestbestände; Bestände werden intern in der kanonischen Basiseinheit
|
||
(Stück/Gramm/Milliliter) gespeichert.
|
||
- **Einlagern:** Drei-Wege-Barcode-Erkennung (bekannt / bei OFF gefunden → inline
|
||
anlegen / unbekannt), mehrere Chargen je Vorgang mit eigenem MHD,
|
||
Warnung bei bereits abgelaufenem MHD.
|
||
- **Auslagern:** automatisch per FEFO **oder** gezielte Auswahl einer Charge/MHD,
|
||
auch Teilmengen.
|
||
- **Chargen bearbeiten:** Menge/MHD korrigieren und Chargen löschen (mit
|
||
Protokollierung als Korrektur-Bewegung).
|
||
- **Lagerorte:** beliebig tief verschachtelbar (Schrank → Fach → Kiste → …).
|
||
- **Mindestbestände:** je Produkt wahlweise in der Produkteinheit oder in Packungen.
|
||
- **Verlauf:** Bewegungsprotokoll (wer/was/wann), **Einstellungen:** Ablauf-Warnfrist.
|
||
- **Import / Export:** CSV (eine Zeile je Charge, in Excel bearbeitbar) und JSON
|
||
(vollständiges Backup inkl. Einheiten, Gruppen, Lagerorte). Der Import ist rein
|
||
additiv – unbekannte Produkte/Gruppen/Lagerorte werden angelegt, Chargen ergänzt,
|
||
nichts gelöscht oder überschrieben.
|
||
- **Migration:** schonendes Nachziehen neuer Spalten beim Start
|
||
(`ADD COLUMN IF NOT EXISTS`), damit bestehende Installationen ihre Daten behalten.
|
||
|
||
## Schritt 2 – Native iOS-App (SwiftUI) ✅
|
||
- Login (Server-URL + Passwort, Token im Keychain), „angemeldet bleiben“ abwählbar.
|
||
- Barcode-Scan (AVFoundation) mit Nahbereich-Autofokus, Licht und Tippen zum Scharfstellen.
|
||
- Einlagern-Flow: scannen → Lookup/Vorbefüllung → Menge + Einheit + MHD (mehrere Chargen).
|
||
- **MHD per Texterkennung** ablesbar (VisionKit); erkannte Daten werden vorgeschlagen,
|
||
nie ungefragt übernommen.
|
||
- Auslagern-Flow: scannen → Menge/Einheit, FEFO oder Chargenauswahl.
|
||
- Einkaufsliste, „bald ablaufend“ und Produktliste mit Bearbeiten.
|
||
- Home-Screen-Shortcuts und URL-Schema `vorrania://checkin` / `checkout`.
|
||
- Anzeige folgt dem Datumsformat aus den Servereinstellungen.
|
||
|
||
## Schritt 2b – MHD-Genauigkeit und Benennung ✅
|
||
- **MHD wahlweise nur mit Monat und Jahr.** Gespeichert wird weiterhin ein echtes
|
||
Datum (Monatsletzter), damit FEFO unverändert arbeitet; die Genauigkeit steht
|
||
getrennt an Charge und Produkt. Export/Import bleiben verlustfrei.
|
||
- **Kategorien als eigene Ebene neben den Gruppen.** Zuvor wurden beide Begriffe
|
||
vermischt – aus der Open-Food-Facts-Kategorie wurde sogar eine *Gruppe* geraten.
|
||
Jetzt getrennt: Die **Gruppe** zählt Bestände mehrerer Marken zusammen
|
||
(Mindestbestand, EAN-Codes), die **Kategorie** ordnet nur die Artikelliste und
|
||
ist verschachtelbar (Süßwaren → Schokolade). Mitgelieferte Startliste,
|
||
Vorschlag aus der OFF-Einordnung, Filter in App und Web.
|
||
- **EAN-Codes an der Gruppe werden automatisch gepflegt:** Beim Zuordnen eines
|
||
Artikels landet sein Code dort, beim Gruppenwechsel wandert er mit, beim
|
||
Löschen verschwindet er. Vorher blieb die Codeliste einer Gruppe leer, obwohl
|
||
Artikel darin lagen.
|
||
- **Eigenes Logo und Favicon** je Installation (Einstellungen → Darstellung).
|
||
|
||
## Schritt 2c – Modulare Startseite mit Auswertungen ✅
|
||
- **Raster statt fester Seite:** Die Übersicht besteht aus Karten, die sich ziehen,
|
||
in der Größe ändern und einrasten lassen (react-grid-layout, 12 Spalten).
|
||
„Anordnen" schaltet das frei, gespeichert wird ausdrücklich.
|
||
- **Pro Benutzer eigene Anordnung** (`dashboard_layouts`, `user_id = NULL` ist die
|
||
Admin-Vorgabe). Admins speichern eine Vorgabe für alle und können sie über
|
||
`dashboard_enforced` auch **verbindlich** setzen; Benutzer setzen jederzeit
|
||
auf die Vorgabe zurück.
|
||
- **15 Karten** im Verzeichnis `web/src/dashboard/cards.jsx`: Schnellzugriff,
|
||
Status, zwei Kennzahlen, Bald ablaufend, Abgelaufen, Einkaufsliste, letzte
|
||
Bewegungen sowie sechs Auswertungen. Eine neue Karte ist ein Eintrag mehr.
|
||
- **Diagramme als eigene SVG-Komponenten** (kein Diagramm-Paket): Ring, gestapelte
|
||
Balken, Linie mit Fadenkreuz. Zustandsfarben (ok/bald/abgelaufen) sind
|
||
Statusangaben aus den Tokens und stehen nie ohne Beschriftung; Kategorien nutzen
|
||
eine geprüfte Farbreihe in fester Ordnung, ab sieben Kategorien wird zu „Andere"
|
||
gebündelt. Hell und Dunkel haben eigene Stufen.
|
||
- **Alle Mengen in Artikeleinheiten** (Gläser, Packungen, Stück). Gramm und Stück
|
||
lassen sich nicht addieren – Gebinde schon. Der Helfer dafür liegt jetzt einmal
|
||
zentral in `backend/app/services/conversion.py::article_unit`.
|
||
- **Verlauf** wird aus den Bewegungen rückwärts vom heutigen Bestand rekonstruiert.
|
||
*Bekannte Ungenauigkeit:* Die Umrechnung nutzt die heutige Packungsgröße; wird
|
||
sie später geändert, verschiebt sich auch die Historie.
|
||
|
||
## Schritt 2e – Verleih von Einzelstücken (Backend ✅, Oberfläche offen)
|
||
- **Eigene Tabelle `item_loans` statt eines Lagerort-Werts „verliehen".** Der
|
||
Lagerort am Stück bleibt stehen und sagt weiter, wohin es nach der Rückgabe
|
||
zurückgehört; ein Lagerort-Wert hätte diese Information vernichtet.
|
||
Je Vorgang: Entleiher (Freitext), Grund (Freitext, optional), Verleihdatum,
|
||
erwartetes Rückgabedatum (**optional** – „kurz mal mitgenommen" soll nicht an
|
||
einem Datumszwang scheitern) und das Konto, das herausgegeben hat.
|
||
- **Zurückgegebene Verleihe bleiben stehen** (`returned_on` gesetzt) – damit ist
|
||
die Tabelle gleichzeitig die Verleih-Historie je Stück. Ein Teilindex auf die
|
||
offenen Vorgänge sichert ab, dass ein Stück nicht zweimal gleichzeitig
|
||
verliehen ist.
|
||
- **Verliehen heißt nicht im Bestand.** Ein verliehenes Stück zählt nirgends
|
||
mehr mit – weder am Lagerort noch im Gesamtbestand. Entschieden wird das an
|
||
einer Stelle (`services/loans.py::nicht_verliehen`), die alle fünf Zählungen
|
||
benutzen. „Was liegt hier?" behauptet damit nicht länger, das Stück liege im
|
||
Regal. *Folge:* Fällt es unter den Mindestbestand, erscheint der Artikel auf
|
||
der Einkaufsliste – gewollt, aber gut zu wissen.
|
||
- **Verleihen darf jeder** angemeldete Benutzer (es ist eine Buchung wie Ein- und
|
||
Auslagern, keine Stammdatenänderung) und wird mit dem Konto protokolliert. Nur
|
||
das *Löschen* eines Vorgangs bleibt Admins vorbehalten, weil es Historie
|
||
vernichtet.
|
||
- **Bewusst keine `Movement`-Bewegung je Verleih:** Der Verlauf rechnet den
|
||
Bestand rückwärts aus vorzeichenbehafteten Bewegungen – ein Abgang ohne
|
||
Gegenbuchung würde diese Kurve verfälschen.
|
||
- **Web-Oberfläche:** Verleihen und Zurücknehmen in der Einzelstück-Tabelle
|
||
eines Artikels und in der Liste aller Einzelstücke (Spalten „Verliehen an" und
|
||
„Zurück bis", filter- und sortierbar), zwei Dashboard-Karten („Verliehen",
|
||
„Überfällige Verleihe") und der Verleih-Verlauf je Stück. Überfällig ist rot,
|
||
demnächst fällig gelb – dieselben Zustandsfarben wie beim MHD.
|
||
- **Nach dem Scannen direkt verleihen:** Das Ziel eines Einzelstück-QR bzw.
|
||
NFC-Tags (`/i/<uid>`) leitete vorher nur zum Artikel weiter. Jetzt zeigt es,
|
||
was man in der Hand hält, und bietet Verleihen bzw. Zurücknehmen an der Stelle
|
||
an – Gegenstück zur Lagerort-Seite (`/l/<code>`).
|
||
- Noch offen: Oberfläche in der iOS-App sowie die **Rückgabe-Erinnerung**. Sie
|
||
kann nur den Weg der Ablauf-Meldungen gehen (lokale Benachrichtigung der App
|
||
aus einem Server-Snapshot, `GET /loans?due_in=…` ist dafür schon die
|
||
Datenquelle) – echtes Push fehlt weiterhin, siehe Schritt 3.
|
||
|
||
## Schritt 3 – Weitere Ideen – offen
|
||
- **Ablauf-Push-Benachrichtigungen** via APNs (setzt die App voraus).
|
||
- **Einkaufsliste verfeinern:** Abhaken dauerhaft speichern, manuelle Einträge,
|
||
Mengenvorschläge in Packungen.
|
||
- **Statistik/Auswertung** über den Bewegungsverlauf (Verbrauch pro Zeitraum).
|
||
- **Lagerort beim Auslagern** berücksichtigen bzw. pro Charge wählbar beim Einlagern.
|
||
- **Produktbilder lokal cachen** (aktuell direkt von Open Food Facts geladen).
|
||
- **Alembic-Migrationen** statt `create_all` + Startup-ALTER, sobald das Schema stabil ist.
|