App: /expiring kann jetzt ohne MHD kommen - das hat sie nicht ausgehalten
Beim Zusammenstellen der Testliste fuer den naechsten iOS-Build
aufgefallen, bevor es jemandem begegnet ist: Seit den angebrochenen
Packungen liefert /expiring auch Chargen OHNE aufgedrucktes MHD - eine
geoeffnete Packung hat nur ein Verbrauchsdatum, und genau die gehoert in
diese Liste. `best_before` ist dort seither null.
Im Swift-Modell stand `let bestBefore: String`, nicht optional. Eine
einzige solche Charge haette das Entschluesseln der GANZEN Antwort
scheitern lassen: keine Ablaufliste, keine Ablaufkarte - und keine
Erinnerungen, denn der Planer laedt dieselbe Antwort.
Gegen den laufenden Server geprueft: Eine angebrochene Packung ohne MHD
kommt als {"best_before": null, "consume_by": "2026-10-05",
"warn_date": "2026-10-05", "warn_source": "consume_by"}.
Mitgenommen, weil es ohnehin an derselben Stelle sitzt: Das Modell kennt
jetzt opened_on, consume_by, warn_date und warn_source. Die Erinnerungen
haengen am Warndatum statt am MHD - eine angebrochene Packung ohne
aufgedrucktes Datum braucht sie am dringendsten und bekam bisher keine.
Liste und Dashboard-Karte schreiben "verbrauchen bis 05.10." statt
"MHD ohne MHD", denn das sind zwei verschiedene Aussagen.
Nicht kompiliert: Auf dieser Maschine gibt es kein Xcode. Geprueft sind
Klammerbalance, Anfuehrungszeichen, dass item.bestBefore nirgends mehr
nicht-optional benutzt wird, dass ExpiringItem nirgends von Hand erzeugt
wird (nur entschluesselt), und der Abgleich jedes Feldnamens gegen eine
echte Serverantwort. Der Build in Xcode bleibt die Probe.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
25e7f23c6c
commit
892c52e49a
@@ -291,7 +291,7 @@ struct ExpiryCard: View {
|
|||||||
HStack {
|
HStack {
|
||||||
VStack(alignment: .leading, spacing: 1) {
|
VStack(alignment: .leading, spacing: 1) {
|
||||||
Text(item.productName).font(.callout)
|
Text(item.productName).font(.callout)
|
||||||
Text("MHD \(display.formatBestBefore(item.bestBefore, precision: item.bestBeforePrecision))")
|
Text("\(item.fristLabel) \(display.formatBestBefore(item.warnDate, precision: item.fristPrecision))")
|
||||||
.font(.caption2).foregroundStyle(.secondary)
|
.font(.caption2).foregroundStyle(.secondary)
|
||||||
}
|
}
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ struct ExpiringView: View {
|
|||||||
HStack {
|
HStack {
|
||||||
VStack(alignment: .leading, spacing: 2) {
|
VStack(alignment: .leading, spacing: 2) {
|
||||||
Text(item.productName)
|
Text(item.productName)
|
||||||
Text("MHD \(display.formatBestBefore(item.bestBefore, precision: item.bestBeforePrecision)) · \(articleAmount(item))")
|
Text("\(item.fristLabel) \(display.formatBestBefore(item.warnDate, precision: item.fristPrecision)) · \(articleAmount(item))")
|
||||||
.font(.caption).foregroundStyle(.secondary)
|
.font(.caption).foregroundStyle(.secondary)
|
||||||
}
|
}
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|||||||
@@ -617,8 +617,19 @@ struct ExpiringItem: Codable, Identifiable {
|
|||||||
let productName: String
|
let productName: String
|
||||||
let quantity: Double
|
let quantity: Double
|
||||||
let baseUnit: String
|
let baseUnit: String
|
||||||
let bestBefore: String
|
/// Darf fehlen! Eine angebrochene Packung ohne aufgedrucktes MHD hat nur ein
|
||||||
|
/// Verbrauchsdatum - und genau die gehoert in diese Liste. Stand hier frueher
|
||||||
|
/// ein nicht-optionaler String, scheiterte das Entschluesseln der GANZEN
|
||||||
|
/// Liste an einer einzigen solchen Charge, samt Erinnerungen.
|
||||||
|
let bestBefore: String?
|
||||||
let bestBeforePrecision: String?
|
let bestBeforePrecision: String?
|
||||||
|
let openedOn: String?
|
||||||
|
let consumeBy: String?
|
||||||
|
/// Das Datum, nach dem gewarnt wird: das fruehere von MHD und
|
||||||
|
/// Verbrauchsdatum. Immer gesetzt - der Server rechnet es aus.
|
||||||
|
let warnDate: String
|
||||||
|
/// "best_before" oder "consume_by" - sagt, welches der beiden Daten gewann.
|
||||||
|
let warnSource: String?
|
||||||
let daysLeft: Int
|
let daysLeft: Int
|
||||||
let packageSize: Double?
|
let packageSize: Double?
|
||||||
let packageLabel: String?
|
let packageLabel: String?
|
||||||
@@ -627,6 +638,17 @@ struct ExpiringItem: Codable, Identifiable {
|
|||||||
|
|
||||||
var id: Int { lotId }
|
var id: Int { lotId }
|
||||||
|
|
||||||
|
/// Ist die Packung angebrochen und draengt deshalb?
|
||||||
|
var istAngebrochen: Bool { warnSource == "consume_by" }
|
||||||
|
|
||||||
|
/// Beschriftung vor dem Datum. "MHD 05.10." und "verbrauchen bis 05.10."
|
||||||
|
/// sind zwei verschiedene Aussagen, und die Liste zeigt beide nebeneinander.
|
||||||
|
var fristLabel: String { istAngebrochen ? "verbrauchen bis" : "MHD" }
|
||||||
|
|
||||||
|
/// Genauigkeit des angezeigten Datums. Ein Verbrauchsdatum ist immer
|
||||||
|
/// taggenau, auch wenn auf der Packung nur ein Monat stand.
|
||||||
|
var fristPrecision: String? { istAngebrochen ? "day" : bestBeforePrecision }
|
||||||
|
|
||||||
/// Restlaufzeit als kurzer Text. „0 Tage" ergibt keinen Sinn - am Ablauftag
|
/// Restlaufzeit als kurzer Text. „0 Tage" ergibt keinen Sinn - am Ablauftag
|
||||||
/// selbst steht „heute". Karte und Liste nutzen dieselbe Formulierung.
|
/// selbst steht „heute". Karte und Liste nutzen dieselbe Formulierung.
|
||||||
var restlaufzeitText: String {
|
var restlaufzeitText: String {
|
||||||
@@ -644,6 +666,10 @@ struct ExpiringItem: Codable, Identifiable {
|
|||||||
case baseUnit = "base_unit"
|
case baseUnit = "base_unit"
|
||||||
case bestBefore = "best_before"
|
case bestBefore = "best_before"
|
||||||
case bestBeforePrecision = "best_before_precision"
|
case bestBeforePrecision = "best_before_precision"
|
||||||
|
case openedOn = "opened_on"
|
||||||
|
case consumeBy = "consume_by"
|
||||||
|
case warnDate = "warn_date"
|
||||||
|
case warnSource = "warn_source"
|
||||||
case daysLeft = "days_left"
|
case daysLeft = "days_left"
|
||||||
case packageSize = "package_size"
|
case packageSize = "package_size"
|
||||||
case packageLabel = "package_label"
|
case packageLabel = "package_label"
|
||||||
|
|||||||
@@ -75,7 +75,9 @@ enum ExpiryPlanner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for item in snapshot.expiring {
|
for item in snapshot.expiring {
|
||||||
guard let mhd = parseTag(item.bestBefore, calendar: calendar) else { continue }
|
// Das Warndatum, nicht das MHD: Eine angebrochene Packung ohne
|
||||||
|
// aufgedrucktes Datum braucht die Erinnerung am dringendsten.
|
||||||
|
guard let mhd = parseTag(item.warnDate, calendar: calendar) else { continue }
|
||||||
let mhdTag = calendar.startOfDay(for: mhd)
|
let mhdTag = calendar.startOfDay(for: mhd)
|
||||||
|
|
||||||
// Welche Regeln gelten fuer dieses Produkt? Kategorie ersetzt Allgemeine:
|
// Welche Regeln gelten fuer dieses Produkt? Kategorie ersetzt Allgemeine:
|
||||||
|
|||||||
Reference in New Issue
Block a user