fix(articles): Mengen-Stepper beim Set-Entfernen wieder aktivieren

Ein Set kann mehrfach bestellt sein (Oberartikel-Menge = Set-Anzahl),
daher bleibt der Mengen-Stepper beim Entfernen über den Oberartikel
erhalten. Die gewählte Set-Anzahl kaskadiert PROPORTIONAL auf die
Komponenten (Stückzahl je Set × entfernte Sets, geklemmt auf Restmenge)
— funktioniert für 1:1-Mengen wie für Komponenten mit Stückzahl je Set.
Einzelne Komponenten bleiben weiterhin nicht direkt entfernbar.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dennis Nemec
2026-06-23 16:04:58 +02:00
parent 4c6bef6897
commit 7e345bd71b

View File

@ -217,34 +217,51 @@ class _ArticleManagementRow extends StatelessWidget {
)); ));
} }
/// Entfernt das GANZE Set (Oberartikel + alle Komponenten) auf einmal — /// Entfernt eine Anzahl Sets über den Oberartikel und kaskadiert auf seine
/// „entweder Parent komplett oder gar nix". Kein Mengen-Stepper /// Komponenten. Der Mengen-Stepper bleibt erhalten — ein Set kann mehrfach
/// (`maxQuantity: null` ⇒ jede Zeile komplett). /// bestellt sein (Oberartikel-Menge = Set-Anzahl). Die Komponenten werden
Future<void> _openSetRemoveDialog(BuildContext context) async { /// **proportional** mitreduziert (Stückzahl je Set × entfernte Sets),
/// geklemmt auf die jeweilige Restmenge. Einzelne Komponenten bleiben
/// nicht direkt entfernbar — nur dieser Weg über den Oberartikel.
Future<void> _openSetRemoveDialog(
BuildContext context, {
required int remaining,
}) async {
final tourBloc = context.read<TourBloc>(); final tourBloc = context.read<TourBloc>();
final actorCarId = _actorCarId(context); final actorCarId = _actorCarId(context);
final result = await showReasonPickerSheet( final result = await showReasonPickerSheet(
context: context, context: context,
title: 'Grund für das Entfernen (ganzes Set)', title: 'Grund für das Entfernen',
presets: ReasonCatalog.itemRemove, presets: ReasonCatalog.itemRemove,
confirmLabel: 'Set entfernen', confirmLabel: 'Entfernen',
maxQuantity: null, maxQuantity: remaining,
); );
if (result == null) return; if (result == null) return;
// Oberartikel + jede Komponente komplett entfernen (quantity null). final n = result.quantity ?? remaining;
final parentRequired = item.requiredQuantity;
// Oberartikel um n Sets reduzieren.
tourBloc.add(RemoveItem( tourBloc.add(RemoveItem(
deliveryItemId: item.id, deliveryItemId: item.id,
reason: result.reason, reason: result.reason,
actorCarId: actorCarId, actorCarId: actorCarId,
quantity: null, quantity: n,
saveReasonAsNote: true, saveReasonAsNote: true,
)); ));
// Komponenten proportional mitreduzieren.
for (final c in components) { for (final c in components) {
final cRemaining = c.requiredQuantity - c.scanProgress.creditedQuantity;
if (cRemaining <= 0) continue;
final proportional = parentRequired > 0
? (c.requiredQuantity * n / parentRequired).round()
: cRemaining;
final removeQty = proportional.clamp(0, cRemaining);
if (removeQty <= 0) continue;
tourBloc.add(RemoveItem( tourBloc.add(RemoveItem(
deliveryItemId: c.id, deliveryItemId: c.id,
reason: result.reason, reason: result.reason,
actorCarId: actorCarId, actorCarId: actorCarId,
quantity: null, quantity: removeQty,
// Grund nur einmal (am Oberartikel) als Notiz festhalten. // Grund nur einmal (am Oberartikel) als Notiz festhalten.
saveReasonAsNote: false, saveReasonAsNote: false,
)); ));
@ -435,7 +452,7 @@ class _ArticleManagementRow extends StatelessWidget {
), ),
onPressed: canCredit onPressed: canCredit
? () => hasComponents ? () => hasComponents
? _openSetRemoveDialog(context) ? _openSetRemoveDialog(context, remaining: remaining)
: _openCreditDialog(context, remaining: remaining) : _openCreditDialog(context, remaining: remaining)
: null, : null,
icon: Icon( icon: Icon(