By: Team T16-04      Since: Aug 2019      Licence: MIT
- 1. Setting up
- 2. Design
- 3. Implementation
- 3.1. Data Presentation: Categories
- 3.2. Data Presentation: Sort Command
- 3.3. Data Presentation: Find Command
- 3.4. Data Presentation: Statistics
- 3.5. Toggle Panel Command
- 3.6. Change Font Command
- 3.7. Set Light/Dark Theme Command
- 3.8. Quality of Life features
- 3.9. Reminders
- 3.10. Design Considerations:
 
- 4. Documentation
- 5. Testing
- 6. Dev Ops
- Appendix A: Product Scope
- Appendix B: User Stories
- Appendix C: Use Cases
- C.1. Use Case 01: View History
- C.2. Use Case 02: Add Expense
- C.3. Use Case 03: Change the details of an existing Expense
- C.4. Use Case 04: Deletes an Expense
- C.5. Use Case 05: Adds a Wish
- C.6. Use Case 06: Converts Wish to Expenditure
- C.7. Use Case 07: Add a recurring expenditure
- C.8. Use Case 08: List recurring expenditures
- C.9. Use Case 09: Add GeneralReminder
- C.10. Use Case 10: Set EntryReminder
- C.11. Use Case 11: Add Category
- C.12. Use Case 12: Change the details of an existing Category
- C.13. Use Case 13: Deleting an existing Category
- C.14. Use Case 14: View Bar Chart
- C.15. Use Case 14: View Pie Chart
- C.16. Use Case 14: View Pie Chart
- C.17. Use Case 15: View Table
- C.18. Use Case 16: Find Expense/Budget/Wish/Income
- C.19. Use Case 16: Sort Expense/Budget/Wish/Income
 
- Appendix D: Non Functional Requirements
- Appendix E: Glossary
- Appendix F: Instructions for Manual Testing
- F.1. Adding a Expense
- F.2. Adding a Category
- F.3. Editing a Category
- F.4. Deleting a Category
- F.5. Viewing Statistics in Table Form
- F.6. Viewing Statistics in Pie Form
- F.7. Viewing Statistics in Bar Form
- F.8. Sorting Expense/Income/Wish/Budget/AutoExp
- F.9. Finding Expense/Income/Wish/Budget/AutoExp
- F.10. Customising the GUI
 
1. Setting up
Refer to the guide here.
2. Design
2.1. Architecture
 
The Architecture Diagram given above explains the high-level design of the App. Given below is a quick overview of each component.
- 
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 interfacewith the same name as the Component.
- 
Exposes its functionality using a {Component Name}Managerclass.
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.
 
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.
 
delete 1 commandThe sections below give more details of each component.
2.2. UI component
 
API :
Ui.java
- 
The UI consists of a MainWindowthat is made up of parts e.g.CommandBox,ResultDisplay,ExpenseListPanel,StatusBarFooteretc. All these, including theMainWindow, inherit from the abstractUiPartclass.
- 
The UIcomponent uses JavaFx UI framework. The layout of these UI parts are defined in matching.fxmlfiles that are in thesrc/main/resources/viewfolder. For example, the layout of theMainWindowis specified inMainWindow.fxml
The UI component
- 
Executes user commands using the Logiccomponent.
- 
Listens for changes to Modeldata so that theUIcan be updated with the modified data.
2.3. Logic component
 
API :
Logic.java
- 
Logicuses theguiltTripParserclass to parse the user command.
- 
This results in a Commandobject which is executed by theLogicManager.
- 
The command execution can affect the Model(e.g. adding a person).
- 
The result of the command execution is encapsulated as a CommandResultobject which is passed back to theUi.
- 
In addition, the CommandResultobject can also instruct theUito 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.
 
delete 1 Command2.4. Model component
 
API : Model.java
The Model
- 
stores a UserPrefobject 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
 
API : Storage.java
The Storage
- 
can save UserPrefobjects 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
 
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 forCategoryList#contains(Category)as well as a need to check if there are existing entries of the originalCategoryto carry out modifications on them.
- 
When deleting a Category, there is a need to check if there are any entries that have theCategoryas a field.
Given below is an example of an activity diagram for editing a category to illustrate the point above.
 
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 CategoryListto 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, andTags.
- 
SortSequence: Ascending, Descending
An Example of Sorting the Expense List is shown below
- 
The user executes the command sortExpense typ/Amount s/ascending
- 
Logicuses theguiltTripParserclass to parse the user command
- 
This results in a SortExpenseCommandobject which is executed by theLogicManager
- 
The SortExpenseCommandcalls theModel#sortFilteredExpenseListto sort the list of expenses
- 
The result of the command execution is encapsulated as a CommandResultobject which is passed back to theUi
- 
Logicreturns theCommandResultobject
| The Model#sortFilteredExpenseListcreates anEntryComparatorwhich takes inSortTypeandSortSequenceto 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.
 
sortExpense typ/Amount s/ascending Command3.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
 
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 onMonthList#updateListOfStats(Category)to calculate the list of Stats across Categories in thatMonthList, thus updating the list ofCategory Statistics.
- 
StatisticsManager#updateBarChart(monthToCalculate): Calculates the daily statistics according to the month specified. Calls onMonthList#CalculateStatisticsForBarChart()which will call onDailyList#CalculateStatisticsForBarChart()to update the list ofDailyStatistics.
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.
 
The overview of this process can be found in the Activity Diagram above.
The details of the process is as below:
- 
The user executes the command viewPie p/2019-09,2019-11
- 
LogicManageruses theguiltTripParserclass to parse the user command.
- 
This results in a viewPieChartCommandobject which is executed by theLogicManager.
- 
The viewPieChartCommandcalls theModel#updateListOfStats(RangeOfDates)'s method which then callsStatisticsManager#updateListOfStats(RangeOfDates)method to calculate the statistics for that type.
- 
StatisticsManager#updateListOfStats(RangeOfDates)detects that the size of the list is 2 and calls#getListOfMonths(RangeOfDates)to retrieve the list ofMonthListMonthListToCalculate from start Date to End Date fromyearlyRecord, theObservableMapinsideStatisticsManager.
- 
StatisticsManager#updateListOfStats(RangeOfDates)then callsStatisticsManager#countStats(MonthListToCalculate, listOfStatistics), which will calculate the list of statistics for expense and income categories and create many newCategoryStatisticsobjects to save the data of the calculated Statistics for each Category.
- 
StatisticsManager#countStats(MonthListToCalculate, listOfStatistics)will replace the all theCategoryStatisticsobjects in theObservableListofCategoryStatisticswith the newly calculatedCategoryStatisticsobjects.
- 
As the ObservableListis updated, the PieChart and Table which uses thisObservableListis also updated, leading to them being updated.
- 
Finally, StatisticsManager#countStats(MonthListToCalculate, listOfStatistics)will set the new TotalExpense and TotalIncome values to the new values calculated, which will also update theUifor Stats which displays the total expense and total income.
- 
The result of the command execution is encapsulated as a CommandResultobject which is passed back to theUi
- 
Logicreturns theCommandResultobject.
Given below is the Sequence Diagram for interactions within the Logic component for the execute("viewPie p/2019-09,2019-11") API call.
 
viewPie p/2019-09,2019-11 Command3.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:
- 
The user executes the command viewBar p/2019-09
- 
LogicManageruses theguiltTripParserclass to parse the user command.
- 
This results in a ViewBarChartCommandobject which is executed by theLogicManager
- 
The ViewBarChartCommandcalls theModel#updateBarChart(MonthToShow)'s method which then callsStatisticsManager#updateBarChart(monthToShow)method to calculate the statistics for that period.
- 
StatisticsManager#updateBarChart(MonthToShow)retrieves the relavant MonthList fromObservableMap, yearlyRecord and callsMonthList#calculateStatisticsForBarChart.
- 
The called MonthListwill then loop through all the DailyList in it and callsDailyList#calculateStatisticsForBarChart, retrieving the result and returning it toStatisticsManager.
- 
StatisticsManager#updateBarChart(MonthToShow)will replace the all theDailyStatisticsobjects in theObservableListofDailyStatisticswith the newly calculatedDailyStatisticsobjects.
- 
As the ObservableListis updated, the BarChart which uses thisObservableListis also updated, leading to them being updated.
- 
The result of the command execution is encapsulated as a CommandResultobject which is passed back to theUi
- 
Logicreturns theCommandResultobject.
Given below is the Sequence Diagram for interactions within the Logic component for the execute("viewBar p/2019-09") API call.
 
viewBar p/2019-09 Command3.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
 
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:
 
toggle wishlist commandThe sequence diagram is as explained below:
- 
The user launches the application and executes the toggle wishlistcommand to toggle thewishlistpanel.
- 
commandResultis obtained inMainWindowafter the command is parsed and executed.
- 
MainWindowchecks if thetogglePanelattribute incommandResultis true.
- 
Since it is true, it retrieves the PanelNameWISHfromcommandResultand calls on its own methodhandleTogglePanel.
- 
This method then calls on another method togglePanel()that toggles the panel and takes in thePanelNameWISHas a parameter.- 
(Not shown in sequence diagram to reduce its complexity) It also checks if the wishlist is already shown in the main panel. 
- 
If it is, then a CommandExceptionis 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:
 
toggle command3.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: MainWindowhas aPanelManagerclass 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 PanelManagerandMainWindow.
 
- 
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:
 
changeFont rockwell commandThe sequence diagram is as explained below:
- 
The user launches the application and executes the changeFont rockwellcommand to change the current application font to rockwell.
- 
commandResultis obtained inMainWindowafter the command is parsed and executed.
- 
MainWindowchecks if thechangeFontattribute incommandResultis true.
- 
Since it is true, it retrieves the FontNameROCKWELLfromcommandResultand calls on its own methodhandleChangeFont.
- 
This method then converts the FontNameROCKWELLto aString "rockwell"and sets thefont-familyattribute ofwindow, that contains all the child nodes, torockwell.
The following activity diagram summarizes what happens when a user executes a changeFont command:
 
changeFont command3.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 ThemeandThemeManagerand betweenThemeManagerandMainWindow).
 
- 
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:
 
setLightTheme commandThe sequence diagram is as explained:
- 
The user launches the application and executes the setLightThemecommand to change the current theme to light.
- 
commandResultis obtained inMainWindowafter the command is parsed and executed.
- 
MainWindowchecks if thechangeThemeattribute incommandResultis true.
- 
Since it is true, it retrieves the newThemefromcommandResult,LIGHT, and calls on its own methodswitchThemeTo(LIGHT).- 
(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. 
 
- 
- 
This implementation is essentially the same for setDarkThemecommand, with thenewThemeasDARKinstead.
The following activity diagram summarizes what happens when a user executes a setLightTheme command:
 
setLightTheme command3.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 ThemeManagerand reduces the number of lines of code inMainWindow.
- 
Cons: Harder to implement; may introduce cyclic dependency. It may also be redundant or excessive as implementing the changeFontcommand 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.
 
Figure: Class Diagram for AutoSuggest.
| Terminology: AutoSuggestrefers to the feature as a whole.AutoSuggestionis the term users see in GuiltTrip application.CommandSuggesteris the functional interface in the source code.GuiltTripCommandSuggesteris the class that gives suggestions based on user input. | 
Currently AutoSuggester carries out two functions:
- 
suggesting full commands when halfway typing any command 
- 
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
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 approximatelyO(n^2)time to create a priority queue of nearest commands.
 
- 
v2.0: parsing the command in real time and suggest possible choicesfor each for every argument.
- 
Possible implementation: Initialize prefix objects with a list of possible Stringof 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.
 
AutoExpense extends the Entry class, but has special attributes of its own:
- 
lastTimewhich keeps track of the date of last Expense generated
- 
Uses Dateattribute to keep track the creation date of this object
- 
Frequencyenum which keeps track of the frequency of theAutoExpense.
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:
 
Design Considerations
- 
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 theAutoExpenseobject every update.- 
Pros: Easier to implement, and more space-friendly. 
- 
Cons: State changes are not saved or recorded in anyway. 
 
- 
- 
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.
 
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.
 
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.
 
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.
 
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.
 
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.
 
| If a command fails its execution, it will not call Model#commitGuiltTrip(), so the finance tracker state will not be saved into theguiltTripStateList. | 
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.
 
| If the currentStatePointeris at index 0, pointing to the initial finance tracker state, then there are no previous finance tracker states to restore. Theundocommand usesModel#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:
 
| The lifeline for UndoCommandshould 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 currentStatePointeris at indexguiltTripStateList.size() - 1, pointing to the latest finance tracker state, then there are no undone finance tracker states to restore. Theredocommand usesModel#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.
 
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.
 
The following activity diagram summarizes what happens when a user executes a new command:
 
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 HistoryManagerandVersionedGuiltTrip.
 
- 
- 
Alternative 2: Use HistoryManagerfor 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 HistoryManagernow 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
- 
User requests to view history of expenses for the past month. 
- 
guiltTrip() shows the history of expenses for the past month. 
- 
User requests to edit a specific expense in the list. 
- 
guiltTrip() edits the expense. Use case ends. 
C.1.2. Extensions
- 
2a. The history is empty. Use case ends. 
- 
3a. The given index is invalid. - 
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
- 
User adds an expense. 
- 
GuiltTrip creates an expense entry. 
- 
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
- 
Guilt Trip displays list of expenses. 
- 
User decides to edit the category/date/description/ tag/ amount of an expense. 
- 
GuiltTrip makes the requested modifications to expenditure entry. 
- 
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
- 
User keys in command deleteExpense, followed by the index of the expense in the list 
- 
GuiltTrip deletes the specified expense from the list. 
- 
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
- 
User adds a Wish. 
- 
GuiltTrip creates a Wish. 
- 
GuiltTrip informs user that the wish have been created. 
C.6. Use Case 06: Converts Wish to Expenditure
- 
User keys in command purchaseWish, followed by the index of the expens wish in the list 
- 
GuiltTrip deletes the specified wish from the list. 
- 
GuiltTrip adds the corresponding expenditure to the expense list. 
- 
GuiltTrip informs user that the wish has been converted. 
C.7. Use Case 07: Add a recurring expenditure
- 
User keys in command addAutoExp, followed by the frequency he would want the expenditure to be, the description and amount of the expenditure.
- 
GuiltTrip creates an auto-expense entry. 
- 
GuiltTrip informs user that the auto-expense have been created. 
C.8. Use Case 08: List recurring expenditures
- 
The user types toggle autoexpense.
- 
GuiltTrip toggles the panel that lists all the current automatically recurring expenditures. 
C.9. Use Case 09: Add GeneralReminder
C.9.1. MSS
- 
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. guiltTrip() notifies user that Reminder has been added. 
Use case ends.
C.9.2. Extensions
- 
1a. User does not indicate any parameters. - 
1a1. GuiltTrip requests for correct parameters. 
 
- 
- 
1b. LowerBound above UpperBound. - 
1b1. GuiltTrip requests for suitable bound values. 
 
- 
- 
1b. End before Start. - 
1b1. GuiltTrip requests for suitabledate. 
 
- 
C.10. Use Case 10: Set EntryReminder
C.10.1. MSS
- 
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. GuiltTrip notifies user that Reminder has been added. 
Use case ends.
C.10.2. Extensions
- 
1a. Entry Date before Current Date. 
- 
1a1. GuiltTrip notifies user that reminder can only be created for events after today. 
- 
1b. Interval between notificaitons larger than period. 
- 
1b1. GuiltTrip notifies user that frequency must be smaller than period. 
- 
1c. Index out of bounds. 
- 
1c1. GuiltTrip requests for suitable index. === Use Case 11: Edit Reminder 
C.10.3. MSS
- 
1. User selects a reminder by the selectReminder() commmand. 
- 
2. GuiltTrip notifies user that Reminder has been selected. 
- 
3. User Edits Header/ Conditions/ Period/ Frequency of Reminder. 
- 
4. GuiltTrip notifies user that Reminder has been edited. 
Use case ends.
C.10.4. Extensions
- 
1a. index out of bounds. 
- 
1a1. GuiltTrip requests for suitable index. 
- 
3a. User tries to set frequency/ period for a General Reminder 
- 
3a1. GuiltTrip notifies user that reminder can only be created for events after today. 
- 
3b. Ineterval between notificaitons larger than period. 
- 
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
- 
User adds an category. 
- 
GuiltTrip creates an category entry. 
- 
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
- 
User decides to edit the category Name of a category. 
- 
GuiltTrip makes the requested modifications to category entry. 
- 
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
- 
User decides to delete an existing category. 
- 
GuiltTrip deletes the specified category from the list. 
- 
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
- 
The user types in the command to view bar chart. 
- 
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
- 
The user types in the command to view pie chart. 
- 
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
- 
The user types in the command to view pie chart. 
- 
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
- 
The user types in the command to view table. 
- 
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
- 
The user types in the command to find the relevant entry. 
- 
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
- 
The user types in the command to sort the list according to his liking. 
- 
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
- 
Brownfield - 
The final product should be a result of evolving/enhancing/morphing the given code base. 
 
- 
- 
Typing Preferred - 
The product should be targeting users who can type fast and prefer typing over other means of input. 
 
- 
- 
Single User - 
The product should be for a single user i.e. (not a multi-user product). 
 
- 
- 
Incremental - 
The product needs to be developed incrementally over the project duration. 
 
- 
- 
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. 
 
- 
- 
Object Oriented - 
The software should follow the Object-oriented paradigm primarily. 
 
- 
- 
Java Version - 
Should work on any mainstream OS as long as it has Java 11 or above installed. 
 
- 
- 
Portable - 
The software should work without requiring an installer. 
 
- 
- 
No Remote Server - 
The software should not depend on your own remote server. 
 
- 
- 
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. 
 
- 
- 
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
- 
Test Case: addExpense cat/Food n/Mala amt/5.50 d/2019-09-09 tg/food - 
Expected: A new Expense is added into guiltTrip. Details of the expense added is showed in the CommandResult. 
 
- 
- 
Test Case: addExpense cat/Food n/Mala amt/5.50 tg/food - 
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. 
 
- 
- 
Test Case: addExpense cat/Food n/Mala amt/5.503 d/2019-09-09 tg/food - 
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
- 
Test Case: addCategory cat/Expense n/Exercise - 
Expected: A new Category is added into guiltTrip. Details of the Category added is showed in the CommandResult. 
 
- 
- 
Test Case: addCategory cat/Budget n/Exercise - 
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. 
 
- 
- 
Test Case: addCategory n/Exercise - 
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
- 
Test Case: editCategory cat/Expense o/Food n/Fitness - 
Expected: The original Category is replaced by the new Category. Details of the Category edited is showed in the CommandResult. 
 
- 
- 
Test Case: editCategory cat/Expense o/Anime n/Fitness - 
Explanation: Anime is a category that doesn’t exists. 
- 
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. 
 
- 
- 
Test Case: editCategory cat/Expense o/Food n/Food - 
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. 
 
- 
- 
Test Case: editCategory cat/Expense o/Food - 
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
- 
Test Case: deleteCategory cat/Expense n/Food - 
Explanation: In this case, Food does not have any entries with it as a Category. 
- 
Expected: Food Category is deleted from guiltTrip. Details of the Category deleted is showed in the CommandResult. 
 
- 
- 
Test Case: deleteCategory cat/Budget n/Food - 
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. 
 
- 
- 
Test Case: deleteCategory cat/Expense n/Food - 
Explanation: In this case, Food has entries with it as a Category. 
- 
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
- 
Test Case: viewTable - 
Expected: The Table showing expense and income details of the current month is shown. 
 
- 
- 
Test Case: viewTable p/2019-09 - 
Expected: The Table showing expense and income details of September 2019 is shown. 
 
- 
- 
Test Case: viewTable p/2019-09, 2019-11 - 
Expected: The Table showing expense and income details of September 2019 to November 2019 is shown. 
 
- 
- 
Test Case: viewTable p/2019-09-01 - 
Expected: GuiltTrip throws an exception stating that it should only be shown in Month Form. 
 
- 
F.6. Viewing Statistics in Pie Form
- 
Test Case: viewPie - 
Expected: The Pie Chart showing expense and income details of the current month is shown. 
 
- 
- 
Test Case: viewPie p/2019-09 - 
Expected: The Pie Chart showing expense and income details of September 2019 is shown. 
 
- 
- 
Test Case: viewPie p/2019-09, 2019-11 - 
Expected: The Pie Chart showing expense and income details of September 2019 to November 2019 is shown. 
 
- 
- 
Test Case: viewPie p/2019-09-01 - 
Expected: GuiltTrip throws an exception stating that it should only be shown in Month Form. 
 
- 
F.7. Viewing Statistics in Bar Form
- 
Test Case: viewBar - 
Expected: The Bar Chart showing expense and income details of the current month is shown. 
 
- 
- 
Test Case: viewBar p/2019-09 - 
Expected: The Bar Chart showing expense and income details of September 2019 is shown. 
 
- 
F.8. Sorting Expense/Income/Wish/Budget/AutoExp
- 
Test Case: sortExpense typ/Amount s/ascending - 
Expected: The Expense list should be sorted by amount in ascending order. 
 
- 
- 
Test Case: sortIncome typ/Time s/ascending - 
Expected: The Income list should be sorted by time in ascending order. 
 
- 
- 
Test Case: sortBudget typ/Time s/asdasdsada - 
Expected: An Exception should be thrown, with GulitTrip displaying in the CommandResult that sequence can only be ascending or descending. 
 
- 
- 
Test Case: sortWish typ/Time s/ascending - 
Expected: The Wish list should be sorted by time in ascending order. 
 
- 
- 
Test Case: sortAutoExp typ/Time s/ascending - 
Expected: The AutoExpense list should be sorted by time in ascending order. 
 
- 
F.9. Finding Expense/Income/Wish/Budget/AutoExp
- 
Test Case: findExpense n/mala - 
Expected: All Expenses with name mala in their description should be filtered to show in the Expense List. 
 
- 
- 
Test Case: findIncome n/mala amt/1900 - 
Expected: All Income with name mala in their description and with amount larger than 1900 should show in the Income List. 
 
- 
- 
Test Case: findBudget cat/Food - 
Expected: All Budget with category Food should show in the BudgetList 
 
- 
- 
Test Case: findWish cat/Food - 
Expected: All Wishes with category Food should show in the WishList 
 
- 
- 
Test Case: findAutoExp cat/Food - 
Expected: All AutoExpense with category Food should show in the AutoExpenseList 
 
- 
F.10. Customising the GUI
F.10.1. Change theme
- 
Changing the theme of the application - 
Prerequisites: Application is in dark theme. 
- 
Test case: setLightTheme
 Expected: Theme changes to the light theme. Result display shows the message that command is executed successfully.
- 
Prerequisites: Application is in light theme. 
- 
Test case: setDarkTheme
 Expected: Theme changes to the dark theme. Result display shows the message that command is executed successfully.
 
- 
- 
Saving theme preferences - 
Change the theme to a different one (see examples above). Close the window. 
- 
Relaunch the application. 
 Expected: The change in theme is saved and application is in the most recent theme.
 
- 
F.10.2. Change font
- 
Changing the font of the application - 
Prerequisites: none - 
Test case: changeFont rockwell
 Expected: Font changes to rockwell.
- 
Test case: changeFont segoe UI
 Expected: Font changes to segoe UI.
 
- 
 
- 
- 
Saving font preferences - 
Change the font to a different one (see examples above). Close the window. 
- 
Relaunch the appication. 
 Expected: The change in font is saved and the application is in the most recent font.
 
- 
F.10.3. Toggle panel
- 
Toggling a specified panel on or off - 
Prerequisites: panel to toggle does not have its contents already shown in main panel - 
Test case: toggle budget
 Expected: If panel was not previously shown, Budget panel will be toggled on. Otherwise, it will be toggled off.
- 
Test case: toggle ae
 Expected: If panel was not previously shown, AutoExpenses panel will be toggled on. Otherwise, it will be toggled off.
 
- 
- 
Prerequisites: panel to toggle already has its contents shown in the main panel, such as by doing listBudget- 
Test case: toggle budget
 Expected: Budget panel is not toggled on. Error message is shown in the result display.
 
- 
 
-