Files
Krezus/lib/domains/budget/budget_bloc.dart

241 lines
8.1 KiB
Dart

import 'dart:async';
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:tunas/repositories/metadata/models/budget.dart';
import 'package:tunas/repositories/metadata/metadata_repository.dart';
import 'package:tunas/repositories/transactions/transactions_repository.dart';
part 'budget_event.dart';
part 'budget_state.dart';
class BudgetBloc extends Bloc<BudgetEvent, BudgetState> {
final MetadataRepository _metadataRepository;
final TransactionsRepository _transactionsRepository;
Timer? setValueTimer;
BudgetBloc({required MetadataRepository metadataRepository, required TransactionsRepository transactionsRepository}) : _metadataRepository = metadataRepository, _transactionsRepository = transactionsRepository, super(const BudgetState()) {
on<BudgetsLoad>(_onBudgetsLoad);
on<BudgetAdd>(_onBudgetAdd);
on<BudgetRemove>(_onBudgetRemove);
on<BudgetSetValue>(_onBudgetSetValue);
on<BudgetSetLabel>(_onBudgetSetLabel);
on<BudgetCompareNext>(_onBudgetCompareNext);
on<BudgetComparePrevious>(_onBudgetComparePrevious);
on<BudgetSetCompare>(_onBudgetSetCompare);
on<BudgetSetInitial>(_onBudgetSetInitial);
_metadataRepository
.getBudgetsStream()
.listen((budgets) => add(BudgetsLoad(budgets)));
_transactionsRepository
.getTransactionsStream()
.listen((transactions) => add(BudgetSetCompare()));
}
_onBudgetsLoad(
BudgetsLoad event, Emitter<BudgetState> emit
) {
emit(_computeState(event.budgets, null));
}
FutureOr<void> _onBudgetAdd(BudgetAdd event, Emitter<BudgetState> emit) {
Budget budget = Budget(
label: event.label,
);
List<Budget> budgets = _computeBudgets(budget);
_saveBudget(budgets);
emit(_computeState(budgets, null));
}
FutureOr<void> _onBudgetRemove(BudgetRemove event, Emitter<BudgetState> emit) {
List<Budget> budgets = _metadataRepository.getBudgets();
Budget budgetToRemove = event.budget;
budgets.removeWhere((budget) => budget.label == budgetToRemove.label);
emit(_computeState(_metadataRepository.saveBudgets(budgets), null));
}
void _onBudgetSetValue(BudgetSetValue event, Emitter<BudgetState> emit) {
Budget budgetToUpdate = event.budget;
double newValue = event.value;
if (state.remainingBudget - (event.value - budgetToUpdate.value) < 0) {
newValue = event.budget.value + state.remainingBudget;
}
// if (state.remainingBudget - event.value < 0 && state.remainingBudget < 10) {
// budgetToUpdate.value =
// } else {
// budgetToUpdate.value = event.value;
// }
budgetToUpdate.value = newValue;
List<Budget> budgets = _computeBudgets(budgetToUpdate);
emit(_computeState(budgets, null));
setValueTimer?.cancel();
setValueTimer = Timer(
const Duration(milliseconds: 100),
() {
_saveBudget(budgets);
}
);
}
void _onBudgetSetLabel(BudgetSetLabel event, Emitter<BudgetState> emit) {
Budget budgetToUpdate = event.budget;
budgetToUpdate.label = event.label;
List<Budget> budgets = _computeBudgets(budgetToUpdate);
_saveBudget(budgets);
emit(_computeState(budgets, null));
}
void _onBudgetCompareNext(BudgetCompareNext event, Emitter<BudgetState> emit) {
num compareMonth = state.compareMonth;
num compareYear = state.compareYear;
if (state.compareMonth == 12) {
compareMonth = 1;
compareYear++;
} else {
compareMonth++;
}
if (state.lastDate != null && state.lastDate!.isBefore(DateTime(compareYear.toInt(), compareMonth.toInt()))) {
return;
}
final compareResult = _computeCompareBudget(state.budgets, compareYear, compareMonth);
emit(state.copyWith(
compareBudgets: compareResult.$1,
otherBudgets: compareResult.$2,
compareMonth: compareMonth,
compareYear: compareYear,
));
}
void _onBudgetComparePrevious(BudgetComparePrevious event, Emitter<BudgetState> emit) {
num compareMonth = state.compareMonth;
num compareYear = state.compareYear;
if (state.compareMonth == 1) {
compareMonth = 12;
compareYear--;
} else {
compareMonth--;
}
if (state.firstDate != null && state.firstDate!.isAfter(DateTime(compareYear.toInt(), compareMonth.toInt()))) {
return;
}
final compareResult = _computeCompareBudget(state.budgets, compareYear, compareMonth);
emit(state.copyWith(
compareBudgets: compareResult.$1,
otherBudgets: compareResult.$2,
compareMonth: compareMonth,
compareYear: compareYear,
));
}
_onBudgetSetCompare(BudgetSetCompare event, Emitter<BudgetState> emit) {
DateTime firstDate = DateTime.now();
DateTime lastDate = DateTime.fromMicrosecondsSinceEpoch(0);
for (var transaction in _transactionsRepository.getTransactions()) {
if (firstDate.compareTo(transaction.date) > 0) {
firstDate = transaction.date;
}
if (lastDate.compareTo(transaction.date) < 0) {
lastDate = transaction.date;
}
}
num compareMonth = state.compareMonth;
num compareYear = state.compareYear;
if (compareMonth == 1 && compareYear == 2000) {
compareMonth = lastDate.month;
compareYear = lastDate.year;
}
final compareResult = _computeCompareBudget(state.budgets, compareYear, compareMonth);
emit(state.copyWith(
firstDate: firstDate,
lastDate: lastDate,
compareMonth: compareMonth,
compareYear: compareYear,
compareBudgets: compareResult.$1,
otherBudgets: compareResult.$2,
));
}
void _onBudgetSetInitial(BudgetSetInitial event, Emitter<BudgetState> emit) {
emit(_computeState(state.budgets, event.value));
}
List<Budget> _computeBudgets(Budget budgetToSave) {
List<Budget> budgets = _metadataRepository.getBudgets();
try {
Budget budgetFound = budgets.firstWhere((category) => category.label == budgetToSave.label);
budgetFound.value = budgetToSave.value;
} catch (e) {
if (budgets.isEmpty) {
budgets = [budgetToSave];
} else {
budgets.add(budgetToSave);
}
}
return budgets;
}
List<Budget> _saveBudget(List<Budget> budgets) {
return _metadataRepository.saveBudgets(budgets);
}
(List<Budget> compareBudgets, List<Budget> otherBudgets) _computeCompareBudget(List<Budget> budgets, num year, num month) {
Map<String, Budget> compareBudgetMap = { for (var budget in budgets) budget.label : Budget(label: budget.label)};
Budget otherBudget = Budget(label: 'Hors budget');
Map<String, Budget> otherBudgetMap = {};
List<Budget> compareBudgets = [];
_transactionsRepository.getTransactions()
.where((transaction) => transaction.value < 0 && transaction.date.year == year && transaction.date.month == month)
.forEach((transaction) {
Budget? budget = compareBudgetMap[transaction.category];
if (budget == null) {
otherBudget.value += transaction.value.abs();
Budget? otherBudgetFromMap = otherBudgetMap[transaction.category];
if (otherBudgetFromMap == null) {
otherBudgetMap[transaction.category] = Budget(label: transaction.category, value: transaction.value.abs());
} else {
otherBudgetFromMap.value += transaction.value.abs();
}
} else {
budget.value += transaction.value.abs();
}
});
compareBudgets.addAll(compareBudgetMap.values);
compareBudgets.add(otherBudget);
return (compareBudgets, otherBudgetMap.values.toList()..sort((a, b) => b.value.compareTo(a.value)));
}
BudgetState _computeState(List<Budget> budgets, double? initialBudget) {
final compareResult = _computeCompareBudget(state.budgets, state.compareYear, state.compareMonth);
final budgetValues = budgets.map((budget) => budget.value);
final budgetReducedValues = budgetValues.isEmpty ? 0 : budgetValues.reduce((value, element) => value + element);
return state.copyWith(
budgets: budgets,
initialBudget: (initialBudget ?? state.initialBudget),
remainingBudget: (initialBudget ?? state.initialBudget) - budgetReducedValues,
compareBudgets: compareResult.$1,
otherBudgets: compareResult.$2,
);
}
}