App: Chargenliste, Anbrechen und Teilverbrauch
In der App gab es beides nicht. Chargen waren ueberhaupt nur innerhalb
eines Artikels zu sehen, und dort nur zum Korrigieren - eine
Chargenliste wie im Web fehlte, und damit jeder Weg, eine Packung
anzubrechen oder eine Teilmenge zu verbrauchen. Das Backend kann das
seit zwei Wochen.
Neu im Tab "Listen": der Eintrag "Chargen" mit allen Chargen ueber alle
Artikel, durchsuchbar, mit Schalter "Nur angebrochene". Je Zeile stehen
Menge, MHD bzw. Verbrauchsdatum, Lagerort und ein Abzeichen
"angebrochen"; bei einer offenen Packung die Restmenge in Basiseinheiten
("980 g von 1.000 g"), denn "0,98 Packungen" sagt niemandem etwas.
Nach links wischen gibt die zwei Buchungen:
"Anbrechen" mit Rueckfrage - eine Packung wird abgespalten und bekommt
ihr Verbrauchsdatum, der Rest bleibt zu und behaelt sein MHD.
"Verbrauchen" oeffnet ein Fenster fuer die VERBRAUCHTE Menge (nicht den
Rest - das ist die Zahl, die man kennt). Die Einheitenauswahl holt die
Hausmasse der Kategorie dazu, "2 EL" ist damit buchbar und landet bei
Mehl als 20 g. War die Charge noch zu, bricht die Buchung sie an; der
Haken dafuer ist abwaehlbar.
Loeschen fehlt in der Liste mit Absicht: Eine Charge verschwindet durchs
Verbrauchen, und das Korrigieren sitzt beim Artikel.
Dort zeigt die Chargenzeile jetzt auch "angebrochen · verbrauchen bis
..." - zwei Zeilen mit gleicher Menge waren sonst nicht zu
unterscheiden.
Alles in bestehende Dateien gelegt, keine neue Quelldatei: Das
mitgelieferte Xcode-Projekt listet die Dateien einzeln, eine neue waere
ohne "xcodegen generate" stillschweigend nicht mitgebaut worden.
Gegen den laufenden Server geprueft, genau mit den Aufrufen, die die App
macht: /lots/rows liefert alle Felder, die das Swift-Modell liest;
{"quantity":null} an /lots/{id}/open spaltet 1000 von 2000 g ab und
setzt consume_by; 2 Essloeffel an /lots/{id}/consume buchen 20 g ab und
die Antwort traegt opened und lot.
Nicht kompiliert: kein Xcode auf dieser Maschine. Geprueft sind Klammer-
und Anfuehrungsbalance (und dabei EIN echter Fehler gefunden: ein
deutsches Anfuehrungszeichen mit geradem Schlusszeichen haette eine
Zeichenkette vorzeitig beendet), dass formatAmount global erreichbar
ist, und dass die benutzten Hilfsfunktionen so heissen. 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
f04d80a330
commit
c860c82912
@@ -344,6 +344,14 @@ struct Lot: Codable, Identifiable, Hashable {
|
||||
let bestBefore: String?
|
||||
let bestBeforePrecision: String?
|
||||
let locationId: String?
|
||||
/// Oeffnungstag. nil = die Packung ist noch zu.
|
||||
let openedOn: String?
|
||||
/// "Zu verbrauchen bis" - beim Anbrechen aus der Frist gerechnet.
|
||||
let consumeBy: String?
|
||||
/// Menge beim Oeffnen, damit "noch 400 von 1000 ml" anzeigbar ist.
|
||||
let openedQuantity: Double?
|
||||
|
||||
var istAngebrochen: Bool { openedOn != nil }
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case id, quantity
|
||||
@@ -351,9 +359,117 @@ struct Lot: Codable, Identifiable, Hashable {
|
||||
case bestBefore = "best_before"
|
||||
case bestBeforePrecision = "best_before_precision"
|
||||
case locationId = "location_id"
|
||||
case openedOn = "opened_on"
|
||||
case consumeBy = "consume_by"
|
||||
case openedQuantity = "opened_quantity"
|
||||
}
|
||||
}
|
||||
|
||||
/// Eine Charge mit den Artikelangaben dran - fuer die uebergreifende
|
||||
/// Chargenliste, in der Chargen verschiedener Artikel nebeneinander stehen.
|
||||
struct LotRow: Codable, Identifiable, Hashable {
|
||||
let id: Int
|
||||
let productId: Int
|
||||
let productName: String
|
||||
let productBrand: String?
|
||||
let quantity: Double
|
||||
let baseUnit: String
|
||||
let packageSize: Double?
|
||||
let packageLabel: String?
|
||||
let unitName: String
|
||||
let unitFactor: Double
|
||||
let bestBefore: String?
|
||||
let bestBeforePrecision: String?
|
||||
let locationId: String?
|
||||
let openedOn: String?
|
||||
let consumeBy: String?
|
||||
let openedQuantity: Double?
|
||||
|
||||
var istAngebrochen: Bool { openedOn != nil }
|
||||
|
||||
/// Basiseinheiten je Artikeleinheit: das Gebinde, sonst die Anzeigeeinheit.
|
||||
var artikelFaktor: Double {
|
||||
if let p = packageSize, p > 0 { return p }
|
||||
return unitFactor > 0 ? unitFactor : 1
|
||||
}
|
||||
|
||||
/// Wie die Menge dasteht. Bei einer offenen Packung zaehlt die Restmenge in
|
||||
/// Basiseinheiten - "0,98 Packungen" sagt niemandem etwas, "980 g" schon.
|
||||
var mengeText: String {
|
||||
if istAngebrochen { return "\(formatAmount(quantity)) \(baseKurz)" }
|
||||
let menge = quantity / artikelFaktor
|
||||
let label = (packageSize ?? 0) > 0 ? (packageLabel ?? "Packung")
|
||||
: (unitName.isEmpty ? baseKurz : unitName)
|
||||
return "\(formatAmount(menge)) \(label)"
|
||||
}
|
||||
|
||||
var baseKurz: String {
|
||||
switch baseUnit {
|
||||
case "gram": return "g"
|
||||
case "milliliter": return "ml"
|
||||
default: return "Stück"
|
||||
}
|
||||
}
|
||||
|
||||
/// Token fuer die Basiseinheit, wie es das Backend beim Buchen erwartet.
|
||||
var baseToken: String {
|
||||
switch baseUnit {
|
||||
case "gram": return "g"
|
||||
case "milliliter": return "ml"
|
||||
default: return "piece"
|
||||
}
|
||||
}
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case id, quantity
|
||||
case productId = "product_id"
|
||||
case productName = "product_name"
|
||||
case productBrand = "product_brand"
|
||||
case baseUnit = "base_unit"
|
||||
case packageSize = "package_size"
|
||||
case packageLabel = "package_label"
|
||||
case unitName = "unit_name"
|
||||
case unitFactor = "unit_factor"
|
||||
case bestBefore = "best_before"
|
||||
case bestBeforePrecision = "best_before_precision"
|
||||
case locationId = "location_id"
|
||||
case openedOn = "opened_on"
|
||||
case consumeBy = "consume_by"
|
||||
case openedQuantity = "opened_quantity"
|
||||
}
|
||||
}
|
||||
|
||||
/// Ein Hausmass, das fuer einen Artikel wirklich gilt - "1 EL = 10 g".
|
||||
///
|
||||
/// Die Mengen haengen an der Kategorie (oder am Artikel), nicht am Mass: Ein
|
||||
/// Essloeffel Mehl wiegt anders als einer Zucker.
|
||||
struct MeasureAmount: Codable, Identifiable, Hashable {
|
||||
let measureId: Int
|
||||
let name: String
|
||||
let short: String?
|
||||
let amount: Double
|
||||
let base: String
|
||||
let herkunft: String
|
||||
|
||||
var id: Int { measureId }
|
||||
|
||||
/// Was beim Buchen als Einheit mitgeschickt wird.
|
||||
var token: String { name }
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case name, short, amount, base, herkunft
|
||||
case measureId = "measure_id"
|
||||
}
|
||||
}
|
||||
|
||||
/// Antwort auf einen Teilverbrauch.
|
||||
struct ConsumeResult: Codable {
|
||||
/// Wurde die Charge dabei angebrochen?
|
||||
let opened: Bool
|
||||
/// Die offene Charge danach - nil, wenn sie dabei leer geworden ist.
|
||||
let lot: Lot?
|
||||
}
|
||||
|
||||
struct StorageLocation: Codable, Identifiable, Hashable {
|
||||
let id: String
|
||||
let name: String
|
||||
@@ -821,6 +937,22 @@ struct ProductUpdateRequest: Encodable {
|
||||
}
|
||||
}
|
||||
|
||||
/// Teilverbrauch aus einer Charge.
|
||||
///
|
||||
/// `unit` nimmt dasselbe wie das Auslagern: die Basiseinheit, eine verwaltete
|
||||
/// Einheit, "package" - und die Hausmasse der Kategorie ("EL", "Tasse").
|
||||
struct ConsumeRequest: Encodable {
|
||||
var quantity: Double
|
||||
var unit: String
|
||||
var note: String?
|
||||
var autoOpen: Bool
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case quantity, unit, note
|
||||
case autoOpen = "auto_open"
|
||||
}
|
||||
}
|
||||
|
||||
struct LotUpdateRequest: Encodable {
|
||||
var quantity: Double?
|
||||
var bestBefore: String?
|
||||
|
||||
Reference in New Issue
Block a user