Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactoring recurring transactions #167

Merged
merged 32 commits into from
Nov 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
ff87484
refactoring recurring transactions: db tables
mikev-cw Apr 22, 2024
08d06e1
refactoring recurring transactions: models and main function
mikev-cw Apr 23, 2024
b146b83
improved recurring transactions logic
mikev-cw Apr 25, 2024
ad5917b
improved recurring transactions logic, added bank account ID field
mikev-cw Apr 26, 2024
26ca899
added resetDatabase() method
mikev-cw Apr 26, 2024
5d1e89e
INIT recurring transactions visualization
K-w-e May 1, 2024
3ea5966
Fix TextField amout new transaction
K-w-e May 3, 2024
7bbf302
INIT add/visualization recurrin transaction
K-w-e May 3, 2024
ce0294d
Fix update and add recurring transaction + style
K-w-e May 6, 2024
314c4ce
added recurring trns highlight, fixed tests
mikev-cw May 7, 2024
763aab4
Fix edit recurring payment
K-w-e May 8, 2024
fcdd3fc
Fix recurring info + new end repetition selector
K-w-e May 10, 2024
1c815f1
Fix test
K-w-e May 11, 2024
8f94262
fix deprecated textstyles in collaborators page
IamRezaMousavi Jun 17, 2024
85cfb42
Merge pull request #169 from IamRezaMousavi/fix-deprecated-textstyles
lucaantonelli Jul 2, 2024
abd9bda
refactoring recurring transactions: db tables
mikev-cw Apr 22, 2024
45956af
refactoring recurring transactions: models and main function
mikev-cw Apr 23, 2024
37cfedd
improved recurring transactions logic
mikev-cw Apr 25, 2024
975d4d0
improved recurring transactions logic, added bank account ID field
mikev-cw Apr 26, 2024
f2c6612
added resetDatabase() method
mikev-cw Apr 26, 2024
74b7b62
INIT recurring transactions visualization
K-w-e May 1, 2024
84eb36e
Fix TextField amout new transaction
K-w-e May 3, 2024
c01e627
INIT add/visualization recurrin transaction
K-w-e May 3, 2024
60b07d5
Fix update and add recurring transaction + style
K-w-e May 6, 2024
cec5f3b
added recurring trns highlight, fixed tests
mikev-cw May 7, 2024
8dd6fe1
Fix edit recurring payment
K-w-e May 8, 2024
6817b44
Fix recurring info + new end repetition selector
K-w-e May 10, 2024
d3a1a49
Fix test
K-w-e May 11, 2024
ec116cc
Merge remote-tracking branch 'origin/main'
mikev-cw Oct 14, 2024
bff77d1
front end changes to the recurring transaction refactor
mikev-cw Oct 25, 2024
a1cfd19
fixed single/recurring transactions CRUD operations behaviors on fron…
mikev-cw Oct 29, 2024
84a0bfe
removed dev code
mikev-cw Oct 29, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"

android {
compileSdkVersion 33
compileSdkVersion 34
ndkVersion flutter.ndkVersion

compileOptions {
Expand Down
7 changes: 6 additions & 1 deletion lib/custom_widgets/transactions_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,10 @@ class TransactionRow extends ConsumerWidget with Functions {
.read(transactionsProvider.notifier)
.transactionUpdateState(transaction)
.whenComplete(
() => Navigator.of(context).pushNamed("/add-page"));
() => Navigator.of(context).pushNamed(
"/add-page",
arguments: {'recurrencyEditingPermitted': !transaction.recurring}
));
},
borderRadius: BorderRadius.vertical(
top: first ? const Radius.circular(8) : Radius.zero,
Expand Down Expand Up @@ -224,6 +227,8 @@ class TransactionRow extends ConsumerWidget with Functions {
const SizedBox(height: 11),
Row(
children: [
if (transaction.recurring) // Check if the transaction is recurring
const Icon(Icons.repeat, color: Colors.blueAccent), // Add an icon for recurring transactions
if (transaction.note != null)
Text(
transaction.note!,
Expand Down
76 changes: 51 additions & 25 deletions lib/database/sossoldi_database.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import '../model/bank_account.dart';
import '../model/budget.dart';
import '../model/category_transaction.dart';
import '../model/currency.dart';
import '../model/recurring_transaction_amount.dart';
import '../model/recurring_transaction.dart';
import '../model/transaction.dart';

class SossoldiDatabase {
Expand Down Expand Up @@ -75,25 +75,26 @@ class SossoldiDatabase {
`${TransactionFields.idBankAccount}` $integerNotNull,
`${TransactionFields.idBankAccountTransfer}` $integer,
`${TransactionFields.recurring}` $integerNotNull CHECK (${TransactionFields.recurring} IN (0, 1)),
`${TransactionFields.recurrencyType}` $text,
`${TransactionFields.recurrencyPayDay}` $integer,
`${TransactionFields.recurrencyFrom}` $text,
`${TransactionFields.recurrencyTo}` $text,
`${TransactionFields.idRecurringTransaction}` $integer,
`${TransactionFields.createdAt}` $textNotNull,
`${TransactionFields.updatedAt}` $textNotNull
)
''');

// Recurring Transactions Amount Table
await database.execute('''
CREATE TABLE `$recurringTransactionAmountTable`(
`${RecurringTransactionAmountFields.id}` $integerPrimaryKeyAutoincrement,
`${RecurringTransactionAmountFields.from}` $textNotNull,
`${RecurringTransactionAmountFields.to}` $textNotNull,
`${RecurringTransactionAmountFields.amount}` $realNotNull,
`${RecurringTransactionAmountFields.idTransaction}` $integerNotNull,
`${RecurringTransactionAmountFields.createdAt}` $textNotNull,
`${RecurringTransactionAmountFields.updatedAt}` $textNotNull
CREATE TABLE `$recurringTransactionTable`(
`${RecurringTransactionFields.id}` $integerPrimaryKeyAutoincrement,
`${RecurringTransactionFields.fromDate}` $textNotNull,
`${RecurringTransactionFields.toDate}` $text,
`${RecurringTransactionFields.amount}` $realNotNull,
`${RecurringTransactionFields.note}` $textNotNull,
`${RecurringTransactionFields.recurrency}` $textNotNull,
`${RecurringTransactionFields.idCategory}` $integerNotNull,
`${RecurringTransactionFields.idBankAccount}` $integerNotNull,
`${RecurringTransactionFields.lastInsertion}` $text,
`${RecurringTransactionFields.createdAt}` $textNotNull,
`${RecurringTransactionFields.updatedAt}` $textNotNull
)
''');

Expand Down Expand Up @@ -154,7 +155,8 @@ class SossoldiDatabase {
(12, "Furniture", "home", 2, '', 11, '${DateTime.now()}', '${DateTime.now()}'),
(13, "Shopping", "shopping_cart", 3, '', null, '${DateTime.now()}', '${DateTime.now()}'),
(14, "Leisure", "subscriptions", 4, '', null, '${DateTime.now()}', '${DateTime.now()}'),
(15, "Salary", "work", 5, '', null, '${DateTime.now()}', '${DateTime.now()}');
(15, "Salary", "work", 5, '', null, '${DateTime.now()}', '${DateTime.now()}'),
(16, "Transports", "directions_car_rounded", 6, '', null, '${DateTime.now()}', '${DateTime.now()}');
''');

// Add currencies
Expand All @@ -169,23 +171,31 @@ class SossoldiDatabase {
// Add fake budgets
await _database?.execute('''
INSERT INTO budget(idCategory, name, amountLimit, active, createdAt, updatedAt) VALUES
(13, "Grocery", 400.00, 1, '${DateTime.now()}', '${DateTime.now()}'),
(13, "Grocery", 900.00, 1, '${DateTime.now()}', '${DateTime.now()}'),
(11, "Home", 123.45, 0, '${DateTime.now()}', '${DateTime.now()}');
''');

// Add fake recurring transactions
await _database?.execute('''
INSERT INTO recurringTransaction(fromDate, toDate, amount, note, recurrency, idCategory, idBankAccount, createdAt, updatedAt) VALUES
("2024-02-23", null, 10.99, "404 Books", "MONTHLY", 14, 70, '${DateTime.now()}', '${DateTime.now()}'),
("2023-12-13", null, 4.97, "ETF Consultant Parcel", "DAILY", 14, 70, '${DateTime.now()}', '${DateTime.now()}'),
("2023-02-11", "2028-02-11", 1193.40, "Car Loan", "QUARTERLY", 16, 72, '${DateTime.now()}', '${DateTime.now()}');
''');

// Add fake transactions
// First initialize some config stuff
final rnd = Random();
var accounts = [70,71,72];
var outNotes = ['Grocery', 'Tolls', 'Toys', 'ETF Consultant Parcel', 'Concert', 'Clothing', 'Pizza', 'Drugs', 'Laundry', 'Taxes', 'Health insurance', 'Furniture', 'Car Fuel', 'Train', 'Amazon', 'Delivery', 'CHEK dividends', 'Babysitter', 'sono.pove.ro Fees', 'Quingentole trip'];
var outNotes = ['Grocery', 'Tolls', 'Toys', 'Boardgames', 'Concert', 'Clothing', 'Pizza', 'Drugs', 'Laundry', 'Taxes', 'Health insurance', 'Furniture', 'Car Fuel', 'Train', 'Amazon', 'Delivery', 'CHEK dividends', 'Babysitter', 'sono.pove.ro Fees', 'Quingentole trip'];
var categories = [10,11,12,13,14];
double maxAmountOfSingleTransaction = 250.00;
int dateInPastMaxRange = (countOfGeneratedTransaction / 90 ).round() * 30; // we want simulate about 90 transactions per month
num fakeSalary = 5000;
DateTime now = DateTime.now();

// start building mega-query
const insertDemoTransactionsQuery = '''INSERT INTO `transaction` (date, amount, type, note, idCategory, idBankAccount, idBankAccountTransfer, recurring, recurrencyType, recurrencyPayDay, recurrencyFrom, recurrencyTo, createdAt, updatedAt) VALUES ''';
const insertDemoTransactionsQuery = '''INSERT INTO `transaction` (date, amount, type, note, idCategory, idBankAccount, idBankAccountTransfer, recurring, idRecurringTransaction, createdAt, updatedAt) VALUES ''';

// init a List with transaction values
final List<String> demoTransactions = [];
Expand Down Expand Up @@ -224,32 +234,48 @@ class SossoldiDatabase {
}

// put generated transaction in our list
demoTransactions.add('''('$randomDate', ${randomAmount.toStringAsFixed(2)}, '$randomType', '$randomNote', $randomCategory, $randomAccount, $idBankAccountTransfer, 0, null, null, null, null, '$randomDate', '$randomDate')''');
demoTransactions.add('''('$randomDate', ${randomAmount.toStringAsFixed(2)}, '$randomType', '$randomNote', $randomCategory, $randomAccount, $idBankAccountTransfer, 0, null, '$randomDate', '$randomDate')''');
}

// add salary every month
for (int i = 1; i < dateInPastMaxRange/30; i++) {
DateTime randomDate = now.subtract(Duration(days: 30*i));
var time = randomDate.toLocal();
DateTime salaryDateTime = DateTime(time.year, time.month, 27, time.hour, time.minute, time.second, time.millisecond, time.microsecond);
demoTransactions.add('''('$salaryDateTime', $fakeSalary, 'IN', 'Salary', 15, 70, null, 0, null, null, null, null, '$salaryDateTime', '$salaryDateTime')''');
demoTransactions.add('''('$salaryDateTime', $fakeSalary, 'IN', 'Salary', 15, 70, null, 0, null, '$salaryDateTime', '$salaryDateTime')''');
}

// add some recurring payment too
demoTransactions.add('''(null, 7.99, 'OUT', 'Netflix', 14, 71, null, 1, 'monthly', 19, '2022-11-14', null, '2022-11-14 03:33:36.048611', '2022-11-14 03:33:36.048611')''');
demoTransactions.add('''(null, 292.39, 'OUT', 'Car Loan', 13, 70, null, 1, 'monthly', 27, '2019-10-03', '2024-10-02', '2022-10-04 03:33:36.048611', '2022-10-04 03:33:36.048611')''');

// finalize query and write!
await _database?.execute("$insertDemoTransactionsQuery ${demoTransactions.join(",")};");
}

Future resetDatabase() async {
// delete database
try{
await _database?.transaction((txn) async {
var batch = txn.batch();
// drop tables
batch.execute('DROP TABLE IF EXISTS $bankAccountTable');
batch.execute('DROP TABLE IF EXISTS `$transactionTable`');
batch.execute('DROP TABLE IF EXISTS $recurringTransactionTable');
batch.execute('DROP TABLE IF EXISTS $categoryTransactionTable');
batch.execute('DROP TABLE IF EXISTS $budgetTable');
batch.execute('DROP TABLE IF EXISTS $currencyTable');
await batch.commit();
});
} catch(error){
throw Exception('DbBase.resetDatabase: $error');
}
await _createDB(_database!, 1);
}

Future clearDatabase() async {
try{
await _database?.transaction((txn) async {
var batch = txn.batch();
batch.delete(bankAccountTable);
batch.delete(transactionTable);
batch.delete(recurringTransactionAmountTable);
batch.delete(recurringTransactionTable);
batch.delete(categoryTransactionTable);
batch.delete(budgetTable);
batch.delete(currencyTable);
Expand All @@ -268,7 +294,7 @@ class SossoldiDatabase {
// WARNING: FOR DEV/TEST PURPOSES ONLY!!
Future<void> deleteDatabase() async {
final databasePath = await getDatabasesPath();
final path = join(databasePath, 'sossoldi.db');
final path = join(databasePath, dbName);
databaseFactory.deleteDatabase(path);
}
}
15 changes: 14 additions & 1 deletion lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:workmanager/workmanager.dart';
import 'package:sossoldi/utils/worker_manager.dart';

import 'model/recurring_transaction.dart';
import 'utils/worker_manager.dart';
import 'pages/notifications/notifications_service.dart';
import 'providers/theme_provider.dart';
import 'routes.dart';
Expand All @@ -16,17 +17,29 @@ bool? _isFirstLogin = true;

void main() async {
WidgetsFlutterBinding.ensureInitialized();

if(Platform.isAndroid){
requestNotificationPermissions();
initializeNotifications();
Workmanager().initialize(callbackDispatcher);
}

SharedPreferences preferences = await SharedPreferences.getInstance();

bool? getPref = preferences.getBool('is_first_login');
getPref == null ? await preferences.setBool('is_first_login', false) : null;
_isFirstLogin = getPref;

// perform recurring transactions checks
DateTime? lastCheckGetPref = preferences.getString('last_recurring_transactions_check') != null ? DateTime.parse(preferences.getString('last_recurring_transactions_check')!) : null;
DateTime? lastRecurringTransactionsCheck = lastCheckGetPref;

if(lastRecurringTransactionsCheck == null || DateTime.now().difference(lastRecurringTransactionsCheck).inDays >= 1){
RecurringTransactionMethods().checkRecurringTransactions();
// update last recurring transactions runtime
await preferences.setString('last_recurring_transactions_check', DateTime.now().toIso8601String());
}

initializeDateFormatting('it_IT', null).then((_) => runApp(const ProviderScope(child: Launcher())));
}

Expand Down
Loading
Loading