Added components to article
This commit is contained in:
@ -13,6 +13,7 @@ import 'package:hl_lieferservice/feature/scan/presentation/scanner.dart';
|
||||
import 'package:hl_lieferservice/feature/settings/bloc/settings_bloc.dart';
|
||||
import 'package:hl_lieferservice/feature/settings/bloc/settings_state.dart';
|
||||
import 'package:hl_lieferservice/model/article.dart';
|
||||
import 'package:hl_lieferservice/model/component.dart';
|
||||
import 'package:hl_lieferservice/model/delivery.dart';
|
||||
import 'package:hl_lieferservice/model/tour.dart';
|
||||
import 'package:hl_lieferservice/widget/home/bloc/navigation_bloc.dart';
|
||||
@ -24,37 +25,42 @@ import 'package:hl_lieferservice/widget/operations/bloc/operation_event.dart';
|
||||
// Data helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
class _ArticleDeliveryEntry {
|
||||
class _DeliveryGroup {
|
||||
final Delivery delivery;
|
||||
final Article article;
|
||||
final String? carPlate;
|
||||
final List<Article> articles;
|
||||
|
||||
const _ArticleDeliveryEntry({
|
||||
const _DeliveryGroup({
|
||||
required this.delivery,
|
||||
required this.article,
|
||||
required this.articles,
|
||||
this.carPlate,
|
||||
});
|
||||
}
|
||||
|
||||
class _ArticleGroup {
|
||||
final String articleNumber;
|
||||
final String name;
|
||||
final int totalAmount;
|
||||
final int totalScanned;
|
||||
final int totalRemoved;
|
||||
final List<_ArticleDeliveryEntry> entries;
|
||||
int get totalArticles => articles.length;
|
||||
|
||||
bool get isComplete => totalScanned + totalRemoved >= totalAmount;
|
||||
int get scannedOrRemoved => totalScanned + totalRemoved;
|
||||
int get completeArticles => articles
|
||||
.where((a) => a.isFullyScanned)
|
||||
.length;
|
||||
|
||||
const _ArticleGroup({
|
||||
required this.articleNumber,
|
||||
required this.name,
|
||||
required this.totalAmount,
|
||||
required this.totalScanned,
|
||||
required this.totalRemoved,
|
||||
required this.entries,
|
||||
});
|
||||
int get totalUnits => articles.fold(0, (sum, a) {
|
||||
if (a.isParent && a.components.isNotEmpty) {
|
||||
return sum + a.components.fold(0, (s, c) => s + c.requiredAmount);
|
||||
}
|
||||
return sum + a.amount;
|
||||
});
|
||||
|
||||
int get scannedUnits => articles.fold(0, (sum, a) {
|
||||
if (a.isParent && a.components.isNotEmpty) {
|
||||
return sum + a.components.fold(0, (s, c) => s + c.scannedAmount);
|
||||
}
|
||||
return sum + a.scannedAmount + a.scannedRemovedAmount;
|
||||
});
|
||||
|
||||
bool get isComplete => totalArticles > 0 && completeArticles == totalArticles;
|
||||
|
||||
bool get hasAnyScanned => scannedUnits > 0;
|
||||
|
||||
bool get isPartial => hasAnyScanned && !isComplete;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@ -128,6 +134,8 @@ class _ScanPageState extends State<ScanPage> with SingleTickerProviderStateMixin
|
||||
/// `<artikelnummer>;<kundennummer>;<belegnummer>`.
|
||||
/// Liefert `null`, wenn der Barcode dem erwarteten Format nicht entspricht.
|
||||
String? _extractArticleNumber(String barcode) {
|
||||
debugPrint("QR CODE: $barcode");
|
||||
|
||||
final parts = barcode.split(';');
|
||||
if (parts.length != 3) return null;
|
||||
final articleNumber = parts[0].trim();
|
||||
@ -156,10 +164,43 @@ class _ScanPageState extends State<ScanPage> with SingleTickerProviderStateMixin
|
||||
final tourState = context.read<TourBloc>().state;
|
||||
if (tourState is! TourLoaded) return;
|
||||
|
||||
// ── 1. Try component match first (Stückliste) ──
|
||||
final componentDeliveries = tourState.tour.deliveries
|
||||
.where((d) => d.state != DeliveryState.finished)
|
||||
.where((d) {
|
||||
final parent = d.findParentOfComponent(articleNumber);
|
||||
if (parent == null) return false;
|
||||
final comp = parent.findComponent(articleNumber);
|
||||
return comp != null && !comp.isFullyScanned;
|
||||
})
|
||||
.toList();
|
||||
|
||||
if (componentDeliveries.isNotEmpty) {
|
||||
if (componentDeliveries.length == 1) {
|
||||
setState(() => _isScanning = true);
|
||||
context.read<TourBloc>().add(ScanComponentEvent(
|
||||
componentArticleNumber: articleNumber,
|
||||
carId: _selectedCarId!.toString(),
|
||||
deliveryId: componentDeliveries.first.id,
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
_showCustomerSelectionSheet(
|
||||
articleNumber,
|
||||
componentDeliveries,
|
||||
tourState.tour,
|
||||
isComponent: true,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// ── 2. Regular article scan ──
|
||||
final needingDeliveries = tourState.tour.deliveries
|
||||
.where((d) => d.state != DeliveryState.finished)
|
||||
.where((d) => d.articles.any((a) =>
|
||||
a.articleNumber == articleNumber &&
|
||||
!a.isParent &&
|
||||
a.scannedAmount + a.scannedRemovedAmount < a.amount))
|
||||
.toList();
|
||||
|
||||
@ -189,8 +230,9 @@ class _ScanPageState extends State<ScanPage> with SingleTickerProviderStateMixin
|
||||
void _showCustomerSelectionSheet(
|
||||
String articleNumber,
|
||||
List<Delivery> deliveries,
|
||||
Tour tour,
|
||||
) {
|
||||
Tour tour, {
|
||||
bool isComponent = false,
|
||||
}) {
|
||||
final tourBloc = context.read<TourBloc>();
|
||||
final carId = _selectedCarId!;
|
||||
|
||||
@ -244,11 +286,19 @@ class _ScanPageState extends State<ScanPage> with SingleTickerProviderStateMixin
|
||||
onTap: () {
|
||||
Navigator.pop(ctx);
|
||||
setState(() => _isScanning = true);
|
||||
tourBloc.add(ScanArticleEvent(
|
||||
articleNumber: articleNumber,
|
||||
carId: carId.toString(),
|
||||
deliveryId: delivery.id,
|
||||
));
|
||||
if (isComponent) {
|
||||
tourBloc.add(ScanComponentEvent(
|
||||
componentArticleNumber: articleNumber,
|
||||
carId: carId.toString(),
|
||||
deliveryId: delivery.id,
|
||||
));
|
||||
} else {
|
||||
tourBloc.add(ScanArticleEvent(
|
||||
articleNumber: articleNumber,
|
||||
carId: carId.toString(),
|
||||
deliveryId: delivery.id,
|
||||
));
|
||||
}
|
||||
},
|
||||
);
|
||||
}),
|
||||
@ -269,37 +319,23 @@ class _ScanPageState extends State<ScanPage> with SingleTickerProviderStateMixin
|
||||
return tour.driver.cars.firstWhereOrNull((c) => c.id == carId)?.plate;
|
||||
}
|
||||
|
||||
List<_ArticleGroup> _buildArticleGroups(Tour tour) {
|
||||
final Map<String, List<_ArticleDeliveryEntry>> grouped = {};
|
||||
List<_DeliveryGroup> _buildDeliveryGroups(Tour tour) {
|
||||
final List<_DeliveryGroup> groups = [];
|
||||
|
||||
for (final delivery in tour.deliveries) {
|
||||
if (delivery.state == DeliveryState.finished) continue;
|
||||
final carPlate = _lookupCarPlate(delivery.carId, tour);
|
||||
for (final article in delivery.articles) {
|
||||
if (!article.scannable) continue;
|
||||
grouped.putIfAbsent(article.articleNumber, () => []);
|
||||
grouped[article.articleNumber]!.add(
|
||||
_ArticleDeliveryEntry(
|
||||
delivery: delivery,
|
||||
article: article,
|
||||
carPlate: carPlate,
|
||||
),
|
||||
);
|
||||
}
|
||||
final scannableArticles =
|
||||
delivery.articles.where((a) => a.scannable).toList();
|
||||
if (scannableArticles.isEmpty) continue;
|
||||
|
||||
groups.add(_DeliveryGroup(
|
||||
delivery: delivery,
|
||||
articles: scannableArticles,
|
||||
carPlate: _lookupCarPlate(delivery.carId, tour),
|
||||
));
|
||||
}
|
||||
|
||||
return grouped.entries.map((e) {
|
||||
final entries = e.value;
|
||||
return _ArticleGroup(
|
||||
articleNumber: e.key,
|
||||
name: entries.first.article.name,
|
||||
totalAmount: entries.fold(0, (sum, e) => sum + e.article.amount),
|
||||
totalScanned: entries.fold(0, (sum, e) => sum + e.article.scannedAmount),
|
||||
totalRemoved: entries.fold(0, (sum, e) => sum + e.article.scannedRemovedAmount),
|
||||
entries: entries,
|
||||
);
|
||||
}).toList()
|
||||
..sort((a, b) => a.name.compareTo(b.name));
|
||||
return groups;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@ -335,7 +371,7 @@ class _ScanPageState extends State<ScanPage> with SingleTickerProviderStateMixin
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildProgressHeader(List<_ArticleGroup> allGroups) {
|
||||
Widget _buildProgressHeader(List<_DeliveryGroup> allGroups) {
|
||||
final total = allGroups.length;
|
||||
final done = allGroups.where((g) => g.isComplete).length;
|
||||
final progress = total > 0 ? done / total : 0.0;
|
||||
@ -355,7 +391,7 @@ class _ScanPageState extends State<ScanPage> with SingleTickerProviderStateMixin
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"$done / $total Artikel",
|
||||
"$done / $total Kunden",
|
||||
style: Theme.of(context).textTheme.titleSmall?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
@ -382,14 +418,9 @@ class _ScanPageState extends State<ScanPage> with SingleTickerProviderStateMixin
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildArticleTile(_ArticleGroup group, {int? carIdFilter}) {
|
||||
Widget _buildDeliveryTile(_DeliveryGroup group) {
|
||||
final isComplete = group.isComplete;
|
||||
final isPartial = group.scannedOrRemoved > 0 && !isComplete;
|
||||
final entries = carIdFilter != null
|
||||
? group.entries
|
||||
.where((e) => e.delivery.carId == carIdFilter)
|
||||
.toList()
|
||||
: group.entries;
|
||||
final isPartial = group.isPartial;
|
||||
|
||||
final Color cardColor;
|
||||
final Color borderColor;
|
||||
@ -434,11 +465,11 @@ class _ScanPageState extends State<ScanPage> with SingleTickerProviderStateMixin
|
||||
key: const ValueKey('done'),
|
||||
)
|
||||
: SizedBox(
|
||||
width: 32,
|
||||
width: 36,
|
||||
key: const ValueKey('progress'),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'${group.scannedOrRemoved}/${group.totalAmount}',
|
||||
'${group.completeArticles}/${group.totalArticles}',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
@ -449,73 +480,154 @@ class _ScanPageState extends State<ScanPage> with SingleTickerProviderStateMixin
|
||||
),
|
||||
),
|
||||
title: Text(
|
||||
group.name,
|
||||
group.delivery.customer.name,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: titleColor,
|
||||
),
|
||||
),
|
||||
subtitle: Text(
|
||||
"Artikelnr. ${group.articleNumber}",
|
||||
group.delivery.customer.address.toString(),
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
trailing: group.carPlate != null
|
||||
? _carBadge(context, group.carPlate!)
|
||||
: null,
|
||||
children: [
|
||||
const Divider(height: 1, indent: 16, endIndent: 16),
|
||||
...entries.map(_buildDeliveryEntry),
|
||||
...group.articles.map(_buildArticleEntry),
|
||||
const SizedBox(height: 4),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDeliveryEntry(_ArticleDeliveryEntry entry) {
|
||||
final article = entry.article;
|
||||
final customer = entry.delivery.customer;
|
||||
final entryDone =
|
||||
article.scannedAmount + article.scannedRemovedAmount >= article.amount;
|
||||
Widget _buildArticleEntry(Article article) {
|
||||
if (article.isParent && article.components.isNotEmpty) {
|
||||
return _buildParentArticleEntry(article);
|
||||
}
|
||||
|
||||
final entryDone = article.isFullyScanned;
|
||||
|
||||
return ListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24, vertical: 2),
|
||||
leading: Icon(
|
||||
entryDone ? Icons.check_circle_outline : Icons.person_outline,
|
||||
entryDone ? Icons.check_circle_outline : Icons.inventory_2_outlined,
|
||||
color: entryDone
|
||||
? Colors.green
|
||||
: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
size: 20,
|
||||
),
|
||||
title: Text(
|
||||
customer.name,
|
||||
article.name,
|
||||
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
|
||||
),
|
||||
subtitle: Text(
|
||||
customer.address.toString(),
|
||||
"Artikelnr. ${article.articleNumber}",
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
trailing: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
if (entry.carPlate != null) ...[
|
||||
_carBadge(context, entry.carPlate!),
|
||||
const SizedBox(height: 4),
|
||||
],
|
||||
Text(
|
||||
'${article.scannedAmount + article.scannedRemovedAmount} / ${article.amount}×',
|
||||
trailing: Text(
|
||||
'${article.scannedAmount + article.scannedRemovedAmount} / ${article.amount}×',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 13,
|
||||
color: entryDone
|
||||
? Colors.green
|
||||
: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Renders a parent article (Stückliste) with its components listed below.
|
||||
Widget _buildParentArticleEntry(Article article) {
|
||||
final allDone = article.isFullyScanned;
|
||||
final scannedCount =
|
||||
article.components.where((c) => c.isFullyScanned).length;
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ListTile(
|
||||
contentPadding:
|
||||
const EdgeInsets.symmetric(horizontal: 24, vertical: 2),
|
||||
leading: Icon(
|
||||
allDone
|
||||
? Icons.check_circle_outline
|
||||
: Icons.account_tree_outlined,
|
||||
color: allDone
|
||||
? Colors.green
|
||||
: Theme.of(context).colorScheme.primary,
|
||||
size: 20,
|
||||
),
|
||||
title: Text(
|
||||
article.name,
|
||||
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600),
|
||||
),
|
||||
subtitle: Text(
|
||||
"Stückliste · $scannedCount/${article.components.length} Komponenten",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 13,
|
||||
color: entryDone
|
||||
? Colors.green
|
||||
: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
fontSize: 12,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
trailing: Icon(
|
||||
allDone ? Icons.check_circle : Icons.pending_outlined,
|
||||
color: allDone ? Colors.green : Colors.orange,
|
||||
size: 18,
|
||||
),
|
||||
),
|
||||
...article.components.map(_buildComponentEntry),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// Single component row, indented below the parent article.
|
||||
Widget _buildComponentEntry(Component component) {
|
||||
final done = component.isFullyScanned;
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 32),
|
||||
child: ListTile(
|
||||
dense: true,
|
||||
contentPadding:
|
||||
const EdgeInsets.symmetric(horizontal: 24, vertical: 0),
|
||||
leading: Icon(
|
||||
done
|
||||
? Icons.check_circle_outline
|
||||
: Icons.radio_button_unchecked,
|
||||
color: done
|
||||
? Colors.green
|
||||
: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
size: 16,
|
||||
),
|
||||
title: Text(
|
||||
component.name,
|
||||
style: const TextStyle(fontSize: 13),
|
||||
),
|
||||
subtitle: Text(
|
||||
"Artikelnr. ${component.articleNumber}",
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
trailing: Text(
|
||||
'${component.scannedAmount} / ${component.requiredAmount}×',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 12,
|
||||
color: done
|
||||
? Colors.green
|
||||
: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -526,8 +638,8 @@ class _ScanPageState extends State<ScanPage> with SingleTickerProviderStateMixin
|
||||
|
||||
Widget _buildOpenTab(
|
||||
TourLoaded state,
|
||||
List<_ArticleGroup> openGroups,
|
||||
List<_ArticleGroup> allGroups,
|
||||
List<_DeliveryGroup> openGroups,
|
||||
List<_DeliveryGroup> allGroups,
|
||||
bool useHardwareScanner,
|
||||
) {
|
||||
return Column(
|
||||
@ -535,7 +647,31 @@ class _ScanPageState extends State<ScanPage> with SingleTickerProviderStateMixin
|
||||
if (_isScanning)
|
||||
const LinearProgressIndicator(),
|
||||
if (!useHardwareScanner && openGroups.isNotEmpty)
|
||||
BarcodeScannerWidget(onBarcodeDetected: _handleBarcodeScanned),
|
||||
Stack(
|
||||
children: [
|
||||
BarcodeScannerWidget(onBarcodeDetected: _handleBarcodeScanned),
|
||||
if (state.pendingScanRequests > 0)
|
||||
Positioned(
|
||||
top: 8,
|
||||
right: 8,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(6),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black54,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: const SizedBox(
|
||||
width: 18,
|
||||
height: 18,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
valueColor: AlwaysStoppedAnimation(Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
_buildProgressHeader(allGroups),
|
||||
const Divider(height: 1),
|
||||
Expanded(
|
||||
@ -551,7 +687,7 @@ class _ScanPageState extends State<ScanPage> with SingleTickerProviderStateMixin
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
const Text(
|
||||
"Alle Artikel geladen!",
|
||||
"Alle Kunden vollständig beladen!",
|
||||
style: TextStyle(fontSize: 16),
|
||||
),
|
||||
],
|
||||
@ -561,14 +697,14 @@ class _ScanPageState extends State<ScanPage> with SingleTickerProviderStateMixin
|
||||
padding: const EdgeInsets.only(top: 8, bottom: 96),
|
||||
itemCount: openGroups.length,
|
||||
itemBuilder: (context, index) =>
|
||||
_buildArticleTile(openGroups[index]),
|
||||
_buildDeliveryTile(openGroups[index]),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLoadedTab(List<_ArticleGroup> loadedGroups) {
|
||||
Widget _buildLoadedTab(List<_DeliveryGroup> loadedGroups) {
|
||||
if (_selectedCarId == null) {
|
||||
return const Center(child: Text("Kein Fahrzeug ausgewählt"));
|
||||
}
|
||||
@ -585,7 +721,7 @@ class _ScanPageState extends State<ScanPage> with SingleTickerProviderStateMixin
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
"Noch keine Artikel im Auto",
|
||||
"Noch keine Kunden im Auto",
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
@ -599,10 +735,8 @@ class _ScanPageState extends State<ScanPage> with SingleTickerProviderStateMixin
|
||||
return ListView.builder(
|
||||
padding: const EdgeInsets.only(top: 8, bottom: 96),
|
||||
itemCount: loadedGroups.length,
|
||||
itemBuilder: (context, index) => _buildArticleTile(
|
||||
loadedGroups[index],
|
||||
carIdFilter: _selectedCarId,
|
||||
),
|
||||
itemBuilder: (context, index) =>
|
||||
_buildDeliveryTile(loadedGroups[index]),
|
||||
);
|
||||
}
|
||||
|
||||
@ -617,7 +751,7 @@ class _ScanPageState extends State<ScanPage> with SingleTickerProviderStateMixin
|
||||
builder: (context, carState) {
|
||||
return BlocConsumer<TourBloc, TourState>(
|
||||
listener: (context, tourState) {
|
||||
if (tourState is TourLoaded) {
|
||||
if (tourState is TourLoaded && tourState.pendingScanRequests == 0) {
|
||||
setState(() => _isScanning = false);
|
||||
}
|
||||
},
|
||||
@ -641,20 +775,19 @@ class _ScanPageState extends State<ScanPage> with SingleTickerProviderStateMixin
|
||||
));
|
||||
}
|
||||
|
||||
final allGroups = _buildArticleGroups(tourState.tour);
|
||||
final allGroups = _buildDeliveryGroups(tourState.tour);
|
||||
|
||||
// Offen: mindestens ein Kundeneintrag ist noch nicht vollständig gescannt
|
||||
final openGroups = allGroups.where((g) => g.entries.any((e) =>
|
||||
e.article.scannedAmount + e.article.scannedRemovedAmount <
|
||||
e.article.amount,
|
||||
)).toList();
|
||||
// Offen: Lieferung hat noch mindestens einen nicht vollständig
|
||||
// gescannten Artikel (über alle Autos hinweg).
|
||||
final openGroups =
|
||||
allGroups.where((g) => !g.isComplete).toList();
|
||||
|
||||
// Im Auto: mindestens ein Kundeneintrag für das aktuelle Auto ist vollständig
|
||||
final loadedGroups = allGroups.where((g) => g.entries.any((e) =>
|
||||
e.delivery.carId == _selectedCarId &&
|
||||
e.article.scannedAmount + e.article.scannedRemovedAmount >=
|
||||
e.article.amount,
|
||||
)).toList();
|
||||
// Im Auto: Lieferung des aktuellen Autos, bei der mindestens ein
|
||||
// Stück gescannt wurde.
|
||||
final loadedGroups = allGroups
|
||||
.where((g) =>
|
||||
g.delivery.carId == _selectedCarId && g.hasAnyScanned)
|
||||
.toList();
|
||||
|
||||
final allDone = tourState.tour.deliveries.isNotEmpty &&
|
||||
openGroups.isEmpty;
|
||||
|
||||
Reference in New Issue
Block a user