Preis-Vorschlag: Auswahl aus mehreren erkannten Beträgen

Findet die Beleg-Analyse mehrere Beträge, liefert der Upload jetzt eine
Kandidatenliste (bester Tipp zuerst). Web und iOS bieten dann ein Dropdown zur
Auswahl des richtigen Kaufpreises, statt nur den automatischen Tipp. 1 neuer
Test; Backend-Suite gruen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-07-27 06:39:03 +02:00
parent 761c131286
commit 2bc871f229
7 changed files with 94 additions and 8 deletions

View File

@@ -104,6 +104,7 @@ struct ItemEditView: View {
@State private var documents: [ItemDocument] = []
@State private var suggWarranty: String?
@State private var suggPrice: Int?
@State private var suggPriceCandidates: [Int] = []
@State private var busy = false
@State private var busyDoc = false
@State private var error: String?
@@ -164,11 +165,24 @@ struct ItemEditView: View {
VStack(alignment: .leading, spacing: 6) {
Text("Im Beleg erkannt:").font(.caption).foregroundStyle(.secondary)
if let w = suggWarranty { Text("Garantie bis \(w)") }
if let p = suggPrice { Text("Kaufpreis \(ItemEditView.formatCents(p)) \(currency)") }
if suggPriceCandidates.count > 1 {
// Mehrere Beträge gefunden Auswahl anbieten.
Picker("Kaufpreis", selection: Binding(
get: { suggPrice ?? suggPriceCandidates.first ?? 0 },
set: { suggPrice = $0 }
)) {
ForEach(suggPriceCandidates, id: \.self) { c in
Text("\(ItemEditView.formatCents(c)) \(currency)").tag(c)
}
}
.pickerStyle(.menu)
} else if let p = suggPrice {
Text("Kaufpreis \(ItemEditView.formatCents(p)) \(currency)")
}
HStack {
Button("Übernehmen") { applySuggestion() }
Spacer()
Button("Verwerfen") { suggWarranty = nil; suggPrice = nil }
Button("Verwerfen") { verwerfeVorschlag() }
.foregroundStyle(.secondary)
}
.font(.callout)
@@ -289,6 +303,7 @@ struct ItemEditView: View {
itemId: item.id, data: data, filename: filename, contentType: contentType)
suggWarranty = res.suggestedWarrantyUntil
suggPrice = res.suggestedPriceCents
suggPriceCandidates = res.suggestedPriceCandidates
await reloadDocuments()
} catch { self.error = error.localizedDescription }
}
@@ -319,8 +334,13 @@ struct ItemEditView: View {
private func applySuggestion() {
if let w = suggWarranty, let d = stringToDate(w) { warranty = d; hasWarranty = true }
if let p = suggPrice { priceText = ItemEditView.formatCents(p) }
verwerfeVorschlag()
}
private func verwerfeVorschlag() {
suggWarranty = nil
suggPrice = nil
suggPriceCandidates = []
}
/// Eingabe in Hauptwährungseinheit Rappen/Cent.

View File

@@ -635,11 +635,21 @@ struct ItemDocumentUpload: Codable {
let id: Int
let suggestedWarrantyUntil: String?
let suggestedPriceCents: Int?
let suggestedPriceCandidates: [Int]
enum CodingKeys: String, CodingKey {
case id
case suggestedWarrantyUntil = "suggested_warranty_until"
case suggestedPriceCents = "suggested_price_cents"
case suggestedPriceCandidates = "suggested_price_candidates"
}
init(from decoder: Decoder) throws {
let c = try decoder.container(keyedBy: CodingKeys.self)
id = try c.decode(Int.self, forKey: .id)
suggestedWarrantyUntil = try c.decodeIfPresent(String.self, forKey: .suggestedWarrantyUntil)
suggestedPriceCents = try c.decodeIfPresent(Int.self, forKey: .suggestedPriceCents)
suggestedPriceCandidates = try c.decodeIfPresent([Int].self, forKey: .suggestedPriceCandidates) ?? []
}
}