Commit Graph

15 Commits

Author SHA1 Message Date
Scarriffle
914118e33a iOS: gescanntes Einzelstueck oeffnet sich (Abbruch behoben)
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>
2026-07-26 11:23:27 +02:00
Scarriffle
dcee7cab6c iOS: Shop nur am Nicht-Einzelstück; QR-Fehler nennt den Server
- „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>
2026-07-26 11:15:38 +02:00
Scarriffle
2d7cfa7fcb iOS: gescannter Einzelstück-Link öffnet wirklich das Stück
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>
2026-07-26 10:24:05 +02:00
Scarriffle
e8471bd951 Universal Links: gescannter Einzelstück-Link öffnet die App
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>
2026-07-26 08:12:45 +02:00
Scarriffle
7f9895245c Lagerort-QR + Zuordnen per Scan
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>
2026-07-26 08:09:00 +02:00
Scarriffle
953e0aa483 iOS: "Nachschlagen"-Scan (nur ansehen)
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>
2026-07-26 08:04:56 +02:00
Scarriffle
2a7f98d61c iOS: Artikel ohne Barcode anlegen offensichtlich machen
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>
2026-07-25 23:33:18 +02:00
Scarriffle
96172c6fa0 iOS: Ablauf-Benachrichtigungen, je Server einstellbar
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>
2026-07-23 21:15:09 +02:00
Scarriffle
2ad197df42 iOS zeigt dieselben Dashboards und kann direkt buchen
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>
2026-07-23 19:27:01 +02:00
Scarriffle
ff158a1e5f iOS: mehrere Server, TabBar, Uebersicht, Verlauf und Stammdaten
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>
2026-07-23 18:24:07 +02:00
Scarriffle
dc1750d23a Umbenennung in Vorrania vollstaendig durchziehen
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>
2026-07-23 13:13:11 +02:00
Scarriffle
47ca4802aa Darstellung der App nachgebessert, Gruppen heissen jetzt Kategorien
Die App zeigte an mehreren Stellen Rohwerte statt lesbarer Angaben, und die
Bedienelemente sahen nicht nach Bedienelementen aus.

Deutsche Lokalisierung: Im Chargen-Abschnitt stand "10. Dec 2027" - deutsches
Format mit englischem Monatsnamen. Die Ursache lag nicht in der Formatierung,
sondern darin, dass die App als englische App gebaut wurde
(CFBundleDevelopmentRegion = $(DEVELOPMENT_LANGUAGE), also "en"). Jetzt ist
Deutsch als Sprache gesetzt; damit zeichnet iOS DatePicker und Monatsnamen von
selbst richtig. Der MonthYearPicker legt seinen Kalender zusaetzlich fest auf
de_DE, damit die Monatsnamen auch bei anderssprachigem Geraet stimmen.

Datumsformat: Die Anzeige folgt jetzt der Servereinstellung (Einstellungen ->
Darstellung), wie die Web-Oberflaeche. Neu DisplaySettings mit den fuenf
Formaten, den deutschen Beschriftungen der Basiseinheiten und der Mengenangabe
in der Leitangabe. Die doppelt ausprogrammierte MHD-Formatierung in
ListViews und CheckOutView ist damit weg.

Chargen-Abschnitt: Jede Charge steht jetzt in einem eigenen Abschnitt mit
Kopfzeile statt gestapelt in einer einzigen Formularzeile - das war der
gequetschte Eindruck. Loeschen sitzt in der Kopfzeile.

Mengenfelder haben einen sichtbaren Rahmen. Ohne ihn las sich das
rechtsbuendige Feld wie fester Text, und es war nicht erkennbar, dass sich die
Menge ueberhaupt aendern laesst.

Kamera: Der Sucher fuellte per ZStack und ignoresSafeArea den ganzen Bildschirm
und wirkte dadurch erdrueckend. Er sitzt jetzt als 4:3-Feld oben, darunter
Hinweis, Meldungen und Knoepfe in gewohnter Form. Am ScannerViewController
musste nichts geaendert werden.

Einkaufsliste: Artikel und Kategorien tragen ein Symbol, Kategoriezeilen
zusaetzlich eine Kennzeichnung. Den Artikelzeilen fehlte bisher jede Einheit
("fehlt 2 - Bestand 7 von 10"); sie nennen jetzt Packungen und Basismenge.

Produktdetails: Die Felder waren blanke TextFields - sobald ein Wert drinstand,
verschwand der Platzhalter und man las nur noch "Bratbutter" / "M-Classic" /
"450". Jetzt mit Beschriftung links, Einheit hinter der Packungsgroesse (vorher
stand dort der rohe API-Wert "gram") und einem Abschnitt "Erkennung" mit
Barcode. Dasselbe im Formular zum Anlegen.

Gruppen heissen in der Oberflaeche jetzt Kategorien - in Web und App. Tabelle,
Feld group_id und die Endpunkte behalten ihren Namen, damit bestehende Zugriffe
wie Home Assistant weiterlaufen; der Unterschied ist rein sprachlich. Die
Kategorie laesst sich nun auch in der App zuweisen, was vorher gar nicht ging.

Dabei ein Fehler gefunden und behoben: Swift laesst nil-Optionals beim Kodieren
weg, das Backend wertet nur mitgeschickte Felder aus. "Keine Kategorie" oder
eine geloeschte Marke waeren damit stumm verpufft. ProductUpdateRequest kodiert
diese Felder jetzt ausdruecklich. Der Mindestbestand steht bewusst nicht mehr
darin, weil die App ihn nicht bearbeitet und ein mitgesendetes null ihn
geloescht haette.

Getestet: iOS-Geraetebuild fehler- und warnungsfrei, Web-Build laeuft durch,
40 pytest-Tests gruen. Die Datumsformatierung wurde eigenstaendig uebersetzt
und gegen elf Faelle geprueft (alle fuenf Formate, Monatsangabe, fehlendes und
unlesbares Datum) - alle korrekt. Im gebauten Bundle ist belegt, dass
CFBundleDevelopmentRegion auf "de" steht. Gegen die laufende API geprueft:
/settings und /groups liefern die erwarteten Felder, das Leeren der Kategorie
wirkt jetzt und der Mindestbestand bleibt dabei erhalten.
Die Oberflaeche selbst habe ich nicht auf dem Geraet bedient.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 19:04:58 +02:00
Scarriffle
f3ee10e4d4 Einkaufsliste, Ablaufliste und Produktverwaltung in der App
Der Startbildschirm bekommt drei weitere Einstiege: Einkaufsliste (Artikel und
Gruppen unter Mindestbestand), Bald ablaufend (Chargen mit Restfrist, abgelaufene
rot) und Produkte. Alle Listen lassen sich durch Herunterziehen aktualisieren.

In den Produktdetails sind Name, Marke, Packungsgroesse, Gebinde-Bezeichnung und
MHD-Angabe aenderbar. Die Chargen werden mit Menge und MHD aufgelistet; Antippen
oeffnet die Korrektur von Menge und MHD (inklusive Umschalten auf Monat/Jahr),
Wischen loescht eine Charge. Mengen werden in der Leitangabe des Artikels
angezeigt und beim Speichern in Basiseinheiten zurueckgerechnet, weil die
Chargen-Schnittstelle in Basiseinheiten arbeitet.

Dabei ist ein bestehender Fehler aufgefallen und mitbehoben: Fuer das Gebinde
kennt das Backend nur das Schluesselwort "package" (services/conversion.py
akzeptiert package/packung/pkg/pack). Die App hat stattdessen die
Gebinde-Bezeichnung selbst als Einheit geschickt. Bei Produkten mit eigener
Bezeichnung - "Glas", "Dose", "Flasche", "Tuete" - schlugen Einlagern und
Auslagern damit mit "Unbekannte Einheit: Dose" fehl. Nur die Standardbezeichnung
"Packung" funktionierte zufaellig, weil sie in der Liste der Schluesselwoerter
steht. Anzeige und uebertragener Wert sind jetzt getrennt (neuer Typ
UnitOption): angezeigt wird weiterhin "Glas", geschickt wird "package".

Ausserdem die Warnung zu den Bildschirmausrichtungen behoben - das iPad verlangt
alle Ausrichtungen, solange die App nicht auf Vollbild besteht.

Getestet: iOS-Geraetebuild fehler- und warnungsfrei. Die Endpunkte hinter den
neuen Ansichten sind gegen die laufende API geprueft, ebenso das Aendern und
Loeschen von Chargen. Der Einheiten-Fehler wurde an der echten API
nachgestellt ("Unbekannte Einheit: Dose") und mit "package" als 200 bestaetigt.
Die neuen Ansichten habe ich nicht auf dem Geraet bedient.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 18:16:15 +02:00
Scarriffle
8e94f15c84 App-Icon eingebunden, fehlendes Auslagern-Symbol und Barcode-Erkennung verbessert
Auslagern-Kachel: "arrow.up.from.line" existiert als SF Symbol nicht, deshalb
blieb das Icon leer und man sah nur die eingefaerbte Kachelflaeche. Ersetzt
durch "arrow.up.to.line", das Gegenstueck zum "arrow.down.to.line" beim
Einlagern. Alle uebrigen im Projekt verwendeten Symbolnamen wurden gegen die
Symbolliste des Systems geprueft und sind gueltig.

App-Icon: Das mitgelieferte AppIcon.icon (Icon-Composer-Format) wird als
Ressource eingebunden und ueber ASSETCATALOG_COMPILER_APPICON_NAME gesetzt.
Die Icon-Eintraege landen korrekt in der selbstgepflegten Info.plist.

Barcode-Erkennung, nachdem ein dunkelgruener Code auf weiss nicht gelesen wurde:
- Aufloesung auf 1920x1080 statt der Voreinstellung, damit kontrastarme und
  kleine Codes ueberhaupt sauber aufgeloest werden.
- Dauerhafter Autofokus mit Beschraenkung auf den Nahbereich; weiches
  Nachfuehren aus, da es hier nur verzoegert.
- Antippen stellt gezielt auf eine Stelle scharf und misst dort die Belichtung.
- Licht-Schalter in der Navigationsleiste beider Scan-Bildschirme; beim
  Verlassen wird das Licht sicher wieder ausgeschaltet.
- Mehr Symbologien (ITF-14, DataMatrix, PDF417, Aztec, Code93). Es werden nur
  die Typen gesetzt, die die Kamera meldet - sonst wirft AVFoundation zur
  Laufzeit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 17:28:21 +02:00
Scarriffle
4bb9a10366 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>
2026-07-22 14:59:04 +02:00