MHD per Texterkennung einlesen

In der Chargenzeile beim Einlagern gibt es einen Knopf "MHD scannen". Er
oeffnet die Texterkennung von VisionKit; erkannte Daten erscheinen als
Vorschlag und werden erst uebernommen, wenn man sie antippt. Bewusst kein
automatisches Uebernehmen: Eine Fehllesung beim MHD faellt sonst erst auf,
wenn die Ware bereits falsch einsortiert ist.

Der Parser deckt die Schreibweisen ab, die auf Verpackungen vorkommen -
Tagesdaten (12.09.2026, 2026-09-12, 12/09/2026, 12.09.26) und Monatsangaben
(09/2026, 09.2026, 2026-09, 09/26). Erkennt der Aufdruck nur Monat und Jahr,
stellt sich die Eingabe sichtbar auf Monat/Jahr um.

Damit aus Chargennummern und EAN-Codes keine Phantasiedaten werden, greifen
mehrere Einschraenkungen: Muster mit Tag werden zuerst geprueft, ein bereits
erkannter Textabschnitt wird nicht erneut ausgewertet, Monate ausserhalb 1-12
entfallen und das Jahr muss im Fenster von fuenf Jahren rueckwaerts bis
dreissig vorwaerts liegen.

Der Knopf erscheint nur, wenn das Geraet Texterkennung unterstuetzt
(Neural Engine); sonst bleibt die Eingabe von Hand.

Getestet: Der Parser wurde eigenstaendig uebersetzt und gegen 14 Faelle
geprueft, alle korrekt - darunter, dass "12.09.2026" nicht als Monatsangabe
gelesen wird, dass eine EAN keine Treffer erzeugt und dass unplausible Jahre
und Monate verworfen werden. Der iOS-Geraetebuild laeuft fehlerfrei durch.
Die Erkennung an einer echten Verpackung habe ich nicht selbst ausprobiert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-07-22 18:10:24 +02:00
parent f682993b62
commit c4acc8bec7
5 changed files with 398 additions and 0 deletions

View File

@@ -1,5 +1,10 @@
import SwiftUI
/// Damit sich eine UUID direkt als sheet(item:) verwenden laesst.
extension UUID: Identifiable {
public var id: UUID { self }
}
extension Date {
/// Erster Tag des Monats - so wird eine Monatsangabe an den Server geschickt.
var startOfMonth: Date {
@@ -77,6 +82,8 @@ struct CheckInFormView: View {
/// Gilt fuer den ganzen Vorgang: Auf einer Packung steht entweder ein
/// Tagesdatum oder nur Monat/Jahr - nicht beides gemischt.
@State private var precision: String = "day"
/// Charge, fuer die gerade das MHD abgescannt wird.
@State private var scanLineId: UUID?
@State private var unit: String = ""
@State private var locationId: Int?
@State private var busy = false
@@ -156,6 +163,15 @@ struct CheckInFormView: View {
} else {
DatePicker("MHD", selection: $line.bestBefore, displayedComponents: .date)
}
if DateScanView.isSupported {
Button {
scanLineId = line.id
} label: {
Label("MHD scannen", systemImage: "text.viewfinder")
.font(.callout)
}
.buttonStyle(.borderless)
}
}
}
.padding(.vertical, 2)
@@ -181,6 +197,19 @@ struct CheckInFormView: View {
.toolbar {
ToolbarItem(placement: .topBarLeading) { Button("Abbrechen") { dismiss() } }
}
.sheet(item: $scanLineId) { id in
NavigationStack {
DateScanView { datum, erkanntePraezision in
// Nennt der Aufdruck keinen Tag, stellt sich die Eingabe
// sichtbar auf Monat/Jahr um.
if erkanntePraezision == "month" { precision = "month" }
if let index = lines.firstIndex(where: { $0.id == id }) {
lines[index].bestBefore = datum
lines[index].hasDate = true
}
}
}
}
.onAppear {
// Gibt es ein Gebinde, ist das die naheliegende Eingabe.
unit = packageOptionName ?? product.unitName