Files
Holzleitner-Lieferservice-App/lib/feature/delivery/detail/model/delivery_payment_status.dart
Dennis Nemec dd7fe9a8eb feat(zahlung): eigener Step "Zahlung" vor der Uebersicht
- Neuer Workflow-Step "Zahlung" zeigt ausgelieferte Artikel mit Menge und
  Preis, Betragsaufstellung, Zahlungsstatus und einen grossen Button
  "Zahlung abwickeln" (fest unten)
- Zahlungs-Modal: grosser offener Betrag, Methoden als Cards aus den
  Backend-Stammdaten, bei EC-Karte gross die Kundennummer; schliesst nur
  ueber Abbrechen, "Zahlung erhalten" oder "Lieferung abbrechen" (mit Grund)
- Bestaetigung wird am Server protokolliert (RecordDeliveryPayment); bis
  dahin sind "Weiter" und die Uebersicht gesperrt, ausser es ist nichts
  offen (bereits bezahlt). Aendert sich der Betrag danach, wird die Zahlung
  als veraltet markiert und muss erneut abgewickelt werden
- Uebersicht zeigt die abgewickelte Zahlung statt eigener Methodenauswahl;
  Unterschrift-Flow ohne doppelte Inkasso-Stufe; Abschluss sendet Methode
  und Inkasso-Flag der gueltigen Zahlung
- DeliveryPaymentStatus als einzige Quelle fuer offenen Betrag
  (gleiche Formel wie Backend); Artikelliste/Betragskarte als geteilte
  Widgets; Begruendungs-Dialog geteilt mit Info-Step
- API-Client aus aktueller Backend-Spec neu generiert
- Tests fuer Zahlungsstatus und Modal

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 14:04:36 +02:00

81 lines
2.8 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:intl/intl.dart';
import 'package:hl_lieferservice/domain/entity/delivery.dart';
import 'package:hl_lieferservice/domain/entity/delivery_payment.dart';
import 'package:hl_lieferservice/domain/entity/tour_details.dart';
/// Zahlungsstand einer Lieferung — einzige Quelle für offenen Betrag und
/// „ist die Zahlung erledigt?". Genutzt von Zahlungs-Step, Übersicht und
/// Abschluss, damit alle exakt dieselben Zahlen sehen.
///
/// Offener Betrag = Warenwert (Σ Stückpreis × ausgelieferte Menge) −
/// Anzahlung − Gutschrift, nie negativ. Identische Formel und Rundung wie
/// im Backend (`open_amount_cents`), das den Betrag beim Protokollieren
/// gegenprüft.
class DeliveryPaymentStatus {
const DeliveryPaymentStatus._({
required this.goodsValue,
required this.prepaidAmount,
required this.creditAmount,
required this.openCents,
required this.latestPayment,
});
factory DeliveryPaymentStatus.of(Delivery delivery, TourDetails details) {
final goods = delivery.items.fold<double>(
0,
(acc, item) => acc + item.lineTotal,
);
final credit = (details.creditOf(delivery.id)?.amountCents ?? 0) / 100.0;
final open = goods - delivery.prepaidAmount - credit;
return DeliveryPaymentStatus._(
goodsValue: goods,
prepaidAmount: delivery.prepaidAmount,
creditAmount: credit,
openCents: open <= 0 ? 0 : (open * 100).round(),
latestPayment: details.paymentOf(delivery.id),
);
}
/// Warenwert in Euro.
final double goodsValue;
/// Bei Bestellung bezahlt (Anzahlung) in Euro.
final double prepaidAmount;
/// Aktuelle Geld-Gutschrift in Euro (0 = keine).
final double creditAmount;
/// Offener Betrag in Cent.
final int openCents;
/// Jüngste protokollierte Zahlung, egal ob noch gültig.
final DeliveryPayment? latestPayment;
double get openAmount => openCents / 100.0;
bool get hasOpenAmount => openCents > 0;
/// Die jüngste Zahlung, sofern sie zum aktuellen offenen Betrag passt.
DeliveryPayment? get validPayment {
final p = latestPayment;
if (p == null || !hasOpenAmount || p.amountCents != openCents) return null;
return p;
}
/// Es wurde abgewickelt, aber der offene Betrag hat sich seitdem geändert.
bool get isStale =>
latestPayment != null && hasOpenAmount && validPayment == null;
/// Zahlungs-Step erledigt: nichts offen oder gültig abgewickelt.
bool get isSettled => !hasOpenAmount || validPayment != null;
}
final NumberFormat _euro = NumberFormat.currency(locale: 'de_DE', symbol: '€');
/// Betrag in Euro im deutschen Format, z. B. `1.234,50 €`.
String formatEuro(double amount) => _euro.format(amount);
/// Cent-Betrag im deutschen Format, z. B. `1.234,50 €`.
String formatEuroCents(int cents) => _euro.format(cents / 100.0);