Der vorige Anlauf war wirkungslos. _import_json baut aus jedem JSON-Eintrag eine
eigene Zeile fuer _get_or_create_product - und image_url stand in dieser Zeile
nicht drin. Die Abfrage lief also immer ins Leere.
Zwei Tests gehen jetzt durch _import_json statt nur durch
_get_or_create_product, weil die Adresse genau zwischen den beiden herausfiel.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Das alte Bild in der Chargen-Karte entfaellt - seit dem neuen Bild neben den
Kennfeldern stand es doppelt auf der Seite.
Der Rahmen ist jetzt hochkant (3:4) statt quadratisch. Lebensmittelverpackungen
sind fast immer hoeher als breit; im Quadrat blieben links und rechts leere
Streifen.
Beim Import wurde image_url komplett verworfen, obwohl das JSON-Backup sie
enthaelt - eingelesene Artikel blieben deshalb dauerhaft ohne Bild. Die Adresse
wird jetzt uebernommen (bei bestehenden Artikeln nur ergaenzend, nie
ueberschreibend); die lokale Kopie holt der Bildabruf beim ersten Ansehen.
Bewusst nicht waehrend des Imports: Ein Backup mit 200 Artikeln wuerde sonst
200 Downloads in einer Anfrage abarbeiten.
Ausserdem holen jetzt auch Artikelliste, Ein- und Auslagern ihr Vorschaubild
aus der eigenen Datenbank statt direkt von Open Food Facts. Einzige Ausnahme
bleibt der OFF-Vorschlag beim Scannen: Dort existiert der Artikel noch nicht.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Open Food Facts liefert nur eine Bildadresse. Direkt dorthin zu verlinken hiesse:
Das Bild verschwindet, wenn OFF es austauscht, die Installation braucht Internet,
und jeder Seitenaufruf verraet OFF, welche Artikel jemand ansieht.
Das Bild wird daher einmal geholt und in einer eigenen Tabelle product_images
abgelegt - eigene Tabelle, damit Artikellisten die Blobs nicht mitziehen.
Geholt wird beim Anlegen, bei geaenderter Bildadresse und beim ersten Abruf
(so bekommen auch bestehende Artikel ihre Kopie, ohne Wanderung ueber alle
Datensaetze).
GET /products/{id}/image verlangt eine Anmeldung - aus den Bildern liesse sich
sonst ohne Konto ablesen, was im Vorrat liegt. Da ein <img src> keinen
Authorization-Header schickt, laedt die Oberflaeche das Bild ueber fetch und
zeigt es als Objekt-URL.
Angezeigt wird es rechts neben Barcode, Name und Marke. Fehlt ein Bild, rendert
die Komponente nichts und die Felder nehmen die volle Breite ein.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Auf ausdruecklichen Wunsch jetzt auch die internen Bezeichner, weil die
Installation noch keine echten Daten enthaelt: POSTGRES_USER/PASSWORD/DB, der
Volume-Name, die Standard-DATABASE_URL, der localStorage-Schluessel und die
iOS-Zeichenketten inklusive Keychain-Konto.
Das setzt eine leere Datenbank voraus: Der neue Volume-Name legt eine neue,
leere Datenbank an. Die alte bleibt als verwaistes Volume liegen und muss von
Hand entfernt werden.
Die Warnung in docker-compose.yml bleibt stehen, damit eine spaetere
Umbenennung nicht versehentlich mit Daten im Volume passiert.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sichtbarer Name ueberall: Kopfzeichen der Oberflaeche, Seitentitel, Favicon,
API-Titel, User-Agent zu Open Food Facts, Dateinamen der Sicherungen, README,
Roadmap, Installer- und Update-Skript, npm-Paketname.
Bewusst NICHT umbenannt, jeweils mit Begruendung im Code:
- POSTGRES_USER/DB und der Volume-Name bleiben "project_good". Ein neuer
Volume-Name legt eine leere Datenbank an, ein neuer Benutzername scheitert am
bestehenden Volume - vorhandene Bestaende waeren verloren.
- Der localStorage-Schluessel bleibt, sonst waeren alle Browser einmal
ausgesperrt.
- ios/ bleibt unangetastet (wird getrennt bearbeitet).
Die Klon-Adresse im README zeigt jetzt auf .../vorrania.git; das Repo in Gitea
muss dafuer umbenannt werden.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bisher waren es zwei Linien mit der Anzahl der Vorgaenge. Gemeint war eine
Bruecke: die Bestandslinie, und schwebende gruene/rote Balken, die jeden ihrer
Spruenge erklaeren.
Damit Balken und Linie ueberhaupt zusammen in ein Bild duerfen, zaehlen die
Balken jetzt Mengen in Artikeleinheiten statt Vorgaengen - beides liegt so auf
derselben Achse. Zwei Skalen in einem Diagramm bleiben ausgeschlossen.
Neuer Endpunkt GET /dashboard/flow liefert je Abschnitt Anfangsbestand, Zugang
und Abgang aus einer Abfrage. Getrennt abgefragt koennten die Abschnitte von
timeline und activity um eine Schrittweite auseinanderliegen - dann stuende ein
Balken neben dem Sprung, den er erklaert.
/dashboard/activity bleibt fuer externe Zugriffe erhalten.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Eine Karte mit dieser Ueberschrift, in der bereits Ueberfaelliges steht,
ist irrefuehrend. KarteAblauf bekommt daher einen Modus soon/expired/alle
statt des bisherigen Schalters "nurAbgelaufen".
Neu ist die Karte "Ablauf-Uebersicht" (alle), die beides zusammen zeigt.
Sie steht auch in der eingebauten Vorgabe, damit dort ohne Zutun weiterhin
Ueberfaelliges sichtbar ist.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Zeitraum-Auswahl:
Sie stand ueber dem Diagramm und nahm ihm Hoehe weg. Jetzt sitzt sie klein im
Kartenkopf neben dem Titel - die ganze Kartenhoehe gehoert dem Diagramm. Die
Wahl wird in der Anordnung mitgespeichert, bleibt also nach dem Neuladen
erhalten; ausserhalb des Bearbeitungsmodus geschieht das still im Hintergrund.
Feinere Aufloesung:
Ein Punkt je Tag verbarg, WANN ein- und ausgelagert wurde. Die Abtastrate
richtet sich jetzt nach dem Zeitraum:
bis 2 Tage -> stuendlich
bis 14 Tage -> alle 6 Stunden
bis 120 Tage -> taeglich
darueber -> woechentlich
So bleibt die Punktzahl immer zwischen etwa 25 und 105 - fein genug zum
Erkennen, grob genug zum Zeichnen. Ein Test haelt diese Spanne fest.
Die Zeitachse traegt entsprechend Uhrzeit statt Datum, wenn stuendlich
abgetastet wird. Punkte heissen jetzt "at" (Zeitpunkt) statt "date".
Zeitstempel aus SQLite kommen ohne Zeitzone zurueck und werden vereinheitlicht,
sonst schluege die Differenzbildung fehl.
Auswahl erweitert: 24 Stunden, 2 Tage, 7, 30, 90 Tage, 6 Monate, 1 Jahr.
Geprueft: "npm run build" laeuft durch. pytest weiterhin nicht ausfuehrbar -
kein Python auf diesem Rechner; die Tests wurden an die neue Aufloesung
angepasst.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Meldungen:
"Gespeichert", "Anordnung gespeichert" und die uebrigen Erfolgsmeldungen standen
als Leiste mitten in der Seite - beim Speichern sprang dadurch das ganze Layout
ein Stueck nach unten. Sie erscheinen jetzt als Overlay am unteren Rand und
verschwinden nach fuenf Sekunden von selbst; ein Kreuz blendet sie sofort aus.
Umgestellt in Uebersicht, Einstellungen, Gefahrenbereich, Benutzer,
Produktformular sowie Ein- und Auslagern.
Bewusst NICHT umgestellt: Fehlermeldungen. Ein Fehler, der nach fuenf Sekunden
verschwindet, ist genau dann weg, wenn man ihn noch braucht - der bleibt an
seiner Stelle stehen.
Rollbalken beim Liniendiagramm:
Den hatte ich mir mit der Zeitraum-Auswahl selbst eingebaut. Das Diagramm hatte
eine feste Hoehe von 200 px; zusammen mit der Auswahl darueber passte das nicht
mehr in die Karte. Jetzt wird auch die Hoehe gemessen, das Diagramm fuellt den
verbleibenden Platz.
Zeitraum "1 Tag":
Ein einzelner Tag ergibt genau einen Messpunkt - eine Linie aus einem Punkt
waere unsinnig. Das Diagramm zeigt in dem Fall die blanke Zahl. Die
Backend-Begrenzung laesst jetzt einen Tag zu.
Geprueft: "npm run build" laeuft durch; kein Verweis mehr auf die entfernten
Meldungszustaende.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Zu kleine Karten (Konstruktionsfehler):
Bei Schnellzugriff, Status und den Kennzahlen war die Mindesthoehe gleich der
Standardhoehe - und beide zu klein. Bei 96 px Zeilenhoehe blieben nach Kartenkopf
und Innenabstand nur 23 px fuer den Inhalt, eine Kennzahl braucht allein rund
45 px. Deshalb erschien schon in der Grundeinstellung ein Rollbalken, und
kleiner ging es nicht.
- Zeilenhoehe von 96 auf 40 px: die Hoehe laesst sich jetzt fein abstufen.
- Jede Karte hat eine Mindesthoehe, in die ihr Inhalt nachweislich passt
(Rechnung steht als Kommentar am Verzeichnis).
- Gespeicherte Anordnungen aus der alten Fassung werden beim Laden auf die
Mindestgroesse angehoben, statt zu flach zu bleiben.
Vorschau im Menue "Karte hinzufuegen":
Der Name allein verriet nicht, was eine Karte zeigt. Jede Karte hat jetzt eine
kleine Skizze (Tabelle, Ring, Balken, Linie, Kennzahl) und eine Beschreibung.
Bewusst gezeichnete Skizzen statt echter Vorschau - sonst muesste das Menue fuer
jede Karte Daten laden.
Zeitraum bei den Verlaufskarten:
Bestandsverlauf, Artikelverlauf und Ein-/Auslagerungen haben eine Auswahl
7 Tage bis 1 Jahr.
Ringdiagramme:
Der Ring klebte oben in der Karte, darunter blieb viel Leerraum. Er fuellt jetzt
die Karte und sitzt mittig.
Geprueft: "npm run build" laeuft durch.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
AssertionError beim Import: "Status code 204 must not have a response body".
Ursache ist eine Wechselwirkung, die nur in dieser Datei auftritt: dashboard.py
nutzt "from __future__ import annotations". Dadurch ist die Rueckgabeangabe
"-> None" eine Zeichenkette, die FastAPI zu NoneType aufloest und als
Antwortmodell wertet. Zusammen mit status_code=204 - das keinen Rumpf haben darf -
schlaegt die Pruefung beim Registrieren der Route fehl und das Backend startet
gar nicht. Andere Router im Projekt haben dieses __future__-Import nicht, deshalb
funktioniert dasselbe Muster dort.
Die Route folgt jetzt dem bereits bewaehrten Muster aus branding.py: Der
Statuscode steht am Response, nicht im Dekorator. Der Grund steht als Kommentar
an der Funktion, damit die Falle nicht erneut zuschnappt.
Geprueft: Alle uebrigen Routen der Datei haben ein explizites response_model im
Dekorator und sind davon nicht betroffen. Ein Start des Backends konnte ich hier
nicht pruefen - auf diesem Rechner ist weder Python noch Docker vorhanden.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Die Uebersicht war fest verdrahtet und fuer alle gleich. Jetzt besteht sie aus
Karten in einem 12-Spalten-Raster, die sich ziehen, in der Groesse aendern und
einrasten lassen (react-grid-layout). "Anordnen" schaltet das frei, gespeichert
wird ausdruecklich - Probieren bleibt folgenlos.
Anordnung:
- Neue Tabelle dashboard_layouts; user_id = NULL ist die Admin-Vorgabe.
- Jeder Benutzer hat seine eigene Anordnung. Admins speichern eine Vorgabe fuer
alle und koennen sie ueber die Einstellung dashboard_enforced verbindlich
machen; dann lehnt das Backend das Speichern eigener Anordnungen ab.
- "Auf Vorgabe zuruecksetzen" verwirft die persoenliche Anordnung.
15 Karten (Verzeichnis web/src/dashboard/cards.jsx, neue Karte = ein Eintrag):
Schnellzugriff, Status, Artikelzahl, Artikeleinheiten, Bald ablaufend,
Abgelaufen, Einkaufsliste, letzte Bewegungen sowie sechs Auswertungen
(Ablauf-Ring, Kategorien-Ring, Kategorien nach Zustand, Bestandsverlauf,
Verlauf eines Artikels, Ein-/Auslagerungen).
Diagramme als eigene SVG-Komponenten statt Diagramm-Paket:
- Zustandsfarben (ok/bald/abgelaufen) sind Statusangaben aus den Tokens und
stehen nie ohne Beschriftung; Kategorien nutzen eine gepruefte Farbreihe in
fester Ordnung, ab sieben Kategorien wird zu "Andere" gebuendelt.
- Eine Werteachse, duenne Marken, zurueckhaltendes Gitter, Fadenkreuz mit
Kurzinfo. Hell und Dunkel haben eigene Farbstufen.
Mengen durchgehend in Artikeleinheiten (Glaeser, Packungen, Stueck): Gramm und
Stueck lassen sich nicht addieren, Gebinde schon. Der bisher dreifach
vorhandene Helfer liegt jetzt einmal in services/conversion.py::article_unit.
Der Verlauf wird rueckwaerts vom heutigen Bestand aus den Bewegungen
rekonstruiert. Bekannte Ungenauigkeit, im Code und in der Roadmap vermerkt:
Die Umrechnung nutzt die heutige Packungsgroesse.
Geprueft: "npm run build" laeuft durch. Die neuen pytest-Tests
(backend/tests/test_dashboard.py) konnten hier nicht laufen - auf diesem
Rechner ist kein Python installiert.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fehler: Ein hochgeladenes Logo blieb unsichtbar, auch nach dem Neuladen.
Die Oberflaeche prueft per Anfrage, ob ein Bild hinterlegt ist, und nutzte dafuer
HEAD. FastAPI registriert fuer eine GET-Route aber - anders als Starlette - kein
HEAD; die Anfrage lief in ein 405, und jedes hinterlegte Bild galt als "nicht
vorhanden". Der Upload selbst war die ganze Zeit in Ordnung.
- Neuer Endpunkt GET /branding liefert nur den Status ({"logo": true, ...}),
ohne Bilddaten zu uebertragen. Die Oberflaeche fragt jetzt diesen ab.
- Die Bild-Route beantwortet zusaetzlich HEAD, damit sie sich erwartungsgemaess
verhaelt.
Ausserdem:
- Die empfohlenen Abmessungen stehen jetzt sichtbar ueber der Vorschau
(Logo etwa 400x72 px, Favicon 64x64 px, jeweils hoechstens 512 KB).
- Nach dem Hochladen wird die tatsaechliche Bildgroesse angezeigt, damit man
sie mit der Empfehlung vergleichen kann.
Geprueft: "npm run build" laeuft durch.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Kategorien:
- Oberkategorien lassen sich per Pfeil zuklappen; ihre Unterkategorien
verschwinden dann aus der Liste. Ausgeblendet wird ueber die gesamte
Vorfahrenkette, nicht nur eine Ebene, damit auch mehrstufige Baeume stimmen.
- Ist eine Kategorie zugeklappt, zeigt ein Badge, wie viele Eintraege darunter
liegen - sonst waere nicht erkennbar, dass dort noch etwas ist.
- Zeilen ohne Unterkategorien bekommen einen Platzhalter, damit alle Namen
buendig stehen.
Badges in Zellen:
- In der Karten-Ansicht verteilte das Raster mehrere Elemente einer Zelle auf
zwei Rasterzellen - deshalb rutschte "eingebaut" unter die Beschriftung.
Jetzt Flexfluss, alle Teile stehen hinter der Beschriftung.
- Einheiten-Tabelle: Name und "eingebaut" liegen in einer Zeile zusammen.
Geprueft: "npm run build" laeuft durch.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Uebersicht, Einheiten und Kategorien zeigten am Rechner die Karten-Ansicht
(Beschriftung links, Wert daneben), weil die Umschaltgrenze bei 700px lag und
neben diesen Bloecken noch etwas steht. Drei bis vier Spalten passen dort aber
locker nebeneinander.
- Grenze auf 480px gesenkt: Karten praktisch nur noch am Handy hochkant.
- .table-wrap faengt Zwischengroessen mit waagerechtem Scrollen ab, statt
Inhalte abzuschneiden.
- Tooltip-Beschriftung ohne Fragezeichen-Cursor.
Geprueft: "npm run build" laeuft durch.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Tabellen (Uebersicht, Einheiten, Kategorien):
Wird der Container schmal, stellen sich Tabellen auf Karten um. Dort trennte
"space-between" Beschriftung und Wert an die beiden Zeilenenden - der Wert stand
weit rechts und wirkte durch die unterschiedlichen Zeilenhoehen nicht auf einer
Linie. Jetzt zwei feste Spalten (Beschriftung, Wert direkt daneben) mit
Ausrichtung an der Schriftlinie. Zahlenspalten stehen in der Karte linksbuendig.
Artikelseite:
Die Erklaerungen unter "MHD-Angabe", "Gruppe" und "Kategorie" standen dauerhaft
im Formular und machten es lang. Sie haengen jetzt als Tooltip an der
Beschriftung; eine gepunktete Linie zeigt an, dass dort eine Erklaerung steht.
(Kategorie hatte denselben Text-Block - der Einheitlichkeit halber mit
umgestellt.)
Geprueft: "npm run build" laeuft durch.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
EAN-Codes:
- Das Kennzeichen "Artikel" ist weg; stattdessen stehen Marke und Artikelname
zusammen ("Zweifel - Moutarde Original Chips"). Die Marke allein reichte nicht,
um einen Code wiederzuerkennen. Hinweistext entsprechend angepasst.
Produktseite:
- "Weitere EAN-Codes" steht jetzt unterhalb von Formular und Chargen statt als
dritte Spalte dazwischen. Die Codes braucht man selten und sie schoben die
beiden wichtigen Bloecke auseinander.
Tabellen:
- "overflow-wrap: anywhere" galt fuer ALLE Zellen und zerriss Woerter mitten
drin ("PROD UKTE", "555 Gram m"). Jetzt nur noch gezielt per Klasse.
- Kopfzeilen und Zahl-plus-Einheit brechen nicht mehr um.
- Eingaben in Tabellen sind kompakter (kleinere Polsterung, Zahlenfelder 8ch),
Auswahlfelder richten sich nach ihrem Text statt 132px Mindestbreite -
dadurch sind die Zeilen deutlich schlanker.
- Kennzeichen wie "niedrig" oder "abgelaufen" sitzen ueber .cell-row in einer
Zeile mit dem Wert und rutschen nicht mehr je nach Zeilenhoehe herum.
Geprueft: "npm run build" laeuft durch. Backend unveraendert.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Die Einkaufsliste war ueber /shopping-list und /shopping-list/groups schon
abrufbar, aber nur mit einem Login-JWT, das nach 7 Tagen ablaeuft - fuer HA
unpraktisch.
- Neue Tabelle api_tokens (Name, SHA-256-Hash, Ersteller, zuletzt genutzt).
Der Klartext wird nur einmal beim Anlegen zurueckgegeben.
- get_current_user akzeptiert jetzt JWT ODER API-Token im Authorization-Header;
bei Nutzung wird last_used_at fortgeschrieben.
- Endpunkte /api-tokens (Admin): auflisten, erzeugen, widerrufen.
- Einstellungsseite: Tokens verwalten inkl. einmaliger Anzeige und einem
aufklappbaren Konfigurationsbeispiel fuer Home Assistant.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
MHD wurde in den Ansichten noch roh als YYYY-MM-DD ausgegeben. Jetzt:
- Neue Einstellung date_format (23.12.2026 | 23.12.26 | 23. Dezember 2026 |
2026-12-23 | 12/23/2026) mit Live-Beispiel auf der Einstellungsseite.
- SettingsProvider stellt Format und formatDate() app-weit bereit und laedt die
Einstellungen nach dem Login.
- Verwendet in Uebersicht (bald ablaufend), Produktdetail (Chargen) und
Auslagern (Chargenauswahl). Datumsfelder zur Eingabe bleiben ISO, wie es das
HTML-Datumsfeld verlangt.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
Barcodes:
- Neue Tabelle barcodes (code, note, product_id ODER group_id). Damit lassen sich
einem Produkt mehrere Codes geben und einer Gruppe beliebig viele Marken
zuordnen (z.B. alle Mehlmarken zu "Mehl"). Je Code eine Notiz wie
"Mehl bei Aldi".
- Lookup loest jetzt auch Alias-Codes auf; ist ein Code einer Gruppe zugeordnet,
liefert die API group_id/group_name zurueck. Beim Anlegen aus einem Scan wird
diese Gruppe automatisch gesetzt (Vorrang vor dem Kategorie-Rateversuch).
- Endpunkte: POST/DELETE /products/{id}/barcodes und /groups/{id}/barcodes.
- Web: wiederverwendbare BarcodeList-Komponente, eingebunden im Produktformular
und auf der Gruppen-Seite.
Gruppen:
- Namen lassen sich jetzt direkt in der Tabelle aendern (PATCH war vorhanden,
nur die Oberflaeche fehlte).
UI-Fix: Auswahlfelder in Tabellen richten sich nach ihrem Text statt nach der
Zellenbreite (Gruppen-/Benutzer-Dropdowns waren abgeschnitten).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Export:
- GET /export/stock.csv - eine Zeile je Charge, Semikolon-getrennt mit BOM,
direkt in Excel/LibreOffice bearbeitbar; Mengen in der Artikeleinheit.
- GET /export/backup.json - vollstaendiges Backup (Einheiten, Gruppen,
Lagerorte, Produkte, Chargen), Referenzen ueber Namen statt IDs.
Import:
- POST /import/stock (Admin, Datei-Upload). Erkennt CSV oder JSON automatisch.
Arbeitet ausschliesslich additiv: unbekannte Produkte/Gruppen/Lagerorte/
Einheiten werden angelegt, Chargen ergaenzt - nichts wird geloescht.
Mengen koennen in Gebinde ("Glas") oder Einheiten ("Gramm") angegeben werden,
Datum als YYYY-MM-DD oder TT.MM.JJJJ. Fehlerhafte Zeilen werden uebersprungen
und im Ergebnis gemeldet; jeder Import wird als Bewegung protokolliert.
Frontend: neue Seite "Import / Export" (Verwaltung) mit Download-Buttons,
Datei-Upload, Ergebniszusammenfassung und einer Beschreibung der CSV-Spalten.
api.js kann jetzt Datei-Downloads mit Auth-Header und FormData-Uploads.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
MovementOut liefert jetzt package_size, package_label sowie Name/Faktor der
Anzeigeeinheit mit. Die Verlauf-Tabelle zeigt die Bewegung als Leitangabe in
Gebinden (z.B. "1 Glas") und darunter klein die Basiseinheit ("195 g").
Ohne Gebinde/abweichende Einheit bleibt es bei einer Zeile.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- buildUnitOptions verwendet product.package_label als Beschriftung der
Gebinde-Option (Wert bleibt intern "package").
- Beim Auswaehlen eines Artikels ist das Gebinde vorbelegt, wenn eine
Packungsgroesse hinterlegt ist.
- Auslagern: Chargen-Dropdown und Hinweis zeigen die Menge in der Artikeleinheit
(z.B. "MHD 2026-12-23 - 1 Glas") statt in der Basiseinheit.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Das datalist-Eingabefeld sah anders aus als die uebrigen Auswahlfelder im
Produktformular. Jetzt ein einheitliches <select>: leerer Standardwert
"Packung (Standard)" plus gaengige Bezeichnungen (Glas, Tuete, Flasche, Dose,
Tube, Becher, Beutel, Karton, Riegel, Rolle). Ein bereits gespeicherter
abweichender Wert bleibt als Option erhalten.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Neues Feld products.package_label: pro Artikel einstellbar, wie das Gebinde
heisst (Packung, Glas, Tuete, Flasche, Dose, Tube, ...) - Eingabe mit
Vorschlagsliste, frei ueberschreibbar. Migration per ADD COLUMN IF NOT EXISTS.
- Produkttabelle: Spalte "Packungen" heisst jetzt "Einheiten (ist / soll)" und
zeigt Bestand und Mindestbestand als "11 / 12 Glas"; die separate Spalte
"Mindest" entfaellt. Das "niedrig"-Badge sitzt jetzt dort.
- Die Bezeichnung wird ueberall verwendet (Chargenanzeige, Einheiten-Umschalter
beim Bearbeiten, Mindestbestand-Auswahl, Umrechnungshinweis).
- Speichern/Abbrechen im Chargen-Editor sind jetzt gleich breit (.btn-pair).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Eingaben haben global width:100% und schrumpften im Flex-Layout zusammen
(Mindestbestand-Einheit abgeschnitten, Chargen-Bearbeitung winzig).
- .field-inline: Zahlenfelder wachsen (min 78px), Einheiten-Dropdowns behalten
ihre Textbreite (min 132px).
- In Tabellenzellen brechen die Felder bei wenig Platz um statt zu quetschen.
- Einengende Inline-maxWidth entfernt; Menge-/MHD-Spalte mit Mindestbreite.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Anzeige getauscht - oben gross die Packungszahl, darunter klein die Menge in der
Produkteinheit (z.B. "10 Pkg" / "1.945 Gramm"). Ohne Packungsgroesse bleibt die
Produkteinheit die Leitangabe. Das Bearbeiten startet passend in Packungen; per
Umschalter laesst sich weiterhin in der Produkteinheit eingeben.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Beim Bearbeiten einer Charge steht neben dem Mengenfeld ein Umschalter
(z.B. Gramm <-> Packung(en)); der Wert wird beim Wechsel umgerechnet und beim
Speichern korrekt in die Basiseinheit zurueckgerechnet.
- Umstaendlicher Hinweistext unter der Mindestmenge entfernt.
- Redundanter Fusstext unter den Chargen ("Mengen in ...") entfernt; es bleibt
nur die knappe Umrechnung "1 Packung = 195 g".
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Holt den neuesten Stand aus Git und baut die Container neu. Findet seinen
eigenen Pfad (von ueberall aufrufbar), nutzt die vorhandene deploy/.env,
erkennt docker compose v1/v2 und prueft am Ende /api/health.
Optionen: --prune (alte Images aufraeumen), --no-pull (nur neu bauen).
Bei fehlgeschlagenem git pull gibt es einen Hinweis auf lokale Aenderungen.
Im Repo direkt als ausfuehrbar markiert; README ergaenzt.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Die Produkt-Einheit bietet bewusst alle Einheiten an (sie legt die Art fest),
der Mindestbestand nur die passender Art. Das sah nach einem Fehler aus, war aber
gewollt - jetzt sichtbar gemacht:
- Produkt-Einheit nach Anzahl/Gewicht/Volumen gruppiert (optgroup).
- Mindestbestand mit Hinweistext auf die Art und "Packung(en) a <Groesse>".
- baseShort wird aus der gewaehlten Einheit abgeleitet (funktioniert auch bei
neuen Produkten).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Produktformular: Auswahl aller verwalteten Einheiten passender Art (plus
Packungen), inkl. Umrechnung beim Wechsel.
- Die gewaehlte Erfassungseinheit wird gespeichert (products.min_stock_unit_id /
min_stock_in_packages) und exakt so wieder angezeigt - vorher wurde sie aus der
Produkteinheit abgeleitet.
- ProductOut liefert min_stock_display + min_stock_unit_label; Produkttabelle
zeigt den Mindestbestand damit in der erfassten Einheit mit Einheit dahinter.
- Migration: ADD COLUMN IF NOT EXISTS fuer die zwei neuen Spalten.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Korrektur zum vorigen Commit: Anzeige und Bearbeitung der Chargen richten sich
nach der im Produkt eingestellten Einheit. Als Untermenge stehen darunter die
Basiseinheit (falls abweichend) und die Packungen (falls Packungsgroesse gesetzt).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ist eine Packungsgroesse hinterlegt, sind Anzeige UND Bearbeitung der Chargen in
Packungen; die Basiseinheit (z.B. Gramm) steht nur noch als Untermenge darunter.
Ohne Packungsgroesse bleibt die Produkteinheit massgeblich. Produktkopf nennt
ebenfalls zuerst die Packungen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Chargen werden in der Produkteinheit angezeigt, darunter die Zweitangabe
(Basiseinheit falls abweichend und/oder Packungen).
- Bearbeiten laeuft jetzt ebenfalls in der Produkteinheit (mit Einheit neben dem
Feld) und rechnet beim Speichern in die Basiseinheit um - vorher zeigte die
Bearbeitung einen anderen Wert als die Anzeige.
- Produktkopf zeigt den Bestand zusaetzlich in Packungen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- .field-inline: Labels ohne eigenen margin-bottom, sonst rutschen daneben
stehende Buttons (z.B. "Nachschlagen") optisch nach unten. Abstand traegt jetzt
die Reihe selbst; verschachtelt in Label/Tabellenzelle bleibt er aus.
- Produkttabelle: Mindestbestand wird in der Produkteinheit mit nachgestellter
Einheit angezeigt (zusaetzlich der Packungswert, falls Packungsgroesse gesetzt).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Einheiten (neu):
- Tabelle units (name, kind=count|weight|volume, factor, is_builtin); eingebaut
Stueck/Gramm/Kilogramm/Milliliter/Liter, Admin kann eigene anlegen (z.B. Pfund=500g).
- Bestaende bleiben intern in kanonischer Basis (Stueck/Gramm/Milliliter);
Product.display_unit_id und Group.min_stock_unit_id als nullable FKs.
- Neuer Umrechnungs-Service (services/conversion.py) ersetzt die feste Einheitenlogik;
Ein-/Auslagern und Gruppen-Mindestbestand rechnen ueber den Faktor.
- Gruppen-Mindestbestand mit Einheit; Gruppenbestand summiert nur Produkte
passender Art. Neue Verwaltungsseite "Einheiten" (Admin).
- Schonende Migration beim Start: ADD COLUMN IF NOT EXISTS (Postgres), damit
bestehende Installationen ihre Daten behalten.
Chargen:
- PATCH /lots/{id} und DELETE /lots/{id}: Menge/MHD korrigieren, Charge loeschen
(wird als Korrektur-Bewegung protokolliert). Bearbeitung im Produktdetail.
Auslagern:
- Optionales lot_id: gezielt aus einer bestimmten Charge/MHD abbuchen statt FEFO;
Auswahl-Dropdown in der Auslagern-Seite.
Sonstiges: Roadmap aktualisiert, Tests fuer die Umrechnung.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Problem: min_stock wird in Basiseinheiten verglichen. Bei einem 190g-Glas (base
gram, package 190) bedeutete "min 2" 2 Gramm statt 2 Glaeser -> Produkt tauchte
nie auf der Einkaufsliste auf.
Fix:
- Produktformular: Mindestbestand wahlweise in Basiseinheit ODER Packungen
eingebbar (mit Umrechnung); Default Packungen bei Packungsprodukten.
- ShoppingItem liefert package_size; Einkaufsliste/Dashboard zeigen Bedarf in
Packungen (+ Basiseinheit in Klammern) via amountText().
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Einlagern:
- Barcode-Erkennung dreiteilig: bekannt -> direkt Menge; unbekannt aber in OFF
-> "Anlegen & einlagern" inline; gar nicht gefunden -> manuell anlegen.
- Mehrere Chargen pro Vorgang: je Zeile eigene Menge + eigenes MHD
(z.B. 5 Glaeser mit unterschiedlichen Daten). Neuer Endpoint /stock/checkin/batch.
Produkte/OFF:
- OFF-Fuellmenge (quantity) wird in Basiseinheit + Packungsgroesse geparst
(kg->g, l->ml, cl/dl); parse_quantity + Tests.
- Produktformular uebernimmt Barcode aus ?barcode= und schlaegt automatisch nach,
fuellt Packungsgroesse; gemeinsame offUtils (guessGroup/suggestionToProduct).
Lagerorte:
- Baum jetzt rekursiv ueber beliebig viele Ebenen (Schrank->Fach->Kiste->...).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Die v2-API liefert kein status:1-Feld (nur code+product) und antwortet mit
HTTP 404 bei Nicht-Treffern; unser Code prueft aber auf status==1 -> selbst
vorhandene Produkte wurden als "unbekannt" gemeldet. Umstellung auf die v0-API
(status 1/0, HTTP 200), plus robustes JSON-Parsing und follow_redirects.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Der Login-Endpunkt nutzt OAuth2PasswordRequestForm (Form-Data); FastAPI
benoetigt dafuer python-multipart. Fehlte in requirements.txt -> RuntimeError
beim Import von auth.py -> Backend-Restart-Loop -> 502.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Verhindert, dass 'chmod +x install.sh' lokal als Aenderung erscheint und
kuenftige 'git pull' mit "local changes would be overwritten" blockiert.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
passlib 1.7.4 vertraegt sich schlecht mit bcrypt 4.x und kann beim Hashen des
Admin-Passworts im Startup (ensure_first_admin) crashen -> Backend-Container in
Restart-Loop -> 502 in der Web-UI. hash_password/verify_password nutzen jetzt
bcrypt direkt (mit 72-Byte-Grenze). passlib aus requirements entfernt.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- install.sh: ensure_example legt .env.example an falls fehlend; env_hint weist
klar auf die anpassbare .env hin; wait_for_backend pollt /api/health und meldet
502-Ursachen mit Log-Hinweis.
- web/api.js: HTML-Fehlerseiten (z.B. nginx 502) nicht mehr roh anzeigen,
stattdessen verständliche Meldung.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Neue Helfer ensure_curl + pkg_install (apt/dnf/yum/apk/pacman/zypper) mit
sudo-Erkennung; wird vor der Docker-Installation aufgerufen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Anzeigename, Paketname, Token-Key, DB-/Volume-Namen auf project_good
- README Clone-Abschnitt mit git.scarriffle.com/Scarriffle/project-good
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>