@marcos_feitoza/personal-finance-frontend-feature-investments 1.1.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,197 +1,104 @@
1
1
  import 'package:flutter/material.dart';
2
2
  import 'package:intl/intl.dart';
3
- import 'package:personal_finance_frontend_core_services/services/transaction_service.dart';
4
- import 'package:personal_finance_frontend_core_ui/widgets/rrsp_contribution_form.dart';
5
3
  import 'package:provider/provider.dart';
6
4
  import 'package:personal_finance_frontend_core_services/providers/auth_provider.dart';
7
- import 'package:personal_finance_frontend_core_ui/utils/app_dialogs.dart'; // Added import for AppDialogs
5
+ import 'package:personal_finance_frontend_core_ui/utils/app_dialogs.dart';
6
+ import 'package:personal_finance_frontend_core_ui/widgets/rrsp_contribution_form.dart';
7
+ import '../viewmodels/rrsp_sun_life_viewmodel.dart';
8
8
 
9
- class RrspSunLifeScreen extends StatefulWidget {
9
+ class RrspSunLifeScreen extends StatelessWidget {
10
10
  const RrspSunLifeScreen({Key? key}) : super(key: key);
11
11
 
12
- @override
13
- _RrspSunLifeScreenState createState() => _RrspSunLifeScreenState();
14
- }
15
-
16
- class _RrspSunLifeScreenState extends State<RrspSunLifeScreen> {
17
- final TransactionService _transactionService = TransactionService();
18
- List<Map<String, dynamic>> _contributions = [];
19
- List<Map<String, dynamic>> _sinteticoSummary = [];
20
- double _rrspTotalValue = 0.0;
21
- double _rrspCashBalance = 0.0;
22
- double _totalPortfolioBookCost = 0.0;
23
- bool _isLoading = true;
24
- String? _token;
25
-
26
- @override
27
- void initState() {
28
- super.initState();
29
- WidgetsBinding.instance.addPostFrameCallback((_) {
30
- final authProvider = Provider.of<AuthProvider>(context, listen: false);
31
- setState(() {
32
- _token = authProvider.token;
33
- });
34
- _fetchData();
35
- });
36
- }
37
-
38
- String currencySymbol = r'$'; // ou poderia vir de configuração, API, etc.
39
-
40
- String _formatCurrency(double value) {
41
- return '$currencySymbol${value.toStringAsFixed(2)}';
42
- }
43
-
44
- Future<void> _fetchData() async {
45
- if (_token == null) return;
46
- if (mounted) {
47
- setState(() {
48
- _isLoading = true;
49
- });
50
- }
51
-
52
- try {
53
- final contributions = await _transactionService.getRrspContributions(
54
- investmentAccount: 'RRSP Sun Life', token: _token);
55
- final balance =
56
- await _transactionService.getAccountBalance('RRSP Sun Life', token: _token);
57
- final totalPortfolioBookCost =
58
- await _transactionService.getTotalPortfolioBookCost(token: _token);
59
-
60
- final sinteticoSummaryData =
61
- _calculateSinteticoSummary(contributions, totalPortfolioBookCost);
62
-
63
- if (!mounted) return;
64
- setState(() {
65
- _contributions = contributions;
66
- _sinteticoSummary = sinteticoSummaryData['summary'];
67
- _rrspCashBalance = balance;
68
- _rrspTotalValue = sinteticoSummaryData['total_value'];
69
- _totalPortfolioBookCost = totalPortfolioBookCost;
70
- });
71
- } catch (e) {
72
- if (!mounted) return;
73
- ScaffoldMessenger.of(context).showSnackBar(
74
- SnackBar(content: Text('Error fetching RRSP data: $e')),
75
- );
76
- } finally {
77
- if (mounted) {
78
- setState(() {
79
- _isLoading = false;
80
- });
81
- }
82
- }
83
- }
84
-
85
- Map<String, dynamic> _calculateSinteticoSummary(
86
- List<Map<String, dynamic>> contributions, double totalPortfolioBookCost) {
87
- double totalUserContribution = 0;
88
- double totalCompanyContribution = 0;
89
- double totalUnrealizedPL = 0;
90
-
91
- for (var c in contributions) {
92
- totalUserContribution += double.parse(c['rrsp_amount'].toString());
93
- totalCompanyContribution += double.parse(c['dpsp_amount'].toString());
94
- totalUnrealizedPL += double.parse(c['return_amount']?.toString() ?? '0.0');
95
- }
96
-
97
- final totalContributed = totalUserContribution + totalCompanyContribution;
98
- final marketValue = totalContributed + totalUnrealizedPL;
99
-
100
- final summaryData = {
101
- 'user_contribution': totalUserContribution,
102
- 'company_contribution': totalCompanyContribution,
103
- 'total_contributed': totalContributed,
104
- 'unrealized_pl': totalUnrealizedPL,
105
- 'percent_return':
106
- (totalContributed > 0) ? (totalUnrealizedPL / totalContributed) * 100 : 0.0,
107
- 'market_value': marketValue,
108
- 'portfolio_allocation_percent': (totalPortfolioBookCost > 0)
109
- ? (marketValue / totalPortfolioBookCost) * 100
110
- : 0.0,
111
- };
112
-
113
- return {
114
- 'summary': [summaryData],
115
- 'total_value': marketValue
116
- };
117
- }
118
-
119
12
  @override
120
13
  Widget build(BuildContext context) {
121
- return Scaffold(
122
- appBar: AppBar(
123
- title: const Text('RRSP Sun Life Portfolio'),
124
- actions: [
125
- Center(
126
- child: Padding(
127
- padding: const EdgeInsets.only(right: 16.0),
128
- child: Column(
129
- mainAxisAlignment: MainAxisAlignment.center,
130
- crossAxisAlignment: CrossAxisAlignment.end,
131
- children: [
132
- Text(
133
- 'Total Portfolio: ${_formatCurrency(_rrspTotalValue)}',
134
- style: const TextStyle(
135
- fontSize: 16, fontWeight: FontWeight.bold),
136
- ),
137
- Text(
138
- 'Account Balance: ${_formatCurrency(_rrspCashBalance)}',
139
- style: const TextStyle(fontSize: 12),
140
- ),
141
- ],
142
- ),
143
- ),
144
- ),
145
- IconButton(
146
- icon: const Icon(Icons.refresh),
147
- onPressed: _fetchData,
148
- tooltip: 'Refresh Data',
149
- ),
150
- ],
151
- ),
152
- body: _isLoading
153
- ? const Center(child: CircularProgressIndicator())
154
- : RefreshIndicator(
155
- onRefresh: _fetchData,
156
- child: SingleChildScrollView(
157
- padding: const EdgeInsets.all(16.0),
158
- child: Column(
159
- crossAxisAlignment: CrossAxisAlignment.stretch,
160
- children: [
161
- Text('Summary',
162
- style: Theme.of(context).textTheme.titleLarge),
163
- const SizedBox(height: 8),
164
- _buildSinteticoTable(),
165
- const SizedBox(height: 24),
166
- RrspContributionForm(
167
- investmentAccount: 'RRSP Sun Life',
168
- onContributionLogged: () => _fetchData(),
169
- token: _token,
170
- ),
171
- const SizedBox(height: 24),
172
- Text(
173
- 'Contribution History',
174
- style: Theme.of(context).textTheme.titleLarge,
14
+ final authProvider = Provider.of<AuthProvider>(context, listen: false);
15
+
16
+ return ChangeNotifierProvider(
17
+ create: (_) => RrspSunLifeViewModel(token: authProvider.token),
18
+ child: Consumer<RrspSunLifeViewModel>(
19
+ builder: (context, viewModel, child) {
20
+ return Scaffold(
21
+ appBar: AppBar(
22
+ title: const Text('RRSP Sun Life Portfolio'),
23
+ actions: [
24
+ Center(
25
+ child: Padding(
26
+ padding: const EdgeInsets.only(right: 16.0),
27
+ child: Column(
28
+ mainAxisAlignment: MainAxisAlignment.center,
29
+ crossAxisAlignment: CrossAxisAlignment.end,
30
+ children: [
31
+ Text(
32
+ 'Total Portfolio: ${_formatCurrency(viewModel.rrspTotalValue)}',
33
+ style: const TextStyle(
34
+ fontSize: 16, fontWeight: FontWeight.bold),
35
+ ),
36
+ Text(
37
+ 'Account Balance: ${_formatCurrency(viewModel.rrspCashBalance)}',
38
+ style: const TextStyle(fontSize: 12),
39
+ ),
40
+ ],
175
41
  ),
176
- const SizedBox(height: 8),
177
- _buildContributionTable(),
178
- ],
42
+ ),
179
43
  ),
180
- ),
44
+ IconButton(
45
+ icon: const Icon(Icons.refresh),
46
+ onPressed: viewModel.fetchData,
47
+ tooltip: 'Refresh Data',
48
+ ),
49
+ ],
181
50
  ),
51
+ body: viewModel.isLoading
52
+ ? const Center(child: CircularProgressIndicator())
53
+ : RefreshIndicator(
54
+ onRefresh: viewModel.fetchData,
55
+ child: SingleChildScrollView(
56
+ padding: const EdgeInsets.all(16.0),
57
+ child: Column(
58
+ crossAxisAlignment: CrossAxisAlignment.stretch,
59
+ children: [
60
+ Text('Summary',
61
+ style: Theme.of(context).textTheme.titleLarge),
62
+ const SizedBox(height: 8),
63
+ _buildSinteticoTable(context, viewModel),
64
+ const SizedBox(height: 24),
65
+ RrspContributionForm(
66
+ investmentAccount: 'RRSP Sun Life',
67
+ onContributionLogged: viewModel.fetchData,
68
+ token: viewModel.token,
69
+ ),
70
+ const SizedBox(height: 24),
71
+ Text(
72
+ 'Contribution History',
73
+ style: Theme.of(context).textTheme.titleLarge,
74
+ ),
75
+ const SizedBox(height: 8),
76
+ _buildContributionTable(context, viewModel),
77
+ ],
78
+ ),
79
+ ),
80
+ ),
81
+ );
82
+ },
83
+ ),
182
84
  );
183
85
  }
184
86
 
185
- Widget _buildSinteticoTable() {
186
- if (_sinteticoSummary.isEmpty) {
87
+ String _formatCurrency(double value) {
88
+ String currencySymbol = r'$';
89
+ return '$currencySymbol${value.toStringAsFixed(2)}';
90
+ }
91
+
92
+ Widget _buildSinteticoTable(
93
+ BuildContext context, RrspSunLifeViewModel viewModel) {
94
+ if (viewModel.sinteticoSummary.isEmpty) {
187
95
  return const SizedBox.shrink();
188
96
  }
189
97
 
190
- final summary = _sinteticoSummary.first;
191
- final double unrealizedPL = summary['unrealized_pl'];
98
+ final summary = viewModel.sinteticoSummary.first;
99
+ final double unrealizedPL = double.tryParse(summary['unrealized_pl']?.toString() ?? '0.0') ?? 0.0;
192
100
  final plColor = (unrealizedPL >= 0) ? Colors.green : Colors.red;
193
101
 
194
-
195
102
  return DataTable(
196
103
  columns: const [
197
104
  DataColumn(label: Text('User Contr.')),
@@ -205,24 +112,33 @@ class _RrspSunLifeScreenState extends State<RrspSunLifeScreen> {
205
112
  rows: [
206
113
  DataRow(
207
114
  cells: [
208
- DataCell(Text(_formatCurrency(summary['user_contribution'] as double))),
209
- DataCell(Text(_formatCurrency(summary['company_contribution'] as double))),
210
115
  DataCell(
211
- Text(_formatCurrency(summary['total_contributed'] as double))),
212
- DataCell(Text(_formatCurrency(unrealizedPL), style: TextStyle(color: plColor))),
116
+ Text(_formatCurrency(double.tryParse(summary['user_contribution']?.toString() ?? '0.0') ?? 0.0))),
213
117
  DataCell(Text(
214
- '${(summary['percent_return'] as double).toStringAsFixed(2)}%', style: TextStyle(color: plColor))),
215
- DataCell(Text(_formatCurrency(summary['market_value'] as double))),
216
- DataCell(Text(
217
- '${(summary['portfolio_allocation_percent'] as double).toStringAsFixed(2)}%')),
118
+ _formatCurrency(double.tryParse(summary['company_contribution']?.toString() ?? '0.0') ?? 0.0))),
119
+ DataCell(
120
+ Text(_formatCurrency(double.tryParse(summary['total_contributed']?.toString() ?? '0.0') ?? 0.0))),
121
+ DataCell(Text(_formatCurrency(unrealizedPL),
122
+ style: TextStyle(color: plColor))),
123
+ DataCell(
124
+ Text('${(double.tryParse(summary['percent_return']?.toString() ?? '0.0') ?? 0.0).toStringAsFixed(2)}%',
125
+ style: TextStyle(color: plColor)),
126
+ ),
127
+ DataCell(
128
+ Text(_formatCurrency(double.tryParse(summary['market_value']?.toString() ?? '0.0') ?? 0.0))),
129
+ DataCell(
130
+ Text(
131
+ '${(double.tryParse(summary['portfolio_allocation_percent']?.toString() ?? '0.0') ?? 0.0).toStringAsFixed(2)}%'),
132
+ ),
218
133
  ],
219
134
  ),
220
135
  ],
221
136
  );
222
137
  }
223
138
 
224
- Widget _buildContributionTable() {
225
- if (_contributions.isEmpty) {
139
+ Widget _buildContributionTable(
140
+ BuildContext context, RrspSunLifeViewModel viewModel) {
141
+ if (viewModel.contributions.isEmpty) {
226
142
  return const Center(
227
143
  child: Padding(
228
144
  padding: EdgeInsets.all(16.0),
@@ -243,21 +159,21 @@ class _RrspSunLifeScreenState extends State<RrspSunLifeScreen> {
243
159
  DataColumn(label: Text('Portfolio %')),
244
160
  DataColumn(label: Text('Actions')),
245
161
  ],
246
- rows: _contributions.map((c) {
162
+ rows: viewModel.contributions.map((c) {
247
163
  final contributionId = c['id'] as int;
248
- final rrspAmount = double.parse(c['rrsp_amount'].toString());
249
- final dpspAmount = double.parse(c['dpsp_amount'].toString());
164
+ final rrspAmount = double.tryParse(c['rrsp_amount']?.toString() ?? '0.0') ?? 0.0;
165
+ final dpspAmount = double.tryParse(c['dpsp_amount']?.toString() ?? '0.0') ?? 0.0;
250
166
  final totalContributed = rrspAmount + dpspAmount;
251
167
 
252
- final returnAmount =
253
- double.parse(c['return_amount']?.toString() ?? '0.0');
254
- final percentReturn =
255
- totalContributed > 0 ? (returnAmount / totalContributed) * 100 : 0.0;
168
+ final returnAmount = double.tryParse(c['return_amount']?.toString() ?? '0.0') ?? 0.0;
169
+ final percentReturn = totalContributed > 0
170
+ ? (returnAmount / totalContributed) * 100
171
+ : 0.0;
256
172
 
257
173
  final cumulativeValue = totalContributed + returnAmount;
258
174
 
259
- final portfolioAllocation = _totalPortfolioBookCost > 0
260
- ? (cumulativeValue / _totalPortfolioBookCost) * 100
175
+ final portfolioAllocation = viewModel.totalPortfolioBookCost > 0
176
+ ? (cumulativeValue / viewModel.totalPortfolioBookCost) * 100
261
177
  : 0.0;
262
178
 
263
179
  String dateStr;
@@ -283,7 +199,8 @@ class _RrspSunLifeScreenState extends State<RrspSunLifeScreen> {
283
199
  DataCell(
284
200
  IconButton(
285
201
  icon: const Icon(Icons.delete, color: Colors.red),
286
- onPressed: () => _confirmAndDeleteContribution(contributionId),
202
+ onPressed: () => _confirmAndDeleteContribution(
203
+ context, viewModel, contributionId),
287
204
  tooltip: 'Delete Contribution',
288
205
  ),
289
206
  ),
@@ -293,34 +210,22 @@ class _RrspSunLifeScreenState extends State<RrspSunLifeScreen> {
293
210
  );
294
211
  }
295
212
 
296
- Future<void> _confirmAndDeleteContribution(int contributionId) async {
213
+ Future<void> _confirmAndDeleteContribution(BuildContext context,
214
+ RrspSunLifeViewModel viewModel, int contributionId) async {
297
215
  final bool? confirm = await AppDialogs.showDeleteConfirmationDialog(
298
216
  context,
299
- 'this contribution', // A generic string for the item being deleted
217
+ 'this contribution',
300
218
  );
301
219
 
302
220
  if (confirm == true) {
303
- if (_token == null) {
221
+ final success = await viewModel.deleteContribution(contributionId);
222
+ if (ScaffoldMessenger.of(context).mounted) {
304
223
  ScaffoldMessenger.of(context).showSnackBar(
305
- const SnackBar(content: Text('Authentication token not available.')),
306
- );
307
- return;
308
- }
309
- try {
310
- final success = await _transactionService.deleteRrspContribution(contributionId, token: _token);
311
- if (success) {
312
- ScaffoldMessenger.of(context).showSnackBar(
313
- const SnackBar(content: Text('Contribution deleted successfully.')),
314
- );
315
- _fetchData(); // Refresh data after deletion
316
- } else {
317
- ScaffoldMessenger.of(context).showSnackBar(
318
- const SnackBar(content: Text('Failed to delete contribution.')),
319
- );
320
- }
321
- } catch (e) {
322
- ScaffoldMessenger.of(context).showSnackBar(
323
- SnackBar(content: Text('Error deleting contribution: $e')),
224
+ SnackBar(
225
+ content: Text(success
226
+ ? 'Contribution deleted successfully.'
227
+ : 'Failed to delete contribution.'),
228
+ ),
324
229
  );
325
230
  }
326
231
  }
@@ -0,0 +1,192 @@
1
+ import 'package:flutter/material.dart';
2
+ import 'package:personal_finance_frontend_core_services/services/crypto_service.dart';
3
+ import 'package:personal_finance_frontend_core_services/services/transaction_service.dart';
4
+
5
+ class CryptoAccountViewModel extends ChangeNotifier {
6
+ final TransactionService _transactionService = TransactionService();
7
+ final CryptoService _cryptoService = CryptoService();
8
+
9
+ final String accountName;
10
+ String? _token;
11
+
12
+ List<Map<String, dynamic>> _trades = [];
13
+ List<Map<String, dynamic>> _assets = [];
14
+ List<Map<String, dynamic>> _portfolioSummary = [];
15
+ Map<String, double> _livePrices = {};
16
+ double _cashBalance = 0.0;
17
+ double _accountTotalValue = 0.0;
18
+ bool _isLoading = true;
19
+ bool _isFetchingPrices = false;
20
+
21
+ // Getters
22
+ List<Map<String, dynamic>> get trades => _trades;
23
+ List<Map<String, dynamic>> get assets => _assets;
24
+ List<Map<String, dynamic>> get portfolioSummary => _portfolioSummary;
25
+ Map<String, double> get livePrices => _livePrices;
26
+ double get cashBalance => _cashBalance;
27
+ double get accountTotalValue => _accountTotalValue;
28
+ bool get isLoading => _isLoading;
29
+ bool get isFetchingPrices => _isFetchingPrices;
30
+ String? get token => _token;
31
+
32
+ CryptoAccountViewModel({required this.accountName, required String? token}) {
33
+ _token = token;
34
+ fetchData();
35
+ }
36
+
37
+ void setTokenAndFetch(String? token) {
38
+ _token = token;
39
+ fetchData();
40
+ }
41
+
42
+ Future<void> fetchData() async {
43
+ if (_token == null) return;
44
+ _isLoading = true;
45
+ notifyListeners();
46
+
47
+ try {
48
+ final futures = <Future>[
49
+ _transactionService.getTrades(investmentAccount: accountName, token: _token),
50
+ _transactionService.getAccountBalance(accountName, token: _token),
51
+ _transactionService.getAssets(investmentAccount: accountName, token: _token),
52
+ _transactionService.getTotalPortfolioBookCost(token: _token),
53
+ ];
54
+ final results = await Future.wait(futures);
55
+
56
+ _trades = results[0] as List<Map<String, dynamic>>;
57
+ _cashBalance = double.tryParse(results[1]?.toString() ?? '0.0') ?? 0.0;
58
+ _assets = results[2] as List<Map<String, dynamic>>;
59
+ final totalPortfolioBookCost = double.tryParse(results[3]?.toString() ?? '0.0') ?? 0.0;
60
+
61
+ _calculateAndApplyPortfolioSummary(totalPortfolioBookCost);
62
+
63
+ await _fetchLivePrices();
64
+
65
+ } catch (e) {
66
+ debugPrint('Error fetching data: $e');
67
+ } finally {
68
+ _isLoading = false;
69
+ notifyListeners();
70
+ }
71
+ }
72
+
73
+ Future<void> _fetchLivePrices() async {
74
+ if (_portfolioSummary.isEmpty) return;
75
+ _isFetchingPrices = true;
76
+ notifyListeners();
77
+
78
+ final idsToFetch = _portfolioSummary
79
+ .map((p) => p['id_crypto'] as String? ?? p['symbol'] as String)
80
+ .where((s) => s.isNotEmpty)
81
+ .toList();
82
+
83
+ if (idsToFetch.isEmpty) {
84
+ _isFetchingPrices = false;
85
+ notifyListeners();
86
+ return;
87
+ }
88
+
89
+ final livePrices = await _cryptoService.getLiveCryptoPrices(idsToFetch);
90
+ _livePrices = livePrices;
91
+
92
+ _recalculateMarketValue();
93
+
94
+ _isFetchingPrices = false;
95
+ notifyListeners();
96
+ }
97
+
98
+ void _calculateAndApplyPortfolioSummary(double totalPortfolioBookCost) {
99
+ Map<String, dynamic> summary = {};
100
+ double accountPortfolioBookCost = 0;
101
+
102
+ for (var trade in _trades) {
103
+ final asset = trade['asset'];
104
+ if (asset == null || asset['symbol'] == null) continue;
105
+ String symbol = asset['symbol'];
106
+ String idCrypto = asset['id_crypto'] ?? symbol;
107
+ double shares = double.tryParse(trade['shares']?.toString() ?? '0.0') ?? 0.0;
108
+ double price = double.tryParse(trade['price']?.toString() ?? '0.0') ?? 0.0;
109
+ String tradeType = trade['trade_type'];
110
+
111
+ if (!summary.containsKey(symbol)) {
112
+ summary[symbol] = {
113
+ 'symbol': symbol,
114
+ 'id_crypto': idCrypto,
115
+ 'name': asset['name'] ?? symbol,
116
+ 'shares': 0.0,
117
+ 'total_cost': 0.0,
118
+ };
119
+ }
120
+
121
+ if (tradeType == 'buy') {
122
+ summary[symbol]['shares'] += shares;
123
+ summary[symbol]['total_cost'] += shares * price;
124
+ } else if (tradeType == 'sell') {
125
+ double originalShares = summary[symbol]['shares'];
126
+ if (originalShares > 0) {
127
+ double avgPrice = summary[symbol]['total_cost'] / originalShares;
128
+ summary[symbol]['total_cost'] -= shares * avgPrice;
129
+ }
130
+ summary[symbol]['shares'] -= shares;
131
+ }
132
+ }
133
+
134
+ summary.removeWhere((key, value) => value['shares'] < 0.01);
135
+ accountPortfolioBookCost = summary.values.fold(0.0, (sum, item) => sum + item['total_cost']);
136
+
137
+ List<Map<String, dynamic>> result = [];
138
+ summary.forEach((symbol, data) {
139
+ double shares = data['shares'];
140
+ double totalCost = data['total_cost'];
141
+ data['avg_price'] = (shares > 0) ? totalCost / shares : 0.0;
142
+ data['account_allocation'] = (accountPortfolioBookCost > 0) ? (totalCost / accountPortfolioBookCost) * 100 : 0.0;
143
+ data['stocks_allocation'] = (totalPortfolioBookCost > 0) ? (totalCost / totalPortfolioBookCost) * 100 : 0.0;
144
+ result.add(data);
145
+ });
146
+
147
+ _portfolioSummary = result;
148
+ _recalculateMarketValue();
149
+ }
150
+
151
+ void _recalculateMarketValue() {
152
+ double newTotalValue = _cashBalance;
153
+ for (final position in _portfolioSummary) {
154
+ final shares = double.tryParse(position['shares']?.toString() ?? '0.0') ?? 0.0;
155
+ final totalCost = double.tryParse(position['total_cost']?.toString() ?? '0.0') ?? 0.0;
156
+ final idForLookup = (position['id_crypto'] as String? ?? position['symbol'] as String).toLowerCase();
157
+ final livePrice = _livePrices[idForLookup];
158
+
159
+ double currentMarketValue;
160
+ if (livePrice != null) {
161
+ currentMarketValue = shares * livePrice;
162
+ newTotalValue += currentMarketValue;
163
+ } else {
164
+ currentMarketValue = totalCost;
165
+ newTotalValue += totalCost;
166
+ }
167
+
168
+ double totalReturnValue = currentMarketValue - totalCost;
169
+ double percentageReturn = (totalCost > 0) ? (totalReturnValue / totalCost) * 100 : 0.0;
170
+
171
+ position['market_value'] = currentMarketValue;
172
+ position['total_return_value'] = totalReturnValue;
173
+ position['percentage_return'] = percentageReturn;
174
+ }
175
+ _accountTotalValue = newTotalValue;
176
+ }
177
+
178
+ Future<bool> deleteTrade(int tradeId) async {
179
+ if (_token == null) return false;
180
+
181
+ try {
182
+ final success = await _transactionService.deleteTrade(tradeId, token: _token);
183
+ if (success) {
184
+ fetchData();
185
+ }
186
+ return success;
187
+ } catch (e) {
188
+ debugPrint("Error deleting trade: $e");
189
+ return false;
190
+ }
191
+ }
192
+ }