diff --git a/lib/models/expense.dart b/lib/models/expense.dart index 1cec144..4143fc6 100644 --- a/lib/models/expense.dart +++ b/lib/models/expense.dart @@ -1,51 +1,24 @@ +import '/models/recurring_tracked_type.dart'; import '/models/frequency.dart'; -class Expense { - final int? id; - final String name; - final double cost; - final Frequency frequency; - final String description; +class Expense extends RecurringTrackedType { + static String amountText = "Cost"; - const Expense({ - this.id, - required this.name, - required this.cost, - required this.frequency, - required this.description, + Expense({ + super.id, + required super.name, + required super.amount, + required super.frequency, + required super.description, }); - @override - String toString() { - //return "$name, $cost, ${frequency.title}, $description"; - return toMap().toString(); - } - - double calcComparableCost() { - return cost * frequency.timesPerYear; - } - - double calcComparableCostDaily() { - return cost / frequency.numDays; - } - factory Expense.fromMap(Map json) => Expense( id: json['id'], name: json['name'], - cost: json['cost'], + amount: json['cost'], frequency: Frequency.values - .where((expense) => expense.title == json['frequency']) + .where((freq) => freq.title == json['frequency']) .first, description: json['description'], ); - - Map toMap() { - return { - 'id': id, - 'name': name, - 'cost': cost, - 'frequency': frequency.title, - 'description': description, - }; - } } diff --git a/lib/models/recurring_tracked_type.dart b/lib/models/recurring_tracked_type.dart new file mode 100644 index 0000000..3eabfde --- /dev/null +++ b/lib/models/recurring_tracked_type.dart @@ -0,0 +1,34 @@ +// 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/models/tracked_type.dart b/lib/models/tracked_type.dart new file mode 100644 index 0000000..03dfecd --- /dev/null +++ b/lib/models/tracked_type.dart @@ -0,0 +1,27 @@ +abstract class TrackedType { + int? id; + String name; + double amount; + String description; + + TrackedType({ + this.id, + required this.name, + required this.amount, + required this.description, + }); + + @override + String toString() { + return toMap().toString(); + } + + Map toMap() { + return { + 'id': id, + 'name': name, + 'amount': amount, + 'description': description, + }; + } +} diff --git a/lib/pages/expense.dart b/lib/pages/expense.dart index 4befcc3..736a9bb 100644 --- a/lib/pages/expense.dart +++ b/lib/pages/expense.dart @@ -6,7 +6,7 @@ import 'package:flutter_expense_tracker/db.dart'; import '/models/expense.dart'; import '/models/frequency.dart'; -// TODO: Make this a generic class based on a suerclass of Expense, Income, and Assets? +// TODO: Make this a generic class based on a superclass of Expense, Income, and Assets? class ExpensePage extends StatefulWidget { const ExpensePage({ @@ -30,10 +30,12 @@ class _ExpensePageState extends State { future: DatabaseHelper.instance.getExpenses(), builder: (BuildContext context, AsyncSnapshot> snapshot) { if (!snapshot.hasData) { - return Center(child: Text('Loading...')); + return Text('Loading...'); } snapshot.data!.sort( - (a, b) => (b.calcComparableCost() - a.calcComparableCost()).toInt(), + (a, b) => (b.calcComparableAmountYearly() - + a.calcComparableAmountYearly()) + .toInt(), ); return snapshot.data!.isEmpty ? Text( @@ -50,7 +52,7 @@ class _ExpensePageState extends State { .toStringAsFixed(2) .endsWith(".00") && curr - .calcComparableCost() + .calcComparableAmountYearly() .toStringAsFixed(3) .endsWith("0") ? "" @@ -59,7 +61,7 @@ class _ExpensePageState extends State { .toStringAsFixed(2) .endsWith(".00") && curr - .calcComparableCostDaily() + .calcComparableAmountDaily() .toStringAsFixed(3) .endsWith("0") ? "" @@ -133,7 +135,7 @@ class _ExpensePageState extends State { style: TextStyle(fontSize: 20.0), ), Text( - "${curr.cost.toStringAsFixed(2)} ${curr.frequency.title}", + "${curr.amount.toStringAsFixed(2)} ${curr.frequency.title}", style: TextStyle(fontSize: 12.0), ), ], @@ -155,12 +157,12 @@ class _ExpensePageState extends State { children: [ //if (curr.frequency != Frequency.daily) Text( - "$estimateSymbolDaily${curr.calcComparableCostDaily().toStringAsFixed(2)} ${Frequency.daily.title}", + "$estimateSymbolDaily${curr.calcComparableAmountDaily().toStringAsFixed(2)} ${Frequency.daily.title}", style: TextStyle(fontSize: 12.0), ), //if (curr.frequency != Frequency.yearly) Text( - "$estimateSymbolYearly${curr.calcComparableCost().toStringAsFixed(2)} ${Frequency.yearly.title}", + "$estimateSymbolYearly${curr.calcComparableAmountYearly().toStringAsFixed(2)} ${Frequency.yearly.title}", style: TextStyle(fontSize: 12.0), ), ], @@ -196,7 +198,7 @@ class _ExpenseInputDialogState extends State { int? _id; String _name = ""; - double _cost = 0; + double _amount = 0; Frequency _freq = Frequency.monthly; String _desc = ""; @@ -205,7 +207,7 @@ class _ExpenseInputDialogState extends State { if (widget.expense != null) { _id = widget.expense!.id; _name = widget.expense!.name; - _cost = widget.expense!.cost; + _amount = widget.expense!.amount; _freq = widget.expense!.frequency; _desc = widget.expense!.description; } @@ -277,29 +279,29 @@ class _ExpenseInputDialogState extends State { keyboardType: TextInputType.numberWithOptions(decimal: true), decoration: InputDecoration( - labelText: "Cost", + labelText: "${Expense.amountText}", hintText: "Example: 10.00", hintStyle: TextStyle(fontSize: 10.0), errorStyle: TextStyle(fontSize: 10.0), ), - initialValue: _cost != 0 ? _cost.toString() : "", + initialValue: _amount != 0 ? _amount.toString() : "", validator: (value) { if (value == null || value.isEmpty) { - return "Cost must be provided."; + return "${Expense.amountText} must be provided."; } if (double.parse(value) < 0) { return "Please use the Income page rather than having negative expenses."; } if (double.parse(value) < 0.01) { - return "Cost must be one hundreth (0.01) or higher."; + return "${Expense.amountText} must be one hundreth (0.01) or higher."; } if (double.tryParse(value) == null) { - return "Cost must be a valid number."; + return "${Expense.amountText} must be a valid number."; } return null; }, onSaved: (value) { - _cost = double.parse(value!); + _amount = double.parse(value!); }, ), DropdownButtonFormField( @@ -374,7 +376,7 @@ class _ExpenseInputDialogState extends State { Expense expense = Expense( id: _id, name: _name, - cost: _cost, + amount: _amount, frequency: _freq, description: _desc, );