added ledger as souec of funds for payouts

This commit is contained in:
Arseni
2026-03-03 21:03:30 +03:00
parent 3f578353da
commit 51c72a87ae
29 changed files with 796 additions and 385 deletions

View File

@@ -15,7 +15,6 @@ import 'package:pshared/provider/resource.dart';
import 'package:pshared/service/ledger.dart';
import 'package:pshared/utils/exception.dart';
class LedgerAccountsProvider with ChangeNotifier {
final LedgerService _service;
OrganizationsProvider? _organizations;
@@ -25,7 +24,9 @@ class LedgerAccountsProvider with ChangeNotifier {
Resource<List<LedgerAccount>> _resource = Resource(data: []);
Resource<List<LedgerAccount>> get resource => _resource;
List<LedgerAccount> get accounts => (_resource.data ?? []).where((la) => la.role == LedgerAccountRole.operating).toList();
List<LedgerAccount> get accounts => (_resource.data ?? [])
.where((la) => la.role == LedgerAccountRole.operating)
.toList();
bool get isLoading => _resource.isLoading;
Exception? get error => _resource.error;
@@ -33,11 +34,13 @@ class LedgerAccountsProvider with ChangeNotifier {
bool get isRefreshingBalances => _isRefreshingBalances;
final Set<String> _refreshingAccounts = <String>{};
bool isWalletRefreshing(String ledgerAccountRef) => _refreshingAccounts.contains(ledgerAccountRef);
bool isWalletRefreshing(String ledgerAccountRef) =>
_refreshingAccounts.contains(ledgerAccountRef);
// Expose current org id so UI controller can reset per-org state if needed.
String? get organizationRef =>
(_organizations?.isOrganizationSet ?? false) ? _organizations!.current.id : null;
String? get organizationRef => (_organizations?.isOrganizationSet ?? false)
? _organizations!.current.id
: null;
// Used to ignore stale async results (org changes / overlapping requests).
int _opSeq = 0;
@@ -69,7 +72,10 @@ class LedgerAccountsProvider with ChangeNotifier {
_isRefreshingBalances = false;
_refreshingAccounts.clear();
_applyResource(_resource.copyWith(isLoading: true, error: null), notify: true);
_applyResource(
_resource.copyWith(isLoading: true, error: null),
notify: true,
);
try {
final base = await _service.list(orgRef);
@@ -78,7 +84,11 @@ class LedgerAccountsProvider with ChangeNotifier {
if (seq != _opSeq) return;
_applyResource(
Resource<List<LedgerAccount>>(data: result.wallets, isLoading: false, error: result.error),
Resource<List<LedgerAccount>>(
data: result.wallets,
isLoading: false,
error: result.error,
),
notify: true,
);
} catch (e) {
@@ -129,7 +139,9 @@ class LedgerAccountsProvider with ChangeNotifier {
if (_refreshingAccounts.contains(ledgerAccountRef)) return;
final existing = accounts.firstWhereOrNull((w) => w.ledgerAccountRef == ledgerAccountRef);
final existing = accounts.firstWhereOrNull(
(w) => w.ledgerAccountRef == ledgerAccountRef,
);
if (existing == null) return;
final orgRef = org.current.id;
@@ -141,12 +153,15 @@ class LedgerAccountsProvider with ChangeNotifier {
try {
final balance = await _service.getBalance(
organizationRef: orgRef,
organizationRef: orgRef,
ledgerAccountRef: ledgerAccountRef,
);
if ((_accountSeq[ledgerAccountRef] ?? 0) != seq) return;
final next = _replaceWallet(ledgerAccountRef, (w) => w.copyWith(balance: balance));
final next = _replaceWallet(
ledgerAccountRef,
(w) => w.copyWith(balance: balance),
);
if (next == null) return;
_applyResource(_resource.copyWith(data: next), notify: false);
@@ -170,7 +185,10 @@ class LedgerAccountsProvider with ChangeNotifier {
final org = _organizations;
if (org == null || !org.isOrganizationSet) return;
_applyResource(_resource.copyWith(isLoading: true, error: null), notify: true);
_applyResource(
_resource.copyWith(isLoading: true, error: null),
notify: true,
);
try {
await _service.create(
@@ -181,20 +199,31 @@ class LedgerAccountsProvider with ChangeNotifier {
);
await loadAccountsWithBalances();
} catch (e) {
_applyResource(_resource.copyWith(isLoading: false, error: toException(e)), notify: true);
_applyResource(
_resource.copyWith(isLoading: false, error: toException(e)),
notify: true,
);
rethrow;
}
}
// ---------- internals ----------
void _applyResource(Resource<List<LedgerAccount>> newResource, {required bool notify}) {
void _applyResource(
Resource<List<LedgerAccount>> newResource, {
required bool notify,
}) {
_resource = newResource;
if (notify) notifyListeners();
}
List<LedgerAccount>? _replaceWallet(String ledgerAccountRef, LedgerAccount Function(LedgerAccount) updater) {
final idx = accounts.indexWhere((w) => w.ledgerAccountRef == ledgerAccountRef);
List<LedgerAccount>? _replaceWallet(
String ledgerAccountRef,
LedgerAccount Function(LedgerAccount) updater,
) {
final idx = accounts.indexWhere(
(w) => w.ledgerAccountRef == ledgerAccountRef,
);
if (idx < 0) return null;
final next = List<LedgerAccount>.from(accounts);
@@ -202,7 +231,10 @@ class LedgerAccountsProvider with ChangeNotifier {
return next;
}
Future<_LedgerAccountLoadResult> _withBalances(String orgRef, List<LedgerAccount> base) async {
Future<_LedgerAccountLoadResult> _withBalances(
String orgRef,
List<LedgerAccount> base,
) async {
Exception? firstError;
final withBalances = await _mapConcurrent<LedgerAccount, LedgerAccount>(
@@ -211,7 +243,7 @@ class LedgerAccountsProvider with ChangeNotifier {
(ledgerAccount) async {
try {
final balance = await _service.getBalance(
organizationRef: orgRef,
organizationRef: orgRef,
ledgerAccountRef: ledgerAccount.ledgerAccountRef,
);
return ledgerAccount.copyWith(balance: balance);
@@ -243,7 +275,10 @@ class LedgerAccountsProvider with ChangeNotifier {
}
}
final workers = List.generate(min(concurrency, items.length), (_) => worker());
final workers = List.generate(
min(concurrency, items.length),
(_) => worker(),
);
await Future.wait(workers);
return results.cast<R>();