WheresMyMoney uses the following libraries
WheresMyMoney uses the following tools for development:
Design and Implementation has been broken down into various sections, each tagged for ease of reference:
A high-level overview of the system is shown in the Architecture Diagram below.

This architecture consist of:
Ui, Main, Parser, and Command classes: These classes stand between the user and the internal processing of the software.Expense, ExpenseList, ExpenseFilter classes: Model expenses that commands can interact with.Storage class: Stores information between sessions.Overview
The Ui class handles I/O operations such as displaying messages and reading user input.
The Parser parses user input and returns the relevant Command Object.
An ArgumentMap object storing the mappings of the arguments to their values is passed in to the constructor of that Command object.
These classes are important for allowing the User to interact with the application.
Methods
The Ui class has the following key methods:
| Method | Description |
|---|---|
displayMessage |
Displays a message (String) |
getUserInput |
Reads User Input |
The Parser class has the following key method:
| Method | Description |
|---|---|
parseInputToCommand |
Parses a given user input and returns the related Command |
The ArgumentMap class extends the HashMap<String, String> class with the following methods
| Method | Description |
|---|---|
isRecur |
Checks if the arguments passed shows that the user wants to access the Recurring Expense List |
getRequired |
Gets a required argument and throws an exception if that argument is not provided. |
getRequiredIndex |
Gets a required index and throws an exception if that index is not provided. |
getPrice |
Gets a price and throws an exception if that price is invalid. |
getRequiredPrice |
Gets a required price and throws an exception if that price is not provided/ invalid. |
Design Considerations
Low-level I/O operations (eg. stdio) are consolidated in the Ui class such that we can easily switch the I/O methods by modifying only the Ui class. This would make it easier to port the application to other platforms if needed.
Ui class is used as part of exception handling for displaying of error messages to the user for feedback.
The Parser also has some considerations such as
/command -> used for the main command keyword/index -> used for the main text argument (the index of the expense to edit/ delete) right after the command keyword/ in the argument values should be escaped
command /argument \/value -> argument: /valuecommand /argument value\/value -> argument:value/valuecommand /argument value/value -> argument:value/value (this is accepted for now, but not recommended)command /argument value\\/value -> argument:value\/value/ doesn’t need to be escaped for
/command /argument value1 -> the command is /command
command /argument/param value -> the argument name is argument/parammain value) are countedAn ArgumentMap class is created as it makes it easier to do argument validation, compared to a regular HashMap<String, String> class.
The abstract Command class has been implemented to introduce an additional layer of abstraction between I/O and command execution, allowing for separation of handling command keywords and executing commands.
Implementation Details
The following diagram is a class diagram for Command and its children classes. This has been heavily simplified and only shows the key commands.

The following diagram is a sequence diagram for execution of Command.

Commands interact with Ui and Parser classes via Main, as illustrated in the following class diagram:

Overview
Storage is mostly handled by the different states themselves (ExpenseList, RecurringExpenseList, CategoryStorage).
This is to keep the storage tightly coupled with the data and ensures that when the data format is updated,
the storage format is updated accordingly, increasing cohesion.
The current implementation abstracted out common Csv functions into the CsvUtils class, but this implementation
also allows more flexible file formats between different classes, instead of relying solely on a certain format.
This might help for future expandability.
However, we also do have a Storage class which handles how these file handling methods interact with one another. This is to consolidate the overall file loading and saving logic in the program. This is useful for certain cases, such as standardising default file paths It would be modified when there is a change of interaction between the various loading/ saving methods of the classes.
The LoadCommand and SaveCommand would reference the Storage class, so ideally they would not need to be changed much for feature changes.
Implementation Details
Here is an illustration of the related classes involved.

Design Considerations
OpenCSV is used for the storage format to allow for reliable handling of CSV files. The CSV file format was used to allow expenses to be easily exported/ imported to and from other programs (like Excel).
Storage is also meant to be an intentional act by the user. This means functionality such as autosave was not implemented due to the fact that it is not an intentional act.
Overview
The Expense class represents an individual expense with a price, description, category and the date added.
The ExpenseList class manages a collection of Expense objects.
It allows for the addition, editing and deletion of expenses.
Methods
The Expense class has no notable methods.
The ExpenseList class has the following key methods:
| Method | Description |
|---|---|
addExpense |
Adds an expense to the list |
deleteExpense |
Removes an expense from the list |
editExpense |
Edits an expense in the list |
There are 2 versions of addExpense: one with a date and one without a date.
Design Considerations
The setters in Expense class checks for null and blank.
The Expense constructors also do, as they use those setters.
The ExpenseList class contains exception handling when attempting to edit or delete an expense that is not in the list.
Implementation Details
UML class diagram to show the dependency between Command and ExpenseList classes:

The following diagram is a UML class diagram for Expense and ExpenseList:

The ExpenseFilter class provides utility methods for selecting expenses based on their category and time range.
Its interaction with ExpenseList is demonstrated in the following UML Class Diagram:

ExpenseFilter is a prerequisite to implementing other features, e.g. Expense statistics and visualization.
Implementation Details
ExpenseFilter filters expenses by 3 criteria: Category, From (date), To (date).
Each criterion is taken care of by a helper method.
Since these filter fields are optional, if they are null, helper methods will evaluate to true.
Given a list of expenses, ExpenseFilter iterates through each expense, applying three criteria checks on them; and
it would add the expense to a new ArrayList if all three checks are satisfied.
The ArrayList is then returned to the caller.
Overview
The DateUtils class provides utility methods to handle date formatting, validation and conversion.
Methods
The DateUtils class has the following key methods:
| Method | Description |
|---|---|
isInDateFormat |
checks if a given string is in the correct date format |
getCurrentDate |
gets the current date in LocalDate format |
stringToDate |
converts from given string to a LocalDate object |
dateFormatToString |
converts from given LocalDate object to a string |
The date format that DateUtils uses, and thus the WheresMyMoney program uses, is dd-MM-yyyy. This ensures consistency in date formatting throughout the program.
Implementation Details
Most methods are essentially wrappers around the existing java.time API methods, but customised to fit this program’s needs.
isInDateFormat is a wrapper for java.time’s parse method, but returns a boolean instead.getCurrentDate is a wrapper for java.time’s now method.stringToDate is a wrapper for java.time’s parse method.dateFormatToString is a wrapper for java.time’s format method.Design Considerations
The DateUtils class’ attributes and methods are all class-level, because:
The VisualizeCommand, similar to the list command, takes in category and from/to dates.
It uses ExpenseFilter to generate an ArrayList<Expense> of matched expenses and passes it to Visualizer.
The Visualizer class, upon receiving expenses, performs the following steps:
beginDate and endDate (the earliest and latest dateAdded among all expenses).getTimeRange() - Calculate dateRange - the difference (in days) between beginDate and endDate plus one.dateRange is within a month (no more than 32 days):
createDateList() - Generate a List<String> timeSeries of dates, spanning from beginDate to endDate.groupPriceByDay() - Create a Hashmap<String, Float> dateToExpenseMap.
The keys are elements of timeSeries, and values are the total expenses in the corresponding day.dateRange is more than a month, perform similar operations where each element of timeSeries is a whole month.drawChart() - Pass data to the CategoryChart object, customize and display the chart.Data is passed to the XChart library in the form of two series - a timeSeries and a valueSeries.
Overview
The RecurringExpense class extends from the Expense class and it represents an indivual recurring expense with a price, description, category, last date added and a frequency.
The RecurringExpenseList class extends from the ExpenseList class and it manages a collection of RecurringExpense objects.
It allows for addition, editing and deletion of expenses.
Methods
The RecurringExpense class has no notable methods.
The RecurringExpenseList class has the following key methods:
| Method | Description |
|---|---|
addRecurringExpense |
Adds a recurring expense to the list |
deleteRecurringExpense |
Removes a recurring expense from the list |
editRecurringExpense |
Edits a recurring expense in the list |
loadFromCsv |
Adds the appropriate amount of Expense objects with the correct date to the ExpenseList |
Design Considerations
Since the programme does not have an auto-save function upon closing the programme or auto-load when starting the programme, it is up to the user to save their work and to load it again.
Adding a recurring expense will only add a singular normal expense for that specified date (or current date if a date was not specified). All other valid expenses will by added after a save and a load command is used.
save command is needed to register the recurring expense into the system.load command is used to trigger the mechanism to add all other valid expenses according to the date specified.Editing a recurring expense will not edit the normal expenses that are asscociated with the recurring expense. You will need to edit the normal expenses yourself.
Deleting a recurring expense will not delete the normal expenses that are associated with the recurring expense. You will need to delete the normal expenses yourself.
Implementation Details
Below is the UML class diagram for RecurringExpense and RecurringExpenseList:

Below is the sequence diagram for when the user calls the load command.

Overview
The CategoryFacade class serves as an interface that simplifies the interaction with various category-related classes (CategoryTracker and CategoryFilter), providing a unified API for the rest of the application (namely the Command classes).
The CategoryTracker class manages a collection of Category-CategoryData pairs. It allows for the addition, editing and deletion of category-related information.
The CategoryData class contains category-related information, namely the cumulative expenditure (the sum of all prices of expenses with that category) and the spending limits for that category.
The CategoryFilter class is responsible for filtering categories based on various criteria.
Methods
The CategoryFacade class has key methods for:
| Method | Description |
|---|---|
addCategory |
The interface for AddCommand when the user adds a new Expense |
deleteCategory |
The interface for DeleteCommand when the user deletes an Expense |
editCategory |
The interface for EditCommand when the user edits an Expense |
loadCategoryInfo |
The interface for LoadCommand to load category information from a CSV file |
displayFilteredCategories |
The interface for LoadCommand to show filtered categories based on spending limits |
saveCategoryInfo |
The interface for SaveCommand to save current category information to a CSV file |
setCategorySpendingLimit |
The interface for SetCommand to set a spending limit for a specified category |
The CategoryTracker class has the following key methods:
| Method | Description |
|---|---|
checkLimitOf |
Prints a message to output if total expenditure is nearing or has exceeded the spending limit |
addCategory |
Adds a new category to the tracker. If already in the tracker, then the total expenditure for that category is increased instead |
deleteCategory |
Decreases total expenditure of a category. If that total drops to zero or below, the category is removed from the tracker |
editCategory |
Updates the old and new category’s total expenditure when an Expense’s category is changed |
setSpendingLimitFor |
Sets a spending limit for a particular category |
The CategoryData class has these key methods:
| Method | Description |
|---|---|
increaseCurrExpenditureBy |
Increments current total by a given price |
decreaseCurrExpenditureBy |
Decrements current total by a given price |
isNearingLimit |
Checks if current total is 80% of limit or more |
hasExceededLimit |
Checks if current total is more than limit |
The CategoryFilter class has key methods for:
| Method | Description |
|---|---|
initMaxHeap |
Initialises a custom max heap that sorts categories by their current total expenditure |
getCategoriesFiltered |
Sorts categories in the tracker, which are nearing or have exceeded the designated spending limit, into max-heaps |
displayFilteredCategories |
Displays the categories in the provided category-filtered max-heap, in a preset format |
displayExceededCategories |
Displays the categories that have exceeded its spending limits |
displayNearingCategories |
Displays the categories that are nearing, but not exceeded, its spending limits |
After the user adds or edits an Expense, it alerts the user if the spending limit is approached or exceeded for that Expenses’s category.
The CategoryStorage class has key methods for:
| Method | Description |
|---|---|
trackCategoriesOf |
Creates a category tracker based on an expense list |
loadFromCsv |
Loads spending limits from a CSV file, only for |
saveToCsv |
Saves all categories and their corresponding spending limits to a CSV file |
After the user loads from file, all categories that have exceeded its designated spending limit will be displayed to the user, followed by all categories that have not exceeded its designated spending limit but are close to it.
Design Considerations
Each of the classes in this package handle a separate concern relating to category management (achieving Separation of Concerns Principle) and each focuses on a specific responsibility (achieving Single Responsibility Principle).
CategoryFacade class
CategoryFilter accesses the tracker in CategoryTracker via the instance attribute in CategoryFacade.CategoryStorage accesses the tracker in CategoryTracker via the instance attribute in CategoryFacade.CategoryTracker class
CategoryData objects, providing quick access and updating of total expenses.CategoryData class
CategoryFilter class
Implementation Details
The following diagram is a UML class diagram for CategoryData, CategoryTracker, CategoryFilter and CategoryFacade:

Overview
The program implements Exception handling and Logging with the WheresMyMoneyException and Logging classes.
Implementation Details
WheresMyMoneyException has various children classes, such as StorageException and InvalidInputException.
These children classes are meant to provide more information on the error to the developer (beyond the message) such
that exception handling in the program could be better targeted in the future.
The Logging class is implemented as a Singleton for ease of use.
Developers can log down certain actions in the program by simply calling the relevant class method log(Level, String).
Our target user profile is frugal and tech-savvy university students.
The application will track how much a user is spending and what they are spending it on. The application can provide summaries and statistical insights to spending habits, optimised for people who prefer a CLI.
| Version | As a … | I want to … | So that I can … |
|---|---|---|---|
| v1.0 | user | add expenses | track how much money I have spent so far |
| v1.0 | user | delete expenses | clear wrong expenses to ensure expense tracking is accurate |
| v1.0 | user | edit expenses | correct inaccurate expenses to ensure expense tracking is accurate |
| v1.0 | user | list expenses | track my spending |
| v1.0 | new user | see usage instructions | refer to them when I forget how to use the application |
| v2.0 | user | save and load my expenses from a file | retain memory of past expenses from past runs of the program |
| v2.0 | frugal user | set spending limits for each category | control my spending |
| v2.0 | frugal user | be alerted when I exceed spending limits for each category and month | control my spending |
| v2.0 | user | visualise my spending in the form of graphs | better conceptualise and understand spending trends and patterns |
| v2.0 | user | detailed statistical information about my spending (mean, highest, etc.) | better quantify and understand spending trends and patterns |
| v2.0 | user | add recurring expenses | automate expense tracking and make it more convenient |
View the User Guide for the full list of commands and their related use case and expected outcomes.
Recurring expenses can be tested by setting its date. Below is an example.
add /recur /price 1.00 /category A /description A /date 01-01-2024 /frequency daily
add /recur /price 10.00 /category B /description B /date 01-01-2024 /frequency weekly
add /recur /price 100.00 /category C /description C /date 31-01-2024 /frequency monthly
save
load
JUnit tests are written in the subdirectory test and serve to test key methods part of the application.