feat(summary): Zahlungsweise-Auswahl bei offen==0 deaktivieren
Steht kein offener Betrag mehr aus (vollständig vorab bezahlt oder per Gutschrift ausgeglichen), wird die Zahlungsmethoden-Auswahl gesperrt und ein erklärender Hinweis angezeigt — analog zur Sperre bei bereits abgeschlossener Lieferung. - Offener-Betrag-Formel in Helper _openAmount(delivery, credit) extrahiert (Single Source; vorher nur in _PaymentSummary). - _PaymentMethodPicker bekommt die Gutschrift und sperrt das Dropdown bei state != active ODER offen == 0 (editable = active && offen > 0). - Sperr-/Info-Hinweis in wiederverwendbares _PickerHint-Widget gezogen. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@ -56,6 +56,7 @@ class StepSummary extends StatelessWidget {
|
||||
_PaymentMethodPicker(
|
||||
delivery: delivery,
|
||||
overrideId: wfState.paymentMethodOverrideId,
|
||||
credit: details.creditOf(delivery.id),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const _SignHint(),
|
||||
@ -213,6 +214,17 @@ class _DeliveredRow extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// Offener Betrag der Lieferung in Euro: Warenwert (Σ Stückpreis × gelieferte
|
||||
/// Menge) − Anzahlung − Gutschrift, nie negativ. Einzige Quelle dieser Formel —
|
||||
/// genutzt von der Zahlungs-Übersicht UND der Zahlungsmethoden-Auswahl.
|
||||
double _openAmount(Delivery delivery, DeliveryCredit? credit) {
|
||||
final creditEuros = (credit?.amountCents ?? 0) / 100.0;
|
||||
final warenwert =
|
||||
delivery.items.fold<double>(0, (acc, item) => acc + item.lineTotal);
|
||||
return (warenwert - delivery.prepaidAmount - creditEuros)
|
||||
.clamp(0.0, double.infinity);
|
||||
}
|
||||
|
||||
class _PaymentSummary extends StatelessWidget {
|
||||
const _PaymentSummary({required this.delivery, required this.credit});
|
||||
|
||||
@ -222,15 +234,13 @@ class _PaymentSummary extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
// Exakt aus Cent (nicht gerundet) — Gutschrift kann Cent-Beträge haben.
|
||||
final creditEuros = (credit?.amountCents ?? 0) / 100.0;
|
||||
// Warenwert = Σ Stückpreis × ausgelieferte Menge (entfernte/teil-entfernte
|
||||
// Positionen fallen automatisch raus).
|
||||
final warenwert = delivery.items
|
||||
.fold<double>(0, (acc, item) => acc + item.lineTotal);
|
||||
// Offener Betrag = Warenwert − Anzahlung − Gutschrift, nie negativ.
|
||||
final open = (warenwert - delivery.prepaidAmount - creditEuros)
|
||||
.clamp(0.0, double.infinity);
|
||||
// Offener Betrag über den gemeinsamen Helper (gleiche Formel wie die
|
||||
// Zahlungsmethoden-Auswahl).
|
||||
final open = _openAmount(delivery, credit);
|
||||
return Card(
|
||||
margin: EdgeInsets.zero,
|
||||
child: Padding(
|
||||
@ -349,10 +359,12 @@ class _PaymentMethodPicker extends StatelessWidget {
|
||||
const _PaymentMethodPicker({
|
||||
required this.delivery,
|
||||
required this.overrideId,
|
||||
required this.credit,
|
||||
});
|
||||
|
||||
final Delivery delivery;
|
||||
final String? overrideId;
|
||||
final DeliveryCredit? credit;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -407,6 +419,11 @@ class _PaymentMethodPicker extends StatelessWidget {
|
||||
// abgeschlossener/abgebrochener/pausierter Lieferung zeigt das
|
||||
// Dropdown den gewählten Stand, ist aber gesperrt.
|
||||
final active = delivery.state == DeliveryState.active;
|
||||
// Steht kein offener Betrag mehr aus (vollständig vorab bezahlt
|
||||
// oder per Gutschrift ausgeglichen), ist keine Zahlungsweise zu
|
||||
// wählen → Auswahl deaktivieren.
|
||||
final hasOpenAmount = _openAmount(delivery, credit) > 0;
|
||||
final editable = active && hasOpenAmount;
|
||||
return Card(
|
||||
margin: EdgeInsets.zero,
|
||||
child: Padding(
|
||||
@ -428,7 +445,7 @@ class _PaymentMethodPicker extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
// `null` deaktiviert das Dropdown (Flutter-Konvention).
|
||||
onChanged: active
|
||||
onChanged: editable
|
||||
? (newId) {
|
||||
if (newId == null) return;
|
||||
context.read<DeliveryWorkflowBloc>().add(
|
||||
@ -447,27 +464,15 @@ class _PaymentMethodPicker extends StatelessWidget {
|
||||
),
|
||||
if (!active) ...[
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
Icon(Icons.lock_outline,
|
||||
size: 16,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Lieferung abgeschlossen — Zahlungsmethode nicht '
|
||||
const _PickerHint(
|
||||
text: 'Lieferung abgeschlossen — Zahlungsmethode nicht '
|
||||
'mehr änderbar.',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall
|
||||
?.copyWith(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
] else if (!hasOpenAmount) ...[
|
||||
const SizedBox(height: 8),
|
||||
const _PickerHint(
|
||||
text: 'Kein offener Betrag — Auswahl der Zahlungsweise '
|
||||
'nicht erforderlich.',
|
||||
),
|
||||
],
|
||||
],
|
||||
@ -479,6 +484,31 @@ class _PaymentMethodPicker extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// Dezenter Sperr-/Info-Hinweis unter dem Zahlungsmethoden-Dropdown
|
||||
/// (Schloss-Icon + Text in gedämpfter Farbe).
|
||||
class _PickerHint extends StatelessWidget {
|
||||
const _PickerHint({required this.text});
|
||||
|
||||
final String text;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final muted = Theme.of(context).colorScheme.onSurfaceVariant;
|
||||
return Row(
|
||||
children: [
|
||||
Icon(Icons.lock_outline, size: 16, color: muted),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
text,
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(color: muted),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SignHint extends StatelessWidget {
|
||||
const _SignHint();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user