Final commit.
This commit is contained in:
99
lib/widget/scanner/item_matcher.dart
Normal file
99
lib/widget/scanner/item_matcher.dart
Normal file
@ -0,0 +1,99 @@
|
||||
import 'package:hl_lieferservice/domain/entity/delivery.dart';
|
||||
import 'package:hl_lieferservice/domain/entity/delivery_item.dart';
|
||||
import 'package:hl_lieferservice/domain/entity/tour_details.dart';
|
||||
|
||||
/// Ergebnis der Item-Auflösung beim Scan. Sealed, damit das UI per
|
||||
/// `switch` exhaustiv pro Fall eine passende Meldung zeigt.
|
||||
sealed class ItemMatch {
|
||||
const ItemMatch();
|
||||
const factory ItemMatch.ok(DeliveryItem item) = ItemMatchOk;
|
||||
const factory ItemMatch.notInDelivery() = ItemMatchNotInDelivery;
|
||||
const factory ItemMatch.notScannable() = ItemMatchNotScannable;
|
||||
const factory ItemMatch.allDone() = ItemMatchAllDone;
|
||||
const factory ItemMatch.allRemoved() = ItemMatchAllRemoved;
|
||||
const factory ItemMatch.notOpen() = ItemMatchNotOpen;
|
||||
}
|
||||
|
||||
class ItemMatchOk extends ItemMatch {
|
||||
const ItemMatchOk(this.item);
|
||||
final DeliveryItem item;
|
||||
}
|
||||
|
||||
class ItemMatchNotInDelivery extends ItemMatch {
|
||||
const ItemMatchNotInDelivery();
|
||||
}
|
||||
|
||||
class ItemMatchNotScannable extends ItemMatch {
|
||||
const ItemMatchNotScannable();
|
||||
}
|
||||
|
||||
class ItemMatchAllDone extends ItemMatch {
|
||||
const ItemMatchAllDone();
|
||||
}
|
||||
|
||||
class ItemMatchAllRemoved extends ItemMatch {
|
||||
const ItemMatchAllRemoved();
|
||||
}
|
||||
|
||||
class ItemMatchNotOpen extends ItemMatch {
|
||||
const ItemMatchNotOpen();
|
||||
}
|
||||
|
||||
/// Findet in der Lieferung das erste **nicht fertig** gescannte Item mit der
|
||||
/// gegebenen Article-Nummer. „Top-down"-Strategie: hat eine Lieferung zwei
|
||||
/// Belegzeilen mit demselben Artikel (z. B. 20 + 10), wird zuerst die
|
||||
/// niedrigere Belegzeile gefüllt. Erst wenn diese fertig ist, „rollt" der
|
||||
/// nächste Scan auf die zweite Zeile weiter.
|
||||
///
|
||||
/// [itemFilter] schränkt die betrachteten Positionen ein — z. B. nur
|
||||
/// Filial-Items in der Abhol-Phase. Ohne Filter werden alle Positionen
|
||||
/// berücksichtigt (Beladen-Phase).
|
||||
///
|
||||
/// Klassifiziert den Misserfolgs-Grund (nicht scanbar / bereits fertig /
|
||||
/// entfernt …), damit das UI dem Fahrer eine sinnvolle Meldung zeigt.
|
||||
ItemMatch matchItem({
|
||||
required Delivery delivery,
|
||||
required TourDetails details,
|
||||
required String articleNumber,
|
||||
bool Function(DeliveryItem item)? itemFilter,
|
||||
}) {
|
||||
final normalized = articleNumber.trim();
|
||||
final candidates = delivery.items
|
||||
.where((it) => itemFilter?.call(it) ?? true)
|
||||
.toList()
|
||||
..sort((a, b) => a.belegzeilenNr.compareTo(b.belegzeilenNr));
|
||||
final matchingArticle = candidates.where((it) {
|
||||
final art = details.articleOf(it.articleId);
|
||||
return art?.articleNumber == normalized;
|
||||
}).toList();
|
||||
|
||||
if (matchingArticle.isEmpty) {
|
||||
return const ItemMatch.notInDelivery();
|
||||
}
|
||||
|
||||
// Erstes Item, das wir tatsächlich scannen können.
|
||||
for (final item in matchingArticle) {
|
||||
if (item.isDone || item.isRemoved) continue;
|
||||
final article = details.articleOf(item.articleId);
|
||||
if (article == null || !article.scannable) continue;
|
||||
return ItemMatch.ok(item);
|
||||
}
|
||||
|
||||
// Kein scannbares offenes Item — Grund anhand der Item-Verteilung
|
||||
// klassifizieren, damit das UI eine sinnvolle Meldung zeigt.
|
||||
final allNotScannable = matchingArticle.every((it) {
|
||||
final art = details.articleOf(it.articleId);
|
||||
return art == null || !art.scannable;
|
||||
});
|
||||
if (allNotScannable) return const ItemMatch.notScannable();
|
||||
|
||||
final allRemoved = matchingArticle.every((it) => it.isRemoved);
|
||||
if (allRemoved) return const ItemMatch.allRemoved();
|
||||
|
||||
final allDone = matchingArticle.every((it) => it.isDone);
|
||||
if (allDone) return const ItemMatch.allDone();
|
||||
|
||||
// Gemischte Konstellation (z. B. eine Zeile entfernt, eine fertig) —
|
||||
// praktisch selten; konservativ als „nicht (mehr) offen" melden.
|
||||
return const ItemMatch.notOpen();
|
||||
}
|
||||
Reference in New Issue
Block a user