Start migration to sqlite using drift
This commit is contained in:
@@ -1,11 +1,9 @@
|
||||
import 'package:drift/drift.dart' show Value;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:okane/database/collections/account.dart';
|
||||
import 'package:okane/database/collections/beneficiary.dart';
|
||||
import 'package:okane/database/database.dart';
|
||||
import 'package:okane/database/sqlite.dart';
|
||||
import 'package:okane/ui/pages/account/breakdown_card.dart';
|
||||
import 'package:okane/ui/pages/account/delete_account.dart';
|
||||
import 'package:okane/ui/pages/account/loan_card.dart';
|
||||
import 'package:okane/ui/pages/account/total_balance_card.dart';
|
||||
import 'package:okane/ui/pages/account/upcoming_transactions_card.dart';
|
||||
@@ -27,7 +25,6 @@ class AccountListPageState extends State<AccountListPage> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final bloc = GetIt.I.get<CoreCubit>();
|
||||
return Stack(
|
||||
children: [
|
||||
ListView(
|
||||
@@ -66,9 +63,12 @@ class AccountListPageState extends State<AccountListPage> {
|
||||
children: [
|
||||
Text(state.accounts[index].name!),
|
||||
FutureBuilder(
|
||||
future: getTotalBalance(
|
||||
state.accounts[index],
|
||||
),
|
||||
future: GetIt.I
|
||||
.get<OkaneDatabase>()
|
||||
.transactionsDao
|
||||
.getTotalBalance([
|
||||
state.accounts[index].id,
|
||||
]),
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData) {
|
||||
return Container();
|
||||
@@ -141,16 +141,21 @@ class AccountListPageState extends State<AccountListPage> {
|
||||
onPressed: () async {
|
||||
if (_accountNameController.text.isEmpty) return;
|
||||
|
||||
final a =
|
||||
Account()..name = _accountNameController.text;
|
||||
final b =
|
||||
Beneficiary()
|
||||
..name = _accountNameController.text
|
||||
..account.value =
|
||||
GetIt.I.get<CoreCubit>().activeAccount
|
||||
..type = BeneficiaryType.account;
|
||||
await upsertAccount(a);
|
||||
await upsertBeneficiary(b);
|
||||
final db = GetIt.I.get<OkaneDatabase>();
|
||||
print("Adding account");
|
||||
final accountId = await db.accountsDao
|
||||
.upsertAccount(
|
||||
AccountsCompanion(
|
||||
name: Value(_accountNameController.text),
|
||||
),
|
||||
);
|
||||
print("Adding beneficiary");
|
||||
final b = BeneficiariesCompanion(
|
||||
name: Value(_accountNameController.text),
|
||||
accountId: Value(accountId),
|
||||
type: Value(BeneficiaryType.account),
|
||||
);
|
||||
await db.beneficiariesDao.upsertBeneficiary(b);
|
||||
_accountNameController.text = "";
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
|
||||
@@ -2,7 +2,7 @@ import 'dart:collection';
|
||||
import 'package:fl_chart/fl_chart.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:okane/database/database.dart';
|
||||
import 'package:okane/database/sqlite.dart';
|
||||
import 'package:okane/i18n/strings.g.dart';
|
||||
import 'package:okane/ui/state/core.dart';
|
||||
import 'package:okane/ui/utils.dart';
|
||||
@@ -26,21 +26,27 @@ class AccountBalanceGraphCard extends StatelessWidget {
|
||||
Future<List<FlSpot>> getAccountBalance() async {
|
||||
final coreCubit = GetIt.I.get<CoreCubit>();
|
||||
final today = toMidnight(DateTime.now());
|
||||
final transactions = await getLastTransactions(
|
||||
final db = GetIt.I.get<OkaneDatabase>();
|
||||
print("Getting transactions");
|
||||
final transactions = await db.transactionsDao.getLastTransactions(
|
||||
coreCubit.activeAccount!,
|
||||
today,
|
||||
30,
|
||||
);
|
||||
final totalBalance = await getTotalBalance(coreCubit.activeAccount!);
|
||||
print("Got transactions. Getting balance");
|
||||
final totalBalance = await db.transactionsDao.getTotalBalance([
|
||||
coreCubit.activeAccount!.id,
|
||||
]);
|
||||
print("Got balance");
|
||||
|
||||
// Compute the differences per day
|
||||
Map<int, double> differences = Map.fromEntries(
|
||||
List.generate(30, (i) => i).map((i) => MapEntry(i, 0)),
|
||||
);
|
||||
for (final item in transactions) {
|
||||
final diff = today.difference(toMidnight(item.date)).inDays;
|
||||
final diff = today.difference(toMidnight(item.transaction.date)).inDays;
|
||||
final balance = differences[diff]!;
|
||||
differences[diff] = balance + item.amount;
|
||||
differences[diff] = balance + item.transaction.amount;
|
||||
}
|
||||
|
||||
// Compute the balance
|
||||
@@ -74,6 +80,7 @@ class AccountBalanceGraphCard extends StatelessWidget {
|
||||
child: FutureBuilder(
|
||||
future: getAccountBalance(),
|
||||
builder: (context, snapshot) {
|
||||
print("SNAPSHOT: ${snapshot.data}");
|
||||
if (!snapshot.hasData) {
|
||||
return CircularProgressIndicator();
|
||||
}
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:fl_chart/fl_chart.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:okane/database/collections/transaction.dart';
|
||||
import 'package:okane/database/database.dart';
|
||||
import 'package:okane/database/sqlite.dart';
|
||||
import 'package:okane/i18n/strings.g.dart';
|
||||
import 'package:okane/ui/state/core.dart';
|
||||
import 'package:okane/ui/utils.dart';
|
||||
@@ -45,31 +42,31 @@ class LegendItem extends StatelessWidget {
|
||||
class BreakdownCard extends StatelessWidget {
|
||||
const BreakdownCard({super.key});
|
||||
|
||||
LegendData _computeSections(List<Transaction> transactions) {
|
||||
LegendData _computeSections(List<TransactionDto> transactions) {
|
||||
Map<String, double> expenses = {};
|
||||
Map<String, Color> colors = {};
|
||||
double usableMoney = 0;
|
||||
transactions.forEach((t) {
|
||||
String category;
|
||||
if (t.amount > 0) {
|
||||
if (t.transaction.amount > 0) {
|
||||
category = CATEGORY_INCOME;
|
||||
colors[CATEGORY_INCOME] = Colors.green;
|
||||
} else {
|
||||
if (t.expenseCategory.value?.name == null) {
|
||||
if (t.expenseCategory?.name == null) {
|
||||
category = CATEGORY_OTHER;
|
||||
colors[category] = Colors.red;
|
||||
} else {
|
||||
category = t.expenseCategory.value!.name;
|
||||
colors[category] = colorHash(t.expenseCategory.value!.name);
|
||||
category = t.expenseCategory!.name;
|
||||
colors[category] = colorHash(t.expenseCategory!.name);
|
||||
}
|
||||
}
|
||||
|
||||
expenses.update(
|
||||
category,
|
||||
(value) => value + t.amount.abs(),
|
||||
ifAbsent: () => t.amount.abs(),
|
||||
(value) => value + t.transaction.amount.abs(),
|
||||
ifAbsent: () => t.transaction.amount.abs().toDouble(),
|
||||
);
|
||||
usableMoney += t.amount;
|
||||
usableMoney += t.transaction.amount;
|
||||
});
|
||||
return (expenses: expenses, colors: colors, usable: usableMoney);
|
||||
}
|
||||
@@ -97,8 +94,13 @@ class BreakdownCard extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
final db = GetIt.I.get<OkaneDatabase>();
|
||||
return FutureBuilder(
|
||||
future: getLastTransactions(bloc.activeAccount!, DateTime.now(), 30),
|
||||
future: db.transactionsDao.getLastTransactions(
|
||||
bloc.activeAccount!,
|
||||
DateTime.now(),
|
||||
30,
|
||||
),
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData) {
|
||||
return _buildCard(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:okane/database/collections/account.dart';
|
||||
import 'package:okane/database/sqlite.dart';
|
||||
import 'package:okane/i18n/strings.g.dart';
|
||||
import 'package:okane/ui/state/core.dart';
|
||||
|
||||
@@ -39,7 +39,7 @@ class DeleteAccountPopup extends StatelessWidget {
|
||||
)
|
||||
: Text(
|
||||
t.pages.accounts.deleteAccount.content(
|
||||
name: account.name!,
|
||||
name: account.name,
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:okane/database/database.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:okane/database/sqlite.dart';
|
||||
import 'package:okane/i18n/strings.g.dart';
|
||||
import 'package:okane/ui/state/core.dart';
|
||||
import 'package:okane/ui/utils.dart';
|
||||
@@ -18,7 +19,7 @@ class TotalLoanCard extends StatelessWidget {
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(16),
|
||||
child: FutureBuilder(
|
||||
future: getTotalLoanSum(),
|
||||
future: GetIt.I.get<OkaneDatabase>().loansDao.getTotalLoanSum(),
|
||||
builder: (context, snapshot) {
|
||||
return Text(
|
||||
snapshot.hasData
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:okane/database/collections/account.dart';
|
||||
import 'package:okane/database/database.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:okane/database/sqlite.dart';
|
||||
import 'package:okane/i18n/strings.g.dart';
|
||||
import 'package:okane/ui/state/core.dart';
|
||||
import 'package:okane/ui/utils.dart';
|
||||
@@ -15,10 +15,13 @@ class TotalBalanceCard extends StatelessWidget {
|
||||
return 0;
|
||||
}
|
||||
|
||||
final results = await Future.wait(accounts.map(getTotalBalance).toList());
|
||||
final loanSum = await getTotalLoanSum();
|
||||
final db = GetIt.I.get<OkaneDatabase>();
|
||||
final totalBalance = await db.transactionsDao.getTotalBalance(
|
||||
accounts.map((a) => a.id),
|
||||
);
|
||||
|
||||
return results.reduce((acc, val) => acc + val) + loanSum;
|
||||
final loanSum = await db.loansDao.getTotalLoanSum();
|
||||
return totalBalance + loanSum;
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -32,6 +35,7 @@ class TotalBalanceCard extends StatelessWidget {
|
||||
child: FutureBuilder(
|
||||
future: _getTotalBalance(state.accounts),
|
||||
builder: (context, snapshot) {
|
||||
print("SNAPSHOT: ${snapshot.data}");
|
||||
return Text(
|
||||
snapshot.hasData
|
||||
? formatCurrency(snapshot.data!)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'dart:math';
|
||||
import 'package:drift/drift.dart' show Value;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:okane/database/collections/recurrent.dart';
|
||||
import 'package:okane/database/database.dart';
|
||||
import 'package:okane/database/sqlite.dart';
|
||||
import 'package:okane/i18n/strings.g.dart';
|
||||
import 'package:okane/ui/state/core.dart';
|
||||
import 'package:okane/ui/utils.dart';
|
||||
@@ -20,8 +20,10 @@ class UpcomingTransactionsCard extends StatelessWidget {
|
||||
builder: (context, state) {
|
||||
final today = DateTime.now();
|
||||
final upcomingRaw =
|
||||
state.recurringTransactions.where((t) => t.isDue(today)).toList();
|
||||
final List<RecurringTransaction> upcoming =
|
||||
state.recurringTransactions
|
||||
.where((t) => isTransactionDue(t.recurring, today))
|
||||
.toList();
|
||||
final List<RecurringTransactionDto> upcoming =
|
||||
upcomingRaw.isEmpty
|
||||
? List.empty()
|
||||
: upcomingRaw.sublist(0, min(upcomingRaw.length, 3));
|
||||
@@ -42,9 +44,9 @@ class UpcomingTransactionsCard extends StatelessWidget {
|
||||
(transaction) => ListTile(
|
||||
title: Text(
|
||||
t.pages.accounts.upcomingTransactions.items.title(
|
||||
name: transaction.template.value!.name,
|
||||
name: transaction.template.name,
|
||||
amount:
|
||||
"${formatCurrency(transaction.template.value!.amount)}€",
|
||||
"${formatCurrency(transaction.template.amount)}€",
|
||||
),
|
||||
),
|
||||
subtitle: Text(
|
||||
@@ -52,17 +54,18 @@ class UpcomingTransactionsCard extends StatelessWidget {
|
||||
number:
|
||||
today
|
||||
.difference(
|
||||
transaction.lastExecution ?? today,
|
||||
transaction.recurring.lastExecution ??
|
||||
today,
|
||||
)
|
||||
.inDays,
|
||||
),
|
||||
),
|
||||
leading: Icon(
|
||||
transaction.template.value!.amount < 0
|
||||
transaction.template.amount < 0
|
||||
? Icons.remove
|
||||
: Icons.add,
|
||||
color:
|
||||
transaction.template.value!.amount < 0
|
||||
transaction.template.amount < 0
|
||||
? Colors.red
|
||||
: Colors.green,
|
||||
),
|
||||
@@ -74,14 +77,27 @@ class UpcomingTransactionsCard extends StatelessWidget {
|
||||
builder:
|
||||
(context) => AddTransactionWidget(
|
||||
activeAccountItem: bloc.activeAccount!,
|
||||
template: transaction.template.value!,
|
||||
template: (
|
||||
template: transaction.template,
|
||||
beneficiary: transaction.beneficiary,
|
||||
expenseCategory: null,
|
||||
),
|
||||
onAdd: (newTransaction) async {
|
||||
// Update the recurring template
|
||||
transaction.lastExecution =
|
||||
newTransaction.date;
|
||||
await upsertRecurringTransaction(
|
||||
transaction,
|
||||
);
|
||||
await GetIt.I
|
||||
.get<OkaneDatabase>()
|
||||
.recurringTransactionsDao
|
||||
.upsertRecurringTransaction(
|
||||
transaction.recurring
|
||||
.copyWith(
|
||||
lastExecution: Value(
|
||||
newTransaction
|
||||
.transaction
|
||||
.date,
|
||||
),
|
||||
)
|
||||
.toCompanion(true),
|
||||
);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:drift/drift.dart' show Value;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:okane/database/collections/budget.dart';
|
||||
import 'package:okane/database/database.dart';
|
||||
import 'package:okane/database/sqlite.dart';
|
||||
import 'package:okane/i18n/strings.g.dart';
|
||||
import 'package:okane/ui/state/core.dart';
|
||||
|
||||
@@ -52,14 +52,17 @@ class AddBudgetState extends State<AddBudgetPopup> {
|
||||
}
|
||||
|
||||
final bloc = GetIt.I.get<CoreCubit>();
|
||||
final budget =
|
||||
Budget()
|
||||
..name = _budgetNameEditController.text
|
||||
..period = BudgetPeriod.month
|
||||
..includeOtherSpendings = false
|
||||
..income = double.parse(_budgetIncomeEditController.text)
|
||||
..account.value = bloc.activeAccount!;
|
||||
await upsertBudget(budget);
|
||||
await GetIt.I.get<OkaneDatabase>().budgetsDao.upsertBudget(
|
||||
BudgetsCompanion(
|
||||
name: Value(_budgetNameEditController.text),
|
||||
period: Value(BudgetPeriod.month),
|
||||
includeOtherSpendings: Value(false),
|
||||
income: Value(
|
||||
double.parse(_budgetIncomeEditController.text),
|
||||
),
|
||||
accountId: Value(bloc.activeAccount!.id),
|
||||
),
|
||||
);
|
||||
widget.onDone();
|
||||
},
|
||||
child: Text(t.modals.add),
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
import 'package:drift/drift.dart' show Value;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:okane/database/collections/budget.dart';
|
||||
import 'package:okane/database/collections/expense_category.dart';
|
||||
import 'package:okane/database/database.dart';
|
||||
import 'package:okane/database/sqlite.dart';
|
||||
import 'package:okane/i18n/strings.g.dart';
|
||||
import 'package:okane/ui/state/core.dart';
|
||||
import 'package:okane/ui/utils.dart';
|
||||
import 'package:okane/ui/widgets/add_expense_category.dart';
|
||||
|
||||
class AddBudgetItemPopup extends StatefulWidget {
|
||||
final VoidCallback onDone;
|
||||
final Budget budget;
|
||||
final BudgetsDto budget;
|
||||
|
||||
const AddBudgetItemPopup({
|
||||
super.key,
|
||||
@@ -74,26 +72,24 @@ class AddBudgetItemState extends State<AddBudgetItemPopup> {
|
||||
_expenseCategory == null) {
|
||||
return;
|
||||
}
|
||||
if (widget.budget.items
|
||||
if (widget.budget.budgetItems
|
||||
.where(
|
||||
(i) =>
|
||||
i.expenseCategory.value!.name ==
|
||||
_expenseCategory!.name,
|
||||
i.expenseCategory.name == _expenseCategory!.name,
|
||||
)
|
||||
.firstOrNull !=
|
||||
null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final item =
|
||||
BudgetItem()
|
||||
..expenseCategory.value = _expenseCategory
|
||||
..amount = double.parse(
|
||||
_budgetItemAmountEditController.text,
|
||||
);
|
||||
await upsertBudgetItem(item);
|
||||
widget.budget.items.add(item);
|
||||
await upsertBudget(widget.budget);
|
||||
await GetIt.I.get<OkaneDatabase>().budgetsDao.upsertBudgetItem(
|
||||
BudgetItemsCompanion(
|
||||
expenseCategoryId: Value(_expenseCategory!.id),
|
||||
amount: Value(
|
||||
double.parse(_budgetItemAmountEditController.text),
|
||||
),
|
||||
),
|
||||
);
|
||||
widget.onDone();
|
||||
},
|
||||
child: Text(t.modals.add),
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:okane/database/collections/budget.dart';
|
||||
import 'package:okane/database/database.dart';
|
||||
import 'package:okane/database/sqlite.dart';
|
||||
import 'package:okane/i18n/strings.g.dart';
|
||||
import 'package:okane/ui/pages/budgets/add_budget_item.dart';
|
||||
import 'package:okane/ui/state/core.dart';
|
||||
@@ -56,7 +55,7 @@ class BudgetDetailsPage extends StatelessWidget {
|
||||
return Text(t.pages.budgets.details.noBudgetSelected);
|
||||
}
|
||||
|
||||
if (state.activeBudget!.items.isEmpty) {
|
||||
if (state.activeBudget!.budgetItems.isEmpty) {
|
||||
return Row(
|
||||
children: [
|
||||
Text(t.pages.budgets.details.noBudgetItems),
|
||||
@@ -73,14 +72,15 @@ class BudgetDetailsPage extends StatelessWidget {
|
||||
|
||||
final bloc = GetIt.I.get<CoreCubit>();
|
||||
final today = DateTime.now();
|
||||
final db = GetIt.I.get<OkaneDatabase>();
|
||||
return FutureBuilder(
|
||||
future: getTransactionsInTimeframe(
|
||||
future: db.transactionsDao.getTransactionsInTimeframe(
|
||||
bloc.activeAccount!,
|
||||
today,
|
||||
TransactionQueryDateOption.thisMonth,
|
||||
),
|
||||
builder: (context, snapshot) {
|
||||
final daysLeft = switch (state.activeBudget!.period) {
|
||||
final daysLeft = switch (state.activeBudget!.budget.period) {
|
||||
BudgetPeriod.month =>
|
||||
monthEnding(today).difference(today).inDays,
|
||||
};
|
||||
@@ -95,16 +95,17 @@ class BudgetDetailsPage extends StatelessWidget {
|
||||
),
|
||||
ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: state.activeBudget!.items.length,
|
||||
itemCount: state.activeBudget!.budgetItems.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = state.activeBudget!.items.elementAt(
|
||||
index,
|
||||
);
|
||||
final amount = formatCurrency(item.amount);
|
||||
final item = state.activeBudget!.budgetItems
|
||||
.elementAt(index);
|
||||
final amount = formatCurrency(item.item.amount);
|
||||
return ListTile(
|
||||
title: Text(
|
||||
t.pages.budgets.details.items.title(
|
||||
name: item.expenseCategory.value!.name,
|
||||
// TODO
|
||||
name: "lol",
|
||||
//name: item.expenseCategory.value!.name,
|
||||
amount: amount,
|
||||
),
|
||||
),
|
||||
@@ -119,26 +120,28 @@ class BudgetDetailsPage extends StatelessWidget {
|
||||
}
|
||||
|
||||
final categories =
|
||||
state.activeBudget!.items
|
||||
.map((i) => i.expenseCategory.value!.name)
|
||||
state.activeBudget!.budgetItems
|
||||
// TODO
|
||||
//.map((i) => i.expenseCategory.value!.name)
|
||||
.map((i) => "lol")
|
||||
.toList();
|
||||
final spending = <String, double>{};
|
||||
for (final t in snapshot.data!) {
|
||||
String categoryName;
|
||||
if (!categories.contains(t.expenseCategory.value?.name)) {
|
||||
if (!state.activeBudget!.includeOtherSpendings) {
|
||||
if (!categories.contains(t.expenseCategory?.name)) {
|
||||
if (!state.activeBudget!.budget.includeOtherSpendings) {
|
||||
continue;
|
||||
}
|
||||
|
||||
categoryName = "Other";
|
||||
} else {
|
||||
categoryName = t.expenseCategory.value!.name;
|
||||
categoryName = t.expenseCategory!.name;
|
||||
}
|
||||
|
||||
spending.update(
|
||||
categoryName,
|
||||
(value) => value + t.amount,
|
||||
ifAbsent: () => t.amount,
|
||||
(value) => value + t.transaction.amount,
|
||||
ifAbsent: () => t.transaction.amount,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -146,8 +149,8 @@ class BudgetDetailsPage extends StatelessWidget {
|
||||
spending.isEmpty
|
||||
? 0
|
||||
: spending.values.reduce((acc, val) => acc + val);
|
||||
final budgetTotal = state.activeBudget!.items
|
||||
.map((i) => i.amount)
|
||||
final budgetTotal = state.activeBudget!.budgetItems
|
||||
.map((i) => i.item.amount)
|
||||
.reduce((acc, val) => acc + val);
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -268,13 +271,13 @@ class BudgetDetailsPage extends StatelessWidget {
|
||||
fallbackText: "",
|
||||
valueConverter: formatCurrency,
|
||||
items:
|
||||
state.activeBudget!.items
|
||||
state.activeBudget!.budgetItems
|
||||
.map(
|
||||
(i) => (
|
||||
title: i.expenseCategory.value!.name,
|
||||
value: i.amount,
|
||||
title: i.expenseCategory.name,
|
||||
value: i.item.amount,
|
||||
color: colorHash(
|
||||
i.expenseCategory.value!.name,
|
||||
i.expenseCategory.name,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -339,18 +342,16 @@ class BudgetDetailsPage extends StatelessWidget {
|
||||
padding: EdgeInsets.all(8),
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: state.activeBudget!.items.length,
|
||||
itemCount: state.activeBudget!.budgetItems.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = state.activeBudget!.items.elementAt(
|
||||
index,
|
||||
);
|
||||
final amount = formatCurrency(item.amount);
|
||||
final spent =
|
||||
spending[item.expenseCategory.value!.name];
|
||||
final item = state.activeBudget!.budgetItems
|
||||
.elementAt(index);
|
||||
final amount = formatCurrency(item.item.amount);
|
||||
final spent = spending[item.expenseCategory.name];
|
||||
final left =
|
||||
spent == null
|
||||
? item.amount
|
||||
: item.amount + spent;
|
||||
? item.item.amount
|
||||
: item.item.amount + spent;
|
||||
final subtitleText =
|
||||
left < 0
|
||||
? t.pages.budgets.details.items.over(
|
||||
@@ -361,7 +362,7 @@ class BudgetDetailsPage extends StatelessWidget {
|
||||
);
|
||||
return ListTile(
|
||||
title: Text(
|
||||
"${item.expenseCategory.value!.name} ($amount)",
|
||||
"${item.expenseCategory.name} ($amount)",
|
||||
),
|
||||
subtitle: Text(
|
||||
subtitleText,
|
||||
|
||||
@@ -28,7 +28,7 @@ class BudgetListPage extends StatelessWidget {
|
||||
itemCount: state.budgets.length,
|
||||
itemBuilder:
|
||||
(context, index) => ListTile(
|
||||
title: Text(state.budgets[index].name),
|
||||
title: Text(state.budgets[index].budget.name),
|
||||
selected: state.budgets[index] == state.activeBudget,
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:okane/database/collections/budget.dart';
|
||||
import 'package:okane/database/database.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:okane/database/sqlite.dart';
|
||||
import 'package:okane/i18n/strings.g.dart';
|
||||
|
||||
class EditBudgetPopup extends StatefulWidget {
|
||||
final Budget budget;
|
||||
final BudgetsDto budget;
|
||||
|
||||
final VoidCallback onDone;
|
||||
|
||||
@@ -27,8 +27,8 @@ class EditBudgetState extends State<EditBudgetPopup> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_budgetNameEditController.text = widget.budget.name;
|
||||
_includeOtherSpendings = widget.budget.includeOtherSpendings;
|
||||
_budgetNameEditController.text = widget.budget.budget.name;
|
||||
_includeOtherSpendings = widget.budget.budget.includeOtherSpendings;
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -61,17 +61,22 @@ class EditBudgetState extends State<EditBudgetPopup> {
|
||||
if (_budgetNameEditController.text.isEmpty) {
|
||||
return;
|
||||
}
|
||||
if (_budgetNameEditController.text == widget.budget.name &&
|
||||
if (_budgetNameEditController.text ==
|
||||
widget.budget.budget.name &&
|
||||
_includeOtherSpendings ==
|
||||
widget.budget.includeOtherSpendings) {
|
||||
widget.budget.budget.includeOtherSpendings) {
|
||||
widget.onDone();
|
||||
return;
|
||||
}
|
||||
|
||||
widget.budget
|
||||
..name = _budgetNameEditController.text
|
||||
..includeOtherSpendings = _includeOtherSpendings;
|
||||
await upsertBudget(widget.budget);
|
||||
await GetIt.I.get<OkaneDatabase>().budgetsDao.upsertBudget(
|
||||
widget.budget.budget
|
||||
.copyWith(
|
||||
name: _budgetNameEditController.text,
|
||||
includeOtherSpendings: _includeOtherSpendings,
|
||||
)
|
||||
.toCompanion(false),
|
||||
);
|
||||
widget.onDone();
|
||||
},
|
||||
child: Text(t.modals.save),
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import 'package:drift/drift.dart' show Value;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:okane/database/collections/beneficiary.dart';
|
||||
import 'package:okane/database/collections/budget.dart';
|
||||
import 'package:okane/database/collections/loan.dart';
|
||||
import 'package:okane/database/database.dart';
|
||||
import 'package:okane/database/sqlite.dart';
|
||||
import 'package:okane/i18n/strings.g.dart';
|
||||
import 'package:okane/ui/state/core.dart';
|
||||
import 'package:searchfield/searchfield.dart';
|
||||
@@ -47,7 +45,7 @@ class AddBudgetState extends State<AddLoanPopup> {
|
||||
.where((el) {
|
||||
final bloc = GetIt.I.get<CoreCubit>();
|
||||
if (el.type == BeneficiaryType.account) {
|
||||
return el.account.value?.id.toInt() ==
|
||||
return el.accountId ==
|
||||
bloc.activeAccount?.id.toInt();
|
||||
}
|
||||
return true;
|
||||
@@ -79,6 +77,7 @@ class AddBudgetState extends State<AddLoanPopup> {
|
||||
return;
|
||||
}
|
||||
|
||||
final db = GetIt.I.get<OkaneDatabase>();
|
||||
Beneficiary? beneficiary = _selectedBeneficiary?.item;
|
||||
if (beneficiary == null ||
|
||||
getBeneficiaryName(beneficiary) != beneficiaryName) {
|
||||
@@ -87,42 +86,49 @@ class AddBudgetState extends State<AddLoanPopup> {
|
||||
context: context,
|
||||
builder:
|
||||
(context) => AlertDialog(
|
||||
title: Text(t.common.beneficiary.addBeneficiary.title),
|
||||
content: Text(
|
||||
t.common.beneficiary.addBeneficiary.body(name: beneficiaryName),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
textStyle: Theme.of(context).textTheme.labelLarge,
|
||||
title: Text(
|
||||
t.common.beneficiary.addBeneficiary.title,
|
||||
),
|
||||
child: Text(t.modals.add),
|
||||
onPressed: () => Navigator.of(context).pop(true),
|
||||
),
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
textStyle: Theme.of(context).textTheme.labelLarge,
|
||||
content: Text(
|
||||
t.common.beneficiary.addBeneficiary.body(
|
||||
name: beneficiaryName,
|
||||
),
|
||||
),
|
||||
child: Text(t.modals.cancel),
|
||||
onPressed: () => Navigator.of(context).pop(false),
|
||||
actions: [
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
textStyle:
|
||||
Theme.of(context).textTheme.labelLarge,
|
||||
),
|
||||
child: Text(t.modals.add),
|
||||
onPressed: () => Navigator.of(context).pop(true),
|
||||
),
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
textStyle:
|
||||
Theme.of(context).textTheme.labelLarge,
|
||||
),
|
||||
child: Text(t.modals.cancel),
|
||||
onPressed: () => Navigator.of(context).pop(false),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
if (result == null || !result) {
|
||||
return;
|
||||
}
|
||||
|
||||
beneficiary =
|
||||
Beneficiary()
|
||||
..name = beneficiaryName
|
||||
..type = BeneficiaryType.other;
|
||||
await upsertBeneficiary(beneficiary);
|
||||
beneficiary = await db.beneficiariesDao.upsertBeneficiary(
|
||||
BeneficiariesCompanion(
|
||||
name: Value(beneficiaryName),
|
||||
type: Value(BeneficiaryType.other),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final loan =
|
||||
Loan()..beneficiary.value = beneficiary;
|
||||
await upsertLoan(loan);
|
||||
await db.loansDao.upsertLoan(
|
||||
LoansCompanion(beneficiaryId: Value(beneficiary.id)),
|
||||
);
|
||||
widget.onDone();
|
||||
},
|
||||
child: Text(t.modals.add),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:drift/drift.dart' show Value;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:okane/database/collections/loan.dart';
|
||||
import 'package:okane/database/database.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:okane/database/sqlite.dart';
|
||||
import 'package:okane/i18n/strings.g.dart';
|
||||
import 'package:okane/ui/utils.dart';
|
||||
|
||||
@@ -9,7 +10,7 @@ enum LoanChangeType { owe, loan }
|
||||
class AddLoanChangePopup extends StatefulWidget {
|
||||
final VoidCallback onDone;
|
||||
|
||||
final Loan loan;
|
||||
final LoanDto loan;
|
||||
|
||||
const AddLoanChangePopup({
|
||||
super.key,
|
||||
@@ -84,13 +85,14 @@ class AddLoanPopupState extends State<AddLoanChangePopup> {
|
||||
LoanChangeType.owe => -1,
|
||||
LoanChangeType.loan => 1,
|
||||
};
|
||||
final loanChange =
|
||||
LoanChange()
|
||||
..amount = sign * double.parse(_amountController.text).abs()
|
||||
..date = DateTime.now();
|
||||
await upsertLoanChange(loanChange);
|
||||
widget.loan.changes.add(loanChange);
|
||||
await upsertLoan(widget.loan);
|
||||
await GetIt.I.get<OkaneDatabase>().loansDao.upsertLoanChange(
|
||||
LoanChangesCompanion(
|
||||
amount: Value(
|
||||
sign * double.parse(_amountController.text).abs(),
|
||||
),
|
||||
date: Value(DateTime.now()),
|
||||
),
|
||||
);
|
||||
widget.onDone();
|
||||
},
|
||||
child: Text(t.modals.add),
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:grouped_list/grouped_list.dart';
|
||||
import 'package:okane/database/collections/loan.dart';
|
||||
import 'package:okane/database/database.dart';
|
||||
import 'package:okane/database/sqlite.dart';
|
||||
import 'package:okane/ui/pages/loans/add_loan_change.dart';
|
||||
import 'package:okane/ui/state/core.dart';
|
||||
import 'package:okane/ui/utils.dart';
|
||||
@@ -35,6 +35,7 @@ class LoanDetailsPage extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
Expanded(
|
||||
child: BlocBuilder<CoreCubit, CoreState>(
|
||||
builder: (context, state) {
|
||||
@@ -55,11 +56,10 @@ class LoanDetailsPage extends StatelessWidget {
|
||||
child: Row(
|
||||
children: [
|
||||
ImageWrapper(
|
||||
title: state.activeLoan!.beneficiary.value!.name,
|
||||
path:
|
||||
state.activeLoan!.beneficiary.value!.imagePath,
|
||||
title: state.activeLoan!.beneficiary.name,
|
||||
path: state.activeLoan!.beneficiary.imagePath,
|
||||
),
|
||||
Text(state.activeLoan!.beneficiary.value!.name),
|
||||
Text(state.activeLoan!.beneficiary.name),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -145,11 +145,10 @@ class LoanDetailsPage extends StatelessWidget {
|
||||
color: Colors.red,
|
||||
),
|
||||
onPressed: () async {
|
||||
state.activeLoan!.changes.remove(
|
||||
item,
|
||||
);
|
||||
await deleteLoanChange(item);
|
||||
await upsertLoan(state.activeLoan!);
|
||||
await GetIt.I
|
||||
.get<OkaneDatabase>()
|
||||
.loansDao
|
||||
.deleteLoanChange(item.id);
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:okane/database/database.dart';
|
||||
import 'package:okane/screen.dart';
|
||||
import 'package:okane/ui/pages/loans/add_loan.dart';
|
||||
import 'package:okane/ui/state/core.dart';
|
||||
@@ -21,7 +20,7 @@ class LoanListPage extends StatelessWidget {
|
||||
itemCount: state.loans.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = state.loans[index];
|
||||
final beneficiary = item.beneficiary.value!;
|
||||
final beneficiary = item.beneficiary;
|
||||
return ListTile(
|
||||
leading: ImageWrapper(
|
||||
title: beneficiary.name,
|
||||
@@ -45,7 +44,8 @@ class LoanListPage extends StatelessWidget {
|
||||
return;
|
||||
}
|
||||
|
||||
await deleteLoan(item);
|
||||
// TODO
|
||||
// await deleteLoan(item);
|
||||
},
|
||||
icon: Icon(Icons.delete, color: Colors.red),
|
||||
),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:okane/database/database.dart';
|
||||
import 'package:okane/database/sqlite.dart';
|
||||
import 'package:okane/i18n/strings.g.dart';
|
||||
import 'package:okane/ui/state/core.dart';
|
||||
import 'package:okane/ui/utils.dart';
|
||||
@@ -21,7 +21,9 @@ class TemplateListState extends State<TemplateListPage> {
|
||||
builder: (context, state) {
|
||||
final account = GetIt.I.get<CoreCubit>().activeAccount;
|
||||
final nonRecurringTemplates =
|
||||
state.transactionTemplates.where((t) => !t.recurring).toList();
|
||||
state.transactionTemplates
|
||||
.where((t) => !t.template.recurring)
|
||||
.toList();
|
||||
return Stack(
|
||||
children: [
|
||||
CustomScrollView(
|
||||
@@ -34,7 +36,7 @@ class TemplateListState extends State<TemplateListPage> {
|
||||
itemBuilder: (context, index) {
|
||||
final template = nonRecurringTemplates[index];
|
||||
return ListTile(
|
||||
title: Text(template.name),
|
||||
title: Text(template.template.name),
|
||||
trailing: IconButton(
|
||||
icon: Icon(Icons.delete),
|
||||
color: Colors.red,
|
||||
@@ -43,14 +45,17 @@ class TemplateListState extends State<TemplateListPage> {
|
||||
context,
|
||||
t.pages.templates.removeTemplate.title,
|
||||
t.pages.templates.removeTemplate.body(
|
||||
name: template.name,
|
||||
name: template.template.name,
|
||||
),
|
||||
);
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
await deleteTransactionTemplate(template);
|
||||
await GetIt.I
|
||||
.get<OkaneDatabase>()
|
||||
.transactionTemplatesDao
|
||||
.deleteTemplate(template.template);
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -64,7 +69,7 @@ class TemplateListState extends State<TemplateListPage> {
|
||||
itemBuilder: (context, index) {
|
||||
final template = state.recurringTransactions[index];
|
||||
return ListTile(
|
||||
title: Text(template.template.value!.name),
|
||||
title: Text(template.template.name),
|
||||
trailing: IconButton(
|
||||
icon: Icon(Icons.delete, color: Colors.red),
|
||||
onPressed: () async {
|
||||
@@ -72,14 +77,17 @@ class TemplateListState extends State<TemplateListPage> {
|
||||
context,
|
||||
t.pages.templates.removeTemplate.title,
|
||||
t.pages.templates.removeTemplate.body(
|
||||
name: template.template.value!.name,
|
||||
name: template.template.name,
|
||||
),
|
||||
);
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
await deleteRecurringTransactionTemplate(template);
|
||||
await GetIt.I
|
||||
.get<OkaneDatabase>()
|
||||
.recurringTransactionsDao
|
||||
.deleteTemplate(template);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:drift/drift.dart' show Value;
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:okane/database/collections/beneficiary.dart';
|
||||
import 'package:okane/database/database.dart';
|
||||
import 'package:okane/database/sqlite.dart';
|
||||
import 'package:okane/i18n/strings.g.dart';
|
||||
import 'package:okane/ui/state/core.dart';
|
||||
import 'package:okane/ui/utils.dart';
|
||||
@@ -43,8 +43,9 @@ class TransactionDetailsPage extends StatelessWidget {
|
||||
await File(file.path!).copy(imagePath);
|
||||
|
||||
print("Updating DB");
|
||||
beneficiary.imagePath = imagePath;
|
||||
await upsertBeneficiary(beneficiary);
|
||||
await GetIt.I.get<OkaneDatabase>().beneficiariesDao.upsertBeneficiary(
|
||||
beneficiary.copyWith(imagePath: Value(imagePath)).toCompanion(false),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -83,13 +84,16 @@ class TransactionDetailsPage extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
StreamBuilder(
|
||||
stream: watchBeneficiaryObject(
|
||||
state.activeTransaction!.beneficiary.value!.id,
|
||||
),
|
||||
stream: GetIt.I
|
||||
.get<OkaneDatabase>()
|
||||
.beneficiariesDao
|
||||
.watchBeneficiary(
|
||||
state.activeTransaction!.beneficiary.id,
|
||||
),
|
||||
builder: (context, snapshot) {
|
||||
final obj =
|
||||
snapshot.data ??
|
||||
state.activeTransaction!.beneficiary.value!;
|
||||
state.activeTransaction!.beneficiary;
|
||||
return ImageWrapper(
|
||||
title: obj.name,
|
||||
path: obj.imagePath,
|
||||
@@ -116,7 +120,6 @@ class TransactionDetailsPage extends StatelessWidget {
|
||||
state
|
||||
.activeTransaction!
|
||||
.beneficiary
|
||||
.value!
|
||||
.name,
|
||||
),
|
||||
),
|
||||
@@ -150,16 +153,16 @@ class TransactionDetailsPage extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
|
||||
// TODO
|
||||
/*
|
||||
Wrap(
|
||||
spacing: 8,
|
||||
children:
|
||||
state.activeTransaction!.tags
|
||||
.map((tag) => Chip(label: Text(tag)))
|
||||
.toList(),
|
||||
),
|
||||
|
||||
if (state.activeTransaction!.expenseCategory.value !=
|
||||
null)
|
||||
),*/
|
||||
if (state.activeTransaction!.expenseCategory != null)
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 8),
|
||||
child: Row(
|
||||
@@ -172,8 +175,7 @@ class TransactionDetailsPage extends StatelessWidget {
|
||||
label: Text(
|
||||
state
|
||||
.activeTransaction!
|
||||
.expenseCategory
|
||||
.value!
|
||||
.expenseCategory!
|
||||
.name,
|
||||
),
|
||||
),
|
||||
@@ -186,11 +188,13 @@ class TransactionDetailsPage extends StatelessWidget {
|
||||
padding: EdgeInsets.symmetric(vertical: 8),
|
||||
child: Row(
|
||||
children: [
|
||||
state.activeTransaction!.amount > 0
|
||||
state.activeTransaction!.transaction.amount > 0
|
||||
? Icon(Icons.add)
|
||||
: Icon(Icons.remove),
|
||||
Text(
|
||||
formatCurrency(state.activeTransaction!.amount),
|
||||
formatCurrency(
|
||||
state.activeTransaction!.transaction.amount,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:grouped_list/grouped_list.dart';
|
||||
import 'package:okane/database/collections/transaction.dart';
|
||||
import 'package:okane/database/sqlite.dart';
|
||||
import 'package:okane/screen.dart';
|
||||
import 'package:okane/ui/pages/account/balance_graph_card.dart';
|
||||
import 'package:okane/ui/state/core.dart';
|
||||
@@ -41,7 +41,9 @@ class TransactionListState extends State<TransactionListPage> {
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
elements: state.transactions,
|
||||
reverse: true,
|
||||
groupBy: (Transaction item) => formatDateTime(item.date),
|
||||
groupBy:
|
||||
(TransactionDto item) =>
|
||||
formatDateTime(item.transaction.date),
|
||||
groupHeaderBuilder:
|
||||
(item) => Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
@@ -54,7 +56,7 @@ class TransactionListState extends State<TransactionListPage> {
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(4),
|
||||
child: Text(
|
||||
formatDateTime(item.date),
|
||||
formatDateTime(item.transaction.date),
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,15 +1,8 @@
|
||||
import 'dart:async';
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:okane/database/collections/account.dart';
|
||||
import 'package:okane/database/collections/beneficiary.dart';
|
||||
import 'package:okane/database/collections/budget.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/database/database.dart' as db;
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:okane/database/sqlite.dart';
|
||||
import 'package:okane/ui/navigation.dart';
|
||||
|
||||
part 'core.freezed.dart';
|
||||
@@ -19,17 +12,17 @@ abstract class CoreState with _$CoreState {
|
||||
const factory CoreState({
|
||||
@Default(OkanePage.accounts) OkanePage activePage,
|
||||
int? activeAccountIndex,
|
||||
@Default(null) Transaction? activeTransaction,
|
||||
@Default(null) TransactionDto? activeTransaction,
|
||||
@Default([]) List<Account> accounts,
|
||||
@Default([]) List<RecurringTransaction> recurringTransactions,
|
||||
@Default([]) List<Transaction> transactions,
|
||||
@Default([]) List<TransactionTemplate> transactionTemplates,
|
||||
@Default([]) List<RecurringTransactionDto> recurringTransactions,
|
||||
@Default([]) List<TransactionDto> transactions,
|
||||
@Default([]) List<TransactionTemplateDto> transactionTemplates,
|
||||
@Default([]) List<Beneficiary> beneficiaries,
|
||||
@Default([]) List<ExpenseCategory> expenseCategories,
|
||||
@Default([]) List<Budget> budgets,
|
||||
@Default(null) Budget? activeBudget,
|
||||
@Default([]) List<Loan> loans,
|
||||
@Default(null) Loan? activeLoan,
|
||||
@Default([]) List<BudgetsDto> budgets,
|
||||
@Default(null) BudgetsDto? activeBudget,
|
||||
@Default([]) List<LoanDto> loans,
|
||||
@Default(null) LoanDto? activeLoan,
|
||||
@Default(false) bool isDeletingAccount,
|
||||
}) = _CoreState;
|
||||
}
|
||||
@@ -48,80 +41,65 @@ class CoreCubit extends Cubit<CoreState> {
|
||||
|
||||
void setupAccountStream() {
|
||||
_accountsStreamSubscription?.cancel();
|
||||
_accountsStreamSubscription = db.watchAccounts().listen((_) async {
|
||||
final resetStreams = state.activeAccountIndex == null;
|
||||
final accounts = await db.getAccounts();
|
||||
emit(
|
||||
state.copyWith(
|
||||
accounts: accounts,
|
||||
activeAccountIndex: state.activeAccountIndex ?? 0,
|
||||
),
|
||||
);
|
||||
|
||||
if (resetStreams) {
|
||||
setupStreams(accounts[0]);
|
||||
}
|
||||
});
|
||||
_accountsStreamSubscription = GetIt.I
|
||||
.get<OkaneDatabase>()
|
||||
.accountsDao
|
||||
.accountsStream()
|
||||
.listen((accounts) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
accounts: accounts,
|
||||
activeAccountIndex:
|
||||
accounts.isNotEmpty ? state.activeAccountIndex ?? 0 : null,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
void setupStreams(Account account) {
|
||||
final db = GetIt.I.get<OkaneDatabase>();
|
||||
setupAccountStream();
|
||||
_recurringTransactionStreamSubscription?.cancel();
|
||||
_recurringTransactionStreamSubscription = db
|
||||
.watchRecurringTransactions(activeAccount!)
|
||||
.listen((_) async {
|
||||
print("RECURRING UPDATE");
|
||||
emit(
|
||||
state.copyWith(
|
||||
recurringTransactions: await db.getRecurringTransactions(
|
||||
activeAccount!,
|
||||
),
|
||||
),
|
||||
);
|
||||
_recurringTransactionStreamSubscription = db.recurringTransactionsDao
|
||||
.recurringTransactionsStream(account)
|
||||
.listen((recurring) async {
|
||||
emit(state.copyWith(recurringTransactions: recurring));
|
||||
});
|
||||
_transactionTemplatesStreamSubcription?.cancel();
|
||||
_transactionTemplatesStreamSubcription = db
|
||||
.watchTransactionTemplates(activeAccount!)
|
||||
.listen((_) async {
|
||||
emit(
|
||||
state.copyWith(
|
||||
transactionTemplates: await db.getTransactionTemplates(
|
||||
activeAccount!,
|
||||
),
|
||||
),
|
||||
);
|
||||
_transactionTemplatesStreamSubcription = db.transactionTemplatesDao
|
||||
.transactionTemplatesStream(account)
|
||||
.listen((templates) async {
|
||||
emit(state.copyWith(transactionTemplates: templates));
|
||||
});
|
||||
_transactionsStreamSubscription?.cancel();
|
||||
_transactionsStreamSubscription = db
|
||||
.watchTransactions(activeAccount!)
|
||||
.listen((_) async {
|
||||
emit(
|
||||
state.copyWith(
|
||||
transactions: await db.getTransactions(activeAccount!),
|
||||
),
|
||||
);
|
||||
_transactionsStreamSubscription = db.transactionsDao
|
||||
.transactionsStream(activeAccount!)
|
||||
.listen((transactions) async {
|
||||
emit(state.copyWith(transactions: transactions));
|
||||
});
|
||||
_beneficiariesStreamSubscription?.cancel();
|
||||
_beneficiariesStreamSubscription = db.watchBeneficiaries().listen((
|
||||
_,
|
||||
) async {
|
||||
emit(state.copyWith(beneficiaries: await db.getBeneficiaries()));
|
||||
});
|
||||
_beneficiariesStreamSubscription = db.beneficiariesDao
|
||||
.beneficiariesStream()
|
||||
.listen((beneficiaries) async {
|
||||
emit(state.copyWith(beneficiaries: beneficiaries));
|
||||
});
|
||||
_expenseCategoryStreamSubscription?.cancel();
|
||||
_expenseCategoryStreamSubscription = db.watchExpenseCategory().listen((
|
||||
_,
|
||||
) async {
|
||||
emit(state.copyWith(expenseCategories: await db.getExpenseCategories()));
|
||||
});
|
||||
_expenseCategoryStreamSubscription = db.expenseCategoriesDao
|
||||
.expenseCategoriesStream(account)
|
||||
.listen((expenseCategories) async {
|
||||
emit(state.copyWith(expenseCategories: expenseCategories));
|
||||
});
|
||||
_budgetsStreamSubscription?.cancel();
|
||||
_budgetsStreamSubscription = db.watchBudgets(activeAccount!).listen((
|
||||
_,
|
||||
) async {
|
||||
emit(state.copyWith(budgets: await db.getBudgets(activeAccount!)));
|
||||
});
|
||||
_budgetsStreamSubscription = db.budgetsDao
|
||||
.budgetsStream(activeAccount!)
|
||||
.listen((budgets) async {
|
||||
emit(state.copyWith(budgets: budgets));
|
||||
});
|
||||
_loanStreamSubscription?.cancel();
|
||||
_loanStreamSubscription = db.watchLoans().listen((_) async {
|
||||
emit(state.copyWith(loans: await db.getLoans()));
|
||||
_loanStreamSubscription = db.loansDao.loansStream(account).listen((
|
||||
loans,
|
||||
) async {
|
||||
emit(state.copyWith(loans: loans));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -137,23 +115,29 @@ class CoreCubit extends Cubit<CoreState> {
|
||||
}
|
||||
|
||||
Future<void> init() async {
|
||||
final accounts = await db.getAccounts();
|
||||
final db = GetIt.I.get<OkaneDatabase>();
|
||||
final accounts = await db.accountsDao.getAccounts();
|
||||
final account = accounts.isEmpty ? null : accounts[0];
|
||||
emit(
|
||||
state.copyWith(
|
||||
accounts: accounts,
|
||||
activeAccountIndex: accounts.isEmpty ? null : 0,
|
||||
transactions: await db.getTransactions(account),
|
||||
beneficiaries: await db.getBeneficiaries(),
|
||||
transactionTemplates: await db.getTransactionTemplates(account),
|
||||
recurringTransactions: await db.getRecurringTransactions(account),
|
||||
expenseCategories: await db.getExpenseCategories(),
|
||||
budgets: await db.getBudgets(account),
|
||||
loans: await db.getLoans(),
|
||||
transactions: await db.transactionsDao.getTransactions(account),
|
||||
beneficiaries: await db.beneficiariesDao.getBeneficiaries(),
|
||||
transactionTemplates: await db.transactionTemplatesDao
|
||||
.getTransactionTemplates(account),
|
||||
recurringTransactions: await db.recurringTransactionsDao
|
||||
.getRecurringTransactions(account),
|
||||
expenseCategories: await db.expenseCategoriesDao.getExpenseCategories(
|
||||
account,
|
||||
),
|
||||
budgets: await db.budgetsDao.getBudgets(account),
|
||||
loans: await db.loansDao.getLoans(account),
|
||||
),
|
||||
);
|
||||
|
||||
if (account != null) {
|
||||
setupAccountStream();
|
||||
setupStreams(account);
|
||||
} else {
|
||||
setupAccountStream();
|
||||
@@ -166,15 +150,18 @@ class CoreCubit extends Cubit<CoreState> {
|
||||
}
|
||||
|
||||
Future<void> setActiveAccountIndex(int index) async {
|
||||
final db = GetIt.I.get<OkaneDatabase>();
|
||||
final account = state.accounts[index];
|
||||
emit(
|
||||
state.copyWith(
|
||||
activeAccountIndex: index,
|
||||
transactions: await db.getTransactions(account),
|
||||
beneficiaries: await db.getBeneficiaries(),
|
||||
transactionTemplates: await db.getTransactionTemplates(account),
|
||||
recurringTransactions: await db.getRecurringTransactions(account),
|
||||
budgets: await db.getBudgets(account),
|
||||
transactions: await db.transactionsDao.getTransactions(account),
|
||||
beneficiaries: await db.beneficiariesDao.getBeneficiaries(),
|
||||
transactionTemplates: await db.transactionTemplatesDao
|
||||
.getTransactionTemplates(account),
|
||||
recurringTransactions: await db.recurringTransactionsDao
|
||||
.getRecurringTransactions(account),
|
||||
budgets: await db.budgetsDao.getBudgets(account),
|
||||
activeBudget: null,
|
||||
activeTransaction: null,
|
||||
activeLoan: null,
|
||||
@@ -183,7 +170,7 @@ class CoreCubit extends Cubit<CoreState> {
|
||||
setupStreams(account);
|
||||
}
|
||||
|
||||
void setActiveTransaction(Transaction? item) {
|
||||
void setActiveTransaction(TransactionDto? item) {
|
||||
emit(state.copyWith(activeTransaction: item));
|
||||
}
|
||||
|
||||
@@ -191,7 +178,7 @@ class CoreCubit extends Cubit<CoreState> {
|
||||
emit(state.copyWith(accounts: accounts));
|
||||
}
|
||||
|
||||
void setActiveBudget(Budget? budget) {
|
||||
void setActiveBudget(BudgetsDto? budget) {
|
||||
emit(state.copyWith(activeBudget: budget));
|
||||
}
|
||||
|
||||
@@ -203,14 +190,15 @@ class CoreCubit extends Cubit<CoreState> {
|
||||
|
||||
cancelStreams();
|
||||
try {
|
||||
await db.deleteAccount(account);
|
||||
// TODO
|
||||
//await db.deleteAccount(account);
|
||||
} finally {
|
||||
emit(state.copyWith(isDeletingAccount: false));
|
||||
}
|
||||
await init();
|
||||
}
|
||||
|
||||
void setActiveLoan(Loan loan) {
|
||||
void setActiveLoan(LoanDto loan) {
|
||||
emit(state.copyWith(activeLoan: loan));
|
||||
}
|
||||
|
||||
|
||||
@@ -19,20 +19,20 @@ final _privateConstructorUsedError = UnsupportedError(
|
||||
mixin _$CoreState {
|
||||
OkanePage get activePage => throw _privateConstructorUsedError;
|
||||
int? get activeAccountIndex => throw _privateConstructorUsedError;
|
||||
Transaction? get activeTransaction => throw _privateConstructorUsedError;
|
||||
TransactionDto? get activeTransaction => throw _privateConstructorUsedError;
|
||||
List<Account> get accounts => throw _privateConstructorUsedError;
|
||||
List<RecurringTransaction> get recurringTransactions =>
|
||||
List<RecurringTransactionDto> get recurringTransactions =>
|
||||
throw _privateConstructorUsedError;
|
||||
List<Transaction> get transactions => throw _privateConstructorUsedError;
|
||||
List<TransactionTemplate> get transactionTemplates =>
|
||||
List<TransactionDto> get transactions => throw _privateConstructorUsedError;
|
||||
List<TransactionTemplateDto> get transactionTemplates =>
|
||||
throw _privateConstructorUsedError;
|
||||
List<Beneficiary> get beneficiaries => throw _privateConstructorUsedError;
|
||||
List<ExpenseCategory> get expenseCategories =>
|
||||
throw _privateConstructorUsedError;
|
||||
List<Budget> get budgets => throw _privateConstructorUsedError;
|
||||
Budget? get activeBudget => throw _privateConstructorUsedError;
|
||||
List<Loan> get loans => throw _privateConstructorUsedError;
|
||||
Loan? get activeLoan => throw _privateConstructorUsedError;
|
||||
List<BudgetsDto> get budgets => throw _privateConstructorUsedError;
|
||||
BudgetsDto? get activeBudget => throw _privateConstructorUsedError;
|
||||
List<LoanDto> get loans => throw _privateConstructorUsedError;
|
||||
LoanDto? get activeLoan => throw _privateConstructorUsedError;
|
||||
bool get isDeletingAccount => throw _privateConstructorUsedError;
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@@ -48,17 +48,17 @@ abstract class $CoreStateCopyWith<$Res> {
|
||||
$Res call({
|
||||
OkanePage activePage,
|
||||
int? activeAccountIndex,
|
||||
Transaction? activeTransaction,
|
||||
TransactionDto? activeTransaction,
|
||||
List<Account> accounts,
|
||||
List<RecurringTransaction> recurringTransactions,
|
||||
List<Transaction> transactions,
|
||||
List<TransactionTemplate> transactionTemplates,
|
||||
List<RecurringTransactionDto> recurringTransactions,
|
||||
List<TransactionDto> transactions,
|
||||
List<TransactionTemplateDto> transactionTemplates,
|
||||
List<Beneficiary> beneficiaries,
|
||||
List<ExpenseCategory> expenseCategories,
|
||||
List<Budget> budgets,
|
||||
Budget? activeBudget,
|
||||
List<Loan> loans,
|
||||
Loan? activeLoan,
|
||||
List<BudgetsDto> budgets,
|
||||
BudgetsDto? activeBudget,
|
||||
List<LoanDto> loans,
|
||||
LoanDto? activeLoan,
|
||||
bool isDeletingAccount,
|
||||
});
|
||||
}
|
||||
@@ -107,7 +107,7 @@ class _$CoreStateCopyWithImpl<$Res, $Val extends CoreState>
|
||||
freezed == activeTransaction
|
||||
? _value.activeTransaction
|
||||
: activeTransaction // ignore: cast_nullable_to_non_nullable
|
||||
as Transaction?,
|
||||
as TransactionDto?,
|
||||
accounts:
|
||||
null == accounts
|
||||
? _value.accounts
|
||||
@@ -117,17 +117,17 @@ class _$CoreStateCopyWithImpl<$Res, $Val extends CoreState>
|
||||
null == recurringTransactions
|
||||
? _value.recurringTransactions
|
||||
: recurringTransactions // ignore: cast_nullable_to_non_nullable
|
||||
as List<RecurringTransaction>,
|
||||
as List<RecurringTransactionDto>,
|
||||
transactions:
|
||||
null == transactions
|
||||
? _value.transactions
|
||||
: transactions // ignore: cast_nullable_to_non_nullable
|
||||
as List<Transaction>,
|
||||
as List<TransactionDto>,
|
||||
transactionTemplates:
|
||||
null == transactionTemplates
|
||||
? _value.transactionTemplates
|
||||
: transactionTemplates // ignore: cast_nullable_to_non_nullable
|
||||
as List<TransactionTemplate>,
|
||||
as List<TransactionTemplateDto>,
|
||||
beneficiaries:
|
||||
null == beneficiaries
|
||||
? _value.beneficiaries
|
||||
@@ -142,22 +142,22 @@ class _$CoreStateCopyWithImpl<$Res, $Val extends CoreState>
|
||||
null == budgets
|
||||
? _value.budgets
|
||||
: budgets // ignore: cast_nullable_to_non_nullable
|
||||
as List<Budget>,
|
||||
as List<BudgetsDto>,
|
||||
activeBudget:
|
||||
freezed == activeBudget
|
||||
? _value.activeBudget
|
||||
: activeBudget // ignore: cast_nullable_to_non_nullable
|
||||
as Budget?,
|
||||
as BudgetsDto?,
|
||||
loans:
|
||||
null == loans
|
||||
? _value.loans
|
||||
: loans // ignore: cast_nullable_to_non_nullable
|
||||
as List<Loan>,
|
||||
as List<LoanDto>,
|
||||
activeLoan:
|
||||
freezed == activeLoan
|
||||
? _value.activeLoan
|
||||
: activeLoan // ignore: cast_nullable_to_non_nullable
|
||||
as Loan?,
|
||||
as LoanDto?,
|
||||
isDeletingAccount:
|
||||
null == isDeletingAccount
|
||||
? _value.isDeletingAccount
|
||||
@@ -181,17 +181,17 @@ abstract class _$$CoreStateImplCopyWith<$Res>
|
||||
$Res call({
|
||||
OkanePage activePage,
|
||||
int? activeAccountIndex,
|
||||
Transaction? activeTransaction,
|
||||
TransactionDto? activeTransaction,
|
||||
List<Account> accounts,
|
||||
List<RecurringTransaction> recurringTransactions,
|
||||
List<Transaction> transactions,
|
||||
List<TransactionTemplate> transactionTemplates,
|
||||
List<RecurringTransactionDto> recurringTransactions,
|
||||
List<TransactionDto> transactions,
|
||||
List<TransactionTemplateDto> transactionTemplates,
|
||||
List<Beneficiary> beneficiaries,
|
||||
List<ExpenseCategory> expenseCategories,
|
||||
List<Budget> budgets,
|
||||
Budget? activeBudget,
|
||||
List<Loan> loans,
|
||||
Loan? activeLoan,
|
||||
List<BudgetsDto> budgets,
|
||||
BudgetsDto? activeBudget,
|
||||
List<LoanDto> loans,
|
||||
LoanDto? activeLoan,
|
||||
bool isDeletingAccount,
|
||||
});
|
||||
}
|
||||
@@ -239,7 +239,7 @@ class __$$CoreStateImplCopyWithImpl<$Res>
|
||||
freezed == activeTransaction
|
||||
? _value.activeTransaction
|
||||
: activeTransaction // ignore: cast_nullable_to_non_nullable
|
||||
as Transaction?,
|
||||
as TransactionDto?,
|
||||
accounts:
|
||||
null == accounts
|
||||
? _value._accounts
|
||||
@@ -249,17 +249,17 @@ class __$$CoreStateImplCopyWithImpl<$Res>
|
||||
null == recurringTransactions
|
||||
? _value._recurringTransactions
|
||||
: recurringTransactions // ignore: cast_nullable_to_non_nullable
|
||||
as List<RecurringTransaction>,
|
||||
as List<RecurringTransactionDto>,
|
||||
transactions:
|
||||
null == transactions
|
||||
? _value._transactions
|
||||
: transactions // ignore: cast_nullable_to_non_nullable
|
||||
as List<Transaction>,
|
||||
as List<TransactionDto>,
|
||||
transactionTemplates:
|
||||
null == transactionTemplates
|
||||
? _value._transactionTemplates
|
||||
: transactionTemplates // ignore: cast_nullable_to_non_nullable
|
||||
as List<TransactionTemplate>,
|
||||
as List<TransactionTemplateDto>,
|
||||
beneficiaries:
|
||||
null == beneficiaries
|
||||
? _value._beneficiaries
|
||||
@@ -274,22 +274,22 @@ class __$$CoreStateImplCopyWithImpl<$Res>
|
||||
null == budgets
|
||||
? _value._budgets
|
||||
: budgets // ignore: cast_nullable_to_non_nullable
|
||||
as List<Budget>,
|
||||
as List<BudgetsDto>,
|
||||
activeBudget:
|
||||
freezed == activeBudget
|
||||
? _value.activeBudget
|
||||
: activeBudget // ignore: cast_nullable_to_non_nullable
|
||||
as Budget?,
|
||||
as BudgetsDto?,
|
||||
loans:
|
||||
null == loans
|
||||
? _value._loans
|
||||
: loans // ignore: cast_nullable_to_non_nullable
|
||||
as List<Loan>,
|
||||
as List<LoanDto>,
|
||||
activeLoan:
|
||||
freezed == activeLoan
|
||||
? _value.activeLoan
|
||||
: activeLoan // ignore: cast_nullable_to_non_nullable
|
||||
as Loan?,
|
||||
as LoanDto?,
|
||||
isDeletingAccount:
|
||||
null == isDeletingAccount
|
||||
? _value.isDeletingAccount
|
||||
@@ -308,14 +308,14 @@ class _$CoreStateImpl implements _CoreState {
|
||||
this.activeAccountIndex,
|
||||
this.activeTransaction = null,
|
||||
final List<Account> accounts = const [],
|
||||
final List<RecurringTransaction> recurringTransactions = const [],
|
||||
final List<Transaction> transactions = const [],
|
||||
final List<TransactionTemplate> transactionTemplates = const [],
|
||||
final List<RecurringTransactionDto> recurringTransactions = const [],
|
||||
final List<TransactionDto> transactions = const [],
|
||||
final List<TransactionTemplateDto> transactionTemplates = const [],
|
||||
final List<Beneficiary> beneficiaries = const [],
|
||||
final List<ExpenseCategory> expenseCategories = const [],
|
||||
final List<Budget> budgets = const [],
|
||||
final List<BudgetsDto> budgets = const [],
|
||||
this.activeBudget = null,
|
||||
final List<Loan> loans = const [],
|
||||
final List<LoanDto> loans = const [],
|
||||
this.activeLoan = null,
|
||||
this.isDeletingAccount = false,
|
||||
}) : _accounts = accounts,
|
||||
@@ -334,7 +334,7 @@ class _$CoreStateImpl implements _CoreState {
|
||||
final int? activeAccountIndex;
|
||||
@override
|
||||
@JsonKey()
|
||||
final Transaction? activeTransaction;
|
||||
final TransactionDto? activeTransaction;
|
||||
final List<Account> _accounts;
|
||||
@override
|
||||
@JsonKey()
|
||||
@@ -344,29 +344,29 @@ class _$CoreStateImpl implements _CoreState {
|
||||
return EqualUnmodifiableListView(_accounts);
|
||||
}
|
||||
|
||||
final List<RecurringTransaction> _recurringTransactions;
|
||||
final List<RecurringTransactionDto> _recurringTransactions;
|
||||
@override
|
||||
@JsonKey()
|
||||
List<RecurringTransaction> get recurringTransactions {
|
||||
List<RecurringTransactionDto> get recurringTransactions {
|
||||
if (_recurringTransactions is EqualUnmodifiableListView)
|
||||
return _recurringTransactions;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_recurringTransactions);
|
||||
}
|
||||
|
||||
final List<Transaction> _transactions;
|
||||
final List<TransactionDto> _transactions;
|
||||
@override
|
||||
@JsonKey()
|
||||
List<Transaction> get transactions {
|
||||
List<TransactionDto> get transactions {
|
||||
if (_transactions is EqualUnmodifiableListView) return _transactions;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_transactions);
|
||||
}
|
||||
|
||||
final List<TransactionTemplate> _transactionTemplates;
|
||||
final List<TransactionTemplateDto> _transactionTemplates;
|
||||
@override
|
||||
@JsonKey()
|
||||
List<TransactionTemplate> get transactionTemplates {
|
||||
List<TransactionTemplateDto> get transactionTemplates {
|
||||
if (_transactionTemplates is EqualUnmodifiableListView)
|
||||
return _transactionTemplates;
|
||||
// ignore: implicit_dynamic_type
|
||||
@@ -392,10 +392,10 @@ class _$CoreStateImpl implements _CoreState {
|
||||
return EqualUnmodifiableListView(_expenseCategories);
|
||||
}
|
||||
|
||||
final List<Budget> _budgets;
|
||||
final List<BudgetsDto> _budgets;
|
||||
@override
|
||||
@JsonKey()
|
||||
List<Budget> get budgets {
|
||||
List<BudgetsDto> get budgets {
|
||||
if (_budgets is EqualUnmodifiableListView) return _budgets;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_budgets);
|
||||
@@ -403,11 +403,11 @@ class _$CoreStateImpl implements _CoreState {
|
||||
|
||||
@override
|
||||
@JsonKey()
|
||||
final Budget? activeBudget;
|
||||
final List<Loan> _loans;
|
||||
final BudgetsDto? activeBudget;
|
||||
final List<LoanDto> _loans;
|
||||
@override
|
||||
@JsonKey()
|
||||
List<Loan> get loans {
|
||||
List<LoanDto> get loans {
|
||||
if (_loans is EqualUnmodifiableListView) return _loans;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_loans);
|
||||
@@ -415,7 +415,7 @@ class _$CoreStateImpl implements _CoreState {
|
||||
|
||||
@override
|
||||
@JsonKey()
|
||||
final Loan? activeLoan;
|
||||
final LoanDto? activeLoan;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool isDeletingAccount;
|
||||
@@ -497,17 +497,17 @@ abstract class _CoreState implements CoreState {
|
||||
const factory _CoreState({
|
||||
final OkanePage activePage,
|
||||
final int? activeAccountIndex,
|
||||
final Transaction? activeTransaction,
|
||||
final TransactionDto? activeTransaction,
|
||||
final List<Account> accounts,
|
||||
final List<RecurringTransaction> recurringTransactions,
|
||||
final List<Transaction> transactions,
|
||||
final List<TransactionTemplate> transactionTemplates,
|
||||
final List<RecurringTransactionDto> recurringTransactions,
|
||||
final List<TransactionDto> transactions,
|
||||
final List<TransactionTemplateDto> transactionTemplates,
|
||||
final List<Beneficiary> beneficiaries,
|
||||
final List<ExpenseCategory> expenseCategories,
|
||||
final List<Budget> budgets,
|
||||
final Budget? activeBudget,
|
||||
final List<Loan> loans,
|
||||
final Loan? activeLoan,
|
||||
final List<BudgetsDto> budgets,
|
||||
final BudgetsDto? activeBudget,
|
||||
final List<LoanDto> loans,
|
||||
final LoanDto? activeLoan,
|
||||
final bool isDeletingAccount,
|
||||
}) = _$CoreStateImpl;
|
||||
|
||||
@@ -516,27 +516,27 @@ abstract class _CoreState implements CoreState {
|
||||
@override
|
||||
int? get activeAccountIndex;
|
||||
@override
|
||||
Transaction? get activeTransaction;
|
||||
TransactionDto? get activeTransaction;
|
||||
@override
|
||||
List<Account> get accounts;
|
||||
@override
|
||||
List<RecurringTransaction> get recurringTransactions;
|
||||
List<RecurringTransactionDto> get recurringTransactions;
|
||||
@override
|
||||
List<Transaction> get transactions;
|
||||
List<TransactionDto> get transactions;
|
||||
@override
|
||||
List<TransactionTemplate> get transactionTemplates;
|
||||
List<TransactionTemplateDto> get transactionTemplates;
|
||||
@override
|
||||
List<Beneficiary> get beneficiaries;
|
||||
@override
|
||||
List<ExpenseCategory> get expenseCategories;
|
||||
@override
|
||||
List<Budget> get budgets;
|
||||
List<BudgetsDto> get budgets;
|
||||
@override
|
||||
Budget? get activeBudget;
|
||||
BudgetsDto? get activeBudget;
|
||||
@override
|
||||
List<Loan> get loans;
|
||||
List<LoanDto> get loans;
|
||||
@override
|
||||
Loan? get activeLoan;
|
||||
LoanDto? get activeLoan;
|
||||
@override
|
||||
bool get isDeletingAccount;
|
||||
@override
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:okane/database/collections/template.dart';
|
||||
import 'package:okane/database/sqlite.dart';
|
||||
import 'package:okane/screen.dart';
|
||||
import 'package:okane/ui/state/core.dart';
|
||||
|
||||
@@ -47,8 +47,10 @@ Future<T?> showDialogOrModal<T>({
|
||||
};
|
||||
}
|
||||
|
||||
Future<TransactionTemplate?> selectTransactionTemplate(BuildContext context) {
|
||||
return showDialogOrModal<TransactionTemplate>(
|
||||
Future<TransactionTemplateDto?> selectTransactionTemplate(
|
||||
BuildContext context,
|
||||
) {
|
||||
return showDialogOrModal<TransactionTemplateDto>(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return BlocBuilder<CoreCubit, CoreState>(
|
||||
@@ -64,7 +66,7 @@ Future<TransactionTemplate?> selectTransactionTemplate(BuildContext context) {
|
||||
itemCount: state.transactionTemplates.length,
|
||||
itemBuilder:
|
||||
(context, index) => ListTile(
|
||||
title: Text(state.transactionTemplates[index].name),
|
||||
title: Text(state.transactionTemplates[index].template.name),
|
||||
onTap: () {
|
||||
Navigator.of(
|
||||
context,
|
||||
@@ -142,3 +144,17 @@ Future<bool> confirm(BuildContext context, String title, String body) async {
|
||||
);
|
||||
return result ?? false;
|
||||
}
|
||||
|
||||
bool isTransactionDue(RecurringTransaction r, DateTime now) {
|
||||
if (r.lastExecution == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final expectedNextExecution = r.lastExecution!.add(Duration(days: r.days));
|
||||
if (now.isAfter(expectedNextExecution)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return now.difference(expectedNextExecution).inDays.abs() <=
|
||||
(r.days * 0.5).toInt();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:okane/database/collections/expense_category.dart';
|
||||
import 'package:okane/database/database.dart';
|
||||
import 'package:okane/i18n/strings.g.dart';
|
||||
import 'package:okane/ui/state/core.dart';
|
||||
|
||||
@@ -50,12 +48,14 @@ class AddExpenseCategoryState extends State<AddExpenseCategory> {
|
||||
Spacer(),
|
||||
OutlinedButton(
|
||||
onPressed: () async {
|
||||
// TODO
|
||||
/*
|
||||
final category =
|
||||
ExpenseCategory()
|
||||
..name = _categoryNameController.text;
|
||||
await upsertExpenseCategory(category);
|
||||
_categoryNameController.text = "";
|
||||
Navigator.of(context).pop(category);
|
||||
Navigator.of(context).pop(category);*/
|
||||
},
|
||||
child: Text(t.modals.add),
|
||||
),
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
import 'package:drift/drift.dart' show Value;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:get_it/get_it.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/recurrent.dart';
|
||||
import 'package:okane/database/collections/template.dart';
|
||||
import 'package:okane/database/database.dart';
|
||||
import 'package:okane/database/sqlite.dart';
|
||||
import 'package:okane/i18n/strings.g.dart';
|
||||
import 'package:okane/ui/state/core.dart';
|
||||
import 'package:okane/ui/transaction.dart';
|
||||
@@ -68,6 +64,7 @@ class _AddTransactionTemplateWidgetState
|
||||
return;
|
||||
}
|
||||
|
||||
final db = GetIt.I.get<OkaneDatabase>();
|
||||
Beneficiary? beneficiary = _selectedBeneficiary?.item;
|
||||
if (beneficiary == null ||
|
||||
getBeneficiaryName(beneficiary) != beneficiaryName) {
|
||||
@@ -101,12 +98,12 @@ class _AddTransactionTemplateWidgetState
|
||||
if (result == null || !result) {
|
||||
return;
|
||||
}
|
||||
|
||||
beneficiary =
|
||||
Beneficiary()
|
||||
..name = beneficiaryName
|
||||
..type = BeneficiaryType.other;
|
||||
await upsertBeneficiary(beneficiary);
|
||||
beneficiary = await db.beneficiariesDao.upsertBeneficiary(
|
||||
BeneficiariesCompanion(
|
||||
name: Value(beneficiaryName),
|
||||
type: Value(BeneficiaryType.other),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final factor = switch (_selectedDirection) {
|
||||
@@ -114,15 +111,16 @@ class _AddTransactionTemplateWidgetState
|
||||
TransactionDirection.receive => 1,
|
||||
};
|
||||
final amount = factor * double.parse(_amountTextController.text).abs();
|
||||
final template =
|
||||
TransactionTemplate()
|
||||
..name = _templateNameController.text
|
||||
..account.value = widget.activeAccountItem
|
||||
..beneficiary.value = beneficiary
|
||||
..expenseCategory.value = _expenseCategory
|
||||
..recurring = _isRecurring
|
||||
..amount = amount;
|
||||
await upsertTransactionTemplate(template);
|
||||
final template = await db.transactionTemplatesDao.upsertTemplate(
|
||||
TransactionTemplatesCompanion(
|
||||
name: Value(_templateNameController.text),
|
||||
accountId: Value(widget.activeAccountItem.id),
|
||||
beneficiaryId: Value(beneficiary.id),
|
||||
expenseCategoryId: Value(_expenseCategory?.id),
|
||||
recurring: Value(_isRecurring),
|
||||
amount: Value(amount),
|
||||
),
|
||||
);
|
||||
|
||||
if (_isRecurring) {
|
||||
final days = switch (_selectedPeriod) {
|
||||
@@ -131,13 +129,14 @@ class _AddTransactionTemplateWidgetState
|
||||
Period.months => _periodSize * 31,
|
||||
Period.years => _periodSize * 365,
|
||||
};
|
||||
final recurringTransaction =
|
||||
RecurringTransaction()
|
||||
..account.value = widget.activeAccountItem
|
||||
..template.value = template
|
||||
..lastExecution = null
|
||||
..days = days;
|
||||
await upsertRecurringTransaction(recurringTransaction);
|
||||
await db.recurringTransactionsDao.upsertRecurringTransaction(
|
||||
RecurringTransactionsCompanion(
|
||||
accountId: Value(widget.activeAccountItem.id),
|
||||
templateId: Value(template.id),
|
||||
lastExecution: Value(null),
|
||||
days: Value(days),
|
||||
),
|
||||
);
|
||||
}
|
||||
widget.onAdd();
|
||||
}
|
||||
@@ -190,8 +189,7 @@ class _AddTransactionTemplateWidgetState
|
||||
.where((el) {
|
||||
final bloc = GetIt.I.get<CoreCubit>();
|
||||
if (el.type == BeneficiaryType.account) {
|
||||
return el.account.value?.id !=
|
||||
bloc.activeAccount?.id;
|
||||
return el.accountId != bloc.activeAccount?.id;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:get_it/get_it.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/template.dart';
|
||||
import 'package:okane/database/collections/transaction.dart';
|
||||
import 'package:okane/database/database.dart';
|
||||
import 'package:okane/database/sqlite.dart';
|
||||
import 'package:okane/i18n/strings.g.dart';
|
||||
import 'package:okane/ui/state/core.dart';
|
||||
import 'package:okane/ui/transaction.dart';
|
||||
@@ -14,14 +10,14 @@ import 'package:okane/ui/utils.dart';
|
||||
import 'package:okane/ui/widgets/add_expense_category.dart';
|
||||
import 'package:searchfield/searchfield.dart';
|
||||
|
||||
typedef AddTransactionCallback = void Function(Transaction);
|
||||
typedef AddTransactionCallback = void Function(TransactionDto);
|
||||
|
||||
class AddTransactionWidget extends StatefulWidget {
|
||||
final AddTransactionCallback onAdd;
|
||||
|
||||
final Account activeAccountItem;
|
||||
|
||||
final TransactionTemplate? template;
|
||||
final TransactionTemplateDto? template;
|
||||
|
||||
const AddTransactionWidget({
|
||||
super.key,
|
||||
@@ -51,6 +47,8 @@ class _AddTransactionWidgetState extends State<AddTransactionWidget> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
// TODO
|
||||
/*
|
||||
if (widget.template != null) {
|
||||
_selectedDirection =
|
||||
widget.template!.amount > 0
|
||||
@@ -58,12 +56,12 @@ class _AddTransactionWidgetState extends State<AddTransactionWidget> {
|
||||
: TransactionDirection.send;
|
||||
_amountTextController.text = widget.template!.amount.toString();
|
||||
_beneficiaryTextController.text =
|
||||
widget.template!.beneficiary.value!.name;
|
||||
widget.template!;
|
||||
_selectedBeneficiary = SearchFieldListItem(
|
||||
getBeneficiaryName(widget.template!.beneficiary.value!),
|
||||
item: widget.template!.beneficiary.value!,
|
||||
);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
String getBeneficiaryName(Beneficiary item) {
|
||||
@@ -81,6 +79,7 @@ class _AddTransactionWidgetState extends State<AddTransactionWidget> {
|
||||
return;
|
||||
}
|
||||
|
||||
final db = GetIt.I.get<OkaneDatabase>();
|
||||
Beneficiary? beneficiary = _selectedBeneficiary?.item;
|
||||
if (beneficiary == null ||
|
||||
getBeneficiaryName(beneficiary) != beneficiaryName) {
|
||||
@@ -115,11 +114,12 @@ class _AddTransactionWidgetState extends State<AddTransactionWidget> {
|
||||
return;
|
||||
}
|
||||
|
||||
beneficiary =
|
||||
Beneficiary()
|
||||
..name = beneficiaryName
|
||||
..type = BeneficiaryType.other;
|
||||
await upsertBeneficiary(beneficiary);
|
||||
beneficiary = await db.beneficiariesDao.upsertBeneficiary(
|
||||
BeneficiariesCompanion(
|
||||
name: Value(beneficiaryName),
|
||||
type: Value(BeneficiaryType.other),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final factor = switch (_selectedDirection) {
|
||||
@@ -127,30 +127,38 @@ class _AddTransactionWidgetState extends State<AddTransactionWidget> {
|
||||
TransactionDirection.receive => 1,
|
||||
};
|
||||
final amount = factor * double.parse(_amountTextController.text).abs();
|
||||
final transaction =
|
||||
Transaction()
|
||||
..account.value = widget.activeAccountItem
|
||||
..beneficiary.value = beneficiary
|
||||
..amount = amount
|
||||
..tags = []
|
||||
..expenseCategory.value = _expenseCategory
|
||||
..date = _selectedDate;
|
||||
await upsertTransaction(transaction);
|
||||
final rawTransaction = TransactionsCompanion(
|
||||
accountId: Value(widget.activeAccountItem.id),
|
||||
beneficiaryId: Value(beneficiary.id),
|
||||
amount: Value(amount),
|
||||
// tags: [],
|
||||
expenseCategoryId: Value(_expenseCategory?.id),
|
||||
date: Value(_selectedDate),
|
||||
);
|
||||
final transaction = await db.transactionsDao.upsertTransaction(
|
||||
rawTransaction,
|
||||
);
|
||||
|
||||
if (beneficiary.type == BeneficiaryType.account) {
|
||||
final otherTransaction =
|
||||
Transaction()
|
||||
..account.value = beneficiary.account.value!
|
||||
..beneficiary.value = await getAccountBeneficiary(
|
||||
widget.activeAccountItem,
|
||||
)
|
||||
..date = _selectedDate
|
||||
..expenseCategory.value = _expenseCategory
|
||||
..amount = -1 * amount;
|
||||
await upsertTransaction(otherTransaction);
|
||||
final otherTransaction = rawTransaction.copyWith(
|
||||
accountId: Value(beneficiary.accountId!),
|
||||
beneficiaryId: Value(
|
||||
(await db.beneficiariesDao.getAccountBeneficiary(
|
||||
widget.activeAccountItem,
|
||||
)).id,
|
||||
),
|
||||
amount: Value(-1 * rawTransaction.amount.value),
|
||||
);
|
||||
await db.transactionsDao.upsertTransaction(otherTransaction);
|
||||
}
|
||||
|
||||
widget.onAdd(transaction);
|
||||
widget.onAdd(
|
||||
TransactionDto(
|
||||
transaction: transaction,
|
||||
beneficiary: beneficiary,
|
||||
expenseCategory: _expenseCategory,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -167,17 +175,17 @@ class _AddTransactionWidgetState extends State<AddTransactionWidget> {
|
||||
return;
|
||||
}
|
||||
|
||||
_amountTextController.text = template.amount.toString();
|
||||
_amountTextController.text = template.template.amount.toString();
|
||||
_selectedDirection =
|
||||
template.amount > 0
|
||||
template.template.amount > 0
|
||||
? TransactionDirection.receive
|
||||
: TransactionDirection.send;
|
||||
_selectedBeneficiary = SearchFieldListItem(
|
||||
getBeneficiaryName(template.beneficiary.value!),
|
||||
item: template.beneficiary.value!,
|
||||
getBeneficiaryName(template.beneficiary),
|
||||
item: template.beneficiary,
|
||||
);
|
||||
_beneficiaryTextController.text = getBeneficiaryName(
|
||||
template.beneficiary.value!,
|
||||
template.beneficiary,
|
||||
);
|
||||
},
|
||||
child: Text(t.pages.transactions.addTransaction.useTemplate),
|
||||
@@ -216,7 +224,7 @@ class _AddTransactionWidgetState extends State<AddTransactionWidget> {
|
||||
.where((el) {
|
||||
final bloc = GetIt.I.get<CoreCubit>();
|
||||
if (el.type == BeneficiaryType.account) {
|
||||
return el.account.value?.id.toInt() ==
|
||||
return el.accountId ==
|
||||
bloc.activeAccount?.id.toInt();
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:okane/database/collections/transaction.dart';
|
||||
import 'package:okane/database/sqlite.dart';
|
||||
import 'package:okane/ui/utils.dart';
|
||||
import 'package:okane/ui/widgets/image_wrapper.dart';
|
||||
|
||||
@@ -13,7 +13,7 @@ class TransactionCard extends StatelessWidget {
|
||||
this.subtitle,
|
||||
});
|
||||
|
||||
final Transaction transaction;
|
||||
final TransactionDto transaction;
|
||||
|
||||
final VoidCallback onTap;
|
||||
|
||||
@@ -24,19 +24,22 @@ class TransactionCard extends StatelessWidget {
|
||||
child: ListTile(
|
||||
onTap: onTap,
|
||||
leading: ImageWrapper(
|
||||
title: transaction.beneficiary.value!.name,
|
||||
path: transaction.beneficiary.value!.imagePath,
|
||||
title: transaction.beneficiary.name,
|
||||
path: transaction.beneficiary.imagePath,
|
||||
),
|
||||
trailing: Text(formatDateTime(transaction.date)),
|
||||
title: Text(transaction.beneficiary.value!.name),
|
||||
trailing: Text(formatDateTime(transaction.transaction.date)),
|
||||
title: Text(transaction.beneficiary.name),
|
||||
subtitle: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
formatCurrency(transaction.amount),
|
||||
formatCurrency(transaction.transaction.amount),
|
||||
style: TextStyle(
|
||||
color: transaction.amount < 0 ? Colors.red : Colors.green,
|
||||
color:
|
||||
transaction.transaction.amount < 0
|
||||
? Colors.red
|
||||
: Colors.green,
|
||||
),
|
||||
),
|
||||
if (subtitle != null) subtitle!,
|
||||
|
||||
Reference in New Issue
Block a user