import 'package:pshared/models/money.dart'; import 'package:pshared/models/payment/fees/line.dart'; import 'package:pshared/models/payment/quote/quote.dart'; import 'package:pshared/utils/currency.dart'; import 'package:pshared/utils/money.dart'; Money? quoteFeeTotal(PaymentQuote? quote) { final preferredCurrency = quote?.amounts?.sourcePrincipal?.currency ?? quote?.amounts?.sourceDebitTotal?.currency; return quoteFeeTotalFromLines( quote?.fees?.lines, preferredCurrency: preferredCurrency, ); } Money? quoteSourceDebitTotal( PaymentQuote? quote, { String? preferredSourceCurrency, }) { final sourceDebitTotal = quote?.amounts?.sourceDebitTotal; final preferredCurrency = _normalizeCurrency( preferredSourceCurrency ?? quote?.amounts?.sourcePrincipal?.currency, ); if (sourceDebitTotal == null) { return _rebuildSourceDebitTotal( quote, preferredSourceCurrency: preferredCurrency, ); } final debitCurrency = _normalizeCurrency(sourceDebitTotal.currency); if (preferredCurrency == null || debitCurrency == null || debitCurrency == preferredCurrency) { return sourceDebitTotal; } final rebuilt = _rebuildSourceDebitTotal( quote, preferredSourceCurrency: preferredCurrency, ); return rebuilt ?? sourceDebitTotal; } Money? quoteFeeTotalFromLines( List? lines, { String? preferredCurrency, }) { if (lines == null || lines.isEmpty) return null; final normalizedPreferred = _normalizeCurrency(preferredCurrency); final totalsByCurrency = {}; for (final line in lines) { final money = line.amount; if (money == null) continue; final currency = _normalizeCurrency(money.currency); if (currency == null) continue; final amount = parseMoneyAmount(money.amount, fallback: double.nan); if (amount.isNaN) continue; final sign = _lineSign(line.side); final signedAmount = sign * amount.abs(); totalsByCurrency[currency] = (totalsByCurrency[currency] ?? 0) + signedAmount; } if (totalsByCurrency.isEmpty) return null; final selectedCurrency = normalizedPreferred != null && totalsByCurrency.containsKey(normalizedPreferred) ? normalizedPreferred : totalsByCurrency.keys.first; final total = totalsByCurrency[selectedCurrency]; if (total == null) return null; return Money(amount: amountToString(total), currency: selectedCurrency); } List aggregateMoneyByCurrency(Iterable values) { final totals = {}; for (final value in values) { if (value == null) continue; final currency = _normalizeCurrency(value.currency); if (currency == null) continue; final amount = parseMoneyAmount(value.amount, fallback: double.nan); if (amount.isNaN) continue; totals[currency] = (totals[currency] ?? 0) + amount; } return totals.entries .map( (entry) => Money(amount: amountToString(entry.value), currency: entry.key), ) .toList(); } Money? _rebuildSourceDebitTotal( PaymentQuote? quote, { String? preferredSourceCurrency, }) { final sourcePrincipal = quote?.amounts?.sourcePrincipal; if (sourcePrincipal == null) return null; final principalCurrency = _normalizeCurrency(sourcePrincipal.currency); if (principalCurrency == null) return null; if (preferredSourceCurrency != null && principalCurrency != preferredSourceCurrency) { return null; } final principalAmount = parseMoneyAmount( sourcePrincipal.amount, fallback: double.nan, ); if (principalAmount.isNaN) return null; double totalAmount = principalAmount; final fee = quoteFeeTotalFromLines( quote?.fees?.lines, preferredCurrency: principalCurrency, ); if (fee != null && _normalizeCurrency(fee.currency) == principalCurrency) { final feeAmount = parseMoneyAmount(fee.amount, fallback: double.nan); if (!feeAmount.isNaN) { totalAmount += feeAmount; } } return Money( amount: amountToString(totalAmount), currency: principalCurrency, ); } double _lineSign(String? side) { final normalized = side?.trim().toLowerCase() ?? ''; switch (normalized) { case 'entry_side_credit': case 'credit': return -1; default: return 1; } } String? _normalizeCurrency(String? currency) { final normalized = currency?.trim().toUpperCase(); if (normalized == null || normalized.isEmpty) return null; return normalized; }