383 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			383 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
| import 'dart:async';
 | |
| 
 | |
| import 'package:isar/isar.dart';
 | |
| import 'package:get_it/get_it.dart';
 | |
| import 'package:more/collection.dart';
 | |
| import 'package:okane/database/collections/account.dart';
 | |
| import 'package:okane/database/collections/beneficiary.dart';
 | |
| import 'package:okane/database/collections/expense_category.dart';
 | |
| import 'package:okane/database/collections/loan.dart';
 | |
| import 'package:okane/database/collections/recurrent.dart';
 | |
| import 'package:okane/database/collections/template.dart';
 | |
| import 'package:okane/database/collections/transaction.dart';
 | |
| import 'package:okane/ui/state/core.dart';
 | |
| import 'package:okane/ui/utils.dart';
 | |
| import 'package:path_provider/path_provider.dart';
 | |
| 
 | |
| import 'collections/budget.dart';
 | |
| 
 | |
| Future<Isar> openDatabase() async {
 | |
|   final dir = await getApplicationDocumentsDirectory();
 | |
|   return Isar.open([
 | |
|     AccountSchema,
 | |
|     BeneficiarySchema,
 | |
|     TransactionSchema,
 | |
|     TransactionTemplateSchema,
 | |
|     RecurringTransactionSchema,
 | |
|     ExpenseCategorySchema,
 | |
|     BudgetSchema,
 | |
|     BudgetItemSchema,
 | |
|     LoanSchema,
 | |
|     LoanChangeSchema,
 | |
|   ], directory: dir.path);
 | |
| }
 | |
| 
 | |
| Future<List<Account>> getAccounts() {
 | |
|   return GetIt.I.get<Isar>().accounts.where().findAll();
 | |
| }
 | |
| 
 | |
| Future<double> getTotalBalance(Account account) async {
 | |
|   return GetIt.I
 | |
|       .get<Isar>()
 | |
|       .transactions
 | |
|       .filter()
 | |
|       .account((q) => q.idEqualTo(account.id))
 | |
|       .amountProperty()
 | |
|       .sum();
 | |
| }
 | |
| 
 | |
| Future<List<Transaction>> getLastTransactions(
 | |
|   Account account,
 | |
|   DateTime today,
 | |
|   int days,
 | |
| ) async {
 | |
|   return GetIt.I
 | |
|       .get<Isar>()
 | |
|       .transactions
 | |
|       .filter()
 | |
|       .account((q) => q.idEqualTo(account.id))
 | |
|       .dateGreaterThan(toMidnight(today.subtract(Duration(days: days))))
 | |
|       .findAll();
 | |
| }
 | |
| 
 | |
| Future<List<RecurringTransaction>> getRecurringTransactions(Account? account) {
 | |
|   if (account == null) {
 | |
|     return Future.value([]);
 | |
|   }
 | |
| 
 | |
|   return GetIt.I
 | |
|       .get<Isar>()
 | |
|       .recurringTransactions
 | |
|       .filter()
 | |
|       .account((q) => q.idEqualTo(account.id))
 | |
|       .findAll();
 | |
| }
 | |
| 
 | |
| Stream<void> watchRecurringTransactions(Account account) {
 | |
|   final account = GetIt.I.get<CoreCubit>().activeAccount!;
 | |
|   return GetIt.I
 | |
|       .get<Isar>()
 | |
|       .recurringTransactions
 | |
|       .filter()
 | |
|       .account((q) => q.idEqualTo(account.id))
 | |
|       .build()
 | |
|       .watchLazy(fireImmediately: true);
 | |
| }
 | |
| 
 | |
| Future<void> upsertAccount(Account account) async {
 | |
|   final db = GetIt.I.get<Isar>();
 | |
|   return db.writeTxn(() async {
 | |
|     await db.accounts.put(account);
 | |
|   });
 | |
| }
 | |
| 
 | |
| Future<void> upsertBeneficiary(Beneficiary beneficiary) async {
 | |
|   final db = GetIt.I.get<Isar>();
 | |
|   return db.writeTxn(() async {
 | |
|     await db.beneficiarys.put(beneficiary);
 | |
|     await beneficiary.account.save();
 | |
|   });
 | |
| }
 | |
| 
 | |
| Future<Beneficiary?> getAccountBeneficiary(Account account) {
 | |
|   return GetIt.I
 | |
|       .get<Isar>()
 | |
|       .beneficiarys
 | |
|       .filter()
 | |
|       .account((q) => q.idEqualTo(account.id))
 | |
|       .findFirst();
 | |
| }
 | |
| 
 | |
| Future<void> upsertTransactionTemplate(TransactionTemplate template) async {
 | |
|   final db = GetIt.I.get<Isar>();
 | |
|   return db.writeTxn(() async {
 | |
|     await db.transactionTemplates.put(template);
 | |
|     await template.beneficiary.save();
 | |
|     await template.account.save();
 | |
|     await template.expenseCategory.save();
 | |
|   });
 | |
| }
 | |
| 
 | |
| Future<void> deleteRecurringTransactionTemplate(RecurringTransaction template) {
 | |
|   final db = GetIt.I.get<Isar>();
 | |
|   return db.writeTxn(() async {
 | |
|     await db.transactionTemplates.delete(template.template.value!.id);
 | |
|     await db.recurringTransactions.delete(template.id);
 | |
|   });
 | |
| }
 | |
| 
 | |
| Future<void> upsertRecurringTransaction(RecurringTransaction template) async {
 | |
|   final db = GetIt.I.get<Isar>();
 | |
|   return db.writeTxn(() async {
 | |
|     await db.recurringTransactions.put(template);
 | |
|     await template.template.save();
 | |
|     await template.account.save();
 | |
|   });
 | |
| }
 | |
| 
 | |
| Future<void> upsertTransaction(Transaction transaction) async {
 | |
|   final db = GetIt.I.get<Isar>();
 | |
|   return db.writeTxn(() async {
 | |
|     await db.transactions.put(transaction);
 | |
|     await transaction.beneficiary.save();
 | |
|     await transaction.account.save();
 | |
|     await transaction.expenseCategory.save();
 | |
|   });
 | |
| }
 | |
| 
 | |
| Stream<void> watchAccounts() {
 | |
|   return GetIt.I.get<Isar>().accounts.watchLazy();
 | |
| }
 | |
| 
 | |
| Stream<void> watchTransactionTemplates(Account account) {
 | |
|   return GetIt.I
 | |
|       .get<Isar>()
 | |
|       .transactionTemplates
 | |
|       .filter()
 | |
|       .account((q) => q.idEqualTo(account.id))
 | |
|       .watchLazy(fireImmediately: true);
 | |
| }
 | |
| 
 | |
| Future<void> deleteTransactionTemplate(TransactionTemplate template) {
 | |
|   final db = GetIt.I.get<Isar>();
 | |
|   return db.writeTxn(() async {
 | |
|     await db.transactionTemplates.delete(template.id);
 | |
|   });
 | |
| }
 | |
| 
 | |
| Future<List<TransactionTemplate>> getTransactionTemplates(Account? account) {
 | |
|   if (account == null) {
 | |
|     return Future.value([]);
 | |
|   }
 | |
| 
 | |
|   return GetIt.I
 | |
|       .get<Isar>()
 | |
|       .transactionTemplates
 | |
|       .filter()
 | |
|       .account((q) => q.idEqualTo(account.id))
 | |
|       .findAll();
 | |
| }
 | |
| 
 | |
| Stream<void> watchTransactions(Account account) {
 | |
|   return GetIt.I
 | |
|       .get<Isar>()
 | |
|       .transactions
 | |
|       .filter()
 | |
|       .account((q) => q.idEqualTo(account.id))
 | |
|       .watchLazy(fireImmediately: true);
 | |
| }
 | |
| 
 | |
| Future<List<Transaction>> getTransactions(Account? account) {
 | |
|   if (account == null) {
 | |
|     return Future.value([]);
 | |
|   }
 | |
| 
 | |
|   return GetIt.I
 | |
|       .get<Isar>()
 | |
|       .transactions
 | |
|       .filter()
 | |
|       .account((q) => q.idEqualTo(account.id))
 | |
|       .findAll();
 | |
| }
 | |
| 
 | |
| Stream<void> watchBeneficiaries() {
 | |
|   return GetIt.I.get<Isar>().beneficiarys.watchLazy(fireImmediately: true);
 | |
| }
 | |
| 
 | |
| Future<List<Beneficiary>> getBeneficiaries() {
 | |
|   return GetIt.I.get<Isar>().beneficiarys.where().findAll();
 | |
| }
 | |
| 
 | |
| Stream<Beneficiary?> watchBeneficiaryObject(Id id) {
 | |
|   return GetIt.I.get<Isar>().beneficiarys.watchObject(id);
 | |
| }
 | |
| 
 | |
| Future<void> upsertExpenseCategory(ExpenseCategory category) {
 | |
|   final db = GetIt.I.get<Isar>();
 | |
|   return db.writeTxn(() => db.expenseCategorys.put(category));
 | |
| }
 | |
| 
 | |
| Future<List<ExpenseCategory>> getExpenseCategories() {
 | |
|   return GetIt.I.get<Isar>().expenseCategorys.where().findAll();
 | |
| }
 | |
| 
 | |
| Stream<void> watchExpenseCategory() {
 | |
|   return GetIt.I.get<Isar>().expenseCategorys.watchLazy(fireImmediately: true);
 | |
| }
 | |
| 
 | |
| Stream<void> watchBudgets(Account account) {
 | |
|   return GetIt.I
 | |
|       .get<Isar>()
 | |
|       .budgets
 | |
|       .filter()
 | |
|       .account((q) => q.idEqualTo(account.id))
 | |
|       .watchLazy(fireImmediately: true);
 | |
| }
 | |
| 
 | |
| Future<List<Budget>> getBudgets(Account? account) {
 | |
|   if (account == null) {
 | |
|     return Future.value([]);
 | |
|   }
 | |
| 
 | |
|   return GetIt.I
 | |
|       .get<Isar>()
 | |
|       .budgets
 | |
|       .filter()
 | |
|       .account((q) => q.idEqualTo(account.id))
 | |
|       .findAll();
 | |
| }
 | |
| 
 | |
| Future<void> upsertBudget(Budget budget) {
 | |
|   final db = GetIt.I.get<Isar>();
 | |
|   return db.writeTxn(() async {
 | |
|     await db.budgets.put(budget);
 | |
|     await budget.items.save();
 | |
|     await budget.account.save();
 | |
|   });
 | |
| }
 | |
| 
 | |
| Future<void> upsertBudgetItem(BudgetItem item) {
 | |
|   final db = GetIt.I.get<Isar>();
 | |
|   return db.writeTxn(() async {
 | |
|     await db.budgetItems.put(item);
 | |
|     await item.expenseCategory.save();
 | |
|   });
 | |
| }
 | |
| 
 | |
| enum TransactionQueryDateOption { thisMonth }
 | |
| 
 | |
| Future<List<Transaction>> getTransactionsInTimeframe(
 | |
|   Account account,
 | |
|   DateTime today,
 | |
|   TransactionQueryDateOption option,
 | |
| ) async {
 | |
|   final lower = switch (option) {
 | |
|     TransactionQueryDateOption.thisMonth => DateTime(
 | |
|       today.year,
 | |
|       today.month,
 | |
|       0,
 | |
|     ),
 | |
|   };
 | |
|   final upper = switch (option) {
 | |
|     TransactionQueryDateOption.thisMonth => monthEnding(today),
 | |
|   };
 | |
| 
 | |
|   return GetIt.I
 | |
|       .get<Isar>()
 | |
|       .transactions
 | |
|       .filter()
 | |
|       .account((q) => q.idEqualTo(account.id))
 | |
|       .dateBetween(lower, upper)
 | |
|       .findAll();
 | |
| }
 | |
| 
 | |
| Future<void> upsertLoan(Loan loan) {
 | |
|   final db = GetIt.I.get<Isar>();
 | |
|   return db.writeTxn(() async {
 | |
|     await db.loans.put(loan);
 | |
|     await loan.beneficiary.save();
 | |
|     await loan.changes.save();
 | |
|   });
 | |
| }
 | |
| 
 | |
| Future<List<Loan>> getLoans() {
 | |
|   return GetIt.I.get<Isar>().loans.where().findAll();
 | |
| }
 | |
| 
 | |
| Stream<void> watchLoans() {
 | |
|   return GetIt.I.get<Isar>().loans.where().watchLazy(fireImmediately: true);
 | |
| }
 | |
| 
 | |
| Future<void> deleteLoan(Loan loan) async {
 | |
|   final db = GetIt.I.get<Isar>();
 | |
|   final loanChangeIds = loan.changes.map((c) => c.id).toList();
 | |
|   return db.writeTxn(() async {
 | |
|     await db.loans.delete(loan.id);
 | |
|     await db.loanChanges.deleteAll(loanChangeIds);
 | |
|   });
 | |
| }
 | |
| 
 | |
| Future<void> upsertLoanChange(LoanChange loanChange) {
 | |
|   final db = GetIt.I.get<Isar>();
 | |
|   return db.writeTxn(() async {
 | |
|     await db.loanChanges.put(loanChange);
 | |
|   });
 | |
| }
 | |
| 
 | |
| Future<void> deleteLoanChange(LoanChange loanChange) {
 | |
|   final db = GetIt.I.get<Isar>();
 | |
|   return db.writeTxn(() async {
 | |
|     await db.loanChanges.delete(loanChange.id);
 | |
|   });
 | |
| }
 | |
| 
 | |
| Future<double> getTotalLoanSum() async {
 | |
|   final loans = await getLoans();
 | |
|   return loans
 | |
|       .map(
 | |
|         (loan) =>
 | |
|             loan.changes.map((l) => l.amount).reduce((acc, val) => acc + val),
 | |
|       )
 | |
|       .reduce((acc, val) => acc + val);
 | |
| }
 | |
| 
 | |
| Future<void> deleteAccount(Account account) async {
 | |
|   final db = GetIt.I.get<Isar>();
 | |
|   final affectedBudgets =
 | |
|       await db.budgets
 | |
|           .filter()
 | |
|           .account((q) => q.idEqualTo(account.id))
 | |
|           .findAll();
 | |
|   final budgetIds = affectedBudgets.map((a) => a.id).toList();
 | |
|   final budgetItemIds =
 | |
|       affectedBudgets.map((a) => a.items.map((i) => i.id)).flatten().toList();
 | |
|   return db.writeTxn(() async {
 | |
|     // Remove transactions
 | |
|     await db.transactions
 | |
|         .filter()
 | |
|         .account((q) => q.idEqualTo(account.id))
 | |
|         .deleteAll();
 | |
|     await db.beneficiarys
 | |
|         .filter()
 | |
|         .account((q) => q.idEqualTo(account.id))
 | |
|         .deleteAll();
 | |
| 
 | |
|     // Remove templates
 | |
|     await db.recurringTransactions
 | |
|         .filter()
 | |
|         .account((q) => q.idEqualTo(account.id))
 | |
|         .deleteAll();
 | |
|     await db.transactionTemplates
 | |
|         .filter()
 | |
|         .account((q) => q.idEqualTo(account.id))
 | |
|         .deleteAll();
 | |
| 
 | |
|     // Remove all budgets
 | |
|     await db.budgetItems.deleteAll(budgetItemIds);
 | |
|     await db.budgets.deleteAll(budgetIds);
 | |
| 
 | |
|     // Remove account
 | |
|     await db.accounts.delete(account.id);
 | |
|   });
 | |
| }
 |