213 lines
6.6 KiB
Dart
213 lines
6.6 KiB
Dart
import 'dart:async';
|
|
import 'dart:io';
|
|
|
|
import 'package:csv/csv.dart';
|
|
import 'package:equatable/equatable.dart';
|
|
import 'package:file_picker/file_picker.dart';
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
import 'package:tunas/repositories/metadata/models/category.dart';
|
|
import 'package:tunas/repositories/metadata/metadata_repository.dart';
|
|
import 'package:tunas/repositories/metadata/models/account.dart';
|
|
import 'package:tunas/repositories/transactions/models/transaction.dart';
|
|
import 'package:tunas/repositories/transactions/transactions_repository.dart';
|
|
import 'package:uuid/uuid.dart';
|
|
|
|
part 'account_event.dart';
|
|
part 'account_state.dart';
|
|
|
|
final colors = [
|
|
'FF74feff',
|
|
'FF64c0ff',
|
|
'FF5873fe',
|
|
'FF4b4cff',
|
|
'FFc0fcfd',
|
|
'FFa7caff',
|
|
'FF8d7efd',
|
|
'FF7f65fe',
|
|
'FFe7ffff',
|
|
'FFd7dafd',
|
|
'FFd8a6ff',
|
|
'FFc065fe',
|
|
'FFffffff',
|
|
'FFffe6fe',
|
|
'FFffbdff',
|
|
'FFff80fe',
|
|
];
|
|
|
|
class AccountBloc extends Bloc<AccountEvent, AccountState> {
|
|
final TransactionsRepository _transactionsRepository;
|
|
final MetadataRepository _metadataRepository;
|
|
|
|
AccountBloc({
|
|
required MetadataRepository metadataRepository,
|
|
required TransactionsRepository transactionsRepository,
|
|
})
|
|
: _metadataRepository = metadataRepository,
|
|
_transactionsRepository = transactionsRepository,
|
|
super(const AccountState()
|
|
) {
|
|
on<AccountImportCSV>(_onAccountImportCSV);
|
|
on<AccountLoad>(_onAccountLoad);
|
|
// on<AccountExportJSON>(_onAccountImportJSON);
|
|
// on<AccountExportCSV>(_onAccountImportJSON);
|
|
on<AccountAdd>(_onAccountAdd);
|
|
on<AccountRemove>(_onAcountRemove);
|
|
on<AccountEditLabel>(_onAccountEditLabel);
|
|
on<AccountEditSaving>(_onAccountEditSaving);
|
|
on<AccountEditColor>(_onAccountEditColor);
|
|
|
|
_metadataRepository
|
|
.getAccountsStream()
|
|
.listen((subAccounts) => add(AccountLoad(subAccounts)));
|
|
}
|
|
|
|
double _universalConvertToDouble(dynamic value) {
|
|
if (value is String) {
|
|
return double.parse(value);
|
|
} else if (value is int) {
|
|
return value.toDouble();
|
|
} else if (value is double) {
|
|
return value;
|
|
} else {
|
|
throw Error();
|
|
}
|
|
}
|
|
|
|
_onAccountImportCSV(AccountImportCSV event, Emitter<AccountState> emit) async {
|
|
int colorIndex = 0;
|
|
FilePickerResult? result = await FilePicker.platform.pickFiles();
|
|
|
|
final csvPath = result?.files.first.path;
|
|
if (csvPath != null) {
|
|
final File csv = File(csvPath);
|
|
final String csvFileContent = await csv.readAsString();
|
|
final List<List<dynamic>> csvList = const CsvToListConverter(fieldDelimiter: '|', eol: '\n').convert(csvFileContent);
|
|
|
|
final Map<String, Category> categoriesMap = {};
|
|
final Map<String, Account> accounts = {};
|
|
|
|
final transactions = csvList
|
|
.map((line) {
|
|
|
|
double value = _universalConvertToDouble(line[4]);
|
|
String? categoryLabel = line[1];
|
|
if (categoryLabel == null || categoryLabel == '') {
|
|
categoryLabel = 'N/A';
|
|
}
|
|
|
|
if (categoriesMap[categoryLabel] == null) {
|
|
// if (categoryLabel == 'N/A') {
|
|
// categoriesMap[categoryLabel] = Category(label: 'N/A', color: 'FFFF0000' );
|
|
// } else {
|
|
// String color = colorIndex >= colors.length ? 'FF0000FF' : colors[colorIndex];
|
|
// colorIndex++;
|
|
// categoriesMap[categoryLabel] = Category(label: categoryLabel, color: color );
|
|
// }
|
|
|
|
categoriesMap[categoryLabel] = Category(label: categoryLabel, color: value > 0 ? 'FF21E297' : 'FFFFB4AB' );
|
|
}
|
|
|
|
String accountLabel = line[3];
|
|
if (accounts[accountLabel] == null) {
|
|
accounts[accountLabel] = Account(label: accountLabel, color: 'FF74feff');
|
|
}
|
|
|
|
return Transaction(
|
|
uuid: const Uuid().v8(),
|
|
date: DateTime.parse(line[0]),
|
|
category: categoryLabel,
|
|
description: line[2],
|
|
account: line[3],
|
|
value: value
|
|
);
|
|
})
|
|
.toList();
|
|
|
|
await _metadataRepository.deleteMetadata();
|
|
await _transactionsRepository.deleteTransactions();
|
|
|
|
await _metadataRepository.saveAccounts(accounts.values.toList());
|
|
await _metadataRepository.saveBudgets([]);
|
|
await _metadataRepository.saveCategories(categoriesMap.values.toList());
|
|
await _transactionsRepository.saveTransactions(transactions);
|
|
}
|
|
}
|
|
|
|
_onAccountLoad(
|
|
AccountLoad event, Emitter<AccountState> emit
|
|
) {
|
|
emit(
|
|
state.copyWith(event.subAccounts)
|
|
);
|
|
}
|
|
|
|
_onAccountAdd(AccountAdd event, Emitter<AccountState> emit) async {
|
|
String uuid = const Uuid().v8();
|
|
Account account = Account(
|
|
label: 'Account $uuid',
|
|
color: 'FF74feff',
|
|
saving: false
|
|
);
|
|
emit(
|
|
state.copyWith(await _saveAccount(account))
|
|
);
|
|
}
|
|
|
|
_onAcountRemove(AccountRemove event, Emitter<AccountState> emit) async {
|
|
Account accountToRemove = event.account;
|
|
List<Account> accounts = state.accounts;
|
|
List<Transaction> transactions = _transactionsRepository.getTransactions();
|
|
|
|
if (transactions.any((transaction) => transaction.account == accountToRemove.label)) {
|
|
emit(AccountRemoveFail());
|
|
emit(AccountState(accounts: accounts));
|
|
} else {
|
|
accounts.removeWhere((account) => account.label == accountToRemove.label);
|
|
emit(AccountRemoveSucess());
|
|
emit(
|
|
state.copyWith(await _metadataRepository.saveAccounts(accounts))
|
|
);
|
|
}
|
|
}
|
|
|
|
_onAccountEditLabel(AccountEditLabel event, Emitter<AccountState> emit) async {
|
|
Account account = event.account;
|
|
// TODO check for existance, rename every transaction
|
|
}
|
|
|
|
_onAccountEditSaving(AccountEditSaving event, Emitter<AccountState> emit) async {
|
|
Account account = event.account;
|
|
account.saving = event.saving;
|
|
emit(
|
|
state.copyWith(await _saveAccount(account))
|
|
);
|
|
}
|
|
|
|
_onAccountEditColor(AccountEditColor event, Emitter<AccountState> emit) async {
|
|
Account account = event.account;
|
|
account.color = event.color;
|
|
emit(
|
|
state.copyWith(await _saveAccount(account))
|
|
);
|
|
}
|
|
|
|
Future<List<Account>> _saveAccount(Account accountToSave) async {
|
|
List<Account> accounts = _metadataRepository.getAccounts();
|
|
|
|
try {
|
|
Account accountFound = accounts.firstWhere((account) => account.label == accountToSave.label);
|
|
accountFound.color = accountToSave.color;
|
|
accountFound.saving = accountToSave.saving;
|
|
} catch (e) {
|
|
if (accounts.isEmpty) {
|
|
accounts = [accountToSave];
|
|
} else {
|
|
accounts.add(accountToSave);
|
|
}
|
|
}
|
|
|
|
await _metadataRepository.saveAccounts(accounts);
|
|
return accounts;
|
|
}
|
|
}
|