few fixes and made sure ledger widget displays the name of ledger wallet

This commit is contained in:
Arseni
2026-03-05 01:48:53 +03:00
parent c59538869b
commit 519a2b1304
18 changed files with 293 additions and 249 deletions

View File

@@ -1,3 +1,8 @@
import 'package:json_annotation/json_annotation.dart';
part 'operation.g.dart';
@JsonSerializable()
class PaymentOperationDTO { class PaymentOperationDTO {
final String? stepRef; final String? stepRef;
final String? operationRef; final String? operationRef;
@@ -24,37 +29,6 @@ class PaymentOperationDTO {
}); });
factory PaymentOperationDTO.fromJson(Map<String, dynamic> json) => factory PaymentOperationDTO.fromJson(Map<String, dynamic> json) =>
PaymentOperationDTO( _$PaymentOperationDTOFromJson(json);
stepRef: _asString(json['stepRef'] ?? json['step_ref']), Map<String, dynamic> toJson() => _$PaymentOperationDTOToJson(this);
operationRef: _asString(json['operationRef'] ?? json['operation_ref']),
gateway: _asString(json['gateway']),
code: _asString(json['code']),
state: _asString(json['state']),
label: _asString(json['label']),
failureCode: _asString(json['failureCode'] ?? json['failure_code']),
failureReason: _asString(
json['failureReason'] ?? json['failure_reason'],
),
startedAt: _asString(json['startedAt'] ?? json['started_at']),
completedAt: _asString(json['completedAt'] ?? json['completed_at']),
);
Map<String, dynamic> toJson() => <String, dynamic>{
'stepRef': stepRef,
'operationRef': operationRef,
'gateway': gateway,
'code': code,
'state': state,
'label': label,
'failureCode': failureCode,
'failureReason': failureReason,
'startedAt': startedAt,
'completedAt': completedAt,
};
}
String? _asString(Object? value) {
final text = value?.toString().trim();
if (text == null || text.isEmpty) return null;
return text;
} }

View File

@@ -13,7 +13,6 @@ class PaymentDTO {
final String? state; final String? state;
final String? failureCode; final String? failureCode;
final String? failureReason; final String? failureReason;
@JsonKey(defaultValue: <PaymentOperationDTO>[])
final List<PaymentOperationDTO> operations; final List<PaymentOperationDTO> operations;
final PaymentQuoteDTO? lastQuote; final PaymentQuoteDTO? lastQuote;
final Map<String, String>? metadata; final Map<String, String>? metadata;

View File

@@ -9,7 +9,14 @@ import 'package:pshared/models/ledger/account.dart';
extension LedgerAccountDTOMapper on LedgerAccountDTO { extension LedgerAccountDTOMapper on LedgerAccountDTO {
LedgerAccount toDomain() => LedgerAccount( LedgerAccount toDomain() {
final mappedDescribable = describable?.toDomain();
final fallbackName = metadata?['name']?.trim() ?? '';
final name = mappedDescribable?.name.trim().isNotEmpty == true
? mappedDescribable!.name
: fallbackName;
return LedgerAccount(
ledgerAccountRef: ledgerAccountRef, ledgerAccountRef: ledgerAccountRef,
organizationRef: organizationRef, organizationRef: organizationRef,
ownerRef: ownerRef, ownerRef: ownerRef,
@@ -22,9 +29,13 @@ extension LedgerAccountDTOMapper on LedgerAccountDTO {
metadata: metadata, metadata: metadata,
createdAt: createdAt, createdAt: createdAt,
updatedAt: updatedAt, updatedAt: updatedAt,
describable: describable?.toDomain() ?? newDescribable(name: '', description: null), describable: newDescribable(
name: name,
description: mappedDescribable?.description,
),
balance: balance?.toDomain(), balance: balance?.toDomain(),
); );
}
} }
extension LedgerAccountModelMapper on LedgerAccount { extension LedgerAccountModelMapper on LedgerAccount {

View File

@@ -1,6 +1,7 @@
import 'package:pshared/data/dto/payment/operation.dart'; import 'package:pshared/data/dto/payment/operation.dart';
import 'package:pshared/models/payment/execution_operation.dart'; import 'package:pshared/models/payment/execution_operation.dart';
extension PaymentOperationDTOMapper on PaymentOperationDTO { extension PaymentOperationDTOMapper on PaymentOperationDTO {
PaymentExecutionOperation toDomain() => PaymentExecutionOperation( PaymentExecutionOperation toDomain() => PaymentExecutionOperation(
stepRef: stepRef, stepRef: stepRef,

View File

@@ -4,6 +4,7 @@ import 'package:pshared/models/file/downloaded_file.dart';
import 'package:pshared/service/authorization/service.dart'; import 'package:pshared/service/authorization/service.dart';
import 'package:pshared/service/services.dart'; import 'package:pshared/service/services.dart';
class PaymentDocumentsService { class PaymentDocumentsService {
static final _logger = Logger('service.payment_documents'); static final _logger = Logger('service.payment_documents');
static const String _objectType = Services.payments; static const String _objectType = Services.payments;

View File

@@ -6,10 +6,9 @@ import 'package:pshared/models/payment/status.dart';
import 'package:pshared/provider/payment/payments.dart'; import 'package:pshared/provider/payment/payments.dart';
import 'package:pweb/models/documents/operation.dart'; import 'package:pweb/models/documents/operation.dart';
import 'package:pweb/utils/payment/operation_code.dart'; import 'package:pweb/utils/report/operations/document_rule.dart';
import 'package:pweb/utils/report/payment_mapper.dart'; import 'package:pweb/utils/report/payment_mapper.dart';
class PaymentDetailsController extends ChangeNotifier { class PaymentDetailsController extends ChangeNotifier {
PaymentDetailsController({required String paymentId}) PaymentDetailsController({required String paymentId})
: _paymentId = paymentId; : _paymentId = paymentId;
@@ -53,9 +52,7 @@ class PaymentDetailsController extends ChangeNotifier {
final gatewayService = operation.gateway; final gatewayService = operation.gateway;
if (gatewayService == null || gatewayService.isEmpty) return null; if (gatewayService == null || gatewayService.isEmpty) return null;
final pair = parseOperationCodePair(operation.code); if (!isOperationDocumentEligible(operation.code)) return null;
if (pair == null) return null;
if (pair.operation != 'card_payout' || pair.action != 'send') return null;
return OperationDocumentRequestModel( return OperationDocumentRequestModel(
gatewayService: gatewayService, gatewayService: gatewayService,

View File

@@ -17,10 +17,7 @@ import 'package:pweb/generated/i18n/app_localizations.dart';
class LedgerAccountCard extends StatelessWidget { class LedgerAccountCard extends StatelessWidget {
final LedgerAccount account; final LedgerAccount account;
const LedgerAccountCard({ const LedgerAccountCard({super.key, required this.account});
super.key,
required this.account,
});
String _formatBalance() { String _formatBalance() {
final money = account.balance?.balance; final money = account.balance?.balance;
@@ -62,8 +59,13 @@ class LedgerAccountCard extends StatelessWidget {
final textTheme = Theme.of(context).textTheme; final textTheme = Theme.of(context).textTheme;
final colorScheme = Theme.of(context).colorScheme; final colorScheme = Theme.of(context).colorScheme;
final loc = AppLocalizations.of(context)!; final loc = AppLocalizations.of(context)!;
final subtitle = account.name.isNotEmpty ? account.name : account.accountCode; final accountName = account.name.trim();
final badge = account.currency.trim().isEmpty ? null : account.currency.toUpperCase(); final accountCode = account.accountCode.trim();
final title = accountName.isNotEmpty ? accountName : loc.paymentTypeLedger;
final subtitle = accountCode.isNotEmpty ? accountCode : null;
final badge = account.currency.trim().isEmpty
? null
: account.currency.toUpperCase();
return Card( return Card(
color: colorScheme.onSecondary, color: colorScheme.onSecondary,
@@ -76,16 +78,14 @@ class LedgerAccountCard extends StatelessWidget {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
BalanceHeader( BalanceHeader(title: title, subtitle: subtitle, badge: badge),
title: loc.paymentTypeLedger,
subtitle: subtitle.isNotEmpty ? subtitle : null,
badge: badge,
),
Row( Row(
children: [ children: [
Consumer<LedgerBalanceMaskController>( Consumer<LedgerBalanceMaskController>(
builder: (context, controller, _) { builder: (context, controller, _) {
final isMasked = controller.isBalanceMasked(account.ledgerAccountRef); final isMasked = controller.isBalanceMasked(
account.ledgerAccountRef,
);
return Row( return Row(
children: [ children: [
Text( Text(
@@ -97,7 +97,9 @@ class LedgerAccountCard extends StatelessWidget {
), ),
const SizedBox(width: 12), const SizedBox(width: 12),
GestureDetector( GestureDetector(
onTap: () => controller.toggleBalanceMask(account.ledgerAccountRef), onTap: () => controller.toggleBalanceMask(
account.ledgerAccountRef,
),
child: Icon( child: Icon(
isMasked ? Icons.visibility_off : Icons.visibility, isMasked ? Icons.visibility_off : Icons.visibility,
size: 24, size: 24,

View File

@@ -13,14 +13,13 @@ import 'package:pweb/pages/dashboard/payouts/multiple/panels/source_quote/summar
import 'package:pweb/pages/dashboard/payouts/multiple/widgets/quote_status.dart'; import 'package:pweb/pages/dashboard/payouts/multiple/widgets/quote_status.dart';
import 'package:pweb/pages/payout_page/send/widgets/send_button.dart'; import 'package:pweb/pages/payout_page/send/widgets/send_button.dart';
import 'package:pweb/widgets/payment/source_of_funds_panel.dart'; import 'package:pweb/widgets/payment/source_of_funds_panel.dart';
import 'package:pweb/widgets/payment/source_wallet_selector.dart'; import 'package:pweb/widgets/payment/source_wallet_selector/view.dart';
import 'package:pweb/widgets/cooldown_hint.dart'; import 'package:pweb/widgets/cooldown_hint.dart';
import 'package:pweb/widgets/refresh_balance/wallet.dart'; import 'package:pweb/widgets/refresh_balance/wallet.dart';
import 'package:pweb/models/state/control_state.dart'; import 'package:pweb/models/state/control_state.dart';
import 'package:pweb/generated/i18n/app_localizations.dart'; import 'package:pweb/generated/i18n/app_localizations.dart';
class SourceQuotePanel extends StatelessWidget { class SourceQuotePanel extends StatelessWidget {
const SourceQuotePanel({super.key, required this.controller}); const SourceQuotePanel({super.key, required this.controller});

View File

@@ -4,7 +4,7 @@ import 'package:provider/provider.dart';
import 'package:pshared/controllers/payment/source.dart'; import 'package:pshared/controllers/payment/source.dart';
import 'package:pweb/widgets/payment/source_wallet_selector.dart'; import 'package:pweb/widgets/payment/source_wallet_selector/view.dart';
class PaymentMethodSelector extends StatelessWidget { class PaymentMethodSelector extends StatelessWidget {
const PaymentMethodSelector({super.key}); const PaymentMethodSelector({super.key});

View File

@@ -0,0 +1,14 @@
import 'package:pweb/utils/payment/operation_code.dart';
const String _documentOperation = 'card_payout';
const String _documentAction = 'send';
bool isOperationDocumentEligible(String? operationCode) {
final pair = parseOperationCodePair(operationCode);
if (pair == null) return false;
return _isDocumentOperationPair(pair);
}
bool _isDocumentOperationPair(OperationCodePair pair) {
return pair.operation == _documentOperation && pair.action == _documentAction;
}

View File

@@ -5,8 +5,7 @@ import 'package:pshared/models/payment/status.dart';
import 'package:pshared/utils/money.dart'; import 'package:pshared/utils/money.dart';
import 'package:pweb/models/report/operation/document.dart'; import 'package:pweb/models/report/operation/document.dart';
import 'package:pweb/utils/payment/operation_code.dart'; import 'package:pweb/utils/report/operations/document_rule.dart';
OperationItem mapPaymentToOperation(Payment payment) { OperationItem mapPaymentToOperation(Payment payment) {
final debit = payment.lastQuote?.amounts?.sourceDebitTotal; final debit = payment.lastQuote?.amounts?.sourceDebitTotal;
@@ -63,9 +62,7 @@ OperationDocumentInfo? _resolveOperationDocument(Payment payment) {
if (operationRef == null || operationRef.isEmpty) continue; if (operationRef == null || operationRef.isEmpty) continue;
if (gatewayService == null || gatewayService.isEmpty) continue; if (gatewayService == null || gatewayService.isEmpty) continue;
final pair = parseOperationCodePair(operation.code); if (!isOperationDocumentEligible(operation.code)) continue;
if (pair == null) continue;
if (pair.operation != 'card_payout' || pair.action != 'send') continue;
return OperationDocumentInfo( return OperationDocumentInfo(
operationRef: operationRef, operationRef: operationRef,

View File

@@ -1,172 +0,0 @@
import 'package:flutter/material.dart';
import 'package:pshared/controllers/payment/source.dart';
import 'package:pshared/models/ledger/account.dart';
import 'package:pshared/models/payment/source_type.dart';
import 'package:pshared/models/payment/wallet.dart';
import 'package:pshared/utils/currency.dart';
import 'package:pshared/utils/money.dart';
import 'package:pweb/generated/i18n/app_localizations.dart';
typedef _SourceOptionKey = ({PaymentSourceType type, String ref});
class SourceWalletSelector extends StatelessWidget {
const SourceWalletSelector({
super.key,
required this.sourceController,
this.isBusy = false,
this.onChanged,
});
final PaymentSourceController sourceController;
final bool isBusy;
final ValueChanged<Wallet>? onChanged;
@override
Widget build(BuildContext context) {
final source = sourceController;
final selectedWallet = source.selectedWallet;
final selectedLedger = source.selectedLedgerAccount;
final selectedValue = switch (source.selectedType) {
PaymentSourceType.wallet =>
selectedWallet == null ? null : _walletKey(selectedWallet.id),
PaymentSourceType.ledger =>
selectedLedger == null
? null
: _ledgerKey(selectedLedger.ledgerAccountRef),
null => null,
};
return _buildSourceSelector(
context: context,
wallets: source.wallets,
ledgerAccounts: source.ledgerAccounts,
selectedValue: selectedValue,
onChanged: (value) {
if (value.type == PaymentSourceType.wallet) {
source.selectWalletByRef(value.ref);
final selected = source.selectedWallet;
if (selected != null) {
onChanged?.call(selected);
}
return;
}
if (value.type == PaymentSourceType.ledger) {
source.selectLedgerByRef(value.ref);
}
},
);
}
Widget _buildSourceSelector({
required BuildContext context,
required List<Wallet> wallets,
required List<LedgerAccount> ledgerAccounts,
required _SourceOptionKey? selectedValue,
required ValueChanged<_SourceOptionKey> onChanged,
}) {
final theme = Theme.of(context);
final l10n = AppLocalizations.of(context)!;
if (wallets.isEmpty && ledgerAccounts.isEmpty) {
return Text(l10n.noWalletsAvailable, style: theme.textTheme.bodySmall);
}
final items = <DropdownMenuItem<_SourceOptionKey>>[
...wallets.map((wallet) {
return DropdownMenuItem<_SourceOptionKey>(
value: _walletKey(wallet.id),
child: Text(
'${_walletDisplayName(wallet, l10n)} - ${_walletBalance(wallet)}',
overflow: TextOverflow.ellipsis,
),
);
}),
...ledgerAccounts.map((ledger) {
return DropdownMenuItem<_SourceOptionKey>(
value: _ledgerKey(ledger.ledgerAccountRef),
child: Text(
'${_ledgerDisplayName(ledger, l10n)} - ${_ledgerBalance(ledger)}',
overflow: TextOverflow.ellipsis,
),
);
}),
];
final knownValues = items
.map((item) => item.value)
.whereType<_SourceOptionKey>()
.toSet();
final effectiveValue = knownValues.contains(selectedValue)
? selectedValue
: null;
return DropdownButtonFormField<_SourceOptionKey>(
initialValue: effectiveValue,
isExpanded: true,
decoration: InputDecoration(
labelText: l10n.whereGetMoney,
border: const OutlineInputBorder(),
contentPadding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 10,
),
),
items: items,
onChanged: isBusy
? null
: (value) {
if (value == null) return;
onChanged(value);
},
);
}
_SourceOptionKey _walletKey(String walletRef) =>
(type: PaymentSourceType.wallet, ref: walletRef);
_SourceOptionKey _ledgerKey(String ledgerAccountRef) =>
(type: PaymentSourceType.ledger, ref: ledgerAccountRef);
String _walletDisplayName(Wallet wallet, AppLocalizations l10n) {
final name = wallet.name.trim();
if (name.isNotEmpty) return name;
return l10n.paymentTypeWallet;
}
String _ledgerDisplayName(LedgerAccount ledger, AppLocalizations l10n) {
final name = ledger.name.trim();
if (name.isNotEmpty) return name;
return l10n.paymentTypeLedger;
}
String _walletBalance(Wallet wallet) {
final symbol = currencyCodeToSymbol(wallet.currency);
return '$symbol ${amountToString(wallet.balance)}';
}
String _ledgerBalance(LedgerAccount account) {
final money = account.balance?.balance;
final rawAmount = money?.amount.trim();
final amount = parseMoneyAmount(rawAmount, fallback: double.nan);
final amountText = amount.isNaN
? (rawAmount == null || rawAmount.isEmpty ? '--' : rawAmount)
: amountToString(amount);
final currencyCode = (money?.currency ?? account.currency)
.trim()
.toUpperCase();
final symbol = currencySymbolFromCode(currencyCode);
if (symbol != null && symbol.trim().isNotEmpty) {
return '$symbol $amountText';
}
if (currencyCode.isNotEmpty) {
return '$amountText $currencyCode';
}
return amountText;
}
}

View File

@@ -0,0 +1,31 @@
import 'package:pshared/models/ledger/account.dart';
import 'package:pshared/models/payment/wallet.dart';
import 'package:pshared/utils/currency.dart';
import 'package:pshared/utils/money.dart';
String walletBalance(Wallet wallet) {
final symbol = currencyCodeToSymbol(wallet.currency);
return '$symbol ${amountToString(wallet.balance)}';
}
String ledgerBalance(LedgerAccount account) {
final money = account.balance?.balance;
final rawAmount = money?.amount.trim();
final amount = parseMoneyAmount(rawAmount, fallback: double.nan);
final amountText = amount.isNaN
? (rawAmount == null || rawAmount.isEmpty ? '--' : rawAmount)
: amountToString(amount);
final currencyCode = (money?.currency ?? account.currency)
.trim()
.toUpperCase();
final symbol = currencySymbolFromCode(currencyCode);
if (symbol != null && symbol.trim().isNotEmpty) {
return '$symbol $amountText';
}
if (currencyCode.isNotEmpty) {
return '$amountText $currencyCode';
}
return amountText;
}

View File

@@ -0,0 +1,19 @@
import 'package:pshared/models/ledger/account.dart';
import 'package:pshared/models/payment/wallet.dart';
import 'package:pweb/generated/i18n/app_localizations.dart';
String walletDisplayName(Wallet wallet, AppLocalizations l10n) {
return sourceDisplayName(name: wallet.name, fallback: l10n.paymentTypeWallet);
}
String ledgerDisplayName(LedgerAccount ledger, AppLocalizations l10n) {
return sourceDisplayName(name: ledger.name, fallback: l10n.paymentTypeLedger);
}
String sourceDisplayName({required String name, required String fallback}) {
final normalized = name.trim();
if (normalized.isNotEmpty) return normalized;
return fallback;
}

View File

@@ -0,0 +1,38 @@
import 'package:flutter/material.dart';
import 'package:pshared/models/ledger/account.dart';
import 'package:pshared/models/payment/wallet.dart';
import 'package:pweb/widgets/payment/source_wallet_selector/balance_formatter.dart';
import 'package:pweb/widgets/payment/source_wallet_selector/display_name.dart';
import 'package:pweb/widgets/payment/source_wallet_selector/options.dart';
import 'package:pweb/generated/i18n/app_localizations.dart';
List<DropdownMenuItem<SourceOptionKey>> buildSourceSelectorItems({
required List<Wallet> wallets,
required List<LedgerAccount> ledgerAccounts,
required AppLocalizations l10n,
}) {
return <DropdownMenuItem<SourceOptionKey>>[
...wallets.map((wallet) {
return DropdownMenuItem<SourceOptionKey>(
value: walletOptionKey(wallet.id),
child: Text(
'${walletDisplayName(wallet, l10n)} - ${walletBalance(wallet)}',
overflow: TextOverflow.ellipsis,
),
);
}),
...ledgerAccounts.map((ledger) {
return DropdownMenuItem<SourceOptionKey>(
value: ledgerOptionKey(ledger.ledgerAccountRef),
child: Text(
'${ledgerDisplayName(ledger, l10n)} - ${ledgerBalance(ledger)}',
overflow: TextOverflow.ellipsis,
),
);
}),
];
}

View File

@@ -0,0 +1,25 @@
import 'package:pshared/controllers/payment/source.dart';
import 'package:pshared/models/payment/source_type.dart';
typedef SourceOptionKey = ({PaymentSourceType type, String ref});
SourceOptionKey walletOptionKey(String walletRef) =>
(type: PaymentSourceType.wallet, ref: walletRef);
SourceOptionKey ledgerOptionKey(String ledgerAccountRef) =>
(type: PaymentSourceType.ledger, ref: ledgerAccountRef);
SourceOptionKey? resolveSelectedSourceOption(PaymentSourceController source) {
final selectedWallet = source.selectedWallet;
final selectedLedger = source.selectedLedgerAccount;
return switch (source.selectedType) {
PaymentSourceType.wallet =>
selectedWallet == null ? null : walletOptionKey(selectedWallet.id),
PaymentSourceType.ledger =>
selectedLedger == null
? null
: ledgerOptionKey(selectedLedger.ledgerAccountRef),
null => null,
};
}

View File

@@ -0,0 +1,57 @@
import 'package:flutter/material.dart';
import 'package:pshared/models/ledger/account.dart';
import 'package:pshared/models/payment/wallet.dart';
import 'package:pweb/widgets/payment/source_wallet_selector/dropdown_items.dart';
import 'package:pweb/widgets/payment/source_wallet_selector/options.dart';
import 'package:pweb/generated/i18n/app_localizations.dart';
Widget buildSourceSelectorField({
required BuildContext context,
required List<Wallet> wallets,
required List<LedgerAccount> ledgerAccounts,
required SourceOptionKey? selectedValue,
required ValueChanged<SourceOptionKey> onChanged,
required bool isBusy,
}) {
final theme = Theme.of(context);
final l10n = AppLocalizations.of(context)!;
if (wallets.isEmpty && ledgerAccounts.isEmpty) {
return Text(l10n.noWalletsAvailable, style: theme.textTheme.bodySmall);
}
final items = buildSourceSelectorItems(
wallets: wallets,
ledgerAccounts: ledgerAccounts,
l10n: l10n,
);
final knownValues = items
.map((item) => item.value)
.whereType<SourceOptionKey>()
.toSet();
final effectiveValue = knownValues.contains(selectedValue)
? selectedValue
: null;
return DropdownButtonFormField<SourceOptionKey>(
initialValue: effectiveValue,
isExpanded: true,
decoration: InputDecoration(
labelText: l10n.whereGetMoney,
border: const OutlineInputBorder(),
contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
),
items: items,
onChanged: isBusy
? null
: (value) {
if (value == null) return;
onChanged(value);
},
);
}

View File

@@ -0,0 +1,51 @@
import 'package:flutter/material.dart';
import 'package:pshared/controllers/payment/source.dart';
import 'package:pshared/models/payment/source_type.dart';
import 'package:pshared/models/payment/wallet.dart';
import 'package:pweb/widgets/payment/source_wallet_selector/options.dart';
import 'package:pweb/widgets/payment/source_wallet_selector/selector_field.dart';
class SourceWalletSelector extends StatelessWidget {
const SourceWalletSelector({
super.key,
required this.sourceController,
this.isBusy = false,
this.onChanged,
});
final PaymentSourceController sourceController;
final bool isBusy;
final ValueChanged<Wallet>? onChanged;
@override
Widget build(BuildContext context) {
final source = sourceController;
return buildSourceSelectorField(
context: context,
wallets: source.wallets,
ledgerAccounts: source.ledgerAccounts,
selectedValue: resolveSelectedSourceOption(source),
onChanged: (value) => _onSourceChanged(source, value),
isBusy: isBusy,
);
}
void _onSourceChanged(PaymentSourceController source, SourceOptionKey value) {
if (value.type == PaymentSourceType.wallet) {
source.selectWalletByRef(value.ref);
final selected = source.selectedWallet;
if (selected != null) {
onChanged?.call(selected);
}
return;
}
if (value.type == PaymentSourceType.ledger) {
source.selectLedgerByRef(value.ref);
}
}
}