App: die selbst draufgesetzten Posten fehlten in der Einkaufsliste

Die Einkaufsliste besteht aus zwei Haelften: gerechnete Bedarfe (Artikel
und Gruppen unter ihrem Mindestbestand, Bedarfe je Lagerort) und das,
was jemand selbst draufgesetzt hat - eigene Eintraege und die aus einem
Rezept uebernommenen Zutaten.

Die App kannte nur die erste Haelfte. Sie fragte /shopping-list,
/shopping-list/groups und /shopping-list/by-location ab - drei Routen,
die ausschliesslich rechnen. Die gespeicherten Eintraege liegen in
/shopping-list/entries und kommen gebuendelt ueber /shopping-list/all;
danach hat die App nie gefragt. Ergebnis: Man setzt im Web etwas auf die
Liste, nimmt das Telefon mit in den Laden, und es ist nicht da.

Jetzt holt die App die gebuendelte Route - ein Aufruf statt drei - und
zeigt die eigenen Posten in einem eigenen Abschnitt "Selbst
draufgesetzt", mit Herkunft ("fuer Pfannkuchen") und Menge. Antippen
hakt ab: Der Posten verschwindet sofort, geht das Speichern schief,
kommt er zurueck - lieber ein Ruecksprung als ein falsches Haekchen.

Dieselbe Blindstelle hatte die Dashboard-Karte der App; sie meldete
"alle Mindestbestaende sind gedeckt", waehrend Posten auf der Liste
standen. Auch sie nimmt jetzt die gebuendelte Route.

Dazu ein Hinweis im Docstring von GET /shopping-list: Diese Route ist
ein Drittel der Liste, wer "die Liste" will, nimmt /all. Die Aufteilung
hat in zwei Tagen zweimal jemanden glauben lassen, es sei nichts da -
beim zweiten Mal war es die App selbst.

Gegen den laufenden Server geprueft: Mit einem Eintrag "Backpapier"
liefert /shopping-list weiterhin [] und /shopping-list/all den Eintrag
samt art, name, menge_text, source, recipe_name und done - genau die
Felder, die das neue Swift-Modell liest.

Nicht kompiliert: kein Xcode auf dieser Maschine. Geprueft sind
Klammer- und Anfuehrungsbalance, dass die drei alten Routen niemand mehr
aufruft, und dass die benutzten Hilfsfunktionen (makeRequest, jsonBody,
send) wirklich so heissen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-10-03 07:24:58 +02:00
co-authored by Claude Opus 5
parent 892c52e49a
commit f04d80a330
5 changed files with 148 additions and 7 deletions
+53
View File
@@ -482,6 +482,59 @@ struct NewProductRequest: Codable {
// MARK: - Listen (Einkaufsliste, bald ablaufend)
/// Ein Posten, den jemand selbst auf die Einkaufsliste gesetzt hat - von Hand
/// oder uebernommen aus einem Rezept.
///
/// Die gerechneten Posten (Artikel und Gruppen unter ihrem Mindestbestand)
/// entstehen bei jedem Aufruf neu; diese hier liegen in der Datenbank und
/// bleiben, bis sie abgehakt sind. Beides zusammen ergibt erst die Liste.
struct ShoppingEntry: Codable, Identifiable {
let id: Int
/// "product" | "group" | "text"
let art: String
let name: String
/// "2 Packungen" bzw. "150 g" - fertig formatiert vom Server, damit App und
/// Weboberflaeche dieselbe Sprache sprechen.
let mengeText: String
let source: String?
let recipeName: String?
let done: Bool
var istNotiz: Bool { art == "text" }
var istGruppe: Bool { art == "group" }
/// Zeile unter dem Namen: woher der Posten kommt und wie viel fehlt.
var untertitel: String {
[recipeName.map { "für \($0)" }, mengeText.isEmpty ? nil : mengeText]
.compactMap { $0 }
.joined(separator: " · ")
}
enum CodingKeys: String, CodingKey {
case id, art, name, source, done
case mengeText = "menge_text"
case recipeName = "recipe_name"
}
}
/// Die komplette Einkaufsliste in einer Antwort: gerechnete Bedarfe UND
/// gespeicherte Eintraege. Die drei Einzelrouten liefern nur die gerechnete
/// Seite - wer nur sie abfragt, sieht die selbst draufgesetzten Posten nicht.
struct ShoppingListAll: Codable {
let products: [ShoppingItem]
let groups: [GroupShoppingItem]
let byLocation: [LocationNeeds]
let entries: [ShoppingEntry]
/// Kennungen der abgehakten GERECHNETEN Posten. Die App zeigt sie noch
/// nicht an, liest sie aber mit, damit das Feld nicht ueberrascht.
let checked: [String]
enum CodingKeys: String, CodingKey {
case products, groups, entries, checked
case byLocation = "by_location"
}
}
struct ShoppingItem: Codable, Identifiable {
let productId: Int
let name: String