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(
|
_PaymentMethodPicker(
|
||||||
delivery: delivery,
|
delivery: delivery,
|
||||||
overrideId: wfState.paymentMethodOverrideId,
|
overrideId: wfState.paymentMethodOverrideId,
|
||||||
|
credit: details.creditOf(delivery.id),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
const _SignHint(),
|
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 {
|
class _PaymentSummary extends StatelessWidget {
|
||||||
const _PaymentSummary({required this.delivery, required this.credit});
|
const _PaymentSummary({required this.delivery, required this.credit});
|
||||||
|
|
||||||
@ -222,15 +234,13 @@ class _PaymentSummary extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final theme = Theme.of(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
|
// Warenwert = Σ Stückpreis × ausgelieferte Menge (entfernte/teil-entfernte
|
||||||
// Positionen fallen automatisch raus).
|
// Positionen fallen automatisch raus).
|
||||||
final warenwert = delivery.items
|
final warenwert = delivery.items
|
||||||
.fold<double>(0, (acc, item) => acc + item.lineTotal);
|
.fold<double>(0, (acc, item) => acc + item.lineTotal);
|
||||||
// Offener Betrag = Warenwert − Anzahlung − Gutschrift, nie negativ.
|
// Offener Betrag über den gemeinsamen Helper (gleiche Formel wie die
|
||||||
final open = (warenwert - delivery.prepaidAmount - creditEuros)
|
// Zahlungsmethoden-Auswahl).
|
||||||
.clamp(0.0, double.infinity);
|
final open = _openAmount(delivery, credit);
|
||||||
return Card(
|
return Card(
|
||||||
margin: EdgeInsets.zero,
|
margin: EdgeInsets.zero,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
@ -349,10 +359,12 @@ class _PaymentMethodPicker extends StatelessWidget {
|
|||||||
const _PaymentMethodPicker({
|
const _PaymentMethodPicker({
|
||||||
required this.delivery,
|
required this.delivery,
|
||||||
required this.overrideId,
|
required this.overrideId,
|
||||||
|
required this.credit,
|
||||||
});
|
});
|
||||||
|
|
||||||
final Delivery delivery;
|
final Delivery delivery;
|
||||||
final String? overrideId;
|
final String? overrideId;
|
||||||
|
final DeliveryCredit? credit;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -407,6 +419,11 @@ class _PaymentMethodPicker extends StatelessWidget {
|
|||||||
// abgeschlossener/abgebrochener/pausierter Lieferung zeigt das
|
// abgeschlossener/abgebrochener/pausierter Lieferung zeigt das
|
||||||
// Dropdown den gewählten Stand, ist aber gesperrt.
|
// Dropdown den gewählten Stand, ist aber gesperrt.
|
||||||
final active = delivery.state == DeliveryState.active;
|
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(
|
return Card(
|
||||||
margin: EdgeInsets.zero,
|
margin: EdgeInsets.zero,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
@ -428,7 +445,7 @@ class _PaymentMethodPicker extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
// `null` deaktiviert das Dropdown (Flutter-Konvention).
|
// `null` deaktiviert das Dropdown (Flutter-Konvention).
|
||||||
onChanged: active
|
onChanged: editable
|
||||||
? (newId) {
|
? (newId) {
|
||||||
if (newId == null) return;
|
if (newId == null) return;
|
||||||
context.read<DeliveryWorkflowBloc>().add(
|
context.read<DeliveryWorkflowBloc>().add(
|
||||||
@ -447,27 +464,15 @@ class _PaymentMethodPicker extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
if (!active) ...[
|
if (!active) ...[
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Row(
|
const _PickerHint(
|
||||||
children: [
|
text: 'Lieferung abgeschlossen — Zahlungsmethode nicht '
|
||||||
Icon(Icons.lock_outline,
|
'mehr änderbar.',
|
||||||
size: 16,
|
),
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant),
|
] else if (!hasOpenAmount) ...[
|
||||||
const SizedBox(width: 8),
|
const SizedBox(height: 8),
|
||||||
Expanded(
|
const _PickerHint(
|
||||||
child: Text(
|
text: 'Kein offener Betrag — Auswahl der Zahlungsweise '
|
||||||
'Lieferung abgeschlossen — Zahlungsmethode nicht '
|
'nicht erforderlich.',
|
||||||
'mehr änderbar.',
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodySmall
|
|
||||||
?.copyWith(
|
|
||||||
color: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.onSurfaceVariant,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
@ -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 {
|
class _SignHint extends StatelessWidget {
|
||||||
const _SignHint();
|
const _SignHint();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user