From be66f52cbf1674c5390fad108d295be7b072caba Mon Sep 17 00:00:00 2001 From: Hyperling Date: Fri, 21 Feb 2025 08:56:50 -0700 Subject: [PATCH] Move down to only 1 abstract data type with a nully Frequency. Should aid in making the Expense page usable for all 3 data type. --- lib/db.dart | 6 +- lib/models/asset.dart | 5 +- lib/models/database_backup.dart | 4 +- lib/models/expense.dart | 6 +- lib/models/income.dart | 6 +- lib/models/tracked_item.dart | 49 ++++++++++ lib/models/tracked_type.dart | 30 ------- lib/models/tracked_type_recurring.dart | 34 ------- lib/pages/expense.dart | 118 +++++++++++++------------ lib/pages/home.dart | 2 +- lib/pages/report.dart | 12 +-- 11 files changed, 133 insertions(+), 139 deletions(-) create mode 100644 lib/models/tracked_item.dart delete mode 100644 lib/models/tracked_type.dart delete mode 100644 lib/models/tracked_type_recurring.dart diff --git a/lib/db.dart b/lib/db.dart index 6599100..b9c4905 100644 --- a/lib/db.dart +++ b/lib/db.dart @@ -9,7 +9,7 @@ import 'package:sqflite/sqflite.dart'; // Local import '/models/expense.dart'; -import '/models/tracked_type_recurring.dart'; +import '/models/tracked_item.dart'; // Leaned on this example: // https://learnflutterwithme.com/sqlite @@ -77,7 +77,7 @@ class DatabaseHelper { return expenseList; } - Future addExpense(RecurringTrackedType expense) async { + Future addExpense(TrackedItem expense) async { Database db = await instance.db; return await db.insert( "expense", @@ -94,7 +94,7 @@ class DatabaseHelper { ); } - Future updateExpense(RecurringTrackedType expense) async { + Future updateExpense(TrackedItem expense) async { Database db = await instance.db; return await db.update( "expense", diff --git a/lib/models/asset.dart b/lib/models/asset.dart index 49512d8..aa76c7a 100644 --- a/lib/models/asset.dart +++ b/lib/models/asset.dart @@ -1,6 +1,7 @@ -import '/models/tracked_type.dart'; +// Local +import '/models/tracked_item.dart'; -class Asset extends TrackedType { +class Asset extends TrackedItem { static String amountText = "Amount"; Asset({ diff --git a/lib/models/database_backup.dart b/lib/models/database_backup.dart index dae3553..968c35b 100644 --- a/lib/models/database_backup.dart +++ b/lib/models/database_backup.dart @@ -29,7 +29,7 @@ class DatabaseBackup { 'id': e.id, 'name': e.name, 'cost': e.amount, - 'frequency': e.frequency.title, + 'frequency': e.frequency!.title, 'description': e.description, }, ], @@ -39,7 +39,7 @@ class DatabaseBackup { 'id': i.id, 'name': i.name, 'revenue': i.amount, - 'frequency': i.frequency.title, + 'frequency': i.frequency!.title, 'description': i.description, }, ], diff --git a/lib/models/expense.dart b/lib/models/expense.dart index 1f0a272..a6c090d 100644 --- a/lib/models/expense.dart +++ b/lib/models/expense.dart @@ -1,8 +1,8 @@ // Local -import '/models/tracked_type_recurring.dart'; +import '/models/tracked_item.dart'; import '/models/frequency.dart'; -class Expense extends RecurringTrackedType { +class Expense extends TrackedItem { static String amountText = "Cost"; Expense({ @@ -29,7 +29,7 @@ class Expense extends RecurringTrackedType { 'id': id, 'name': name, 'cost': amount, - 'frequency': frequency.title, + 'frequency': frequency!.title, 'description': description, }; } diff --git a/lib/models/income.dart b/lib/models/income.dart index b35033f..030b75e 100644 --- a/lib/models/income.dart +++ b/lib/models/income.dart @@ -1,8 +1,8 @@ // Local -import '/models/tracked_type_recurring.dart'; +import '/models/tracked_item.dart'; import '/models/frequency.dart'; -class Income extends RecurringTrackedType { +class Income extends TrackedItem { static String amountText = "Revenue"; Income({ @@ -29,7 +29,7 @@ class Income extends RecurringTrackedType { 'id': id, 'name': name, 'revenue': amount, - 'frequency': frequency.title, + 'frequency': frequency!.title, 'description': description, }; } diff --git a/lib/models/tracked_item.dart b/lib/models/tracked_item.dart new file mode 100644 index 0000000..bea8c00 --- /dev/null +++ b/lib/models/tracked_item.dart @@ -0,0 +1,49 @@ +// Local +import '/models/frequency.dart'; + +abstract class TrackedItem { + int? id; + String name; + double amount; + Frequency? frequency; + String description; + + TrackedItem({ + this.id, + required this.name, + required this.amount, + this.frequency, + required this.description, + }); + + static String amountText = "Amount"; + String getAmountText() => amountText; + + @override + String toString() { + return toMap().toString(); + } + + double calcComparableAmountYearly() { + return frequency == null ? 0 : amount * frequency!.timesPerYear; + } + + double calcComparableAmountDaily() { + return frequency == null ? 0 : amount / frequency!.numDays; + } + + Map toMap() { + return frequency == null ? { + 'id': id, + 'name': name, + 'amount': amount, + 'description': description, + } : { + 'id': id, + 'name': name, + 'amount': amount, + 'frequency': frequency!.title, + 'description': description, + }; + } +} diff --git a/lib/models/tracked_type.dart b/lib/models/tracked_type.dart deleted file mode 100644 index 8070a32..0000000 --- a/lib/models/tracked_type.dart +++ /dev/null @@ -1,30 +0,0 @@ -abstract class TrackedType { - int? id; - String name; - double amount; - String description; - - TrackedType({ - this.id, - required this.name, - required this.amount, - required this.description, - }); - - static String amountText = "Amount"; - String getAmountText() => amountText; - - @override - String toString() { - return toMap().toString(); - } - - Map toMap() { - return { - 'id': id, - 'name': name, - 'amount': amount, - 'description': description, - }; - } -} diff --git a/lib/models/tracked_type_recurring.dart b/lib/models/tracked_type_recurring.dart deleted file mode 100644 index 3eabfde..0000000 --- a/lib/models/tracked_type_recurring.dart +++ /dev/null @@ -1,34 +0,0 @@ -// Local -import '/models/tracked_type.dart'; -import '/models/frequency.dart'; - -abstract class RecurringTrackedType extends TrackedType { - Frequency frequency; - - RecurringTrackedType({ - super.id, - required super.name, - required super.amount, - required this.frequency, - required super.description, - }); - - double calcComparableAmountYearly() { - return amount * frequency.timesPerYear; - } - - double calcComparableAmountDaily() { - return amount / frequency.numDays; - } - - @override - Map toMap() { - return { - 'id': id, - 'name': name, - 'amount': amount, - 'frequency': frequency.title, - 'description': description, - }; - } -} diff --git a/lib/pages/expense.dart b/lib/pages/expense.dart index 891b86b..32b2816 100644 --- a/lib/pages/expense.dart +++ b/lib/pages/expense.dart @@ -2,8 +2,7 @@ import 'package:flutter/material.dart'; // Local -import '/models/tracked_type.dart'; -import '/models/tracked_type_recurring.dart'; +import '/models/tracked_item.dart'; import '/models/expense.dart'; import '/models/frequency.dart'; import '/db.dart'; @@ -28,10 +27,10 @@ class _ExpensePageState extends State { Widget build(BuildContext context) { final theme = Theme.of(context); - return FutureBuilder>( + return FutureBuilder>( future: DatabaseHelper.instance.getExpenses(), builder: (BuildContext context, - AsyncSnapshot> snapshot) { + AsyncSnapshot> snapshot) { if (!snapshot.hasData) { return Text('Loading...'); } @@ -49,58 +48,67 @@ class _ExpensePageState extends State { itemCount: snapshot.data!.length, itemBuilder: (_, index) { //List expenses = snapshot.data!; - final RecurringTrackedType curr = snapshot.data![index]; + final TrackedItem curr = snapshot.data![index]; final itemKey = Key(curr.id!.toString()); final String itemTitle = curr.name; + final String itemAmount; - // TODO: How can we do RecurringTrackedType vs TrackedType here - // if the Widgets are expecting RecurringTrackedType, but we - // need to be using Frequency? Change to only have one abstract - // class and make it nully again? Hmmm... - //if (curr is RecurringTrackedType && curr.frequency != null) { + if (curr.frequency != null) { itemAmount = - "${curr.amount.toStringAsFixed(2)} ${curr.frequency.title}"; - /*} else { + "${curr.amount.toStringAsFixed(2)} ${curr.frequency!.title}"; + } else { itemAmount = curr.amount.toStringAsFixed(2); - }*/ + } final String itemDescription = curr.description; - final double itemDayAmount = - curr.calcComparableAmountDaily(); - final String estimateSymbolDaily = curr.frequency.numDays - .toStringAsFixed(2) - .endsWith(".00") && - itemDayAmount.toStringAsFixed(3).endsWith("0") - ? "" - : "~"; + final double itemDayAmount, itemMonthAmount, itemYearAmount; + final String estimateSymbolDaily, + estimateSymbolMonthly, + estimateSymbolYearly; - final double itemMonthAmount = - (curr.calcComparableAmountYearly() / 12); - final String estimateSymbolMonthly = curr - .frequency.timesPerYear - .toStringAsFixed(2) - .endsWith(".00") && - itemMonthAmount.toStringAsFixed(3).endsWith("0") - ? "" - : "~"; + if (curr.frequency != null) { + itemDayAmount = curr.calcComparableAmountDaily(); + estimateSymbolDaily = curr.frequency!.numDays + .toStringAsFixed(2) + .endsWith(".00") && + itemDayAmount.toStringAsFixed(3).endsWith("0") + ? "" + : "~"; - final double itemYearAmount = - curr.calcComparableAmountYearly(); - final String estimateSymbolYearly = curr - .frequency.timesPerYear - .toStringAsFixed(2) - .endsWith(".00") && - itemYearAmount.toStringAsFixed(3).endsWith("0") - ? "" - : "~"; + itemMonthAmount = + (curr.calcComparableAmountYearly() / 12); + estimateSymbolMonthly = curr.frequency!.timesPerYear + .toStringAsFixed(2) + .endsWith(".00") && + itemMonthAmount.toStringAsFixed(3).endsWith("0") + ? "" + : "~"; - final String itemTopText = - "$estimateSymbolDaily${itemDayAmount.toStringAsFixed(2)} ${Frequency.daily.title}"; - final String itemMiddleText = - "$estimateSymbolMonthly${itemMonthAmount.toStringAsFixed(2)} ${Frequency.monthly.title}"; - final String itemBottomText = - "$estimateSymbolYearly${itemYearAmount.toStringAsFixed(2)} ${Frequency.yearly.title}"; + itemYearAmount = curr.calcComparableAmountYearly(); + estimateSymbolYearly = curr.frequency!.timesPerYear + .toStringAsFixed(2) + .endsWith(".00") && + itemYearAmount.toStringAsFixed(3).endsWith("0") + ? "" + : "~"; + } else { + itemDayAmount = -1; + estimateSymbolDaily = ""; + itemMonthAmount = curr.amount; + estimateSymbolMonthly = ""; + itemYearAmount = -1; + estimateSymbolYearly = ""; + } + final String itemTopText = itemDayAmount < 0 + ? "" + : "$estimateSymbolDaily${itemDayAmount.toStringAsFixed(2)} ${Frequency.daily.title}"; + final String itemMiddleText = itemMonthAmount < 0 + ? "" + : "$estimateSymbolMonthly${itemMonthAmount.toStringAsFixed(2)} ${Frequency.monthly.title}"; + final String itemBottomText = itemYearAmount < 0 + ? "" + : "$estimateSymbolYearly${itemYearAmount.toStringAsFixed(2)} ${Frequency.yearly.title}"; return Padding( padding: const EdgeInsets.all(4.0), @@ -138,7 +146,7 @@ class _ExpensePageState extends State { showDialog( context: context, builder: (_) => AlertDialog( - content: RecurringTrackedTypeInputDialog( + content: TrackedItemInputDialog( notifyParent: refresh, entry: curr, amountText: curr.getAmountText(), @@ -218,12 +226,12 @@ class _ExpensePageState extends State { } } -class RecurringTrackedTypeInputDialog extends StatefulWidget { +class TrackedItemInputDialog extends StatefulWidget { final Function() notifyParent; - final RecurringTrackedType? entry; + final TrackedItem? entry; final String? amountText; - const RecurringTrackedTypeInputDialog({ + const TrackedItemInputDialog({ super.key, required this.notifyParent, this.entry, @@ -231,12 +239,12 @@ class RecurringTrackedTypeInputDialog extends StatefulWidget { }); @override - State createState() => - _RecurringTrackedTypeInputDialogState(); + State createState() => + _TrackedItemInputDialogState(); } -class _RecurringTrackedTypeInputDialogState - extends State { +class _TrackedItemInputDialogState + extends State { final _expenseFormKey = GlobalKey(); int? _id; @@ -251,12 +259,12 @@ class _RecurringTrackedTypeInputDialogState _id = widget.entry!.id; _name = widget.entry!.name; _amount = widget.entry!.amount; - _freq = widget.entry!.frequency; + widget.entry!.frequency == null ? null : _freq = widget.entry!.frequency!; _desc = widget.entry!.description; } String amountText = - widget.amountText != null ? widget.amountText! : TrackedType.amountText; + widget.amountText != null ? widget.amountText! : TrackedItem.amountText; return Column( // prevent AlertDialog from taking full vertical height. diff --git a/lib/pages/home.dart b/lib/pages/home.dart index b01b538..06cbba8 100644 --- a/lib/pages/home.dart +++ b/lib/pages/home.dart @@ -33,7 +33,7 @@ class _HomePageState extends State { switch (pageSelected) { case 0: page = ExpensePage(); - dialog = RecurringTrackedTypeInputDialog( + dialog = TrackedItemInputDialog( notifyParent: refresh, ); case 1: diff --git a/lib/pages/report.dart b/lib/pages/report.dart index 9c61daa..6a07f49 100644 --- a/lib/pages/report.dart +++ b/lib/pages/report.dart @@ -2,8 +2,8 @@ import 'package:flutter/material.dart'; // Local -import 'package:flutter_expense_tracker/db.dart'; -import 'package:flutter_expense_tracker/models/tracked_type_recurring.dart'; +import '/db.dart'; +import '/models/tracked_item.dart'; /// TODO: /// - Expenses (total number, totals by day / month / year) @@ -57,22 +57,22 @@ class SummaryCardForTotals extends StatelessWidget { required this.summaryTypeLabel, }); - final Future> list; + final Future> list; final String summaryTypeLabel; @override Widget build(BuildContext context) { - return FutureBuilder>( + return FutureBuilder>( future: list, builder: ( BuildContext context, - AsyncSnapshot> snapshot, + AsyncSnapshot> snapshot, ) { if (!snapshot.hasData) { return Text('Loading $summaryTypeLabel Section...'); } double dailyTotal = 0, monthlyTotal = 0, yearlyTotal = 0; - for (RecurringTrackedType e in snapshot.data!) { + for (TrackedItem e in snapshot.data!) { dailyTotal += e.calcComparableAmountDaily(); monthlyTotal += e.calcComparableAmountYearly() / 12; yearlyTotal += e.calcComparableAmountYearly();