Skip to content

Latest commit

 

History

History
1288 lines (906 loc) · 53.7 KB

File metadata and controls

1288 lines (906 loc) · 53.7 KB

ExpenseLa - Developer Guide

1. Introduction

ExpenseLa is an application for NUS students to be able to track their spending and also gain insights to their monthly spending through data analytics. The data analytics portion of ExpenseLa aims to aid students in viewing and comparing their monthly expenditure on items of different categories such as shopping and groceries, as well as keeping track of their monthly budgets. ExpenseLa records daily incoming and outgoing transactions and constantly keeps track of the user’s budget, income and spending. ExpenseLa is optimised for users who prefer to work with a Command Line Interface (CLI) which works in parallel with a Graphical User Interface (GUI). It is an easy, insight-driven application that hopes to help students keep better track of their expenses.

1.1. Purpose

This developer guide aims to communicate the design and architecture of the software implementation to developers working on ExpenseLa. It also includes future implementations to give developers an idea of the direction that ExpenseLa intends to take on both current and future features. A developer should be able to understand the design, architecture and future goals of ExpenseLa upon reading this guide.

1.2. Scope

This developer guide specifies the technical and non-technical details of ExpenseLa. The technical aspects include the design and architecture of the software and the non-technical aspects include the user stories, use cases and non-functional requirements.

2. Setting up

Pleas refer to the guide here.

3. Design

3.1. Architecture (Pang Kim Jin)

In this section, we will be introducing the individual components that form ExpenseLa using various diagrams.

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.

💡
The .puml files used to create diagrams in this document can be found in the diagrams folder. Refer to the Using PlantUML guide to learn how to create and edit diagrams.

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.

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.

3.2. UI component (Choi Min Suk)

UiClassDiagram
Figure 4. Structure of the UI Component

API : Ui.java

The UI consists of a MainWindow that is made up of parts e.g.CommandBox, ResultDisplay, TransactionListPanel, 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 does the following actions:

  • Executes user commands using the Logic component.

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

3.3. Logic component (Hubert Halim)

LogicClassDiagram
Figure 5. Structure of the Logic Component

API : Logic.java

Logic is an interface which LogicManager implements, allowing access to the API. The following items are examples on how the LogicManager class can be interacted with:

  1. Logic uses the ExpenseLaParser 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 Transaction).

  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
ℹ️
The lifeline for DeleteCommandParser should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
ClearSequenceDiagram
Figure 7. Interactions Inside the Logic Component for the clear Command

3.4. Model component (Pang Kim Jin)

ModelClassDiagram
Figure 8. Structure of the Model Component

API : Model.java

The Model,

  • stores an ArrayList which contains the user’s command history.

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

  • stores the ExpenseLa data.

  • stores the GlobalData which contains the recurring budget, transactions, total balance, and last updated date.

  • stores a MonthlyData object which contains budget, expense, and income information set by the user.

  • stores a ToggleView object that represents the nature of transaction information displayed to the user.

  • stores a Filter object which represents the filter on the transactions as set by the user

  • stores TransactionList which contains the list of all transactions

  • exposes an unmodifiable ObservableList<Transaction> 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.

3.5. Storage component (BenjaminFheng)

StorageClassDiagram
Figure 9. Structure of the Storage Component

API : Storage.java

The Storage component,

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

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

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

3.6. Common classes

Classes used by multiple components are in the seedu.ExpenseLa.commons package.

4. Implementation

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

4.1. Adding transactions (BenjaminFheng)

We allow users to add Expense/Income transactions into ExpenseLa which denotes a positive or negative inflow of money. This section shows how we handle these requests from the user.

4.1.1. Implementation

We store every single Transaction added by the user into an ObservableList<Transaction>, which is a list object in TransactionList. We used an ObservableList to easily reflect changes to the list by any other component of ExpenseLa using the list.

These are the ways of implementing either a positive or negative Transaction:

  • Adding an expense (negative transaction): add

  • Adding an income (positive transaction): add i/

These two commands will indicate whether the transaction is positive or negative.

Indicating whether it is a recurring transaction or not will depend if rc/ is present in the input

When inserting a new Expense/Income, the AddCommandParser will determine which object to initialise depending on whether the "i/" CLI syntax is present. Afterwhich, the AddCommandParser will parse the values of the transaction depending on whether the respective CLI Syntaxes are present.

  • Name is parsed by AddCommandParser#parseName(ArgumentMultimap), CLI Syntax is n/.

  • Amount is parsed by AddCommandParser#parseAmount(ArgumentMultimap), CLI Syntax is a/.

  • Date is parsed by AddCommandParser#parseDate(ArgumentMultimap), CLI Syntax is d/.

  • Category is parsed by AddCommandParser#parseCategory(ArgumentMultimap), CLI Syntax is c/.

  • Remark is parsed by AddCommandParser#parseRemark(ArgumentMultimap), CLI Syntax is r/.

  • CLI Syntax "rc/" will set the transaction to be a monthly recurring transaction.

ℹ️
ArgumentMultimap is a class that stores all the parsed parameters taken from the user input.
ℹ️
Category has a set enum list of values FOOD, SHOPPING, TRANSPORT, GROCERIES, HEALTH, RECREATION, MISC, UTILITIES, INCOME.

The following sequence diagram shows how the add transaction operation works:

AddCommandSequenceDiagram

Figure 9. Sequence diagram of how adding a new Transaction is processed depending on syntax.

Transaction are normally instantiated by AddCommandParser#parse(String args), which attempts to parse the various parameters supplied in args and return either a positive or negative Transaction. The following conditions will cause a ParseException to be thrown by the parser:

  • Missing parameters

  • Incorrect syntax (i.e. missing prefix if required)

  • Illegal values in parameters (i.e. special character and symbols entered for an integer only field)

  • Multiple occurence of parameters which only expects single entry

ℹ️
Incorrect user input will display ParseException message.

We will demonstrate how a Transaction is added into ExpenseLa below:

Step 1. The user executes the command add n/Pizza a/20.5 d/2020-02-02 to insert a negative transaction with its Name set to "Pizza", its Amount set to "20.50" and the Date set to 02 Feb 2020. The input is now checked and an attempt to parse each parameter occurs:

ℹ️
Category is set to default category MISC

Since the user input is valid, the Transaction is successfully created and inserted into the transaction list. The transaction list now contains 1 Transaction object.

AddTransactionToList1

Step 2. The user executes add i/ n/Salary a/3000 d/2020-02-03 r/Monthly Salary c/income rc/ to indicate his monthly pay, to insert a positive Transaction.

ℹ️
"rc/" CLI Syntax will set the transaction to be a recurring transaction.

Again, since the input is valid, the positive Transaction is successfully added into the transaction list. The transaction list now contains 2 Transaction objects.

AddTransactionToList2

The following activity diagram summarizes what happens when the user executes a command to add a new Transaction:

AddTransactionActivityDiagram
Figure 10. Activity diagram of adding a Transaction into the transaction list.

4.1.2. Design considerations

There are many different ways to implement how a transaction is added into ExpenseLa. In this section, we will justify why we chose to implement it the way we did.

Aspect: Differentiating between positive and negative transactions.
  • Alternative 1: (current choice): Adding a simple "i/" syntax in the input to differentiate between positive and negative transactions

    • Pros: Increases the speed and simplicity of adding a positive or negative transaction. Updating balance in MonthlyData information only requires an iteration through all the transaction amounts in transaction list for calculation.

    • Cons: Transactions may not be easily distinguishable as positive or negative transactions on first sight, and may only be distinguished when the transaction amount is inspected. This may cause slower processing times when extracting all only positive or only negative transactions.

  • Alternative 2: Having separate classes for positive and negative** transactions.

    • Pros: Maintains an intuitive design: NegativeTransaction deducts money and PositiveTransaction increases money.

    • Cons: May incur significant overhead since it is likely that both NegativeTransaction and PositiveTransaction will have very similar methods.

Alternative 1 was chosen because we want the application to be as simple and quick as possible to indicate positive and negative transactions. We focused on separating the data between months so that the analytics function could calculate data faster.

Aspect: Managing how RecurringTransactions are handled.
  • Alternative 1: (current choice): Adding a simple "rc/" syntax in the input to differentiate between recurring and non-recurring transactions. "rc/" is quickly parsed by the AddCommandParser and the transaction is labelled as recurring, which is added to RecurringTransactionsList before the command is added to the TransactionsList in Model.

    • Pros: "rc/" is quick and intuitive to indicate during input. Almost no hassle to add the information to RecurringTransactionsList.

    • Cons: Inability for existing transactions to set as recurring transactions.

  • Alternative 2: Create a separate command to indicate which transactions are recurring and the range of when it recurs.

    • Pros: Transactions are more customizable for users. Users are able to set recurring transactions to repeat over required months.

    • Cons: Users may easily lose track of the transactions that are recurring which will affect their monthly budgeting.

Alternative 1 was chosen because we want to again keep the recurring transactions intuitive and simple. Users can clear their recurring transaction lists whenever they become invalid, and input the new recurring transactions whenever there are changes

4.2. Editing transactions (BenjaminFheng)

The edit functionality modifies details of a specified Transaction in the existing list and saves modifications to the external storage file.

4.2.1. Implementation

Edit mechanism utilizes Logic operations with the EditCommand class in place of Command, and a unique EditCommandParser class. The following methods are the implementation for edit operations:

  • EditCommandParser#parse() - Parses the user’s input via the index of the transaction and creates an EditCommand to execute the command.

  • EditCommand#execute() - Modifies the Transaction in Model with new details and returns a CommandResult.

  • TransactionList#setTransaction(Transaction target, Transaction editedTransaction) - Sets the modified Transaction to its correct position in the existing TransactionList.

Command example: edit 1 n/[NAME] a/[AMOUNT] r/[REMARK] will edit the name, amount and remark of transaction of index 1 with the respective inputs.

Below is an example usage scenario for editing a transaction and explanation of how the edit mechanism behaves at each step:

Step 1. The user starts up the application with an initial list loaded from a sample transaction list.

editCommand1
Initial transaction list

Step 2. The user inputs edit 1 n/Laksa Noodle a/6.00 to edit the transaction (with index 1) name to "Laksa Noodle" and value to "6". Input is parsed by EditCommandParser#parse() which creates an EditCommand.

Step 3. EditCommand#execute() creates a new transaction that reflects the changes and gets the index of current transaction to be edited.

editCommand2
New edited transaction in transaction list

Step 4. EditCommand#execute() replaces original transaction in the list with the eddited transaction.

The following activity diagram gives an overview of what ExpenseLa shows the user when executing edit command:

EditActivityDiagram
Figure 11. Activity diagram for execution of edit command

4.2.2. Design considerations

This subsection explores some alternative designs considered for certain aspects of the feature’s implementation.

Aspect: Modifying details of a transaction
  • Alternative 1 (current choice): Replace the values of the original transaction with new editd values.

    • Pros: Can easily check for inputs that result in no changes.

    • Cons: May incur overhead when creating new instance of Transaction.

  • Alternative 2: Modify the transaction directly using setter methods.

    • Pros: Easy to implement and efficient.

    • Cons: Modifying transactions violates the immutability principle, possibly resulting in bugs for UI or accessing modified transaction fields.

Alternative 1 chosen to maintain better coding practices and maintain immutability of transactions for the entire project. Overhead of creating new Transaction is insignificant due to relatively small object size.

Aspect: Edit transaction by getting it’s index or by unique transaction ID
  • Alternative 1 (current choice): Get the index of original transaction in the transactions list and edit its values in the list.

    • Pros: High certainty of obtaining the correct transaction, editing its values and replacing the same index in the transaction list.

    • Cons: If there are too many transactions in the list, it might be tedious to obtain the transaction index by scrolling.

  • Alternative 2: Iterate through the transactions list and edit the transaction with the correct transaction ID.

    • Pros: Virtually impossible to not be able to distinguish between similar transactions of different IDs.

    • Cons: Transactions ID would cause significant overhead when looking into each transaction for it’s ID.

4.3. Setting budgets (Hubert Halim)

We allow the user to maintain a Budget for the current month and subsequent months. This section details how ExpenseLa handles requests made by the user who is trying to set a budget both for a one time and recurring budget. Budget is contained inside MonthlyData object along with Expense and Income and application only has 1 MonthlyData object for the current month. object looks like:

BudgetClassDiagram

If user decides to create a recurring budget, there’ll be additional step of updating the recurringBudget variable in GlobalData. BudgetCommand in addition to modifying Budget in MonthlyData, it will also modify recurringBudget in GlobalData.

GlobalDataClassDiagram

4.3.1. Implementation

Whenever the user attempts to set a new Budget, ExpenseLa will create a new MonthlyData object with the given amount. The application will then call ModelManager#setMonthlyData(MonthlyData toSet). During the creation of the new MonthlyData, the Budget class will internally check if the budget amount is valid.

We will demonstrate what happens at the back-end whenever the user sets a budget:

Case 1. The user wishes to set their budget to $1500, non-recurring. They execute the command: budget b/1500. The user’s entry is checked by BudgetCommandParser#parse() and an attempt to parse each parameter occurs:

  • Budget is parsed by ParseUtil#parseBudget(ArgumentMultimap)

  • rc/ prefix does not exist, so it is not recurring

ℹ️
ArgumentMultimap is a class that stores all the parsed parameters taken from the user input.

Since the user input is valid, the Budget is successfully created and inserted into a newly created MonthlyData.

Case 2. The user made a typo when setting their budget. They execute the command budget b/1500. The user’s entry is checked by BudgetCommandParser#parse() and an attempt to parse each parameter occurs:

  • Budget is parsed by ParseUtil#parseBudget(ArgumentMultimap)

Budget class then is attempted to be created with the parsed budget amount in the constructor. Internally Budget will do a validity check using Regex and throw a ParseExection since amount is not valid.

Case 3. The user wishes to set their budget to $1500, recurring. They execute the command: budget b/1500 rc/. The user’s entry is checked by BudgetCommandParser#Parse() and an attempt to parse each parameter occurs:

  • Budget is parsed by ParseUtil#parseBudget(ArgumentMultimap)

  • rc/ prefix exists, so it is recurring

Since the user input is valid, the Budget is successfully created and inserted into a newly created MonthlyData. BudgetCommand will then modify GlobalData in Model by calling Logic#setGlobalData. RecurringBudget value in GlobalData is now set to the new Budget

The sequence diagram below depicts what was just elaborated:

SetBudgetSequenceDiagram
Figure 12. Sequence diagram showing how a Budget is set
SetBudgetActivityDiagram
Figure 13. Activity diagram showing how a Budget is set

4.3.2. Design considerations

We have considered various ways as to how Budget should be stored in ExpenseLa. In this section, we will explain the rationale on our course of actions.

Aspect: Make Budget a part of a bigger class called MonthlyData
  • Alternative 1 (current choice): Budget is a part of MonthlyData and any Budget operations is through MonthlyData

    • Pros: Easier to handle Budget together with other MonthlyData objects and all data inside is synchronised as it is handled by a single object.

    • Cons: Overhead when modifying Budget as to maintain immutability, a new MonthlyData object has to be created.

  • Alternative 2: Budget should be an independent class with a direct reference in ExpenseLa.

    • Pros: More freedom and efficiency in doing modifications on Budget

    • Cons: Need to maintain more references for all different objects.

Again, we went with alternative 1 because it is easier to view Budget along with the other MonthlyData components as a collective. And easier to just handle 1 reference in ExpenseLa.

4.4. Command History navigation (Hubert Halim)

Users can navigate to previous commands by pressing the up or down button on the keyboard. Only successful commands are stored in the CommandHistory list and only a maximum of 50 commands can be stored at a time.

4.4.1. Implementation

Every time the user key in a command and press kbd:[Enter], CommandBox#handleCommandEntered method will be called. The method will attempt to execute the command by calling CommandExecutor#execute method. That method throws an error if command is invalid. So if the command is valid, the CommandBox#handleCommandEntered method will call Logic#deleteCommandFromHistory to delete the command if it exists in the current command history. It will then call Logic#addToCommandhistory to add the command to the command history as its latest entry. Both commands for add and delete takes in an integer variable called offset. This variable is maintained by CommandBox and determines which command the user is currently at in the command history. Offset starts from -1 indicating CommandBox is empty and resets to -1 every time a successful command is entered

Command History is an array list that resides in ModelManager object. It can be accessed through Logic by calling

The diagrams below depicts what was just elaborated:

CommandHistorySequenceDiagram
Figure 14. Sequence diagram showing what happens when user enters a Command
CommandHistoryActivityDiagram
Figure 15. Activity diagram showing what happens when user enters a Command
CommandHistoryNavigateSequenceDiagram
Figure 16. Sequence diagram showing what happens when presses Up/Down button
CommandHistoryNavigateActivityDiagram
Figure 17. Activity diagram showing what happens when presses Up/Down button

4.4.2. Design considerations

We have considered various ways as to how to implement CommandHistory to support navigation to previous commands Since we need to capture keyboard events when user press the keyboard, we decided to implement the event listener and handler in CommandBox component as it is more convenient because when a keyboard event is captured, the app can straight away modify the TextField in CommandBox. Since the event when user enter a command is also handled in CommandBox and we only store successful commands in CommandHistory, we wait for execution of the Command by CommandExecutor, if it is successful, the String for the command is added to, otherwise due to the error thrown and caught somewhere else, the command is not stored.

4.5. Filtering Transactions (Pang Kim Jin)

The Filter command allows the user to bring up a list of Transaction, and filter it by either category, month, or both at the same time. This is implemented by using a predicate for category and another predicate for month, both of which inheriting from Predicate<Transaction> to filter the Transaction.

4.5.1. Implementation

FilterCommand is instantiated by FilterCommandParser#parse(String args) method, which parses the arguments supplied in the user command to return a FilterCommand object.

The below sequence diagram depicts the interactions within the Logic component for the execute("filter c/FOOD m/2020-04") call: FilterSequenceDiagram

The below scenario shows a typical usage of the filter feature:

Step 1: Upon application launch, the filter for all categories and the current month is automatically applied. Filter SS 1

Step 2: User executes the command filter c/food m/2020-02 to bring up transactions in the category "FOOD" for the month of February 2020. (Note: The command in the command line disappears upon hitting Enter, the command in the command line is purely for illustration purposes). Filter SS 2

Step 3: The FilterCommandParser#parse(String args) parses the arguments.

Step 4: Since user input is correct and the arguments are parsed, a new FilterCommand object is created by the FilterCommandParser.

Step 5: The FilterCommand object will use a Predicate<Transaction> based on the specified category, month, or both, to filter the list of transactions.

Step 6: The list of filtered transactions is brought up. The filter category and month UI will also update accordingly to show the category and month that the transactions are filtered by.

The below activity diagram gives an overview of the command execution: FilterActivityDiagram

4.5.2. Design Considerations

Aspect: Using Predicate to improve extendability of the Filter feature in the future.

  • Alternative 1 (current choice): Create a new Predicate<Transaction> for each new filter type

    • Pros: Greater flexibility can be provided for each filter type since different filter types have different requirements (e.g. Month vs Category)

    • Cons: Tedious to implement a new class for each new type of filter

  • Alternative 2: Use a single Predicate<Transaction> to filter for all filter types

    • Pros: Easy to implement

    • Cons: Prone to being inflexible for extensions

We decided to go with Alternative 1 since the current filter feature supports increasing the number of filter types - on top of the current category and month filters. Despite having a different Predicate for each filter type, we use a composed Predicate comprising of both Predicate s, making it much easier to support extensions to this feature.

4.5.3. Proposed Extension

We plan to enhance the filter feature to support other arguments in the command to filter by different types such as price range or date range. This allows the user to have greater flexibility and have a better understanding of his/her expenses.

The design consideration mentioned earlier hence facilitates this proposed extension, reducing the difficulty of such a future implementation.

4.6. Chart Analytics (Choi Min Suk + Pang Kim Jin)

The toggleview command allows the user to switch between viewing the list of 'Transactions' and viewing an analysis of his expenditure.

In the expenditure analysis view we have a bar chart to show expenditure breakdown by date as well as a pie chart to show expenditure breakdown by category.

4.6.1. Implementation

MainWindow decides whether to show a list of transactions or chart analysis based on ToggleView#isViewList, by accessing Logic#getToggleView().

Here is a Class Diagram for the implementation of ToggleView:

ToggleViewClassDiagram

The ToggleView mechanism utilizes Logic operations with the ToggleViewCommand class in place of Command. The following methods are concrete implementations for the toggle operation:

  • ToggleViewCommand#execute() - Modifies the ToggleView in Model to view list of transactions or view analytics, and returns a CommandResult (Step 4 of Logic).

  • ToggleView#switchIsViewList() - Modifies the boolean value isViewList in ToggleView to the negation of it’s current value.

    • This ToggleView is wrapped in ExpenseLa and its switchIsViewList() is called through ExpenseLa#switchToggleView().

    • ExpenseLa#switchToggleView() is exposed in the Model interface as Model#switchToggleView().

The following sequence diagram illustrates toggleview command execution:

ToggleViewSequenceDiagram
Figure 18. Sequence diagram showing execution of toggleview
Example of usage

Given next is an example and explanation of how the ToggleView mechanism behaves at each step:

Step 1. The user starts up the application with an initial list loaded from external storage file. The diagram here depicts the example list used throughout this scenario.

ToggleViewStep1
Example list on startup

Step 2. The user inputs toggleview to change the view from list of transactions to chart analysis.

ToggleViewStep2
User input for toggle view

Step 3. ToggleViewCommand#execute() switches isViewList of ToggleView from true to false.

ToggleViewStep3
isViewList of ToggleView switched from ToggleViewCommand#execute()

Step 4. MainWindow#executeCommand() checks the boolean value of isViewList in ToggleView, which is false, and displays chart analysis.

ToggleViewStep4
Switched from showing list of transactions to chart analysis

Step 5. User inputs toggleview again to change view back to list of transactions.

ToggleViewStep2
ToggleViewStep1
Switched from showing list of transactions to chart analysis

Step 6. User can set filter to a certain month to view a different kind of bar chart.

ToggleViewStep6
Example of stacked bar chart of expenditure for a certain month

The following code snippet from MainWindow#executeCommand() checking of the boolean value of isViewList in ToggleView, and deciding whether to show a list of transactions of chart analysis, and what bar graph to show:

MainWindow#executeCommand()
// The if else statement checks the value of isViewList from ToggleView
if (logic.getToggleView().getIsViewList()) {
    // Creates ui for list of transactions
    transactionListPanel = new TransactionListPanel(logic.getFilteredTransactionList());
    transactionListAndChartAnalyticsPanelPlaceholder.getChildren().add(transactionListPanel.getRoot());
} else {
    // Creates ui for chart analysis
    // Calls logic.getIsFilterMonth() to check if the filter is set to a specific month or not, to decide which bar graph to build
    chartAnalyticsPanel = new ChartAnalyticsPanel(logic.getFilteredTransactionList(), logic.getIsFilterMonth());
    transactionListAndChartAnalyticsPanelPlaceholder.getChildren().add(chartAnalyticsPanel.getRoot());
}
Execution shown to user

The following activity diagram gives an overview of what ExpenseLa shows the user when executing toggleview command:

ToggleViewActivityDiagram
Figure 19. Activity diagram for execution of toggleview command

4.6.2. Design considerations

This section shows some of the design considerations taken when implementing the undo and redo features.

Aspect: Design used to implement toggelview feature
  • Alternative 1 (current choice): Create a ToggleView Class to keep a boolean value of isViewList to keep track of showing list of transactions or chart analysis.

    • Pros: Easily extendable next time to accommodate more different kind of views by changing boolean to possibly enum.

    • Pros: Easy to implement functions to change values in ToggleView object, which allows easy extendability next time also.

    • Cons: Needs to implement many functions through Logic and Model.

  • Alternative 2: Create a boolean value in Model to track whether to show list of transactions or chart analysis.

    • Pros: Easy implementation and checking of boolean value by MainWindow to check which view to show.

    • Cons: Not extendable next time when trying to accommodate different kind of views.

Alternative 1 was chosen because it is easily extendable, in case we want to improve or develop on the feature in the future. It also follows better OOP principles, making the code much neater and understandable.

4.7. Export (Choi Min Suk)

The export command allows user to export currently filtered transactions to a csv file, in case he would like to use the data for his own analysis.

4.7.1. Implementation

ExportCommand is instantiated by ExpenseLaParser#parseCommand(String userInput), which attempts to split the userInput into the command word and its parameters. Since ExportCommand does not require any arguments, it is instantiated directly.

ExportCommand obtains the filtered list of transaction to export using Model#getFilteredTransactionList(), which then attempts to create a csv file in the current directory using the attributes of each transaction.

The sequence diagram below shows how the execution of export is like:

ExportSequenceDiagram
Figure 20. Sequence diagram of how export command is applied at the back-end.

The following conditions will cause a CommandException to be thrown by the command:

  1. Empty filtered transaction list

  2. Failure in creating the file

  3. Failure in writing to the file (Possibly due to the directory changing while the command is being executed)

The image below shows how the csv file looks when user executes export command successfully:

ExportCsv
Figure 21. List of transactions in a csv file opened in Microsoft Excel

4.7.2. Proposed extension

In the near future, we plan to enhance the export feature. We want to improve the export command to take in view as a possible argument, thus allowing the user to choose between exporting list of transactions or the chart analysis. The user can use the chart analysis generated for visual presentations, especially if the expense tracker is for a business.

The image below shows how a possible future implementation of this feature could look like:

ExportChartCommand
Figure 22. Expected command to export chart analytics
ExportBarGraph
ExportPieChart
Figure 23. Expected images to be exported when export chartanalysis

4.8. [Proposed] Data Encryption (Pang Kim Jin)

Given the sensitive nature of the information provided by users, we would like to safeguard the information provided by our users through encryption. Naturally, the information would be encrypted and decrypted in the back-end without the user requiring to do any of the encryption, decryption, or even any knowledge of how it works.

4.8.1. Proposed Implementation

We thus propose a Keystore module to contain authorisation certificates or public key certificates interacting with the Logic and Storage modules. With this addition, the following architecture diagram gives an overview of how it would fit in:

DataEncryptionClassDiagram

The Keystore module would have a KeystoreManager which implements the following methods:

  • KeystoreManager#setCipher(Cipher cipher) - sets the Cipher for encryption usage.

  • KeystoreManager#encryptExpenseLa(ExpenseLa expenseLa) - encrypts the given ExpenseLa object with the encryption cipher set with every call to LogicManager#execute() method.

  • KeystoreManager#decryptExpenseLa(ExpenseLa expenseLa) - Decrypts the encrypted json file upon application launch.

The following class diagram provides a depiction of the above:

DataEncryptionClassDiagram2
ℹ️
KeystoreManager#encryptExpenseLa(ExpenseLa expenseLa) and KeystoreManager#decryptExpenseLa(ExpenseLa expenseLa) will be using the Advanced Encryption Standard (AES 256) encryption algorithm.

4.8.2. Design Considerations

Aspect: Encryption Algorithm

  • Alternative 1: Data Encryption Standard

    • Pros: Simpler to implement encryption and decryption

    • Cons: Weaker security, easy to brute force

  • Alternative 2 (current choice) : Advanced Encryption Standard

    • Pros: 256 bit key is exponentially more secure than DES' 56 bit key

    • Cons: Harder to implement

4.9. [Proposed] ExpenseLa Monthly Statement (Pang Kim Jin)

Similar to how banks issue a statement of account, we believe that it would be helpful to provide our users with an overview of their expenses. This statement would include the user’s balance, budget, expense, income, and transactions in a user specified time frame.The user can choose to include/exclude certain transactions based on their categories or dates.

4.9.1. Proposed Implementation

To generate the statement, we propose a StatementCommand that extends Command and works with ModelManager just like all other commands, as depicted in the following diagram:

StatementCommandClassDiagram
  • The user uses the FilterCommand to filter the list of transactions to show only the transactions with the user’s preferred category and transaction month

  • Then StatementCommand#execute() will retrieve the FilteredList of transactions and generate a Portable Document Format (PDF) file with Java’s PDFWrite API.

Below is a truncated example of the PDF ExpenseLa statement:

Statement

4.9.2. Design Considerations

Aspect: Time and Nature of Transactions to Export

  • Alternative 1 (current choice): Users get to choose when to generate their statement and which month and categories of transactions to include.

    • Pros: Users get a statement tailored according to their needs.

    • Cons: Users may forget to include certain types of transactions.

  • Alternative 2: At the end of every month, a statement of all transactions and user information is exported

    • Pros: Users get a comprehensive view of their expenses

    • Cons: Users may be overloaded with information

Ultimately we chose Alternative 1 as we prioritise our user’s freedom of choice and we understand that not all transactions may be relevant for the purposes of exporting the statement.

4.10. Logging

We are using java.util.logging package for logging. The LogsCenter class is used to manage the logging levels and logging destinations.

  • The logging level can be controlled using the logLevel setting in the configuration file (See Section 4.11, “Configuration”)

  • The Logger for a class can be obtained using LogsCenter.getLogger(Class) which will log messages according to the specified logging level

  • Currently log messages are output through: Console and to a .log file.

Logging Levels

  • SEVERE : Critical problem detected which may possibly cause the termination of the application

  • WARNING : Can continue, but with caution

  • INFO : Information showing the noteworthy actions by the App

  • FINE : Details that is not usually noteworthy but may be useful in debugging e.g. print the actual list instead of just its size

4.11. Configuration

Certain properties of the application can be controlled (e.g user prefs file location, logging level) through the configuration file (default: config.json).

5. Documentation

Refer to the guide here.

6. Testing

Refer to the guide here.

7. Dev Ops

Refer to the guide here.

Appendix A: Product Scope

Target user profile:

  • has a need to keep track of their expenses

  • prefer desktop apps over other types

  • can type fast

  • prefers typing over mouse input

  • is reasonably comfortable using CLI apps

Value proposition: efficient way to keep track of expenses and make decisions based on data and analytics provided

Appendix B: User Stories

Priorities: High (must have) - * * *, Medium (nice to have) - * *, Low (unlikely to have) - *

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

* * *

new user

see usage instructions

refer to instructions when I forget how to use the App

* * *

general user

add a new expense entry

keep track of my expenses

* * *

someone who earns income

add a new income entry

keep track of my income

* * *

high-income user

keep track of all the money I earn

make decisions on where my most lucrative source of income is

* * *

low-income user

set budget for current month

limit my expenditure for the month

* * *

low-income user

be notified by the application if i am spending too much money

be wary of overshooting my budget

* * *

consistent thrifty user

set budget for every month(recurrent budget) once

have no need to constant;y set my unchanging budget

* * *

user with varying income

be flexible with my budgets

spend more or less on certain months depending on my financial situation

* * *

visual user

be visually alerted when I spend a certain proportion of my budget

adjust my spending habit for the rest of the month

* * *

careless user

delete an expense or income entry

remove entries that I added in by mistake

* * *

forgetful user

find an entry by keyword

check if I spent money on a particular thing

* * *

spendthrift user

filter expense based on category

know if I generally spend a lot of money or only on certain months

* * *

forward-looking user

look at my spending trend by week or month

keep track of my income

* * *

user trying to save money

filter expense based on date or time period

see how much money I have spent in that time period and make better decisions

* * *

user trying to save money

view amount of budget left to spend

adjust spending habit for the rest of the month

* * *

general user

view total money I have

be able to tell how much I can spend

* * *

user trying to save money

view total expense for a particular month

decide on my future expenditures

* * *

visually analytical user

view pie chart of money spent based on category

see where I spend the most money on

* * *

visually analytical user

view bar chart of money spent based on time period

see when I spend the most money

* * *

organized user

organize my expenditure into different categories

better able to track where I am spending my money

* * *

not one-off user

all my expenditures and income to be saved

continue on from previous whenever I exit and launch back the application

* *

smart analytical user

export my expenditure and income

use the data to make my own analysis

* *

secretive user

set a password to login tp the application

prevent unwanted users from viewing my expenses

* *

businessman

have multiple accounts

manage my expenses not only for myself but my business

* *

parent

have multiple accounts

help manage my children’s expenses

* *

user with many friends

add friends in the application

help each other in their savings

* *

concerned friend

look at my friend’s spending habit

keep a lookout for their expenditure

*

user who owes people money

view the people who I owe money to

keep track of who I owe

*

user who lends people money

request payment from people who owe me money

keep track of my loans

*

sociable user

indicate when my expenditure is within the budget

share the achievement with my friends

*

lazy user

have the application make recommendations on my spending habits

Appendix C: Use Cases (Hubert Halim)

(For all use cases below, the System is the ExpenseLa and the Actor is the user, unless specified otherwise)

Use case: Delete expense

MSS

  1. User requests to list all transactions

  2. System removes all filters and show all expenses

  3. User requests to delete a specific transaction in the list

  4. System deletes the transaction

    Use case ends.

Extensions

  • 2a. The list is empty.

    Use case ends.

  • 3a. The given index is invalid.

    • 3a1. System shows an error message.

      Use case resumes at step 2.

Use case: filter transactions by category

MSS

  1. User requests to list filtered transactions

  2. System queries list of transactions

  3. Apply filter predicate to update list of filtered transactions

  4. System shows filtered list

    Use case ends.

Extensions

  • 2a. The list is empty.

    Use case ends.

  • 3a. The category given is not valid.

    • 3a1. System shows an error message.

      Use case resumes at step 2.

Use case: Set recurring monthly budget

  1. User requests to set a recurring budget of a specified amount.

  2. ExpenseLa processes the request and sets the specified amount as the budget for the current month.

  3. ExpenseLa then update the value of recurringBudget variable in GlobalData to the specified amount.

    Use case ends.

Extensions

  • 1a. The parameters specified by the user are not valid.

    • 1a1. ExpenseLa displays an invalid parameter error to the user and the monthly budget is not updated.

      Use case ends.

Use Case: Add an expense

MSS

  1. User requests to add a new expense into their expensela.

  2. The system processes the request and adds the expense transaction into the transactions list.

    Use case ends.

Extensions

  • 1a. The parameters specified by the user are not valid.

    • 1a1. The system displays an invalid parameter error to the user and the transactions list is not updated.

      Use case ends.

Use Case: Add a recurring income

MSS

  1. User requests to add a new recurring income into their expensela.

  2. The system processes the request and adds the income transaction into the transactions list.

  3. The system then add the income transaction to RecurringTransactionList in GlobalData.

    Use case ends.

Extensions

  • 1a. The parameters specified by the user are not valid.

    • 1a1. The system displays an invalid parameter error to the user and the transactions list is not updated.

      Use case ends.

Use Case: Show chart analytics view

MSS

  1. User requests to toggle to chart analytics view.

  2. The system switches the view to charts view.

    • 2a. The filter for month is set to all, bar chart displays data of the last 2 years by month.

    • 2b. The filter for month is set to a specific month, bar chart displays data by day of the week.

      Use case ends.

Use Case: Clear transaction list

MSS*

  1. User requests to clear all transactions.

  2. The system clears transaction list in ExpenseLa.

  3. The system resets MonthlyData and GlobalData

    Use case ends.

Use Case: Export to CSV file

MSS

  1. User requests to export current list being viewed to a CSV file.

  2. The system saves the transactions to that csv file named transactions.csv by the system.

Use case ends.

Extensions

  • 1a. The file does not exists.

    • 1a1. The system creates a file with the name transactions.csv.

      Use case resumes at step 2.

Use Case: Import from CSV file

MSS

  1. User requests to import transaction data from a CSV file.

  2. Import the transactions data from the file specified by the user ignoring duplicate and invalid transactions

    Use case ends.

  • 1a. The file specified by user does not exists.

    • 1a1. The system shows an error message prompting user to rectify their command.

      Use case ends.

Use Case: Find transactions whose name contains certain words

MSS

  1. User requests to list transactions whose name contains certain words.

  2. The system queries all transactions

  3. The system applies predicate to filter only transactions that contain the words specified by user

    Use case ends.

  • 2a. The list is empty.

    Use case ends.

  • 3a. The list is empty.

    Use case ends.

Appendix D: Non Functional Requirements

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

  2. Should be able to hold up to 2000 transactions(expenses and incomes) without any apparent slowdown for normal usage.

  3. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse.

{More to be added}

Appendix E: Glossary

ExpenseLa

Stands for the application that this developer guide is written for.

API

Stands for "Application Programming Interface" which simplifies programming by abstracting the underlying implementation and only exposing objects or actions the developer needs.

PlantUML

Stands for a software tool that we use to render the diagrams used in this document.

Mainstream OS

Stands for commonly used Operating Systems (OS) such as Windows, Linux, Unix, OS-X

System Administration

Stands for the field of work in which someone manages one or more systems, be they software, hardware, servers or workstations with the goal of ensuring the systems are running efficiently and effectively.

MSS

Stands for Main Success Scenario that describes the interaction for a given use case, which assumes that nothing goes wrong.

CLI Syntax

Stands for the Command Line Interface Syntax such as "a/" and "c/" that preceeds input values which act as indicators for the system to detect those values.

UI

Stands for the user interface which is the display which interacts with the user.

Appendix F: Product Survey

ExpenseLa

Author: Team Halim

Pros:

  • Very easy and quick tracking for people that prefer command line interfaces.

  • Analytics serve as a quick bird’s eye view on monthly spending.

  • Analytics breakdown of spending per category helps me gain insights on the categories which I have spent too much money on.

  • Well made prompts to warn me when I am going to exceed my monthly budget. Helps to better my budgeting practices.

Cons:

  • Takes quite a bit of time to learn all the commands.

  • It can get quite tedious to input every single transaction at any time. Could possibly automate some inputs.

Appendix G: Instructions for Manual Testing

Given below are instructions to test the app manually.

ℹ️
These instructions only provide a starting point for testers to work on; testers are expected to do more exploratory testing.

G.1. Launch and Shutdown

  1. Initial launch

    1. Download the jar file and copy into an empty folder

    2. Double-click the jar file
      Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum.

  2. Saving window preferences

    1. Resize the window to an optimum size. Move the window to a different location. Close the window.

    2. Re-launch the app by double-clicking the jar file.
      Expected: The most recent window size and location is retained.

{ more test cases …​ }

G.2. Deleting a person

  1. Deleting a person while all persons are listed

    1. Prerequisites: List all persons using the list command. Multiple persons in the list.

    2. Test case: delete 1
      Expected: First contact is deleted from the list. Details of the deleted contact shown in the status message. Timestamp in the status bar is updated.

    3. Test case: delete 0
      Expected: No person is deleted. Error details shown in the status message. Status bar remains the same.

    4. Other incorrect delete commands to try: delete, delete x (where x is larger than the list size) {give more}
      Expected: Similar to previous.

{ more test cases …​ }

G.3. Saving data

  1. Dealing with missing/corrupted data files

    1. {explain how to simulate a missing/corrupted file and the expected behavior}

{ more test cases …​ }