Je Einzelstück eine ausklappbare Detailzeile: Kaufpreis (Betrag + CHF/EUR) und
Belege (Rechnung/Garantieschein) hochladen, ansehen, löschen. Nach dem Upload
eines PDFs schlägt der Server „Garantie bis" und Kaufpreis vor – per Knopf
übernehmbar. Belege werden mit Anmeldung geladen (kein offener Zugriff).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
guess_price_cents zieht den Kaufpreis aus dem Belegtext: bevorzugt den Betrag
nach einem Summen-Stichwort (Gesamtbetrag/Total/…), sonst den groessten Betrag.
Versteht Tausendertrenner (1'299.00) und deutsches Format (1.299,00). Der
Upload liefert den Vorschlag als suggested_price_cents mit. 5 neue Tests.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Item: price_cents + currency (Kaufpreis rappen-/centgenau). Migration ergaenzt.
- Neue Tabelle item_documents (mehrere Belege je Stueck, PDF oder Bild); Blob
wird verzoegert geladen, damit Item-Listen leicht bleiben.
- Endpunkte: Upload (POST), Ansehen/Download (GET), Loeschen (DELETE) je Stueck.
Dateiname beim Download gehaertet (keine Header-Injektion).
- services/warranty.py: schaetzt aus PDF-Text lokal ein Garantieende
(Zeitraum + Kaufdatum, oder Datum nahe Garantie-Stichwort); pypdf ergaenzt.
Der Upload liefert den Vorschlag zurueck. 10 neue Tests, Suite 136 gruen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Lagerorte: LocationEditor hängt bestehende Orte jetzt um (Elternort auch beim
Bearbeiten, eigene Unterorte ausgeschlossen); Client updateLocation +
LocationUpdateRequest (parent_id wird immer gesendet, auch null).
- Kategorien: beim Anlegen optionale Oberkategorie wählbar (Unterkategorie);
mit Eltern erbt sie deren Verwaltungsart, sonst wird sie gewählt.
- Eigene Felder: neue CategoryFieldsView + FieldEditor (anlegen/bearbeiten/
löschen von Feldern je Kategorie), erreichbar aus dem Kategorie-Editor;
Client ownFieldDefinitions/create/update/deleteFieldDefinition + Models.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Zellen, die mit = + - @ (oder Tab/CR) beginnen, werden mit einem vorangestellten
Apostroph zu Text entschaerft. So fuehrt ein Produktname wie "=HYPERLINK(...)"
beim Oeffnen der Etiketten-CSV in Excel/P-touch nicht mehr als Formel aus.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bestehende Lagerorte lassen sich jetzt nachträglich einem anderen Elternort
zuordnen oder auf die oberste Ebene holen.
- Backend: LocationUpdate um parent_id (Name jetzt optional); update_location
haengt um, mit Schutz gegen Ringe (weder auf sich selbst noch auf einen
eigenen Unterort) und Existenzpruefung des Ziels. 6 Tests.
- Web: Lagerorte-Seite bekommt je Ort Bearbeiten (Name + Elternort ueber den
CategorySelect-Baum, eigene Unterorte ausgeschlossen); api.updateLocation.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Hat ein Artikel ein Bild, zeigt die Produktliste jetzt ein Vorschaubild statt
des neutralen shippingbox-Symbols; ohne Bild bleibt das Symbol. Ein gemeinsamer
ProductImageCache verhindert, dass beim Scrollen dieselben Bilder immer wieder
geladen werden, und merkt sich auch "kein Bild". ProductPhotoSection haelt den
Cache bei Laden/Upload/Entfernen synchron.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Backend und API-Client konnten Felddefinitionen laengst aendern (PATCH), nur die
Oberflaeche bot bisher nur Anlegen und Loeschen. In der Felder-Verwaltung einer
Kategorie gibt es jetzt je Feld einen Bearbeiten-Knopf: er laedt Name, Typ,
Einheit, Auswahlmoeglichkeiten und Pflicht ins Formular; Speichern aktualisiert
das Feld (oder Abbrechen). Geerbte und eingebaute Felder bleiben unveraendert.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Statt der nativen "— —"-Dropdowns nutzen jetzt auch Produktformular
(Kategorie + Inline-"Neue Kategorie"-Elternwahl), Einlagern-Maske und
Produkte-Filter den CategorySelect-Baum. CategorySelect um optionale
Zusatz-Eintraege (extraOptions) erweitert, damit der Produkte-Filter
"Alle Kategorien"/"Ohne Kategorie" weiter anbietet.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Gemeinsame TreeMasterView fuer hierarchische Stammdaten (Ein-/Ausklappen,
Anlegen, Bearbeiten, Loeschen):
- Kategorien: Segment-Filter Alle/Lebensmittel/Gegenstaende (wie im Web); in
der Alle-Ansicht zeigt jede Zeile ihre Art. Anlegen/Bearbeiten waehlt die
Verwaltungsart (neu: createCategory mit tracking, updateCategory).
- Lagerorte: werden jetzt als aufklappbarer Baum gezeigt statt flach. Beim
Anlegen laesst sich ein Elternort waehlen (Hierarchie aufbauen); Umhaengen
unterstuetzt der Server nicht, Bearbeiten bleibt daher Umbenennen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Die Stammdaten-Kategorien waren nur eingerueckt und immer komplett offen. Jetzt
lassen sich Unterkategorien per Pfeil ein- und ausklappen (wie im Web): zugeklappt
zeigt die Zeile "N ausgeblendet". Anlegen, Umbenennen (Antippen) und Loeschen
(wischen) bleiben unveraendert.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Die iOS-Stammdaten hatten keinen Punkt fuer Shops – die "gekauft bei"-Liste war
nur im Web pflegbar. Jetzt gibt es unter Verwaltung > Stammdaten "Shops" mit
Anlegen, Bearbeiten (Name + optionale Website) und Loeschen, analog zum Web.
Client um createShop/updateShop/deleteShop samt Request-Models ergaenzt.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Der Entfernen-Knopf stand auch ohne Bild da. ProduktBild meldet jetzt via
onLoaded, ob ein Bild geladen wurde; das Produktformular blendet "Entfernen"
entsprechend ein oder aus. "Foto machen" und "Galerie" bleiben immer sichtbar.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Kategorien und Gruppen standen in der Alltags-Navigation, obwohl sie – wie
Lagerorte, Shops, Einheiten und Gebinde – nur von Admins gepflegte Katalog-
Stammdaten sind. Jetzt sind alle Stammdaten zusammen im Admin-Bereich, der in
zwei Abschnitte gegliedert ist:
- Stammdaten: Kategorien, Gruppen, Lagerorte, Shops, Einheiten, Gebinde
- System: Benutzer, Import/Export, Einstellungen
Normale Nutzer sehen nur noch den Alltag (Uebersicht, Ein-/Auslagern, Produkte,
Einkaufsliste, Verlauf). /categories und /groups sind jetzt Admin-Routen. Das
entspricht der Gliederung der iOS-App.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Auswertungen (by_category, expiry_split, stats) iterierten nur Lots. Gegenstaende
mit Menge-je-Lagerort liegen als Lots und tauchten auf – Einzelstuecke liegen
aber als Items und fehlten dadurch komplett (Dashboard zeigte z.B. nur die
Powerbank, nicht Kamera/Kabel). Neuer Helfer _bestand_beitraege deckt beide
Speicherformen ab: jedes Item zaehlt als ein Stueck ohne MHD. Regressionstest
ergaenzt; volle Suite gruen (120).
CategorySelect: Scrollen IM aufklappbaren Menue schloss es, weil jeder Scroll als
Seiten-Scroll gewertet wurde. Jetzt schliesst nur ein Scroll ausserhalb des
Menues; die lange Liste laesst sich scrollen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Die uebergeordnete Kategorie wird nicht mehr ueber ein <select> mit
"— —"-Einrueckung gewaehlt, sondern ueber einen aufklappbaren Baum-Picker
(CategorySelect): echte Einrueckung, Aeste per Pfeil auf-/zuklappbar wie in der
Liste. Das Menue haengt per Portal am <body>, damit der horizontal scrollende
Tabellenrahmen es nicht abschneidet. Ersetzt beide Stellen (Zeilen-Elternwahl
und Anlege-Formular); die kompakten Knoepfe machen die Zeile schmaler, sodass
die Tabelle nicht mehr seitwaerts ueber den Block hinauslaeuft.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Der Abruf schlug mit „Abgebrochen" (URLError.cancelled) fehl, nicht mit 404:
openItemUid wurde zu Beginn des .task(id:) auf nil gesetzt, wodurch SwiftUI die
laufende Anfrage abbrach. Jetzt wird openItemUid erst NACH dem Laden
zurueckgesetzt – das Stueck oeffnet sich.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- „Gekauft bei" (Shop) gehoert bei Einzelstuecken zum einzelnen Stueck, nicht
ans Modell. Wie im Web wird der Shop-Picker am Produkt jetzt ausgeblendet,
sobald „Einzelstuecke" aktiv ist; der Produktlink (beschreibt das Produkt
allgemein) bleibt. Beim Speichern wird fuer Einzelstuecke kein Shop ans
Modell geschrieben.
- Wird ein gescanntes Einzelstueck nicht gefunden, nennt die Meldung jetzt den
abgefragten Server und den Original-Fehler. So ist sofort erkennbar, ob der
aktive Server ein anderer ist als der im QR-Code (haeufigste Ursache bei
korrekter UID) oder ob ein Netz-/Serverfehler vorliegt.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ITSAppUsesNonExemptEncryption=false in die Info.plist. Die App nutzt nur die
Standard-Verschluesselung des Systems (HTTPS), faellt damit unter die Ausnahme
und die "App Encryption Documentation"-Frage entfaellt bei jedem Upload.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Universal Link oeffnete die App, sprang aber nicht aufs Stueck. Jetzt robuster:
- Kaltstart wird abgefangen (AppDelegate application(_:continue:) + pending-URL),
zusaetzlich zu den SwiftUI-Hooks.
- Beim Link wird auf das Server-Profil gewechselt, dessen Adresse zum Host passt
(sonst wuerde die UID am falschen Server gesucht).
- Schlaegt das Aufloesen fehl (nicht angemeldet, UID unbekannt), erscheint jetzt
eine Meldung statt stiller Nichtreaktion.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
P-touch will Komma, wir hatten fest Semikolon - jetzt einstellbar und in
localStorage gemerkt, sodass man es nur einmal setzt. Standard ist Komma.
Anfuehrungszeichen werden passend zum gewaehlten Trennzeichen gesetzt.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Auf "Import / Export" neue Karte: Kategorien auswaehlen (inkl. Unterkategorien)
und je Einzelstueck eine CSV-Zeile herunterladen - Spalten QR-Inhalt (…/i/<UID>),
UID, Produkt, Marke, Kategorie, Lagerort. In der Etiketten-Software als Datenbank
verknuepfen, dann entsteht QR + Text automatisch - kein einzelnes Bild-Download
und Zuordnen mehr.
Backend: GET /export/labels?category_ids=... liefert die Zeilen (JSON); den
QR-Inhalt setzt das Web mit window.location.origin dazu.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Beim Nachschlagen/Öffnen eines Einzelstuecks stand oben das QR-Bild - unnoetig,
den hat man ja gerade gescannt. Jetzt oben das Produktbild plus Name/Marke; die
stueckspezifischen Angaben (UID, Kaufdatum, Garantie, Ort, Notiz) bleiben.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Server liefert /.well-known/apple-app-site-association (nur /i/* beansprucht;
/l/* bleibt im Browser). iOS: Associated-Domains-Entitlement, Universal Link
(https://…/i/<UID>) wird abgefangen (onContinueUserActivity) und öffnet das
Einzelstück direkt.
Hinweis: Damit das Signieren durchläuft, muss die Capability "Associated
Domains" einmalig im Apple-Developer-Portal für die App-ID aktiv sein.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Web: druckbare QR-Etiketten fuer Lagerorte (Locations-Seite) und Route /l/<id>
(zeigt den Ort im Browser). Geteilter QR-Helfer (web/src/qr.jsx).
iOS: neuer Ablauf "Ort zuordnen" - erst Einzelstueck-QR (…/i/<UID>) scannen, dann
Lagerort-QR (…/l/<ID>); das Stueck wird dem Ort zugewiesen, danach gleich das
naechste. Plus die zuvor gebaute "Nachschlagen"-Kachel.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Neue Kachel im Scannen-Tab: Barcode -> Artikel, Einzelstueck-QR -> genau das
Stueck (Kaufdatum, Garantie, Ort ...), rein lesend ohne Ein-/Auslagern. Praktisch
mit Einzelstuecken, um schnell nachzuschauen, welches Exemplar man in der Hand
haelt.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
iOS-Parität zum Web: Item-Modell + API, Einzelstueck-Liste in der Produkt-
Detailansicht (anlegen, je-Stueck Lagerort/Kaufdatum/Garantie/Bezugsquelle/Notiz
aendern, entfernen mit Grund, QR je Stueck via CoreImage). Anlegen-Formular hat
den Schalter "Einzelstuecke". Der Scanner erkennt Einzelstueck-QR (…/i/<UID>) und
oeffnet direkt das Stueck.
Seed: "Kaufdatum"/"Garantie bis" nicht mehr als Modell-Felder auf Elektronik -
diese Angaben gehoeren je Einzelstueck ans Item, nicht ans Modell.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Gegenstands-Produkte haben jetzt den Umschalter "Menge je Lagerort / Einzelstücke".
Bei Einzelstücken erscheint (volle Breite) eine Liste je Stueck mit eigener UID,
QR-Code, Lagerort, Kaufdatum, Garantie, Bezugsquelle und Notiz - direkt inline
aenderbar, mit Entfernen (Grund) und druckbaren QR-Etiketten. Ein Scan oeffnet
ueber die Route /i/<uid> das passende Stueck. QR via qrcode (gebundelt, offline).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Gegenstands-Produkte koennen als Einzelstuecke gefuehrt werden (Product.individual):
jedes physische Stueck ist ein Item mit eigener kurzer UID (fuer QR), eigenem
Lagerort, Kaufdatum, Garantie, Bezugsquelle und Notiz. So laesst sich dasselbe
Modell mehrfach getrennt fuehren (Powerbank 2024 + 2025).
Neu: models.Item + Product.individual (+Migration), Schemas, services/items.py
(UID-Erzeugung, Anreicherung), routers/items.py (CRUD, /items/by-uid/{uid} zur
QR-Aufloesung, /items/{id}/remove mit Grund als Bewegung). current_stock zaehlt
bei Einzelstueck-Produkten die Items. Tests + HTTP-Smoke gruen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Durch die neue Spalte "Uebergeordnet" wurde die Kategorie-Tabelle im halben Grid
zu breit und scrollte seitwaerts. Die Kategorien-Seite nutzt jetzt das
wide-aside-Layout; dessen Block daneben ist zudem schmal gedeckelt (clamp), damit
die Tabelle den restlichen Platz bekommt. Gilt auch fuer die Gruppen-Seite.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
In der Kategorien-Verwaltung gibt es je Zeile eine Spalte "Uebergeordnet" mit
Auswahl - so laesst sich eine (Unter-)Kategorie unter eine andere haengen oder
auf die oberste Ebene holen. Zielauswahl auf denselben Typ beschraenkt und um die
eigenen Unterkategorien bereinigt; das Backend verhindert Ringe zusaetzlich.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Zum leichteren Unterscheiden gibt es in der Produkte-Liste und in der
Kategorien-Verwaltung einen Umschalter Alle/Lebensmittel/Gegenstaende. Er filtert
die jeweilige Liste und in der Produktsuche auch die Kategorieauswahl auf den Typ.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Gleiche Idee wie im Web: ein Typ-Umschalter filtert die Kategorieauswahl, statt
Werkzeug & Co. zwischen den Lebensmittel-Kategorien zu suchen. Im Anlege-Formular
blenden sich zudem die Lebensmittel-Felder (Gruppe, Einheit, MHD) aus, wenn
"Gegenstand" gewaehlt ist. Die Detailansicht filtert die Kategorieliste auf den
Typ des Artikels. CategoryPicker bekam dafuer einen optionalen Typ-Filter.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Statt die Verwaltungsart nur implizit ueber die Kategorie zu waehlen (und dabei
Werkzeug & Co. zwischen 50 Lebensmittel-Kategorien zu suchen), gibt es jetzt oben
einen klaren Umschalter. Die Kategorieauswahl zeigt nur Kategorien des gewaehlten
Typs; neu angelegte Kategorien erben den Typ automatisch. Neuanlage startet als
Gegenstand, beim Bearbeiten kommt der Modus aus dem Artikel.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Anlegen ohne EAN war nur versteckt im Scan-Screen erreichbar. Jetzt zwei klare
Wege: ein "+" in der Produkte-Liste und eine Kachel "Artikel anlegen" im
Scannen-Tab (jeweils fuer Admins). Beide oeffnen das Anlege-Formular ohne
Barcode; ein Abbrechen-Knopf schliesst es.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Der Barcode-Lookup fragt schon immer beide Quellen ab (Open Food Facts und
Open Products Facts, ueber /products/lookup). Die Scan-Ansicht sprach aber nur
von "Open Food Facts" und liess es so wirken, als wuerde nur dort gesucht.
Texte neutralisiert bzw. beide Quellen genannt.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- nginx client_max_body_size auf 12m (Standard 1 MB loeste beim Bild-Upload
einen 413 aus, bevor das Backend den Upload sah).
- Backend-Bildlimit auf 8 MB angehoben, bleibt unter dem nginx-Limit, damit
Uebergroesse eine klare 400-Meldung statt eines nackten 413 ergibt.
- Web: Foto vor dem Upload clientseitig verkleinern (max 1600px, JPEG) -
schneller, kleiner in der DB, zuverlaessig unter dem Limit.
- iOS: Kamera ueber fullScreenCover statt sheet und Schliessen deterministisch
ueber ein Binding - behebt das "Kamera geht direkt wieder zu"-Flackern.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bei Gegenstaenden fragt das Backend Open Products Facts ab, bei Lebensmitteln
Open Food Facts. Die sichtbaren Texte (Vergleichs-Button, Toasts, das
Gegenueberstellungs-Panel) nannten aber immer "Open Food Facts". Jetzt richtet
sich die Beschriftung nach der Verwaltungsart der Kategorie.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Web: Kategorien/Unterkategorien und eigene Felder direkt im Artikelformular
anlegen (ohne Umweg über die Kategorien-Seite).
- Artikelfoto per Kamera oder Galerie hochladen – Backend-Endpunkt
(PUT/DELETE /products/{id}/image), Web (Foto machen / Galerie) und iOS
(Kamera + PhotosPicker, inline in der Produktansicht angezeigt).
- CSV-Import: neue Spalte "art" (Gegenstand/Lebensmittel) steuert den Modus
automatisch angelegter Kategorien; Export schreibt sie mit.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Die Kategorie bestimmt die Verwaltungsart (food/object). Gegenstaende:
Menge je Lagerort statt Chargen/MHD, Umlagern (ohne Grund) und Entfernen
mit Pflicht-Grund samt Entnahme-Statistik. Beliebig viele eigene Felder
je Kategorie (vererbt an Unterkategorien), "gekauft bei" ueber eine
verwaltbare Shop-Liste und ein Produktlink. Barcode-Lookup zusaetzlich
ueber Open Products Facts.
Der Lebensmittel-Teil (Chargen/MHD/FEFO) bleibt unveraendert. Umgesetzt in
Backend (FastAPI, +Tests gruen), Web (React) und iOS (SwiftUI).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Der Start-Tab spiegelte bisher fest die Web-Dashboards - dieselbe gespeicherte
Anordnung, und Diagramme gab es nur den Hinweis "im Web". Das war der Punkt,
der geaergert hat.
Jetzt ist der Modus je Server umschaltbar: "Web-Uebersicht spiegeln" (weiter
Standard, damit nach dem Update nichts ueberrascht) oder "Eigene
App-Uebersicht". Die eigene Uebersicht liegt nur auf dem Geraet (pro Server),
unabhaengig vom Web und nicht synchronisiert. Beim ersten Umschalten entsteht
gleich eine sinnvolle Startanordnung.
Sie funktioniert wie im Web, nur fuers Telefon: mehrere Dashboards, Karten
einspaltig untereinander, im Bearbeiten-Modus hinzufuegen, per Ziehen sortieren
und loeschen; Artikelkarten bekommen ihren Artikel zugewiesen. Anzeigen und
Bearbeiten sind getrennt - so laufen Sortieren und Loeschen sauber, statt an
Sektionsgrenzen zu haken.
Die Diagramme sind nativ mit Swift Charts: Ablauf- und Kategorien-Anteile
(Ring ab iOS 17, sonst ein 100-%-Balken - das Ziel ist iOS 16, deshalb kein
stilles Anheben), Kategorien nach Zustand als gestapelte Balken, Bestands- und
Artikelverlauf als Linie, Ein-/Auslagerungen als gruppierte Balken. Dieselben
Endpunkte wie das Web-Dashboard, kein Backend-Eingriff. Der "gibt es nur im
Web"-Hinweis faellt damit weg - auch beim Spiegeln werden die Diagramme jetzt
nativ gezeichnet.
Geprueft: Build; alle vier Diagramm-Modelle gegen echte Serverantworten
dekodiert (auch der Zeitstempel mit Z und Sekundenbruchteilen); refactorte
DashboardCardView ohne alte Aufrufstellen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Die App zeigte "Bald ablaufend" nur beim Oeffnen. Jetzt erinnert sie aktiv vor
dem Ablauf - mit frei zusammenstellbaren Regeln, pro Server getrennt.
Eine Regel ist "X Tage/Wochen vor Ablauf", optional nur fuer eine Kategorie
(inklusive deren Unterkategorien), einmalig oder taeglich wiederkehrend. Eine
Kategorie-Regel ersetzt fuer ihre Produkte die allgemeine - so kann Molkerei
frueher warnen als Chips. Dazu am Ablauftag eine letzte, deutliche Warnung fuer
alle Produkte (eigener Schalter). Die Meldungen kommen als Tages-Sammelmeldung,
je Server eine, zur pro Server eingestellten Uhrzeit.
Weil Vorrania im Heimnetz laeuft und von unterwegs oft nicht erreichbar ist,
kann die App nicht zuverlaessig im Hintergrund abfragen - und iOS verlangt
ohnehin, dass lokale Benachrichtigungen im Voraus geplant werden. Also: aus den
Ablaufdaten kuenftige Termine berechnen und einplanen; aufgefrischt beim
Oeffnen, im Vordergrund und best-effort per Hintergrund-Aktualisierung. Geplant
wird fuer alle eingerichteten Server, jeder mit seinem Token aus dem Keychain;
unerreichbare werden uebersprungen. iOS erlaubt 64 ausstehende Meldungen je App,
deshalb die naechstliegenden zuerst und bei 60 gekappt.
Einstellungen sitzen hinter der Glocke je Server in der Server-Verwaltung (nicht
admin-beschraenkt, es ist eine Geraete-Einstellung). Getippt wechselt die
Meldung auf den betroffenen Server und oeffnet "Bald ablaufend". Keine
Backend-Aenderung: die Kategorie kommt aus /products, der Ablauf aus /expiring.
Die Rechenlogik (ExpiryPlanner) ist bewusst netz- und iOS-frei und mit einem
eigenstaendigen Swift-Harnisch geprueft: Kategorie-Vorrang, Unterkategorien,
Rueckfall auf die allgemeine Regel, einmalig vs. wiederkehrend und die letzte
Warnung. Modelle gegen echte /expiring- und /products-Antworten abgeglichen,
App startet ohne Absturz (auch die BGTask-Registrierung).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Die Ein-/Auslagern-Karten schickten unit: "article" - eine Einheit, die der
Server nicht kennt (services/units.py akzeptiert nur package/packung bzw. die
Basiseinheiten g/ml/stück). Jede Buchung ueber die Karte scheiterte damit an
"Unbekannte Einheit: article", in der Web-Oberflaeche wie in der App.
Jetzt wird die Menge in Artikeleinheiten gebucht: gibt es ein Gebinde, ist das
eine Packung, sonst die Basiseinheit des Produkts. Gegen ein laufendes Backend
geprueft (package und Basiseinheit je 200).
Aufgefallen beim Testen der Ablauf-Benachrichtigungen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Die Uebersicht in der App war fest verdrahtet: vier Kacheln, unabhaengig
davon, was am Rechner zusammengestellt wurde. Jetzt liest sie dieselben
Dashboards wie die Web-Oberflaeche und stellt sie nativ dar.
Das Raster laesst sich auf einem Telefon nicht sinnvoll nachbilden, deshalb
stehen die Karten untereinander - in der Lesereihenfolge des Rasters, erst
Zeile, dann Spalte. Zusammengestellt wird weiterhin am Rechner; gibt es
mehrere Dashboards, schaltet oben ein Menue um.
Neu ist die Buchungskarte: Artikel suchen, Menge, ein- oder auslagern, ohne
den Scan-Ablauf zu oeffnen. Nach dem Buchen zieht die Karte den Bestand nach
und laesst die uebrigen Karten neu laden - sonst stuende dort weiter die alte
Zahl.
Diagrammkarten bekommen bewusst keinen halbgaren Nachbau, sondern den
ehrlichen Hinweis, dass es sie in der Web-Oberflaeche gibt.
Geprueft mit der echten Antwort eines laufenden Servers: Sie decodiert in die
Modelle der App, samt zweier Karten derselben Art mit verschiedenen Artikeln.
OverviewView faellt weg, DashboardView tritt an ihre Stelle.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Drei Dinge in einem, weil sie dieselbe Umstellung brauchen.
Karten sind jetzt Instanzen: Die Anordnung merkt sich neben der Kennung auch
die Art und ein "props" je Karte. Dadurch laesst sich dieselbe Art mehrfach
auflegen, und "Verlauf eines Artikels" merkt sich endlich, welcher Artikel
gemeint ist - vorher lag diese Wahl nur in useState und war nach jedem
Neuladen wieder weg, was die Karte zum Beobachten unbrauchbar machte. Wer
mehrere Artikel im Blick haben will, legt jetzt einfach mehrere Karten an.
Mehrere Dashboards stehen als eingerueckte Eintraege unter "Übersicht" in der
Seitenleiste, anlegen und umbenennen laeuft ueber die Kopfzeile. Ab dem
zweiten werden sie aufgezaehlt; bei einem einzigen waere die Liste nur eine
Verdopplung.
Neu sind die Karten "Einlagern" und "Auslagern": Artikel suchen, Menge
eintragen, gebucht. MHD und Lagerort bleiben bewusst weg - wer die braucht,
ist auf der vollen Seite besser aufgehoben. Die alte Karte "Schnellzugriff"
bleibt als reine Verlinkung bestehen.
Der Bestaetigungsdialog kann jetzt auch nach Text fragen (prompt: true). Das
war noetig, weil Dashboards benannt werden - und window.prompt sieht aus wie
der Browser, nicht wie diese Anwendung; derselbe Grund, aus dem es hier schon
kein window.confirm gibt.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Zwei Grenzen sassen tief im Datenmodell. Erstens war "i" im gespeicherten
Layout zugleich die Kartenart; da react-grid-layout eindeutige Kennungen
verlangt, ging jede Art genau einmal - zwei Artikel gleichzeitig beobachten war
damit unmoeglich. Zweitens war dashboard_layouts.user_id unique, also genau ein
Dashboard je Benutzer.
Jetzt ist "i" die Kennung dieser einen Karte und "type" ihre Art, dazu kommt
"props" fuer das, was nur diese Karte angeht (etwa welcher Artikel). Aeltere
Anordnungen haben kein "type"; fuer sie gilt die alte Kennung als Art, sodass
gespeicherte Startseiten unveraendert weiterlaufen.
Die Tabelle bekommt Name und Reihenfolge, das unique faellt. Sein Name haengt
davon ab, wie die Tabelle entstanden ist, deshalb wird er in pg_constraint
nachgeschlagen statt geraten.
Eine Feinheit beim Anlegen: Wer noch auf der Admin-Vorgabe sitzt und sein
erstes eigenes Dashboard anlegt, wuerde die Vorgabe schlagartig verlieren -
sobald eigene Zeilen da sind, zaehlen nur noch die. Deshalb wird sie vorher
uebernommen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Beim Scannen stand auf der Karte "Kategorie ... vorgeschlagen", im Formular war
das Feld danach trotzdem leer - man musste dieselbe Kategorie von Hand
nochmal auswaehlen. Der Vorschlag war damit wertlos: Wer weiss, dass Pesto eine
Sosse ist, gewinnt nichts daraus, es dem Formular noch einmal zu erzaehlen.
Die Ursache lag nicht am Server. Der Lookup liefert category_id und
category_name; die App merkte sich aber nur den Namen fuer die Anzeige und warf
die ID weg. ProductFormView bekam sie gar nicht erst gereicht - anders als die
Gruppe, die schon immer uebernommen wurde. Die Web-Oberflaeche macht es seit
jeher richtig (setNewCategoryId in pages/CheckIn.jsx), nur die App nicht.
Jetzt wird die ID durchgereicht und wie die Gruppe vorbelegt. Aenderbar bleibt
sie: Gespeichert wird ohnehin erst mit "Anlegen & weiter". Der Hinweis auf der
Karte heisst deshalb nicht mehr "vorgeschlagen", sondern "wird zugeordnet" -
das war vorher richtig und waere es jetzt nicht mehr.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Die App konnte fuenf Dinge, die Web-Oberflaeche vierzehn. Alles darueber hinaus
- nachsehen was gestern passiert ist, einen Lagerort anlegen - zwang zurueck an
den Browser.
Mehrere Server: Statt einer Adresse und einem Token kennt die App jetzt
beliebig viele Profile, jedes mit eigenem Token im Keychain. Ein Wechsel ist
damit ein Tipp und meldet an keinem der Server ab. Jedes Profil hat einen frei
waehlbaren Namen ("Zuhause"), der oben in der App steht; ohne Angabe faellt er
auf den Rechnernamen zurueck. Eine bestehende Anmeldung wird beim ersten Start
in ein Profil ueberfuehrt - ohne das waere man nach dem Update abgemeldet.
Statt einer Startseite mit Kacheln gibt es vier Tabs: Start, Scannen, Listen
und - nur fuer Administratoren, wie im Web - Verwaltung. Neu darin sind eine
Uebersicht mit den Kennzahlen des Servers, die per Tipp in die passende Liste
fuehren, der Bewegungsverlauf (auch je Artikel, weil /movements danach filtern
kann) und die Stammdaten: Lagerorte, Einheiten, Gebinde, Kategorien und
Gruppen. Die fuenf teilen sich eine Ansicht und unterscheiden sich nur darin,
wie geladen und geschrieben wird.
Zwei Dinge, die beim Pruefen gegen einen echten Server auffielen: Der Server
schickt Zeitstempel in UTC, haengt hinter SQLite aber keine Zeitzone an. Naiv
gelesen haette die App jede Uhrzeit um die eigene Zeitverschiebung daneben
angezeigt, deshalb wird die nackte Form ausdruecklich als UTC gelesen. Und die
Anzeigeeinstellungen gehoeren dem Server, werden beim Wechsel also neu geladen.
Bewusst nicht dabei: Benutzer, Einstellungen, Branding und Import/Export. Die
bleiben vorerst im Web.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Beide kannte die API bisher nur als Anlegen und Loeschen. Wer sich vertippt
hatte, musste den Eintrag wegwerfen und neu anlegen - und verlor dabei genau
das, was daran haengt: Chargen zeigen auf die Lagerort-ID, Produkte und Gruppen
auf die Einheiten-ID. Ein Tippfehler kostete also Zuordnungen.
Neu ist je ein PATCH nach dem Vorbild der Gebinde, samt Pruefung auf doppelte
Namen ohne Ruecksicht auf Gross- und Kleinschreibung. Anders als beim Loeschen
duerfen auch eingebaute Einheiten umbenannt werden - auch das wie bei den
Gebinden. Art und Faktor einer Einheit bleiben dagegen fest: Sie stecken in
bereits umgerechneten Bestaenden, eine Aenderung wuerde die still verfaelschen.
Die Tests halten fest, worum es eigentlich geht - dass die Verweise das
Umbenennen ueberleben.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Die Fuellung war eine einzelne Farbe fuer alle Erscheinungsbilder. Jetzt
liegen Fassungen fuer hell, dunkel und getoent nebeneinander, damit das Symbol
im dunklen Modus nicht heraussticht.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>