PROJECT: Budget Buddy
This portfolio provides a detailed report of my contributions to Budget Buddy.
Overview
Students taking part in this project were tasked to either enhance or morph a simple CLI-based address book application. We decided to create a finances tracker, Budget Buddy, for NUS Computing Students, to both address the need by students to manage their finances, as well as customise the application’s features to suit our users as much as possible, leveraging on their familiarity with typing and using a CLI.
Budget Buddy is a finances tracker designed around the needs and habits of NUS School of Computing students. Interaction is mainly through a Command Line Interface (CLI), and results are displayed via a Graphical User Interface (GUI) powered by JavaFX.
Budget Buddy can keep track of transactions across separate accounts, so that users can clearly and conveniently organise their spending. The application can also keep track of loans that users have made, addressing the habit of our users often borrowing money from one another, but having trouble remembering exactly who owes money to them, and how much they owe.
Going above and beyond the basic functions that many other trackers have, Budget Buddy also has the ability to run Scripts and Rules that users have created. These scripts and rules can then carry out custom actions as defined by the users, who should have some experience with writing code. By capitalising on their programming know-how to create a truly personalised experience, Budget Buddy distinguishes itself from other finance trackers out there.
Project Role
My role was mainly to implement Transactions within Budget Buddy. Transactions formed the core of Budget Buddy’s finances tracking, and I implemented the functionality to add, edit and delete Transactions from the finances tracker.
The following sections will elaborate further on my contributions regarding this feature, as well as any other contributions that I have made.
Summary of contributions
The following section summarizes my contributions to Budget Buddy. |
Major enhancement
Added functionality to add, edit and remove transactions, as well as to list, sort and filter them.
-
Function: Transactions provide the ability for users to record their day-to-day spending. The ability to delete and edit Transactions allows users to correct mistakes in finances recording, or retroactively update past finances.
-
Purpose: Transactions form the core of the functionality of Budget Buddy. Users will be using it to record their transactions daily, as well as use the sorting and filtering functions to get a clear view of their spending habits and trends.
-
Challenges: Implementing Transactions on their own was a straightforward task, but the difficulties arose with making sure it integrated well with the rest of the functions of Budget Buddy. One example was another enhancement I made to the GUI for showing the active account, and implementing the corresponding logic. The full details are explained below, as a minor enhancement.
Minor enhancement
Make manipulating transactions more intuitive when integrated with accounts, by implementing logic to determine the active account.
-
Function: Because transactions are never manipulated in isolation in Budget Buddy, the Accounts containing them need to be taken care of as well. This feature provided the logic to keep track of the active account, so that transaction commands would not always need to have a specified account, helping Budget Buddy feel more user-friendly and intuitive.
-
Purpose: Transactions are the core feature of Budget Buddy, and hence they should be as user-friendly and intuitive to manipulate as possible.
-
Challenges: Implementing a responsive GUI for Transactions proved to be a challenge, as this was our first time working with JavaFX. It was challenging to think about how to provide a intuitive user experience as well, while maintaining a logical flow to manipulating transactions.
Other contributions
-
GUI Enhancements:
-
Implemented responsive GUI for Active account: Pull request #194
-
-
Bug fixes:
-
Fixed bugs reported regarding Transactions during PE Dry run: Pull request #166
-
-
Documentation:
-
Made contributions to the User Guide/Developer Guide as shown below
-
-
Community:
-
PRs reviewed with non-trivial comments: #34
-
Contributions to the User Guide
Given below are sections I contributed to the User Guide. They showcase my ability to write documentation targeting end-users. |
Managing transactions: txn
Transactions form the core of Budget Buddy. They can be used to track any purchases you make, or any money that you receive. Transactions can further be filtered by various criteria, if you want to find out how much money you’ve spent in a certain time period, or in a certain category of spending.
Add a transaction: txn out|in
This command will add a new transaction of the specified amount and with the given description.
Format: txn dn/out|in x/<amount> d/<description> [a/<account>] [c/<category>] [w/<date>]
Examples:
-
txn dn/out x/3 d/apples a/food c/fruits w/5/4/2019
-
txn dn/in x/50 d/pocket money a/school
Edit a transaction: txn edit
This command will edit the specified transaction, setting the specified fields. At least one field should be provided for editing.
Format: txn edit <id> dn/out|in [x/<amount>] [d/<description>] [a/<account>] [c/<category>] [w/<date>]
Examples:
-
txn edit 1 x/50 d/apple juice c/groceries
-
txn edit 4 a/personal
Delete a transaction: txn delete
This command will delete the transaction with the specified ID in the displayed list of transactions.
Format: txn delete <id>
Examples:
-
txn delete 3
List transaction(s): 'txn list'
This will list the transactions from the currently active account, and you can optionally filter/sort them as well.
Format: txn list [c/category] [f/from date] [u/until date] [af/from amount] [au/until amount] [d/description]
[s/aw|dw|ax|dx|ad|dd]
Examples:
-
txn list c/food u/10/4/2019 af/5 au/10 d/fruits s/ad
Contributions to the Developer Guide
Given below are sections I contributed to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project. |
Transaction Model
Implementation
Transactions are the main elements of the BudgetBuddy’s expenses tracker. Within each Account
, a TransactionList
is
stored, and inside each TransactionList
, Transaction
objects are stored internally in the internalList
of the
TransactionList
.
The class diagram below shows how Transaction
objects are stored within an Account
.
Each Transaction
object must have:
-
direction: Direction
— The direction of the transaction (eitherIN
for money inflow orOUT
for money outflow). -
amount: Amount
— The amount of money transacted. -
date: LocalDate
— The date of the transaction.
Optional attributes for Transaction
objects:
-
description: Description
— A short description about the transaction. -
categories: Set<Category>
— ASet
ofCategory
objects that categorize the transaction.
Because Transaction
and TransactionList
objects are contained within Account
objects, which are in turn stored within the
AccountsManager
, it is called when any operations is made on a Transaction
. Additionally, AccountsManager
has an
activeTransactionList
which holds the TransactionList
of the current active Account
. activeTransactionList
is then
wrapped by a SortedList
and FilteredList
to enable the sorting and filtering of the Transaction
objects.
These methods in AccountsManager
below that manipulate the activeTransactionList
are called when the list of
transactions needs to be filtered/sorted:
-
AccountsManager#getFilteredTransactionList()
— Returns anObservableList
of the filtered transactions. -
AccountsManager#updateFilteredTransactionList(Predicate<Transaction> predicate)
— Updates thePredicate<Transaction>
of theFilteredList
to filter by the givenPredicate<Transaction>
-
AccountsManager#resetFilteredTransactionList()
— Resets and removes any filter on theFilteredList
. -
AccountsManager#updateSortedTransactionList(Comparator<Transaction> comparator)
— Updates theComparator<Transaction>
of theSortedList
to sort by the givenComparator<Transaction>
-
AccountsManager#resetSortedTransactionList()
— Resets theComparator
on theFilteredList
to the default comparator.
When Transaction
objects are added/edited/deleted, the following implemented methods are called from Account
:
-
Account#addTransaction(Transaction toAdd)
— Adds aTransaction
to thetransactionList
. -
Account#updateTransaction(Index txnIndex, Transaction editedTxn)
— Sets aTransaction
to thetransactionList
at the specifiedIndex
. -
Account#deleteTransaction(Transaction toDelete)
— Deletes aTransaction
from thetransactionList
.
Transaction
objects are saved within their respective Account
objects after every command, and they are saved
into the file accounts.json
, stored in the same directory as the JAR executable.
Design Considerations
Aspect: Reference to respective Account in Transaction
-
Alternative 1 (current choice):
Transaction
has no reference toAccount
; any commands that create/edit/deleteTransaction
objects are called via the activeAccount
.-
Pros: Reduced coupling, which makes testing/maintenance easier.
-
Cons: More methods are required to ensure the logic of
Transaction
commands and the current activeAccount
, instead of calling getAccount() on aTransaction
.
-
-
Alternative 2:
Transaction
holds a reference toAccount
; commands that create/edit/deleteTransaction
objects directly reach theAccount
through theTransaction
.-
Pros: It would result in shorter code by avoiding having to go through
AccountsManager
to find the respectiveAccount
. -
Cons: This would result in a circular dependency between
Transaction
andAccount
, as well as high coupling. This lowers testability and increases the risk of bugs.
-
Adding Transactions: txn
Given below is an example usage scenario and how the txn
command is processed and executed at each step.
Step 1: The user (ideally an NUS Computing Student) executes txn dn/out x/50 d/books a/school
to add a purchase of $50 worth of
books, to be added to the school
Account
.
Step 2: LogicManager
calls parseCommand
on the command string txn dn/out x/50 d/books a/school
Step 3: CommandLineParser
creates a new TransactionAddCommandParser
by identifying the txn
String that represents a
TransactionAddCommand
Step 4: TransactionAddCommandParser
parses the rest of the command string dn/out x/50 d/books a/school
and return a
new TransactionAddCommand
with the parsed details.
Step 5: LogicManager
then calls execute()
on the TransactionAddCommand
, which gets the Account
school
from the
AccountsManager
Step 6: addTransaction
is called on the returned Account
to add the Transaction
to the TransactionList
of
the Account`.
Step 7: After adding the Transaction
, RuleEngine is called to execute its rules over the newly added Transaction
.
The full diagram sequence diagram for this step can be found below, in the Implementation of the Rules Execution feature.
Step 8: The Account
which was added to is set as the active Account
with setActiveAccount()
.
Step 9: Finally, TransactionAddCommand
returns a CommandResult
to the LogicManager
.
Step 10: Before LogicManager
returns the CommandResult
, it calls save(Model model)
on the StorageManager
, which
saves all the Account
objects.
The following sequence diagram illustrates the scenario as described above (the execution of txn dn/out x/50 d/books a/school
):
Editing transactions: txn edit
txn edit
allows users to edit any attribute of the Transaction
. It uses a TransactionEditDescriptor
to store the details
of the new Transaction
, then uses getUpdatedTransaction
to generate the edited Transaction
as well as validate that
changes have been made to the transaction.
Below is an example scenario to demonstrate how a Transaction
is edited, as well as certain additional steps that are taken when certain
attributes of the Transaction
are edited, for example to edit the Account
or to edit the categories
. The activity diagram
accompanies this explanation, to help visualise the steps that might take place.
Assume that txn add dn/out x/20 d/bill a/home
was just executed, and that the Transaction
shows up in the first index:
Step 1: The user (ideally an NUS Computing Student) executes txn edit 1 x/28 d/phone bill a/personal c/phone c/bill
to edit the previously added Transaction
to a $28 phone bill, to be added to the personal
Account
.
Step 2: LogicManager
calls parseCommand
on the command string txn edit 1 x/28 d/phone bill a/personal c/phone c/bill
Step 3: CommandLineParser
creates a new TransactionEditCommandParser
by identifying the txn edit
String that represents a
TransactionAddCommand
Step 4: TransactionAddCommandParser
parses the rest of the command string 1 x/28 d/phone bill a/personal c/phone c/bill
and return a
new TransactionEditCommand
along with the TransactionEditDescriptor
and the updatedAccountName
, which represents the
Name
of the new target Account
. A new Set<Categories>
is also created from the c/phone
and c/bill
arguments provided.
Step 5: LogicManager
then calls execute()
on the TransactionEditCommand
. Then, the original Transaction
is obtained,
followed by the target Account
and the edited Transaction
via getUpdatedTransaction
. Meanwhile, validation occurs
at every step, to ensure that the parsed attributes conform to the requirements to edit the `Transaction.
Step 6: deleteTransaction
is called on the Account
containing the Transaction
to be edited.
Step 7: addTransaction
is called on the target Account
, followed by the execution of the RulesEngine, similar to the
situation as shown further below.
Step 8: The Account
which was added to is set as the active Account
with setActiveAccount()
.
Step 9: Finally, TransactionAddCommand
returns a CommandResult
to the LogicManager
.
Step 10: Before LogicManager
returns the CommandResult
, it calls save(Model model)
on the StorageManager
, which
saves all the Account
objects.
Active Account
Implementation
The active Account
allows operations on Transaction
objects to not have to specify the concerning Account
when
the command is entered. This allows for a more user-friendly and intuitive experience with managing finances. By updating
ActiveAccountIndex
where appropriate, the activeAccount
is implemented on the idea that users want to continue to
manipulate transactions within the same Account
that they last interacted with.
The current active Account
is indicated in the UI by a highlight as shown below.
The following user commands, if executed successfully, can change the active account:
-
AccountAddCommand
— The addedAccount
becomes the active account. -
AccountDeleteCommand
— If the activeAccount
is deleted, a new activeAccount
will be selected. -
AccountEditCommand
— The editedAccount
becomes the active account. -
AccountFindCommand
— The firstAccount
in the list is set as the activeAccount
. -
AccountListCommand
— The firstAccount
in the list is set as the activeAccount
. -
AccountReportCommand
— The targetAccount
is set as the new activeAccount
. -
AccountSwitchCommand
— The targetAccount
is set as the new activeAccount
. -
TransactionAddCommand
— The targetAccount
is set as the new activeAccount
. -
TransactionEditCommand
— The targetAccount
is set as the new activeAccount
.
Design Considerations
Aspect: responsive UI for showing the active Account
An Extractor
is used to update the UI when an Account
is set as active, or when the balance
is updated. This is because
the FilteredList<Account>
only updates automatically when the Accounts themselves are changed, but not when their internal
elements are altered.
-
Alternative 1 (current choice): Use an
Extractor
to listen for changes toisActiveBooleanProperty
andbalanceLongProperty
.-
Pros: Less overhead/unnecessary movement of accounts
-
Cons: Not as easy for new developers to understand and implement.
-
-
Alternative 2: Refresh the account list by forcing removal and re-addition of each of the accounts.
-
Pros: Easy to implement, no need to change members to adapt to the
Property
class, and easier for newer developers to read and understand. -
Cons: High overhead, as more and more accounts are added to the BudgetBuddy.
-