By: Team T16-04 Since: Aug 2019 Licence: MIT

1. Setting up

Refer to the guide here.

2. Design

2.1. Architecture

ArchitectureDiagram
Figure 1. Architecture Diagram

The Architecture Diagram given above explains the high-level design of the App. Given below is a quick overview of each component.

Main has two classes called Main and MainApp. It is responsible for,

  • At app launch: Initializes the components in the correct sequence, and connects them up with each other.

  • At shut down: Shuts down the components and invokes cleanup method where necessary.

Commons represents a collection of classes used by multiple other components. The following class plays an important role at the architecture level:

  • LogsCenter : Used by many classes to write log messages to the App’s log file.

The rest of the App consists of four components.

  • UI: The UI of the App.

  • Logic: The command executor.

  • Model: Holds the data of the App in-memory.

  • Storage: Reads data from, and writes data to, the hard disk.

  • Statistics: Holds the statistics calculations of App.

Each of the four components

  • Defines its API in an interface with the same name as the Component.

  • Exposes its functionality using a {Component Name}Manager class.

For example, the Logic component (see the class diagram given below) defines it’s API in the Logic.java interface and exposes its functionality using the LogicManager.java class.

LogicClassDiagram
Figure 2. Class Diagram of the Logic Component

How the architecture components interact with each other

The Sequence Diagram below shows how the components interact with each other for the scenario where the user issues the command delete 1.

ArchitectureSequenceDiagram
Figure 3. Component interactions for delete 1 command

The sections below give more details of each component.

2.2. UI component

UiClassDiagram
Figure 4. Structure of UI component

API : Ui.java

  • The UI consists of a MainWindow that is made up of parts e.g. CommandBox, ResultDisplay, ExpenseListPanel, StatusBarFooter etc. All these, including the MainWindow, inherit from the abstract UiPart class.

  • The UI component uses JavaFx UI framework. The layout of these UI parts are defined in matching .fxml files that are in the src/main/resources/view folder. For example, the layout of the MainWindow is specified in MainWindow.fxml

The UI component

  • Executes user commands using the Logic component.

  • Listens for changes to Model data so that the UI can be updated with the modified data.

2.3. Logic component

LogicClassDiagram
Figure 5. Structure of the Logic Component

API : Logic.java

  1. Logic uses the guiltTripParser class to parse the user command.

  2. This results in a Command object which is executed by the LogicManager.

  3. The command execution can affect the Model (e.g. adding a person).

  4. The result of the command execution is encapsulated as a CommandResult object which is passed back to the Ui.

  5. In addition, the CommandResult object can also instruct the Ui to perform certain actions, such as displaying help to the user.

Given below is the Sequence Diagram for interactions within the Logic component for the execute("delete 1") API call.

DeleteSequenceDiagram
Figure 6. Interactions Inside the Logic Component for the delete 1 Command

2.4. Model component

ModelClassDiagram
Figure 7. Structure of the Model Component

API : Model.java

The Model

  • stores a UserPref object that represents the user’s preferences.

  • stores the Guilt Trip data.

  • exposes an unmodifiable ObservableList<Entry> that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change.

  • does not depend on any of the other three components.

2.5. Storage component

StorageClassDiagram
Figure 8. Structure of the Storage Component

API : Storage.java

The Storage

  • can save UserPref objects in json format and read it back.

  • can save the GuiltTrip data in json format and read it back.

  • This includes instances of Entry subclasses (Expense, Income, Wish, AutoExpenses, Budget, etc.)

3. Implementation

This section describes some details on how certain features are implemented.

3.1. Data Presentation: Categories

3.1.1. Implementation

CategoryListClassDiagram
Figure 9. Class Diagram for Categories

For all entries in guiltTrip, the entries should always belong to one category. Creation of categories are also helpful for breakdown of statistics to be complete. The implementation of Category and CategoryList are largely similar to UniqueEntryList in the original AddressBook. However, the slight difference lies in that Category is a field of Entry instead of being a child of Entry itself. A Category can only belong under Expense or Income, which is defined by the Enum CategoryType.EXPENSE or CategoryType.INCOME. When the user launches the application for the first time or if there are errors with data/guiltTrip.json, the application will load the default set of Category by SampleDataUtil#getSampleCategories().

There are 3 main checks to be carried out when interacting with Category.

  • When adding a Category, there is a need to check that the new Category added does not exist in the existing guiltTrip, hence the need for CategoryList#contains(Category).

  • When editing a Category, there is a need to check that the new Category added does not exist in the existing guiltTrip, hence the need for CategoryList#contains(Category) as well as a need to check if there are existing entries of the original Category to carry out modifications on them.

  • When deleting a Category, there is a need to check if there are any entries that have the Category as a field.

Given below is an example of an activity diagram for editing a category to illustrate the point above.

EditingCategory
Figure 10. Activity Diagram for Editing Category

As the rest of the implementation is similar to AB3’s CRUD, it won’t be covered.

3.1.2. Design Considerations

Aspect: Deciding whether to allow addition of Categories

  • Alternative 1: Having a fixed set of Categories in the CategoryList, users are unable to add delete or edit the existing set of Categories.

    • Pros: Easy to implement.

    • Cons: Results in less flexibility for the user.

  • Alternative 2: Users are allowed to have any category names for their entries. There is no CategoryList to carry out validation checks on.

    • Pros: Intuitive and convenient for the user.

    • Cons: Calculation of Statistics would be messy if the user adds many different categories for their entries on a whim, the breakdown of statistics by category could be huge.

  • Alternative 3(Current): There is a fixed set of categories, with users able to add delete or edit the existing set of Categories, but a command must be consciously called by the user to modify the categories in the CategoryList.

    • Pros: Allows the flexibility for addition of additional categories as well as solving the many different categories problem if alternative 2 was taken as users have to make the conscious effort to create a new category.

    • Cons: Slightly more complicated to implement.

3.2. Data Presentation: Sort Command

3.2.1. Implementation

The sort command extends the Command class. It works on the ObservableList by wrapping the ObservableList on a SortedList and adding a EntryComparator to the List. By default, the Entry in GuiltTrip are sorted by Date, followed by Amount, Description, Category, and finally Tags. In addition, after every CRUD command, the list is sorted by default for the user’s convenience.

A Sort Command contains:

  • SortType : Date, Amount, Description, Category, and Tags.

  • SortSequence: Ascending, Descending

An Example of Sorting the Expense List is shown below

  1. The user executes the command sortExpense typ/Amount s/ascending

  2. Logic uses the guiltTripParser class to parse the user command

  3. This results in a SortExpenseCommand object which is executed by the LogicManager

  4. The SortExpenseCommand calls the Model#sortFilteredExpenseList to sort the list of expenses

  5. The result of the command execution is encapsulated as a CommandResult object which is passed back to the Ui

  6. Logic returns the CommandResult object

The Model#sortFilteredExpenseList creates an EntryComparator which takes in SortType and SortSequence to sort the list.

Given below is the Sequence Diagram for interactions within the Logic component for the execute("sortExpense typ/Amount s/ascending") API call.

SortSequence
Figure 11. Interactions Inside the Logic Component for the sortExpense typ/Amount s/ascending Command

3.3. Data Presentation: Find Command

Finding is similar to the implementation of AB3, hence it will not be covered in detail. However, the find command is expanded to include finding by multiple predicates at once. For Example, the user can find by both Amount and Description. These are the relevant predicates:

  • EntryContainsAmountPredicate: Will filter the list to include those with equal or higher amounts than the amount specified.

  • EntryContainsCategoryPredicate: Will filter the list to include the category specified.

  • EntryContainsDatePredicate: Will filter the list to include the Date specified. Currently only supports searching within the month.

  • EntryContainsTagsPredicate: Will filter the list to include those with all the specified tags.

  • EntryContainsDescriptionPredicate: Will filter the list to include only those with descriptions that contain the keywords.

3.4. Data Presentation: Statistics

StatisticsClassDiagram
Figure 12. Class Diagram for Statistics Component.

The Statistics class diagram is shown above. Many of the operations are handled by StatisticsManager. The two main operations for calculation of Statistics are:

  • StatisticsManager#updateListOfStats(rangeOfDates): Calculates the statistics for categories according to the range of dates specified. Calls on MonthList#updateListOfStats(Category) to calculate the list of Stats across Categories in that MonthList, thus updating the list of Category Statistics.

  • StatisticsManager#updateBarChart(monthToCalculate): Calculates the daily statistics according to the month specified. Calls on MonthList#CalculateStatisticsForBarChart() which will call on DailyList#CalculateStatisticsForBarChart() to update the list of DailyStatistics.

3.4.1. Implementation: (Statistics) - ViewPieChart/ViewTable Command

The ViewPie and ViewTable commands are a unique case as they both depend on CategoryStatistics. StatisticsManager has two ObservableList of CategoryStatistics, one for Expense, listOfStatsForExpense and one for Income, listOfStatsForIncome. The StatisticsPieChart and StatisticsTable in guiltTrip listens to these two ObservableList, and will update accordingly. Hence all operations which involve calculation of category statistics needs to update this ObservableList by replacing its entries so as to update the relevant Pie Chart and Table in the Ui.

ViewPieActivityDiagram
Figure 13. Activity Diagram when user wants to view the statistics in Pie Chart form.

The overview of this process can be found in the Activity Diagram above.

The details of the process is as below:

  1. The user executes the command viewPie p/2019-09,2019-11

  2. LogicManager uses the guiltTripParser class to parse the user command.

  3. This results in a viewPieChartCommand object which is executed by the LogicManager.

  4. The viewPieChartCommand calls the Model#updateListOfStats(RangeOfDates) 's method which then calls StatisticsManager#updateListOfStats(RangeOfDates) method to calculate the statistics for that type.

  5. StatisticsManager#updateListOfStats(RangeOfDates) detects that the size of the list is 2 and calls #getListOfMonths(RangeOfDates) to retrieve the list of MonthList MonthListToCalculate from start Date to End Date from yearlyRecord, the ObservableMap inside StatisticsManager.

  6. StatisticsManager#updateListOfStats(RangeOfDates) then calls StatisticsManager#countStats(MonthListToCalculate, listOfStatistics), which will calculate the list of statistics for expense and income categories and create many new CategoryStatistics objects to save the data of the calculated Statistics for each Category.

  7. StatisticsManager#countStats(MonthListToCalculate, listOfStatistics) will replace the all the CategoryStatistics objects in the ObservableList of CategoryStatistics with the newly calculated CategoryStatistics objects.

  8. As the ObservableList is updated, the PieChart and Table which uses this ObservableList is also updated, leading to them being updated.

  9. Finally, StatisticsManager#countStats(MonthListToCalculate, listOfStatistics) will set the new TotalExpense and TotalIncome values to the new values calculated, which will also update the Ui for Stats which displays the total expense and total income.

  10. The result of the command execution is encapsulated as a CommandResult object which is passed back to the Ui

  11. Logic returns the CommandResult object.

Given below is the Sequence Diagram for interactions within the Logic component for the execute("viewPie p/2019-09,2019-11") API call.

ViewPieChartSequenceDiagram
Figure 14. Interactions Inside the Logic Component for the viewPie p/2019-09,2019-11 Command

3.4.2. Implementation: (Statistics) - ViewBarChartCommand

Similar to ViewPie and ViewTable, the StatisticsBarChart class listens to the ObservableList of DailyStatistics and will update according to changes in it. Hence all operations which involve calculation of daily statistics needs to update this ObservableList by replacing its entries so as to update the relevant Bar Chart in Ui.

The details of the process is as below:

  1. The user executes the command viewBar p/2019-09

  2. LogicManager uses the guiltTripParser class to parse the user command.

  3. This results in a ViewBarChartCommand object which is executed by the LogicManager

  4. The ViewBarChartCommand calls the Model#updateBarChart(MonthToShow) 's method which then calls StatisticsManager#updateBarChart(monthToShow) method to calculate the statistics for that period.

  5. StatisticsManager#updateBarChart(MonthToShow) retrieves the relavant MonthList from ObservableMap, yearlyRecord and calls MonthList#calculateStatisticsForBarChart.

  6. The called MonthList will then loop through all the DailyList in it and calls DailyList#calculateStatisticsForBarChart, retrieving the result and returning it to StatisticsManager.

  7. StatisticsManager#updateBarChart(MonthToShow) will replace the all the DailyStatistics objects in the ObservableList of DailyStatistics with the newly calculated DailyStatistics objects.

  8. As the ObservableList is updated, the BarChart which uses this ObservableList is also updated, leading to them being updated.

  9. The result of the command execution is encapsulated as a CommandResult object which is passed back to the Ui

  10. Logic returns the CommandResult object.

Given below is the Sequence Diagram for interactions within the Logic component for the execute("viewBar p/2019-09") API call.

ViewBarChartSequenceDiagram
Figure 15. Interactions Inside the Logic Component for the viewBar p/2019-09 Command

3.4.3. Design Considerations: Statistics

Aspect: Calculation of Income and Expenses

  • Alternative 1: Set a predicate on the filteredlist of income and filteredlist of expense to filter out the number of income and expenses which are within the time period of the statistics query.

    • Pros: Easy to implement.

    • Cons: May have performance issues in terms of runtime, as if multiple queries are carried out in a row which are the same, recalculation needs to be done every single time.

  • Alternative 2(current choice): Have MonthList and DailyList classes which store the specific filteredlist of expenses for that month. This is a new filteredlist which observes the changes in the original list of expenses and is updated if a new expense is added which corresponds to the month.

    • Pros: Will be faster as initiating the expenses in the MonthList is only done at the start of the application. Any queries after that just refers to the already constructed MonthList. It is also structured as calculations of expenses now involve going to the related MonthList to carry out the calculations.

    • Cons: More complicated to implement.

      There isn’t a need to use YearList as most users will usually want to see their statistics breakdown over a period of a month rather than over a period of years.

Aspect: Updating of charts whenever add delete edit commands is called

  • Alternative 1: Disallow non-stats commands in the stats window.

    • Pros: Easy to implement.

    • Cons: May not be intuitive for the user and creates hassle.

  • Alternative 2(current choice): Adds a Listener to the list of filtered expenses. The listener will update the relevant charts whenever it detects that there is a change in the expenses or incomes.

    • Pros: Intuitive for the user.

    • Cons: Takes a toll on the time complexity if large bulks of data was added through AutoExpense.

3.4.4. Proposed Extension

  • Currently Statistics Breakdown doesn’t show details like trends across months. A future implementation could involve showing the user what category of spending increases across the months. For example, it could reflect that spending for category Entertainment increased the most in the past months.

  • Bar Chart can be further improved to show analysis of breakdown of category by day and observe trends for the user. For example, it could notice that the user has been spending a lot every Tuesday and alert the user about it.

3.5. Toggle Panel Command

3.5.1. Implementation

PartialUiClassDiagramForToggle
Figure 16. Partial class diagram showing only the classes in UI involved in the Toggle Panel Command.

The toggle command extends from the Command class. MainWindow checks using the CommandResult obtained from Logic if the user wants to toggle a specified panel. If so, it toggles the isVisible and isManaged properties of the place holder for that panel.

The following sequence diagram shows how an example usage scenario toggle wishlist would work:

ToggleSequenceDiagram
Figure 17. Interactions inside the Logic and UI components for the toggle wishlist command

The sequence diagram is as explained below:

  1. The user launches the application and executes the toggle wishlist command to toggle the wishlist panel.

  2. commandResult is obtained in MainWindow after the command is parsed and executed.

  3. MainWindow checks if the togglePanel attribute in commandResult is true.

  4. Since it is true, it retrieves the PanelName WISH from commandResult and calls on its own method handleTogglePanel.

  5. This method then calls on another method togglePanel() that toggles the panel and takes in the PanelName WISH as a parameter.

    1. (Not shown in sequence diagram to reduce its complexity) It also checks if the wishlist is already shown in the main panel.

    2. If it is, then a CommandException is thrown to prevent the user from toggling the wishlist side panel when the wishlist is already shown in the main panel.

The following activity diagram summarizes what happens when a user executes a toggle command:

ToggleActivityDiagram
Figure 18. Activity diagram showing what happens when user executes a toggle command

3.5.2. Design Considerations

  • Alternative 1 (current method): Toggle the panels from within MainWindow.

    • Pros: Easy to implement.

    • Cons: Might not be as OOP as other designs.

  • Alternative 2: MainWindow has a PanelManager class that manages all the side panels (toggling them on and off).

    • Pros: More OOP, reduces number of methods and lines of code in MainWindow.

    • Cons: May introduce cyclic dependency between PanelManager and MainWindow.

3.6. Change Font Command

3.6.1. Implementation

The changeFont command extends from the Command class. MainWindow checks using the CommandResult obtained from Logic if the user wants to change the application font. If so, it immediately changes the font without requiring the user to exit and launch the application again.

This change in font is also saved in UserPrefs.

The following sequence diagram shows how an example usage scenario changeFont rockwell would work:

ChangeFontSequenceDiagram
Figure 19. Interactions inside Logic and UI components for the changeFont rockwell command

The sequence diagram is as explained below:

  1. The user launches the application and executes the changeFont rockwell command to change the current application font to rockwell.

  2. commandResult is obtained in MainWindow after the command is parsed and executed.

  3. MainWindow checks if the changeFont attribute in commandResult is true.

  4. Since it is true, it retrieves the FontName ROCKWELL from commandResult and calls on its own method handleChangeFont.

  5. This method then converts the FontName ROCKWELL to a String "rockwell" and sets the font-family attribute of window, that contains all the child nodes, to rockwell.

The following activity diagram summarizes what happens when a user executes a changeFont command:

ChangeFontActivityDiagram
Figure 20. Activity diagram showing what happens when user executes a changeFont command

3.6.2. Design Considerations

  • Alternative 1 (current choice): Change the application font from within MainWindow.

    • Pros: Easy to implement.

    • Cons: May not be as OOP as other methods.

  • Alternative 2: Use a separate class to control the theme, such as ThemeManager.

    • Pros: More OOP, reduces amount of code in MainWindow.

    • Cons: As the implementation is not very complicated, introducing a new class just to change the theme may not be worth the increase in dependency (introduces dependency between Theme and ThemeManager and between ThemeManager and MainWindow).

3.7. Set Light/Dark Theme Command

3.7.1. Implementation

The setLightTheme/setDarkTheme command extends from the Command class. MainWindow checks using the CommandResult obtained from Logic if the user wants to change the theme of the application. If so, it immediately changes the theme without requiring the user to exit and launch the application again.

This change in the application theme is also saved in UserPrefs.

The following sequence diagram shows how an example usage scenario setLightTheme would work:

SetLightThemeSequenceDiagram
Figure 21. Interactions inside Logic and UI components for setLightTheme command

The sequence diagram is as explained:

  1. The user launches the application and executes the setLightTheme command to change the current theme to light.

  2. commandResult is obtained in MainWindow after the command is parsed and executed.

  3. MainWindow checks if the changeTheme attribute in commandResult is true.

  4. Since it is true, it retrieves the newTheme from commandResult, LIGHT, and calls on its own method switchThemeTo(LIGHT).

    1. (Following details were trivial and thus omitted from the diagram) This method retrieves the URLs for the light theme and corresponding extensions css files and adds it to the stylesheets for the scene. This is done after removing the stylesheets for the previous theme.

  5. This implementation is essentially the same for setDarkTheme command, with the newTheme as DARK instead.

The following activity diagram summarizes what happens when a user executes a setLightTheme command:

SetLightThemeActivityDiagram
Figure 22. Activity diagram showing what happens when user executes a setLightTheme command

3.7.2. Design Considerations

  • Alternative 1 (current choice): Change the theme from within MainWindow.

    • Pros: Easy to implement.

    • Cons: May not be as OOP as it could be, increases number of lines of code in MainWindow.

  • Alternative 2: Use a separate class to control the theme, such as ThemeManager.

    • Pros: Abstracts out the methods regarding changing of theme to be contained in ThemeManager and reduces the number of lines of code in MainWindow.

    • Cons: Harder to implement; may introduce cyclic dependency. It may also be redundant or excessive as implementing the changeFont command is not very complicated.

3.8. Quality of Life features

3.8.1. AutoSuggester

Implementation

AutoSuggester is one of the most visible and widely used UX feature in the GuiltTrip application. It grabs users' input from the command box using a listener, and returns suggetions using a CommandSuggester 's suggest() method, displayed in the ResultDisplay panel.

AutoSuggestClassDiagram

Figure: Class Diagram for AutoSuggest.

Terminology: AutoSuggest refers to the feature as a whole. AutoSuggestion is the term users see in GuiltTrip application. CommandSuggester is the functional interface in the source code. GuiltTripCommandSuggester is the class that gives suggestions based on user input.

Currently AutoSuggester carries out two functions:

  1. suggesting full commands when halfway typing any command

  2. displaying help messages when a complete command is detected.

While a user is typing out commands, AutoSuggester will provide recommendation to the nearest valid command. For example, typing out add will suggest addIncome, addExpense, help etc, based on the matching algorithm used. Currently, an edit distance metric based on edit, delete and inserting letters is used, as implemented by the EditDistanceComparator class.

Upon typing out any valid command in full, AutoSuggester fetches the usage of the current command and show it in the ResultDisplay. This usage message is distinguished from error messages by prepending a line prefixed [Autosuggestion] above the usage message displayed.

Design considerations
Aspect: How to select the closest match to commands

We notice this is the well researched Approximate String Matching problem and has complicated ways to implement.

  • Alternative 1: Use the algorithm implemented in FZF, the fuzzy file finder.

    • Pros: Very user friendly. especially for longer commands. Suitable for history searching when paired with Mozilla’s Frecency algorithm.

    • Cons: Needs time to understand and implement.

  • Alternative 2: (currently implemented) Simple edit distance using a memoized dynamic programming approach.

    • Pros: Easier to implement. Merely 10 lines with memoization.

    • Cons: Takes O(nm) time to compare two strings, which means approximately O(n^2) time to create a priority queue of nearest commands.

To be implemented in v2.0: parsing the command in real time and suggest possible choices

for each for every argument.

  • Possible implementation: Initialize prefix objects with a list of possible String of values given that prefix, which can be displayed in autosuggestion.

3.8.2. AutoExpenses

Implementation

AutoExpenses will create expenses automatically once created. This update is done during every startup, when model manager is being initialized.

AutoExpenseSequenceDiagram

AutoExpense extends the Entry class, but has special attributes of its own:

  1. lastTime which keeps track of the date of last Expense generated

  2. Uses Date attribute to keep track the creation date of this object

  3. Frequency enum which keeps track of the frequency of the AutoExpense.

The only state-changing, outfacing method is generateNewExpenses(), which will not only return a List of Expenses since lastTime, but also update the lastTime attribute to match the latest Expense in the list returned.

The following activity diagram summarizes what happens when user creates a new AutoExpense:

AutoExpenseActivityDiagram
Design Considerations
Aspect: How to encode the recurring nature of AutoExpense
  • Alternative 1: Embrace the good practice of immutable objects and create a new AutoExpense object every time a new Expense is generated

    • Pros: Easier to prevent duplicate Expense generation since state is not changed.

    • Cons: Space intensive, also not necessary to recreate as only one field change (lastTime)

  • Alternative 2: (currently implemented) Keep track of the state using an attribute lastTime, and change the state of the AutoExpense object every update.

    • Pros: Easier to implement, and more space-friendly.

    • Cons: State changes are not saved or recorded in anyway.

Aspect: How to update AutoExpenses to generate new Expense objects
  • Alternative 1:: Use a thread to check every now and then, preferable every minute for consistent updates.

    • Pros: Reliable and more orthodox way of checking for updates.

    • Cons: Hard to implement. Not sure how to debug.

  • Alternative 2: (currently implemented) Update at every startup only.

    • Pros: Resource efficient. We take advantage of the assumption that

      • Users will usually open the app for no longer than a day.

      • The highest frequency an AutoExpense can be is currently daily.

    • Cons: Above assumptions have to be true.

3.9. Reminders

3.9.1. Implementation

The reminders implementation is facilitated by the reminder class, and heavily makes use of the observable pattern to keep track with property changes in the GuilTrip model to display messages in a timely fashion.

ReminderClassDiagram
There are 2 types of reminders. General and entry reminders.

General reminders are not tied to any specific entry, and sent the user notifications whenever an entry matching the user specified conditions is entered in the app. These specified conditions include entry type, a lower and upper quota for the entry amount, a specified time period in which an entry takes place, or a list of tags which the entry must have.

GRS
The sequence diagram above illustrates what happens when an entry fulfils all conditions in an entry.

STEP 1) When an expense is logged in to GuiltTrip.java, in addition to being stored in the Expense List, the expense is also passed into the Condition Manager, which iterates through its list of conditions to see which conditions are met by the entry.

STEP 2) Each condition makes use of a self-implemented ObservableSupport class that enables it to function as an Observable object, with the reminders being its listeners.

STEP 3) When a condition is met, it notifies the reminder it belongs to. The reminders keep track of the number of conditions met, and only when all conditions are met does it make use of ObservableSupport to notify the reminderlist about a change in its status.

STEP 4) The reminder list generates a notification corresponding to the reminder and adds it to an observable list which is displayed by the Ui.

STEP 5) The number of conditions met is reset a the end of the process so the reminders may continue to produce notifications when subsequent entries meeting the requirements are keyed into the system.

An entry reminder targets a specific expense/ income or wish. It is set to send notifications at a specified period at specified intervals before the date of the event.

ERS
The sequence diagram above illustrates what happens when an it is time for an entry reminder to send a notification.

STEP 1) This is made possible with TimeUtil, which is a singleton class with a single instance checking the local date at periodic intervals and updating its listeners (Using the ObservableSupport) of the current date.

STEP 2) All entry reminders are listeners of TimeUtil. When the updated current date equals the date to send a notification, it notifies the reminder list which generates a corresponding notification and sets the next date to notify the user.

STEP 3) Once the date of the event itself has passed, the reminder is deactivated and not saved the next time GuiltTrip is closed.

3.10. Design Considerations:

  • Alternative 1 (current method): Users must first select a reminder before they can edit or remove reminders.*

    • Pros: Easier to implement. By automatically toggling the reminder list view on when selecting a reminder, the user also will see what reminder they have selected before they proceed to make any changes. (As opposed to selecting and modifying the reminder in a single command).

    • Cons: Involves one more step. Not as efficient.

  • Alternative 2 Users commands require an index argument to indicate the reminder to modify.

    • Pros: Faster as it involves one less step. May be more convenient for users who frequently forget to first select reminder to modify.

    • Cons: Aforementioned benefits are mitigated as reminders are hidden in default GUI settings, and most users will have to open up the reminderList to know which reminder to modify anyway. === Undo/ Redo

3.10.1. Implementation

The undo/redo mechanism is facilitated by VersionedGuiltTrip. It extends GuiltTrip with an undo/redo history, stored internally as an guiltTripStateList and currentStatePointer. Additionally, it implements the following operations:

  • VersionedGuiltTrip#commit() — Saves the current finance tracker state in its history.

  • VersionedGuiltTrip#undo() — Restores the previous finance trackerk state from its history.

  • VersionedGuiltTrip#redo() — Restores a previously undone finance tracker state from its history.

These operations are exposed in the Model interface as Model#commitGuiltTrip(), Model#undoGuiltTrip() and Model#redoGuiltTrip() respectively.

Given below is an example usage scenario and how the undo/redo mechanism behaves at each step.

Step 1. The user launches the application for the first time. The VersionedGuiltTrip will be initialized with the initial finance tracker state, and the currentStatePointer pointing to that single finance tracker state.

UndoRedoState0

Step 2. The user executes delete 5 command to delete the 5th entry in the finance tracker. The delete command calls Model#commitGuiltTrip(), causing the modified state of the finance tracker after the delete 5 command executes to be saved in the guiltTripStateList, and the currentStatePointer is shifted to the newly inserted finance tracker state.

UndoRedoState1

Step 3. The user executes add typ/Expense…​ to add a new expense. The add command also calls Model#commitGuiltTrip(), causing another modified finance tracker state to be saved into the guiltTripStateList.

UndoRedoState2
If a command fails its execution, it will not call Model#commitGuiltTrip(), so the finance tracker state will not be saved into the guiltTripStateList.

Step 4. The user now decides that adding the expense was a mistake, and decides to undo that action by executing the undo command. The undo command will call Model#undoGuiltTrip(), which will shift the currentStatePointer once to the left, pointing it to the previous finance tracker state, and restores the finance tracker to that state.

UndoRedoState3
If the currentStatePointer is at index 0, pointing to the initial finance tracker state, then there are no previous finance tracker states to restore. The undo command uses Model#canUndoGuiltTrip() to check if this is the case. If so, it will return an error to the user rather than attempting to perform the undo.

The following sequence diagram shows how the undo operation works:

UndoSequenceDiagram
The lifeline for UndoCommand should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.

The redo command does the opposite — it calls Model#redoGuiltTrip(), which shifts the currentStatePointer once to the right, pointing to the previously undone state, and restores the finance tracker to that state.

If the currentStatePointer is at index guiltTripStateList.size() - 1, pointing to the latest finance tracker state, then there are no undone finance tracker states to restore. The redo command uses Model#canRedoGuiltTrip() to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo.

Step 5. The user then decides to execute the command list. Commands that do not modify the finance tracker, such as list, will usually not call Model#commitGuiltTrip(), Model#undoGuiltTrip() or Model#redoGuiltTrip(). Thus, the guiltTripStateList remains unchanged.

UndoRedoState4

Step 6. The user executes clear, which calls Model#commitGuiltTrip(). Since the currentStatePointer is not pointing at the end of the guiltTripStateList, all finance tracker states after the currentStatePointer will be purged. We designed it this way because it no longer makes sense to redo the add typ/Expense …​ command. This is the behavior that most modern desktop applications follow.

UndoRedoState5

The following activity diagram summarizes what happens when a user executes a new command:

CommitActivityDiagram

3.10.2. Design Considerations

Aspect: How undo & redo executes
  • Alternative 1 (current choice): Saves the entire GuiltTrip.

    • Pros: Easy to implement.

    • Cons: May have performance issues in terms of memory usage.

  • Alternative 2: Individual command knows how to undo/redo by itself.

    • Pros: Will use less memory (e.g. for delete, just save the person being deleted).

    • Cons: We must ensure that the implementation of each individual command are correct.

Aspect: Data structure to support the undo/redo commands
  • Alternative 1 (current choice): Use a list to store the history of finance tracker states.

    • Pros: Easy to understand and implement.

    • Cons: Logic is duplicated twice. For example, when a new command is executed, we must remember to update both HistoryManager and VersionedGuiltTrip.

  • Alternative 2: Use HistoryManager for undo/redo

    • Pros: We do not need to maintain a separate list, and just reuse what is already in the codebase.

    • Cons: Requires dealing with commands that have already been undone: We must remember to skip these commands. Violates Single Responsibility Principle and Separation of Concerns as HistoryManager now needs to do two different things.

4. Documentation

Refer to the guide here.

5. Testing

Refer to the guide here.

6. Dev Ops

Refer to the guide here.

Appendix A: Product Scope

A.1. Target User Profile

Youths and young adults in Singapore in the age range of 20-30 who are interested in keeping track of their spending.

A.2. Value Proposition

A convenient financial tracker targeted at users who prefer typing over other inputs.

Appendix B: User Stories

Priority As a…​ I want to… So that I can…

High

As a forgetful user

I would want to ability to list all my expenses

So I can see all my expenses in one glance.

High

As a thrifty user

I would like to be able to add items to my wishlist and see the progress made for each of the wishlisted items

So that I can see how much I’ve saved to each goal.

High

As a student with limited income

I need a convenient way to keep track of my spending and my daily expenditure

So that I can better review my finances.

High

As a student with almost regular spending habits

I want to record basic, recurring expenses (lunch, shopping, transport etc) easily

So that it is convenient for me to review and reflect on my expenditure.

High

As a user

I would like a search function

So that it is convenient for me to find a previous record.

High

As a user

I would like a manual to refer to when I need help using the app

So that I can still use the app when I forget the commands.

High

As a new user

I want to be informed when I submit invalid commands

So that I can input the correct command.

High

As a new user

I want to know what commands are available

So that I can use the application on the fly.

High

As a careless user

I might want to undo/modify/delete the fields of a specific expense

so that I can easily amend any mistakes I made.

High

As a detailed and careful user

I need to be able to add the details of the records into specific categories

So that I can stay organised.

Medium

As a user with limited allowance

I want to be able to set budgets for how much I want to spend in a week/month, according to different categories

So that I can closely keep track of my spending.

Medium

As someone who may wish to restart on a clean slate

I wish to be able to clear all of my data

So I can start afresh.

Medium

As a user

I would like to see my expenses and transactions separated according to different time periods (e.g. week, month, year)

so that I can have a clearer overview of my expenditure.

Medium

As a user

I want to be able to customise how the UI looks (color, font, font size, set background feature etc.)

so that it looks more customised towards the user.

Medium

As an expert user

I want to be able to set the time(s) that I would receive reminders to record my spending

so that I can do so at convenient times.

Medium

As a student trying to improve my spending habits

I want to be able to be able to see the daily break down of my spending

so that I can see the trend of my spending across the months.

Medium

As a college student with monthly spending on entertainment sites such as Netflix and Spotify

I want to have these expenses recorded automatically

so that I do not have to record a recurring expense every month.

Medium

As a user

I want to differentiate my spendings and wish list items based on whether they are a need or a want

so that I can better plan my finances around what I should buy.

Medium

As a forgetful user

I want to have a tooltip to pop up to remind me what inputs I should type in

so that in the event that I forget the commands, I can still use them when the application reminds me.

Medium

As a lazy student

I want my finances to be planned automatically rather than having to customize them myself

so I don’t need to spend much time during the first setup and lose interest. I should be able to edit it whenever I want to.

Medium

As an expert user

I want to be able to define/customise my own categories for expenses

so that I can customize the software for myself.

Medium

As a lazy and expert user

I want to be able to define my own shortcuts to certain functionality myself (eg. spend mala ytd lunch 10.50), and extend/customize them from time to time

so that I can complete commands with convenience and ease.

Medium

As a student with limited income

I need a visualizer to show my urgent wishlist

so I can see how much I have saved to each goal.

Medium

As a student with limited income

I need a visualizer

so I can see my expenses in proportion to my income at a glance.

Medium

As a student who has difficulty planning his finances

I want the app to show me the break down of my spending for me

so I can know which areas that I have overspent.

Medium

As a user who do not have the habit of tracking my expenses

I want to receive some incentive/motivation when I track my expenses

so that I would continue tracking it in the long run.

Medium

As a forgetful user

I need to have the ability to add notes to my wishlist detailing where I want to buy the product, link to buy the product etc

so that I can easily refer to the wishlist whenever I forget about the details.

Medium

As a student facing problems with student debt

I need an app to help me plan my spending with respect to my loan

so that I can work on paying off my loan eventually.

Medium

As a student trying to guilttrip his/her spending habits

I need an app that reminds me if I am spending too much

so that I can work on reducing my spending and improve my habits.

Low

As a user

I would like to be able to import details for my wishlist using external files

so that I do not need to key each item in individually.

Low

As a student who’s easily influenced

I want the app to provide me with reminders

So that I do not overindulge in things I do not need.

Low

As a lazy/busy student

I do not want to be required to write a description for my expense or income records every single time

so that I can save time and record many expenses quickly.

Appendix C: Use Cases

For all use cases:

  • System: guiltTrip()

  • Actor: User

C.1. Use Case 01: View History

C.1.1. MSS

  1. User requests to view history of expenses for the past month.

  2. guiltTrip() shows the history of expenses for the past month.

  3. User requests to edit a specific expense in the list.

  4. guiltTrip() edits the expense. Use case ends.

C.1.2. Extensions

  1. 2a. The history is empty. Use case ends.

  2. 3a. The given index is invalid.

    1. 3a1. guiltTrip() shows an error message. Use case resumes at step 2

C.2. Use Case 02: Add Expense

C.2.1. MSS

Use Case: user adds an expense

  1. User adds an expense.

  2. GuiltTrip creates an expense entry.

  3. GuiltTrip informs user that the expense have been created.

C.2.2. Extensions

1a GuiltTrip detects errors in the entered details.

1a1.GuiltTrip informs the user about the error.

1a2. User keys in new data.

Steps 1a1-1a2 are repeated until the data entered are correct. Use case resumes from step 2.

C.3. Use Case 03: Change the details of an existing Expense

C.3.1. MSS

  1. Guilt Trip displays list of expenses.

  2. User decides to edit the category/date/description/ tag/ amount of an expense.

  3. GuiltTrip makes the requested modifications to expenditure entry.

  4. GuiltTrip informs user that changes have been made.

C.3.2. Extensions

2a. GuiltTrip detects errors in the entered details.

2a1. GuiltTrip informs the user about the error.

2a2. User keys in new data.

Steps 2a1-2a2 are repeated until the data entered are correct. Use case resumes from step 3.

C.4. Use Case 04: Deletes an Expense

  1. User keys in command deleteExpense, followed by the index of the expense in the list

  2. GuiltTrip deletes the specified expense from the list.

  3. GuiltTrip informs user that the expense has been deleted.

C.4.1. Extensions

1a. GuiltTrip detects errors in the entered details.

1a1. GuiltTrip informs the user about the error.

1a2. User keys in new data.

Steps 1a1-1a2 are repeated until the data entered are correct. Use case resumes from step 2.

C.5. Use Case 05: Adds a Wish

  1. User adds a Wish.

  2. GuiltTrip creates a Wish.

  3. GuiltTrip informs user that the wish have been created.

C.6. Use Case 06: Converts Wish to Expenditure

  1. User keys in command purchaseWish, followed by the index of the expens wish in the list

  2. GuiltTrip deletes the specified wish from the list.

  3. GuiltTrip adds the corresponding expenditure to the expense list.

  4. GuiltTrip informs user that the wish has been converted.

C.7. Use Case 07: Add a recurring expenditure

  1. User keys in command addAutoExp, followed by the frequency he would want the expenditure to be, the description and amount of the expenditure.

  2. GuiltTrip creates an auto-expense entry.

  3. GuiltTrip informs user that the auto-expense have been created.

C.8. Use Case 08: List recurring expenditures

  1. The user types toggle autoexpense.

  2. GuiltTrip toggles the panel that lists all the current automatically recurring expenditures.

C.9. Use Case 09: Add GeneralReminder

C.9.1. MSS

  1. 1. User adds a GeneralReminder, indicates the reminder header, entry type/ lower bound/ upper bound/ start date/ end date/ and tags that a entry must have to trigger a notification.

  2. 2. guiltTrip() notifies user that Reminder has been added.

Use case ends.

C.9.2. Extensions

  1. 1a. User does not indicate any parameters.

    1. 1a1. GuiltTrip requests for correct parameters.

  2. 1b. LowerBound above UpperBound.

    1. 1b1. GuiltTrip requests for suitable bound values.

  3. 1b. End before Start.

    1. 1b1. GuiltTrip requests for suitabledate.

C.10. Use Case 10: Set EntryReminder

C.10.1. MSS

  1. 1. User adds an EntryReminder indicating the index of the entry in the list the reminder header, type of entry, period before the entry date to activate reminder and frequency of notifications.

  2. 2. GuiltTrip notifies user that Reminder has been added.

Use case ends.

C.10.2. Extensions

  1. 1a. Entry Date before Current Date.

  2. 1a1. GuiltTrip notifies user that reminder can only be created for events after today.

  3. 1b. Interval between notificaitons larger than period.

  4. 1b1. GuiltTrip notifies user that frequency must be smaller than period.

  5. 1c. Index out of bounds.

  6. 1c1. GuiltTrip requests for suitable index. === Use Case 11: Edit Reminder

C.10.3. MSS

  1. 1. User selects a reminder by the selectReminder() commmand.

  2. 2. GuiltTrip notifies user that Reminder has been selected.

  3. 3. User Edits Header/ Conditions/ Period/ Frequency of Reminder.

  4. 4. GuiltTrip notifies user that Reminder has been edited.

Use case ends.

C.10.4. Extensions

  1. 1a. index out of bounds.

  2. 1a1. GuiltTrip requests for suitable index.

  3. 3a. User tries to set frequency/ period for a General Reminder

  4. 3a1. GuiltTrip notifies user that reminder can only be created for events after today.

  5. 3b. Ineterval between notificaitons larger than period.

  6. 3b1. GuiltTrip notifies user that frequency must be smaller than period.

C.11. Use Case 11: Add Category

C.11.1. MSS

Use Case: user adds an category

  1. User adds an category.

  2. GuiltTrip creates an category entry.

  3. GuiltTrip informs user that the expense have been created.

C.11.2. Extensions

1a GuiltTrip detects errors in the entered details.

1a1.GuiltTrip informs the user about the error.

1a2. User keys in new data.

Steps 1a1-1a2 are repeated until the data entered are correct. Use case resumes from step 2.

1b GuiltTrip detects that the new category is a duplicate.

1b1 GuiltTrip informs user that the category is a duplicate.

1b2. User keys in new data. Steps 1b1-1b2 are repeated until the data entered are correct. Use case resumes from step 2

C.12. Use Case 12: Change the details of an existing Category

C.12.1. MSS

  1. User decides to edit the category Name of a category.

  2. GuiltTrip makes the requested modifications to category entry.

  3. GuiltTrip informs user that changes have been made.

C.12.2. Extensions

1a GuiltTrip detects errors in the entered details.

1a1.GuiltTrip informs the user about the error.

1a2. User keys in new data.

Steps 1a1-1a2 are repeated until the data entered are correct. Use case resumes from step 2.

1b GuiltTrip detects that the new edited category is a duplicate.

1b1 GuiltTrip informs user that the edited category is a duplicate.

1b2. User keys in new data.

Steps 1b1-1b2 are repeated until the data entered are correct. Use case resumes from step 2.

C.13. Use Case 13: Deleting an existing Category

C.13.1. MSS

  1. User decides to delete an existing category.

  2. GuiltTrip deletes the specified category from the list.

  3. GuiltTrip informs user that the category has been deleted.

C.13.2. Extensions

1a GuiltTrip detects errors in the entered details.

1a1.GuiltTrip informs the user about the error.

1a2. User keys in new data.

Steps 1a1-1a2 are repeated until the data entered are correct.

Use case resumes from step 2.

1b GuiltTrip detects that the to be deleted category has existing entries with the category.

1b1 GuiltTrip informs the user about the error.

Use case ends.

C.14. Use Case 14: View Bar Chart

  1. The user types in the command to view bar chart.

  2. GuiltTrip shows the user the relevant bar chart.

C.14.1. Extensions

1a GuiltTrip detects errors in the entered details.

1a1.GuiltTrip informs the user about the error.

1a2. User keys in new data.

Steps 1a1-1a2 are repeated until the data entered are correct.

Use case resumes from step 2.

C.15. Use Case 14: View Pie Chart

  1. The user types in the command to view pie chart.

  2. GuiltTrip shows the user the relevant pie chart.

C.15.1. Extensions

1a GuiltTrip detects errors in the entered details.

1a1.GuiltTrip informs the user about the error.

1a2. User keys in new data.

Steps 1a1-1a2 are repeated until the data entered are correct.

Use case resumes from step 2.

C.16. Use Case 14: View Pie Chart

  1. The user types in the command to view pie chart.

  2. GuiltTrip shows the user the relevant pie chart.

C.16.1. Extensions

1a GuiltTrip detects errors in the entered details.

1a1.GuiltTrip informs the user about the error.

1a2. User keys in new data.

Steps 1a1-1a2 are repeated until the data entered are correct.

Use case resumes from step 2.

C.17. Use Case 15: View Table

  1. The user types in the command to view table.

  2. GuiltTrip shows the user the relevant table.

C.17.1. Extensions

1a GuiltTrip detects errors in the entered details.

1a1.GuiltTrip informs the user about the error.

1a2. User keys in new data.

Steps 1a1-1a2 are repeated until the data entered are correct.

Use case resumes from step 2.

C.18. Use Case 16: Find Expense/Budget/Wish/Income

  1. The user types in the command to find the relevant entry.

  2. GuiltTrip shows the user the relevant entries after filtering according to the users’s input.

C.18.1. Extensions

1a GuiltTrip detects errors in the entered details.

1a1.GuiltTrip informs the user about the error.

1a2. User keys in new data.

Steps 1a1-1a2 are repeated until the data entered are correct.

Use case resumes from step 2.

C.19. Use Case 16: Sort Expense/Budget/Wish/Income

  1. The user types in the command to sort the list according to his liking.

  2. GuiltTrip shows the user the relevant entries after sorting according to the users’s input.

C.19.1. Extensions

1a GuiltTrip detects errors in the entered details.

1a1.GuiltTrip informs the user about the error.

1a2. User keys in new data.

Steps 1a1-1a2 are repeated until the data entered are correct.

Use case resumes from step 2.

Appendix D: Non Functional Requirements

  1. Brownfield

    • The final product should be a result of evolving/enhancing/morphing the given code base.

  2. Typing Preferred

    • The product should be targeting users who can type fast and prefer typing over other means of input.

  3. Single User

    • The product should be for a single user i.e. (not a multi-user product).

  4. Incremental

    • The product needs to be developed incrementally over the project duration.

  5. Human Editable File & no DBMS

    • The software should not have a database management system and the data should be stored locally and should be in a human editable text file.

  6. Object Oriented

    • The software should follow the Object-oriented paradigm primarily.

  7. Java Version

    • Should work on any mainstream OS as long as it has Java 11 or above installed.

  8. Portable

    • The software should work without requiring an installer.

  9. No Remote Server

    • The software should not depend on your own remote server.

  10. External Software

    • The use of third-party frameworks/libraries is allowed but only if they are free, open-source, and have permissive license terms, do not require any installation by the user of your software, do not violate other constraints.

  11. Quality Requirements

    • The software should be able to be used by a user who has never used an expenditure tracking app before

    • The software should be able to work on different computers if distributed

Appendix E: Glossary

  • Category - Income, Expense, Wishlist, Budget

  • Entry - any item in a category

  • Tag - label(s) attached to an entry

Appendix F: Instructions for Manual Testing

F.1. Adding a Expense

  1. Test Case: addExpense cat/Food n/Mala amt/5.50 d/2019-09-09 tg/food

    1. Expected: A new Expense is added into guiltTrip. Details of the expense added is showed in the CommandResult.

  2. Test Case: addExpense cat/Food n/Mala amt/5.50 tg/food

    1. Expected: GuiltTrip throws an exception in the form of an error message in CommandResult, specifying that the d/DATE field is missing. The expense is not added.

  3. Test Case: addExpense cat/Food n/Mala amt/5.503 d/2019-09-09 tg/food

    1. Expected: GuiltTrip throws an exception in the form of an error message in CommandResult, specifying that the amt/AMOUNT field has more than the required d.p. The expense is not added.

F.2. Adding a Category

  1. Test Case: addCategory cat/Expense n/Exercise

    1. Expected: A new Category is added into guiltTrip. Details of the Category added is showed in the CommandResult.

  2. Test Case: addCategory cat/Budget n/Exercise

    1. Expected: GuiltTrip throws an exception in the form of an error message in CommandResult, specifying that the cat/CATEGORY TYPE must either be expense or income. The Category is not added.

  3. Test Case: addCategory n/Exercise

    1. Expected: GuiltTrip throws an exception in the form of an error message in CommandResult, specifying that the cat/CATEGORY NAME. The Category is not added.

F.3. Editing a Category

  1. Test Case: editCategory cat/Expense o/Food n/Fitness

    1. Expected: The original Category is replaced by the new Category. Details of the Category edited is showed in the CommandResult.

  2. Test Case: editCategory cat/Expense o/Anime n/Fitness

    1. Explanation: Anime is a category that doesn’t exists.

    2. Expected: GuiltTrip throws an exception in the form of an error message in CommandResult, specifying that the Expense Category List does not have an existing Category named anime. The Category is not edited.

  3. Test Case: editCategory cat/Expense o/Food n/Food

    1. Expected: GuiltTrip throws an exception in the form of an error message in CommandResult, specifying that the category already exists in GuiltTrip as nothing was changed. The Category is not edited.

  4. Test Case: editCategory cat/Expense o/Food

    1. Expected: GuiltTrip throws an exception in the form of an error message in CommandResult, specifying that there is a missing field n/CATEGORY NEW NAME. The Category is not edited.

F.4. Deleting a Category

  1. Test Case: deleteCategory cat/Expense n/Food

    1. Explanation: In this case, Food does not have any entries with it as a Category.

    2. Expected: Food Category is deleted from guiltTrip. Details of the Category deleted is showed in the CommandResult.

  2. Test Case: deleteCategory cat/Budget n/Food

    1. Expected: GuiltTrip throws an exception in the form of an error message in CommandResult, specifying that the cat/CATEGORY TYPE must either be expense or income. The Category is not deleted.

  3. Test Case: deleteCategory cat/Expense n/Food

    1. Explanation: In this case, Food has entries with it as a Category.

    2. Expected: Food Category is not deleted from guiltTrip.GuiltTrip throws an exception in the form of an error message in CommandResult, specifying that the Category to be deleted has existing entries.

F.5. Viewing Statistics in Table Form

  1. Test Case: viewTable

    1. Expected: The Table showing expense and income details of the current month is shown.

  2. Test Case: viewTable p/2019-09

    1. Expected: The Table showing expense and income details of September 2019 is shown.

  3. Test Case: viewTable p/2019-09, 2019-11

    1. Expected: The Table showing expense and income details of September 2019 to November 2019 is shown.

  4. Test Case: viewTable p/2019-09-01

    1. Expected: GuiltTrip throws an exception stating that it should only be shown in Month Form.

F.6. Viewing Statistics in Pie Form

  1. Test Case: viewPie

    1. Expected: The Pie Chart showing expense and income details of the current month is shown.

  2. Test Case: viewPie p/2019-09

    1. Expected: The Pie Chart showing expense and income details of September 2019 is shown.

  3. Test Case: viewPie p/2019-09, 2019-11

    1. Expected: The Pie Chart showing expense and income details of September 2019 to November 2019 is shown.

  4. Test Case: viewPie p/2019-09-01

    1. Expected: GuiltTrip throws an exception stating that it should only be shown in Month Form.

F.7. Viewing Statistics in Bar Form

  1. Test Case: viewBar

    1. Expected: The Bar Chart showing expense and income details of the current month is shown.

  2. Test Case: viewBar p/2019-09

    1. Expected: The Bar Chart showing expense and income details of September 2019 is shown.

F.8. Sorting Expense/Income/Wish/Budget/AutoExp

  1. Test Case: sortExpense typ/Amount s/ascending

    1. Expected: The Expense list should be sorted by amount in ascending order.

  2. Test Case: sortIncome typ/Time s/ascending

    1. Expected: The Income list should be sorted by time in ascending order.

  3. Test Case: sortBudget typ/Time s/asdasdsada

    1. Expected: An Exception should be thrown, with GulitTrip displaying in the CommandResult that sequence can only be ascending or descending.

  4. Test Case: sortWish typ/Time s/ascending

    1. Expected: The Wish list should be sorted by time in ascending order.

  5. Test Case: sortAutoExp typ/Time s/ascending

    1. Expected: The AutoExpense list should be sorted by time in ascending order.

F.9. Finding Expense/Income/Wish/Budget/AutoExp

  1. Test Case: findExpense n/mala

    1. Expected: All Expenses with name mala in their description should be filtered to show in the Expense List.

  2. Test Case: findIncome n/mala amt/1900

    1. Expected: All Income with name mala in their description and with amount larger than 1900 should show in the Income List.

  3. Test Case: findBudget cat/Food

    1. Expected: All Budget with category Food should show in the BudgetList

  4. Test Case: findWish cat/Food

    1. Expected: All Wishes with category Food should show in the WishList

  5. Test Case: findAutoExp cat/Food

    1. Expected: All AutoExpense with category Food should show in the AutoExpenseList

F.10. Customising the GUI

F.10.1. Change theme

  1. Changing the theme of the application

    1. Prerequisites: Application is in dark theme.

    2. Test case: setLightTheme
      Expected: Theme changes to the light theme. Result display shows the message that command is executed successfully.

    3. Prerequisites: Application is in light theme.

    4. Test case: setDarkTheme
      Expected: Theme changes to the dark theme. Result display shows the message that command is executed successfully.

  2. Saving theme preferences

    1. Change the theme to a different one (see examples above). Close the window.

    2. Relaunch the application.
      Expected: The change in theme is saved and application is in the most recent theme.

F.10.2. Change font

  1. Changing the font of the application

    1. Prerequisites: none

      1. Test case: changeFont rockwell
        Expected: Font changes to rockwell.

      2. Test case: changeFont segoe UI
        Expected: Font changes to segoe UI.

  2. Saving font preferences

    1. Change the font to a different one (see examples above). Close the window.

    2. Relaunch the appication.
      Expected: The change in font is saved and the application is in the most recent font.

F.10.3. Toggle panel

  1. Toggling a specified panel on or off

    1. Prerequisites: panel to toggle does not have its contents already shown in main panel

      1. Test case: toggle budget
        Expected: If panel was not previously shown, Budget panel will be toggled on. Otherwise, it will be toggled off.

      2. Test case: toggle ae
        Expected: If panel was not previously shown, AutoExpenses panel will be toggled on. Otherwise, it will be toggled off.

    2. Prerequisites: panel to toggle already has its contents shown in the main panel, such as by doing listBudget

      1. Test case: toggle budget
        Expected: Budget panel is not toggled on. Error message is shown in the result display.