- 1. Introduction
- 2. Setting up
- 3. Design
- 4. Implementation
- 4.1. Adding transactions (BenjaminFheng)
- 4.2. Editing transactions (BenjaminFheng)
- 4.3. Setting budgets (Hubert Halim)
- 4.4. Command History navigation (Hubert Halim)
- 4.5. Filtering Transactions (Pang Kim Jin)
- 4.6. Chart Analytics (Choi Min Suk + Pang Kim Jin)
- 4.7. Export (Choi Min Suk)
- 4.8. [Proposed] Data Encryption (Pang Kim Jin)
- 4.9. [Proposed] ExpenseLa Monthly Statement (Pang Kim Jin)
- 4.10. Logging
- 4.11. Configuration
- 5. Documentation
- 6. Testing
- 7. Dev Ops
- Appendix A: Product Scope
- Appendix B: User Stories
- Appendix C: Use Cases (Hubert Halim)
- Appendix D: Non Functional Requirements
- Appendix E: Glossary
- Appendix F: Product Survey
- Appendix G: Instructions for Manual Testing
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.
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.
Pleas refer to the guide here.
In this section, we will be introducing the individual components that form ExpenseLa using various diagrams.
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.
|
-
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.
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.
The Sequence Diagram below shows how the components interact with each other for the scenario where the user issues the command delete 1.
The sections below give more details of each 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
Logiccomponent. -
Listens for changes to
Modeldata so that the UI can be updated with the modified data.
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:
-
Logicuses theExpenseLaParserclass 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 aTransaction). -
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.
|
ℹ️
|
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.
|
API : Model.java
The Model,
-
stores an ArrayList which contains the user’s command history.
-
stores a
UserPrefobject that represents the user’s preferences. -
stores the
ExpenseLadata. -
stores the
GlobalDatawhich contains the recurring budget, transactions, total balance, and last updated date. -
stores a
MonthlyDataobject which contains budget, expense, and income information set by the user. -
stores a
ToggleViewobject that represents the nature of transaction information displayed to the user. -
stores a
Filterobject which represents the filter on the transactions as set by the user -
stores
TransactionListwhich 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.
API : Storage.java
The Storage component,
-
can save
UserPrefobjects in json format and read it back. -
can save the
ExpenseLadata in json format and read it back. -
can save
GlobalDatadata in json format and read it back.
This section describes some noteworthy details on how certain features are implemented.
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.
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.
-
Nameis parsed byAddCommandParser#parseName(ArgumentMultimap), CLI Syntax is n/. -
Amountis parsed byAddCommandParser#parseAmount(ArgumentMultimap), CLI Syntax is a/. -
Dateis parsed byAddCommandParser#parseDate(ArgumentMultimap), CLI Syntax is d/. -
Categoryis parsed byAddCommandParser#parseCategory(ArgumentMultimap), CLI Syntax is c/. -
Remarkis parsed byAddCommandParser#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:
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.
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.
The following activity diagram summarizes what happens when the user executes a command to add a new Transaction:
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.
-
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
MonthlyDatainformation 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:
NegativeTransactiondeducts money andPositiveTransactionincreases money. -
Cons: May incur significant overhead since it is likely that both
NegativeTransactionandPositiveTransactionwill 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.
-
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
The edit functionality modifies details of a specified Transaction in the existing list and saves
modifications to the external storage file.
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 anEditCommandto execute the command. -
EditCommand#execute()- Modifies theTransactioninModelwith new details and returns aCommandResult. -
TransactionList#setTransaction(Transaction target, Transaction editedTransaction)- Sets the modifiedTransactionto its correct position in the existingTransactionList.
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.
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.
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:
This subsection explores some alternative designs considered for certain aspects of the feature’s implementation.
-
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.
-
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.
-
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:
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.
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:
-
Budgetis parsed byParseUtil#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:
-
Budgetis parsed byParseUtil#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:
-
Budgetis parsed byParseUtil#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:
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.
-
Alternative 1 (current choice):
Budgetis a part ofMonthlyDataand anyBudgetoperations is throughMonthlyData-
Pros: Easier to handle
Budgettogether with otherMonthlyDataobjects and all data inside is synchronised as it is handled by a single object. -
Cons: Overhead when modifying
Budgetas to maintain immutability, a newMonthlyDataobject has to be created.
-
-
Alternative 2:
Budgetshould be an independent class with a direct reference inExpenseLa.-
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.
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.
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:
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.
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.
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:

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.
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).
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.
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.
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.
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.
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:
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 theToggleViewinModelto view list of transactions or view analytics, and returns aCommandResult(Step 4 of Logic). -
ToggleView#switchIsViewList()- Modifies the boolean valueisViewListinToggleViewto the negation of it’s current value.-
This
ToggleViewis wrapped inExpenseLaand itsswitchIsViewList()is called throughExpenseLa#switchToggleView(). -
ExpenseLa#switchToggleView()is exposed in theModelinterface asModel#switchToggleView().
-
The following sequence diagram illustrates toggleview command execution:
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.
Step 2. The user inputs toggleview to change the view from list of transactions to chart analysis.
Step 3. ToggleViewCommand#execute() switches isViewList of ToggleView from true to false.
Step 4. MainWindow#executeCommand() checks the boolean value of isViewList in ToggleView, which is false,
and displays chart analysis.
Step 5. User inputs toggleview again to change view back to list of transactions.
Step 6. User can set filter to a certain month to view a different kind of bar chart.
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:
// 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());
}This section shows some of the design considerations taken when implementing the undo and redo features.
-
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
ToggleViewobject, which allows easy extendability next time also. -
Cons: Needs to implement many functions through
LogicandModel.
-
-
Alternative 2: Create a boolean value in
Modelto track whether to show list of transactions or chart analysis.-
Pros: Easy implementation and checking of boolean value by
MainWindowto 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.
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.
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:
The following conditions will cause a CommandException to be thrown by the command:
-
Empty filtered transaction list
-
Failure in creating the file
-
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:
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:
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.
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:
The Keystore module would have a KeystoreManager which implements the following methods:
-
KeystoreManager#setCipher(Cipher cipher)- sets theCipherfor encryption usage. -
KeystoreManager#encryptExpenseLa(ExpenseLa expenseLa)- encrypts the givenExpenseLaobject with the encryption cipher set with every call toLogicManager#execute()method. -
KeystoreManager#decryptExpenseLa(ExpenseLa expenseLa)- Decrypts the encrypted json file upon application launch.
The following class diagram provides a depiction of the above:
|
ℹ️
|
KeystoreManager#encryptExpenseLa(ExpenseLa expenseLa) and KeystoreManager#decryptExpenseLa(ExpenseLa expenseLa)
will be using the Advanced Encryption Standard (AES 256) encryption algorithm.
|
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
-
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.
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:
-
The user uses the
FilterCommandto filter the list of transactions to show only the transactions with the user’s preferred category and transaction month -
Then
StatementCommand#execute()will retrieve theFilteredListof transactions and generate a Portable Document Format (PDF) file with Java’s PDFWrite API.
Below is a truncated example of the PDF ExpenseLa statement:
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.
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
logLevelsetting in the configuration file (See Section 4.11, “Configuration”) -
The
Loggerfor a class can be obtained usingLogsCenter.getLogger(Class)which will log messages according to the specified logging level -
Currently log messages are output through:
Consoleand to a.logfile.
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
Refer to the guide here.
Refer to the guide here.
Refer to the guide here.
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
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 |
(For all use cases below, the System is the ExpenseLa and the Actor is the user, unless specified otherwise)
MSS
-
User requests to list all transactions
-
System removes all filters and show all expenses
-
User requests to delete a specific transaction in the list
-
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.
-
MSS
-
User requests to list filtered transactions
-
System queries list of transactions
-
Apply filter predicate to update list of filtered transactions
-
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.
-
-
User requests to set a recurring budget of a specified amount.
-
ExpenseLaprocesses the request and sets the specified amount as the budget for the current month. -
ExpenseLathen 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.
ExpenseLadisplays an invalid parameter error to the user and the monthly budget is not updated.Use case ends.
-
MSS
-
User requests to add a new expense into their expensela.
-
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.
-
MSS
-
User requests to add a new recurring income into their expensela.
-
The system processes the request and adds the income transaction into the transactions list.
-
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.
-
MSS
-
User requests to toggle to chart analytics view.
-
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.
-
MSS*
-
User requests to clear all transactions.
-
The system clears transaction list in ExpenseLa.
-
The system resets MonthlyData and GlobalData
Use case ends.
MSS
-
User requests to export current list being viewed to a CSV file.
-
The system saves the transactions to that csv file named
transactions.csvby 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.
-
MSS
-
User requests to import transaction data from a CSV file.
-
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.
-
MSS
-
User requests to list transactions whose name contains certain words.
-
The system queries all transactions
-
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.
-
Should work on any mainstream OS as long as it has Java
11or above installed. -
Should be able to hold up to 2000 transactions(expenses and incomes) without any apparent slowdown for normal usage.
-
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}
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.
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. |
-
Initial launch
-
Download the jar file and copy into an empty folder
-
Double-click the jar file
Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum.
-
-
Saving window preferences
-
Resize the window to an optimum size. Move the window to a different location. Close the window.
-
Re-launch the app by double-clicking the jar file.
Expected: The most recent window size and location is retained.
-
{ more test cases … }
-
Deleting a person while all persons are listed
-
Prerequisites: List all persons using the
listcommand. Multiple persons in the list. -
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. -
Test case:
delete 0
Expected: No person is deleted. Error details shown in the status message. Status bar remains the same. -
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 … }



































