# 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/`) 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/`). - **iOS-App:** Verleih-Abschnitt im Einzelstück mit Herausgeben und Zurücknehmen, Verleih-Verlauf, Zustand in der Einzelstück-Liste und ein Filter *Verliehen / Überfällig / Im Haus*. Weil alle Scan-Wege (QR über *Nachschlagen*, NFC-Tag, `vorrania://i/`) in derselben Ansicht enden, ist „scannen und verleihen" damit überall möglich, ohne einen eigenen Weg dafür. - Noch offen: 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. Beachten: iOS erlaubt 60 geplante Meldungen je App, die sich Verleih und MHD dann teilen müssen. ## 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.