feat(delivery): Abschluss-Navigation, Mengen-Hinweis, Set-Handling
A) Nach erfolgreichem Abschluss (aktiv→completed) poppt die Detail-Page automatisch zurück zur Übersicht. Scaffold ist jetzt StatefulWidget mit BlocListener<TourBloc>; nur „gearmt", wenn die Lieferung beim Öffnen aktiv war → erneutes Öffnen einer fertigen Lieferung poppt nicht. B) Step „Info": Artikelliste zeigt weiter die Ursprungsmenge (requiredQuantity). Bei entfernten/teilweise gutgeschriebenen Positionen erscheint pro Zeile ein „Menge geändert"-Hinweis + ein tappbares Banner, das zu Step 3 „Artikel" springt. C) Beladen: nicht-scanbare Set-Köpfe (Parent-Komponenten) werden jetzt IMMER mit ihrem Set gezeigt — als Kopf in der Lagergruppe ihrer Komponenten statt isoliert unter „Dienstleistungen". _ItemRow leitet scanNotRequired aus der Artikel-Scanbarkeit ab. D) Step „Übersicht": Wording der Zahlungsweise-Sperre bei offen==0 präzisiert („Keine Zahlung mehr offen (bereits bezahlt)"). E) Step „Artikel": Komponenten eines Sets sind einzeln nicht mehr entfernbar (kein Button + Hinweis). Das Entfernen/Wiederherstellen läuft nur über den Oberartikel und kaskadiert auf das ganze Set (ganz oder gar nix). Set-Entfernen ist blockiert, solange eine Komponente noch nicht verladen ist. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@ -535,6 +535,39 @@ class _CustomerBody extends StatelessWidget {
|
||||
// erkennt. Liegen außerhalb von `groups` (die nur scanbare Items führen).
|
||||
final serviceItems = details.nonScannableItems(delivery).toList();
|
||||
|
||||
// ── Set-Köpfe (Parent-Komponenten) immer mit ihrem Set anzeigen ──────
|
||||
// Ein nicht-scanbarer Set-Kopf (Stücklisten-Oberartikel, z. B. als
|
||||
// Pauschale geführt) soll NICHT isoliert unter „Dienstleistungen"
|
||||
// erscheinen, sondern als Kopf über seinen (scanbaren) Komponenten in der
|
||||
// jeweiligen Lagergruppe. Ein Item ist Set-Kopf, wenn seine Artikelnummer
|
||||
// von einer Komponente als parentArtikelNr referenziert wird.
|
||||
String? artNrOf(DeliveryItem it) =>
|
||||
details.articleOf(it.articleId)?.articleNumber;
|
||||
// Set-Köpfe je Lagergruppe (warehouseId → einzuhängende Köpfe) +
|
||||
// gesammelte IDs, um sie aus der Dienstleistungs-Sektion zu entfernen.
|
||||
final injectedParentsByWarehouseId = <String, List<DeliveryItem>>{};
|
||||
final injectedParentIds = <String>{};
|
||||
for (final group in groups) {
|
||||
final groupParentNrs = group.items
|
||||
.where((it) => it.isComponent)
|
||||
.map((it) => it.parentArtikelNr)
|
||||
.whereType<String>()
|
||||
.toSet();
|
||||
if (groupParentNrs.isEmpty) continue;
|
||||
final parents = serviceItems
|
||||
.where((s) => groupParentNrs.contains(artNrOf(s)))
|
||||
.toList();
|
||||
if (parents.isEmpty) continue;
|
||||
injectedParentsByWarehouseId[group.warehouse.id] = parents;
|
||||
injectedParentIds.addAll(parents.map((p) => p.id));
|
||||
}
|
||||
// Set-Köpfe, die in eine Gruppe eingehängt wurden, nicht doppelt unter
|
||||
// „Dienstleistungen" zeigen. Set-Köpfe ohne scanbare Komponenten (kommen
|
||||
// hier nicht vor) blieben weiterhin in der Liste.
|
||||
final serviceItemsView = serviceItems
|
||||
.where((s) => !injectedParentIds.contains(s.id))
|
||||
.toList();
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
// Header bekommt einen eigenen, leicht abgehobenen Hintergrund
|
||||
@ -627,26 +660,30 @@ class _CustomerBody extends StatelessWidget {
|
||||
warehouse: group.warehouse,
|
||||
items: group.items,
|
||||
),
|
||||
for (final item in _parentFirst(group.items))
|
||||
// Nicht-scanbare Set-Köpfe dieser Gruppe vor ihre Komponenten
|
||||
// einhängen; `_parentFirst` ordnet Kopf vor Komponenten.
|
||||
for (final item in _parentFirst([
|
||||
...?injectedParentsByWarehouseId[group.warehouse.id],
|
||||
...group.items,
|
||||
]))
|
||||
_ItemRow(
|
||||
item: item,
|
||||
details: details,
|
||||
onAction: (action) => onItemAction(item, action),
|
||||
),
|
||||
],
|
||||
// Gebuchte Dienstleistungen (nicht-scanbare Positionen): eigene
|
||||
// Zwischenüberschrift „Dienstleistungen" (optisch wie
|
||||
// Standardlager) und dieselbe Item-Card wie scanbare Artikel —
|
||||
// einziger Unterschied ist der Hinweis, dass kein Scanvorgang
|
||||
// nötig ist (`scanNotRequired`).
|
||||
if (serviceItems.isNotEmpty) ...[
|
||||
// Gebuchte Dienstleistungen (nicht-scanbare Positionen ohne
|
||||
// eigene Set-Komponenten): eigene Zwischenüberschrift
|
||||
// „Dienstleistungen" (optisch wie Standardlager) und dieselbe
|
||||
// Item-Card wie scanbare Artikel — einziger Unterschied ist der
|
||||
// Hinweis, dass kein Scanvorgang nötig ist.
|
||||
if (serviceItemsView.isNotEmpty) ...[
|
||||
const _ServiceSectionHeader(),
|
||||
for (final item in _parentFirst(serviceItems))
|
||||
for (final item in _parentFirst(serviceItemsView))
|
||||
_ItemRow(
|
||||
item: item,
|
||||
details: details,
|
||||
onAction: (action) => onItemAction(item, action),
|
||||
scanNotRequired: true,
|
||||
),
|
||||
],
|
||||
],
|
||||
@ -1111,24 +1148,23 @@ class _ItemRow extends StatelessWidget {
|
||||
required this.item,
|
||||
required this.details,
|
||||
required this.onAction,
|
||||
this.scanNotRequired = false,
|
||||
});
|
||||
|
||||
final DeliveryItem item;
|
||||
final TourDetails details;
|
||||
final void Function(_ItemAction action) onAction;
|
||||
|
||||
/// `true` für gebuchte Dienstleistungen (nicht-scanbare Positionen): die
|
||||
/// Card wird GENAU SO wie ein Standardlager-Artikel gerendert, bekommt aber
|
||||
/// unten den Hinweis „Kein Scanvorgang notwendig". Der Manuell-Button
|
||||
/// entfällt bei nicht-scanbaren Positionen ohnehin (`canManualConfirm`).
|
||||
final bool scanNotRequired;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final article = details.articleOf(item.articleId);
|
||||
final warehouse = details.warehouseOf(item.warehouseId);
|
||||
// `true` für nicht-scanbare Positionen (Dienstleistungen / Pauschalen /
|
||||
// Set-Köpfe): die Card wird GENAU SO wie ein scanbarer Artikel gerendert,
|
||||
// bekommt aber unten den Hinweis „Kein Scanvorgang notwendig" und keinen
|
||||
// Mengen-Zähler. Aus dem Artikel abgeleitet, damit ein in eine Lagergruppe
|
||||
// eingehängter nicht-scanbarer Set-Kopf automatisch korrekt rendert.
|
||||
final scanNotRequired = !(article?.scannable ?? false);
|
||||
final isExternalWarehouse = warehouse != null && !warehouse.isStandard;
|
||||
// Manueller Fallback-Button: nur für scanbare, noch offene Positionen
|
||||
// (nicht done/entfernt/pausiert) — analog dazu, was ein Barcode-Scan
|
||||
|
||||
Reference in New Issue
Block a user