diff --git a/.gitignore b/.gitignore
index f69985ef1f..a9a3d4cd12 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,3 +15,5 @@ bin/
/text-ui-test/ACTUAL.txt
text-ui-test/EXPECTED-UNIX.TXT
+data.txt
+/src/main/java/META-INF
diff --git a/README.md b/README.md
index 46933fbeb2..1576e97901 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# Duke project template
-This is a project template for a greenfield Java project. It's named after the Java mascot _Duke_. Given below are instructions on how to use it.
+This is a project template for a greenfield Java project. It's named after the Java mascot _Duke_. Given below are instructions on how to use it
## Setting up in Intellij
diff --git a/build.gradle b/build.gradle
index b0c5528fb5..19c34eef0d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -29,7 +29,7 @@ test {
}
application {
- mainClassName = "seedu.duke.Duke"
+ mainClassName = "seedu.mindmymoney.MindMyMoney"
}
shadowJar {
@@ -43,4 +43,5 @@ checkstyle {
run{
standardInput = System.in
+ enableAssertions = true
}
diff --git a/docs/AboutUs.md b/docs/AboutUs.md
index 0f072953ea..d515fc06c4 100644
--- a/docs/AboutUs.md
+++ b/docs/AboutUs.md
@@ -1,9 +1,9 @@
# About us
-Display | Name | Github Profile | Portfolio
---------|:----:|:--------------:|:---------:
-![](https://via.placeholder.com/100.png?text=Photo) | John Doe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
-![](https://via.placeholder.com/100.png?text=Photo) | Don Joe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
-![](https://via.placeholder.com/100.png?text=Photo) | Ron John | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
-![](https://via.placeholder.com/100.png?text=Photo) | John Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
-![](https://via.placeholder.com/100.png?text=Photo) | Don Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
+| Display | Name | Github Profile | Portfolio |
+|:--------------------------------------------------:|:---------------------|:-------------------------------------------:|:-------------------------------------:|
+| ![](./images/Dan_Profile_Picture.png) | Dan Baterisna | [Github](https://github.com/danbaterisna) | [Portfolio](./team/danbaterisna.md) |
+| ![](./images/Glendon_Profile_Picture.png) | Ng Zhao Zhi, Glendon | [Github](https://github.com/GlendonNotGlen) | [Portfolio](./team/glendonnotglen.md) |
+| ![](./images/LimJieRui_Profile_Picture(Final).png) | Lim Jie Rui | [Github](https://github.com/limjierui) | [Portfolio](./team/limjierui.md) |
+| ![](./images/KitHan_Profile_Picture.png) | Seah Kit Han | [Github](https://github.com/khseah) | [Portfolio](./team/khseah.md) |
+| ![](./images/Sean_Profile_Picture.png) | Sean Ho Wen Bin | [Github](https://github.com/SeanHoWB) | [Portfolio](./team/seanhowb.md) |
diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md
index 64e1f0ed2b..105c52cf5b 100644
--- a/docs/DeveloperGuide.md
+++ b/docs/DeveloperGuide.md
@@ -1,38 +1,906 @@
-# Developer Guide
+# MindMyMoney Developer Guide
-## Acknowledgements
+## Content Page
+* [Content Page](#content-page)
+* [Introduction](#introduction)
+ * [Purpose](#purpose)
+ * [Acknowledgements](#acknowledgements)
+ * [Using the Developer Guide](#using-the-developer-guide)
+* [Design](#design)
+ * [Architecture Overview](#architecture-overview)
+ * [Component Overview ](#component-overview )
+ * [UI component](#ui-component)
+ * [Parser component](#parser-component)
+ * [Command component](#command-component)
+ * [Storage component](#storage-component)
+* [Implementation](#implementation)
+ * [Add Command](#add-command)
+ * [Add Expenditure](#add-expenditure-e)
+ * [Add Credit Card](#add-credit-card-cc)
+ * [Add Income](#add-income-i)
+ * [AddCommand Design Considerations](#addcommand-design-considerations)
+ * [CalculateInput Command](#calculateinputcommand-feature)
+ * [CalculateInputCommand Design Considerations](#calculateinputcommand-design-considerations)
+ * [List Command](#list-command)
+ * [List Expenditure](#list-expenditure-e)
+ * [List Credit Card](#list-credit-card-cc)
+ * [List Income](#list-income-i)
+ * [ListCommand Design Considerations](#list-command-design-considerations)
+ * [Delete Command](#delete-command)
+ * [Delete Expenditure](#delete-expenditure-e)
+ * [Delete Credit Card](#delete-credit-card-cc)
+ * [Delete Income](#delete-income-i)
+ * [DeleteCommand Design Considerations](#delete-command-design-considerations)
+ * [Update Command](#update-command)
+ * [Update Expenditure](#update-expenditure-e)
+ * [Update Credit Card](#update-credit-card-cc)
+ * [Update Income](#update-income-i)
+ * [UpdateCommand Design Considerations](#update-command-design-considerations)
+ * [Storage](#storage)
+ * [Loading](#loading)
+ * [Saving](#saving)
+ * [StorageCommand Design Considerations](#storage-design-considerations)
+* [Appendix Requirements](#appendix-requirements)
+ * [Product scope](#product-scope)
+ * [User Stories](#user-stories)
+ * [Non-Functional Requirements](#non-functional-requirements)
+ * [Glossary](#glossary)
+* [Instructions for manual testing](#instructions-for-manual-testing)
-{list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well}
+
-## Design & implementation
+## Introduction
-{Describe the design and implementation of the product. Use UML diagrams and short code snippets where applicable.}
+**MindMyMoney** (M3) is a desktop app for managing users' personal finances, optimized for use via a
+Command Line Interface (CLI). With the application, users can track their expenses across multiple payment methods,
+calculate monthly expenditure, and set financial goals. The application is targeted at students looking to start
+managing their personal finances.
+
-## Product scope
-### Target user profile
+### Purpose
+This document specifies the architecture and software design decisions for the application, MindMyMoney.
+The intended audience of this document is the developers, designers, and software testers of MindMyMoney.
-{Describe the target user profile}
+
-### Value proposition
+### Acknowledgements
+We would like to thank [AddressBook-3](https://se-education.org/addressbook-level3/) for assisting us in developing
+MindMyMoney.
-{Describe the value proposition: what problem does it solve?}
+
-## User Stories
+### Using the Developer Guide
+Along the guide you may encounter several icons. These icons will provide several useful information.
+> **💡 Note:**
+>- This tells you that there is additional information that is useful when you are using the application.
-|Version| As a ... | I want to ... | So that I can ...|
-|--------|----------|---------------|------------------|
-|v1.0|new user|see usage instructions|refer to them when I forget how to use the application|
-|v2.0|user|find a to-do item by name|locate a to-do without having to go through the entire list|
+> **⚠️Warning⚠️**
+> - This tells you that there is some **important** information you should take note of to prevent issues from arising
+ when you are using the application.
-## Non-Functional Requirements
+Click on the hyperlinks in the [content page](#content-page) to quickly navigate the developer's guide.
-{Give non-functional requirements}
+
-## Glossary
+## Design
+**MindMyMoney** is written fully in **Java 11** using Object-Oriented Programming (OOP) paradigm to help structure and
+organise the code. This enables the efficiency of future improvements and revisions.
+Data stored in the application is saved into text files locally on the user's device.
-* *glossary item* - Definition
+
+
+### Architecture Overview
+![architecture diagram](images/ArchitectureDiagramFinal.png)
+Fig 1 - Architecture Diagram for MindMyMoney
+
+The Architecture Diagram above shows the high-level design of the application. The **main components**
+consist of:
+- `MMM`: Initialises the components in the correct sequence and connects them with each other. Also holds the user's
+expenditures in memory.
+- `Ui`: The User Interface of the application and deals with interaction with the user.
+- `Parser`: Deals with making sense of user commands.
+- `Commands`: The collection of all executable commands.
+- `Storage`: Reads data from, and writes data to the hard disk.
+
+By abstracting out closely related code into classes, it allows `MMM` to deal at a higher level, without worrying
+about the lower level details. Higher cohesion is also achieved and coupling is minimized.
+
+
+
+The Sequence Diagram below shows an example of how the components interact with each other for the scenario
+where the user issues the command `add /e /pm cash /c food /d Porridge /a 3 /t 04/04/2022` to add an expenditure.
+
+![sequence_diagram](images/ComponentsSequenceDiagramFinal.png)
+
Fig 2 - Sequence Diagram showing the Add Command
+
+
+
+### Component Overview
+The major code is broken down into components for better abstraction.
+The sections below give more details for each component.
+
+
+
+### UI component
+The source code can be found in [`Ui.java`](https://github.com/AY2122S2-CS2113T-T10-4/tp/blob/master/src/main/java/seedu/mindmymoney/Ui.java)
+
+![ui_diagram](images/UiClassDiagram.png)
+
Fig 3 - Ui Class Diagram
+
+The UI component consists of a `Ui` and `PrintStrings` class.
+
+The UI component:
+- Prints the welcome banner and message on startup, as well as a financial tip from the `PrintStrings` class.
+- Prints `>` before the user's input, to help the user differentiate between their input and an output from the
+application.
+- Reads input from the user.
+
+
+
+### Parser component
+The source code can be found in [`Parser.java`](https://github.com/AY2122S2-CS2113T-T10-4/tp/blob/master/src/main/java/seedu/mindmymoney/Parser.java)
+
+![parser_diagram](images/ParserClassDiagram.png)
+
Fig 4 - Parser Class Diagram
+
+The Parser component consists of a `Parser` and `User` class. The `User` class further consists of an `ExpenditureList`,
+`CreditCardList` and `IncomeList` class, which makes use of the `Expenditure`, `CreditCard` and `Income` class respectively.
+
+The Parser component:
+- Receives user's input and splits it into the Command Type and Description using the `GeneralFunctions` class.
+- Uses the `User` class and user's input to instantiate a `Command` object based on the Command Type.
+- Returns the `Command` object that can then be executed.
+
+We pass in the `User` class to the `Command` object instead of using a global variable to ease testing. This way, we can
+add, delete and update entries in a new `User` during testing without affecting the actual `User`.
+
+
+
+### Command component
+The source can be found in [`command`](https://github.com/AY2122S2-CS2113T-T10-4/tp/blob/master/src/main/java/seedu/mindmymoney/command)
+
+![command_diagram](images/CommandClassDiagram.png)
+Fig 5 - Command Class Diagram
+
+The Command component consists of Command abstract class, `AddCommand`, `ByeCommand`, `DeleteCommand`, `HelpCommand`
+,`CalculateInputCommand`, `ListCommand`and `UpdateCommand` that extends the `Command` class.
+
+The Command component:
+- Provides all the Command classes which can be instantiated by `Parser.parseCommand()`. The Command objects can then be
+executed. Only 1 Command object can be created.
+- Includes a `Command.executeCommand()` method in each Command classes which performs the relevant command and throws
+exceptions if an error is encountered. The error is then handled.
+
+
+
+### Storage component
+The source can be found in [`Storage.java`](https://github.com/AY2122S2-CS2113T-T10-4/tp/blob/master/src/main/java/seedu/mindmymoney/Storage.java)
+
+![storage_diagram](images/StorageClassDiagram.png)
+Fig 6 - Storage Class Diagram
+
+The Storage component consists of `Storage` class.
+
+The Storage component:
+- `MMM` class initialises a `Storage` object upon start up. The `Storage` class consists of
+`Storage.load()` and `Storage.save()` methods.
+- Concurrently, `MMM` will call the `Storage.load()` method and load any data that is stored on the hard disk.
+- `MMM` calls the `Storage.save()` method and stores remaining data onto the hard disk when the program exits.
+
+
+
+## Implementation
+This section describes some noteworthy details on how certain features of MindMyMoney are implemented.
+
+
+
+### Add Command
+The source code can be found in [`AddCommand.java`](https://github.com/AY2122S2-CS2113T-T10-4/tp/blob/master/src/main/java/seedu/mindmymoney/command/AddCommand.java)
+
+The Add Command feature allows users to add expenditures, credit cards or their income using a single command.
+This provides speed and ease of use by only requiring a single line of input.
+
+The Add Command has 3 parts. These parts are differentiated by their flags:
+- Add expenditure `/e`.
+- Add credit card `/cc`.
+- Add income `/i`.
+
+The sequence diagram below shows the interactions when an `AddCommand` is executed.
+
+![add_command_sequence_diagram](images/AddCommandSequenceDiagram.png)
+Fig 7 - AddCommand Sequence Diagram
+
+Below is an example showing how the AddCommand behaves at each step.
+1. The `Parser` component parses user input and returns the new `AddCommand` object to the
+ `MindMyMoney`.
+2. `AddCommand` instantiates `addInput`, `expenditureList`, `creditCardList`, `incomeList`.
+3. The application invokes `AddCommand.executeCommand()` to execute user instruction.
+4. If user input contains `/e`, the application executes `AddCommand.addExpenditure()`.
+5. Else user input contains `/cc`, the application executes `AddCommand.addCreditCard()`.
+6. Else if user input contains `/i`, the application executes `AddCommand.addIncome()`.
+7. The application then returns to the Parser component.
+
+
+
+#### Add Expenditure `/e`
+A key functionality of MindMyMoney is the ability to add and track user expenditure. Expenditures are added through
+the `AddCommand.addExpenditure()` method, invoked when using the `/e` flag. Additional parameters `PAYMENT_METHOD`,
+`CATEGORY`, `DESCRIPTION`, `AMOUNT` and `TIME` are also required.
+
+
+![add_expenditure_command_sequence_diagram](images/AddExpenditureSequenceDiagram.png)
+Fig 8 - Add Expenditure Command Sequence Diagram
+
+The sequence diagram above shows the interactions of different classes when adding an expenditure
+to the list.
+
+1. During the execution, `AddCommand.addExpenditure()` will parse through user input to obtain the `PAYMENT_METHOD`, `CATEGORY`,
+ `DESCRIPTION`, `AMOUNT` and `TIME` fields.
+2. Once all the fields are obtained, `AddCommand.addExpenditure()` will run tests for its respective fields.
+3. The `AddCommand.addExpenditure()` object formats the `CATEGORY` and `AMOUNT` fields, and the `PAYMENT_METHOD` if it is cash.
+4. The `AddCommand.addExpenditure()` object instantiates a new `Expenditure` object with the 5 fields and adds them
+ into the `ExpenditureList`.
+5. The `AddCommand.addExpenditure()` object prints a list to show the user what it has saved.
+6. If user input for `PAYMENT_METHOD` is not cash, `AddCommand.addExpenditure()` invokes `AddCommand.updateCreditCardTotalExpenditure` to update the corresponding `creditCard.totalExpenditure`
+7. The `AddCommand.addExpenditure()` returns to `AddCommand`.
+
+
+
+#### Add Credit Card `/cc`
+MindMyMoney allows users to track their different credit cards. Credit cards are added through the `AddCommand.addCreditCard()`
+method, invoked when using the `/cc` flag. Additional parameters `CREDIT_CARD_NAME`, `CASHBACK` and `CARD_LIMIT` are also
+required.
+
+![add_credit_card_command_sequence_diagram](images/AddCreditCardSequenceDiagram.png)
+Fig 9 - Add Credit Card Command Sequence Diagram
+
+The sequence diagram above shows the interactions of different classes when adding a credit card to the list.
+
+1. During the execution, `AddCommand.addCreditCard()` will parse through user input to obtain the `CREDIT_CARD_NAME`, `CATEGORY`,
+ `CASH_BACK` and `CARD_LIMIT` fields.
+2. Once all the fields are obtained, `AddCommand.addExpenditure()` will run tests for its respective fields.
+3. The `AddCommand.addCreditCard()` object instantiates a new `CreditCard`
+ object with the aforementioned 3 fields and adds them into the `CreditCardList`.
+4. The `AddCommand.addCreditCard()` object prints a list to show the user what it has saved.
+5. The `AddCommand.addCreditCard()` returns to `AddCommand`.
+
+
+
+#### Add Income `/i`
+MindMyMoney allows users to track their sources of income. Incomes are added through the `AddCommand.addIncome()`
+method, invoked when using the `/i` flag. Additional parameters `AMOUNT` and `CATEGORY` are also required.
+
+![add_income_sequence_diagram](images/AddIncomeSequenceDiagram.png)
+
Fig 10 - Add Income Sequence Diagram
+
+The sequence diagram above shows the interactions of different classes when adding an income to the list.
+
+1. After receiving the `AddCommand` object from `Parser`, `MMM` calls the `AddCommand.executeCommand()` method.
+2. `AddCommand.addIncome()` method is invoked as the `/i` flag is present. It parses through the user's input to obtain
+`AMOUNT` and `CATEGORY` fields. It also runs tests on these fields to ensure the inputs are valid.
+3. An `Income` object is instantiated using the aforementioned fields and is added into the `IncomeList`.
+4. Control is returned to `MMM`.
+
+
+
+#### AddCommand Design Considerations
+Aspect: How to ask user for the fields of input.
+* Alternative 1 (current choice): User is asked to put in all fields at once, separated using flags.
+ * Pros: Faster input, user can enter an expenditure using a single input.
+ * Cons: User must be able to remember all the flags and its sequence.
+
+* Alternative 2: User is asked iteratively to put in all fields, prompted by a message after each input.
+ * Pros: Beginner friendly, easily understandable, no need to remember flags.
+ * Cons: Slower, implementation when user is familiar with the application.
+
+
+
+### CalculateInputCommand feature
+The source code can be found in [`CalculateInputCommand.java`](https://github.com/AY2122S2-CS2113T-T10-4/tp/blob/master/src/main/java/seedu/mindmymoney/command/CalculateInputCommand.java)
+and [`Calculations.java`](https://github.com/AY2122S2-CS2113T-T10-4/tp/blob/master/src/main/java/seedu/mindmymoney/helper/Calculations.java)
+
+MindMyMoney allows users to view their finances in a more meaningful manner by displaying the total expenditure and breakdown of expenses in a
+bar chart format.
+
+The CalculateCommand can take in 3 different `[DATE]` fields:
+- `calculate /epm [DD/MM/YYYY]` allows user to calculate total expenditure of the specific date.
+- `calculate /epm [MM/YYYY]` allows user to calculate total expenditure of the specific month.
+- `calculate /epm [YYYY]` allows user to calculate total expenditure of the specific year.
+
+
+
+![calculate_command_sequence_diagram](images/CalculateCommandSequenceDiagramFinal.png)
+
Fig 11 - Calculate Input Command Sequence Diagram
+
+The sequence diagram above shows the interactions of different classes when calculating the expenditure.
+
+1. After receiving the `CalculateInputCommand` object from `Parser`, `MMM` calls the `CalculateInputCommand.executeCommand()` method.
+2. `GeneralFunctions.parseInput()` method is invoked to obtain the flag and date of the input.
+3. If `/epm` flag is present. It calls `Calculations.calculateExpenditure()` method to obtain the total expenditure of the date specified.
+4. During the execution of `Calculations.calculateExpenditure()`, the `GeneralFunctions.findItemInList()` is invoked to
+find the items that contain the specified date.
+5. Afterwards, `Calculations.displayCalculationBreakdown()` is invoked to show the breakdown of expenses in a bar chart format.
+6. If `/epm` flag is not present, MindMyMoneyException is thrown.
+
+
+
+#### CalculateInputCommand Design Considerations
+Aspect: How to allow users to have a better understanding of their own expenses.
+* User is required to input either the date, month or year in order to calculate their expenses.
+ * Pros: User can have a better understanding of their expenditure breakdowns by the specified time they want to look for.
+ * Cons: User is required to fill in a date, and the date must be found in the list of expenditures.
+
+* Use of bar chart to represent breakdown of expenses in the CalculateInputCommand.
+ * Pros: Users can view their overall expenses in a bar chart format, which is easier to view at one glance.
+ * Cons: Some users may not prefer to visualise their data in a bar chart format.
+
+
+
+### List Command
+The source code can be found in [`ListCommand.java`](https://github.com/AY2122S2-CS2113T-T10-4/tp/blob/master/src/main/java/seedu/mindmymoney/command/ListCommand.java)
+
+The List Command feature allows users to view their current expenditures, credit cards and incomes, using their
+respective flags:
+- List expenditure `/e`.
+- List credit card `/cc`.
+- List income `/i`.
+
+![list_command_sequence_diagram](images/ListCommandSequenceDiagram.png)
+
Fig 12 - List Command Sequence Diagram
+
+The sequence diagram above shows the interactions when a `ListCommand` is executed.
+1. After receiving the `ListCommand` object from `Parser`, `MMM` calls the `ListCommand.executeCommand()` method.
+2. If the expenses flag `/e` is present, it calls the `printExpenditureList()` method.
+3. Else if the credit card flag `/cc` is present, it calls the `printCreditCardList()` method.
+4. Else if the income flag `/i` is present, it calls the `printIncomeList()` method.
+5. Else, it throws an error, which is then handled by printing an error message to the user.
+
+
+
+#### List Expenditure `/e`
+Allows the user to view their list of current expenditures. The list is printed through the `ListCommand.printExpenditureList()`
+method, invoked when using the `/e` flag.
+
+![list_expenditure_sequence_diagram](images/ListExpenditureSequenceDiagram.png)
+
Fig 13 - List Expenditure Sequence Diagram
+
+The sequence diagram above shows the interactions when listing expenditures.
+1. After receiving the `ListCommand` object from `Parser`, `MMM` calls the `ListCommand.executeCommand()` method.
+2. `ListCommand.printExpenditureList()`method is invoked as the `/e` flag is present.
+3. If the optional parameter `{DATE}` is not present, `ListCommand.listString()` method is invoked. This loops through
+`expenditureList` and concatenates each expenditure entry into a String `listInString`.
+4. Else if `{DATE}` is present, `ListCommand.listStringWithDate()` method is invoked. This loops through `expenditureList`
+but only concatenates expenditure entries corresponding to the provided `{DATE}` into `listInString`.
+5. The `listInString` is returned which is then printed out.
+6. Control is returned to `MMM`.
+
+
+
+#### List Credit Card `/cc`
+Allows the user to view their list of current credit cards. The list is printed through the `ListCommand.printCreditCardList()`
+method, invoked when using the `/cc` flag.
+
+![list_credit_card_sequence_diagram](images/ListCreditCardSequenceDiagram.png)
+
Fig 14 - List Credit Card Sequence Diagram
+
+The sequence diagram above shows the interactions when listing credit cards.
+1. After receiving the `ListCommand` object from `Parser`, `MMM` calls the `ListCommand.executeCommand()` method.
+2. `ListCommand.printCreditCardList()`method is invoked as the `/e` flag is present.
+3. `ListCommand.creditCardListToString()` method is then invoked, which loops through the `creditCardList` and
+ concatenates each credit card into a String `listInString`.
+4. The `listInString` is returned which is then printed out.
+5. Control is returned to `MMM`.
+
+
+
+#### List Income `/i`
+Allows the user to view their list of current incomes. The list is printed through the `ListCommand.printIncomeList()`
+method, invoked when using the `/i` flag.
+
+![list_income_sequence_diagram](images/ListIncomeSequenceDiagram.png)
+
Fig 15 - List Income Sequence Diagram
+
+The sequence diagram above shows the interactions when listing incomes.
+1. After receiving the `ListCommand` object from `Parser`, `MMM` calls the `ListCommand.executeCommand()` method.
+2. `ListCommand.printIncomeList()`method is invoked as the `/i` flag is present.
+3. `ListCommand.incomeListToString()` method is then invoked, which loops through the `incomeList` and
+ concatenates each income entry into a String `listInString`.
+4. The `listInString` is returned which is then printed out.
+5. Control is returned to `MMM`.
+
+
+
+#### List Command design considerations
+Aspect: To ease testing of `ListCommand` using JUnit.
+* Alternative 1 (current choice): Abstract the conversion of `Expenditure`, `CreditCard` and `Income` to `String` in a
+separate `ListCommand.listToString()` method.
+ * Pros: Easily tested using JUnit by checking the String that the `ListCommand.listToString()` method returns.
+ * Cons: Added layer of abstraction that may be deemed redundant.
+
+* Alternative 2: Print directly in the `ListCommand.executeCommand()` method.
+ * Pros: Easily implemented with lesser lines of code.
+ * Cons: JUnit testing would require I/O redirection prior to checking the output matches expectations.
+
+
+
+
+### Delete Command
+The source code can be found in [`DeleteCommand.java`](https://github.com/AY2122S2-CS2113T-T10-4/tp/blob/master/src/main/java/seedu/mindmymoney/command/DeleteCommand.java)
+
+The List Command feature allows users to delete an entry in their current `Expenditure`, `Credit Card` or `Income` list, using their
+respective flags and followed by the index of the entry to be deleted:
+- Delete an expenditure `/e [INDEX]`.
+- Delete a credit card `/cc [INDEX]`.
+- Delete an income `/i [INDEX]`.
+
+![delete_command_sequence_diagram](images/DeleteCommandSequenceDiagram.png)
+
Fig 16 - Delete Command Sequence Diagram
+
+The sequence diagram above shows the interactions when a `DeleteCommand` is executed.
+1. After receiving the `DeleteCommand` object from `Parser`, `MMM` calls the `DeleteCommand.executeCommand()` method.
+2. If the expenses flag `/e` is present, it calls the `deleteExpenditure()` method.
+3. Else if the credit card flag `/cc` is present, it calls the `deleteCreditCard()` method.
+4. Else if the income flag `/i` is present, it calls the `deleteIncome()` method.
+5. Else, it throws an error, which is then handled by printing an error message to the user.
+
+
+
+#### Delete Expenditure `/e`
+Deletes an `Expenditure` specified by the user using the `Expenditure`'s index. The `Expenditure` is deleted through the `DeleteCommand.deleteExpenditure()`
+method, invoked when using the `/e` flag.
+
+![delete_expenditure_sequence_diagram](images/DeleteExpenditureSequenceDiagramFinal.png)
+
Fig 17 - Delete Expenditure Sequence Diagram
+
+The sequence diagram above shows the interactions when deleting an expenditure.
+1. After receiving the `DeleteCommand` object from `Parser`, `MMM` calls the `DeleteCommand.executeCommand()` method.
+2. `DeleteCommand.deleteExpenditure()`method is invoked as the `/e` flag is present.
+3. `expenditureList.get()` method is then invoked, which retrieves the `Expenditure` to be deleted.
+4. `Expenditure.getPaymentMethod()` is then used to check if the payment method was in `CASH`.
+5. For Credit Card payment methods, the amount of expenditure is deducted from `CreditCard.totalExpenditure`.
+6. Details of the deleted `Expenditure` is then printed out.
+7. Control is returned to `MMM`.
+
+
+
+#### Delete Credit Card `/cc`
+Deletes a `Credit Card` specified by the user using the `Credit Card`'s index. The `Credit Card` is deleted through the `DeleteCommand.deleteCreditCard()`
+method, invoked when using the `/cc` flag.
+
+![Delete_credit_card_sequence_diagram](images/DeleteCreditCardSequenceDiagram.png)
+
Fig 18 - Delete Credit Card Sequence Diagram
+
+The sequence diagram above shows the interactions when deleting a `Credit Card`.
+1. After receiving the `DeleteCommand` object from `Parser`, `MMM` calls the `DeleteCommand.executeCommand()` method.
+2. `DeleteCommand.deleteCreditCard()`method is invoked as the `/cc` flag is present.
+3. `creditCardList.delete()` method is then invoked, which removes the `Credit Card` specified by the user.
+4. Details of the deleted `Credit Card` is then printed out.
+5. Control is returned to `MMM`.
+
+
+
+
+#### Delete Income `/i`
+Deletes an `Income` specified by the user using the `Income`'s index. The `Income` is deleted through the `DeleteCommand.deleteIncome()`
+method, invoked when using the `/i` flag.
+
+![Delete_income_sequence_diagram](images/DeleteIncomeSequenceDiagramFinal.png)
+
Fig 19 - Delete Income Sequence Diagram
+
+The sequence diagram above shows the interactions when deleting an `Income`.
+1. After receiving the `DeleteCommand` object from `Parser`, `MMM` calls the `DeleteCommand.executeCommand()` method.
+2. `DeleteCommand.deleteIncome()`method is invoked as the `/i` flag is present.
+3. `IncomeList.delete()` method is then invoked, which removes the `Income` specified by the user.
+4. Details of the deleted `Income` is then printed out.
+5. Control is returned to `MMM`.
+
+
+
+
+#### Delete Command design considerations
+Aspect: To print deleted object from User's list.
+* Alternative 1 (current choice): Prints details of deleted `Expenditure`, `Credit Card`, or `Income`.
+ * Pros: Users can easily verify that they have correctly deleted the object as intended.
+ * Cons: Printing extraneous lines onto the Command-Line may affect user-experience and find the output overwhelming.
+
+* Alternative 2: Quietly delete the specified `Expenditure`, `Credit Card`, or `Income`.
+ * Pros: Easily implemented.
+ * Cons: Users are unable to verify that the correct object was deleted.
+
+
+
+### Update Command
+The source code can be found in [`UpdateCommand.java`](https://github.com/AY2122S2-CS2113T-T10-4/tp/blob/master/src/main/java/seedu/mindmymoney/command/UpdateCommand.java)
+
+The Add Command feature allows users to update an `Expenditure`, `Credit Card`, or `Income` using a single command.
+This provides speed and ease of use by only requiring a single line of input, when making amendments to an entry in their list.
+- Update an expenditure `/e`.
+- Update a credit card `/cc`.
+- Update an income `/i`.
+
+![update_command_sequence_diagram](images/UpdateCommandSequenceDiagram.png)
+
Fig 20 - Update Command Sequence Diagram
+
+The sequence diagram above shows the interactions when an `UpdateCommand` is executed.
+1. After receiving the `UpdateCommand` object from `Parser`, `MMM` calls the `UpdateCommand.executeCommand()` method.
+2. If the expenses flag `/e` is present, it calls the `updateExpenditure()` method.
+3. Else if the credit card flag `/cc` is present, it calls the `updateCreditCard()` method.
+4. Else if the income flag `/i` is present, it calls the `updateIncome()` method.
+5. Else, it throws an error, which is then handled by printing an error message to the user.
+
+
+
+
+#### Update Expenditure `/e`
+Updates an `Expenditure` specified by the user using the `Expenditure`'s index. The `Expenditure` is updated through the `UpdateCommand.updateExpenditure()`
+method, invoked when using the `/e` flag.
+
+![update_expenditure_sequence_diagram](images/UpdateExpenditureSequenceDiagramFinal.png)
+
Fig 21 - Update Expenditure Sequence Diagram
+
+The sequence diagram above shows the interactions when updating an `Expenditure`.
+1. During the execution, `UpdateCommand.updateExpenditure()` will parse through user input to obtain the `PAYMENT_METHOD`, `CATEGORY`, `DESCRIPTION`, `AMOUNT` and `TIME` fields.
+2. Once all the fields are obtained, `UpdateCommand.updateExpenditure()` will run tests for its respective fields.
+3. The `UpdateCommand.updateExpenditure()` object formats the `CATEGORY` and `AMOUNT` fields, and the `PAYMENT_METHOD` if it is in `CASH`.
+4. `UpdateCommand.updateExpenditure()` checks if the updated `Expenditure` is identical to the old `Expenditure` it is replacing, and throws an error if it is.
+5. If the old `Expenditure`'s `PAYMENT_METHOD` or new `Expenditure`'s `PAYMENT_METHOD` is not in `CASH`, `UpdateCommand.updateExpenditure()` invokes `UpdateCommand.updatePaymentMethod()` to update the corresponding `creditCard.totalExpenditure`.
+6. The `UpdateCommand.updateExpenditure()` object instantiates a new `Expenditure` object with the 5 fields and sets it into the `ExpenditureList`.
+7. Details of the updated `Expenditure` is printed out.
+8. Control is returned to `MMM`.
+
+
+
+#### Update Credit Card `/cc`
+Updates a `Credit Card` specified by the user using the `Credit Card`'s index. The `Credit Card` is updated through the `UpdateCommand.updateCreditCard()`
+method, invoked when using the `/cc` flag.
+
+![Update_credit_card_sequence_diagram](images/UpdateCreditCardSequenceDiagramFinal.png)
+
Fig 22 - Update Credit Card Sequence Diagram
+
+The sequence diagram above shows the interactions when updating a `Credit Card`.
+1. During the execution, `UpdateCommand.updateCreditCard()` will parse through user input to obtain the `CREDIT_CARD_NAME`, `CATEGORY`, `CASH_BACK` and `CARD_LIMIT` fields.
+2. Once all the fields are obtained, `UpdateCommand.updateCreditCard()` will run checks for its respective fields.
+3. The `UpdateCommand.updateCreditCard()` object instantiates a new `Credit Card` object with the aforementioned 3 fields and sets them into the `CreditCardList`, at the specified index.
+5. Details of the updated `Credit Card` is printed out.
+6. Control is returned to `MMM`.
+
+
+
+#### Update Income `/i`
+Updates an `Income` specified by the user using the `Income`'s index. The `Income` is updated through the `UpdateCommand.updateIncome()`
+method, invoked when using the `/i` flag.
+
+![Update_income_sequence_diagram](images/UpdateIncomeSequenceDiagramFinal.png)
+
Fig 23 - Update Income Sequence Diagram
+
+The sequence diagram above shows the interactions when updating an `Income`.
+1. After receiving the AddCommand object from Parser, MMM calls the `UpdateCommand.executeCommand()` method.
+2. `UpdateCommand.updateIncome()` method is invoked as the `/i` flag is present. It parses through the user’s input to obtain `AMOUNT` and `CATEGORY` fields. It also runs tests on these fields to ensure the inputs are valid.
+3. An `Income` object is instantiated using the aforementioned fields and is added into the `IncomeList`.
+4. Details of the updated `Income` is then printed out.
+5. Control is returned to `MMM`.
+
+
+
+#### Update Command design considerations
+Aspect: To allow updating of a similar object from User's list.
+* Alternative 1 (current choice): Throws an exception and warns the user that the updated `Expenditure`, `Credit Card`, or `Income` is similar to the object they are replacing.
+ * Pros: Users can easily verify that they have made a change in the updated object.
+ * Cons: Implementing this requires extraneous checks and increased coupling.
+
+* Alternative 2: Do not warn the user that the old object being replaced is similar to the new object.
+ * Pros: Easily implemented.
+ * Cons: Users may not be aware that the new object has no difference from the old object.
+
+
+
+### Storage
+
+Loads the user's saved information upon startup, and saves the information after every successful
+command execution.
+
+
+
+#### Loading
+
+Loads the user's saved information upon startup.
+
+![Loading_sequence_diagram](images/LoadingSequenceDiagramFinal.png)
+
+Fig 24 - Loading Sequence Diagram
+
+The sequence diagram above shows the interactions when loading user data.
+
+1. On startup, `MMM` creates a `Storage` object that loads and saves data to `data.txt`. The `Storage` object
+creates the file if it does not exist.
+2. `MMM` then calls `Storage.load()`, which initializes a `Scanner` that reads the data file.
+3. `Storage` invokes `User.deserializeFrom()`, which reads a serialized User over the `Scanner`.
+4. `User` calls `ExpenditureList.deserializeFrom()`, which returns an `ExpenditureList` read from the `Scanner`.
+5. `User` then does the same for `CreditCardList.deserializeFrom()` and `IncomeList.deserializeFrom()`, which
+return a `CreditCardList` and `IncomeList`, respectively.
+
+![Deserialize_list_sequence_diagram](images/DeserializeListSequenceDiagramFinal.png)
+
+
+Fig 25 - ExpenditureList Deserialization Sequence Diagram
+
+The above sequence diagram shows the interactions when a list of
+`MMMSerializable`s is being deserialized. Although the given diagram shows the interaction for an
+`ExpenditureList`, the interactions for `CreditCardList` and `IncomeList` are similar.
+
+1. `ExpenditureList` calls `SerializerFunctions.convertInputToList()`, which accepts a function
+that deserializes a line of input. Here, `Expenditure.deserialize()` is passed to `convertInputToList()`.
+2. For each line in the input which corresponds to an `Expenditure`, `SerializerFunctions`
+invokes `Expenditure.deserialize()` on this line, which returns an `Expenditure`. This is repeated until
+the designated terminator is read from the `Scanner`.
+3. `Expenditure` invokes `PropertyList.deserialize()` to convert the input line into a set of key-value pairs.
+Then, it makes a series of `PropertyList.getValue()` calls to obtain the values of each individual property.
+4. Once all attributes have been processed, `Expenditure.deserialize()` returns an `Expenditure`.
+5. These expenditures are aggregated into an `ExpenditureList`, which is returned
+to the `deserializeFrom()` call.
+
+#### Saving
+
+Saves user information after every successful
+command execution.
+
+![Saving_sequence_diagram](images/SavingSequenceDiagramFinal.png)
+
+
+Fig 26 - Saving Sequence Diagram
+
+The sequence diagram above shows the interactions when saving user data.
+
+1. After every command, `MMM` invokes `Storage.save()`.
+2. `Storage` invokes `User.serialize()`.
+3. `User` calls `ExpenditureList.serialize()`, which returns a `String`, representing the serialized
+`ExpenditureList`.
+4. `User` then does the same for `CreditCardList.deserializeFrom()` and `IncomeList.deserializeFrom()`, which
+ return `String`s representing a serialized `CreditCardList` and `IncomeList`, respectively
+5. `User` compiles all these into one `String`, and returns this to `Storage`.
+6. `Storage` writes the returned serialized `User` into the data file.
+
+![Serialize_list_sequence_diagram](images/SerializeListSequenceDiagramFinal.png)
+
+
+Fig 27 - ExpenditureList Serialization Sequence Diagram
+
+The above sequence diagram shows the interactions when a list of
+`MMMSerializable` is being serialized. Although the given diagram shows the interaction for an
+`ExpenditureList`, the interactions for `CreditCardList` and `IncomeList` are similar.
+
+1. `ExpenditureList` calls `SerializerFunctions.addListToStringBuilder()`, passing in an `ArrayList` of
+`Expenditure`s.
+2. For each entry in the list, `SerializerFunctions`
+ invokes `Expenditure.propetyList()` on this line, which returns an `Expenditure`. This is repeated until
+ the designated terminator is read from the `Scanner`.
+3. `Expenditure` creates a `PropertyList` to stores its properties into.
+ Then, it makes a series of `PropertyList.setValue()` calls to save each of its attributes.
+4. Once all attributes have been processed, `PropertyList.serialize()` is called, and
+this serialized `PropertyList` is returned by `Expenditure.serialize()`.
+5. These serialized expenditures are aggregated into an `String`, which is returned
+ to the `deserializeFrom()` call.
+
+#### Storage Design Considerations
+
+Aspect: When to save user data.
+* Alternative 1 (current choice): After every command.
+ * Pros: User state is still saved when program exits in ways other than a `ByeCommand`.
+ * Cons: Saving after every command may degrade performance.
+* Alternative 2: At program exit (i.e. execution of `ByeCommand`).
+ * Pros: Less overhead per command, due to not having to save.
+ * Cons: User state is not saved when program exits otherwise (e.g. due to a crash).
+
+Aspect: What format to use in saving user data.
+* Alternative 1 (current choice): Custom key-value pair format.
+ * Pros: Easy to read and modify by hand.
+ * Cons: More involved implementation.
+* Alternative 2: Java's default `java.io.Serializable` interface.
+ * Pros: Is simple to implement.
+ * Cons: Is non-human-readable, which violates the requirements of this project.
+* Alternative 3: JSON.
+ * Pros: Easy-to-read, widely established standard.
+ * Cons: Requires external libraries.
+* Alternative 4: Incremental list of changes to user data.
+ * Pros: Improved performance, due to having to save less data after every command.
+ * Cons: More difficult to modify by hand.
+
+
+
+## Appendix Requirements
+
+### Product scope
+
+**Target user profile**
+- a student beginner who wants to start managing their finances
+- possess an income
+- prefer desktop application
+- is proficient in CLI
+- can type fast
+- prefers typing to mouse interactions
+
+**Value proposition**
+Manage finances containing multiple payment methods faster than a typical mouse/GUI driven app.
+
+
+
+### User Stories
+
+| Version | As a ... | I want to ... | So that I can ... |
+|---------|----------|-------------------------------------------------------------------------|----------------------------------------------------------------------------|
+| v1.0 | new user | have a 'help' command that lists the functions of the program | view implemented features of the program |
+| v1.0 | user | create an entry for expenditure | - |
+| v1.0 | user | list existing entries | view my expenses |
+| v1.0 | user | update existing entries | edit details of entries I have previously deleted |
+| v1.0 | user | remove existing entries | delete wrongly added entries |
+| v1.0 | user | know if my command entered is invalid | not expect a successful operation with bad inputs |
+| v1.0 | user | exit the application | - |
+| v1.0 | user | see a welcome page after running the application | have a better user experience and know the application loaded successfully |
+| v2.0 | user | add a date to expenditure | track my expenditure daily, monthly or yearly |
+| v2.0 | user | add my credit cards, with the credit card limits, cashback | track my expenditure on my credit card |
+| v2.0 | user | allocate my expenditure into categories | know which main categories I spent the most on |
+| v2.0 | user | see a graph of my expenditures for each category | know the breakdown of my expenditures |
+| v2.0 | user | add my income | see if I am spending beyond my income |
+| v2.0 | user | list my income, credit card and expenditure separately | better visualise the separate lists instead of seeing all at once |
+| v2.0 | user | see the help page for my income, credit card and expenditure separately | refer to the the specific help page that I need |
+| v2.1 | user | save and load my data | I am able to close and re run the application without losing data |
+
+
+### Non Functional Requirements
+
+1. Should work on any mainstream OS as long as it has Java 11 or above installed.
+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.
+
+
+### Glossary
+
+* Mainstream OS: Windows, Linux, Unix, OS-X
## Instructions for manual testing
-{Give instructions on how to do a manual product testing e.g., how to load sample data to be used for testing}
+Given below are instructions to test the app manually.
+
+> **💡 Note:**
+>- These instructions only provide a starting point for testers to work on; testers are expected to do more exploratory testing.
+
+### Launch and shutdown
+
+1. Initial launch
+ 1. Download the jar file, and copy it into an empty folder.
+ 2. Open a terminal window whose current directory is that folder, and run `java -jar MindMyMoney.jar`.
+ Expected: Outputs a splash screen, with a daily finance tip.
+2. Shutdown
+ 1. Enter `bye` to exit the software.
+ Expected: A farewell message is printed, and the software exits gracefully.
+
+### Adding data
+
+1. Adding expenditure paid with cash
+ 1. Test case: `add /e /pm Cash /c Food /d Chicken rice /a 4.50 /t 30/03/2022`
+ Expected: Expenditure added to account. Message displaying summary of new
+ expenditure is displayed.
+ 2. Test case: Missing flag, or reordering of `/pm`, `/c`, `/d`, `/a`, `/t`
+ Expected: Add command is rejected, with an appropriate error message.
+ 3. Test case: Invalid amount or date (date in incorrect format, future date, non-positive amount, etc.)
+ Expected: Add command is rejected, with an appropriate error message.
+ 4. Test case: Other valid category (Transport, Utilities, Personal, Entertainment, Others)
+ (Note: Category is case-insensitive)
+ Expected: Expenditure added to account. Message displaying summary of new
+ expenditure is displayed.
+ 5. Test case: Invalid category
+ Expected: Add command is rejected, with an appropriate error message.
+2. Adding credit card
+ 1. Test case: `add /cc /n /cc DBS /cb 1.0 /cl 10000`
+ Expected: Credit card added to account. Message displaying summary of new
+ credit card is displayed.
+ 2. Test case: Cashback not in range [0, 100]
+ Expected: Add command is rejected, with an appropriate error message.
+ 3. Test case: Credit card limit is invalid
+ Expected: Add command is rejected, with an appropriate error message.
+ 4. Test case: Name is the same as an already existing credit card
+ Expected: Add command is rejected, with an appropriate error message.
+3. Adding income source
+ 1. Test case: `add /i /a 200 /c Allowance`
+ Expected: Income source added to account. Message displaying summary of new
+ income source is displayed.
+ 2. Test case: Other valid category (Salary, Investment, Others)
+ Expected: Income source added to account. Message displaying summary of new
+ income source is displayed.
+ 3. Test case: Invalid category
+ Expected: Add command is rejected, with an appropriate error message.
+ 4. Test case: Invalid amount (not a positive integer)
+ Expected: Add command is rejected, with an appropriate error message.
+4. Adding expenditure paid for with credit card
+ 1. Prerequisite: A credit card named `DBS` has been added, with a credit limit of at least 10.
+ 2. Test case: `add /e /pm DBS /c Personal /d Gift to friend /a 10.00 /t 01/01/2022`
+ Expected: Expenditure added to account. Message displaying summary of new
+ expenditure, along with updated balance of credit card, is displayed.
+ 3. Test case: Non-existent credit card
+ Expected: Add command is rejected, with an appropriate error message.
+ 4. Test case: Amount exceeding credit card's limit
+ Expected: Add command is rejected, with an appropriate error message.
+
+### Updating data
+
+1. Updating expenditures
+ 1. Prerequisite: There is at least 1 expenditure.
+ 2. Test case: `update /e 1 /pm Cash /c Entertainment /d PS5 /a 899.00 /t 25/03/2022`
+ Expected: First expenditure is updated accordingly, with a message summarizing
+ the new data.
+2. Updating credit cards
+ 1. Prerequisite: There are at least 4 credit cards.
+ 2. Test case: `update /cc 4 /n OCBC /cb 2.00 /cl 30000`
+ Expected: Fourth credit card is updated accordingly, with a message summarizing
+ the new data.
+3. Updating income sources
+ 1. Prerequisite: There are at least 2 income sources.
+ 2. Test case: `update /i 2 /a 500 /c Investment`
+ Expected: Second income source is updated accordingly, with a message summarizing
+ the new data.
+4. Update edge cases
+ 1. Test case: Invalid index into list (not integer, negative, out of bounds, etc)
+ Expected: Update command is rejected, with an appropriate message.
+ 2. Note that all input format edge cases from `add` also apply here.
+
+### Deleting data
+
+1. Deleting expenditures
+ 1. Prerequisite: There are at least 2 expenditures.
+ 2. Test case: `delete /e 2`
+ Expected: Second expenditure is deleted, with a message stating its description and amount.
+2. Deleting credit cards
+ 1. Prerequisite: There are at least 3 credit cards.
+ 2. Test case: `delete /cc 3`
+ Expected: Third credit card is deleted, with a message stating its name.
+3. Deleting income sources
+ 1. Prerequisite: There is at least 1 income source.
+ 2. Test case: `delete /i 1`
+ Expected: First income source is deleted, with a message stating its category.
+4. Delete edge cases
+ 1. Test case: Invalid index into list (not integer, negative, out of bounds, etc.)
+ Expected: Delete command is rejected, with an appropriate message.
+
+### Displaying summary information
+
+1. Listing information
+ 1. Prerequisite for ii, iii, iv: Some expenditures, credit cards, and income sources have already been `add`ed.
+ 2. Test case: `list /cc`
+ Expected: A list of credit cards is shown. For each credit card, its name,
+ cashback percentage, cashback gained, card limit, and current remaining balance
+ is displayed.
+ 3. Test case: `list /i`
+ Expected: A list of income sources is shown. For each income source, its
+ amount and category is displayed.
+ 4. Test case: `list /e`
+ Expected: A list of expenditures is displayed. For each expenditure, its
+ amount, category, description, and date is displayed, as well as how it was
+ paid for. If {DATE} is specified, a list of expenditures with the {DATE} is displayed.
+ 5. Prerequisite for vi: There are some expenditures whose date is in March 2022, and others which are not.
+ 6. Test case: `list /e 03/2022`
+ Expected: Only the expenses occurring in March 2022 are listed.
+ 7. Test case: `list` when the respective data source (expenditures, credit cards, income sources) is empty.
+ Expected: An error message telling the user to add data is displayed.
+2. Calculating summary expenses
+ 1. Prerequisite: Some expenses have been added, under appropriate categories, all occurring sometime in March 2022.
+ 2. Test case: `calculate /epm 03/2022`
+ Expected: A breakdown of expenses in March 2022 per category is displayed.
+
+### Saving data
+
+1. Normal operations
+ 1. Prerequisites: Some data has already been added into the system.
+ 2. Test case: There is no `data.txt` file on startup.
+ Expected: A new file is created, and operation continues as normal.
+ 3. Test case: Perform `list /e`, `list /cc`, and `list /i` to view the data in the system.
+ Restart the software, and perform the list commands again.
+ Expected: The two sets of list commands will have the same output.
+2. Manual editing
+ 1. Prerequisites: A save file has been created, with relevant data.
+ 2. Test case: A value in the save file is manually edited
+ Expected: Upon restarting the program, the new value is correctly reflected.
+ 3. Test case: The save file is corrupted (e.g. missing/misspelled parameters, improper
+ begin block/end block, missing quotes, improper values for parameters)
+ Expected: An error message detailing the parse error is displayed, and
+ the software proceeds to work as if the save file started as empty.
\ No newline at end of file
diff --git a/docs/README.md b/docs/README.md
index bbcc99c1e7..738663b064 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,6 +1,31 @@
-# Duke
+# MindMyMoney
-{Give product intro here}
+MindMyMoney (M3) was proposed for users with multiple payment methods who wish to track their expenditures. Conducting
+anecdotal interviews, we realized users face difficulty tracking their expenditures across different payment platforms
+on vastly different items. MindMyMoney was then conceptualized on the basis of tracking user’s expenditures.
+
+The technical goal for the MindMyMoney (M3) is to develop an app for managing your expenditures, optimized for use via
+a Command Line Interface (CLI). MindMyMoney (M3) aims to be a one-stop shop for you to consolidate your expenditure across multiple
+platforms, set budget goals and track your spending.
+
+
+# Quick Start
+
+1. Ensure that you have Java 11 or above installed.
+2. Down the latest version of `MindMyMoney` from [here](https://github.com/AY2122S2-CS2113T-T10-4/tp/releases).
+
+If the setup is correct, you should see the following in your terminal:
+````
+ __ __ _ _ __ __ __ __
+| \/ (_)_ _ __| | \/ |_ _| \/ |___ _ _ ___ _ _
+| |\/| | | ' \/ _` | |\/| | || | |\/| / _ \ ' \/ -_) || |
+|_| |_|_|_||_\__,_|_| |_|\_, |_| |_\___/_||_\___|\_, |
+ |__/ |__/
+<< Set a budget and stick to it >>
+
+Welcome to MindMyMoney
+What can I do for you?
+````
Useful links:
* [User Guide](UserGuide.md)
diff --git a/docs/UserGuide.md b/docs/UserGuide.md
index abd9fbe891..9050e3faaf 100644
--- a/docs/UserGuide.md
+++ b/docs/UserGuide.md
@@ -1,42 +1,865 @@
-# User Guide
+# MindMyMoney User Guide
+
+## Content Page
+
+* [Introduction](#introduction)
+* [Quick start](#quick-start)
+* [Features](#features)
+ * [Introduction to Commands](#introduction-to-commands)
+ * [Expenditure](#expenditure)
+ * [Display help page for expenditures: `help`](#display-help-page-for-expenditures-help)
+ * [Add an expenditure: `add`](#add-an-expenditure-add)
+ * [Display expenditures: `list` ](#display-expenditures-list)
+ * [Modify an expenditure: `update`](#modify-an-expenditure-update)
+ * [Remove an expenditure: `delete`](#remove-an-expenditure-delete)
+ * [Calculate expenditures: `calculate`](#calculate-expenditures-calculate)
+ * [Credit Card](#credit-card)
+ * [Display help page for credit cards: `help`](#display-help-page-for-credit-cards-help)
+ * [Add a credit card: `add`](#add-a-credit-card-add)
+ * [Display credit cards: `list` ](#display-credit-cards-list)
+ * [Modify a credit card: `update`](#modify-a-credit-card-update)
+ * [Remove a credit card: `delete`](#remove-a-credit-card-delete)
+ * [Calculate cashback: `calculate` [coming in v3.0]](#calculate-cashback-calculate-coming-in-v30)
+ * [Income](#income)
+ * [Display help page for incomes: `help`](#display-help-page-for-incomes-help)
+ * [Add an income: `add`](#add-an-income-add)
+ * [Display incomes: `list`](#display-incomes-list)
+ * [Modify an income: `update`](#modify-an-income-update)
+ * [Remove an income: `delete`](#remove-an-income-delete)
+ * [Exit MindMyMoney application: `bye`](#exit-mindmymoney-application-bye)
+ * [Save the data](#save-the-data)
+ * [Editing the Save File](#editing-the-save-file)
+* [FAQ](#faq)
+* [Command summary (Expenditure)](#command-summary-expenditure)
+* [Command summary (Credit Card)](#command-summary-credit-card)
+* [Command summary (Income)](#command-summary-income)
## Introduction
-{Give a product intro}
+### MindMyMoney
+
+`MindMyMoney` (M3) is a desktop application for managing your personal finances, optimized for use via a
+Command Line Interface (CLI). You can use it to track your expenditures across multiple payment methods, calculate
+monthly expenditure, and track your income. If you are a student looking to manage your personal finances, this
+application is for you!
+
+### Using the User Guide
+
+This guide aims to equip you with the knowledge on how to set up the application and to utilise its many features. Click
+on the hyperlinks in the [Content Page](#content-page) to quickly navigate the user guide! Along the guide you may
+encounter several icons. These icons will provide you with several useful information.
+> **💡 Note:**
+>- This tells you that there is additional information that is useful when you are using the application.
+
+> **⚠️Warning⚠️**
+>- This tells you that there is some **important** information you should take note of to prevent issues from arising when you are using the application.
+
+
+
## Quick Start
-{Give steps to get started quickly}
+1. Ensure that you have Java 11 or above installed. Click
+ [here](https://docs.aws.amazon.com/corretto/latest/corretto-11-ug/downloads-list.html) for the link to the Java 11
+ installer.
+2. Download the latest version of `MindMyMoney.jar` from [here](https://github.com/AY2122S2-CS2113T-T10-4/tp/releases).
+3. Copy the file to the folder you want to use as the _home folder_ for your MindMyMoney.
+4. Open a command line terminal in your _home folder_ and run `java -jar MindMyMoney.jar`. The startup interface similar
+ to the one below should appear in a few seconds.
+
+````
+ __ __ _ _ __ __ __ __
+| \/ (_)_ _ __| | \/ |_ _| \/ |___ _ _ ___ _ _
+| |\/| | | ' \/ _` | |\/| | || | |\/| / _ \ ' \/ -_) || |
+|_| |_|_|_||_\__,_|_| |_|\_, |_| |_\___/_||_\___|\_, |
+ |__/ |__/
+<< Set a budget and stick to it >>
+
+Welcome to MindMyMoney
+What can I do for you?
+````
+
+5. Type the command in the command box and press Enter to execute it. For example: typing **`help`** and pressing Enter
+ will show a help page.
Some example commands you can try:
+ * **`add`**`/e /pm cash /c Food /d Porridge /a 3 /t 12/03/2022` :
+ Adds a $3 expenditure of the description 'Porridge' that was paid in cash on 12 March 2022 to your list of
+ expenditures.
+ * **`list /e`** : Lists all expenditures.
+ * **`calculate`**`/epm 03/2022` : Calculates the total expenditure in the month of March 2022.
+ * **`update`**`/e 1 /pm cash /c Food /d Chicken Rice /a 4.50 /t 12/03/2022` :
+ Updates the first expenditure on your expenditure list to reflect a $4.50 expenditure of the description 'Chicken
+ Rice' that was paid in cash on 12 March 2022.
+ * **`delete`**`/e 1` : Deletes the first expenditure in your expenditure list.
+ * **`bye`** : Exits the application.
+
+6. Refer to the [Features](#features) for more details of each command.
+
+
+
+# Features
+
+The following are features of the `MindMyMoney` application.
+
+## Introduction to Commands
+
+A `MindMyMoney` command typically contains 4 types of terms.
+
+- The first term in the command is known as the instruction.
+- Terms in `[SQUARE_BRACKETS]` are compulsory parameters.
+- Terms in `{CURLY_BRACKETS}` are optional parameters.
+- Terms starting with a `/` are flags.
+
+For Example:
+
+- `list /e {DATE}`
+ - `list` is an instruction, `/e` is a flag and `{DATE}` is an optional parameter.
+- `calculate /epm [DATE]`
+ - `calculate` is an instruction, `/epm` is a flag and `[DATE]` is a compulsory parameter.
+
+> **💡 Note:**
+>- Parameters and flags are space-separated. For example: `list/e` is not a valid command while `list /e` is valid.
+
+> **⚠️Warning⚠️**
+>- Input the parameters in the order shown in the guide, else the application will not be able to read your
+ > input.
+>- Only use ASCII characters when entering commands into the application. Otherwise, your input may not be properly displayed in the command line.
+
+
+
+## Expenditure
+
+Expenditure refers to the various expenses you make.
+
+### Display help page for expenditures: `help`
+
+Prints a list of commands related to expenditure.
+
+#### Format: `help /e`
+
+#### Expected Outcome:
+
+For example: `help /e`
+Shows the help page for expenditure related commands.
+
+````
+> help /e
+---------------------------------------Expenditure Help Page---------------------------------------
+1. Listing all Expenditures: list /e {DATE}
+2. Adding an Expenditure entry: add /e /pm [PAYMENT_METHOD] /c [CATEGORY] /d [DESCRIPTION] /a [AMOUNT] /t [DATE]
+3. Calculating the total expenditure in a month: calculate /epm [DATE]
+4. Updating an Expenditure: update /e [NEW_INDEX] /pm [NEW_PAYMENT_METHOD] /c [NEW_CATEGORY] /d [NEW_DESCRIPTION] /a [NEW_AMOUNT] /t [NEW_DATE]
+5. Removing an Expenditure entry: delete /e [INDEX]
+6. Exiting the program: bye
+---------------------------------------------------------------------------------------------------
+````
+
+
+
+### Add an expenditure: `add`
+
+Adds an expenditure into your expenditure list. Only **one** expenditure can be added per command.
+
+#### Format: `add /e /pm [PAYMENT_METHOD] /c [CATEGORY] /d [DESCRIPTION] /a [AMOUNT] /t [DATE]`
+
+* `[PAYMENT_METHOD]` refers to the method of payment used.
+ * Enter `cash` or the name of a credit card you have saved.
+* `[CATEGORY]` refers to the supported categories of expenditure.
+ * Enter `Food`, `Transport`, `Utilities`, `Personal`, `Entertainment` or `Others`.
+* `[DESCRIPTION]` refers to the description of the expenditure.
+ * For example: `Porridge`.
+* `[AMOUNT]` refers to the cost of the expenditure.
+ * Enter the amount in dollars, rounded off to the nearest cent.
+ * For example: an item that cost $420.69 will be entered as `420.69`.
+* `[DATE]` refers to the date of the purchase of the expenditure.
+ * Format of the date is `dd/mm/yyyy`.
+ * For example: `12 March 2022` will be entered as `12/03/2022`.
+
+#### Expected Outcome:
+
+For example: `add /e /pm cash /c Food /d Porridge /a 4.50 /t 12/03/2022`
+Adds a $4.50 expenditure of food item 'Porridge' that was paid in cash on 12 March 2022 to your expenditure list.
+
+````
+> add /e /pm cash /c Food /d Porridge /a 4.50 /t 12/03/2022
+Successfully added:
+
+Description: Porridge
+Amount: $4.50
+Category: Food
+Payment method: Cash
+Date: 12/03/2022
+
+into the account
+````
+
+> **💡 Note:**
+>- `[CATEGORY]` and `[PAYMENT_METHOD]` are **case-insensitive**.
+>- Your credit card has to be [added](#add-a-credit-card-add) first before entering the name of the credit card as `[PAYMENT_METHOD]`.
+>- `[AMOUNT]` only accepts numbers with 2 decimal places. Any additional decimals will be rounded off or ignored.
+>- Maximum `[AMOUNT]` allowed for user is 1000000 ($1 million).
+
+
+> **⚠️Warning⚠️**
+>- `[CATEGORY]`: Any input that is not `Food`, `Transport`, `Utilities`, `Personal`, `Entertainment` or `Others` will be rejected.
+>- `[DATE]` not in the format of `dd/mm/yyyy` will be rejected.
+>- Input dates later than the current date will be rejected.
+>- Illogical input dates will be rejected.
+
+
+
+### Display expenditures: `list`
+
+Prints your current list of expenditures.
+
+#### Format: `list /e {DATE}`
+
+* `{DATE}` refers to the date of the expenditures you would like to view.
+ * Enter the `{DATE}` in `dd/mm/yyyy`, `mm/yyyy` or `yyyy` format.
+
+#### Expected Outcome:
+
+For example: `list /e`
+Lists all your expenditures.
+
+```
+> list /e
+-----------------------------------------------
+1. $300.00 was spent on Nike Shoes(Personal) using Cash [30/03/2022]
+2. $20.00 was spent on Grab(Transport) using Cash [30/03/2022]
+3. $3.21 was spent on Bubble Tea(Food) using Cash [30/01/1999]
+4. $4.50 was spent on Porridge(Food) using Cash [12/03/2022]
+-----------------------------------------------
+```
+
+For example: `list /e 03/2022`
+Lists all your expenditures in March 2022.
+
+```
+> list /e 03/2022
+-----------------------------------------------
+1. $300.00 was spent on Nike Shoes(Personal) using Cash [30/03/2022]
+2. $20.00 was spent on Grab(Transport) using Cash [30/03/2022]
+3. $4.50 was spent on Porridge(Food) using Cash [12/03/2022]
+-----------------------------------------------
+```
+
+For example: `list /e 30/03/2022`
+Lists all your expenditures in 30 March 2022.
+
+```
+> list /e 30/03/2022
+-----------------------------------------------
+1. $300.00 was spent on Nike Shoes(Personal) using Cash [30/03/2022]
+2. $20.00 was spent on Grab(Transport) using Cash [30/03/2022]
+-----------------------------------------------
+```
+
+> **💡 Note:**
+>- `list /e` will show you all expenditures stored in the list.
+
+> **⚠️Warning⚠️**
+>- `{DATE}` not in the format of `dd/mm/yyyy`, `mm/yyyy` or `yyyy` will be rejected.
+>- Before entering the command, an expenditure with the same input date must exist in the expenditure list.
+
+
+
+### Modify an expenditure: `update`
+
+Modifies an expenditure on your expenditure list at the specified index.
+Use the `list /e` command to view the indexes of your expenditures.
+
+#### Format: `update /e [INDEX] /pm [NEW_PAYMENT_METHOD] /c [NEW_CATEGORY] /d [NEW_DESCRIPTION] /a [NEW_AMOUNT] /t [NEW_DATE]`
+
+* `[INDEX]` refers to the index of expenditure in list in which you want to update.
+ * Enter `1` if you want to update the first expenditure in your list.
+* `[NEW_PAYMENT_METHOD]` refers to the new method of payment used.
+ * Enter `cash` or the name of a credit card you have saved.
+* `[NEW_CATEGORY]` refers to the new category of the expenditure.
+ * Enter `Food`, `Transport`, `Utilities`, `Personal`, `Entertainment` or `Others`.
+* `[NEW_DESCRIPTION]` refers to the new description of the expenditure.
+ * For example: `Chicken rice`.
+* `[NEW_AMOUNT]` refers to the updated of the expenditure.
+ * Enter the amount in dollars, rounded off to the nearest cent.
+ * For example: an item that cost $420.69 will be entered as `420.69`.
+* `[NEW_DATE]` refers to the new date of the purchase of the expenditure.
+ * Format of the date is `dd/mm/yyyy`.
+ * For example: `12 March 2022` will be entered as `12/03/2022`.
+
+#### Expected Outcome:
+
+For example: `update /e 1 /pm cash /c Food /d chicken rice /a 5 /t 12/03/2022`.
+Updates the first expenditure in your list to a $5.00 expenditure on food item 'chicken rice' that was paid in cash on 12
+March 2022.
+
+````
+> list /e
+-----------------------------------------------
+1. $300.00 was spent on Nike Shoes(Personal) using Cash [30/03/2022]
+2. $20.00 was spent on Grab(Transport) using Cash [30/03/2022]
+3. $3.21 was spent on Bubble Tea(Food) using Cash [30/01/1999]
+4. $4.50 was spent on Porridge(Food) using Cash [12/03/2022]
+-----------------------------------------------
+
+> update /e 4 /pm cash /c Food /d chicken rice /a 5 /t 12/03/2022
+Successfully set expenditure 4 to:
+$5.00 was spent on chicken rice(Food) using Cash [12/03/2022]
+````
+
+> **💡 Note:**
+> - This command is similar to the [add an expenditure](#add-an-expenditure-add) command.
+> - Parameters that are labeled starting with NEW follow the same restrictions
+ in [add an expenditure](#add-an-expenditure-add).
+> - For example: `[NEW_CATEGORY]` is **case-insensitive**, similar to `[CATEGORY]`
+> - `[INDEX]` is based on the list generated from `list /e`, **not** the other variations of `list /e {DATE}`.
+>- Only enter `[INDEX]` that exist in the expenditure list. For example: if you have 4 expenditures in your list, specify `[INDEX]` to be a number from 1 to 4.
+
+> **⚠️Warning⚠️**
+> - `[NEW_CATEGORY]`: Any input not in the accepted list of categories will be rejected.
+> - `[NEW_DATE]` not in the format of `dd/mm/yyyy` will be rejected.
+> - Input dates later than the current date will be rejected.
+
+
+
+### Remove an expenditure: `delete`
+
+Deletes an expenditure from your expenditure list at the specified index.
+Use the `list /e` command to view the current indexes of your expenditures.
+
+#### Format: `delete /e [INDEX]`
+
+* `[INDEX]` refers to the index of expenditure in list in which you want to delete.
+
+#### Expected Outcome:
+
+For example: `delete /e 1`
+Deletes the first expenditure on your list.
+
+````
+> list /e
+-----------------------------------------------
+1. $300.00 was spent on Nike Shoes(Personal) using Cash [30/03/2022]
+2. $20.00 was spent on Grab(Transport) using Cash [30/03/2022]
+3. $3.21 was spent on Bubble Tea(Food) using Cash [30/01/1999]
+4. $4.50 was spent on Porridge(Food) using Cash [12/03/2022]
+-----------------------------------------------
+
+> delete /e 1
+I have removed Nike Shoes of $300.00 from the account
+````
+
+> **💡 Note:**
+>- Only enter `[INDEX]` that exist in your list. For example: if you have 4 expenditures in your list, specify `[INDEX]` to be a number from 1 to 4.
+>- `[INDEX]` is based on the list generated from `list /e`, **not** the other variations of `list /e {DATE}`.
+>- Do not use `delete /e` when your expenditure list is empty.
+
+
+
+### Calculate expenditures: `calculate`
+
+Shows the total expenditure breakdown for a specified day, month or year in a horizontal bar chart.
+
+#### Format: `calculate /epm [DATE]`
+
+* `[DATE]` can be of the format `dd/mm/yyyy`, `mm/yyyy` or `yyyy`, depending on the duration you are interested in.
+
+#### Expected Outcome:
+
+For example: `calculate /epm 03/2022`
+Shows your total expenditure in March 2022 and breakdown of expenses.
+
+````
+> calculate /epm 03/2022
+Total expenditure in 03/2022 is $24.50.
+
+BREAKDOWN OF EXPENSES:
+-----------------------------------------------
+FOOD: $$$$$$$$ [18.37%]
+TRANSPORT: $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ [81.63%]
+UTILITIES: [0.0%]
+PERSONAL: [0.0%]
+ENTERTAINMENT: [0.0%]
+OTHERS: [0.0%]
+-----------------------------------------------
+````
+
+> **💡 Note**
+> - An error will be shown if the date that you would like to calculate is not found in the expenditure list.
+
+
+
+## Credit Card
+
+Credit card refers to the various credit cards you might have.
+
+### Display help page for credit cards: `help`
+
+Prints a list of commands related to credit card.
+
+#### Format: `help /cc`
+
+#### Expected Outcome:
+
+For example: `help /cc`
+Shows the help page for credit card related commands.
+
+````
+> help /cc
+---------------------------------------Credit Card Help Page---------------------------------------
+1. Listing all Credit Cards: list /cc
+2. Adding a Credit Card: add /cc /n [CREDIT_CARD_NAME] /cb [CASHBACK] /cl [CREDIT_LIMIT]
+3. Updating a Credit Card: update /cc [INDEX] /n [NEW_NAME] /cb [NEW_CASHBACK] /cl [NEW_CREDIT_LIMIT]
+4. Removing a credit card: delete /cc [INDEX]
+5. Exiting the program: bye
+---------------------------------------------------------------------------------------------------
+````
+
+
+
+### Add a credit card: `add`
+
+Adds a credit card into your credit card list. Only **one** credit card can be added per command. You can then use
+this credit card as a payment method when [adding an expenditure](#add-an-expenditure-add).
+
+#### Format: `add /cc /n [CREDIT_CARD_NAME] /cb [CASHBACK] /cl [CARD_LIMIT]`
+
+* `[CREDIT_CARD_NAME]` refers to the name your Credit Card will be saved as.
+ * Use abbreviations for ease of adding expenditures to this credit card. For example:
+ storing `DBS Live Fresh Credit Card` as `DBS LF`.
+* `[CASHBACK]` refers to the amount of cash back received when spending on the credit card.
+ * Enter the amount of cashback in percentage.
+ * For example: a credit card with `2% cashback` can be represented as `/cb 2`.
+* `[CARD_LIMIT]` refers to the maximum monthly expenditure on this credit card.
+ * Enter the monthly maximum amount that can be spent on the credit card in dollars.
+
+#### Expected Outcome:
+
+For example:`add /cc /n dbs /cb 2 /cl 1000`
+Adds your credit card of the name 'DBS' with a cashback of 2% and a monthly spending limit of $1000.
+
+````
+> add /cc /n dbs /cb 2 /cl 1000
+Successfully added:
-1. Ensure that you have Java 11 or above installed.
-1. Down the latest version of `Duke` from [here](http://link.to/duke).
+Credit card: dbs
+Cash back: 2.00%
+Card limit: $1000.00
-## Features
+into the account
+````
-{Give detailed description of each feature}
+> **💡 Note**
+>- `[CREDIT_CARD_NAME]` is **case-insensitive**.
-### Adding a todo: `todo`
-Adds a new item to the list of todo items.
-Format: `todo n/TODO_NAME d/DEADLINE`
+> **⚠️Warning⚠️**
+>- `[CREDIT_CARD_NAME]` cannot be `cash`, `CASH`, or a combination of either.
+> - You are not allowed to add 2 credit cards with the same in `[CREDIT_CARD_NAME]`. Instead, you can abbreviate the cards differently.
+> - For example: If you have two DBS credit cards, you can enter the `[CREDIT_CARD_NAME]` of the first card to be `DBS_one` and the other as `DBS_two`.
+>- `[CASHBACK]` cannot be more than 100%.
+>- `[CARD_LIMIT]` cannot be more than $40,000. Generally, students should not have a monthly income of more than
+> $10,000, and hence a monthly credit card limit of $40,000 calculated through [here](https://www.moneysmart.sg/credit-cards/credit-limit-singapore-ms).
-* The `DEADLINE` can be in a natural language format.
-* The `TODO_NAME` cannot contain punctuation.
+
-Example of usage:
+### Display credit cards: `list`
-`todo n/Write the rest of the User Guide d/next week`
+Prints your current list of credit cards.
-`todo n/Refactor the User Guide to remove passive voice d/13/04/2020`
+#### Format: `list /cc`
+
+#### Expected Outcome:
+
+For example: `list /cc`
+Lists all your credit cards.
+
+````
+> list /cc
+-----------------------------------------------
+1. Name: dbs [Cashback: 2.00%] [Cashback gained: $0.00] [Card limit: $1000.00] [Balance left: $1000.00]
+-----------------------------------------------
+````
+
+
+
+### Modify a credit card: `update`
+
+Modifies a credit card on your credit card list at the specified index.
+Use the `list /cc` command to view the current indexes of your credit cards.
+
+#### Format: `update /cc [INDEX] /n [NEW_NAME] /cb [NEW_CASHBACK] /cl [NEW_CARD_LIMIT]`
+
+* `[INDEX]` refers to the index of credit card in list in which you want to update.
+ * Enter `1` if you want to update the first credit card in your list.
+* `[NEW_NAME]` refers to the name your Credit Card will be updated to.
+ * Use abbreviations for ease of adding expenditures to this credit card. For example:
+ storing `DBS Live Fresh Credit Card` as `DBS LF`.
+* `[NEW_CASHBACK]` refers to the updated amount of cash back received when spending on the credit card.
+ * Enter the amount of cashback in percentage.
+ * For example: a credit card with `2% cashback` can be represented as `/cb 2`.
+* `[NEW_CARD_LIMIT]` refers to the updated maximum monthly expenditure on this credit card.
+ * Enter the monthly maximum amount that can be spent on the credit card in dollars.
+
+#### Expected Outcome:
+
+For example: `update /cc 1 /n OCBC /cb 1.5 /cl 500`
+Updates the first credit card on your list to have a name of 'OCBC' with a cashback of 1.5% and a monthly spending limit
+of $500.
+
+````
+> list /cc
+-----------------------------------------------
+1. Name: dbs [Cashback: 2.00%] [Cashback gained: $0.00] [Card limit: $1000.00] [Balance left: $1000.00]
+-----------------------------------------------
+
+> update /cc 1 /n OCBC /cb 1.5 /cl 500
+Successfully set credit card 1 to:
+Name: OCBC [Cashback: 1.50%] [Cashback gained: $0.00] [Card limit: $500.00] [Card balance: $500.00]
+````
+
+> **💡 Note:**
+> - This command is similar to the [add a credit card](#add-a-credit-card-add) command.
+> - Parameters that are labeled starting with NEW follow the same restrictions
+ in [add a credit card](#add-a-credit-card-add).
+> - For example: `[NEW_NAME]` can be abbreviated like `[CREDIT_CARD_NAME]`.
+> - Only enter `[INDEX]` that exist in your list. For example if you have 4 items in the credit card list, specify `[INDEX]` to be a number from 1 to 4.
+> - You are **not allowed** to update the spending limit to an amount below what you have already spent using this card.
+> - For Example: You have already spent $500 using the card. You will not be allowed to update the spending limit to a
+ > number less than $500.
+
+> **⚠️Warning⚠️**
+>- Updating a credit card would cause its cashback earned to **reset to 0**. Similarly, its balance left will also
+ > be **reset to the spending limit**.
+
+
+
+### Remove a credit card: `delete`
+
+Deletes a credit card from your list at the specified index.
+Use the `list /cc` command to view the current indexes of your credit cards.
+
+#### Format: `delete /cc [INDEX]`
+
+* `[INDEX]` refers to the index of credit card in your credit card list in which you want to delete.
+ * Enter `2` if you want to delete the second credit card in your credit card list.
+
+#### Expected Outcome
+
+For example: `delete /cc 1`
+Deletes the first credit card on your credit card list.
+
+````
+> list /cc
+-----------------------------------------------
+1. Name: OCBC [Cashback: 1.50%] [Cashback gained: $0.00] [Card limit: $500.00] [Balance left: $500.00]
+-----------------------------------------------
+
+> delete /cc 1
+I have removed OCBC from your list of credit card(s).
+````
+
+> **💡 Note:**
+>- Only enter `[INDEX]` that exist in your list. For example: if you have 4 credit cards in your list, specify `[INDEX]` to be a number from 1 to 4.
+>- Do not use `delete /cc` when your credit card list is empty.
+
+
+
+### Calculate cashback: `calculate` [coming in v3.0]
+
+Details coming soon...
+
+
+
+## Income
+
+Income refers to the various sources of income you might have.
+
+### Display help page for incomes: `help`
+
+Prints a list of commands related to income.
+
+#### Format: `help /i`
+
+#### Expected Outcome:
+
+For example: `help /i`
+Shows the help page for income related commands.
+
+````
+> help /i
+--------------------------------Income Help Page---------------------------------------
+1. Listing all Incomes: list /i
+2. Adding an Income entry: add /i /a [AMOUNT] /c [CATEGORY]
+3. Updating an Income entry: update /i [INDEX] /a [NEW_AMOUNT] /c [NEW_CATEGORY]
+4. Removing an Income entry: delete /i [INDEX]
+---------------------------------------------------------------------------------------
+````
+
+
+
+### Add an income: `add`
+
+Adds an income into your income list. Only **one** income can be added per command.
+
+#### Format: `add /i /a [AMOUNT] /c [CATEGORY]`
+
+* `[AMOUNT]` refers to the monthly sum received, as a whole number.
+* `[CATEGORY]` refers to the supported categories of income.
+ * Enter `Salary`, `Allowance`, `Investment` or `Others`.
+
+#### Expected Outcome:
+
+For example: `add /i /a 3000 /c salary`
+Adds an income of $3000 categorised as your Salary.
+
+````
+> add /i /a 3000 /c salary
+Successfully added:
+
+Amount: $3000
+Category: Salary
+
+into the account
+````
+
+> **💡 Note:**
+>- `[CATEGORY]` is **case-insensitive**.
+
+> **⚠️Warning⚠️**
+>- `[CATEGORY]`: Any input that is not `Salary`, `Allowance`, `Investment` or `Others` will be rejected.
+>- `[AMOUNT]`: Takes in whole numbers as an input.
+ > Round off your income to the nearest whole number when entering it into MindMyMoney.
+
+
+
+### Display incomes: `list`
+
+Prints your current list of income entries.
+
+#### Format: `list /i`
+
+#### Expected Outcome:
+
+For example: `list /i`
+
Lists all your income entries.
+
+````
+> list /i
+-----------------------------------------------
+1. Amount: $3000
+ Category: Salary
+-----------------------------------------------
+````
+
+
+
+### Modify an income: `update`
+
+Modifies an income in your income list at the specified index.
+Use the `list /i` command to view the current indexes of your income entries.
+
+#### Format: `update /i [INDEX] /a [NEW_AMOUNT] /c [NEW_CATEGORY]`
+
+* `[INDEX]` refers to the index of income in list in which you want to update.
+ * Enter `1` if you want to update the first entry in the list.
+* `[NEW_AMOUNT]` refers to the updated monthly sum received, as a whole number.
+* `[NEW_CATEGORY]` refers to the supported categories of income.
+ * Enter `Salary`, `Allowance`, `Investment` or `Others`.
+
+#### Expected Outcome:
+
+For example: `update /i 1 /a 4000 /c salary`
+Updates the first income entry on your income list to $4000 categorised as your salary.
+
+````
+> list /i
+-----------------------------------------------
+1. Amount: $3000
+ Category: Salary
+-----------------------------------------------
+
+> update /i 1 /a 4000 /c salary
+Successfully set income 1 to:
+Amount: $4000
+Category: Salary
+````
+
+> **💡 Note:**
+> - This command is similar to the [add an income](#add-an-income-add) command.
+> - Fields that are labeled starting with NEW follow the same restrictions in [add an income](#add-an-income-add).
+> - For example: `[NEW_AMOUNT]` input has to be a whole number, similar to `[AMOUNT]`.
+>- `[CATEGORY]` is **case-insensitive**.
+> - Only enter `[INDEX]` that exist in your list. For example: if you have 4 incomes in your income list, specify `[INDEX]` to be a number from 1 to 4.
+
+> **⚠️Warning⚠️**
+> - `[NEW_CATEGORY]`: Any input not in the accepted list of categories will be rejected.
+
+
+### Remove an income: `delete`
+
+Deletes an income from your income list at the specified index.
+Use the `list /i` command to view the current indexes of your income entries.
+
+#### Format: `delete /i [INDEX]`
+
+* `[INDEX]` refers to the index of income in your income list you want to delete.
+ * Enter `1` if you want to delete the first income in your income list.
+
+#### Expected Outcome:
+
+For example: `delete /i 1`
+Deletes the first income entry on your income list.
+
+````
+> list /i
+-----------------------------------------------
+1. Amount: $4000
+ Category: Salary
+-----------------------------------------------
+
+> delete /i 1
+I have removed Salary from your list of income(s).
+````
+
+> **💡 Note:**
+>- Only enter `[INDEX]` that exist in your list. For example: if you have 4 incomes in your income list, specify `[INDEX]` to be a number from 1 to 4.
+>- Do not use `delete /i` when your income list is empty.
+
+
+
+## Exit MindMyMoney application: `bye`
+
+Shuts down the MindMyMoney application.
+
+### Format: `bye`
+
+### Expected Outcome:
+
+For example: `bye`
+Exits the program.
+
+````
+> bye
+Bye, hope to see you again!
+````
+
+> **💡 Note**
+> - Any input after the `bye` command is ignored. For example: `bye Hello World` will still exit the program.
+
+
+
+## Save the Data
+
+Your MindMyMoney data is saved in the hard disk automatically after any command that changes the data. There is no need
+for you to save manually. You can view the saved contents of MindMyMoney by reading the `data.txt` file in your current
+directory.
+
+> **💡 Note:**
+>- If you wish to back up your MindMyMoney data, you can copy the `data.txt` file into the folder you want to save it in.
+>- To load the backup data into MindMyMoney, copy `data.txt` from the backup folder into the folder containing MindMyMoney, replacing the existing copy of `data.txt`.
+
+### Editing the Save File
+If you are experienced in using MindMyMoney, you may wish to directly edit the `data.txt` file. Below is a short
+description of its format.
+
+> **⚠️Warning⚠️**
+>- Be careful when modifying `data.txt` to follow the correct format, since doing so can corrupt the data in MindMyMoney.
+ > When in doubt, keep a backup, as stated above. If you are less experienced, you may use the `update` and `add`
+ > commands to edit the data.
+
+
+`data.txt` must contain the following six lines, in this order:
+
+```
+# BEGIN EXPENDITURES
+# END EXPENDITURES
+# BEGIN CREDIT CARDS
+# END CREDIT CARDS
+# BEGIN INCOME SOURCES
+# END INCOME SOURCES
+```
+
+Each expenditure, credit card, and income is stored in one line between their respective `BEGIN`/`END` lines. No line
+should be left blank.
+
+Each piece of data is stored as a series of `key : value` pairs, separated by spaces. Both `key` and `value`
+are enclosed in quotes. A `\"` in a key or value represents a quotation mark, while a `\\` represents a backslash.
+
+The following are the keys required for each type of data:
+
+- Expenditures contain `amount`, `description`, `paymentMethod`, `time`, and `category` keys.
+- Credit cards contain `totalExpenditure`, `monthlyCardLimit`, `nameOfCard`, and `cashback` keys.
+- Incomes contain `amount` and `category` fields.
+
+Their meanings are the same as in the `add` commands. The `totalExpenditure` key of a credit card contains the total
+amount spent using that credit card.
+
+If a key is missing, MindMyMoney will consider the save file invalid.
+
+Here is an example of a valid save file:
+
+```
+# BEGIN EXPENDITURES
+ "amount": "3.0" "description": "Commute" "paymentMethod": "Cash" "time": "08/03/2022" "category": "Transport"
+ "amount": "1.0" "description": "Mala" "paymentMethod": "DBS" "time": "30/03/2022" "category": "Food"
+ "amount": "1.0" "description": "Chicken rice" "paymentMethod": "DBS" "time": "30/03/2022" "category": "Food"
+ "amount": "1.0" "description": "Katsudon" "paymentMethod": "Cash" "time": "07/03/2022" "category": "Food"
+# END EXPENDITURES
+# BEGIN CREDIT CARDS
+ "totalExpenditure": "2.0" "monthlyCardLimit": "10000.0" "nameOfCard": "DBS" "cashback": "1.0"
+ "totalExpenditure": "0.0" "monthlyCardLimit": "20000.0" "nameOfCard": "OCBC" "cashback": "4.0"
+# END CREDIT CARDS
+# BEGIN INCOME SOURCES
+ "amount": "200" "category": "Allowance"
+ "amount": "500" "category": "Investment"
+# END INCOME SOURCES
+```
+
+
## FAQ
-**Q**: How do I transfer my data to another computer?
+**Q**: Why is my data not saved when I run MindMyMoney in different folders?
+
+**A**: MindMyMoney saves data in the current directory. To ensure all the data is saved properly, run MindMyMoney only
+in the _home folder_. If you wish to run MindMyMoney in different folders and still contain your data, copy
+the `data.txt` file found in the current directory into a new folder where you want to run MindMyMoney in.
+
+
+
+## Command Summary (Expenditure)
+
+| Command | Format, examples |
+|-----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| Help | `help /e`
Prints a list of commands related to expenditures. |
+| Add | `add /e /pm [PAYMENT_METHOD] /c [CATEGORY] /d [DESCRIPTION] /a [AMOUNT] /t [DATE]`
For example: `add /e /pm cash /c Food /d Porridge /a 4.50 /t 10/03/2022`
Adds a $4.50 expenditure of Food item 'Porridge' that was paid in cash on 10 March 2022 to your expenditure list. |
+| List | `list /e {DATE}`
For example: `list /e 03/03/2022`
Displays your current list of expenditures on 3 March 2022. |
+| Calculate | `calculate /epm [DATE]`
For example: `calculate /epm 03/2022`
Prints a breakdown of your expenditures for Mar 2022. |
+| Delete | `delete /e [INDEX]`
For example: `delete 1`
Deletes the first expenditure from your expenditure list. |
+| Update | `update /e [INDEX] /pm [NEW_PAYMENT_METHOD] /c [NEW_CATEGORY] /d [NEW_DESCRIPTION] /a [NEW_AMOUNT] /t [NEW_DATE]`
For example: `update /e 1 /pm cash /c Food /d chicken rice /a 5 /t 12/03/2022`
Updates the first expenditure in your expenditure list to a $5.00 expenditure of Food item 'chicken rice' that was paid in cash on 12 March 2022. |
+| Exit | `bye`
Ends the `MindMyMoney` application. |
+
+
+
+## Command Summary (Credit Card)
-**A**: {your answer here}
+| Command | Format, examples |
+|-----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| Help | `help /cc`
Prints a list of commands related to credit cards. |
+| Add | `add /cc /n [CREDIT_CARD_NAME] /cb [CASHBACK] /cl [CARD_LIMIT]`
For example: `add /cc /n dbs /cb 2 /cl 1000`
Adds a credit card of the name 'DBS' with a cashback of 2% and a monthly spending limit of $1000. |
+| List | `list /cc`
Displays your current list of credit cards. |
+| Delete | `delete /cc [INDEX]`
For example: `delete /cc 1`
Deletes the first credit card from your credit card list. |
+| Update | `update /cc [INDEX] /n [NEW_CARD_NAME] /cb [NEW_CASHBACK] /cl [NEW_CREDIT_LIMIT]`
For example: `update /cc 1 /n OCBC /cb 1.5 /cl 500`
Updates the first credit card on your credit card list to have a name of 'OCBC' with a cashback of 1.5% and a monthly spending limit of $500. |
+| Exit | `bye`
Ends the `MindMyMoney` application. |
-## Command Summary
+
-{Give a 'cheat sheet' of commands here}
+## Command Summary (Income)
-* Add todo `todo n/TODO_NAME d/DEADLINE`
+| Command | Format, examples |
+|-----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| Help | `help /i`
Prints a list of commands related to income. |
+| Add | `add /i /a [AMOUNT] /c [CATEGORY]`
For example: `add /i /a 3000 /c salary`
Adds an income of $3000 categorised as your Salary. |
+| List | `list /i`
Displays your current list of income entries. |
+| Delete | `delete /i [INDEX]`
For example: `delete /i 1`
Deletes the first income from your income list. |
+| Update | `update /i [INDEX] /a [NEW_AMOUNT] /c [NEW_SALARY]`
For example: `update /i 1 /a 4000 /c salary`
Updates the first income entry on your income list to $4000 categorised as your Salary. |
+| Exit | `bye`
Ends the `MindMyMoney` application. |
diff --git a/docs/_config.yml b/docs/_config.yml
new file mode 100644
index 0000000000..0827e40308
--- /dev/null
+++ b/docs/_config.yml
@@ -0,0 +1,2 @@
+theme: jekyll-theme-cayman
+header: MindMyMoney
diff --git a/docs/images/AddCommandSequenceDiagram.png b/docs/images/AddCommandSequenceDiagram.png
new file mode 100644
index 0000000000..86da53f80e
Binary files /dev/null and b/docs/images/AddCommandSequenceDiagram.png differ
diff --git a/docs/images/AddCreditCardSequenceDiagram.png b/docs/images/AddCreditCardSequenceDiagram.png
new file mode 100644
index 0000000000..a61e4ff978
Binary files /dev/null and b/docs/images/AddCreditCardSequenceDiagram.png differ
diff --git a/docs/images/AddCreditCardSequence_Diagram.png b/docs/images/AddCreditCardSequence_Diagram.png
new file mode 100644
index 0000000000..a61e4ff978
Binary files /dev/null and b/docs/images/AddCreditCardSequence_Diagram.png differ
diff --git a/docs/images/AddExpenditureCommandSequenceDiagram.png b/docs/images/AddExpenditureCommandSequenceDiagram.png
new file mode 100644
index 0000000000..f7cf64e311
Binary files /dev/null and b/docs/images/AddExpenditureCommandSequenceDiagram.png differ
diff --git a/docs/images/AddExpenditureSequenceDiagram.png b/docs/images/AddExpenditureSequenceDiagram.png
new file mode 100644
index 0000000000..f7cf64e311
Binary files /dev/null and b/docs/images/AddExpenditureSequenceDiagram.png differ
diff --git a/docs/images/AddIncomeSequenceDiagram.png b/docs/images/AddIncomeSequenceDiagram.png
new file mode 100644
index 0000000000..1044f83830
Binary files /dev/null and b/docs/images/AddIncomeSequenceDiagram.png differ
diff --git a/docs/images/ArchitectureDiagramFinal.png b/docs/images/ArchitectureDiagramFinal.png
new file mode 100644
index 0000000000..e658b8b020
Binary files /dev/null and b/docs/images/ArchitectureDiagramFinal.png differ
diff --git a/docs/images/CalculateCommandSequenceDiagramFinal.png b/docs/images/CalculateCommandSequenceDiagramFinal.png
new file mode 100644
index 0000000000..9f8210511c
Binary files /dev/null and b/docs/images/CalculateCommandSequenceDiagramFinal.png differ
diff --git a/docs/images/CommandClassDiagram.png b/docs/images/CommandClassDiagram.png
new file mode 100644
index 0000000000..b6c6eb3e47
Binary files /dev/null and b/docs/images/CommandClassDiagram.png differ
diff --git a/docs/images/ComponentsSequenceDiagramFinal.png b/docs/images/ComponentsSequenceDiagramFinal.png
new file mode 100644
index 0000000000..04582d91b3
Binary files /dev/null and b/docs/images/ComponentsSequenceDiagramFinal.png differ
diff --git a/docs/images/Dan_Profile_Picture.png b/docs/images/Dan_Profile_Picture.png
new file mode 100644
index 0000000000..2aded313a7
Binary files /dev/null and b/docs/images/Dan_Profile_Picture.png differ
diff --git a/docs/images/DeleteCommandSequenceDiagram.png b/docs/images/DeleteCommandSequenceDiagram.png
new file mode 100644
index 0000000000..c55215848f
Binary files /dev/null and b/docs/images/DeleteCommandSequenceDiagram.png differ
diff --git a/docs/images/DeleteCreditCardSequenceDiagram.png b/docs/images/DeleteCreditCardSequenceDiagram.png
new file mode 100644
index 0000000000..2a44a83146
Binary files /dev/null and b/docs/images/DeleteCreditCardSequenceDiagram.png differ
diff --git a/docs/images/DeleteExpenditureSequenceDiagramFinal.png b/docs/images/DeleteExpenditureSequenceDiagramFinal.png
new file mode 100644
index 0000000000..a52a959593
Binary files /dev/null and b/docs/images/DeleteExpenditureSequenceDiagramFinal.png differ
diff --git a/docs/images/DeleteIncomeSequenceDiagram.png b/docs/images/DeleteIncomeSequenceDiagram.png
new file mode 100644
index 0000000000..924446a7e3
Binary files /dev/null and b/docs/images/DeleteIncomeSequenceDiagram.png differ
diff --git a/docs/images/DeleteIncomeSequenceDiagramFinal.png b/docs/images/DeleteIncomeSequenceDiagramFinal.png
new file mode 100644
index 0000000000..924446a7e3
Binary files /dev/null and b/docs/images/DeleteIncomeSequenceDiagramFinal.png differ
diff --git a/docs/images/DeserializeListSequenceDiagramFinal.png b/docs/images/DeserializeListSequenceDiagramFinal.png
new file mode 100644
index 0000000000..d94c01c552
Binary files /dev/null and b/docs/images/DeserializeListSequenceDiagramFinal.png differ
diff --git a/docs/images/Glendon_Profile_Picture.png b/docs/images/Glendon_Profile_Picture.png
new file mode 100644
index 0000000000..e1acfac1d9
Binary files /dev/null and b/docs/images/Glendon_Profile_Picture.png differ
diff --git a/docs/images/KitHan_Profile_Picture.png b/docs/images/KitHan_Profile_Picture.png
new file mode 100644
index 0000000000..4a33c52dff
Binary files /dev/null and b/docs/images/KitHan_Profile_Picture.png differ
diff --git a/docs/images/LimJieRui_Profile_Picture(Final).png b/docs/images/LimJieRui_Profile_Picture(Final).png
new file mode 100644
index 0000000000..f01cccff63
Binary files /dev/null and b/docs/images/LimJieRui_Profile_Picture(Final).png differ
diff --git a/docs/images/ListCommandSequenceDiagram.png b/docs/images/ListCommandSequenceDiagram.png
new file mode 100644
index 0000000000..05ebcea205
Binary files /dev/null and b/docs/images/ListCommandSequenceDiagram.png differ
diff --git a/docs/images/ListCreditCardSequenceDiagram.png b/docs/images/ListCreditCardSequenceDiagram.png
new file mode 100644
index 0000000000..fdb88f2190
Binary files /dev/null and b/docs/images/ListCreditCardSequenceDiagram.png differ
diff --git a/docs/images/ListExpenditureSequenceDiagram.png b/docs/images/ListExpenditureSequenceDiagram.png
new file mode 100644
index 0000000000..a1297e24ce
Binary files /dev/null and b/docs/images/ListExpenditureSequenceDiagram.png differ
diff --git a/docs/images/ListIncomeSequenceDiagram.png b/docs/images/ListIncomeSequenceDiagram.png
new file mode 100644
index 0000000000..84b2c11c1a
Binary files /dev/null and b/docs/images/ListIncomeSequenceDiagram.png differ
diff --git a/docs/images/LoadingSequenceDiagramFinal.png b/docs/images/LoadingSequenceDiagramFinal.png
new file mode 100644
index 0000000000..f169065a83
Binary files /dev/null and b/docs/images/LoadingSequenceDiagramFinal.png differ
diff --git a/docs/images/ParserClassDiagram.png b/docs/images/ParserClassDiagram.png
new file mode 100644
index 0000000000..c39ffec5aa
Binary files /dev/null and b/docs/images/ParserClassDiagram.png differ
diff --git a/docs/images/SavingSequenceDiagramFinal.png b/docs/images/SavingSequenceDiagramFinal.png
new file mode 100644
index 0000000000..f8a8c10720
Binary files /dev/null and b/docs/images/SavingSequenceDiagramFinal.png differ
diff --git a/docs/images/Sean_Profile_Picture.png b/docs/images/Sean_Profile_Picture.png
new file mode 100644
index 0000000000..543e7758e3
Binary files /dev/null and b/docs/images/Sean_Profile_Picture.png differ
diff --git a/docs/images/SerializeListSequenceDiagramFinal.png b/docs/images/SerializeListSequenceDiagramFinal.png
new file mode 100644
index 0000000000..50e564e16e
Binary files /dev/null and b/docs/images/SerializeListSequenceDiagramFinal.png differ
diff --git a/docs/images/StorageClassDiagram.png b/docs/images/StorageClassDiagram.png
new file mode 100644
index 0000000000..822cad5238
Binary files /dev/null and b/docs/images/StorageClassDiagram.png differ
diff --git a/docs/images/UiClassDiagram.png b/docs/images/UiClassDiagram.png
new file mode 100644
index 0000000000..478a83beb6
Binary files /dev/null and b/docs/images/UiClassDiagram.png differ
diff --git a/docs/images/UpdateCommandSequenceDiagram.png b/docs/images/UpdateCommandSequenceDiagram.png
new file mode 100644
index 0000000000..b5de07c723
Binary files /dev/null and b/docs/images/UpdateCommandSequenceDiagram.png differ
diff --git a/docs/images/UpdateCreditCardSequenceDiagramFinal.png b/docs/images/UpdateCreditCardSequenceDiagramFinal.png
new file mode 100644
index 0000000000..7092eef4bf
Binary files /dev/null and b/docs/images/UpdateCreditCardSequenceDiagramFinal.png differ
diff --git a/docs/images/UpdateExpenditureSequenceDiagramFinal.png b/docs/images/UpdateExpenditureSequenceDiagramFinal.png
new file mode 100644
index 0000000000..1bdd3f86ac
Binary files /dev/null and b/docs/images/UpdateExpenditureSequenceDiagramFinal.png differ
diff --git a/docs/images/UpdateIncomeSequenceDiagramFinal.png b/docs/images/UpdateIncomeSequenceDiagramFinal.png
new file mode 100644
index 0000000000..c6c46a84ba
Binary files /dev/null and b/docs/images/UpdateIncomeSequenceDiagramFinal.png differ
diff --git a/docs/puml/AddCommandSequenceDiagram.puml b/docs/puml/AddCommandSequenceDiagram.puml
new file mode 100644
index 0000000000..d084010640
--- /dev/null
+++ b/docs/puml/AddCommandSequenceDiagram.puml
@@ -0,0 +1,58 @@
+@startuml
+!include Style.puml
+
+skinparam sequence {
+ArrowColor black
+participantBorderColor black
+LifelineBorderColor black
+boxBorderColor black
+}
+
+box AddCommand
+participant ":MMM" as MMM MMM_COLOUR
+participant "CommandType:AddCommand" as AddCommand COMMANDS_COLOUR
+end box
+hide footbox
+
+skinparam sequence {
+ArrowColor black
+participantBorderColor black
+LifelineBorderColor black
+boxBorderColor black
+}
+
+MMM -> AddCommand: executeCommand()
+activate AddCommand COMMANDS_COLOUR
+
+alt hasCreditCardFlag()
+AddCommand -> AddCommand: addCreditCard()
+activate AddCommand COMMANDS_COLOUR
+AddCommand --> AddCommand
+deactivate AddCommand
+AddCommand --> MMM
+
+
+else hasIncomeFlag()
+AddCommand -> AddCommand: addIncome()
+activate AddCommand COMMANDS_COLOUR
+AddCommand --> AddCommand
+deactivate AddCommand
+AddCommand --> MMM
+
+
+else hasExpenditureFlag()
+AddCommand -> AddCommand: addExpenditure()
+activate AddCommand COMMANDS_COLOUR
+AddCommand --> AddCommand:
+deactivate AddCommand COMMANDS_COLOUR
+AddCommand --> MMM
+
+else else
+AddCommand --> MMM
+deactivate AddCommand COMMANDS_COLOUR
+note right
+An exception containing warning messages is thrown
+end note
+end
+@enduml
+@enduml
diff --git a/docs/puml/AddCreditCardSequenceDiagram.puml b/docs/puml/AddCreditCardSequenceDiagram.puml
new file mode 100644
index 0000000000..034bb72de1
--- /dev/null
+++ b/docs/puml/AddCreditCardSequenceDiagram.puml
@@ -0,0 +1,58 @@
+@startuml
+!include Style.puml
+
+skinparam sequence {
+ArrowColor black
+participantBorderColor black
+LifelineBorderColor black
+boxBorderColor black
+}
+
+box addCreditCard()
+participant ":MMM" as MMM MMM_COLOUR
+participant "CommandType:AddCommand" as AddCommand COMMANDS_COLOUR
+participant ":GeneralFunctions" as GeneralFunctions HELPER_COLOUR
+participant ":AddCommandInputTest" as AddCommandInputTest HELPER_COLOUR
+participant ":CreditCard" as CreditCard USERFINANCIAL_COLOUR
+participant ":CreditCardList" as CreditCardList DATA_COLOUR
+
+end box
+hide footbox
+MMM -> AddCommand: executeCommand()
+activate AddCommand COMMANDS_COLOUR
+
+AddCommand -> AddCommand: addCreditCard()
+activate AddCommand COMMANDS_COLOUR
+
+AddCommand -> GeneralFunctions: parseInputWithCommandFlag()
+activate GeneralFunctions HELPER_COLOUR
+GeneralFunctions --> AddCommand
+deactivate GeneralFunctions
+
+AddCommand -> AddCommandInputTest: testCreditCardParameters()
+activate AddCommandInputTest HELPER_COLOUR
+
+AddCommandInputTest --> AddCommand
+deactivate AddCommandInputTest
+
+create CreditCard
+AddCommand -> CreditCard: CreditCard()
+activate CreditCard USERFINANCIAL_COLOUR
+CreditCard --> AddCommand
+deactivate CreditCard
+
+AddCommand -> CreditCardList: add()
+activate CreditCardList DATA_COLOUR
+CreditCardList --> AddCommand
+deactivate CreditCardList
+
+note left
+Details of newly added
+Credit Card is printed out
+end note
+
+AddCommand --> AddCommand:
+deactivate AddCommand
+AddCommand --> MMM:
+deactivate AddCommand
+@enduml
diff --git a/docs/puml/AddExpenditureCommandSequenceDiagram.puml b/docs/puml/AddExpenditureCommandSequenceDiagram.puml
new file mode 100644
index 0000000000..5f17bed642
--- /dev/null
+++ b/docs/puml/AddExpenditureCommandSequenceDiagram.puml
@@ -0,0 +1,96 @@
+@startuml
+!include Style.puml
+
+skinparam sequence {
+ArrowColor black
+participantBorderColor black
+LifelineBorderColor black
+boxBorderColor black
+}
+
+box addExpenditure()
+participant ":MMM" as MMM MMM_COLOUR
+participant "CommandType:AddCommand" as AddCommand COMMANDS_COLOUR
+participant ":GeneralFunctions" as GeneralFunctions HELPER_COLOUR
+participant ":AddCommandInputTest" as AddCommandInputTest HELPER_COLOUR
+participant ":Expenditure" as Expenditure USERFINANCIAL_COLOUR
+participant ":ExpenditureList" as ExpenditureList DATA_COLOUR
+participant ":CreditCardList" as CreditCardList DATA_COLOUR
+participant ":CreditCard" as CreditCard USERFINANCIAL_COLOUR
+end box
+hide footbox
+
+MMM -> AddCommand: executeCommand()
+activate AddCommand COMMANDS_COLOUR
+
+AddCommand -> AddCommand: addExpenditure()
+activate AddCommand COMMANDS_COLOUR
+
+AddCommand -> GeneralFunctions: parseInputWithCommandFlag()
+activate GeneralFunctions HELPER_COLOUR
+GeneralFunctions --> AddCommand
+deactivate GeneralFunctions
+
+
+AddCommand -> AddCommandInputTest: testExpenditureParameters()
+activate AddCommandInputTest HELPER_COLOUR
+
+AddCommandInputTest --> AddCommand
+deactivate AddCommandInputTest
+AddCommand --> AddCommand
+deactivate AddCommand COMMANDS_COLOUR
+
+opt PAYMENT_METHOD == cash
+AddCommand -> GeneralFunctions: capitalise(paymentMethod)
+activate GeneralFunctions HELPER_COLOUR
+GeneralFunctions --> AddCommand
+deactivate GeneralFunctions
+end
+AddCommand -> GeneralFunctions: capitalise(category)
+activate GeneralFunctions HELPER_COLOUR
+GeneralFunctions --> AddCommand
+deactivate GeneralFunctions
+
+AddCommand -> GeneralFunctions: formatFloat(amount)
+activate GeneralFunctions HELPER_COLOUR
+GeneralFunctions --> AddCommand
+deactivate GeneralFunctions
+
+create Expenditure
+AddCommand -> Expenditure: Expenditure()
+activate Expenditure USERFINANCIAL_COLOUR
+Expenditure --> AddCommand
+deactivate Expenditure
+
+AddCommand -> ExpenditureList: add()
+activate ExpenditureList DATA_COLOUR
+ExpenditureList --> AddCommand
+deactivate ExpenditureList
+
+note left
+Details of newly added expenditure
+is printed out
+end note
+
+opt PAYMENT_METHOD != cash
+
+AddCommand -> AddCommand: updateCreditCardTotalExpenditure()
+activate AddCommand COMMANDS_COLOUR
+AddCommand -> CreditCardList: get()
+activate CreditCardList DATA_COLOUR
+CreditCardList --> AddCommand
+deactivate CreditCardList
+
+AddCommand -> CreditCard: addExpenditure(amount)
+activate CreditCard USERFINANCIAL_COLOUR
+CreditCard --> AddCommand
+deactivate CreditCard
+
+AddCommand --> AddCommand
+deactivate AddCommand
+
+end
+
+AddCommand --> MMM
+deactivate AddCommand
+@enduml
diff --git a/docs/puml/AddIncomeSequenceDiagram.puml b/docs/puml/AddIncomeSequenceDiagram.puml
new file mode 100644
index 0000000000..6d76e3cc0b
--- /dev/null
+++ b/docs/puml/AddIncomeSequenceDiagram.puml
@@ -0,0 +1,63 @@
+@startuml
+!include Style.puml
+
+hide footbox
+skinparam sequenceMessageAlign center
+
+skinparam sequence {
+ArrowColor black
+participantBorderColor black
+LifelineBorderColor black
+boxBorderColor black
+}
+
+box addIncome()
+participant ":MMM" as MMM MMM_COLOUR
+participant "CommandType:AddCommand" as C COMMANDS_COLOUR
+participant ":GeneralFunctions" as GF HELPER_COLOUR
+participant ":AddCommandInputTests" as IT HELPER_COLOUR
+participant ":Income" as I USERFINANCIAL_COLOUR
+participant ":IncomeList" as IL DATA_COLOUR
+end box
+
+MMM -> C: executeCommand()
+
+activate C COMMANDS_COLOUR
+C -> C: addIncome()
+activate C COMMANDS_COLOUR
+
+C -> GF: parseInputWithCommandFlag()
+activate GF HELPER_COLOUR
+GF --> C
+deactivate GF
+
+C -> IT: testIncomeParameters()
+activate IT HELPER_COLOUR
+
+IT --> C
+deactivate IT
+
+
+create I
+C -> I: Income()
+activate I USERFINANCIAL_COLOUR
+I --> C
+deactivate
+
+C -> IL: add()
+activate IL DATA_COLOUR
+IL --> C
+deactivate
+
+C --> C
+deactivate
+
+note left
+Details of newly added
+Income is printed out
+end note
+
+C --> MMM
+deactivate
+
+@enduml
diff --git a/docs/puml/ArchitectureDiagram.puml b/docs/puml/ArchitectureDiagram.puml
new file mode 100644
index 0000000000..cb6757ee99
--- /dev/null
+++ b/docs/puml/ArchitectureDiagram.puml
@@ -0,0 +1,29 @@
+@startuml
+!include Style.puml
+
+skinparam componentStyle rectangle
+skinparam ArrowColor black
+skinparam ActorBorderColor black
+
+actor User
+folder data.txt
+component MMM MMM_COLOUR
+component Ui UI_COLOUR
+component Parser PARSER_COLOUR
+component Commands COMMANDS_COLOUR
+component Storage STORAGE_COLOUR
+
+note bottom of MMM
+MMM refers to
+MindMyMoney
+end note
+
+User -d-> Ui
+Ui <-> MMM
+MMM <-u-> Parser
+MMM -r-> Commands
+Parser --> Commands
+MMM <-d-> Storage
+Storage <-d-> data.txt
+
+@enduml
diff --git a/docs/puml/CalculateCommandSequenceDiagram.puml b/docs/puml/CalculateCommandSequenceDiagram.puml
new file mode 100644
index 0000000000..e31b63150c
--- /dev/null
+++ b/docs/puml/CalculateCommandSequenceDiagram.puml
@@ -0,0 +1,51 @@
+@startuml
+!include Style.puml
+
+skinparam sequence {
+ArrowColor black
+participantBorderColor black
+LifelineBorderColor black
+boxBorderColor black
+}
+
+box CalculateInputCommand
+participant ":MMM" as MMM MMM_COLOUR
+participant "CommandType:CalculateInputCommand" as CalculateInputCommand COMMANDS_COLOUR
+participant ":Calculations" as Calculations HELPER_COLOUR
+participant ":GeneralFunctions" as GeneralFunctions HELPER_COLOUR
+end box
+hide footbox
+
+MMM -> CalculateInputCommand: executeCommand()
+activate CalculateInputCommand COMMANDS_COLOUR
+
+CalculateInputCommand -> GeneralFunctions: parseInput()
+activate GeneralFunctions HELPER_COLOUR
+GeneralFunctions --> CalculateInputCommand
+deactivate GeneralFunctions HELPER_COLOUR
+
+
+alt contain FLAG_OF_EXPENDITURE_PER_MONTH
+CalculateInputCommand -> Calculations: calculateExpenditure()
+activate Calculations HELPER_COLOUR
+Calculations -> GeneralFunctions: findItemInList()
+activate GeneralFunctions HELPER_COLOUR
+GeneralFunctions --> Calculations
+deactivate GeneralFunctions HELPER_COLOUR
+Calculations -> Calculations: displayCalculationBreakdown()
+activate Calculations HELPER_COLOUR
+Calculations --> Calculations
+deactivate Calculations HELPER_COLOUR
+Calculations --> CalculateInputCommand
+deactivate Calculations HELPER_COLOUR
+CalculateInputCommand --> MMM
+
+else else
+CalculateInputCommand --> MMM
+note right
+An exception containing warning messages is thrown
+end note
+deactivate CalculateInputCommand COMMANDS_COLOUR
+end
+
+@enduml
\ No newline at end of file
diff --git a/docs/puml/CommandClassDiagram.puml b/docs/puml/CommandClassDiagram.puml
new file mode 100644
index 0000000000..2a1a7faf12
--- /dev/null
+++ b/docs/puml/CommandClassDiagram.puml
@@ -0,0 +1,38 @@
+@startuml
+!include Style.puml
+
+skinparam componentStyle rectangle
+skinparam packageStyle rectangle
+skinparam ArrowColor black
+
+package Parser {
+component Parser as parser PARSER_COLOUR
+
+package Command {
+component "{abstract}\nCommand" as command COMMANDS_COLOUR
+component "AddCommand" as addCommand COMMANDS_COLOUR
+component "ByeCommand" as byeCommand COMMANDS_COLOUR
+component "DeleteCommand" as deleteCommand COMMANDS_COLOUR
+component "CalculateInputCommand" as calculateInputCommand COMMANDS_COLOUR
+component "HelpCommand" as helpCommand COMMANDS_COLOUR
+component "ListCommand" as listCommand COMMANDS_COLOUR
+component "UpdateCommand" as updateCommand COMMANDS_COLOUR
+
+}
+
+}
+component MMM MMM_COLOUR
+
+MMM .r.> parser
+
+parser ...> "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t0..1" command: "creates >"
+
+command <|-left- addCommand
+command <|-- byeCommand
+command <|-- deleteCommand
+command <|-- calculateInputCommand
+command <|-up- helpCommand
+command <|-up- listCommand
+command <|-right- updateCommand
+
+@enduml
\ No newline at end of file
diff --git a/docs/puml/ComponentsSequenceDiagram.puml b/docs/puml/ComponentsSequenceDiagram.puml
new file mode 100644
index 0000000000..b6b6d8efa7
--- /dev/null
+++ b/docs/puml/ComponentsSequenceDiagram.puml
@@ -0,0 +1,47 @@
+@startuml
+!include Style.puml
+
+hide footbox
+skinparam sequenceMessageAlign center
+
+skinparam sequence {
+ArrowColor black
+participantBorderColor black
+LifelineBorderColor black
+boxBorderColor black
+ActorBorderColor black
+}
+
+box Components Sequence Diagram
+actor User
+participant ":MMM" as MMM MMM_COLOUR
+participant ":Ui" as Ui UI_COLOUR
+participant ":Parser" as Parser PARSER_COLOUR
+participant ":Commands" as Commands COMMANDS_COLOUR
+participant ":Storage" as Storage STORAGE_COLOUR
+end box
+
+MMM -> Ui: readInput()
+activate Ui UI_COLOUR
+User -> Ui: \t\t add /e /pm cash /c food /d Porridge /a 3 /t 04/04/2022
+Ui --> MMM: add /e /pm cash /c food /d Porridge /a 3 /t 04/04/2022
+deactivate Ui
+
+MMM -> Parser: parseCommand()
+activate Parser PARSER_COLOUR
+Parser --> MMM: AddCommand()
+deactivate Parser
+
+MMM -> Commands: executeCommand()
+activate Commands COMMANDS_COLOUR
+Commands --> MMM
+deactivate Commands
+
+MMM -> Storage: save()
+activate Storage STORAGE_COLOUR
+Storage --> MMM
+deactivate Storage
+
+MMM --> User
+
+@enduml
diff --git a/docs/puml/DeleteCommandSequenceDiagram.puml b/docs/puml/DeleteCommandSequenceDiagram.puml
new file mode 100644
index 0000000000..7e6b9cfce5
--- /dev/null
+++ b/docs/puml/DeleteCommandSequenceDiagram.puml
@@ -0,0 +1,50 @@
+@startuml
+!include Style.puml
+
+hide footbox
+skinparam sequenceMessageAlign center
+
+skinparam sequence {
+ArrowColor black
+participantBorderColor black
+LifelineBorderColor black
+boxBorderColor black
+}
+
+box DeleteCommand
+participant ":MMM" as MMM MMM_COLOUR
+participant "CommandType:DeleteCommand" as C COMMANDS_COLOUR
+end box
+
+MMM -> C: executeCommand()
+activate C COMMANDS_COLOUR
+
+alt hasExpensesFlag()
+C -> C: deleteExpenditure()
+activate C COMMANDS_COLOUR
+C --> C
+deactivate C
+
+else hasCreditCardListFlag()
+C -> C: deleteCreditCard()
+activate C COMMANDS_COLOUR
+C --> C
+deactivate C
+
+else hasIncomeListFlag()
+C -> C: deleteIncome()
+activate C COMMANDS_COLOUR
+C --> C
+deactivate C
+
+else else
+C --> MMM
+note right
+An exception containing warning messages is thrown
+end note
+
+end
+C --> MMM
+deactivate C
+
+@enduml
diff --git a/docs/puml/DeleteCreditCardSequenceDiagram.puml b/docs/puml/DeleteCreditCardSequenceDiagram.puml
new file mode 100644
index 0000000000..db752df9af
--- /dev/null
+++ b/docs/puml/DeleteCreditCardSequenceDiagram.puml
@@ -0,0 +1,43 @@
+@startuml
+!include Style.puml
+
+hide footbox
+skinparam sequenceMessageAlign center
+
+skinparam sequence {
+ArrowColor black
+participantBorderColor black
+LifelineBorderColor black
+boxBorderColor black
+}
+
+box deleteCreditCard()
+participant ":MMM" as MMM MMM_COLOUR
+participant "CommandType:DeleteCommand" as C COMMANDS_COLOUR
+participant ":CreditCardList" as CreditCardList DATA_COLOUR
+end box
+
+MMM -> C: executeCommand()
+activate C COMMANDS_COLOUR
+
+C -> C: deleteCreditCard()
+activate C COMMANDS_COLOUR
+
+C -> CreditCardList: delete()
+activate CreditCardList DATA_COLOUR
+CreditCardList --> C
+deactivate CreditCardList
+
+C --> C
+
+note left
+Details of deleted
+Credit Card is printed out
+end note
+
+deactivate C
+
+C --> MMM
+deactivate C
+
+@enduml
diff --git a/docs/puml/DeleteExpenditureSequenceDiagram.puml b/docs/puml/DeleteExpenditureSequenceDiagram.puml
new file mode 100644
index 0000000000..4307159abe
--- /dev/null
+++ b/docs/puml/DeleteExpenditureSequenceDiagram.puml
@@ -0,0 +1,67 @@
+@startuml
+!include Style.puml
+
+hide footbox
+skinparam sequenceMessageAlign center
+
+skinparam sequence {
+ArrowColor black
+participantBorderColor black
+LifelineBorderColor black
+boxBorderColor black
+}
+
+box deleteExpenditure()
+participant ":MMM" as MMM MMM_COLOUR
+participant "CommandType:DeleteCommand" as C COMMANDS_COLOUR
+participant ":ExpenditureList" as ExpenditureList DATA_COLOUR
+participant ":Expenditure" as Expenditure USERFINANCIAL_COLOUR
+participant ":CreditCardList" as CreditCardList DATA_COLOUR
+participant ":CreditCard" as CreditCard USERFINANCIAL_COLOUR
+end box
+
+MMM -> C: executeCommand()
+activate C COMMANDS_COLOUR
+
+C -> C: deleteExpenditure()
+activate C COMMANDS_COLOUR
+
+C -> ExpenditureList: get()
+activate ExpenditureList DATA_COLOUR
+ExpenditureList --> C
+deactivate ExpenditureList
+
+C -> Expenditure: getPaymentMethod()
+activate Expenditure USERFINANCIAL_COLOUR
+Expenditure --> C
+deactivate Expenditure
+
+opt PAYMENT_METHOD != cash
+C -> C: updateCreditCardTotalExpenditure()
+activate C COMMANDS_COLOUR
+
+C -> CreditCardList: get()
+activate CreditCardList DATA_COLOUR
+CreditCardList --> C
+deactivate CreditCardList
+
+C -> CreditCard: deductExpenditure()
+activate CreditCard USERFINANCIAL_COLOUR
+CreditCard --> C
+deactivate CreditCard
+
+C --> C
+note right
+Details of deleted
+expenditure is printed out
+end note
+deactivate C
+end
+
+C --> C
+deactivate C
+
+C --> MMM
+deactivate C
+
+@enduml
diff --git a/docs/puml/DeleteIncomeSequenceDiagram.puml b/docs/puml/DeleteIncomeSequenceDiagram.puml
new file mode 100644
index 0000000000..880c898548
--- /dev/null
+++ b/docs/puml/DeleteIncomeSequenceDiagram.puml
@@ -0,0 +1,43 @@
+@startuml
+!include Style.puml
+
+hide footbox
+skinparam sequenceMessageAlign center
+
+skinparam sequence {
+ArrowColor black
+participantBorderColor black
+LifelineBorderColor black
+boxBorderColor black
+}
+
+box deleteIncome()
+participant ":MMM" as MMM MMM_COLOUR
+participant "CommandType:DeleteCommand" as C COMMANDS_COLOUR
+participant ":IncomeList" as IL DATA_COLOUR
+end box
+
+MMM -> C: executeCommand()
+activate C COMMANDS_COLOUR
+
+C -> C: deleteIncome()
+activate C COMMANDS_COLOUR
+
+C -> IL: delete()
+activate IL DATA_COLOUR
+IL --> C
+deactivate IL
+
+C --> C
+
+note right
+Details of deleted
+Income is printed out
+end note
+
+deactivate C
+
+C --> MMM
+deactivate C
+
+@enduml
diff --git a/docs/puml/DeserializeListSequenceDiagram.puml b/docs/puml/DeserializeListSequenceDiagram.puml
new file mode 100644
index 0000000000..ba180d845d
--- /dev/null
+++ b/docs/puml/DeserializeListSequenceDiagram.puml
@@ -0,0 +1,41 @@
+@startuml
+!include Style.puml
+
+skinparam sequence {
+ArrowColor black
+participantBorderColor black
+LifelineBorderColor black
+boxBorderColor black
+}
+
+box Deserialize List
+participant ":ExpenditureList" as ExpenditureList DATA_COLOUR
+participant ":SerializerFunctions" as SerializerFunctions HELPER_COLOUR
+participant ":Expenditure" as Expenditure USERFINANCIAL_COLOUR
+participant ":PropertyList" as PropertyList DATA_COLOUR
+end box
+hide footbox
+
+[-> ExpenditureList : deserializeFrom()
+activate ExpenditureList DATA_COLOUR
+ExpenditureList -> SerializerFunctions : convertInputToList()
+activate SerializerFunctions HELPER_COLOUR
+loop until end line is read
+ SerializerFunctions -> Expenditure ** : deserialize()
+ activate Expenditure USERFINANCIAL_COLOUR
+ deactivate Expenditure
+
+ Expenditure -> PropertyList ** : deserialize()
+ activate PropertyList DATA_COLOUR
+ deactivate PropertyList
+ PropertyList --> Expenditure --
+ Expenditure -> PropertyList: getValue()
+ activate PropertyList DATA_COLOUR
+ PropertyList --> Expenditure --
+
+ Expenditure --> SerializerFunctions --
+end
+SerializerFunctions --> ExpenditureList --
+[<-- ExpenditureList --
+
+@enduml
diff --git a/docs/puml/ListCommandSequenceDiagram.puml b/docs/puml/ListCommandSequenceDiagram.puml
new file mode 100644
index 0000000000..3668bb07d9
--- /dev/null
+++ b/docs/puml/ListCommandSequenceDiagram.puml
@@ -0,0 +1,50 @@
+@startuml
+!include Style.puml
+
+hide footbox
+skinparam sequenceMessageAlign center
+
+skinparam sequence {
+ArrowColor black
+participantBorderColor black
+LifelineBorderColor black
+boxBorderColor black
+}
+
+box ListCommand
+participant ":MMM" as MMM MMM_COLOUR
+participant "CommandType:ListCommand" as C COMMANDS_COLOUR
+end box
+
+MMM -> C: executeCommand()
+activate C COMMANDS_COLOUR
+
+alt hasExpensesFlag()
+C -> C: printExpenditureList()
+activate C COMMANDS_COLOUR
+C --> C
+deactivate C
+
+else hasCreditCardListFlag()
+C -> C: printCreditCardList()
+activate C COMMANDS_COLOUR
+C --> C
+deactivate C
+
+else hasIncomeListFlag()
+C -> C: printIncomeList()
+activate C COMMANDS_COLOUR
+C --> C
+deactivate C
+
+else else
+C --> MMM
+note right
+An exception containing warning messages is thrown
+end note
+
+end
+C --> MMM
+deactivate C
+
+@enduml
diff --git a/docs/puml/ListCreditCardSequenceDiagram.puml b/docs/puml/ListCreditCardSequenceDiagram.puml
new file mode 100644
index 0000000000..75a6f3b424
--- /dev/null
+++ b/docs/puml/ListCreditCardSequenceDiagram.puml
@@ -0,0 +1,36 @@
+@startuml
+!include Style.puml
+
+hide footbox
+skinparam sequenceMessageAlign center
+
+skinparam sequence {
+ArrowColor black
+participantBorderColor black
+LifelineBorderColor black
+boxBorderColor black
+}
+
+box listCreditCard()
+participant ":MMM" as MMM MMM_COLOUR
+participant "CommandType:ListCommand" as C COMMANDS_COLOUR
+end box
+
+MMM -> C: executeCommand()
+activate C COMMANDS_COLOUR
+
+C -> C: printCreditCardList()
+activate C COMMANDS_COLOUR
+
+C -> C: creditCardListToString()
+activate C COMMANDS_COLOUR
+
+C --> C: listInString
+deactivate C
+
+C --> C
+deactivate C
+
+C --> MMM
+deactivate C
+@enduml
diff --git a/docs/puml/ListExpenditureSequenceDiagram.puml b/docs/puml/ListExpenditureSequenceDiagram.puml
new file mode 100644
index 0000000000..834f7fe07d
--- /dev/null
+++ b/docs/puml/ListExpenditureSequenceDiagram.puml
@@ -0,0 +1,44 @@
+@startuml
+!include Style.puml
+
+hide footbox
+skinparam sequenceMessageAlign center
+
+skinparam sequence {
+ArrowColor black
+participantBorderColor black
+LifelineBorderColor black
+boxBorderColor black
+}
+
+box listExpenditure()
+participant ":MMM" as MMM MMM_COLOUR
+participant "CommandType:ListCommand" as C COMMANDS_COLOUR
+end box
+
+MMM -> C: executeCommand()
+activate C COMMANDS_COLOUR
+
+C -> C: printExpenditureList()
+activate C COMMANDS_COLOUR
+
+alt listInput.equals("/e")
+C -> C: listString()
+activate C COMMANDS_COLOUR
+C --> C: listInString
+deactivate C
+
+else else
+C -> C: listStringWithDate()
+activate C COMMANDS_COLOUR
+C --> C: listInString
+deactivate C
+end
+
+C --> C
+deactivate C
+
+C --> MMM
+deactivate C
+
+@enduml
diff --git a/docs/puml/ListIncomeSequenceDiagram.puml b/docs/puml/ListIncomeSequenceDiagram.puml
new file mode 100644
index 0000000000..d5131ff970
--- /dev/null
+++ b/docs/puml/ListIncomeSequenceDiagram.puml
@@ -0,0 +1,37 @@
+@startuml
+!include Style.puml
+
+hide footbox
+skinparam sequenceMessageAlign center
+
+skinparam sequence {
+ArrowColor black
+participantBorderColor black
+LifelineBorderColor black
+boxBorderColor black
+}
+
+box listIncome()
+participant ":MMM" as MMM MMM_COLOUR
+participant "CommandType:ListCommand" as C COMMANDS_COLOUR
+end box
+
+MMM -> C: executeCommand()
+activate C COMMANDS_COLOUR
+
+C -> C: printIncomeList()
+activate C COMMANDS_COLOUR
+
+C -> C: incomeListToString()
+activate C COMMANDS_COLOUR
+
+C --> C: listInString
+deactivate C
+
+C --> C
+deactivate C
+
+C --> MMM
+deactivate C
+
+@enduml
diff --git a/docs/puml/LoadingSequenceDiagram.puml b/docs/puml/LoadingSequenceDiagram.puml
new file mode 100644
index 0000000000..19ff8f632a
--- /dev/null
+++ b/docs/puml/LoadingSequenceDiagram.puml
@@ -0,0 +1,41 @@
+@startuml
+!include Style.puml
+
+skinparam sequence {
+ArrowColor black
+participantBorderColor black
+LifelineBorderColor black
+boxBorderColor black
+}
+
+box Loading
+participant ":MMM" as MMM MMM_COLOUR
+participant ":Storage" as Storage STORAGE_COLOUR
+participant ":User" as User USERFINANCIAL_COLOUR
+participant ":ExpenditureList" as ExpenditureList DATA_COLOUR
+participant ":CreditCardList" as CreditCardList DATA_COLOUR
+participant ":IncomeList" as IncomeList DATA_COLOUR
+end box
+hide footbox
+
+MMM -> Storage ** : Storage()
+activate Storage STORAGE_COLOUR
+Storage --> MMM --
+
+MMM -> Storage : load()
+activate Storage STORAGE_COLOUR
+Storage -> User : deserializeFrom()
+activate User USERFINANCIAL_COLOUR
+User -> ExpenditureList : deserializeFrom()
+activate ExpenditureList DATA_COLOUR
+ref over User, ExpenditureList : deserialize list
+ExpenditureList --> User --
+User -> CreditCardList : deserializeFrom()
+activate CreditCardList DATA_COLOUR
+CreditCardList --> User --
+User -> IncomeList : deserializeFrom()
+activate IncomeList DATA_COLOUR
+IncomeList --> User --
+User --> Storage --
+Storage --> MMM --
+@enduml
diff --git a/docs/puml/ParserClassDiagram.puml b/docs/puml/ParserClassDiagram.puml
new file mode 100644
index 0000000000..54483204d4
--- /dev/null
+++ b/docs/puml/ParserClassDiagram.puml
@@ -0,0 +1,39 @@
+@startuml
+!include Style.puml
+
+skinparam componentStyle rectangle
+skinparam packageStyle rectangle
+skinparam ArrowColor black
+
+package Parser {
+component Parser as P PARSER_COLOUR
+
+Package User {
+component User as U USERFINANCIAL_COLOUR
+component ExpenditureList DATA_COLOUR
+component Expenditure USERFINANCIAL_COLOUR
+component CreditCardList DATA_COLOUR
+component CreditCard USERFINANCIAL_COLOUR
+component IncomeList DATA_COLOUR
+component Income USERFINANCIAL_COLOUR
+}
+
+}
+component MMM MMM_COLOUR
+component Commands COMMANDS_COLOUR
+component GeneralFunctions HELPER_COLOUR
+
+MMM .d.> P
+P .r.> GeneralFunctions: "uses >"
+P .d.> Commands: "instantiates >"
+P .d.> U
+
+U -d-> "0..1" ExpenditureList: "has >"
+U -d-> "0..1" CreditCardList: "has >"
+U -d-> "0..1" IncomeList: "has >"
+
+ExpenditureList .d.> Expenditure
+CreditCardList .d.> CreditCard
+IncomeList .d.> Income
+
+@enduml
diff --git a/docs/puml/SavingSequenceDiagram.puml b/docs/puml/SavingSequenceDiagram.puml
new file mode 100644
index 0000000000..345b433628
--- /dev/null
+++ b/docs/puml/SavingSequenceDiagram.puml
@@ -0,0 +1,40 @@
+@startuml
+!include Style.puml
+
+skinparam sequence {
+ArrowColor black
+participantBorderColor black
+LifelineBorderColor black
+boxBorderColor black
+}
+
+box Saving
+participant ":MMM" as MMM MMM_COLOUR
+participant ":Storage" as Storage STORAGE_COLOUR
+participant ":User" as User USERFINANCIAL_COLOUR
+participant ":ExpenditureList" as ExpenditureList DATA_COLOUR
+participant ":CreditCardList" as CreditCardList DATA_COLOUR
+participant ":IncomeList" as IncomeList DATA_COLOUR
+end box
+hide footbox
+
+MMM -> Storage : save()
+activate Storage STORAGE_COLOUR
+Storage -> User : serialize()
+activate User USERFINANCIAL_COLOUR
+User -> ExpenditureList : serialize()
+activate ExpenditureList DATA_COLOUR
+
+ref over User, ExpenditureList : serialize list
+ExpenditureList --> User --
+User -> CreditCardList : serialize()
+activate CreditCardList DATA_COLOUR
+
+CreditCardList --> User --
+User -> IncomeList : serialize()
+activate IncomeList DATA_COLOUR
+
+IncomeList --> User --
+User --> Storage --
+deactivate Storage
+@enduml
diff --git a/docs/puml/SerializeListSequenceDiagram.puml b/docs/puml/SerializeListSequenceDiagram.puml
new file mode 100644
index 0000000000..10267e515c
--- /dev/null
+++ b/docs/puml/SerializeListSequenceDiagram.puml
@@ -0,0 +1,43 @@
+@startuml
+!include Style.puml
+
+skinparam sequence {
+ArrowColor black
+participantBorderColor black
+LifelineBorderColor black
+boxBorderColor black
+}
+
+box Serialize List
+participant ":ExpenditureList" as ExpenditureList DATA_COLOUR
+participant ":SerializerFunctions" as SerializerFunctions HELPER_COLOUR
+participant ":Expenditure" as Expenditure USERFINANCIAL_COLOUR
+participant ":PropertyList" as PropertyList DATA_COLOUR
+end box
+hide footbox
+
+[-> ExpenditureList : serialize()
+activate ExpenditureList DATA_COLOUR
+ExpenditureList -> SerializerFunctions : addListToStringBuilder()
+activate SerializerFunctions HELPER_COLOUR
+loop for each element in list
+ SerializerFunctions -> Expenditure : serialize()
+ activate Expenditure USERFINANCIAL_COLOUR
+
+ Expenditure -> PropertyList ** : PropertyList()
+ activate PropertyList DATA_COLOUR
+ deactivate PropertyList
+ PropertyList --> Expenditure --
+ Expenditure -> PropertyList : setValue()
+ activate PropertyList DATA_COLOUR
+ PropertyList --> Expenditure --
+ Expenditure -> PropertyList : serialize()
+ activate PropertyList DATA_COLOUR
+ PropertyList --> Expenditure --
+
+ Expenditure --> SerializerFunctions --
+end
+SerializerFunctions --> ExpenditureList --
+[<-- ExpenditureList --
+
+@enduml
diff --git a/docs/puml/StorageClassDiagram.puml b/docs/puml/StorageClassDiagram.puml
new file mode 100644
index 0000000000..d60f723f20
--- /dev/null
+++ b/docs/puml/StorageClassDiagram.puml
@@ -0,0 +1,30 @@
+@startuml
+!include Style.puml
+
+allowmixing
+skinparam componentStyle rectangle
+skinparam classAttributeIconSize 0
+hide circle
+
+skinparam class {
+ArrowColor black
+BorderColor black
+}
+
+component MMM MMM_COLOUR
+
+component Storage {
+class Storage
+}
+
+class Storage STORAGE_COLOUR {
+-storageFile: File
+
++load(): User
++save(User: user): void
+}
+
+
+MMM -right-> "1" Storage : loads/saves >
+
+@enduml
\ No newline at end of file
diff --git a/docs/puml/Style.puml b/docs/puml/Style.puml
new file mode 100644
index 0000000000..2001f8da62
--- /dev/null
+++ b/docs/puml/Style.puml
@@ -0,0 +1,14 @@
+!define LOGIC_COLOR #3333C4
+!define LOGIC_COLOR_T1 #7777DB
+!define LOGIC_COLOR_T2 #5252CE
+!define LOGIC_COLOR_T3 #1616B0
+!define LOGIC_COLOR_T4 #101086
+
+!define MMM_COLOUR #dddddd
+!define PARSER_COLOUR #00b0f0
+!define UI_COLOUR #92d050
+!define COMMANDS_COLOUR #ff5050
+!define STORAGE_COLOUR #ffc000
+!define HELPER_COLOUR #e6d6c2
+!define USERFINANCIAL_COLOUR #ca8dfd
+!define DATA_COLOUR #aeed00
diff --git a/docs/puml/UiClassDiagram.puml b/docs/puml/UiClassDiagram.puml
new file mode 100644
index 0000000000..e58dcd84bd
--- /dev/null
+++ b/docs/puml/UiClassDiagram.puml
@@ -0,0 +1,35 @@
+@startuml
+!include Style.puml
+
+allowmixing
+skinparam componentStyle rectangle
+skinparam classAttributeIconSize 0
+hide circle
+
+skinparam class {
+ArrowColor black
+BorderColor black
+}
+
+component MMM MMM_COLOUR
+
+component Ui {
+class Ui
+class "{abstract}\nPrintStrings"
+}
+
+class Ui UI_COLOUR {
+{static} +PROMPT: String = ">"
+
++printIntro(): void
++readInput(): String
+}
+
+class "{abstract}\nPrintStrings" UI_COLOUR {
+{static} +tips:String[]
+}
+
+MMM --> "1" Ui
+Ui -> "{abstract}\nPrintStrings": uses tips >
+
+@enduml
diff --git a/docs/puml/UpdateCommandSequenceDiagram.puml b/docs/puml/UpdateCommandSequenceDiagram.puml
new file mode 100644
index 0000000000..ba98068247
--- /dev/null
+++ b/docs/puml/UpdateCommandSequenceDiagram.puml
@@ -0,0 +1,51 @@
+@startuml
+!include Style.puml
+
+hide footbox
+skinparam sequenceMessageAlign center
+
+skinparam sequence {
+ArrowColor black
+participantBorderColor black
+LifelineBorderColor black
+boxBorderColor black
+}
+
+box UpdateCommand
+participant ":MMM" as MMM MMM_COLOUR
+participant "CommandType:UpdateCommand" as C COMMANDS_COLOUR
+end box
+
+MMM -> C: executeCommand()
+activate C COMMANDS_COLOUR
+
+alt hasExpensesFlag()
+C -> C: updateExpenditure()
+activate C COMMANDS_COLOUR
+C --> C
+deactivate C
+
+else hasCreditCardListFlag()
+C -> C: updateCreditCard()
+activate C COMMANDS_COLOUR
+C --> C
+deactivate C
+
+else hasIncomeListFlag()
+C -> C: updateIncome()
+activate C COMMANDS_COLOUR
+C --> C
+deactivate C
+
+else else
+C --> MMM
+note right
+An exception containing warning messages is thrown
+end note
+
+end
+C --> MMM
+deactivate C
+deactivate MMM
+
+@enduml
diff --git a/docs/puml/UpdateCreditCardSequenceDiagram.puml b/docs/puml/UpdateCreditCardSequenceDiagram.puml
new file mode 100644
index 0000000000..47fdeec702
--- /dev/null
+++ b/docs/puml/UpdateCreditCardSequenceDiagram.puml
@@ -0,0 +1,53 @@
+@startuml
+!include Style.puml
+
+skinparam sequence {
+ArrowColor black
+participantBorderColor black
+LifelineBorderColor black
+boxBorderColor black
+}
+
+box updateCreditCard()
+participant ":MMM" as MMM MMM_COLOUR
+participant "CommandType:UpdateCommand" as updateCommand COMMANDS_COLOUR
+participant ":GeneralFunctions" as GeneralFunctions HELPER_COLOUR
+participant ":CreditCard" as CreditCard USERFINANCIAL_COLOUR
+participant ":CreditCardList" as CreditCardList DATA_COLOUR
+
+end box
+hide footbox
+
+MMM -> updateCommand: executeCommand()
+activate updateCommand COMMANDS_COLOUR
+
+updateCommand -> updateCommand: updateCreditCard()
+activate updateCommand COMMANDS_COLOUR
+
+updateCommand -> GeneralFunctions: parseInputWithCommandFlag()
+activate GeneralFunctions HELPER_COLOUR
+GeneralFunctions --> updateCommand
+deactivate GeneralFunctions
+
+create CreditCard
+updateCommand -> CreditCard: CreditCard()
+activate CreditCard USERFINANCIAL_COLOUR
+CreditCard --> updateCommand
+deactivate CreditCard
+
+updateCommand -> CreditCardList: set()
+activate CreditCardList DATA_COLOUR
+CreditCardList --> updateCommand
+deactivate CreditCardList
+
+updateCommand --> updateCommand
+deactivate updateCommand
+
+note left
+Details of newly updated
+expenditure is printed out
+end note
+
+updateCommand --> MMM
+deactivate updateCommand
+@enduml
diff --git a/docs/puml/UpdateExpenditureSequenceDiagram.puml b/docs/puml/UpdateExpenditureSequenceDiagram.puml
new file mode 100644
index 0000000000..c4273f28a4
--- /dev/null
+++ b/docs/puml/UpdateExpenditureSequenceDiagram.puml
@@ -0,0 +1,83 @@
+@startuml
+!include Style.puml
+
+skinparam sequence {
+ArrowColor black
+participantBorderColor black
+LifelineBorderColor black
+boxBorderColor black
+}
+
+box updateExpenditure()
+participant ":MMM" as MMM MMM_COLOUR
+participant "CommandType:UpdateCommand" as updateCommand COMMANDS_COLOUR
+participant ":GeneralFunctions" as GeneralFunctions HELPER_COLOUR
+participant ":AddCommandInputTest" as AddCommandInputTest HELPER_COLOUR
+participant ":Expenditure" as Expenditure USERFINANCIAL_COLOUR
+participant ":ExpenditureList" as ExpenditureList DATA_COLOUR
+
+end box
+hide footbox
+
+MMM -> updateCommand: executeCommand()
+activate updateCommand COMMANDS_COLOUR
+
+updateCommand -> updateCommand: updateExpenditure()
+activate updateCommand COMMANDS_COLOUR
+
+updateCommand -> GeneralFunctions: parseInputWithCommandFlag()
+activate GeneralFunctions HELPER_COLOUR
+GeneralFunctions --> updateCommand
+deactivate GeneralFunctions
+
+
+updateCommand -> AddCommandInputTest: testUpdateExpenditureParameters()
+activate AddCommandInputTest HELPER_COLOUR
+
+
+AddCommandInputTest --> updateCommand
+deactivate AddCommandInputTest
+updateCommand --> updateCommand
+deactivate updateCommand COMMANDS_COLOUR
+
+opt NEW_PAYMENT_METHOD == cash
+updateCommand -> GeneralFunctions: capitalise(paymentMethod)
+activate GeneralFunctions HELPER_COLOUR
+GeneralFunctions --> updateCommand
+deactivate GeneralFunctions
+end
+
+updateCommand -> GeneralFunctions: capitalise(category)
+activate GeneralFunctions HELPER_COLOUR
+GeneralFunctions --> updateCommand
+deactivate GeneralFunctions
+
+updateCommand -> GeneralFunctions: formatFloat(amount)
+activate GeneralFunctions HELPER_COLOUR
+GeneralFunctions --> updateCommand
+deactivate GeneralFunctions
+
+updateCommand -> updateCommand: updatePaymentMethod()
+activate updateCommand COMMANDS_COLOUR
+updateCommand --> updateCommand
+deactivate updateCommand
+
+create Expenditure
+updateCommand -> Expenditure: Expenditure()
+activate Expenditure USERFINANCIAL_COLOUR
+Expenditure --> updateCommand
+deactivate Expenditure
+
+updateCommand -> ExpenditureList: set()
+activate ExpenditureList DATA_COLOUR
+ExpenditureList --> updateCommand
+deactivate ExpenditureList
+
+note left
+Details of newly updated
+expenditure is printed out
+end note
+
+updateCommand --> MMM
+deactivate updateCommand
+@enduml
diff --git a/docs/puml/UpdateIncomeSequenceDiagram.puml b/docs/puml/UpdateIncomeSequenceDiagram.puml
new file mode 100644
index 0000000000..7df93b9ce3
--- /dev/null
+++ b/docs/puml/UpdateIncomeSequenceDiagram.puml
@@ -0,0 +1,66 @@
+@startuml
+!include Style.puml
+
+skinparam sequence {
+ArrowColor black
+participantBorderColor black
+LifelineBorderColor black
+boxBorderColor black
+}
+
+box updateIncome()
+participant ":MMM" as MMM MMM_COLOUR
+participant "CommandType:UpdateCommand" as updateCommand COMMANDS_COLOUR
+participant ":GeneralFunctions" as GeneralFunctions HELPER_COLOUR
+participant ":AddCommandInputTest" as AddCommandInputTest HELPER_COLOUR
+participant ":Income" as Income USERFINANCIAL_COLOUR
+participant ":IncomeList" as IncomeList DATA_COLOUR
+
+end box
+hide footbox
+
+MMM -> updateCommand: executeCommand()
+activate updateCommand COMMANDS_COLOUR
+
+updateCommand -> updateCommand: updateIncome()
+activate updateCommand COMMANDS_COLOUR
+
+updateCommand -> GeneralFunctions: parseInputWithCommandFlag()
+activate GeneralFunctions HELPER_COLOUR
+GeneralFunctions --> updateCommand
+deactivate GeneralFunctions
+
+
+updateCommand -> AddCommandInputTest: testUpdateIncomeParameters()
+activate AddCommandInputTest HELPER_COLOUR
+
+AddCommandInputTest --> updateCommand
+deactivate AddCommandInputTest
+
+updateCommand -> GeneralFunctions: capitalise(inputCategory)
+activate GeneralFunctions HELPER_COLOUR
+GeneralFunctions --> updateCommand
+deactivate GeneralFunctions
+
+create Income
+updateCommand -> Income: Income()
+activate Income USERFINANCIAL_COLOUR
+Income --> updateCommand
+deactivate Income
+
+updateCommand -> IncomeList: set()
+activate IncomeList DATA_COLOUR
+IncomeList --> updateCommand
+deactivate IncomeList
+
+updateCommand --> updateCommand
+deactivate updateCommand
+
+note left
+Details of newly updated
+Income is printed out
+end note
+
+updateCommand --> MMM
+deactivate updateCommand
+@enduml
diff --git a/docs/team/danbaterisna.md b/docs/team/danbaterisna.md
new file mode 100644
index 0000000000..afdae388e8
--- /dev/null
+++ b/docs/team/danbaterisna.md
@@ -0,0 +1,64 @@
+# Dan Baterisna - Project Portfolio Page
+
+## Overview
+`MindMyMoney` (M3) is a desktop app for managing your personal finances, optimized for use via a Command Line
+Interface (CLI). You can use it to track your expenditures across multiple payment methods, calculate monthly
+expenditure, and set financial goals.
+
+
+
+## Contribution Summary
+
+### Code Contribution
+Code Contribution can be viewed [here](https://nus-cs2113-ay2122s2.github.io/tp-dashboard/?search=danbaterisna&breakdown=true)
+
+Enhancements implemented:
+
+1. Implemented initial `UpdateCommand`.
+ - Accepted an index, new description and new amount for an expenditure, and updated
+2. Added `PropertyList`
+ - List of key-value pairs that can be saved to and loaded from a string
+ - Forms underlying basis for de/serialization.
+3. Implemented `Storage` component.
+ - Has `save` and `load` methods, which save a `User` to and load a `User` from a file, respectively.
+4. Implemented `serialize`, `deserialize` and `deserializeFrom` methods for `UserFinancial` classes.
+ - `User`, `ExpenditureList`, `CreditCardList`, `IncomeList`, `Expenditure`, `CreditCard`, and `Income`
+ can now save themselves to and load themselves from either strings or `Scanner`s.
+5. Implemented `SerializerFunctions`.
+ - Abstracts out common functionality needed by de/serializers, improving DRY and SLAP.
+ - Implements `addListToStringBuilder` and `convertInputToList` methods.
+6. Implemented `ValidatorFunctions`.
+ - Abstracts out common functionality used to check that the content of the save file is valid.
+7. Added JUnit tests.
+ - Added JUnit tests for `UpdateCommand`, `Storage`, `Expenditure`, and `PropertyList`.
+ - Updated `UpdateCommand` and `Expenditure` tests during transition to v2.0.
+ - Ensures that the code is bug free, and helps in regression testing future iterations.
+8. Miscellaneous
+ - Wrote JavaDoc comments for classes and methods created.
+ - Fixed bugs raised during PE-D.
+ - Updated command formats shown by `help` command during transition to v2.1.
+
+
+### UG Contribution
+1. Added short explanation of save file format.
+
+### DG Contribution
+
+1. Added sequence diagrams for loading.
+ - Explains interaction between classes for loading, as well as hierarchical nature
+ of the loading design.
+2. Added sequence diagrams for saving.
+ - Explains interaction between classes for saving, as well as hierarchical nature
+ of the saving design.
+
+### Team-Based Tasks Contribution
+
+1. Wrote manual testing instructions.
+
+
+### Miscellaneous Contribution
+PR reviews (with non-trivial comments):
+[#41](https://github.com/AY2122S2-CS2113T-T10-4/tp/pull/41), [#69](https://github.com/AY2122S2-CS2113T-T10-4/tp/pull/69),
+[#206](https://github.com/AY2122S2-CS2113T-T10-4/tp/pull/206)
+
+High number of bugs found (11) during PE-Dry testing for `AY2122S2-CS2113-T11-2` can be viewed [here](https://github.com/danbaterisna/ped/issues).
\ No newline at end of file
diff --git a/docs/team/glendonnotglen.md b/docs/team/glendonnotglen.md
new file mode 100644
index 0000000000..fe5d938175
--- /dev/null
+++ b/docs/team/glendonnotglen.md
@@ -0,0 +1,63 @@
+# Ng Zhao Zhi Glendon - Project Portfolio Page
+
+## Overview
+`MindMyMoney` (M3) is a desktop app for managing your personal finances, optimized for use via a Command Line
+Interface (CLI). You can use it to track your expenditures across multiple payment methods, calculate monthly
+expenditure, and set financial goals.
+
+## Contribution Summary
+
+### Code Contribution
+Code Contribution can be viewed [here](https://nus-cs2113-ay2122s2.github.io/tp-dashboard/?search=glendonnotglen&breakdown=true).
+
+Enhancements implemented:
+1. Added `Credit Card` class.
+ - Implemented methods for adding, listing, updating and deleting Credit Cards.
+ - Implemented relevant testing methods for input validations for adding and updating Credit Cards.
+2. Added code structure for `ListCommand`.
+ - Added JUnit tests for `HelpCommand`, `AddCommand` and `DeleteCommand`.
+3. Updated `HelpCommand` instructions for `help /e` and `help /cc`.
+ - Added JUnit tests for `HelpCommand`, `AddCommand` and `DeleteCommand`.
+4. Updated `Parser.java` to detect varying sizes of flags.
+ - Adds versatility to our program's input.
+5. Miscellaneous
+ - Added JUnit tests for `DeleteCommand`, `AddCommand`, `ListCommand` and `Parser`.
+ - Updated JUnit testing for multiple tests, including `CalculateInput`, `UpdateCommand`, and `HelpCommand`
+ - Wrote JavaDoc comments for a majority of methods.
+ - Fixed bugs raised during PE-Dry testing pertaining to `CreditCard` addition and improving invalid input handling.
+
+### UG Contribution
+1. Wrote the `Credit Card` documentations for Users.
+ - Add, List, Update, and Delete commands.
+ - Updated Command Summary (Credit Card) table.
+
+2. Streamlined content page.
+ - Standardised content throughout the User Guide.
+ - Improved User Guide as per feedback from Professor.
+ - Proofread and reduced Grammatical errors.
+
+### DG Contribution
+1. Wrote `Delete` section.
+ - Includes deleting `Expenditure`, `Credit Card`, and `Income`.
+ - Included Design considerations.
+ - Designed relevant command sequence diagrams to improve readability and understanding of Developer's Guide
+
+2. Wrote `Update` section.
+ - Includes update `Expenditure`, `Credit Card`, and `Income`.
+ - Included Design considerations.
+ - Designed relevant command sequence diagrams to improve readability and understanding of Developer's Guide
+
+3. Redo numerous sequence diagrams to standardise colours used for each Class and improved readability of diagrams
+
+### Team-Based tasks Contribution
+1. Created and added members into `developers` team on GitHub.
+
+2. Proofread teammates' contributions to User Guide and Developer Guide.
+
+
+### Miscellaneous Contribution
+PR reviews (with non-trivial comments):
+[#69](https://github.com/AY2122S2-CS2113T-T10-4/tp/pull/69), [#78](https://github.com/AY2122S2-CS2113T-T10-4/tp/pull/78),
+[#162](https://github.com/AY2122S2-CS2113T-T10-4/tp/pull/162), [#201](https://github.com/AY2122S2-CS2113T-T10-4/tp/pull/201)
+
+Moderate number of bugs found (7) during PE-Dry testing for `AY2122S2-CS2113-T11-3` can be viewed [here](https://github.com/glendonnotglen/ped/issues).
diff --git a/docs/team/johndoe.md b/docs/team/johndoe.md
deleted file mode 100644
index ab75b391b8..0000000000
--- a/docs/team/johndoe.md
+++ /dev/null
@@ -1,6 +0,0 @@
-# John Doe - Project Portfolio Page
-
-## Overview
-
-
-### Summary of Contributions
diff --git a/docs/team/khseah.md b/docs/team/khseah.md
new file mode 100644
index 0000000000..0501a20333
--- /dev/null
+++ b/docs/team/khseah.md
@@ -0,0 +1,73 @@
+# Seah Kit Han - Project Portfolio Page
+
+## Overview
+`MindMyMoney` (M3) is a desktop app for managing your personal finances, optimized for use via a Command Line
+Interface (CLI). You can use it to track your expenditures across multiple payment methods, calculate monthly
+expenditure, and set financial goals.
+
+## Contribution Summary
+
+### Code Contribution
+Code Contribution can be viewed [here](https://nus-cs2113-ay2122s2.github.io/tp-dashboard/?search=khseah&breakdown=true).
+
+1. Set up the initial structure to build our code base upon.
+ - `MindMyMoney` class as the entry point, `Ui` class, `Parser` class and `Command` class.
+ - Allows the team to have a common foundation to build upon and sets the architecture structure.
+
+2. Added the `Ui` class.
+ - Implemented the `>` prompt to help the user differentiate between their input and the program's output.
+ - Sourced for 49 financial tips, of which 1 would be printed on startup, making it more interactive.
+
+3. Added Add, Delete, Update and List functionalities for `Income`.
+ - As a Personal Finance app, it is imperative that income can be tracked, so that users can monitor their income
+inflow and outflow.
+ - Affected existing Add, Delete, Update and List commands as these commands were also used for `Expenditure` and
+`CreditCard`.
+
+4. Added the `User` class.
+ - Abstracts out `ExpenditureList`, `CreditCardList` and `IncomeList` classes into 1 class, increasing OOP.
+ - Affected a majority of existing code as previous methods had the 3 lists as parameters.
+
+5. Miscellaneous.
+ - Added JUnit tests for `HelpCommand`, `AddCommand` and `DeleteCommand`.
+ - Fixed bugs raised during PE-Dry testing pertaining to `CreditCard` limit.
+
+### UG Contribution
+1. Wrote the `Income` portion.
+ - Help, Add, List, Update and Delete.
+ - Command Summary (Income) table.
+
+
+2. Streamlined content page.
+ - Shortened section headers and standardized action verbs (Display, Add, Modify etc).
+
+### DG Contribution
+1. Wrote the `Architecture overview` portion.
+ - Added Fig 1 - Architecture Diagram and Fig 2 - Sequence Diagram.
+
+2. Wrote the `Ui` and `Parser Component overview` portion.
+ - Added Fig 3 - Ui Class Diagram and Fig 4 - Parser Class Diagram.
+
+3. Wrote the `Add Income implementation` portion.
+ - Added Fig 10 - Add Income Sequence Diagram.
+
+4. Wrote the `List Command implementation` portion.
+ - Added Fig 12 - List Command Sequence Diagram, Fig 13 - List Expenditure Sequence Diagram, Fig 14 - List Credit Card
+Sequence Diagram and Fig 15 - List Income Sequence Diagram
+
+### Team-Based tasks Contribution
+1. Set up Issue Tracker.
+ - Added `Issue type`, `Priority` and `Severity` labels.
+ - Created and assigned issues for `v1.0` based on user stories.
+ - Created and closed `v1.0` Milestone.
+
+2. Released `v1.0` and `v2.0` JAR files.
+
+### Miscellaneous Contribution
+PR reviews (with non-trivial comments):
+[#39](https://github.com/AY2122S2-CS2113T-T10-4/tp/pull/39), [#46](https://github.com/AY2122S2-CS2113T-T10-4/tp/pull/46),
+[#72](https://github.com/AY2122S2-CS2113T-T10-4/tp/pull/72), [#86](https://github.com/AY2122S2-CS2113T-T10-4/tp/pull/86),
+[#87](https://github.com/AY2122S2-CS2113T-T10-4/tp/pull/87), [#143](https://github.com/AY2122S2-CS2113T-T10-4/tp/pull/143).
+
+[DG review](https://github.com/nus-cs2113-AY2122S2/tp/pull/6/files/4125efa69fbb7ffda1b2ade950ec48b6e80f5baf) for `Simplst`,
+[Bugs found](https://github.com/khseah/ped/issues) during PE-Dry testing for `Spendvelope`.
diff --git a/docs/team/limjierui.md b/docs/team/limjierui.md
new file mode 100644
index 0000000000..5ea457d86b
--- /dev/null
+++ b/docs/team/limjierui.md
@@ -0,0 +1,70 @@
+# Lim Jie Rui - Project Portfolio Page
+
+## Overview
+`MindMyMoney` (M3) is a desktop app for managing your personal finances, optimized for use via a Command Line
+Interface (CLI). You can use it to track your expenditures across multiple payment methods, calculate monthly
+expenditure, and set financial goals.
+
+## Contribution Summary
+
+### Code Contribution
+Code Contribution can be viewed [here](https://nus-cs2113-ay2122s2.github.io/tp-dashboard/?search=limjierui&breakdown=true)
+
+Enhancements implemented:
+
+1. Refactor initial code to add more abstractions and OOP.
+ - Added `expenditureList` class to tackle the global list issue during initial testing.
+ - Refactored all command classes to have more OOP and abstractions.
+
+2. Added `bye` and `delete` command.
+ - Added `bye` class that allows users to exit when they want to end the program.
+ - Set up the `delete` class to delete any items in the expenditure list with an index.
+
+3. Added `MindMyMoneyException` class.
+ - Set up `MindMyMoneyException` class to allow exceptions to be thrown as MindMyMoneyException.
+
+4. Enhanced `CalculateInputCommand` command.
+ - Added a breakdown of expenses by categories.
+ - Enhanced visual appeal by displaying details in a bar chart format.
+ - Enhanced command to allow users to search by date, month or year.
+
+5. Enhanced `addCommand`,`listCommand` and `updateCommand` commands.
+ - Added enhancements to check if the date input is in the correct format and before current date of user.
+ - Added enhancements to check if the date input is a valid date in a leap year or non leap year.
+ - Enhanced `listCommand` to list expenditures by date specified (date, month or year).
+ - Added checks for `updateCommand` to prevent user from updating an expenditure if all field details are the same.
+
+6. Added and updated JUnit tests.
+ - Tests for `deleteCommand`, `calculateInputCommand`, `listCommand`, `helpCommand`, `updateCommand`, `addCommand`
+
+7. Miscellaneous
+ - Fixed formatting issues and wrong error message outputs in JUnit tests.
+ - Standardised and coded clearer output messages across different commands.
+ - Wrote JavaDoc comments for a majority of methods.
+ - Fixed bugs raised during PE-D.
+
+### UG Contribution
+1. Added initial example pictures into the UG for different commands.
+2. Standardised command input parameters.
+3. Edited examples across commands to have a flow that had a user story to it.
+4. Checked and edited wrong example outputs.
+
+### DG Contribution
+1. Wrote and explained the `Command` component.
+ - Added command component class diagram and how it works.
+2. Wrote and explained the `Storage` component.
+ - Added storage component class diagram and how it works.
+3. Wrote and explained the `CalculateInputCommand` component.
+ - Added CalculateInputCommand sequence diagram and how it works.
+
+### Team-Based Tasks Contribution
+1. Issued v2.1 issues to members and assigned v2.1 milestones to issues after PE-D.
+2. Refactored code and added abstractions to allow members to add JUnit tests and functional code more easily in future implementations.
+3. Standardised sequence and class diagrams to have the same format.
+
+### Miscellaneous Contribution
+PR reviews (with non-trivial comments):
+[#34](https://github.com/AY2122S2-CS2113T-T10-4/tp/pull/34), [#69](https://github.com/AY2122S2-CS2113T-T10-4/tp/pull/69),
+[#77](https://github.com/AY2122S2-CS2113T-T10-4/tp/pull/77), [#90](https://github.com/AY2122S2-CS2113T-T10-4/tp/pull/90),
+[#213](https://github.com/AY2122S2-CS2113T-T10-4/tp/pull/213)
+High number of [bugs found](https://github.com/limjierui/ped/issues) (16) during PE-Dry testing for `AY2122S2-CS2113-F10-1`.
\ No newline at end of file
diff --git a/docs/team/seanhowb.md b/docs/team/seanhowb.md
new file mode 100644
index 0000000000..c61369fc70
--- /dev/null
+++ b/docs/team/seanhowb.md
@@ -0,0 +1,100 @@
+# Ho Wen Bin, Sean - Project Portfolio Page
+
+## Overview
+
+`MindMyMoney` (M3) is a desktop app for managing your personal finances, optimized for use via a Command Line
+Interface (CLI). You can use it to track your expenditures across multiple payment methods, calculate monthly
+expenditure, and set financial goals.
+
+
+## Contribution Summary
+
+### Code Contribution
+
+Code Contribution can be
+viewed [here](https://nus-cs2113-ay2122s2.github.io/tp-dashboard/?search=SeanHoWB&breakdown=true).
+
+Enhancements implemented:
+
+1. Set up the initial structure to build our code base upon.
+ - `AddCommand` class to allow users to add commands into `MindMyMoney`.
+ - `CalculationInputCommand` class to allow users to calculate their expenses.
+ - `userfinancial` package containing `Expenditure` and `CreditCard` classes.
+ - `helper` package containing helper functions used throughout the codebase, improving SLAP by extracting out code
+ from classes.
+ - Allows the team to use functions and classes essential to `MindMyMoney`.
+
+2. Added the `AddCommand` class.
+ - Allows users to track and add their expenditure in `MindMyMoney`, and see a summary of all the inputs they have
+ added.
+ - Added its relevant `AddCommandInputTests` that serves as input validation and checks the input of each field in
+ the add command for its validity.
+ - This command was challenging as it has many fields that needed to be validated and formatted. It also interacts
+ with other commands in the codebase which made it more complicated.
+
+3. Set up the `CalculationInputCommand` class.
+ - Allows users to calculate the total amount of expenditures, according to date.
+ - Added `Calculations` class to perform the calculations needed by `CalculationInputCommand`.
+
+4. Added`Expenditure` class.
+ - As a key functionality of `MindMyMoney`, users can now track their expenditures and get their total expenditure.
+
+5. Added `CreditCard` class.
+ - Being part of the value proposition of `MindMyMoney`, it allows users to track their multiple forms of payment.
+
+6. Added `GeneralFunction` class
+ - Allows the team to call frequently used functions in `MindMyMoney` from just one class, increases SLAP.
+ - Implemented `findItemsInList` function that aided the search for expenditure by date and by category in
+ `calculateExpenditurePerMonth`.
+ - Implemented `parseInputWithCommandFlag` to parse user input containing flags. This was used throughout the code
+ across many commands.
+ - Implemented `capitalise`, `formatFloat` functions, to format user input.
+
+7. Added JUnit tests
+ - Added JUnit tests to `AddCommand`, `ListCommand`, `UpdateCommand`, `CalculateInputCommand`,
+ `GeneralFunctions`, `HelpCommand`, `DeleteCommand`, `Parser`, `ByeCommand`, `MindMyMoney`, `Ui`.
+ - Increased the test coverage for these classes to 88% classes and 87% lines covered.
+ - Implemented functions to allow capturing of terminal output, used throughout other JUnit tests.
+
+8. Miscellaneous
+ - Fixed bugs raised during PE-Dry testing pertaining to inconsistent UG images.
+ - `ExpenditureCategoryType` enum for containing the types of category of expenditure.
+ - `ExpenditureFields` enum for containing the fields in an add expenditure command.
+ - `Indexes` class containing constants referencing indexes used in arrays.
+
+### UG Contribution
+
+1. Wrote the `Add an expenditure` portion.
+ - Explained the flags of expenditure and how to use it.
+
+2. Wrote the Introduction and reformatted the guide.
+ - Wrote the introduction, using the user guide and quick start.
+ - Changed the way expected output is displayed, from images to text form for better formatting.
+ - Formatted the headers and the `Format` and `Expected Outcome` field.
+
+### DG Contribution
+
+1. Contributed to the `Add Command` portion.
+ - Added `Add Expenditure` portion.
+ - Added the initial Fig 7 - AddCommand Sequence Diagram and Fig 8 - Add Expenditure Command Sequence Diagram.
+
+2. Wrote the `Introduction` and `Appendix Requirements`.
+ - Added Product Scope, Non-Functional Requirements, Glossary.
+ - Added the purpose, acknowledgements and using the developer guide.
+
+### Team-Based tasks Contribution
+
+1. Issued issues and milestones.
+ - Created and assigned issues for `v2.0` based on user stories.
+ - Created and closed `v2.0` Milestone.
+ - Opened `v2.1` Milestone.
+
+### Miscellaneous Contribution
+
+PR reviews (with non-trivial comments):
+[#38](https://github.com/AY2122S2-CS2113T-T10-4/tp/pull/38), [#78](https://github.com/AY2122S2-CS2113T-T10-4/tp/pull/78)
+,
+[#51](https://github.com/AY2122S2-CS2113T-T10-4/tp/pull/51), [#86](https://github.com/AY2122S2-CS2113T-T10-4/tp/pull/86)
+,
+[#74](https://github.com/AY2122S2-CS2113T-T10-4/tp/pull/74),
+
diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java
deleted file mode 100644
index 5c74e68d59..0000000000
--- a/src/main/java/seedu/duke/Duke.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package seedu.duke;
-
-import java.util.Scanner;
-
-public class Duke {
- /**
- * Main entry-point for the java.duke.Duke application.
- */
- public static void main(String[] args) {
- String logo = " ____ _ \n"
- + "| _ \\ _ _| | _____ \n"
- + "| | | | | | | |/ / _ \\\n"
- + "| |_| | |_| | < __/\n"
- + "|____/ \\__,_|_|\\_\\___|\n";
- System.out.println("Hello from\n" + logo);
- System.out.println("What is your name?");
-
- Scanner in = new Scanner(System.in);
- System.out.println("Hello " + in.nextLine());
- }
-}
diff --git a/src/main/java/seedu/mindmymoney/MindMyMoney.java b/src/main/java/seedu/mindmymoney/MindMyMoney.java
new file mode 100644
index 0000000000..39603056bc
--- /dev/null
+++ b/src/main/java/seedu/mindmymoney/MindMyMoney.java
@@ -0,0 +1,66 @@
+package seedu.mindmymoney;
+
+import seedu.mindmymoney.command.Command;
+import seedu.mindmymoney.userfinancial.User;
+
+import java.io.File;
+
+/**
+ * Represents the entry point of the MindMyMoney program. Initializes the program and starts interaction with the
+ * user.
+ */
+public class MindMyMoney {
+ private final Ui ui;
+ private User user;
+ private final Storage storage;
+ private static final String STORAGE_FILENAME = "data.txt";
+
+ public MindMyMoney() {
+ Storage savedStorage;
+ ui = new Ui();
+ user = new User();
+ try {
+ savedStorage = new Storage(new File(STORAGE_FILENAME));
+ } catch (MindMyMoneyException e) {
+ System.out.println(e.getMessage());
+ savedStorage = null;
+ }
+ storage = savedStorage;
+ }
+
+ public void run() {
+ ui.printIntro();
+
+ if (storage != null) {
+ try {
+ user = storage.load();
+ } catch (MindMyMoneyException e) {
+ System.out.println(e.getMessage());
+ System.out.println(System.lineSeparator());
+ }
+ }
+
+ boolean isExit = false;
+ while (!isExit) {
+ try {
+ String input = ui.readInput();
+ Command commandType = Parser.parseCommand(input, user);
+ commandType.executeCommand();
+
+ isExit = commandType.isExit();
+
+ if (storage != null) {
+ storage.save(user);
+ }
+
+ } catch (MindMyMoneyException e) {
+ System.out.println(e.getMessage());
+ System.out.print(System.lineSeparator());
+ }
+ }
+ }
+
+ public static void main(String[] args) {
+ new MindMyMoney().run();
+ }
+}
diff --git a/src/main/java/seedu/mindmymoney/MindMyMoneyException.java b/src/main/java/seedu/mindmymoney/MindMyMoneyException.java
new file mode 100644
index 0000000000..31c5682449
--- /dev/null
+++ b/src/main/java/seedu/mindmymoney/MindMyMoneyException.java
@@ -0,0 +1,10 @@
+package seedu.mindmymoney;
+
+/**
+ * Represents exceptions that are exclusive to MindMyMoney program.
+ */
+public class MindMyMoneyException extends Exception {
+ public MindMyMoneyException(String errorMessage) {
+ super(errorMessage);
+ }
+}
diff --git a/src/main/java/seedu/mindmymoney/Parser.java b/src/main/java/seedu/mindmymoney/Parser.java
new file mode 100644
index 0000000000..125fbb5c9c
--- /dev/null
+++ b/src/main/java/seedu/mindmymoney/Parser.java
@@ -0,0 +1,67 @@
+package seedu.mindmymoney;
+
+import seedu.mindmymoney.command.AddCommand;
+import seedu.mindmymoney.command.CalculateInputCommand;
+import seedu.mindmymoney.command.Command;
+import seedu.mindmymoney.command.HelpCommand;
+import seedu.mindmymoney.command.ByeCommand;
+import seedu.mindmymoney.command.UpdateCommand;
+import seedu.mindmymoney.command.DeleteCommand;
+import seedu.mindmymoney.command.ListCommand;
+import seedu.mindmymoney.helper.GeneralFunctions;
+import seedu.mindmymoney.userfinancial.User;
+
+import static seedu.mindmymoney.constants.Flags.EMPTY_PARAMETER;
+import static seedu.mindmymoney.constants.Flags.FLAG_OF_EXPENSES;
+import static seedu.mindmymoney.constants.Indexes.INDEX_OF_FIRST_ITEM;
+import static seedu.mindmymoney.constants.Indexes.INDEX_OF_SECOND_ITEM;
+
+/**
+ * Represents the input parser and deals with making sense of user commands.
+ */
+public class Parser {
+
+ /**
+ * Returns a Command object with respect to their input. The command object can then be executed to perform
+ * the said command.
+ *
+ * @param input The command to be parsed.
+ * @param user The user object, which contains income, expenditure and credit card list.
+ * @return Command object with respect to user's input.
+ */
+ public static Command parseCommand(String input, User user) {
+ try {
+ String[] parsedInput = GeneralFunctions.parseInput(input);
+ assert parsedInput[INDEX_OF_FIRST_ITEM] != null : "First element in parsedInput is null";
+
+ switch (parsedInput[INDEX_OF_FIRST_ITEM].toLowerCase()) {
+ case "help":
+ if (hasAdditionalParameters(parsedInput)) {
+ return new HelpCommand(true, parsedInput[INDEX_OF_SECOND_ITEM]);
+ }
+ return new HelpCommand(true, EMPTY_PARAMETER);
+ case "bye":
+ return new ByeCommand();
+ case "add":
+ return new AddCommand(parsedInput[INDEX_OF_SECOND_ITEM], user);
+ case "update":
+ return new UpdateCommand(parsedInput[INDEX_OF_SECOND_ITEM], user);
+ case "list":
+ return new ListCommand(parsedInput[INDEX_OF_SECOND_ITEM], user);
+ case "delete":
+ return new DeleteCommand(input, user);
+ case "calculate":
+ return new CalculateInputCommand(parsedInput[INDEX_OF_SECOND_ITEM], user);
+ default:
+ return new HelpCommand(false, FLAG_OF_EXPENSES);
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ System.out.print("");
+ }
+ return new HelpCommand(false, FLAG_OF_EXPENSES);
+ }
+
+ private static boolean hasAdditionalParameters(String[] input) {
+ return input.length > 1;
+ }
+}
diff --git a/src/main/java/seedu/mindmymoney/Storage.java b/src/main/java/seedu/mindmymoney/Storage.java
new file mode 100644
index 0000000000..cff4ed9a23
--- /dev/null
+++ b/src/main/java/seedu/mindmymoney/Storage.java
@@ -0,0 +1,69 @@
+package seedu.mindmymoney;
+
+import seedu.mindmymoney.userfinancial.User;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Scanner;
+
+/**
+ * Class for handling loading and saving of expenditure lists.
+ */
+public class Storage {
+ private File storageFile;
+
+ public Storage(File storageFile) throws MindMyMoneyException {
+ this.storageFile = storageFile;
+ if (!storageFile.exists()) {
+ try {
+ storageFile.createNewFile();
+ } catch (IOException e) {
+ throw new MindMyMoneyException("WARNING: Failed to create save file. "
+ + "You may still use MindMyMoney; however, your data may not be saved.");
+ }
+ }
+ }
+
+ /**
+ * Loads information from the save file. If the file does not exist, or if there is
+ * an error when reading the file, return an empty list, and print a warning message.
+ * @return The saved list.
+ * @throws MindMyMoneyException if an error occurs while reading the file, or if the file has an invalid format.
+ */
+ public User load() throws MindMyMoneyException {
+ try {
+ Scanner scanner = new Scanner(storageFile);
+ User savedUser = User.deserializeFrom(scanner);
+ scanner.close();
+ return savedUser;
+ } catch (FileNotFoundException e) {
+ throw new MindMyMoneyException("WARNING: Save file not found. MindMyMoney cannot read your saved data.");
+ } catch (MindMyMoneyException e) {
+ throw new MindMyMoneyException("WARNING: Error when reading save data: " + e.getMessage() + "\n"
+ + "MindMyMoney will create a new save file, possibly overwriting the existing file.\n"
+ + "If you have important data stored there, make a copy of the current save file.");
+ }
+ }
+
+ /**
+ * Saves the information associated with the given User. If the file does not exist, or if there is an error
+ * when saving to the file, print a warning message.
+ * @param user User whose lists need to be saved.
+ * @throws MindMyMoneyException if an error occurs while saving.
+ */
+ public void save(User user) throws MindMyMoneyException {
+ try {
+ BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(storageFile));
+ bufferedWriter.write(user.serialize());
+ bufferedWriter.flush();
+ bufferedWriter.close();
+ } catch (FileNotFoundException e) {
+ throw new MindMyMoneyException("WARNING: Save file not found. MindMyMoney cannot save your data.");
+ } catch (IOException e) {
+ throw new MindMyMoneyException("WARNING: Error when saving expenditure list: " + e.getMessage() + "\n");
+ }
+ }
+}
diff --git a/src/main/java/seedu/mindmymoney/Ui.java b/src/main/java/seedu/mindmymoney/Ui.java
new file mode 100644
index 0000000000..2e5a6104ad
--- /dev/null
+++ b/src/main/java/seedu/mindmymoney/Ui.java
@@ -0,0 +1,51 @@
+package seedu.mindmymoney;
+
+import java.util.Scanner;
+import java.util.Random;
+
+import static seedu.mindmymoney.constants.PrintStrings.tips;
+
+/**
+ * Deals with interactions with the user.
+ */
+public class Ui {
+ public static final String PROMPT = "> ";
+
+ private final Scanner in;
+ private final Random rand;
+
+ public Ui() {
+ this.in = new Scanner(System.in);
+ this.rand = new Random();
+ }
+
+ /**
+ * Prints logo and welcome message at startup.
+ */
+ public void printIntro() {
+ String logo = " __ __ _ _ __ __ __ __\n"
+ + "| \\/ (_)_ _ __| | \\/ |_ _| \\/ |___ _ _ ___ _ _\n"
+ + "| |\\/| | | ' \\/ _` | |\\/| | || | |\\/| / _ \\ ' \\/ -_) || |\n"
+ + "|_| |_|_|_||_\\__,_|_| |_|\\_, |_| |_\\___/_||_\\___|\\_, |\n"
+ + " |__/ |__/";
+
+ System.out.println(System.lineSeparator());
+ System.out.println(logo);
+ int randomIndex = rand.nextInt(48);
+ assert randomIndex >= 0 && randomIndex <= 48 : "randomIndex is not within the bounds";
+ System.out.println("<< " + tips[randomIndex] + " >>" + System.lineSeparator());
+ System.out.println("Welcome to MindMyMoney");
+ System.out.println("What can I do for you?" + System.lineSeparator());
+ }
+
+ /**
+ * Prints the prompt and reads user's input.
+ *
+ * @return User's input.
+ */
+ public String readInput() {
+ System.out.print(PROMPT);
+ String input = in.nextLine();
+ return input;
+ }
+}
diff --git a/src/main/java/seedu/mindmymoney/command/AddCommand.java b/src/main/java/seedu/mindmymoney/command/AddCommand.java
new file mode 100644
index 0000000000..b3e3a21f44
--- /dev/null
+++ b/src/main/java/seedu/mindmymoney/command/AddCommand.java
@@ -0,0 +1,209 @@
+package seedu.mindmymoney.command;
+
+import seedu.mindmymoney.MindMyMoneyException;
+import seedu.mindmymoney.data.CreditCardList;
+import seedu.mindmymoney.data.ExpenditureList;
+import seedu.mindmymoney.data.IncomeList;
+import seedu.mindmymoney.userfinancial.Expenditure;
+import seedu.mindmymoney.userfinancial.CreditCard;
+import seedu.mindmymoney.userfinancial.Income;
+import seedu.mindmymoney.userfinancial.User;
+
+import static seedu.mindmymoney.constants.Flags.FLAG_END_VALUE;
+import static seedu.mindmymoney.constants.Flags.FLAG_OF_AMOUNT;
+import static seedu.mindmymoney.constants.Flags.FLAG_OF_CARD_LIMIT;
+import static seedu.mindmymoney.constants.Flags.FLAG_OF_CARD_NAME;
+import static seedu.mindmymoney.constants.Flags.FLAG_OF_CASHBACK;
+import static seedu.mindmymoney.constants.Flags.FLAG_OF_CATEGORY;
+import static seedu.mindmymoney.constants.Flags.FLAG_OF_CREDIT_CARD;
+import static seedu.mindmymoney.constants.Flags.FLAG_OF_DESCRIPTION;
+import static seedu.mindmymoney.constants.Flags.FLAG_OF_EXPENSES;
+import static seedu.mindmymoney.constants.Flags.FLAG_OF_INCOME;
+import static seedu.mindmymoney.constants.Flags.FLAG_OF_PAYMENT_METHOD;
+import static seedu.mindmymoney.constants.Flags.FLAG_OF_TIME;
+import static seedu.mindmymoney.helper.AddCommandInputTests.testExpenditureParameters;
+import static seedu.mindmymoney.helper.AddCommandInputTests.testIncomeParameters;
+import static seedu.mindmymoney.helper.AddCommandInputTests.testCreditCardParameters;
+import static seedu.mindmymoney.helper.GeneralFunctions.capitalise;
+import static seedu.mindmymoney.helper.GeneralFunctions.parseInputWithCommandFlag;
+import static seedu.mindmymoney.helper.GeneralFunctions.formatFloat;
+
+/**
+ * Represents the Add command.
+ */
+public class AddCommand extends Command {
+ private String addInput;
+ public ExpenditureList expenditureList;
+ public CreditCardList creditCardList;
+ public IncomeList incomeList;
+
+ public AddCommand(String addInput, User user) {
+ this.addInput = addInput;
+ this.expenditureList = user.getExpenditureListArray();
+ this.creditCardList = user.getCreditCardListArray();
+ this.incomeList = user.getIncomeListArray();
+ }
+
+ /**
+ * Indicates whether the program should exit.
+ *
+ * @return true if the program should exit, false otherwise.
+ */
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+
+ /**
+ * Indicates whether the add command is to add an expenditure by checking for the expenditure flag.
+ *
+ * @return true if the /e flag is present, false otherwise.
+ */
+ private boolean hasExpenditureFlag() {
+ return addInput.startsWith(FLAG_OF_EXPENSES);
+ }
+
+ /**
+ * Indicates whether the add command is to add a credit card by looking for the credit card flag.
+ *
+ * @return true if the credit card flag is present, false otherwise.
+ */
+ private boolean hasCreditCardFlag() {
+ return addInput.startsWith(FLAG_OF_CREDIT_CARD);
+ }
+
+ /**
+ * Updates the total expenditure field in the credit card specified in the expenditure item and returns
+ * the balance left.
+ *
+ * @param cardName Name of credit card to be updated.
+ * @param amount amount of new expenditure.
+ * @return The credit card balance left.
+ * @throws MindMyMoneyException when the card is not found in user's credit card list.
+ */
+ private float updateCreditCardTotalExpenditure(String cardName, float amount) throws MindMyMoneyException {
+ CreditCard creditCard = creditCardList.get(cardName);
+ if (creditCard == null) {
+ throw new MindMyMoneyException("Invalid Card Name!");
+ }
+ creditCard.addExpenditure(amount);
+ return creditCard.getBalanceLeft();
+ }
+
+ /**
+ * Indicates whether the add command is to add an income by looking for the /i flag.
+ *
+ * @return true if the /i flag is present, false otherwise.
+ */
+ private boolean hasIncomeFlag() {
+ return addInput.startsWith(FLAG_OF_INCOME);
+ }
+
+ /**
+ * Inserts an Expenditure object into user's list of expenditure(s).
+ *
+ * @throws MindMyMoneyException when inputs are invalid or flags are missing.
+ */
+ public void addExpenditure() throws MindMyMoneyException {
+ String paymentMethod = parseInputWithCommandFlag(addInput, FLAG_OF_PAYMENT_METHOD, FLAG_OF_CATEGORY).trim();
+ String inputCategory = parseInputWithCommandFlag(addInput, FLAG_OF_CATEGORY, FLAG_OF_DESCRIPTION).trim();
+ String description = parseInputWithCommandFlag(addInput, FLAG_OF_DESCRIPTION, FLAG_OF_AMOUNT).trim();
+ String amountAsString = parseInputWithCommandFlag(addInput, FLAG_OF_AMOUNT, FLAG_OF_TIME).trim();
+ String inputTime = parseInputWithCommandFlag(addInput, FLAG_OF_TIME, FLAG_END_VALUE).trim();
+ testExpenditureParameters(paymentMethod, inputCategory, description, amountAsString, inputTime, creditCardList);
+
+ if (capitalise(paymentMethod).equals("Cash")) {
+ paymentMethod = capitalise(paymentMethod);
+ }
+ String category = capitalise(inputCategory);
+ float amountAsFloat = Float.parseFloat(amountAsString);
+ float amountFloat = formatFloat(amountAsFloat);
+ expenditureList.add(new Expenditure(paymentMethod, category, description, amountFloat, inputTime));
+
+ System.out.println("Successfully added: \n\n"
+ + "Description: " + description + "\n"
+ + "Amount: $" + String.format("%.2f", amountFloat) + "\n"
+ + "Category: " + category + "\n"
+ + "Payment method: " + paymentMethod + "\n"
+ + "Date: " + inputTime + "\n\n"
+ + "into the account");
+
+ if (!paymentMethod.equals("Cash")) {
+ float balanceLeft = updateCreditCardTotalExpenditure(paymentMethod, amountFloat);
+ System.out.printf(paymentMethod + " has a balance of $%.2f left%n", balanceLeft);
+ }
+ System.out.print(System.lineSeparator());
+ }
+
+ /**
+ * Inserts a CreditCard object into user's list of credit card(s).
+ *
+ * @throws MindMyMoneyException Exception thrown when input is invalid
+ */
+ public void addCreditCard() throws MindMyMoneyException {
+ final String cardName = parseInputWithCommandFlag(addInput, FLAG_OF_CARD_NAME,
+ FLAG_OF_CASHBACK).trim();
+ final String cashBack = parseInputWithCommandFlag(addInput, FLAG_OF_CASHBACK,
+ FLAG_OF_CARD_LIMIT).trim();
+ final String cardLimit = parseInputWithCommandFlag(addInput, FLAG_OF_CARD_LIMIT,
+ FLAG_END_VALUE).trim();
+ testCreditCardParameters(cardName, cashBack, cardLimit, creditCardList);
+ Float cashBackAsFloat = formatFloat(Float.parseFloat(cashBack));
+ Float cardLimitAsFloat = formatFloat(Float.parseFloat(cardLimit));
+ creditCardList.add(new CreditCard(cardName, cashBackAsFloat, cardLimitAsFloat));
+
+ System.out.println("Successfully added: \n\n"
+ + "Credit card: " + cardName + "\n"
+ + "Cash back: " + String.format("%.2f", cashBackAsFloat) + "%\n"
+ + "Card limit: $" + String.format("%.2f", cardLimitAsFloat) + "\n\n"
+ + "into the account");
+ System.out.print(System.lineSeparator());
+ }
+
+ /**
+ * Inserts an Income object into user's list of income(s).
+ *
+ * @throws MindMyMoneyException when the input amount is not a number.
+ */
+ public void addIncome() throws MindMyMoneyException {
+ String amountAsString = parseInputWithCommandFlag(addInput, FLAG_OF_AMOUNT, FLAG_OF_CATEGORY).trim();
+
+ try {
+ int amountAsInt = Integer.parseInt(amountAsString);
+ String inputCategory = parseInputWithCommandFlag(addInput, FLAG_OF_CATEGORY, FLAG_END_VALUE).trim();
+ testIncomeParameters(amountAsInt, inputCategory);
+ String category = capitalise(inputCategory);
+
+ incomeList.add(new Income(amountAsInt, category));
+
+ System.out.print("Successfully added: \n\n"
+ + "Amount: $" + amountAsInt + "\n"
+ + "Category: " + category + "\n\n"
+ + "into the account");
+ System.out.println(System.lineSeparator());
+ } catch (NumberFormatException e) {
+ throw new MindMyMoneyException("Income must be a whole number or your income is too high!");
+ }
+ }
+
+ /**
+ * Inserts either an Expenditure, CreditCard or Income object into the user's list based on the input.
+ *
+ * @throws MindMyMoneyException when an invalid command is received, along with its corresponding error message.
+ */
+ @Override
+ public void executeCommand() throws MindMyMoneyException {
+ if (hasExpenditureFlag()) {
+ addExpenditure();
+ } else if (hasCreditCardFlag()) {
+ addCreditCard();
+ } else if (hasIncomeFlag()) {
+ addIncome();
+ } else {
+ throw new MindMyMoneyException("You are missing a flag in your command\n"
+ + "Type \"help /e\" to view the list of supported expenditure commands\n"
+ + "Type \"help /cc\" to view the list of supported Credit Card commands\n"
+ + "Type \"help /i\" to view the list of supported income commands");
+ }
+ }
+}
diff --git a/src/main/java/seedu/mindmymoney/command/ByeCommand.java b/src/main/java/seedu/mindmymoney/command/ByeCommand.java
new file mode 100644
index 0000000000..6c029b7aca
--- /dev/null
+++ b/src/main/java/seedu/mindmymoney/command/ByeCommand.java
@@ -0,0 +1,26 @@
+package seedu.mindmymoney.command;
+
+/**
+ * Represents the Bye command.
+ */
+public class ByeCommand extends Command {
+
+ /**
+ * Indicates whether the program should exit.
+ *
+ * @return true if the program should exit, false otherwise.
+ */
+ @Override
+ public boolean isExit() {
+ return true;
+ }
+
+ /**
+ * Prints the bye message.
+ */
+ @Override
+ public void executeCommand() {
+ System.out.print("Bye, hope to see you again!"
+ + System.lineSeparator());
+ }
+}
diff --git a/src/main/java/seedu/mindmymoney/command/CalculateInputCommand.java b/src/main/java/seedu/mindmymoney/command/CalculateInputCommand.java
new file mode 100644
index 0000000000..a0c3af525b
--- /dev/null
+++ b/src/main/java/seedu/mindmymoney/command/CalculateInputCommand.java
@@ -0,0 +1,57 @@
+package seedu.mindmymoney.command;
+
+import seedu.mindmymoney.MindMyMoneyException;
+import seedu.mindmymoney.data.ExpenditureList;
+import seedu.mindmymoney.helper.GeneralFunctions;
+import seedu.mindmymoney.userfinancial.User;
+
+import static seedu.mindmymoney.constants.Indexes.INDEX_OF_FIRST_ITEM;
+import static seedu.mindmymoney.constants.Flags.FLAG_OF_EXPENDITURE_PER_MONTH;
+import static seedu.mindmymoney.constants.Indexes.INDEX_OF_SECOND_ITEM;
+import static seedu.mindmymoney.helper.Calculations.calculateExpenditure;
+
+/**
+ * Represents the Calculate command.
+ */
+public class CalculateInputCommand extends Command {
+ private String calculateInput;
+ public ExpenditureList expenditureList;
+
+ public CalculateInputCommand(String calculateInput, User user) {
+ this.calculateInput = calculateInput;
+ this.expenditureList = user.getExpenditureListArray();
+ }
+
+ /**
+ * Indicates whether the program should exit.
+ *
+ * @return true if the program should exit, false otherwise.
+ */
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+
+ /**
+ * Parses the input for calculate command.
+ *
+ * @throws MindMyMoneyException when inputs are invalid or flags are missing.
+ */
+ @Override
+ public void executeCommand() throws MindMyMoneyException {
+ try {
+ String[] parsedCalculateInput = GeneralFunctions.parseInput(calculateInput);
+ assert parsedCalculateInput[INDEX_OF_FIRST_ITEM] != null
+ : "First element in parsedCalculateInput is null";
+ switch (parsedCalculateInput[INDEX_OF_FIRST_ITEM].toLowerCase()) {
+ case FLAG_OF_EXPENDITURE_PER_MONTH:
+ calculateExpenditure(parsedCalculateInput[INDEX_OF_SECOND_ITEM], expenditureList);
+ break;
+ default:
+ throw new MindMyMoneyException("Remember to use a proper flag!");
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new MindMyMoneyException("Missing input after command!");
+ }
+ }
+}
diff --git a/src/main/java/seedu/mindmymoney/command/Command.java b/src/main/java/seedu/mindmymoney/command/Command.java
new file mode 100644
index 0000000000..c2eb9166f9
--- /dev/null
+++ b/src/main/java/seedu/mindmymoney/command/Command.java
@@ -0,0 +1,22 @@
+package seedu.mindmymoney.command;
+
+import seedu.mindmymoney.MindMyMoneyException;
+
+/**
+ * Represents an executable command. This class cannot be instantiated and serves as a Parent class for specific
+ * Command classes to inherit from.
+ */
+public abstract class Command {
+
+ /**
+ * Executes the given command. This method is to be implemented by child classes.
+ */
+ public abstract void executeCommand() throws MindMyMoneyException;
+
+ /**
+ * Indicates whether the program should exit.
+ *
+ * @return true if the program should exit, false otherwise.
+ */
+ public abstract boolean isExit();
+}
diff --git a/src/main/java/seedu/mindmymoney/command/DeleteCommand.java b/src/main/java/seedu/mindmymoney/command/DeleteCommand.java
new file mode 100644
index 0000000000..73d58bcc55
--- /dev/null
+++ b/src/main/java/seedu/mindmymoney/command/DeleteCommand.java
@@ -0,0 +1,218 @@
+package seedu.mindmymoney.command;
+
+import seedu.mindmymoney.MindMyMoneyException;
+
+import seedu.mindmymoney.data.ExpenditureList;
+import seedu.mindmymoney.data.CreditCardList;
+import seedu.mindmymoney.data.IncomeList;
+import seedu.mindmymoney.userfinancial.CreditCard;
+import seedu.mindmymoney.userfinancial.Expenditure;
+import seedu.mindmymoney.userfinancial.User;
+
+import static seedu.mindmymoney.constants.Flags.FLAG_OF_CREDIT_CARD;
+import static seedu.mindmymoney.constants.Flags.FLAG_OF_EXPENSES;
+import static seedu.mindmymoney.constants.Flags.FLAG_OF_INCOME;
+import static seedu.mindmymoney.constants.Indexes.INDEX_OF_THIRD_ITEM;
+import static seedu.mindmymoney.constants.Indexes.LIST_INDEX_CORRECTION;
+import static seedu.mindmymoney.constants.Indexes.MINIMUM_CREDIT_CARD_COMMAND_LENGTH;
+import static seedu.mindmymoney.constants.Indexes.MINIMUM_EXPENDITURE_COMMAND_LENGTH;
+import static seedu.mindmymoney.constants.Indexes.MINIMUM_INCOME_COMMAND_LENGTH;
+
+/**
+ * Represents the Delete command.
+ */
+public class DeleteCommand extends Command {
+ private String input;
+ public ExpenditureList expenditureList;
+ public CreditCardList creditCardList;
+ public IncomeList incomeList;
+
+ public DeleteCommand(String input, User user) {
+ this.input = input;
+ this.expenditureList = user.getExpenditureListArray();
+ this.creditCardList = user.getCreditCardListArray();
+ this.incomeList = user.getIncomeListArray();
+ }
+
+ /**
+ * Indicates whether the program should exit.
+ *
+ * @return true if the program should exit, false otherwise.
+ */
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+
+ /**
+ * Indicates whether the delete command is to delete a credit card by looking for the /e flag.
+ *
+ * @return true if the /cc flag is present, false otherwise.
+ */
+ private boolean hasExpenditureFlag() {
+ return input.contains(FLAG_OF_EXPENSES);
+ }
+
+ /**
+ * Indicates whether the delete command is to delete a credit card by looking for the /cc flag.
+ *
+ * @return true if the /cc flag is present, false otherwise.
+ */
+ private boolean hasCreditCardFlag() {
+ return input.contains(FLAG_OF_CREDIT_CARD);
+ }
+
+ /**
+ * Indicates whether the delete command is to delete an income by looking for the /i flag.
+ *
+ * @return true if the /i flag is present, false otherwise.
+ */
+ private boolean hasIncomeFlag() {
+ return input.contains(FLAG_OF_INCOME);
+ }
+
+ /**
+ * Updates the total expenditure field in the credit card specified in the expenditure item.
+ *
+ * @param cardName Name of credit card to be updated.
+ * @param amount amount of new expenditure.
+ */
+ private void updateCreditCardTotalExpenditure(String cardName, float amount) {
+ CreditCard creditCard = creditCardList.get(cardName);
+ if (creditCard != null) {
+ creditCard.deductExpenditure(amount);
+ }
+ }
+
+ /**
+ * Removes an expenditure from user's list of expenditure(s).
+ *
+ * @throws MindMyMoneyException when expenditure list is empty or an invalid command is received.
+ */
+ public void deleteExpenditure() throws MindMyMoneyException {
+ try {
+ if (expenditureList.isEmpty()) {
+ throw new MindMyMoneyException(System.lineSeparator()
+ + "Please add something to your expenditure list first:)"
+ + System.lineSeparator());
+ }
+
+ String[] splitMessage = input.split(" ");
+ if (splitMessage.length != MINIMUM_EXPENDITURE_COMMAND_LENGTH) {
+ throw new MindMyMoneyException(System.lineSeparator() + "Please check your input parameters\n"
+ + "Enter 'delete /e [INDEX]' to remove an expenditure from your list.\n");
+ }
+
+ String getNumber = splitMessage[INDEX_OF_THIRD_ITEM];
+ int positionToDelete = Integer.parseInt(getNumber) + LIST_INDEX_CORRECTION;
+ Expenditure expenditure = expenditureList.get(positionToDelete);
+
+ String paymentMethod = expenditure.getPaymentMethod();
+ if (!paymentMethod.equals("Cash")) {
+ updateCreditCardTotalExpenditure(paymentMethod, expenditure.getAmount());
+ }
+
+ System.out.println("I have removed "
+ + expenditure.getDescription()
+ + " of $" + String.format("%.2f",expenditure.getAmount())
+ + " from the account" + System.lineSeparator());
+ expenditureList.delete(positionToDelete);
+ assert positionToDelete >= 0 : "Index should always be >= 0";
+
+ } catch (NumberFormatException e) {
+ throw new MindMyMoneyException("INDEX must be a number");
+ } catch (IndexOutOfBoundsException e) {
+ throw new MindMyMoneyException("Please input a valid index");
+ }
+ }
+
+ /**
+ * Removes a credit card from user's list of credit card(s).
+ *
+ * @throws MindMyMoneyException when credit card list is empty or an invalid command is received.
+ */
+ public void deleteCreditCard() throws MindMyMoneyException {
+ try {
+ if (creditCardList.isEmpty()) {
+ throw new MindMyMoneyException(System.lineSeparator()
+ + "Please add something to your credit card list first:)"
+ + System.lineSeparator());
+ }
+
+ String[] splitMessage = input.split(" ");
+ if (splitMessage.length != MINIMUM_CREDIT_CARD_COMMAND_LENGTH) {
+ throw new MindMyMoneyException(System.lineSeparator() + "Please input a number\n"
+ + "For eg. 'delete /cc 2' to remove the second credit card on your list.\n");
+ }
+
+ String getNumber = splitMessage[INDEX_OF_THIRD_ITEM];
+ int positionToDelete = Integer.parseInt(getNumber) + LIST_INDEX_CORRECTION;
+
+ System.out.println("I have removed "
+ + creditCardList.get(positionToDelete).getNameOfCard()
+ + " from your list of credit card(s)." + System.lineSeparator());
+ creditCardList.delete(positionToDelete);
+ assert positionToDelete >= 0 : "Index should always be >= 0";
+
+ } catch (NumberFormatException e) {
+ throw new MindMyMoneyException("INDEX must be a number");
+ } catch (IndexOutOfBoundsException e) {
+ throw new MindMyMoneyException("Please input a valid index");
+ }
+ }
+
+ /**
+ * Removes an income from user's list of income(s).
+ *
+ * @throws MindMyMoneyException when income list is empty or an invalid command is received.
+ */
+ public void deleteIncome() throws MindMyMoneyException {
+ try {
+ if (incomeList.isEmpty()) {
+ throw new MindMyMoneyException(System.lineSeparator()
+ + "Please add something to your income list first:)"
+ + System.lineSeparator());
+ }
+
+ String[] splitMessage = input.split(" ");
+ if (splitMessage.length != MINIMUM_INCOME_COMMAND_LENGTH) {
+ throw new MindMyMoneyException(System.lineSeparator() + "Please input a number\n"
+ + "For eg. 'delete /i 2' to remove the second income on your list.\n");
+ }
+
+ String getNumber = splitMessage[INDEX_OF_THIRD_ITEM];
+ int positionToDelete = Integer.parseInt(getNumber) + LIST_INDEX_CORRECTION;
+
+ System.out.println("I have removed "
+ + incomeList.get(positionToDelete).getCategory()
+ + " from your list of income(s)." + System.lineSeparator());
+ incomeList.delete(positionToDelete);
+
+ } catch (NumberFormatException e) {
+ throw new MindMyMoneyException("Input a number!");
+ } catch (NullPointerException | IndexOutOfBoundsException e) {
+ throw new MindMyMoneyException("Input a valid number!");
+ }
+ }
+
+ /**
+ * Removes an expenditure, credit card or income from the user's list based on the input.
+ *
+ * @throws MindMyMoneyException when an invalid command is received, along with its corresponding error message.
+ */
+ @Override
+ public void executeCommand() throws MindMyMoneyException {
+ if (hasExpenditureFlag()) {
+ deleteExpenditure();
+ } else if (hasCreditCardFlag()) {
+ deleteCreditCard();
+ } else if (hasIncomeFlag()) {
+ deleteIncome();
+ } else {
+ throw new MindMyMoneyException("You are missing a flag in your command\n"
+ + "Type \"help /e\" to view the list of supported expenditure commands\n"
+ + "Type \"help /cc\" to view the list of supported Credit Card commands\n"
+ + "Type \"help /i\" to view the list of supported income commands");
+ }
+ }
+}
diff --git a/src/main/java/seedu/mindmymoney/command/HelpCommand.java b/src/main/java/seedu/mindmymoney/command/HelpCommand.java
new file mode 100644
index 0000000000..9cc32e38ec
--- /dev/null
+++ b/src/main/java/seedu/mindmymoney/command/HelpCommand.java
@@ -0,0 +1,145 @@
+package seedu.mindmymoney.command;
+
+import seedu.mindmymoney.MindMyMoneyException;
+
+import static seedu.mindmymoney.constants.Flags.FLAG_OF_CREDIT_CARD;
+import static seedu.mindmymoney.constants.Flags.FLAG_OF_EXPENSES;
+import static seedu.mindmymoney.constants.Flags.FLAG_OF_INCOME;
+import static seedu.mindmymoney.constants.Flags.EMPTY_PARAMETER;
+
+/**
+ * Represents the Help command. This class also serves as a dummy class to return when an invalid command is
+ * received.
+ */
+public class HelpCommand extends Command {
+ protected boolean isFromUser;
+ public String helpInput;
+
+ public HelpCommand(boolean isFromUser, String helpInput) {
+ this.isFromUser = isFromUser;
+ this.helpInput = helpInput;
+ }
+
+ /**
+ * Indicates whether the program should exit.
+ *
+ * @return true if the program should exit, false otherwise.
+ */
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+
+ /**
+ * Indicates whether the help command is for expenses by looking for the /e flag.
+ *
+ * @return true if the /e flag is present, false otherwise.
+ */
+ private boolean hasExpensesFlag() {
+ return helpInput.equals(FLAG_OF_EXPENSES);
+ }
+
+ /**
+ * Indicates whether the help command is for credit card by looking for the /cc flag.
+ *
+ * @return true if the /cc flag is present, false otherwise.
+ */
+ private boolean hasCreditCardListFlag() {
+ return FLAG_OF_CREDIT_CARD.equals(helpInput);
+ }
+
+ /**
+ * Indicates whether the help command is for income by looking for the /i flag.
+ *
+ * @return true if the /i flag is present, false otherwise.
+ */
+ private boolean hasIncomeFlag() {
+ return FLAG_OF_INCOME.equals(helpInput);
+ }
+
+ /**
+ * Prints out the help page if the user requested for it. If not, it means an invalid command was received,
+ * and prints out an error message.
+ */
+ public void printExpenditureHelpPage() {
+ if (isFromUser) {
+ String helpPage = "---------------------------------------Expenditure Help Page------------------------"
+ + "---------------\n"
+ + "1. Listing all Expenditures: list /e {DATE}\n"
+ + "2. Adding an Expenditure entry: add /e /pm [PAYMENT_METHOD] /c [CATEGORY] "
+ + "/d [DESCRIPTION] /a [AMOUNT] /t [DATE]\n"
+ + "3. Calculating the total expenditure in a month: calculate /epm [DATE]\n"
+ + "4. Updating an Expenditure: update /e [NEW_INDEX] /pm [NEW_PAYMENT_METHOD] /c [NEW_CATEGORY] "
+ + "/d [NEW_DESCRIPTION] /a [NEW_AMOUNT] /t [NEW_DATE]\n"
+ + "5. Removing an Expenditure entry: delete /e [INDEX]\n"
+ + "6. Exiting the program: bye\n"
+ + "----------------------------------------------------------------------------------------------"
+ + "-----\n";
+
+ System.out.println(helpPage);
+ } else {
+ System.out.println("Invalid command! \n"
+ + "Type \"help /e\" to view the list of supported expenditure commands\n"
+ + "Type \"help /cc\" to view the list of supported Credit Card commands\n"
+ + "Type \"help /i\" to view the list of supported income commands\n");
+ }
+ }
+
+ /**
+ * Prints out the help page if the user requested for it. If not, it means an invalid command was received,
+ * and prints out an error message.
+ */
+ public void printCreditCardHelpPage() {
+ String helpPage = "---------------------------------------Credit Card Help Page--------------------------"
+ + "-------------\n"
+ + "1. Listing all Credit Cards: list /cc\n"
+ + "2. Adding a Credit Card: add /cc /n [CREDIT_CARD_NAME] /cb [CASHBACK] /cl [CREDIT_LIMIT]\n"
+ + "3. Updating a Credit Card: update /cc [INDEX] /n [NEW_NAME] /cb [NEW_CASHBACK] "
+ + "/cl [NEW_CREDIT_LIMIT]\n"
+ + "4. Removing a credit card: delete /cc [INDEX]\n"
+ + "5. Exiting the program: bye\n"
+ + "-----------------------------------------------------------------------------------------------"
+ + "----\n";
+
+ System.out.println(helpPage);
+ }
+
+ /**
+ * Prints out the Income help page.
+ */
+ public void printIncomeHelpPage() {
+ String incomeHelpPage = "--------------------------------Income Help Page------------------------------"
+ + "---------\n"
+ + "1. Listing all Incomes: list /i\n"
+ + "2. Adding an Income entry: add /i /a [AMOUNT] /c [CATEGORY]\n"
+ + "3. Updating an Income entry: update /i [INDEX] /a [NEW_AMOUNT] /c [NEW_CATEGORY]\n"
+ + "4. Removing an Income entry: delete /i [INDEX]\n"
+ + "---------------------------------------------------------------------------------------\n";
+
+ System.out.println(incomeHelpPage);
+ }
+
+ /**
+ * Prints either the Expenditure, Credit Card or Income help page based on the user's input.
+ *
+ * @throws MindMyMoneyException when an invalid command is received.
+ */
+ public void executeCommand() throws MindMyMoneyException {
+ if (helpInput.equals(EMPTY_PARAMETER)) {
+ printExpenditureHelpPage();
+ printCreditCardHelpPage();
+ printIncomeHelpPage();
+ } else if (hasExpensesFlag()) {
+ printExpenditureHelpPage();
+ } else if (hasCreditCardListFlag()) {
+ printCreditCardHelpPage();
+ } else if (hasIncomeFlag()) {
+ printIncomeHelpPage();
+ } else {
+ throw new MindMyMoneyException("Please ensure that you have entered a valid help command.\n"
+ + "Type \"help /e\" to view the list of supported expenditure commands\n"
+ + "Type \"help /cc\" to view the list of supported Credit Card commands\n"
+ + "Type \"help /i\" to view the list of supported income commands");
+ }
+ }
+}
diff --git a/src/main/java/seedu/mindmymoney/command/ListCommand.java b/src/main/java/seedu/mindmymoney/command/ListCommand.java
new file mode 100644
index 0000000000..89200c2f99
--- /dev/null
+++ b/src/main/java/seedu/mindmymoney/command/ListCommand.java
@@ -0,0 +1,262 @@
+package seedu.mindmymoney.command;
+
+import seedu.mindmymoney.MindMyMoneyException;
+import seedu.mindmymoney.constants.PrintStrings;
+import seedu.mindmymoney.data.CreditCardList;
+import seedu.mindmymoney.data.ExpenditureList;
+import seedu.mindmymoney.data.IncomeList;
+import seedu.mindmymoney.helper.GeneralFunctions;
+import seedu.mindmymoney.userfinancial.CreditCard;
+import seedu.mindmymoney.userfinancial.Expenditure;
+import seedu.mindmymoney.userfinancial.Income;
+import seedu.mindmymoney.userfinancial.User;
+
+import static seedu.mindmymoney.constants.Flags.FLAG_OF_EXPENSES;
+import static seedu.mindmymoney.constants.Flags.FLAG_OF_INCOME;
+import static seedu.mindmymoney.constants.Flags.FLAG_OF_CREDIT_CARD;
+import static seedu.mindmymoney.constants.Indexes.INDEX_OF_FIRST_ITEM;
+import static seedu.mindmymoney.constants.Indexes.INDEX_OF_SECOND_ITEM;
+import static seedu.mindmymoney.helper.TimeFunctions.isValidInputCalculateCommand;
+
+/**
+ * Represents the List command.
+ */
+public class ListCommand extends Command {
+ public ExpenditureList expenditureList;
+ public CreditCardList creditCardList;
+ public IncomeList incomeList;
+ private String listInput;
+ private static final int COUNTVALUE = 1;
+
+ public ListCommand(String listInput, User user) {
+ this.expenditureList = user.getExpenditureListArray();
+ this.creditCardList = user.getCreditCardListArray();
+ this.incomeList = user.getIncomeListArray();
+ this.listInput = listInput;
+ }
+
+ /**
+ * Indicates whether the program should exit.
+ *
+ * @return true if the program should exit, false otherwise.
+ */
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+
+ /**
+ * Indicates whether the list command is to list expenditure(s) by looking for the /e flag.
+ *
+ * @return true if the /pm flag is present, false otherwise.
+ */
+ private boolean hasExpensesFlag() {
+ return listInput.startsWith(FLAG_OF_EXPENSES);
+ }
+
+ /**
+ * Indicates whether the list command is to list credit card(s) by looking for the /cc flag.
+ *
+ * @return true if the /cc flag is present, false otherwise.
+ */
+ private boolean hasCreditCardListFlag() {
+ return FLAG_OF_CREDIT_CARD.equals(listInput);
+ }
+
+ /**
+ * Indicates whether the list command is to list income(s) by looking for the /i flag.
+ *
+ * @return true if the /i flag is present, false otherwise.
+ */
+ private boolean hasIncomeListFlag() {
+ return FLAG_OF_INCOME.equals(listInput);
+ }
+
+ /**
+ * Gets all expenditures and formats them into a String to be printed.
+ *
+ * @return String of expenditures.
+ * @throws MindMyMoneyException Throws an exception when the date is not in the correct format
+ */
+ public String expenditureListToString() throws MindMyMoneyException {
+ try {
+ int count = COUNTVALUE;
+ String listInString = "";
+ if (listInput.equals(FLAG_OF_EXPENSES)) {
+ listInString = listString(count, listInString);
+ } else {
+ listInString = outputListWithDate(count, listInString);
+ }
+ assert listInString.length() != 0 : "Return string should be non-empty";
+ return listInString;
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new MindMyMoneyException("Please ensure that you have entered a valid list command.\n"
+ + "Use 'list /e' to view your current list of expenditure\n"
+ + "Use 'list /cc' to view your current list of stored credit cards\n"
+ + "Use list /i to view your current list of incomes");
+ }
+ }
+
+ /**
+ * Outputs the list of expenses with date.
+ *
+ * @param count To obtain the numbering when listing the expenses.
+ * @param listInString String where the content of output is appended to.
+ * @return String of expenditures.
+ * @throws MindMyMoneyException Throws an exception when the date is not in the correct format.
+ */
+ public String outputListWithDate(int count, String listInString) throws MindMyMoneyException {
+ String[] inputArray = GeneralFunctions.parseInput(listInput);
+ if (!inputArray[INDEX_OF_SECOND_ITEM].equals("")) {
+ if (!isValidInputCalculateCommand(inputArray[INDEX_OF_SECOND_ITEM])) {
+ throw new MindMyMoneyException("Date has to be valid and"
+ + " in \"dd/mm/yyyy\", \"mm/yyyy\" or \"yyyy\" format!");
+ }
+ if (listStringWithDate(count, listInString, inputArray).equals("")) {
+ throw new MindMyMoneyException("Date not found in the list! Do check your input");
+ }
+ return PrintStrings.LINE + listStringWithDate(count, listInString, inputArray) + PrintStrings.LINE;
+ } else {
+ return listString(count, listInString);
+ }
+ }
+
+ /**
+ * Formats the output of expenses in list according to date.
+ *
+ * @param count To obtain the numbering when listing the expenses.
+ * @param listInString String where the content of output is appended to.
+ * @return String of expenditures
+ */
+ public String listStringWithDate(int count, String listInString, String[] inputArray) {
+ for (Expenditure expenditure : expenditureList.expenditureListArray) {
+ if (expenditure.getTime().contains(inputArray[INDEX_OF_SECOND_ITEM])) {
+ listInString += count + ". $" + String.format("%.2f", expenditure.getAmount()) + " was spent on "
+ + expenditure.getDescription() + "(" + expenditure.getCategory() + ") " + "using "
+ + expenditure.getPaymentMethod() + " [" + expenditure.getTime() + "]" + "\n";
+ count++;
+ }
+ }
+ return listInString;
+ }
+
+ /**
+ * Formats the output of all expenses in list.
+ *
+ * @param count To obtain the numbering when listing the expenses.
+ * @param listInString String where the content of output is appended to.
+ * @return String of expenditures
+ */
+ public String listString(int count, String listInString) {
+ listInString += PrintStrings.LINE;
+ for (Expenditure expenditure : expenditureList.expenditureListArray) {
+ listInString += count + ". $" + String.format("%.2f", expenditure.getAmount()) + " was spent on "
+ + expenditure.getDescription() + "(" + expenditure.getCategory() + ") " + "using "
+ + expenditure.getPaymentMethod() + " [" + expenditure.getTime() + "]" + "\n";
+ count++;
+ }
+ listInString += PrintStrings.LINE;
+ return listInString;
+ }
+
+ /**
+ * Prints user's current list of expenditures.
+ *
+ * @throws MindMyMoneyException when expenditure list is empty.
+ */
+ public void printExpenditureList() throws MindMyMoneyException {
+ if (expenditureList.isEmpty()) {
+ throw new MindMyMoneyException(
+ "Your expenditure list is currently empty! Please add some expenditures to your list first");
+ } else {
+ System.out.println(expenditureListToString());
+ }
+ }
+
+ /**
+ * Gets all credit cards and formats them into a String to be printed.
+ *
+ * @return String of credit cards.
+ */
+ public String creditCardListToString() {
+ int indexOfList = 1;
+ String listInString = "";
+ for (CreditCard creditCard : creditCardList.creditCardListArray) {
+ listInString += indexOfList + ". " + creditCard.toString();
+ indexOfList++;
+ }
+
+ assert listInString.length() != 0 : "Return string should be non-empty";
+ return listInString;
+ }
+
+ /**
+ * Prints user's current list of credit cards.
+ *
+ * @throws MindMyMoneyException when credit card list is empty.
+ */
+ public void printCreditCardList() throws MindMyMoneyException {
+ if (creditCardList.isEmpty()) {
+ throw new MindMyMoneyException(
+ "Your credit card list is currently empty! Please add some credit cards to your account first");
+ } else {
+ System.out.print(PrintStrings.LINE);
+ System.out.print(creditCardListToString());
+ System.out.println(PrintStrings.LINE);
+ }
+ }
+
+ /**
+ * Gets all incomes and formats them into a String to be printed.
+ *
+ * @return String of incomes.
+ */
+ public String incomeListToString() {
+ int indexOfList = 1;
+ String listInString = "";
+ for (Income income : incomeList.incomeListArray) {
+ listInString += indexOfList + ". " + income.toString();
+ indexOfList++;
+ }
+
+ assert listInString.length() != 0 : "Return string should be non-empty";
+ return listInString;
+ }
+
+ /**
+ * Prints user's current list of incomes.
+ *
+ * @throws MindMyMoneyException when income list is empty.
+ */
+ public void printIncomeList() throws MindMyMoneyException {
+ if (incomeList.isEmpty()) {
+ throw new MindMyMoneyException("Your income list is currently empty! "
+ + "Please add some incomes to your account first");
+ } else {
+ System.out.print(PrintStrings.LINE);
+ System.out.print(incomeListToString());
+ System.out.println(PrintStrings.LINE);
+ }
+ }
+
+ /**
+ * Prints either a list of expenditure(s), credit card(s) or income(s) based on the user's input.
+ *
+ * @throws MindMyMoneyException when an invalid command is received, along with its corresponding error message.
+ */
+ @Override
+ public void executeCommand() throws MindMyMoneyException {
+ if (hasExpensesFlag()) {
+ printExpenditureList();
+ } else if (hasCreditCardListFlag()) {
+ printCreditCardList();
+ } else if (hasIncomeListFlag()) {
+ printIncomeList();
+ } else {
+ throw new MindMyMoneyException("Please ensure that you have entered a valid list command.\n"
+ + "Use 'list /e' to view your current list of expenditure\n"
+ + "Use 'list /cc' to view your current list of stored credit cards\n"
+ + "Use list /i to view your current list of incomes");
+ }
+ }
+}
diff --git a/src/main/java/seedu/mindmymoney/command/UpdateCommand.java b/src/main/java/seedu/mindmymoney/command/UpdateCommand.java
new file mode 100644
index 0000000000..2bbc576b42
--- /dev/null
+++ b/src/main/java/seedu/mindmymoney/command/UpdateCommand.java
@@ -0,0 +1,344 @@
+package seedu.mindmymoney.command;
+
+import seedu.mindmymoney.MindMyMoneyException;
+import seedu.mindmymoney.data.CreditCardList;
+import seedu.mindmymoney.data.ExpenditureList;
+import seedu.mindmymoney.data.IncomeList;
+import seedu.mindmymoney.userfinancial.CreditCard;
+import seedu.mindmymoney.userfinancial.Expenditure;
+import seedu.mindmymoney.userfinancial.Income;
+import seedu.mindmymoney.userfinancial.User;
+
+import static seedu.mindmymoney.constants.Flags.FLAG_OF_CREDIT_CARD;
+import static seedu.mindmymoney.constants.Flags.FLAG_OF_INCOME;
+import static seedu.mindmymoney.constants.Flags.FLAG_OF_CATEGORY;
+import static seedu.mindmymoney.constants.Flags.FLAG_OF_PAYMENT_METHOD;
+import static seedu.mindmymoney.constants.Flags.FLAG_OF_AMOUNT;
+import static seedu.mindmymoney.constants.Flags.FLAG_OF_TIME;
+import static seedu.mindmymoney.constants.Flags.FLAG_END_VALUE;
+import static seedu.mindmymoney.constants.Flags.FLAG_OF_CARD_LIMIT;
+import static seedu.mindmymoney.constants.Flags.FLAG_OF_CARD_NAME;
+import static seedu.mindmymoney.constants.Flags.FLAG_OF_CASHBACK;
+import static seedu.mindmymoney.constants.Flags.FLAG_OF_DESCRIPTION;
+import static seedu.mindmymoney.constants.Flags.FLAG_OF_EXPENSES;
+
+import static seedu.mindmymoney.constants.Indexes.INDEX_OF_SECOND_ITEM;
+import static seedu.mindmymoney.constants.Indexes.LIST_INDEX_CORRECTION;
+import static seedu.mindmymoney.data.CreditCardList.isEqualName;
+import static seedu.mindmymoney.data.CreditCardList.isEqualCashback;
+import static seedu.mindmymoney.data.CreditCardList.isEqualCardLimit;
+import static seedu.mindmymoney.data.ExpenditureList.isEqualCategory;
+import static seedu.mindmymoney.data.ExpenditureList.isEqualPaymentMethod;
+import static seedu.mindmymoney.data.ExpenditureList.isEqualDescription;
+import static seedu.mindmymoney.data.ExpenditureList.isEqualAmount;
+import static seedu.mindmymoney.data.ExpenditureList.isEqualTime;
+import static seedu.mindmymoney.data.IncomeList.isEqualIncomeCategory;
+import static seedu.mindmymoney.data.IncomeList.isEqualIncomeAmount;
+import static seedu.mindmymoney.helper.AddCommandInputTests.testCreditCardParameters;
+import static seedu.mindmymoney.helper.AddCommandInputTests.testUpdateExpenditureParameters;
+import static seedu.mindmymoney.helper.AddCommandInputTests.testUpdateIncomeParameters;
+import static seedu.mindmymoney.helper.GeneralFunctions.capitalise;
+import static seedu.mindmymoney.helper.GeneralFunctions.parseInputWithCommandFlag;
+import static seedu.mindmymoney.helper.GeneralFunctions.formatFloat;
+
+/**
+ * Represents the Update command.
+ */
+public class UpdateCommand extends Command {
+ private final String updateInput;
+ public ExpenditureList expenditureList;
+ public CreditCardList creditCardList;
+ public IncomeList incomeList;
+
+ public UpdateCommand(String updateInput, User user) {
+ this.updateInput = updateInput;
+ this.expenditureList = user.getExpenditureListArray();
+ this.creditCardList = user.getCreditCardListArray();
+ this.incomeList = user.getIncomeListArray();
+ }
+
+ /**
+ * Indicates whether the program should exit.
+ *
+ * @return true if the program should exit, false otherwise.
+ */
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+
+ /**
+ * Indicates whether the help command is for expenses by looking for the /e flag.
+ *
+ * @return true if the /e flag is present, false otherwise.
+ */
+ private boolean hasExpensesFlag() {
+ return updateInput.startsWith(FLAG_OF_EXPENSES);
+ }
+
+ /**
+ * Indicates whether the update command is to update a credit card by looking for the /cc flag.
+ *
+ * @return true if the /cc flag is present, false otherwise.
+ */
+ private boolean hasCreditCardFlag() {
+ return updateInput.startsWith(FLAG_OF_CREDIT_CARD);
+ }
+
+ /**
+ * Indicates whether the update command is to update an income by looking for the /i flag.
+ *
+ * @return true if the /i flag is present, false otherwise.
+ */
+ private boolean hasIncomeFlag() {
+ return updateInput.startsWith(FLAG_OF_INCOME);
+ }
+
+ /**
+ * Updates the total expenditure field in the credit card specified in the expenditure item, if the payment
+ * method is not Cash.
+ *
+ * @param newPaymentMethod Name of payment method to be updated.
+ * @param newExpenditureAmount Amount of new expenditure.
+ * @param expenditureIndex Index of expenditure to be updated
+ * @throws MindMyMoneyException when the payment method is not cash and is not found in user's credit card list.
+ */
+ private void updatePaymentMethod(String newPaymentMethod, float newExpenditureAmount, int expenditureIndex)
+ throws MindMyMoneyException {
+ Expenditure oldExpenditure = expenditureList.get(expenditureIndex);
+ String oldPaymentMethod = oldExpenditure.getPaymentMethod();
+ if (!oldPaymentMethod.equals("Cash")) {
+ CreditCard oldCreditCard = creditCardList.get(oldPaymentMethod);
+ oldCreditCard.deductExpenditure(oldExpenditure.getAmount());
+ }
+
+ if (!newPaymentMethod.equalsIgnoreCase("cash")) {
+ CreditCard newCreditCard = creditCardList.get(newPaymentMethod);
+ if (newCreditCard == null) {
+ throw new MindMyMoneyException("Please double-check your input! New payment method is not found!");
+ }
+ newCreditCard.addExpenditure(newExpenditureAmount);
+ }
+ }
+
+ /**
+ * Updates an Expenditure entry in user's expenditure list.
+ *
+ * @throws MindMyMoneyException when an invalid command is received.
+ */
+ public void updateExpenditure() throws MindMyMoneyException {
+ try {
+ String[] parseUpdateInput = updateInput.split(" ");
+ String indexAsString = parseUpdateInput[INDEX_OF_SECOND_ITEM];
+ final int indexToUpdate = Integer.parseInt(indexAsString) + LIST_INDEX_CORRECTION;
+
+ String newPaymentMethod =
+ parseInputWithCommandFlag(updateInput, FLAG_OF_PAYMENT_METHOD, FLAG_OF_CATEGORY).trim();
+ String inputCategory = parseInputWithCommandFlag(updateInput, FLAG_OF_CATEGORY, FLAG_OF_DESCRIPTION).trim();
+ String newDescription = parseInputWithCommandFlag(updateInput, FLAG_OF_DESCRIPTION, FLAG_OF_AMOUNT).trim();
+ String newAmountAsString = parseInputWithCommandFlag(updateInput, FLAG_OF_AMOUNT, FLAG_OF_TIME).trim();
+ String inputTime = parseInputWithCommandFlag(updateInput, FLAG_OF_TIME, FLAG_END_VALUE).trim();
+
+ testUpdateExpenditureParameters(indexToUpdate, newPaymentMethod, inputCategory, newDescription,
+ newAmountAsString, inputTime, creditCardList, expenditureList);
+
+ if (capitalise(newPaymentMethod).equals("Cash")) {
+ newPaymentMethod = capitalise(newPaymentMethod);
+ }
+ final String newCategory = capitalise(inputCategory);
+ float newAmountAsFloat = formatFloat(Float.parseFloat(newAmountAsString));
+
+ if (isSimilarExpenditure(indexToUpdate, newPaymentMethod, newCategory, newDescription, newAmountAsFloat,
+ inputTime)) {
+ throw new MindMyMoneyException("Expense fields to be updated is similar to the expense in the list.\n"
+ + "Please make sure the field descriptions you want to change are different.");
+ }
+
+ updatePaymentMethod(newPaymentMethod, newAmountAsFloat, indexToUpdate);
+
+ // Create new expenditure object to substitute in
+ Expenditure newExpenditure = new Expenditure(newPaymentMethod, newCategory, newDescription,
+ newAmountAsFloat, inputTime);
+ expenditureList.set(indexToUpdate, newExpenditure);
+ System.out.println("Successfully set expenditure " + indexAsString + " to:\n"
+ + "$" + String.format("%.2f", newExpenditure.getAmount()) + " was spent on "
+ + newExpenditure.getDescription()
+ + "(" + newExpenditure.getCategory() + ") " + "using " + newExpenditure.getPaymentMethod()
+ + " [" + newExpenditure.getTime() + "]");
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new MindMyMoneyException("Did you forget to input INDEX, DESCRIPTION or AMOUNT?");
+ } catch (NumberFormatException e) {
+ throw new MindMyMoneyException("AMOUNT and INDEX must be a number");
+ } catch (IndexOutOfBoundsException e) {
+ throw new MindMyMoneyException("Please input a valid index");
+ }
+ }
+
+ /**
+ * Checks if the fields in the update command is similar to the fields in the expenditure in the list.
+ *
+ * @param index index of expenditure to update.
+ * @param newPaymentMethod new payment method field to be updated.
+ * @param newCategory new category field to be updated.
+ * @param newDescription new description field to be updated.
+ * @param newAmountAsFloat new amount field to be updated.
+ * @param newTime new time field to be updated.
+ * @return true if fields are similar, false otherwise.
+ */
+ public boolean isSimilarExpenditure(int index, String newPaymentMethod, String newCategory, String newDescription,
+ float newAmountAsFloat, String newTime) {
+ if (isEqualPaymentMethod(expenditureList, index, newPaymentMethod)
+ && isEqualCategory(expenditureList, index, newCategory)
+ && isEqualDescription(expenditureList, index, newDescription)
+ && isEqualAmount(expenditureList, index, newAmountAsFloat)
+ && isEqualTime(expenditureList, index, newTime)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Updates a Credit Card entry in user's credit card list.
+ *
+ * @throws MindMyMoneyException when an invalid command is received.
+ */
+ public void updateCreditCard() throws MindMyMoneyException {
+ try {
+ String[] parseUpdateInput = updateInput.split(" ");
+
+ // Get index to update
+ String indexAsString = parseUpdateInput[INDEX_OF_SECOND_ITEM];
+
+ // Parse data from input
+ String newCardName = parseInputWithCommandFlag(updateInput, FLAG_OF_CARD_NAME,
+ FLAG_OF_CASHBACK).trim();
+ String newCashBack = parseInputWithCommandFlag(updateInput, FLAG_OF_CASHBACK,
+ FLAG_OF_CARD_LIMIT).trim();
+ String newCardLimit = parseInputWithCommandFlag(updateInput, FLAG_OF_CARD_LIMIT,
+
+ FLAG_END_VALUE).trim();
+ testCreditCardParameters(newCardName, newCashBack, newCardLimit, creditCardList);
+
+ int indexToUpdate = Integer.parseInt(indexAsString) + LIST_INDEX_CORRECTION;
+ float newCashBackAsDouble = formatFloat(Float.parseFloat(newCashBack));
+ float newCardLimitAsFloat = formatFloat(Float.parseFloat(newCardLimit));
+ CreditCard oldCreditCard = creditCardList.get(indexToUpdate);
+ if (oldCreditCard.getTotalExpenditure() > newCardLimitAsFloat) {
+ throw new MindMyMoneyException("Current spending has already exceeded the new limit!");
+ }
+ if (isSimilarCreditCard(indexToUpdate, newCardName, newCashBackAsDouble, newCardLimitAsFloat)) {
+ throw new MindMyMoneyException("Credit Card fields to be updated is similar to the credit card in "
+ + "the list.\n" + "Please make sure the field descriptions you want to change are different.");
+ }
+ CreditCard newCreditCard = new CreditCard(newCardName, newCashBackAsDouble,
+ newCardLimitAsFloat);
+
+ creditCardList.set(indexToUpdate, newCreditCard);
+ System.out.println("Successfully set credit card " + indexAsString + " to:\n"
+ + newCreditCard);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new MindMyMoneyException("Did you forget to input INDEX, NAME, CASHBACK or CREDIT LIMIT?");
+ } catch (NumberFormatException e) {
+ throw new MindMyMoneyException("INDEX, CASHBACK and CREDIT LIMIT must be a number");
+ } catch (IndexOutOfBoundsException e) {
+ throw new MindMyMoneyException("Please input a valid index");
+ }
+ }
+
+ /**
+ * Checks if the fields in the update command is similar to the fields in the credit card in the list.
+ *
+ * @param index index of credit card to update.
+ * @param newCardName new card name field to be updated.
+ * @param newCashback new cash back field to be updated.
+ * @param newCardLimit new card limit field to be updated.
+ * @return true if fields are similar, false otherwise.
+ */
+ public boolean isSimilarCreditCard(int index, String newCardName, double newCashback, float newCardLimit) {
+ if (isEqualName(creditCardList, index, newCardName)
+ && isEqualCashback(creditCardList, index, newCashback)
+ && isEqualCardLimit(creditCardList, index, newCardLimit)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Updates an Income entry in user's income list.
+ *
+ * @throws MindMyMoneyException when an invalid command is received.
+ */
+ public void updateIncome() throws MindMyMoneyException {
+ try {
+ String[] parseUpdateInput = updateInput.split(" ");
+
+ String indexAsString = parseUpdateInput[INDEX_OF_SECOND_ITEM];
+ int indexToUpdate = Integer.parseInt(indexAsString) + LIST_INDEX_CORRECTION;
+
+ String newAmountAsString = parseInputWithCommandFlag(updateInput, FLAG_OF_AMOUNT,
+ FLAG_OF_CATEGORY).trim();
+ int newAmountAsInt = Integer.parseInt(newAmountAsString);
+
+ String inputCategory = parseInputWithCommandFlag(updateInput, FLAG_OF_CATEGORY,
+ FLAG_END_VALUE).trim();
+
+ testUpdateIncomeParameters(newAmountAsInt, inputCategory);
+ String newCategory = capitalise(inputCategory);
+ if (isSimilarIncome(indexToUpdate, newAmountAsInt, newCategory)) {
+ throw new MindMyMoneyException("Income fields to be updated is similar to the income in the list.\n"
+ + "Please make sure the field descriptions you want to change are different.");
+ }
+ Income newIncome = new Income(newAmountAsInt, newCategory);
+ incomeList.set(indexToUpdate, newIncome);
+
+ System.out.print("Successfully set income " + indexAsString + " to:\n"
+ + "Amount: $" + newAmountAsString + "\n"
+ + "Category: " + newCategory + "\n"
+ + System.lineSeparator());
+
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new MindMyMoneyException("Did you forget to input AMOUNT or CATEGORY?");
+ } catch (NumberFormatException e) {
+ throw new MindMyMoneyException("AMOUNT must be a number");
+ } catch (IndexOutOfBoundsException e) {
+ throw new MindMyMoneyException("Please input a valid index");
+ }
+ }
+
+ /**
+ * Checks if the fields in the update command is similar to the fields in the income in the list.
+ *
+ * @param index index of income to update.
+ * @param newAmount new amount to be updated.
+ * @param newCategory new category to be updated.
+ * @return true if fields are similar, false otherwise.
+ */
+ public boolean isSimilarIncome(int index, int newAmount, String newCategory) {
+ if (isEqualIncomeCategory(incomeList, index, newCategory)
+ && isEqualIncomeAmount(incomeList, index, newAmount)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Updates either an Expenditure, Credit Card or Income entry based on the user's input.
+ *
+ * @throws MindMyMoneyException when an invalid command is received, along with its corresponding error message.
+ */
+ @Override
+ public void executeCommand() throws MindMyMoneyException {
+ if (hasExpensesFlag()) {
+ updateExpenditure();
+ } else if (hasCreditCardFlag()) {
+ updateCreditCard();
+ } else if (hasIncomeFlag()) {
+ updateIncome();
+ } else {
+ throw new MindMyMoneyException("You are missing a flag in your command\n"
+ + "Type \"help /e\" to view the list of supported expenditure commands\n"
+ + "Type \"help /cc\" to view the list of supported Credit Card commands\n"
+ + "Type \"help /i\" to view the list of supported income commands");
+ }
+ }
+}
diff --git a/src/main/java/seedu/mindmymoney/constants/CalculationConversion.java b/src/main/java/seedu/mindmymoney/constants/CalculationConversion.java
new file mode 100644
index 0000000000..2c79ab4f08
--- /dev/null
+++ b/src/main/java/seedu/mindmymoney/constants/CalculationConversion.java
@@ -0,0 +1,8 @@
+package seedu.mindmymoney.constants;
+
+/**
+ * Container for numbers used in calculations.
+ */
+public class CalculationConversion {
+ public static final float FLOAT_TO_PERCENTAGE = (float) 0.01;
+}
diff --git a/src/main/java/seedu/mindmymoney/constants/ExpenditureCategoryTypes.java b/src/main/java/seedu/mindmymoney/constants/ExpenditureCategoryTypes.java
new file mode 100644
index 0000000000..c61c5030ab
--- /dev/null
+++ b/src/main/java/seedu/mindmymoney/constants/ExpenditureCategoryTypes.java
@@ -0,0 +1,13 @@
+package seedu.mindmymoney.constants;
+
+/**
+ * Container for Expenditure Category types.
+ */
+public enum ExpenditureCategoryTypes {
+ FOOD,
+ TRANSPORT,
+ UTILITIES,
+ PERSONAL,
+ ENTERTAINMENT,
+ OTHERS
+}
diff --git a/src/main/java/seedu/mindmymoney/constants/ExpenditureFields.java b/src/main/java/seedu/mindmymoney/constants/ExpenditureFields.java
new file mode 100644
index 0000000000..31655875e2
--- /dev/null
+++ b/src/main/java/seedu/mindmymoney/constants/ExpenditureFields.java
@@ -0,0 +1,12 @@
+package seedu.mindmymoney.constants;
+
+/**
+ * Container for fields in expenditure.
+ */
+public enum ExpenditureFields {
+ EXPENDITURE,
+ CATEGORY,
+ DESCRIPTION,
+ AMOUNT,
+ TIME,
+}
diff --git a/src/main/java/seedu/mindmymoney/constants/Flags.java b/src/main/java/seedu/mindmymoney/constants/Flags.java
new file mode 100644
index 0000000000..9ea6ce22f1
--- /dev/null
+++ b/src/main/java/seedu/mindmymoney/constants/Flags.java
@@ -0,0 +1,21 @@
+package seedu.mindmymoney.constants;
+
+/**
+ * Container for flags used in commands.
+ */
+public class Flags {
+ public static final String FLAG_OF_PAYMENT_METHOD = "/pm";
+ public static final String FLAG_OF_CATEGORY = "/c";
+ public static final String FLAG_OF_DESCRIPTION = "/d";
+ public static final String FLAG_OF_AMOUNT = "/a";
+ public static final String FLAG_OF_TIME = "/t";
+ public static final String FLAG_END_VALUE = "";
+ public static final String FLAG_OF_EXPENDITURE_PER_MONTH = "/epm";
+ public static final String FLAG_OF_CARD_NAME = "/n";
+ public static final String FLAG_OF_CASHBACK = "/cb";
+ public static final String FLAG_OF_CARD_LIMIT = "/cl";
+ public static final String FLAG_OF_CREDIT_CARD = "/cc";
+ public static final String FLAG_OF_EXPENSES = "/e";
+ public static final String FLAG_OF_INCOME = "/i";
+ public static final String EMPTY_PARAMETER = "";
+}
diff --git a/src/main/java/seedu/mindmymoney/constants/IncomeCategoryTypes.java b/src/main/java/seedu/mindmymoney/constants/IncomeCategoryTypes.java
new file mode 100644
index 0000000000..4592cf6b0a
--- /dev/null
+++ b/src/main/java/seedu/mindmymoney/constants/IncomeCategoryTypes.java
@@ -0,0 +1,11 @@
+package seedu.mindmymoney.constants;
+
+/**
+ * Container for Income Category types.
+ */
+public enum IncomeCategoryTypes {
+ SALARY,
+ ALLOWANCE,
+ INVESTMENT,
+ OTHERS
+}
diff --git a/src/main/java/seedu/mindmymoney/constants/Indexes.java b/src/main/java/seedu/mindmymoney/constants/Indexes.java
new file mode 100644
index 0000000000..ea98ce2d75
--- /dev/null
+++ b/src/main/java/seedu/mindmymoney/constants/Indexes.java
@@ -0,0 +1,22 @@
+package seedu.mindmymoney.constants;
+
+/**
+ * Container for indexes used by handlers.
+ */
+public class Indexes {
+ public static final int INDEX_OF_FIRST_ITEM = 0;
+ public static final int INDEX_OF_SECOND_ITEM = 1;
+ public static final int INDEX_OF_THIRD_ITEM = 2;
+ public static final int SPLIT_LIMIT = 2;
+ public static final int LIST_INDEX_CORRECTION = -1;
+ public static final int MINIMUM_EXPENDITURE_COMMAND_LENGTH = 3;
+ public static final int MINIMUM_CREDIT_CARD_COMMAND_LENGTH = 3;
+ public static final int MINIMUM_INCOME_COMMAND_LENGTH = 3;
+ public static final int MIN_STUDENT_INCOME = 0;
+ public static final int MAX_STUDENT_INCOME = 1000000;
+ public static final int MIN_EXPENDITURE_AMOUNT = 0;
+ public static final int MIN_CASHBACK_AMOUNT = 0;
+ public static final int MAX_CASHBACK_AMOUNT = 100;
+ public static final int MAX_EXPENDITURE_AMOUNT = 1000000;
+ public static final int MAX_CREDIT_CARD_LIMIT = 40000;
+}
diff --git a/src/main/java/seedu/mindmymoney/constants/PaymentMethod.java b/src/main/java/seedu/mindmymoney/constants/PaymentMethod.java
new file mode 100644
index 0000000000..de01a65873
--- /dev/null
+++ b/src/main/java/seedu/mindmymoney/constants/PaymentMethod.java
@@ -0,0 +1,8 @@
+package seedu.mindmymoney.constants;
+
+/**
+ * Container for expenditure types.
+ */
+public enum PaymentMethod {
+ CASH,
+}
diff --git a/src/main/java/seedu/mindmymoney/constants/PrintStrings.java b/src/main/java/seedu/mindmymoney/constants/PrintStrings.java
new file mode 100644
index 0000000000..b3120d6deb
--- /dev/null
+++ b/src/main/java/seedu/mindmymoney/constants/PrintStrings.java
@@ -0,0 +1,62 @@
+package seedu.mindmymoney.constants;
+
+/**
+ * Container for strings to be printed.
+ */
+public abstract class PrintStrings {
+ private PrintStrings() {
+ }
+
+ public static final String LINE = "----------------------"
+ + "-------------------------" + System.lineSeparator();
+
+ public static final String[] tips = new String[] {"Set quantifiable financial goals",
+ "Split your income into different bank accounts, such as a Savings and Expenses account",
+ "Spend no more than 50% of your income on necessities",
+ "Only spend a maximum of 20% of your income on desires",
+ "At least 30% of your income should go into long-term savings and investments",
+ "Your emergency savings should be worth 3 to 6 months of your monthly salary",
+ "Your emergency funds should be relatively liquid",
+ "Understand your monthly cash flow and track how much you are spending",
+ "Put your emergency funds into high-interest savings accounts",
+ "Pay yourself first. Set aside a fixed amount for savings, before factoring other costs",
+ "Credit cards come with perks, but also high interest charges if you miss your payment",
+ "Credit cards are convenient, and paying them off on time helps build a good credit score",
+ "Banks will be more keen on lending money if you have a good credit score",
+ "Set a budget and stick to it",
+ "Don't make impulse purchases. Wait a week before deciding",
+ "Be frugal, not cheap",
+ "Discuss finances with your significant other",
+ "It is never too early to plan for retirement",
+ "First step of retirement planning is to determine how much money is needed at your desired age",
+ "Don't live beyond your means, especially to keep up appearances on social media",
+ "Start to read and learn more about personal finance",
+ "Check your bank accounts regularly to detect any unexpected charges",
+ "Be aware of your bank's minimum balance charges",
+ "Track your net worth, which can help visualize your progress towards your financial goals",
+ "It is often a better idea to buy a higher quality product even if it costs more, as it lasts longer",
+ "You can have too much savings. Start considering about investing",
+ "Invest in yourself. Stay updated on industry developments and continuously upgrade your skills",
+ "Optimize your earnings. Look out for side income opportunities and make smart career moves",
+ "Consider buying medical insurance, to enable you to make claims for treatment",
+ "If you have dependents such as kids or aged parents, life insurance is a must",
+ "Life insurance pays a lump sum upon death or permanent disability",
+ "Critical illness insurance pays a lump sum upon diagnosis of a critical illness",
+ "Must have insurance plans include a Shield Plan, Critical Illness and Life Insurance",
+ "Insurance is for protection, not investments. Get sufficient coverage for the lowest cost",
+ "In general, you should not spend more than 10% of your income on insurance premiums",
+ "The older you are, the more expensive insurance becomes",
+ "Make use of compound interest and start investing early",
+ "Time in market is better than timing the market",
+ "Dollar cost averaging refers to investing small amounts at regular intervals",
+ "Dollar cost averaging maximises your chances of paying a low average price over time",
+ "Investing for retirement and financial goals is better than just saving",
+ "Only invest in what you know. Investing always comes with risk",
+ "Having a diversified investment portfolio helps reduce risk",
+ "Investment-linked policies are policies that have insurance coverage and investment elements",
+ "Investment-linked policies may be an expensive way to invest",
+ "Investment-linked policies may not provide adequate insurance coverage",
+ "Pay attention to additional fees when investing",
+ "Invest in assets, and avoid buying liabilities",
+ "You can start investing as low as $100 per month through a Regular Savings Plan (RSP)"};
+}
diff --git a/src/main/java/seedu/mindmymoney/constants/ValidationRegexTypes.java b/src/main/java/seedu/mindmymoney/constants/ValidationRegexTypes.java
new file mode 100644
index 0000000000..968ce60100
--- /dev/null
+++ b/src/main/java/seedu/mindmymoney/constants/ValidationRegexTypes.java
@@ -0,0 +1,11 @@
+package seedu.mindmymoney.constants;
+
+/**
+ * Container for validation regex types used in commands.
+ */
+public class ValidationRegexTypes {
+ public static final String VALIDATION_REGEX_D =
+ "^([0][1-9]|[12][0-9]|3[01])/([0][1-9]|1[012])/([0-9][0-9][0-9][0-9])$";
+ public static final String VALIDATION_REGEX_M = "^([0][1-9]|1[012])/([0-9][0-9][0-9][0-9])$";
+ public static final String VALIDATION_REGEX_Y = "^([0-9][0-9][0-9][0-9])$";
+}
diff --git a/src/main/java/seedu/mindmymoney/data/CreditCardList.java b/src/main/java/seedu/mindmymoney/data/CreditCardList.java
new file mode 100644
index 0000000000..b4bdee2fa3
--- /dev/null
+++ b/src/main/java/seedu/mindmymoney/data/CreditCardList.java
@@ -0,0 +1,164 @@
+package seedu.mindmymoney.data;
+
+import seedu.mindmymoney.MindMyMoneyException;
+import seedu.mindmymoney.userfinancial.CreditCard;
+
+import java.util.ArrayList;
+import java.util.Scanner;
+
+import static seedu.mindmymoney.helper.SerializerFunctions.SERIALIZATION_CREDIT_CARD_END_MARKER;
+import static seedu.mindmymoney.helper.SerializerFunctions.SERIALIZATION_CREDIT_CARD_START_MARKER;
+import static seedu.mindmymoney.helper.SerializerFunctions.addListToStringBuilder;
+import static seedu.mindmymoney.helper.SerializerFunctions.convertInputToList;
+
+
+/**
+ * Container for Credit Cards.
+ */
+public class CreditCardList {
+ public ArrayList creditCardListArray;
+
+ public CreditCardList() {
+ this(new ArrayList<>());
+ }
+
+ public CreditCardList(ArrayList listArray) {
+ this.creditCardListArray = listArray;
+ }
+
+ /**
+ * Checks if the list is empty.
+ *
+ * @return true if list is empty, false otherwise.
+ */
+ public boolean isEmpty() {
+ return creditCardListArray.isEmpty();
+ }
+
+ /**
+ * Represents the size of the CreditCard list.
+ *
+ * @return size of the task list.
+ */
+ public int size() {
+ return creditCardListArray.size();
+ }
+
+ /**
+ * Retrieves the CreditCard entry at the given index.
+ *
+ * @param index Index of the CreditCard entry.
+ * @return The CreditCard object.
+ */
+ public CreditCard get(int index) {
+ return creditCardListArray.get(index);
+ }
+
+
+ /**
+ * Retrieves the CreditCard with the given name.
+ *
+ * @param name name of credit card to be searched.
+ * @return CreditCard object with matching name as parameter.
+ */
+ public CreditCard get(String name) {
+ for (CreditCard creditCard : creditCardListArray) {
+ if (creditCard.getNameOfCard().equalsIgnoreCase(name.toLowerCase())) {
+ return creditCard;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Deletes the CreditCard entry from the list.
+ *
+ * @param index Index of the CreditCard entry to delete.
+ */
+ public void delete(int index) {
+ creditCardListArray.remove(index);
+ }
+
+ /**
+ * Adds an CreditCard item to the list.
+ *
+ * @param item The CreditCard item to be added.
+ */
+ public void add(CreditCard item) {
+ creditCardListArray.add(item);
+ }
+
+ /**
+ * Updates the credit card entry at the given index.
+ *
+ * @param index Index of the entry to be updated.
+ * @param creditCard The new CreditCard entry.
+ */
+ public void set(int index, CreditCard creditCard) {
+ creditCardListArray.set(index, creditCard);
+ }
+
+ /**
+ * Checks if card name is equal.
+ *
+ * @param creditCardList List of credit card details.
+ * @param index Index of credit card list item.
+ * @param name Card name to compare with.
+ * @return True if card name is equal, false otherwise.
+ */
+ public static boolean isEqualName(CreditCardList creditCardList, int index, String name) {
+ return creditCardList.get(index).getNameOfCard().equals(name);
+ }
+
+ /**
+ * Checks if cashback is equal.
+ *
+ * @param creditCardList List of credit card details.
+ * @param index Index of credit card list item.
+ * @param cashback Cashback to compare with.
+ * @return True if cashback is equal, false otherwise.
+ */
+ public static boolean isEqualCashback(CreditCardList creditCardList, int index, double cashback) {
+ return creditCardList.get(index).getCashback() == cashback;
+ }
+
+ /**
+ * Checks if card limit is equal.
+ *
+ * @param creditCardList List of credit card details.
+ * @param index Index of credit card list item.
+ * @param cardLimit Card limit to compare with.
+ * @return True if card limit is equal, false otherwise.
+ */
+ public static boolean isEqualCardLimit(CreditCardList creditCardList, int index, float cardLimit) {
+ return creditCardList.get(index).getMonthlyCardLimit() == cardLimit;
+ }
+
+ /**
+ * Converts this CreditCardList into a machine-readable format.
+ * @return The serialized CreditCardList
+ */
+ public String serialize() {
+ StringBuilder sb = new StringBuilder();
+ addListToStringBuilder(SERIALIZATION_CREDIT_CARD_START_MARKER,
+ SERIALIZATION_CREDIT_CARD_END_MARKER,
+ creditCardListArray,
+ sb);
+ return sb.toString();
+ }
+
+ /**
+ * Reads a serialized CreditCardList from the scanner.
+ * @param scanner A scanner
+ * @returns A CreditCardList
+ * @throws MindMyMoneyException if the format is invalid.
+ */
+ public static CreditCardList deserializeFrom(Scanner scanner) throws MindMyMoneyException {
+ CreditCardList creditCardList = new CreditCardList();
+ creditCardList.creditCardListArray = convertInputToList(
+ SERIALIZATION_CREDIT_CARD_START_MARKER,
+ SERIALIZATION_CREDIT_CARD_END_MARKER,
+ scanner, CreditCard::deserialize);
+ return creditCardList;
+ }
+}
diff --git a/src/main/java/seedu/mindmymoney/data/ExpenditureList.java b/src/main/java/seedu/mindmymoney/data/ExpenditureList.java
new file mode 100644
index 0000000000..72ee05e6b1
--- /dev/null
+++ b/src/main/java/seedu/mindmymoney/data/ExpenditureList.java
@@ -0,0 +1,166 @@
+package seedu.mindmymoney.data;
+
+import seedu.mindmymoney.MindMyMoneyException;
+import seedu.mindmymoney.userfinancial.Expenditure;
+
+import java.util.ArrayList;
+import java.util.Scanner;
+
+import static seedu.mindmymoney.helper.SerializerFunctions.SERIALIZATION_EXPENDITURE_END_MARKER;
+import static seedu.mindmymoney.helper.SerializerFunctions.SERIALIZATION_EXPENDITURE_START_MARKER;
+import static seedu.mindmymoney.helper.SerializerFunctions.addListToStringBuilder;
+import static seedu.mindmymoney.helper.SerializerFunctions.convertInputToList;
+
+/**
+ * Container for expenditure lists.
+ */
+public class ExpenditureList {
+ public ArrayList expenditureListArray;
+
+ public ExpenditureList() {
+ this(new ArrayList<>());
+ }
+
+ public ExpenditureList(ArrayList listArray) {
+ this.expenditureListArray = listArray;
+ }
+
+ /**
+ * Checks if the list is empty.
+ *
+ * @return true if list is empty, false otherwise
+ */
+ public boolean isEmpty() {
+ return expenditureListArray.isEmpty();
+ }
+
+ /**
+ * Represents the size of the task list.
+ *
+ * @return size of the task list.
+ */
+ public int size() {
+ return expenditureListArray.size();
+ }
+
+ /**
+ * Retrieves the Expenditure entry at the given index.
+ *
+ * @param index Index of the Expenditure entry.
+ * @return The Expenditure object.
+ */
+ public Expenditure get(int index) {
+ return expenditureListArray.get(index);
+ }
+
+ /**
+ * Deletes the Expenditure entry from the list.
+ *
+ * @param index Index of the Expenditure entry to delete.
+ */
+ public void delete(int index) {
+ expenditureListArray.remove(index);
+ }
+
+ /**
+ * Adds an Expenditure entry to the list.
+ *
+ * @param item The Expenditure entry to be added.
+ */
+ public void add(Expenditure item) {
+ expenditureListArray.add(item);
+ }
+
+ /**
+ * Updates the Expenditure entry at the given index.
+ *
+ * @param index Index of the entry to be updated.
+ * @param item The new Expenditure entry.
+ */
+ public void set(int index, Expenditure item) {
+ expenditureListArray.set(index, item);
+ }
+
+ /**
+ * Checks if payment method is equal.
+ * @param expenditureList List of expenditures.
+ * @param index Index of expenditure in list.
+ * @param paymentMethod Payment method to compare with.
+ * @return True if payment method is equal, false otherwise.
+ */
+ public static boolean isEqualPaymentMethod(ExpenditureList expenditureList, int index, String paymentMethod) {
+ return expenditureList.get(index).getPaymentMethod().equals(paymentMethod);
+ }
+
+ /**
+ * Checks if category is equal.
+ * @param expenditureList List of expenditures details.
+ * @param index Index of expenditure in list.
+ * @param category Category to compare with.
+ * @return True if category is equal, false otherwise.
+ */
+ public static boolean isEqualCategory(ExpenditureList expenditureList, int index, String category) {
+ return expenditureList.get(index).getCategory().equals(category);
+ }
+
+ /**
+ * Checks if description is equal.
+ * @param expenditureList List of expenditures details.
+ * @param index Index of expenditure in list.
+ * @param description Description to compare with.
+ * @return True if description is equal, false otherwise.
+ */
+ public static boolean isEqualDescription(ExpenditureList expenditureList, int index, String description) {
+ return expenditureList.get(index).getDescription().equals(description);
+ }
+
+ /**
+ * Checks if amount is equal.
+ * @param expenditureList List of expenditures details.
+ * @param index Index of expenditure in list.
+ * @param amount Amount to compare with.
+ * @return True if amount is equal, false otherwise.
+ */
+ public static boolean isEqualAmount(ExpenditureList expenditureList, int index, float amount) {
+ return expenditureList.get(index).getAmount() == amount;
+ }
+
+ /**
+ * Checks if time is equal.
+ * @param expenditureList List of expenditures details.
+ * @param index Index of expenditure in list.
+ * @param time Time to compare with.
+ * @return True if time is equal, false otherwise.
+ */
+ public static boolean isEqualTime(ExpenditureList expenditureList, int index, String time) {
+ return expenditureList.get(index).getTime().equals(time);
+ }
+
+ /**
+ * Converts this ExpenditureList into a machine-readable format.
+ * @return The serialized ExpenditureList
+ */
+ public String serialize() {
+ StringBuilder sb = new StringBuilder();
+ addListToStringBuilder(SERIALIZATION_EXPENDITURE_START_MARKER,
+ SERIALIZATION_EXPENDITURE_END_MARKER,
+ expenditureListArray,
+ sb);
+ return sb.toString();
+ }
+
+ /**
+ * Reads a serialized ExpenditureList from the scanner.
+ * @param scanner A scanner
+ * @returns An ExpenditureList.
+ * @throws MindMyMoneyException if the format is invalid.
+ */
+ public static ExpenditureList deserializeFrom(Scanner scanner) throws MindMyMoneyException {
+ ExpenditureList savedExpenditureList = new ExpenditureList();
+ savedExpenditureList.expenditureListArray = convertInputToList(
+ SERIALIZATION_EXPENDITURE_START_MARKER,
+ SERIALIZATION_EXPENDITURE_END_MARKER,
+ scanner, Expenditure::deserialize);
+ return savedExpenditureList;
+ }
+}
diff --git a/src/main/java/seedu/mindmymoney/data/IncomeList.java b/src/main/java/seedu/mindmymoney/data/IncomeList.java
new file mode 100644
index 0000000000..4c4228f8ed
--- /dev/null
+++ b/src/main/java/seedu/mindmymoney/data/IncomeList.java
@@ -0,0 +1,124 @@
+package seedu.mindmymoney.data;
+
+import seedu.mindmymoney.MindMyMoneyException;
+import seedu.mindmymoney.userfinancial.Income;
+
+import java.util.ArrayList;
+import java.util.Scanner;
+
+import static seedu.mindmymoney.helper.SerializerFunctions.SERIALIZATION_INCOME_END_MARKER;
+import static seedu.mindmymoney.helper.SerializerFunctions.SERIALIZATION_INCOME_START_MARKER;
+import static seedu.mindmymoney.helper.SerializerFunctions.addListToStringBuilder;
+import static seedu.mindmymoney.helper.SerializerFunctions.convertInputToList;
+
+/**
+ * Container for income lists.
+ */
+public class IncomeList {
+ public ArrayList incomeListArray;
+
+ public IncomeList() {
+ this(new ArrayList<>());
+ }
+
+ public IncomeList(ArrayList incomeListArray) {
+ this.incomeListArray = incomeListArray;
+ }
+
+ /**
+ * Adds an Income entry to the list.
+ *
+ * @param income The Income entry to be added.
+ */
+ public void add(Income income) {
+ incomeListArray.add(income);
+ }
+
+ /**
+ * Checks if the list is empty.
+ *
+ * @return true if list is empty, false otherwise
+ */
+ public boolean isEmpty() {
+ return incomeListArray.isEmpty();
+ }
+
+ /**
+ * Retrieves the Income entry at the given index.
+ *
+ * @param index Index of the income entry.
+ * @return The Income object.
+ */
+ public Income get(int index) {
+ return incomeListArray.get(index);
+ }
+
+ /**
+ * Updates the Income entry at the given index.
+ *
+ * @param index Index of the entry to be updated.
+ * @param income The new Income entry.
+ */
+ public void set(int index, Income income) {
+ incomeListArray.set(index, income);
+ }
+
+ /**
+ * Deletes the Income entry from the list.
+ *
+ * @param index Index of the Income entry to delete.
+ */
+ public void delete(int index) {
+ incomeListArray.remove(index);
+ }
+
+ /**
+ * Checks if income amount is equal.
+ * @param incomeList List of income details.
+ * @param index Index of income detail in list.
+ * @param amount Amount to compare with.
+ * @return True if amount is equal, false otherwise.
+ */
+ public static boolean isEqualIncomeAmount(IncomeList incomeList, int index, int amount) {
+ return incomeList.get(index).getAmount() == amount;
+ }
+
+ /**
+ * Checks if income category is equal.
+ * @param incomeList List of income details.
+ * @param index Index of income detail in list.
+ * @param category Category to compare with.
+ * @return True if category is equal, false otherwise.
+ */
+ public static boolean isEqualIncomeCategory(IncomeList incomeList, int index, String category) {
+ return incomeList.get(index).getCategory().equals(category);
+ }
+
+ /**
+ * Converts this IncomeList into a machine-readable format.
+ * @return The serialized IncomeList
+ */
+ public String serialize() {
+ StringBuilder sb = new StringBuilder();
+ addListToStringBuilder(SERIALIZATION_INCOME_START_MARKER,
+ SERIALIZATION_INCOME_END_MARKER,
+ incomeListArray,
+ sb);
+ return sb.toString();
+ }
+
+ /**
+ * Reads a serialized IncomeList from the scanner.
+ * @param scanner A scanner
+ * @returns An IncomeList
+ * @throws MindMyMoneyException if the format is invalid.
+ */
+ public static IncomeList deserializeFrom(Scanner scanner) throws MindMyMoneyException {
+ IncomeList incomeList = new IncomeList();
+ incomeList.incomeListArray = convertInputToList(
+ SERIALIZATION_INCOME_START_MARKER,
+ SERIALIZATION_INCOME_END_MARKER,
+ scanner, Income::deserialize);
+ return incomeList;
+ }
+}
diff --git a/src/main/java/seedu/mindmymoney/data/PropertyList.java b/src/main/java/seedu/mindmymoney/data/PropertyList.java
new file mode 100644
index 0000000000..dcc96469b9
--- /dev/null
+++ b/src/main/java/seedu/mindmymoney/data/PropertyList.java
@@ -0,0 +1,177 @@
+package seedu.mindmymoney.data;
+
+import seedu.mindmymoney.MindMyMoneyException;
+
+import java.util.HashMap;
+
+/** Class for storing a list of key-value pairs. This list can be serialized
+ * to a string. */
+public class PropertyList {
+ private final HashMap properties;
+
+ public PropertyList() {
+ properties = new HashMap();
+ }
+
+ /**
+ * Converts the PropertyList into a String using a machine-readable format.
+ * @return The serialized PropertyList
+ */
+ public String serialize() {
+ StringBuilder sb = new StringBuilder();
+ for (String key : properties.keySet()) {
+ String serializedKeyValue = String.format(" %s: %s ",
+ quoteString(key), quoteString(properties.get(key)));
+ sb.append(serializedKeyValue);
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public String toString() {
+ return serialize();
+ }
+
+ /**
+ * Adds a key-value pair to the PropertyList.
+ * @param key The key
+ * @param value The value.
+ */
+ public void addProperty(String key, String value) {
+ properties.put(key, value);
+ }
+
+ /**
+ * Retrieves the value associated with the given property. If the property does not exist,
+ * throw a MindMyMoneyException whose message is the property.
+ * @param property The property whose value to retrieve.
+ * @return The value.
+ * @throws MindMyMoneyException if the property is not in the PropertyList.
+ */
+ public String getValue(String property) throws MindMyMoneyException {
+ String value = properties.get(property);
+ if (value == null) {
+ throw new MindMyMoneyException(property);
+ }
+ return value;
+ }
+
+ /**
+ * Replaces all double quotes and backslashes in a string with versions safe to use in
+ * a quoted string.
+ * @param string The string to process.
+ * @return The string, with quotes and backslashes escaped.
+ */
+ private static String escapeQuotesInString(String string) {
+ return string.replace("\\", "\\\\").replace("\"", "\\\"");
+ }
+
+ /**
+ * Quotes a string. This puts it between double quotes, while adding backslash characters to any
+ * double quotes and backslashes the string has.
+ * @param string The string to quote.
+ * @return A quoted string.
+ */
+ private static String quoteString(String string) {
+ return String.format("\"%s\"", escapeQuotesInString(string));
+ }
+
+ /**
+ * Converts a quoted string into its original form.
+ * @param string The string to unquote.
+ * @return An unquoted string.
+ * @throws MindMyMoneyException if the format is invalid.
+ */
+ private static String unquoteString(String string) throws MindMyMoneyException {
+ String unescaped = string.replace("\\\"", "\"")
+ .replace("\\\\", "\\");
+ if (unescaped.length() < 2 || unescaped.charAt(0) != '\"'
+ || unescaped.charAt(unescaped.length() - 1) != '\"') {
+ throw new MindMyMoneyException("Invalid unquoted string: " + string);
+ }
+ return unescaped.substring(1, unescaped.length() - 1);
+ }
+
+ /**
+ * Reads a quoted string from the given string, starting at the given position.
+ * @param string The string to read from.
+ * @param startPos The start position.
+ * @return A quoted string.
+ * @throws MindMyMoneyException if the format is invalid.
+ */
+ private static String readQuotedString(String string, int startPos) throws MindMyMoneyException {
+ if (string.charAt(startPos) != '\"') {
+ throw new MindMyMoneyException(string.substring(startPos) + " does not start with quoted string");
+ }
+ int endPos = startPos + 1;
+ while (endPos < string.length()) {
+ if (string.charAt(endPos) == '\\') {
+ endPos += 2;
+ continue;
+ }
+ endPos += 1;
+ if (string.charAt(endPos - 1) == '\"') {
+ break;
+ }
+ }
+ return string.substring(startPos, endPos);
+ }
+
+ /**
+ * Parses the output of serialize.
+ * @param string A serialized PropertyList.
+ * @return A PropertyList containing the same key-value pairs.
+ * @throws MindMyMoneyException if the format is invalid.
+ */
+ public static PropertyList deserialize(String string) throws MindMyMoneyException {
+ PropertyList plist = new PropertyList();
+ int propertyStart = 0;
+ while (propertyStart < string.length()) {
+ propertyStart = consumeWhitespace(string, propertyStart);
+ String quotedKey = readQuotedString(string, propertyStart);
+ propertyStart += quotedKey.length();
+
+ propertyStart = consumeWhitespace(string, propertyStart);
+ if (propertyStart == string.length() || string.charAt(propertyStart) != ':') {
+ throw new MindMyMoneyException("Expecting to start with colon, got "
+ + string.substring(propertyStart));
+ }
+ propertyStart++;
+
+ propertyStart = consumeWhitespace(string, propertyStart);
+ String quotedValue = readQuotedString(string, propertyStart);
+ propertyStart += quotedValue.length();
+
+ propertyStart = consumeWhitespace(string, propertyStart);
+
+ String rawKey = unquoteString(quotedKey);
+ String rawValue = unquoteString(quotedValue);
+ plist.addProperty(rawKey, rawValue);
+ }
+ return plist;
+ }
+
+ /**
+ * Moves an index into a string forward, until that index does not point to whitespace.
+ * @param string The string
+ * @param propertyStart The index
+ * @return The index, after having been moved forward.
+ */
+ private static int consumeWhitespace(String string, int propertyStart) {
+ while (isStartAtWhitespace(string, propertyStart)) {
+ propertyStart++;
+ }
+ return propertyStart;
+ }
+
+ /**
+ * Checks if an index into a string points to whitespace.
+ * @param string The string
+ * @param propertyStart The index
+ * @return Whether or not the index is in range of the string, and produces a valid index.
+ */
+
+ private static boolean isStartAtWhitespace(String string, int propertyStart) {
+ return propertyStart < string.length() && string.charAt(propertyStart) == ' ';
+ }
+}
diff --git a/src/main/java/seedu/mindmymoney/helper/AddCommandInputTests.java b/src/main/java/seedu/mindmymoney/helper/AddCommandInputTests.java
new file mode 100644
index 0000000000..45228cf08d
--- /dev/null
+++ b/src/main/java/seedu/mindmymoney/helper/AddCommandInputTests.java
@@ -0,0 +1,405 @@
+package seedu.mindmymoney.helper;
+
+import seedu.mindmymoney.MindMyMoneyException;
+import seedu.mindmymoney.constants.ExpenditureCategoryTypes;
+import seedu.mindmymoney.constants.IncomeCategoryTypes;
+import seedu.mindmymoney.data.CreditCardList;
+import seedu.mindmymoney.data.ExpenditureList;
+import seedu.mindmymoney.userfinancial.CreditCard;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+
+import static seedu.mindmymoney.constants.Indexes.MAX_CASHBACK_AMOUNT;
+import static seedu.mindmymoney.constants.Indexes.MAX_CREDIT_CARD_LIMIT;
+import static seedu.mindmymoney.constants.Indexes.MAX_EXPENDITURE_AMOUNT;
+import static seedu.mindmymoney.constants.Indexes.MAX_STUDENT_INCOME;
+import static seedu.mindmymoney.constants.Indexes.MIN_CASHBACK_AMOUNT;
+import static seedu.mindmymoney.constants.Indexes.MIN_EXPENDITURE_AMOUNT;
+import static seedu.mindmymoney.constants.Indexes.MIN_STUDENT_INCOME;
+import static seedu.mindmymoney.constants.PaymentMethod.CASH;
+import static seedu.mindmymoney.helper.TimeFunctions.checkAfterCurrentDate;
+import static seedu.mindmymoney.helper.TimeFunctions.checkValidDate;
+
+/**
+ * Input validation for Add Command.
+ */
+public class AddCommandInputTests {
+
+ /**
+ * Checks if input is cash or a name of credit card.
+ *
+ * @param input the item to be checked.
+ * @return true if item is in the list, false otherwise.
+ */
+ public static boolean isExpenditureInList(String input, CreditCardList creditCardList) {
+ if (input.equalsIgnoreCase(CASH.toString())) {
+ return true;
+ }
+ for (CreditCard str : creditCardList.creditCardListArray) {
+ if (str.getNameOfCard().equals(input)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Checks if input is in Expenditure Category Types.
+ *
+ * @param input the item to be checked.
+ * @return true if item is in the list, false otherwise.
+ */
+ public static boolean isExpenditureCategoryInList(String input) {
+ for (ExpenditureCategoryTypes str : ExpenditureCategoryTypes.values()) {
+ if (str.name().equalsIgnoreCase(input)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Checks if input is in Income Category Types.
+ *
+ * @param input the item to be checked.
+ * @return true if item is in the list, false otherwise.
+ */
+ public static boolean isIncomeCategoryInList(String input) {
+ for (IncomeCategoryTypes str : IncomeCategoryTypes.values()) {
+ if (str.name().equalsIgnoreCase(input)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Checks if user input of expenditure is null or not a type of expenditure.
+ *
+ * @param inputPaymentMethod User input of Expenditure.
+ * @throws MindMyMoneyException when input is null or not a type of expenditure.
+ */
+ public static void testPaymentMethod(String inputPaymentMethod, CreditCardList creditCardList)
+ throws MindMyMoneyException {
+ if (inputPaymentMethod.trim().equals("")) {
+ throw new MindMyMoneyException("Expenditure cannot be null!");
+ }
+
+ if (!isExpenditureInList(inputPaymentMethod, creditCardList)) {
+ throw new MindMyMoneyException("Input Cash or a Credit Card after the /pm field!");
+ }
+ }
+
+ /**
+ * Checks if user input of category is null or not a type of expenditure.
+ *
+ * @param inputCategory User input of Category.
+ * @throws MindMyMoneyException when input is null or not a type of category.
+ */
+ public static void testExpenditureCategory(String inputCategory) throws MindMyMoneyException {
+ if (inputCategory.trim().equals("")) {
+ throw new MindMyMoneyException("Category cannot be null!");
+ }
+ if (!isExpenditureCategoryInList(inputCategory)) {
+ throw new MindMyMoneyException("Input Food, Transport, Utilities, Personal, Entertainment or Others after"
+ + " the /c field!");
+ }
+ }
+
+ /**
+ * Checks if user input of category is null.
+ *
+ * @param inputDescription User input of Description.
+ * @throws MindMyMoneyException when input is null.
+ */
+ public static void testDescription(String inputDescription) throws MindMyMoneyException {
+ if (inputDescription.trim().equals("")) {
+ throw new MindMyMoneyException("Description cannot be null!");
+ }
+ }
+
+ /**
+ * Checks if the expenditure amount is above the credit card limit or balance.
+ *
+ * @param inputAmountAsFloat The expenditure amount.
+ * @param paymentMethod Either as cash or as a credit card.
+ * @param creditCardList User's current list of credit cards.
+ * @return true if expenditure amount is over the card limit or balance, false otherwise.
+ */
+ public static boolean isOverLimit(Float inputAmountAsFloat, String paymentMethod, CreditCardList creditCardList) {
+ if (paymentMethod.equalsIgnoreCase("cash")) {
+ return false;
+ }
+
+ CreditCard creditcard = creditCardList.get(paymentMethod);
+ float balanceLeft = creditcard.getBalanceLeft();
+
+ if (inputAmountAsFloat > balanceLeft) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Checks if user input of amount is a positive number more than 0.
+ *
+ * @param inputAmount User input of Amount.
+ * @param paymentMethod User's payment method.
+ * @param creditCardList User's current list of credit cards
+ * @throws MindMyMoneyException when input is less than or equal to 0 or null.
+ */
+ public static void testExpenditureAmount(String inputAmount, String paymentMethod,
+ CreditCardList creditCardList) throws MindMyMoneyException {
+ float inputAmountAsFloat;
+
+ if (inputAmount == null) {
+ throw new MindMyMoneyException("Amount cannot be empty!");
+ }
+
+ try {
+ inputAmountAsFloat = Float.parseFloat(inputAmount);
+ } catch (NumberFormatException e) {
+ throw new MindMyMoneyException("Amount must be a number");
+ }
+
+ if (isOverLimit(inputAmountAsFloat, paymentMethod, creditCardList)) {
+ throw new MindMyMoneyException("You have exceeded your credit card limit!");
+ }
+
+ if (inputAmountAsFloat <= MIN_EXPENDITURE_AMOUNT) {
+ throw new MindMyMoneyException("Amount must be more than 0");
+ }
+ if (inputAmountAsFloat > MAX_EXPENDITURE_AMOUNT) {
+ throw new MindMyMoneyException("Expenditure cannot be more than $1 million!");
+ }
+ assert inputAmountAsFloat > 0 : "Amount should have a positive value";
+ }
+
+ public static void testIncomeAmount(int inputAmount) throws MindMyMoneyException {
+ if (inputAmount < MIN_STUDENT_INCOME) {
+ throw new MindMyMoneyException("Amount cannot be negative!");
+ } else if (inputAmount > MAX_STUDENT_INCOME) {
+ throw new MindMyMoneyException("Amount too high!");
+ }
+ }
+
+ public static void testIncomeCategory(String inputCategory) throws MindMyMoneyException {
+ if (inputCategory.trim().equals("")) {
+ throw new MindMyMoneyException("Category cannot be null!");
+ }
+
+ if (!isIncomeCategoryInList(inputCategory)) {
+ throw new MindMyMoneyException("Input Salary, Allowance, Investment or Others!");
+ }
+ }
+
+ public static void testUpdateIncomeParameters(int inputAmount, String inputCategory) throws MindMyMoneyException {
+ testIncomeAmount(inputAmount);
+ testIncomeCategory(inputCategory);
+ }
+
+ /**
+ * Checks if user input of credit card name is valid.
+ * Credit Card name that is empty, as "Cash" and that already exist in the list are not accepted.
+ *
+ * @throws MindMyMoneyException when Credit Card name is cash or has a
+ */
+ public static void testCreditCardName(String inputCreditCardName, CreditCardList creditCardList)
+ throws MindMyMoneyException {
+ if (inputCreditCardName.equalsIgnoreCase("")) {
+ throw new MindMyMoneyException("Credit card name cannot be empty!");
+ }
+ assert inputCreditCardName != null : "Credit Card name should not be empty.";
+
+ if (inputCreditCardName.equalsIgnoreCase("cash")) {
+ throw new MindMyMoneyException("Credit card name cannot be abbreviated as `Cash`.");
+ }
+ for (CreditCard creditCard : creditCardList.creditCardListArray) {
+ if (creditCard.getNameOfCard().toLowerCase().equalsIgnoreCase(inputCreditCardName)) {
+ throw new MindMyMoneyException("You already have this card in the list! "
+ + "Please abbreviate the card as a different name.");
+ }
+ }
+
+ }
+
+ /**
+ * Checks if user input of cashback is a positive number more than 0.
+ * Cashback also cannot be more than 100%.
+ *
+ * @param inputCashback User input of Cash back.
+ * @throws MindMyMoneyException when input cashback is less than 0 or null.
+ */
+ public static void testCashbackAmount(String inputCashback) throws MindMyMoneyException {
+ double inputAmountAsDouble;
+ if (inputCashback == null) {
+ throw new MindMyMoneyException("Cashback cannot be empty!");
+ }
+
+ try {
+ inputAmountAsDouble = Double.parseDouble(inputCashback);
+ } catch (NumberFormatException e) {
+ throw new MindMyMoneyException("Cashback must be a number");
+ }
+
+ if (inputAmountAsDouble < MIN_CASHBACK_AMOUNT) {
+ throw new MindMyMoneyException("Cashback must be more than or equals to 0");
+ } else if (inputAmountAsDouble >= MAX_CASHBACK_AMOUNT) {
+ throw new MindMyMoneyException("Cashback cannot be 100% or more!");
+ }
+ assert inputAmountAsDouble >= MIN_CASHBACK_AMOUNT : "Cashback should have a non-negative value";
+ }
+
+ /**
+ * Checks if user input for credit card limit is valid.
+ *
+ * @param inputLimit User input of credit card limit.
+ * @throws MindMyMoneyException when input credit card limit is less than or equal to 0 or null.
+ */
+ public static void testCreditCardLimit(String inputLimit) throws MindMyMoneyException {
+ float inputAmountAsDouble;
+ if (inputLimit == null) {
+ throw new MindMyMoneyException("Limit amount cannot be empty!");
+ }
+
+ try {
+ inputAmountAsDouble = Float.parseFloat(inputLimit);
+ } catch (NumberFormatException e) {
+ throw new MindMyMoneyException("Limit amount must be a number");
+ }
+
+ if (inputAmountAsDouble <= 0) {
+ throw new MindMyMoneyException("Limit amount must be more than 0");
+ }
+ assert inputAmountAsDouble > 0 : "Limit amount should have a positive value";
+
+ if (inputAmountAsDouble > MAX_CREDIT_CARD_LIMIT) {
+ throw new MindMyMoneyException("Limit amount must be $40,000 or less.\n"
+ + "If you do have a credit card with more than $40,000 limit, "
+ + "do inform the MindMyMoney team through GitHub.");
+ }
+
+ }
+
+ /**
+ * Tests if the input parameters of expenditure from the user are valid.
+ *
+ * @param paymentMethod The payment method used, either as cash or the credit card.
+ * @param inputCategory The category as indicated by the user.
+ * @param description The description of the expenditure.
+ * @param amountAsString Price of the expenditure.
+ * @param inputTime Date of when the expenditure was bought.
+ * @throws MindMyMoneyException when the parameters are invalid.
+ */
+ public static void testExpenditureParameters(String paymentMethod, String inputCategory, String description,
+ String amountAsString, String inputTime,
+ CreditCardList creditCardList) throws MindMyMoneyException {
+ testPaymentMethod(paymentMethod, creditCardList);
+ testExpenditureCategory(inputCategory);
+ testDescription(description);
+ testExpenditureAmount(amountAsString, paymentMethod, creditCardList);
+ checkValidDate(inputTime);
+ LocalDate date = LocalDate.parse(inputTime, DateTimeFormatter.ofPattern("dd/MM/yyyy"));
+ checkAfterCurrentDate(date);
+ }
+
+ /**
+ * Tests if the input parameters of update expenditure from the user are valid.
+ *
+ * @param newPaymentMethod The payment method used, either as cash or the credit card.
+ * @param inputCategory The category as indicated by the user.
+ * @param description The description of the expenditure.
+ * @param amountAsString Price of the expenditure.
+ * @param inputTime Date of when the expenditure was bought.
+ * @throws MindMyMoneyException when the parameters are invalid.
+ */
+ public static void testUpdateExpenditureParameters(int indexToUpdate, String newPaymentMethod, String inputCategory,
+ String description, String amountAsString, String inputTime,
+ CreditCardList creditCardList, ExpenditureList expenditureList)
+ throws MindMyMoneyException {
+ testPaymentMethod(newPaymentMethod, creditCardList);
+ testExpenditureCategory(inputCategory);
+ testDescription(description);
+ checkValidDate(inputTime);
+ LocalDate date = LocalDate.parse(inputTime, DateTimeFormatter.ofPattern("dd/MM/yyyy"));
+ checkAfterCurrentDate(date);
+
+ //Test updated expenditure amount
+ String oldPaymentMethod = getOldPaymentMethod(indexToUpdate, expenditureList);
+ if (isSamePaymentMethod(oldPaymentMethod, newPaymentMethod)
+ && !newPaymentMethod.equalsIgnoreCase("cash")) {
+ testSameCreditCardExpenditure(indexToUpdate, amountAsString, expenditureList, creditCardList,
+ newPaymentMethod);
+ } else {
+ testExpenditureAmount(amountAsString, newPaymentMethod, creditCardList);
+ }
+ }
+
+ private static String getOldPaymentMethod(int indexToUpdate, ExpenditureList expenditureList) {
+ return expenditureList.get(indexToUpdate).getPaymentMethod();
+ }
+
+ private static boolean isSamePaymentMethod(String oldPaymentMethod, String newPaymentMethod) {
+ return oldPaymentMethod.equalsIgnoreCase(newPaymentMethod);
+ }
+
+ private static void testSameCreditCardExpenditure(int indexToUpdate, String inputAmount,
+ ExpenditureList expenditureList, CreditCardList creditCardList,
+ String paymentMethod)
+ throws MindMyMoneyException {
+
+ float inputAmountAsFloat;
+ if (inputAmount == null) {
+ throw new MindMyMoneyException("Amount cannot be empty!");
+ }
+ try {
+ inputAmountAsFloat = Float.parseFloat(inputAmount);
+ } catch (NumberFormatException e) {
+ throw new MindMyMoneyException("Amount must be a number");
+ }
+
+ CreditCard creditCard = creditCardList.get(paymentMethod);
+ float oldExpenditureAmount = expenditureList.get(indexToUpdate).getAmount();
+ float newTotalExpenditure = creditCard.getTotalExpenditure() - oldExpenditureAmount
+ + inputAmountAsFloat;
+ boolean isOverLimit = creditCard.getMonthlyCardLimit() < newTotalExpenditure;
+
+ if (isOverLimit) {
+ throw new MindMyMoneyException("You have exceeded your credit card limit!");
+ }
+
+ if (inputAmountAsFloat <= MIN_EXPENDITURE_AMOUNT) {
+ throw new MindMyMoneyException("Amount must be more than 0");
+ }
+ assert inputAmountAsFloat > 0 : "Amount should have a positive value";
+ }
+
+ /**
+ * Tests if the input parameters of income from the user are valid.
+ *
+ * @param amountAsInt Integer amount of the income.
+ * @param inputCategory Source of income.
+ * @throws MindMyMoneyException when the parameters are invalid.
+ */
+ public static void testIncomeParameters(int amountAsInt, String inputCategory) throws MindMyMoneyException {
+ testIncomeAmount(amountAsInt);
+ testIncomeCategory(inputCategory);
+ }
+
+ /**
+ * Tests if the input parameters of credit card from the user are valid.
+ *
+ * @param cardName The name of the credit card.
+ * @param cashBack The amount of cashback the card provides.
+ * @param cardLimit The spending limit of the credit card.
+ * @throws MindMyMoneyException when the parameters are invalid.
+ */
+ public static void testCreditCardParameters(String cardName, String cashBack, String cardLimit,
+ CreditCardList creditCardList) throws MindMyMoneyException {
+ testCreditCardName(cardName, creditCardList);
+ testCashbackAmount(cashBack);
+ testCreditCardLimit(cardLimit);
+ }
+
+}
diff --git a/src/main/java/seedu/mindmymoney/helper/Calculations.java b/src/main/java/seedu/mindmymoney/helper/Calculations.java
new file mode 100644
index 0000000000..26676985f0
--- /dev/null
+++ b/src/main/java/seedu/mindmymoney/helper/Calculations.java
@@ -0,0 +1,123 @@
+package seedu.mindmymoney.helper;
+
+import seedu.mindmymoney.MindMyMoneyException;
+import seedu.mindmymoney.constants.ExpenditureCategoryTypes;
+import seedu.mindmymoney.constants.PrintStrings;
+import seedu.mindmymoney.data.ExpenditureList;
+import seedu.mindmymoney.userfinancial.Expenditure;
+
+import java.util.ArrayList;
+
+import static seedu.mindmymoney.constants.ExpenditureCategoryTypes.FOOD;
+import static seedu.mindmymoney.constants.ExpenditureCategoryTypes.UTILITIES;
+import static seedu.mindmymoney.constants.ExpenditureCategoryTypes.TRANSPORT;
+import static seedu.mindmymoney.constants.ExpenditureCategoryTypes.PERSONAL;
+import static seedu.mindmymoney.constants.ExpenditureCategoryTypes.ENTERTAINMENT;
+import static seedu.mindmymoney.constants.ExpenditureCategoryTypes.OTHERS;
+import static seedu.mindmymoney.constants.ExpenditureFields.TIME;
+import static seedu.mindmymoney.helper.GeneralFunctions.findItemsInList;
+import static seedu.mindmymoney.helper.GeneralFunctions.formatFloat;
+import static seedu.mindmymoney.helper.GeneralFunctions.findMatchingCategoryInArraylist;
+import static seedu.mindmymoney.helper.TimeFunctions.isValidInputCalculateCommand;
+
+/**
+ * Container for functions that help do calculations.
+ */
+public class Calculations {
+ public static final double INTERVAL_OF_INCREMENT = 5;
+
+ /**
+ * Calculates the total expenditure in a given month.
+ *
+ * @param input The month to calculate expenditure for.
+ * @param expenditureList The list containing all expenditures to search for.
+ * @throws MindMyMoneyException When findItemsInList throws MindMyMoneyException.
+ */
+ public static void calculateExpenditure(String input, ExpenditureList expenditureList)
+ throws MindMyMoneyException {
+ if (!isValidInputCalculateCommand(input)) {
+ throw new MindMyMoneyException("Date has to be in \"dd/mm/yyyy\", \"mm/yyyy\" or \"yyyy\" format!");
+ }
+ ArrayList foundItems = findItemsInList(input, TIME.toString(), expenditureList);
+ float sumOfExpenditure = 0;
+ for (Expenditure item : foundItems) {
+ sumOfExpenditure += item.getAmount();
+ }
+ sumOfExpenditure = formatFloat(sumOfExpenditure);
+ if (sumOfExpenditure == 0.0) {
+ throw new MindMyMoneyException("Date not found in the list! Do check your input");
+ }
+ System.out.println("Total expenditure in " + input + " is $" + String.format("%.2f", sumOfExpenditure) + ".");
+ displayExpenditureBreakdown(foundItems, sumOfExpenditure);
+ }
+
+ /**
+ * Displays the expenditure breakdown for the given month.
+ *
+ * @param foundItems The list containing all the expenses in the month.
+ * @param sumOfExpenditure Total sum of expenses in the month.
+ */
+ public static void displayExpenditureBreakdown(ArrayList foundItems, float sumOfExpenditure) {
+ float foodPercentage = calculatePercentage(FOOD, foundItems, sumOfExpenditure);
+ float transportPercentage = calculatePercentage(TRANSPORT, foundItems, sumOfExpenditure);
+ float utilitiesPercentage = calculatePercentage(UTILITIES, foundItems, sumOfExpenditure);
+ float personalPercentage = calculatePercentage(PERSONAL, foundItems, sumOfExpenditure);
+ float entertainmentPercentage = calculatePercentage(ENTERTAINMENT, foundItems, sumOfExpenditure);
+ float othersPercentage = calculatePercentage(OTHERS, foundItems, sumOfExpenditure);
+ System.out.println(System.lineSeparator() + "BREAKDOWN OF EXPENSES:");
+ System.out.print(PrintStrings.LINE);
+ System.out.println("FOOD: " + printOutput(foodPercentage));
+ System.out.println("TRANSPORT: " + printOutput(transportPercentage));
+ System.out.println("UTILITIES: " + printOutput(utilitiesPercentage));
+ System.out.println("PERSONAL: " + printOutput(personalPercentage));
+ System.out.println("ENTERTAINMENT: " + printOutput(entertainmentPercentage));
+ System.out.println("OTHERS: " + printOutput(othersPercentage));
+ System.out.println(PrintStrings.LINE);
+ }
+
+ /**
+ * Prints the bar chart.
+ *
+ * @param percentage Percentage of each category type that is part of the expenses.
+ * @return The remaining part of string to be printed.
+ */
+ public static String printOutput(float percentage) {
+ return printBar(percentage) + " [" + percentage + "%]";
+ }
+
+ /**
+ * Fills the chart for each category type.
+ *
+ * @param percentage Percentage of each category type that is part of the expenses.
+ * @return The 'bar' chart to be output for the category type.
+ */
+ public static String printBar(float percentage) {
+ String output = "";
+ for (float i = 0; i < percentage; i += INTERVAL_OF_INCREMENT) {
+ output += "$$";
+ }
+ return output;
+ }
+
+ /**
+ * Calculates the percentage of expenses for a particular category type.
+ *
+ * @param categoryType Category type to calculate for.
+ * @param foundItems The list containing all the expenses in the month.
+ * @param sumOfExpenditure Total sum of expenses in the month.
+ * @return Percentage of expenses for that particular category type.
+ */
+ public static float calculatePercentage(ExpenditureCategoryTypes categoryType, ArrayList foundItems,
+ float sumOfExpenditure) {
+ ArrayList foundCategoryTypeItems = new ArrayList<>();
+ foundCategoryTypeItems = findMatchingCategoryInArraylist(categoryType, foundItems, foundCategoryTypeItems);
+ float sumOfCategoryType = 0;
+ for (Expenditure item : foundCategoryTypeItems) {
+ sumOfCategoryType += item.getAmount();
+ }
+ sumOfCategoryType = formatFloat(sumOfCategoryType);
+ float percentage = (sumOfCategoryType / sumOfExpenditure) * 100;
+ percentage = formatFloat(percentage);
+ return percentage;
+ }
+}
diff --git a/src/main/java/seedu/mindmymoney/helper/GeneralFunctions.java b/src/main/java/seedu/mindmymoney/helper/GeneralFunctions.java
new file mode 100644
index 0000000000..f57886d59b
--- /dev/null
+++ b/src/main/java/seedu/mindmymoney/helper/GeneralFunctions.java
@@ -0,0 +1,258 @@
+package seedu.mindmymoney.helper;
+
+import seedu.mindmymoney.MindMyMoneyException;
+import seedu.mindmymoney.constants.ExpenditureCategoryTypes;
+import seedu.mindmymoney.constants.ExpenditureFields;
+import seedu.mindmymoney.constants.Indexes;
+import seedu.mindmymoney.data.ExpenditureList;
+import seedu.mindmymoney.userfinancial.Expenditure;
+
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+
+import static seedu.mindmymoney.constants.Indexes.INDEX_OF_FIRST_ITEM;
+import static seedu.mindmymoney.constants.Indexes.INDEX_OF_SECOND_ITEM;
+
+/**
+ * Container for general functions used throughout the program.
+ */
+public class GeneralFunctions {
+ private static final DecimalFormat df = new DecimalFormat("0.00");
+
+ /**
+ * Separates the user input into the command and description for easy reference.
+ *
+ * @return String array of user input.
+ */
+ public static String[] parseInput(String inputCommand) {
+ String[] inputAsArray = inputCommand.split(" ", Indexes.SPLIT_LIMIT);
+ return inputAsArray;
+ }
+
+ /**
+ * Checks if user's input contains the correct flag formats.
+ *
+ * @param input user's input.
+ * @param startingFlag the flag before the interested parameter.
+ * @param endingFlag the flag after the interested parameter.
+ * @return true if the input contains the correct flag formats, false otherwise.
+ */
+ public static boolean hasCorrectFlagFormat(String input, String startingFlag, String endingFlag) {
+ if (input.contains(startingFlag + " ") && input.contains(" " + endingFlag)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Extracts out the interested parameter from the user's input based on the given flags.
+ * For example, if user's input was: "add /i /a 3000 /c salary", this method extracts out "3000" or "salary"
+ * based on the flags given.
+ *
+ * @param input user's input.
+ * @param startingFlag the flag before the interested parameter.
+ * @param endingFlag the flag after the interested parameter.
+ * @return the interested parameter.
+ * @throws MindMyMoneyException when an invalid command is received, along with the corresponding error message.
+ */
+ public static String parseInputWithCommandFlag(String input, String startingFlag, String endingFlag)
+ throws MindMyMoneyException {
+ try {
+ if (!hasCorrectFlagFormat(input, startingFlag, endingFlag)) {
+ throw new MindMyMoneyException("You are missing a flag or lack the spacing between the flags!\n"
+ + "For eg. \"add /e /pm cash /c Food /d Porridge /a 4.50 /t 30/03/2022\"");
+ }
+
+ startingFlag = startingFlag + " ";
+ input = input.substring(input.indexOf(startingFlag) + startingFlag.length());
+ if (!endingFlag.equals("")) {
+ endingFlag = " " + endingFlag;
+ input = input.substring(0, input.indexOf(endingFlag));
+ }
+ } catch (StringIndexOutOfBoundsException e) {
+ throw new MindMyMoneyException("You are missing one or more of the parameters! Please check your command "
+ + "again.\n");
+ }
+ return input;
+ }
+
+ /**
+ * Finds an item in a given list provided the search term and the field to search in.
+ *
+ * @param searchTerm The matching term to search the list for.
+ * @param fieldToSearch The object in the list to search for.
+ * @param itemList The list to search in.
+ * @return An ArrayList containing the found items.
+ * @throws MindMyMoneyException if fieldToSearch is not in the list, amount is not a number
+ * and if the list of found items is empty.
+ */
+ public static ArrayList findItemsInList(String searchTerm, String fieldToSearch,
+ ExpenditureList itemList) throws MindMyMoneyException {
+ ArrayList foundItems = new ArrayList<>();
+ try {
+ ExpenditureFields fieldToSearchAsEnumType = ExpenditureFields.valueOf(fieldToSearch);
+ switch (fieldToSearchAsEnumType) {
+ case EXPENDITURE:
+ findMatchingExpenditure(searchTerm, foundItems, itemList);
+ break;
+ case CATEGORY:
+ findMatchingCategory(searchTerm, foundItems, itemList);
+ break;
+ case DESCRIPTION:
+ findMatchingDescription(searchTerm, foundItems, itemList);
+ break;
+ case AMOUNT:
+ findMatchingAmount(searchTerm, foundItems, itemList);
+ break;
+ case TIME:
+ findMatchingTime(searchTerm, foundItems, itemList);
+ break;
+ default:
+ throw new MindMyMoneyException("Search term that have yet to be implemented! "
+ + "Look out for our future updates");
+ }
+ if (foundItems.size() == 0) {
+ throw new MindMyMoneyException("The item \"" + searchTerm + "\" was not found in the list, sorry!");
+ } else {
+ return foundItems;
+ }
+ } catch (IllegalArgumentException e) {
+ throw new MindMyMoneyException("Input a valid search term!");
+ }
+ }
+
+ /**
+ * Searches for matching items in Expenditure field of itemList and returns a list of found items.
+ *
+ * @param searchTerm String to search for.
+ * @param foundItems List to store items found.
+ * @param itemList List to search in.
+ * @return foundItems, updated with new items found.
+ */
+ public static ArrayList findMatchingExpenditure(String searchTerm, ArrayList foundItems,
+ ExpenditureList itemList) {
+ for (Expenditure item : itemList.expenditureListArray) {
+ if (item.getPaymentMethod().contains(searchTerm)) {
+ foundItems.add(item);
+ }
+ }
+ return foundItems;
+ }
+
+ /**
+ * Searches for matching items in Category field of itemList and returns a list of found items.
+ *
+ * @param searchTerm String to search for.
+ * @param foundItems List to store items found.
+ * @param itemList List to search in.
+ * @return foundItems, updated with new items found.
+ */
+ public static ArrayList findMatchingCategory(String searchTerm, ArrayList foundItems,
+ ExpenditureList itemList) {
+ for (Expenditure item : itemList.expenditureListArray) {
+ if (item.getCategory().contains(searchTerm)) {
+ foundItems.add(item);
+ }
+ }
+ return foundItems;
+ }
+
+ /**
+ * Searches for matching items in Category field from an arraylist and returns a list of found items.
+ *
+ * @param categoryType Category type to search for
+ * @param foundItems List of expense items.
+ * @param foundCategoryTypeList List to store items found.
+ * @return The list that stores the expense items found.
+ */
+ public static ArrayList findMatchingCategoryInArraylist(ExpenditureCategoryTypes categoryType,
+ ArrayList foundItems,
+ ArrayList foundCategoryTypeList) {
+ for (Expenditure item : foundItems) {
+ if (item.getCategory().contains(capitalise(categoryType.toString()))) {
+ foundCategoryTypeList.add(item);
+ }
+ }
+ return foundCategoryTypeList;
+ }
+
+ /**
+ * Searches for matching items in Description field of itemList and returns a list of found items.
+ *
+ * @param searchTerm String to search for.
+ * @param foundItems List to store items found.
+ * @param itemList List to search in.
+ * @return foundItems, updated with new items found.
+ */
+ public static ArrayList findMatchingDescription(String searchTerm, ArrayList foundItems,
+ ExpenditureList itemList) {
+ for (Expenditure item : itemList.expenditureListArray) {
+ if (item.getDescription().contains(searchTerm)) {
+ foundItems.add(item);
+ }
+ }
+ return foundItems;
+ }
+
+ /**
+ * Searches for matching items in Amount field of itemList and returns a list of found items.
+ *
+ * @param searchTerm String to search for.
+ * @param foundItems List to store items found.
+ * @param itemList List to search in.
+ * @return foundItems, updated with new items found.
+ */
+ public static ArrayList findMatchingAmount(String searchTerm, ArrayList foundItems,
+ ExpenditureList itemList) throws MindMyMoneyException {
+ try {
+ for (Expenditure item : itemList.expenditureListArray) {
+ if (item.getAmount() == Float.parseFloat(searchTerm)) {
+ foundItems.add(item);
+ }
+ }
+ } catch (NumberFormatException e) {
+ throw new MindMyMoneyException("AMOUNT must be a number");
+ }
+ return foundItems;
+ }
+
+ /**
+ * Searches for matching items in Time field of itemList and returns a list of found items.
+ *
+ * @param searchTerm String to search for.
+ * @param foundItems List to store items found.
+ * @param itemList List to search in.
+ * @return foundItems, updated with new items found.
+ */
+ public static ArrayList findMatchingTime(String searchTerm, ArrayList foundItems,
+ ExpenditureList itemList) {
+ for (Expenditure item : itemList.expenditureListArray) {
+ if (item.getTime().contains(searchTerm)) {
+ foundItems.add(item);
+ }
+ }
+ return foundItems;
+ }
+
+ /**
+ * Sets the string to lower case and then capitalise the first character in string.
+ *
+ * @param str String to be capitalised.
+ * @return Capitalised string.
+ */
+ public static String capitalise(String str) {
+ str = str.toLowerCase();
+ return str.substring(INDEX_OF_FIRST_ITEM, INDEX_OF_SECOND_ITEM).toUpperCase()
+ + str.substring(INDEX_OF_SECOND_ITEM);
+ }
+
+ /**
+ * Round off float to 2dp.
+ *
+ * @param number float to be rounded off.
+ * @return float rounded off to 2dp.
+ */
+ public static float formatFloat(Float number) {
+ return Float.parseFloat(df.format(number));
+ }
+}
diff --git a/src/main/java/seedu/mindmymoney/helper/SerializerFunctions.java b/src/main/java/seedu/mindmymoney/helper/SerializerFunctions.java
new file mode 100644
index 0000000000..b023006ad7
--- /dev/null
+++ b/src/main/java/seedu/mindmymoney/helper/SerializerFunctions.java
@@ -0,0 +1,77 @@
+package seedu.mindmymoney.helper;
+
+import seedu.mindmymoney.MindMyMoneyException;
+import seedu.mindmymoney.userfinancial.MindMyMoneySerializable;
+
+import java.util.ArrayList;
+import java.util.Scanner;
+
+public class SerializerFunctions {
+
+ public static final String SERIALIZATION_EXPENDITURE_START_MARKER = "# BEGIN EXPENDITURES";
+ public static final String SERIALIZATION_CREDIT_CARD_START_MARKER = "# BEGIN CREDIT CARDS";
+ public static final String SERIALIZATION_INCOME_START_MARKER = "# BEGIN INCOME SOURCES";
+ public static final String SERIALIZATION_EXPENDITURE_END_MARKER = "# END EXPENDITURES";
+ public static final String SERIALIZATION_CREDIT_CARD_END_MARKER = "# END CREDIT CARDS";
+ public static final String SERIALIZATION_INCOME_END_MARKER = "# END INCOME SOURCES";
+
+ public interface DeserializerFunction {
+ T apply(String s) throws MindMyMoneyException;
+ }
+
+ /**
+ * Adds an ArrayList of objects that implement MMMSerializable line-by-line into a StringBuilder.
+ * The list will have a start marker and an end marker placed before and after the list, respectively,
+ * to help with deserializing this list.
+ * @param startMarker A String marking the start of the list.
+ * @param endMarker A String marking the end of the list.
+ * @param list The list to add.
+ * @param stringBuilder A StringBuilder to add to.
+ * @param A MMMSerializable type to add. <\T>
+ */
+ public static
+ void addListToStringBuilder(String startMarker, String endMarker,
+ ArrayList list, StringBuilder stringBuilder) {
+ stringBuilder.append(startMarker).append("\n");
+ for (T serializable : list) {
+ stringBuilder.append(serializable.serialize()).append("\n");
+ }
+ stringBuilder.append(endMarker).append("\n");
+ }
+
+ /**
+ * Reads a list of MMMSerializables from a Scanner. The list should start with startMarker, and end
+ * with endMarker. Each line in between will be passed to a deserializer function that converts
+ * the line into an MMMSerializable.
+ * @param startMarker A String marking the start of the list.
+ * @param endMarker A String marking the end of the list.
+ * @param scanner The Scanner to read from.
+ * @param deserializer A function that accepts a string and deserializes it.
+ * @param A MMMSerializable type to convert to. <\T>
+ * @return An ArrayList of list elements.
+ */
+ public static
+ ArrayList convertInputToList(String startMarker, String endMarker,
+ Scanner scanner,
+ DeserializerFunction deserializer) throws MindMyMoneyException {
+ ArrayList list = new ArrayList();
+ if (!scanner.hasNextLine()) {
+ return list;
+ }
+ String nextLine = scanner.nextLine();
+ if (!nextLine.equals(startMarker)) {
+ throw new MindMyMoneyException("Expected " + startMarker + ", got " + nextLine);
+ }
+ while (true) {
+ if (!scanner.hasNextLine()) {
+ throw new MindMyMoneyException("Expected " + endMarker + " , got EOF");
+ }
+ nextLine = scanner.nextLine();
+ if (nextLine.equals(endMarker)) {
+ break;
+ }
+ list.add(deserializer.apply(nextLine));
+ }
+ return list;
+ }
+}
diff --git a/src/main/java/seedu/mindmymoney/helper/TimeFunctions.java b/src/main/java/seedu/mindmymoney/helper/TimeFunctions.java
new file mode 100644
index 0000000000..1dde99d57a
--- /dev/null
+++ b/src/main/java/seedu/mindmymoney/helper/TimeFunctions.java
@@ -0,0 +1,87 @@
+package seedu.mindmymoney.helper;
+
+import seedu.mindmymoney.MindMyMoneyException;
+
+import java.time.LocalDate;
+
+import static seedu.mindmymoney.constants.Indexes.INDEX_OF_FIRST_ITEM;
+import static seedu.mindmymoney.constants.Indexes.INDEX_OF_SECOND_ITEM;
+import static seedu.mindmymoney.constants.Indexes.INDEX_OF_THIRD_ITEM;
+import static seedu.mindmymoney.constants.ValidationRegexTypes.VALIDATION_REGEX_D;
+import static seedu.mindmymoney.constants.ValidationRegexTypes.VALIDATION_REGEX_M;
+import static seedu.mindmymoney.constants.ValidationRegexTypes.VALIDATION_REGEX_Y;
+
+/**
+ * Container for functions needed to calculate and format time.
+ */
+public class TimeFunctions {
+ private static final int LEAP_YEAR_NUMBER = 4;
+
+ /**
+ * Checks if date input format is valid.
+ *
+ * @param input The string of the date input.
+ * @return true if format is valid, false otherwise.
+ */
+ public static boolean isValidInputCalculateCommand(String input) {
+ if (input.matches(VALIDATION_REGEX_D)
+ || input.matches(VALIDATION_REGEX_M)
+ || input.matches(VALIDATION_REGEX_Y)) {
+ return true;
+ }
+ return false;
+
+ }
+
+ /**
+ * Checks if date input format is valid.
+ *
+ * @param input The string of the date input.
+ * @return true if format is valid, false otherwise.
+ */
+ public static boolean isValidInputAddCommand(String input) {
+ if (input.matches(VALIDATION_REGEX_D)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Checks is parsed date is a valid date in the calendar.
+ *
+ * @param inputTime date that is parsed in.
+ * @throws MindMyMoneyException throws an exception when the date parsed is in not in the calendar.
+ */
+ public static void checkValidDate(String inputTime) throws MindMyMoneyException {
+ if (!isValidInputAddCommand(inputTime)) {
+ throw new MindMyMoneyException("Date has to be valid and in this format \"dd/mm/yyyy\"");
+ }
+ String[] date = inputTime.split("/");
+ String day = date[INDEX_OF_FIRST_ITEM];
+ int dayInInt = Integer.parseInt(day);
+ String month = date[INDEX_OF_SECOND_ITEM];
+ String year = date[INDEX_OF_THIRD_ITEM];
+ int yearInInt = Integer.parseInt(year);
+ if (!(yearInInt % LEAP_YEAR_NUMBER == 0) && month.equals("02") && (dayInInt > 28)) {
+ throw new MindMyMoneyException(day + "/" + month + " is not a valid dd/mm in a non leap year!");
+ } else if ((yearInInt % LEAP_YEAR_NUMBER == 0) && month.equals("02") && (dayInInt > 29)) {
+ throw new MindMyMoneyException(day + "/" + month + " is not a valid dd/mm in a leap year!");
+ } else if ((month.equals("04") || month.equals("06") || month.equals("09") || month.equals("11"))
+ && dayInInt > 30) {
+ throw new MindMyMoneyException(day + "/" + month + " is not a valid dd/mm in this month!");
+ }
+ }
+
+ /**
+ * Checks if parsed date is after the current date.
+ *
+ * @param date date that is parsed in.
+ * @throws MindMyMoneyException throws an exception when the date parsed is after current date.
+ */
+ public static void checkAfterCurrentDate(LocalDate date) throws MindMyMoneyException {
+ LocalDate currentDate = LocalDate.now();
+ if (date.isAfter(currentDate)) {
+ throw new MindMyMoneyException("Please enter a valid date that is before today or today's date itself.");
+ }
+ }
+}
diff --git a/src/main/java/seedu/mindmymoney/helper/ValidatorFunctions.java b/src/main/java/seedu/mindmymoney/helper/ValidatorFunctions.java
new file mode 100644
index 0000000000..c39e73618f
--- /dev/null
+++ b/src/main/java/seedu/mindmymoney/helper/ValidatorFunctions.java
@@ -0,0 +1,135 @@
+package seedu.mindmymoney.helper;
+
+import seedu.mindmymoney.MindMyMoneyException;
+import seedu.mindmymoney.data.CreditCardList;
+import seedu.mindmymoney.data.ExpenditureList;
+import seedu.mindmymoney.userfinancial.CreditCard;
+import seedu.mindmymoney.userfinancial.Expenditure;
+import seedu.mindmymoney.userfinancial.ValidationException;
+
+import java.util.HashSet;
+
+/** Class for helper functions in validating the save file. */
+public class ValidatorFunctions {
+
+ /**
+ * Checks if value is above lowerBound, or if inclusive is true, if it is equal. Throws
+ * a ValidationException if this condition is not met.
+ * @param value The value to check.
+ * @param lowerBound The lower bound to validate against.
+ * @param inclusive Whether or not to accept value if it is equal to lowerBound.
+ * @param label A label to use in the error message.
+ * @throws ValidationException if the value does not satisfy the lower bound.
+ */
+ public static void validateLowerBound(int value, int lowerBound, boolean inclusive, String label)
+ throws ValidationException {
+ if (value > lowerBound) {
+ return;
+ }
+ if (value == lowerBound && inclusive) {
+ return;
+ }
+ String comparatorInMessage = (inclusive ? ">=" : ">");
+ throw new ValidationException("Values for " + label + " should be " + comparatorInMessage
+ + Integer.toString(lowerBound));
+ }
+
+ /**
+ * Checks if value is between lower and upper, inclusive. Throws a ValidationException
+ * if this condition is not met.
+ * @param value The value to check.
+ * @param lower The lower bound.
+ * @param upper The upper bound.
+ * @param label A label to use in the error message.
+ * @throws ValidationException if the value is not in range.
+ */
+ public static void validateInRange(double value, double lower, double upper, String label)
+ throws ValidationException {
+ if (lower <= value && value <= upper) {
+ return;
+ }
+ throw new ValidationException("Values for " + label + " should be between "
+ + Double.toString(lower) + " and " + Double.toString(upper));
+ }
+
+ /**
+ * Checks if category is a valid category for Incomes. Throws a ValidationException
+ * if this condition is not met.
+ * @param category The category to check.
+ * @throws ValidationException if the category is invalid.
+ */
+ public static void validateIncomeCategory(String category) throws ValidationException {
+ if (AddCommandInputTests.isIncomeCategoryInList(category)) {
+ return;
+ }
+ throw new ValidationException(category + " is an invalid income category");
+ }
+
+ /**
+ * Checks if category is a valid category for Expenditures. Throws a ValidationException
+ * if this condition is not met.
+ * @param category The category to check.
+ * @throws ValidationException if the category is invalid.
+ */
+ public static void validateExpenditureCategory(String category) throws ValidationException {
+ if (AddCommandInputTests.isExpenditureCategoryInList(category)) {
+ return;
+ }
+ throw new ValidationException(category + " is an invalid expenditure category");
+ }
+
+ /**
+ * Check if time is a valid date for Expenditures. Throws a ValidationException if
+ * this condition is not met.
+ * @param time The time to check.
+ * @throws ValidationException if the date is invalid.
+ */
+ public static void validateDate(String time) throws ValidationException {
+ try {
+ TimeFunctions.checkValidDate(time);
+ } catch (MindMyMoneyException e) {
+ throw new ValidationException(time + " is an invalid date:" + e.getMessage());
+ }
+ }
+
+ /**
+ * Checks if all payment methods in an ExpenditureList is either Cash, or can be found
+ * in a CreditCardList. Throws a ValidationException if this condition is not met.
+ * @param expenditures The ExpenditureList to validate.
+ * @param creditCards The CreditCardList to validate against.
+ * @throws ValidationException if a payment method is invalid.
+ */
+ public static void validatePaymentMethods(ExpenditureList expenditures,
+ CreditCardList creditCards) throws ValidationException {
+ HashSet creditCardNames = new HashSet<>();
+ for (CreditCard creditCard : creditCards.creditCardListArray) {
+ creditCardNames.add(creditCard.getNameOfCard());
+ }
+ for (Expenditure expenditure : expenditures.expenditureListArray) {
+ if (expenditure.getPaymentMethod().equals("Cash")) {
+ continue;
+ }
+ if (!creditCardNames.contains(expenditure.getPaymentMethod())) {
+ throw new ValidationException(expenditure.getPaymentMethod()
+ + " does not appear as a credit card");
+ }
+ }
+ }
+
+ /**
+ * Checks if no two credit cards in a CreditCardList have the same name. Throws a ValidationException if this
+ * condition is not met.
+ * @param creditCards The CreditCardList to validate
+ * @throws ValidationException if a name repeats.
+ */
+ public static void validateCreditCardNames(CreditCardList creditCards) throws ValidationException {
+ HashSet creditCardNames = new HashSet<>();
+ for (CreditCard creditCard : creditCards.creditCardListArray) {
+ String name = creditCard.getNameOfCard();
+ if (creditCardNames.contains(name)) {
+ throw new ValidationException(name + " appears twice in credit card list");
+ }
+ creditCardNames.add(name);
+ }
+ }
+}
diff --git a/src/main/java/seedu/mindmymoney/userfinancial/CreditCard.java b/src/main/java/seedu/mindmymoney/userfinancial/CreditCard.java
new file mode 100644
index 0000000000..5426a3e270
--- /dev/null
+++ b/src/main/java/seedu/mindmymoney/userfinancial/CreditCard.java
@@ -0,0 +1,132 @@
+package seedu.mindmymoney.userfinancial;
+
+
+import seedu.mindmymoney.MindMyMoneyException;
+import seedu.mindmymoney.data.PropertyList;
+import seedu.mindmymoney.helper.ValidatorFunctions;
+
+import static seedu.mindmymoney.constants.CalculationConversion.FLOAT_TO_PERCENTAGE;
+import static seedu.mindmymoney.helper.GeneralFunctions.formatFloat;
+
+
+/**
+ * Represents the credit card entry.
+ */
+public class CreditCard implements MindMyMoneySerializable {
+ private float monthlyCardLimit;
+ private double cashback;
+ private String nameOfCard;
+ private float totalExpenditure = 0;
+
+ public CreditCard(String nameOfCard, double cashback, float monthlyCardLimit) {
+ setNameOfCard(nameOfCard);
+ setCashback(cashback);
+ setMonthlyCardLimit(monthlyCardLimit);
+ }
+
+ public void setNameOfCard(String nameOfCard) {
+ this.nameOfCard = nameOfCard;
+ }
+
+ public String getNameOfCard() {
+ return nameOfCard;
+ }
+
+ public void setCashback(double cashback) {
+ this.cashback = cashback;
+ }
+
+ public double getCashback() {
+ return cashback;
+ }
+
+ public void setMonthlyCardLimit(float monthlyCardLimit) {
+ this.monthlyCardLimit = monthlyCardLimit;
+ }
+
+ public float getMonthlyCardLimit() {
+ return monthlyCardLimit;
+ }
+
+ public float getTotalExpenditure() {
+ return totalExpenditure;
+ }
+
+ public float getBalanceLeft() {
+ return monthlyCardLimit - totalExpenditure;
+ }
+
+ public void addExpenditure(float amount) {
+ this.totalExpenditure += amount;
+ }
+
+ public void deductExpenditure(float amount) {
+ this.totalExpenditure -= amount;
+ }
+
+ public float getTotalCashback() {
+ return formatFloat((float)(totalExpenditure * (cashback * FLOAT_TO_PERCENTAGE)));
+ }
+
+ @Override
+ public String toString() {
+ return "Name: " + getNameOfCard() + " [Cashback: " + String.format("%.2f", getCashback())
+ + "%] [Cashback gained: $" + String.format("%.2f", getTotalCashback())
+ + "] [Card limit: $" + String.format("%.2f", getMonthlyCardLimit())
+ + "] [Balance left: $" + String.format("%.2f", getBalanceLeft()) + "]\n";
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (!(object instanceof CreditCard)) {
+ return false;
+ }
+ CreditCard creditCard = (CreditCard) object;
+ return nameOfCard.equals(creditCard.nameOfCard) && (cashback == creditCard.cashback)
+ && (monthlyCardLimit == creditCard.monthlyCardLimit);
+ }
+
+ /**
+ * Returns a String representation of this credit card, in a machine-readable format.
+ * @return The serialized CreditCard.
+ */
+ public String serialize() {
+ PropertyList plist = new PropertyList();
+ plist.addProperty("monthlyCardLimit", Float.toString(monthlyCardLimit));
+ plist.addProperty("cashback", Double.toString(cashback));
+ plist.addProperty("nameOfCard", nameOfCard);
+ plist.addProperty("totalExpenditure", Float.toString(totalExpenditure));
+ return plist.serialize();
+ }
+
+ /**
+ * Converts the output of CreditCard#serialize back into a CreditCard.
+ * @param serialized The serialized CreditCard
+ * @return A CreditCard.
+ * @throws MindMyMoneyException if the format is invalid.
+ */
+ public static CreditCard deserialize(String serialized) throws MindMyMoneyException {
+ PropertyList plist = PropertyList.deserialize(serialized);
+ try {
+ double cashback = Double.parseDouble(plist.getValue("cashback"));
+ double monthlyCardLimit = Double.parseDouble(plist.getValue("monthlyCardLimit"));
+ double totalExpenditure = Double.parseDouble(plist.getValue("totalExpenditure"));
+ ValidatorFunctions.validateInRange(cashback, 0, 100, "cashback");
+ ValidatorFunctions.validateInRange(monthlyCardLimit, 0, 40000, "monthly limit");
+ ValidatorFunctions.validateInRange(totalExpenditure, 0, monthlyCardLimit, "total expenditures");
+ CreditCard cc = new CreditCard(plist.getValue("nameOfCard"),
+ cashback,
+ (float) monthlyCardLimit);
+ cc.totalExpenditure = (float) totalExpenditure;
+ return cc;
+ } catch (NumberFormatException e) {
+ throw new MindMyMoneyException("Invalid number during deserialization of " + serialized);
+ } catch (ValidationException e) {
+ throw e;
+ } catch (MindMyMoneyException e) {
+ String missingProperty = e.getMessage();
+ throw new MindMyMoneyException("Line [" + serialized + "] does not contain required value "
+ + missingProperty);
+ }
+ }
+}
diff --git a/src/main/java/seedu/mindmymoney/userfinancial/Expenditure.java b/src/main/java/seedu/mindmymoney/userfinancial/Expenditure.java
new file mode 100644
index 0000000000..a7f4b9c5de
--- /dev/null
+++ b/src/main/java/seedu/mindmymoney/userfinancial/Expenditure.java
@@ -0,0 +1,136 @@
+package seedu.mindmymoney.userfinancial;
+
+import seedu.mindmymoney.MindMyMoneyException;
+import seedu.mindmymoney.data.PropertyList;
+import seedu.mindmymoney.helper.ValidatorFunctions;
+
+
+import java.util.Objects;
+
+/**
+ * Represents the expenditure entry.
+ */
+public class Expenditure implements MindMyMoneySerializable {
+ private String description;
+ private float amount;
+ private String category;
+ private String paymentMethod;
+ private String time;
+
+ public Expenditure(String paymentMethod, String category, String description, float amount, String time) {
+ setDescription(description);
+ setAmount(amount);
+ setCategory(category);
+ setPaymentMethod(paymentMethod);
+ setTime(time);
+ }
+
+ public void setAmount(float amount) {
+ this.amount = amount;
+ }
+
+ public float getAmount() {
+ return amount;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setCategory(String category) {
+ this.category = category;
+ }
+
+ public String getCategory() {
+ return category;
+ }
+
+ public void setPaymentMethod(String expenditure) {
+ this.paymentMethod = expenditure;
+ }
+
+ public String getPaymentMethod() {
+ return paymentMethod;
+ }
+
+ public void setTime(String time) {
+ this.time = time;
+ }
+
+ public String getTime() {
+ return time;
+ }
+
+ @Override
+ public String toString() {
+ return "$" + getAmount() + " on " + getDescription() + ". Paid using "
+ + getPaymentMethod() + " [" + getCategory() + "]" + " [" + getTime() + "]";
+
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (!(object instanceof Expenditure)) {
+ return false;
+ }
+ Expenditure expenditure = (Expenditure) object;
+ return description.equals(expenditure.description) && (amount == expenditure.amount)
+ && category.equals(expenditure.category) && paymentMethod.equals(expenditure.paymentMethod)
+ && time.equals(expenditure.time);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(description, amount, category, paymentMethod, time);
+ }
+
+
+ /**
+ * Returns a String representation of this expenditure, in a machine-readable format.
+ * @return The serialized Expenditure.
+ */
+ public String serialize() {
+ PropertyList plist = new PropertyList();
+ plist.addProperty("description", description);
+ plist.addProperty("category", category);
+ plist.addProperty("paymentMethod", paymentMethod);
+ plist.addProperty("time", time);
+ plist.addProperty("amount", Float.toString(amount));
+ return plist.serialize();
+ }
+
+ /**
+ * Converts the output of Expenditure#serialize back into an Expenditure.
+ * @param serialized The serialized Expenditure
+ * @return An Expenditure.
+ * @throws MindMyMoneyException if the format is invalid.
+ */
+ public static Expenditure deserialize(String serialized) throws MindMyMoneyException {
+ PropertyList plist = PropertyList.deserialize(serialized);
+ try {
+ String category = plist.getValue("category");
+ ValidatorFunctions.validateExpenditureCategory(category);
+ float amount = Float.parseFloat(plist.getValue("amount"));
+ ValidatorFunctions.validateInRange(amount, 0, Float.POSITIVE_INFINITY, "amount");
+ String time = plist.getValue("time");
+ ValidatorFunctions.validateDate(time);
+ return new Expenditure(plist.getValue("paymentMethod"),
+ category,
+ plist.getValue("description"),
+ amount,
+ time);
+ } catch (NumberFormatException e) {
+ throw new MindMyMoneyException("Invalid number for amount during deserialization of " + serialized);
+ } catch (ValidationException e) {
+ throw e;
+ } catch (MindMyMoneyException e) {
+ String missingProperty = e.getMessage();
+ throw new MindMyMoneyException("Line [" + serialized + "] does not contain required value "
+ + missingProperty);
+ }
+ }
+}
diff --git a/src/main/java/seedu/mindmymoney/userfinancial/Income.java b/src/main/java/seedu/mindmymoney/userfinancial/Income.java
new file mode 100644
index 0000000000..a8d1315112
--- /dev/null
+++ b/src/main/java/seedu/mindmymoney/userfinancial/Income.java
@@ -0,0 +1,84 @@
+package seedu.mindmymoney.userfinancial;
+
+import seedu.mindmymoney.MindMyMoneyException;
+import seedu.mindmymoney.data.PropertyList;
+import seedu.mindmymoney.helper.ValidatorFunctions;
+
+/**
+ * Represents the income entry.
+ */
+public class Income implements MindMyMoneySerializable {
+ private int amount;
+ private String category;
+
+ public Income(int amount, String category) {
+ this.amount = amount;
+ this.category = category;
+ }
+
+ public int getAmount() {
+ return amount;
+ }
+
+ public String getCategory() {
+ return category;
+ }
+
+ /**
+ * Returns the income entry as a string format.
+ *
+ * @return String format.
+ */
+ @Override
+ public String toString() {
+ String incomeInfo = "Amount: $" + amount + "\n"
+ + " Category: " + category + "\n";
+ return incomeInfo;
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (!(object instanceof Income)) {
+ return false;
+ }
+ Income income = (Income) object;
+ return (amount == income.amount) && (category.equals(income.category));
+ }
+
+ /**
+ * Returns a String representation of this income source, in a machine-readable format.
+ * @return The serialized Income.
+ */
+ public String serialize() {
+ PropertyList plist = new PropertyList();
+ plist.addProperty("category", category);
+ plist.addProperty("amount", Integer.toString(amount));
+ return plist.serialize();
+ }
+
+ /**
+ * Converts the output of Income#serialize back into an Income.
+ * @param serialized The serialized Income.
+ * @return An Income.
+ * @throws MindMyMoneyException if the format is invalid.
+ */
+ public static Income deserialize(String serialized) throws MindMyMoneyException {
+ PropertyList plist = PropertyList.deserialize(serialized);
+ try {
+ int amount = Integer.parseInt(plist.getValue("amount"));
+ String category = plist.getValue("category");
+ ValidatorFunctions.validateIncomeCategory(category);
+ ValidatorFunctions.validateLowerBound(amount, 0, true, "amount");
+ return new Income(Integer.parseInt(plist.getValue("amount")),
+ plist.getValue("category"));
+ } catch (NumberFormatException e) {
+ throw new MindMyMoneyException("Invalid number for amount during deserialization of " + serialized);
+ } catch (ValidationException e) {
+ throw e;
+ } catch (MindMyMoneyException e) { // catches errors that are NOT ValidationExceptions
+ String missingProperty = e.getMessage();
+ throw new MindMyMoneyException("Line [" + serialized + "] does not contain required value "
+ + missingProperty);
+ }
+ }
+}
diff --git a/src/main/java/seedu/mindmymoney/userfinancial/MindMyMoneySerializable.java b/src/main/java/seedu/mindmymoney/userfinancial/MindMyMoneySerializable.java
new file mode 100644
index 0000000000..02eeb07335
--- /dev/null
+++ b/src/main/java/seedu/mindmymoney/userfinancial/MindMyMoneySerializable.java
@@ -0,0 +1,11 @@
+package seedu.mindmymoney.userfinancial;
+
+/**
+ * Interface for serializable objects.
+ * Note that this is different from java.io.Serializable,
+ * as classes implementing this interface should ensure that their
+ * serializations are human-readable.
+ */
+public interface MindMyMoneySerializable {
+ String serialize();
+}
diff --git a/src/main/java/seedu/mindmymoney/userfinancial/User.java b/src/main/java/seedu/mindmymoney/userfinancial/User.java
new file mode 100644
index 0000000000..8b29b50fa3
--- /dev/null
+++ b/src/main/java/seedu/mindmymoney/userfinancial/User.java
@@ -0,0 +1,89 @@
+package seedu.mindmymoney.userfinancial;
+
+import seedu.mindmymoney.MindMyMoneyException;
+import seedu.mindmymoney.data.CreditCardList;
+import seedu.mindmymoney.data.ExpenditureList;
+import seedu.mindmymoney.data.IncomeList;
+import seedu.mindmymoney.helper.ValidatorFunctions;
+
+import java.util.Scanner;
+
+/**
+ * Represents the user.
+ */
+public class User {
+ private ExpenditureList expenditureListArray;
+ private CreditCardList creditCardListArray;
+ private IncomeList incomeListArray;
+
+ public User() {
+ setExpenditureListArray(new ExpenditureList());
+ setCreditCardListArray(new CreditCardList());
+ setIncomeListArray(new IncomeList());
+ }
+
+ public User(ExpenditureList expenditureListArray, CreditCardList creditCardListArray,
+ IncomeList incomeListArray) {
+ setExpenditureListArray(expenditureListArray);
+ setCreditCardListArray(creditCardListArray);
+ setIncomeListArray(incomeListArray);
+ }
+
+ public void setExpenditureListArray(ExpenditureList expenditureListArray) {
+ this.expenditureListArray = expenditureListArray;
+ }
+
+ public void setCreditCardListArray(CreditCardList creditCardListArray) {
+ this.creditCardListArray = creditCardListArray;
+ }
+
+ public void setIncomeListArray(IncomeList incomeListArray) {
+ this.incomeListArray = incomeListArray;
+ }
+
+ public ExpenditureList getExpenditureListArray() {
+ return expenditureListArray;
+ }
+
+ public CreditCardList getCreditCardListArray() {
+ return creditCardListArray;
+ }
+
+ public IncomeList getIncomeListArray() {
+ return incomeListArray;
+ }
+
+ /**
+ * Returns a String representation of this user in a machine-readable format.
+ * @return A serialized User.
+ */
+ public String serialize() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(expenditureListArray.serialize());
+ sb.append(creditCardListArray.serialize());
+ sb.append(incomeListArray.serialize());
+ return sb.toString();
+ }
+
+
+ /**
+ * Converts the output of User#serialized back into a User. This method reads from
+ * a Scanner
+ * @param scanner A Scanner from which to read a serialized User.
+ * @return The User.
+ * @throws MindMyMoneyException if the format is incorrect.
+ */
+ public static User deserializeFrom(Scanner scanner) throws MindMyMoneyException {
+ User savedUser = new User();
+
+ savedUser.setExpenditureListArray(ExpenditureList.deserializeFrom(scanner));
+ savedUser.setCreditCardListArray(CreditCardList.deserializeFrom(scanner));
+ savedUser.setIncomeListArray(IncomeList.deserializeFrom(scanner));
+
+ ValidatorFunctions.validateCreditCardNames(savedUser.creditCardListArray);
+ ValidatorFunctions.validatePaymentMethods(savedUser.expenditureListArray, savedUser.creditCardListArray);
+
+ return savedUser;
+ }
+
+}
diff --git a/src/main/java/seedu/mindmymoney/userfinancial/ValidationException.java b/src/main/java/seedu/mindmymoney/userfinancial/ValidationException.java
new file mode 100644
index 0000000000..57474027c2
--- /dev/null
+++ b/src/main/java/seedu/mindmymoney/userfinancial/ValidationException.java
@@ -0,0 +1,10 @@
+package seedu.mindmymoney.userfinancial;
+
+import seedu.mindmymoney.MindMyMoneyException;
+
+/** Class for representing validation errors. */
+public class ValidationException extends MindMyMoneyException {
+ public ValidationException(String s) {
+ super(s);
+ }
+}
diff --git a/src/test/java/seedu/duke/DukeTest.java b/src/test/java/seedu/duke/DukeTest.java
deleted file mode 100644
index 2dda5fd651..0000000000
--- a/src/test/java/seedu/duke/DukeTest.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package seedu.duke;
-
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-import org.junit.jupiter.api.Test;
-
-class DukeTest {
- @Test
- public void sampleTest() {
- assertTrue(true);
- }
-}
diff --git a/src/test/java/seedu/mindmymoney/MindMyMoneyTest.java b/src/test/java/seedu/mindmymoney/MindMyMoneyTest.java
new file mode 100644
index 0000000000..782bfc43aa
--- /dev/null
+++ b/src/test/java/seedu/mindmymoney/MindMyMoneyTest.java
@@ -0,0 +1,21 @@
+package seedu.mindmymoney;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.NoSuchElementException;
+
+class MindMyMoneyTest {
+
+ /**
+ * Asserts if MMM is able to run and await for new line.
+ * If a NoSuchElementException occurs, MM is trying to read a newline but unable to, hence ran successfully.
+ */
+ @Test
+ public void mindMyMoney_runAndInputNewLine_expectedException() {
+ assertThrows(NoSuchElementException.class,
+ () -> new MindMyMoney().run());
+
+ }
+}
diff --git a/src/test/java/seedu/mindmymoney/ParserTest.java b/src/test/java/seedu/mindmymoney/ParserTest.java
new file mode 100644
index 0000000000..97298eb5c8
--- /dev/null
+++ b/src/test/java/seedu/mindmymoney/ParserTest.java
@@ -0,0 +1,135 @@
+package seedu.mindmymoney;
+
+import org.junit.jupiter.api.Test;
+import seedu.mindmymoney.command.ByeCommand;
+import seedu.mindmymoney.command.CalculateInputCommand;
+import seedu.mindmymoney.command.DeleteCommand;
+import seedu.mindmymoney.command.ListCommand;
+import seedu.mindmymoney.command.AddCommand;
+import seedu.mindmymoney.command.UpdateCommand;
+import seedu.mindmymoney.command.HelpCommand;
+import seedu.mindmymoney.data.CreditCardList;
+import seedu.mindmymoney.data.ExpenditureList;
+import seedu.mindmymoney.data.IncomeList;
+import seedu.mindmymoney.userfinancial.User;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class ParserTest {
+ private final ByteArrayOutputStream capturedOut = new ByteArrayOutputStream();
+ private final PrintStream stdout = System.out;
+
+ public void setUp() {
+ System.setOut(new PrintStream(capturedOut));
+ }
+
+ public static final int TEST_PRICE = 1; //arbitrary number for testing
+ public static final int TEST_INDEX = 0; //arbitrary number for testing
+
+ /**
+ * Checks Parser.parseCommand() that it returns a Command object for each test input.
+ */
+ @Test
+ void parseCommand_normalInput_expectCorrectCommandObject() {
+ String testInput = "help";
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeTestList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeTestList);
+ Parser testParser = new Parser();
+
+ assertTrue(testParser.parseCommand(testInput, user) instanceof HelpCommand);
+ assert testParser.parseCommand(testInput, user)
+ instanceof HelpCommand : "testParser should return an " + "instance of HelpCommand";
+
+ testInput = "add description " + TEST_PRICE;
+ assertTrue(testParser.parseCommand(testInput, user) instanceof AddCommand);
+
+ testInput = "update " + TEST_INDEX + " description " + TEST_PRICE;
+ assertTrue(testParser.parseCommand(testInput, user) instanceof UpdateCommand);
+
+ testInput = "list /e";
+ assertTrue(testParser.parseCommand(testInput, user) instanceof ListCommand);
+
+ testInput = "delete " + TEST_INDEX;
+ assertTrue(testParser.parseCommand(testInput, user) instanceof DeleteCommand);
+
+ testInput = "bye";
+ assertTrue(testParser.parseCommand(testInput, user) instanceof ByeCommand);
+
+ testInput = "calculate /epm 01/02/2022";
+ assertTrue(testParser.parseCommand(testInput, user) instanceof CalculateInputCommand);
+ }
+
+ /**
+ * Invalid input by user should return a HelpCommand object
+ * and print an invalid command message.
+ */
+ @Test
+ void parseCommand_invalidInput_expectHelpCommand() {
+ Parser testParser = new Parser();
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeTestList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeTestList);
+
+ assertTrue(testParser.parseCommand("", user) instanceof HelpCommand);
+ }
+
+ /**
+ * "Help /cc" by user should return the credit card help page
+ * and print an invalid command message.
+ */
+ @Test
+ void parseCommand_helpWithParameters_expectHelpCommand() throws MindMyMoneyException {
+ Parser testParser = new Parser();
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeTestList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeTestList);
+ setUp();
+ String expectedOutput =
+ "---------------------------------------Credit Card Help Page--------------------------"
+ + "-------------\n"
+ + "1. Listing all Credit Cards: list /cc\n"
+ + "2. Adding a Credit Card: add /cc /n [CREDIT_CARD_NAME] /cb [CASHBACK] /cl [CREDIT_LIMIT]\n"
+ + "3. Updating a Credit Card: update /cc [INDEX] /n [NEW_NAME] /cb [NEW_CASHBACK] "
+ + "/cl [NEW_CREDIT_LIMIT]\n"
+ + "4. Removing a credit card: delete /cc [INDEX]\n"
+ + "5. Exiting the program: bye\n"
+ + "-----------------------------------------------------------------------------------------------"
+ + "----";
+ (testParser.parseCommand("help /cc", user)).executeCommand();
+ tearDown();
+ assertEquals(expectedOutput, capturedOut.toString().trim());
+ }
+
+ /**
+ * Add input without parameters should return a HelpCommand object
+ * and print an invalid command message.
+ */
+ @Test
+ void parseCommand_addWithoutParameters_expectException() throws MindMyMoneyException {
+ Parser testParser = new Parser();
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeTestList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeTestList);
+ setUp();
+ String expectedOutput = "Invalid command! \n"
+ + "Type \"help /e\" to view the list of supported expenditure commands\n"
+ + "Type \"help /cc\" to view the list of supported Credit Card commands\n"
+ + "Type \"help /i\" to view the list of supported income commands";
+ (testParser.parseCommand("add", user)).executeCommand();
+ tearDown();
+ assertEquals(expectedOutput, capturedOut.toString().trim());
+ }
+
+ public void tearDown() {
+ System.setOut(stdout);
+ }
+}
diff --git a/src/test/java/seedu/mindmymoney/StorageTest.java b/src/test/java/seedu/mindmymoney/StorageTest.java
new file mode 100644
index 0000000000..52969a9258
--- /dev/null
+++ b/src/test/java/seedu/mindmymoney/StorageTest.java
@@ -0,0 +1,62 @@
+package seedu.mindmymoney;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+import seedu.mindmymoney.data.CreditCardList;
+import seedu.mindmymoney.data.ExpenditureList;
+import seedu.mindmymoney.data.IncomeList;
+import seedu.mindmymoney.userfinancial.Expenditure;
+import seedu.mindmymoney.userfinancial.User;
+
+import java.io.File;
+import java.io.IOException;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+
+public class StorageTest {
+ @TempDir
+ File storageDir;
+
+ @Test
+ void initialStartup_nonexistentFile_emptyList() {
+ File storageFile = new File(storageDir, "list.txt");
+ try {
+ Storage storage = new Storage(storageFile);
+ } catch (MindMyMoneyException e) {
+ fail();
+ }
+ }
+
+ @Test
+ void normalUse_savedFile_listRetrieved() {
+ File storageFile = new File(storageDir, "list.txt");
+ try {
+ storageFile.createNewFile();
+ } catch (IOException e) {
+ Assertions.fail("Creating storage file for test threw IOException");
+ }
+ User savedUser = new User();
+ savedUser.setCreditCardListArray(new CreditCardList());
+ savedUser.setIncomeListArray(new IncomeList());
+
+ ExpenditureList expenditureList = new ExpenditureList();
+ expenditureList.add(new Expenditure("Cash", "Food",
+ "test", 1, "05/03/2022"));
+ expenditureList.add(new Expenditure("Cash", "Food",
+ "Make tests", 999, "05/03/2022"));
+
+ savedUser.setExpenditureListArray(expenditureList);
+
+ try {
+ Storage storage = new Storage(storageFile);
+ storage.save(savedUser);
+ User loadedUser = storage.load();
+ assertEquals(expenditureList.expenditureListArray,
+ loadedUser.getExpenditureListArray().expenditureListArray);
+ } catch (MindMyMoneyException e) {
+ fail();
+ }
+ }
+}
diff --git a/src/test/java/seedu/mindmymoney/UiTest.java b/src/test/java/seedu/mindmymoney/UiTest.java
new file mode 100644
index 0000000000..2271394a00
--- /dev/null
+++ b/src/test/java/seedu/mindmymoney/UiTest.java
@@ -0,0 +1,5 @@
+package seedu.mindmymoney;
+
+class UiTest {
+
+}
\ No newline at end of file
diff --git a/src/test/java/seedu/mindmymoney/command/AddCommandTest.java b/src/test/java/seedu/mindmymoney/command/AddCommandTest.java
new file mode 100644
index 0000000000..e044796ffd
--- /dev/null
+++ b/src/test/java/seedu/mindmymoney/command/AddCommandTest.java
@@ -0,0 +1,622 @@
+package seedu.mindmymoney.command;
+
+import org.junit.jupiter.api.Test;
+import seedu.mindmymoney.MindMyMoneyException;
+import seedu.mindmymoney.data.CreditCardList;
+import seedu.mindmymoney.data.ExpenditureList;
+import seedu.mindmymoney.data.IncomeList;
+import seedu.mindmymoney.userfinancial.CreditCard;
+import seedu.mindmymoney.userfinancial.Expenditure;
+import seedu.mindmymoney.userfinancial.Income;
+import seedu.mindmymoney.userfinancial.User;
+
+import java.util.ArrayList;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static seedu.mindmymoney.constants.Indexes.LIST_INDEX_CORRECTION;
+
+class AddCommandTest {
+
+ /**
+ * Asserts if user is able to add an input with cash.
+ */
+ @Test
+ void addCommand_oneExpenditureInput_expectExpenditureListUpdated() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+
+ String inputString = "/e /pm cash /c Personal /d Nike Shoes /a 300 /t 30/03/2022";
+ new AddCommand(inputString, user).executeCommand();
+ ArrayList testList = new ArrayList<>();
+ testList.add(new Expenditure("Cash", "Personal", "Nike Shoes",
+ 300, "30/03/2022"));
+ String expectedOutput = getExpenditureOutput(testList);
+ String actualOutput = getExpenditureOutput(expenditureTestList.expenditureListArray);
+ assertEquals(expectedOutput, actualOutput);
+ testList.clear();
+ }
+
+ /**
+ * Tests for insertion of credit card with valid parameters.
+ */
+ @Test
+ void addCommand_validAddCreditCardInput_expectCreditCardListUpdate() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+
+ String inputString = "/cc /n DBS /cb 1.5 /cl 500";
+ new AddCommand(inputString, user).executeCommand();
+ ArrayList testList = new ArrayList<>();
+ testList.add(new CreditCard("DBS", 1.5, 500));
+ String expectedOutput = testList.get(0).toString();
+ String actualOutput = user.getCreditCardListArray().get(0).toString();
+ assertEquals(expectedOutput, actualOutput);
+ testList.clear();
+ }
+
+ /**
+ * Asserts if user is able to add an input that is not case-sensitive.
+ */
+ @Test
+ void addCommand_caseInsensitiveExpenditureInput_expectExpenditureListUpdated() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputString = "/e /pm cASh /c PerSONal /d Nike Shoes /a 300 /t 30/03/2022";
+
+ new AddCommand(inputString, user).executeCommand();
+ ArrayList testList = new ArrayList<>();
+ testList.add(new Expenditure("Cash", "Personal", "Nike Shoes",
+ 300, "30/03/2022"));
+ String expectedOutput = getExpenditureOutput(testList);
+ String actualOutput = getExpenditureOutput(expenditureTestList.expenditureListArray);
+ assertEquals(expectedOutput, actualOutput);
+ testList.clear();
+ }
+
+ /**
+ * Asserts if user is able to add an amount that has more decimal place than float.
+ */
+ @Test
+ void addCommand_wrongDecimalPlaceExpenditureInput_expectExpenditureListUpdated() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputString = "/e /pm Cash /c Personal /d Nike Shoes /a 300.1299786222834 /t 30/03/2022";
+
+ new AddCommand(inputString, user).executeCommand();
+ ArrayList testList = new ArrayList<>();
+ testList.add(new Expenditure("Cash", "Personal", "Nike Shoes",
+ (float) 300.13, "30/03/2022"));
+ String expectedOutput = getExpenditureOutput(testList);
+ String actualOutput = getExpenditureOutput(expenditureTestList.expenditureListArray);
+ assertEquals(expectedOutput, actualOutput);
+ testList.clear();
+ }
+
+ /**
+ * Asserts if user is able to add an expenditure input with credit card.
+ */
+ @Test
+ void addCommand_expenditureWithCreditCardInput_expectExpenditureListUpdated() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ creditCardTestList.add(new CreditCard("posb", 0.05, 500));
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputString = "/e /pm posb /c Personal /d Nike Shoes /a 300 /t 30/03/2022";
+
+ new AddCommand(inputString, user).executeCommand();
+ ArrayList testList = new ArrayList<>();
+ testList.add(new Expenditure("posb", "Personal", "Nike Shoes",
+ 300, "30/03/2022"));
+ String expectedOutput = getExpenditureOutput(testList);
+ String actualOutput = getExpenditureOutput(expenditureTestList.expenditureListArray);
+ assertEquals(expectedOutput, actualOutput);
+ testList.clear();
+ }
+
+ /**
+ * Asserts if user is able to add an Income entry.
+ *
+ * @throws MindMyMoneyException when an invalid command is received.
+ */
+ @Test
+ void addCommand_incomeInput_expectIncomeListUpdated() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+
+ String inputString = "/i /a 3000 /c Salary";
+ new AddCommand(inputString, user).executeCommand();
+
+ ArrayList testList = new ArrayList<>();
+ testList.add(new Income(3000, "Salary"));
+
+ String expectedOutput = getIncomeOutput(testList);
+ String actualOutput = getIncomeOutput(incomeList.incomeListArray);
+ assertEquals(expectedOutput, actualOutput);
+
+ testList.clear();
+ }
+
+ /**
+ * Asserts if user is able to add an Income entry that is not case-sensitive.
+ */
+ @Test
+ void addCommand_caseInsensitiveIncomeInput_expectIncomeListUpdated() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+
+ String inputString = "/i /a 3000 /c SaLaRy";
+ new AddCommand(inputString, user).executeCommand();
+
+ ArrayList testList = new ArrayList<>();
+ testList.add(new Income(3000, "Salary"));
+
+ String expectedOutput = getIncomeOutput(testList);
+ String actualOutput = getIncomeOutput(incomeList.incomeListArray);
+ assertEquals(expectedOutput, actualOutput);
+
+ testList.clear();
+ }
+
+ /**
+ * Asserts if user is able to add an empty input.
+ */
+ @Test
+ void addCommand_missingIncomeInput_expectException() {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputString = "";
+
+ assertThrows(MindMyMoneyException.class,
+ () -> new AddCommand(inputString, user).executeCommand());
+ }
+
+ /**
+ * Asserts if user is able to add a non-numerical amount.
+ */
+ @Test
+ void addCommand_nonNumberIncomeAmount_expectException() {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputString = "/e /pm cash /c Personal /d Nike Shoes /a abcd /t 30/03/2022";
+
+ assertThrows(MindMyMoneyException.class,
+ () -> new AddCommand(inputString, user).executeCommand());
+ }
+
+ /**
+ * Asserts if user is able to add an incorrect flag.
+ */
+ @Test
+ void addCommand_incorrectExpenditureFlags_expectException() {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputString = "/e /pm cash /z Personal /d Nike Shoes /a 500 /t 30/03/2022";
+
+ assertThrows(MindMyMoneyException.class,
+ () -> new AddCommand(inputString, user).executeCommand());
+ }
+
+ /**
+ * Asserts if user is able to add an incorrect order of flag.
+ */
+ @Test
+ void addCommand_incorrectOrderOfExpenditureFlags_expectException() {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputString = "/e /pm cash /d Nike Shoes /a 500 /t 30/03/2022 /c Personal";
+
+ assertThrows(MindMyMoneyException.class,
+ () -> new AddCommand(inputString, user).executeCommand());
+ }
+
+ /**
+ * Asserts if user is able to add an incorrect Expenditure Method.
+ */
+ @Test
+ void addCommand_incorrectExpenditurePaymentMethod_expectException() {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputString = "/e /pm casssh /c Personal /d Nike Shoes /a 500 /t 30/03/2022 ";
+
+ assertThrows(MindMyMoneyException.class,
+ () -> new AddCommand(inputString, user).executeCommand());
+ }
+
+ /**
+ * Asserts if user is able to add an incorrect Category.
+ */
+ @Test
+ void addCommand_incorrectExpenditureCategory_expectException() {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputString = "/e /pm cash /c Person /d Nike Shoes /a 500 /t 30/03/2022";
+
+ assertThrows(MindMyMoneyException.class,
+ () -> new AddCommand(inputString, user).executeCommand());
+ }
+
+ /**
+ * Asserts if user is able to add an incorrect Date.
+ */
+ @Test
+ void addCommand_incorrectExpenditureDate_expectException() {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+
+ String firstInputString = "/e /pm cash /c Person /d Nike Shoes /a 500 /t 30/4/2022";
+ assertThrows(MindMyMoneyException.class,
+ () -> new AddCommand(firstInputString, user).executeCommand());
+ String secondInputString = "/e /pm cash /c Person /d Nike Shoes /a 500 /t 04/2022";
+
+ assertThrows(MindMyMoneyException.class,
+ () -> new AddCommand(secondInputString, user).executeCommand());
+ String thirdInputString = "/e /pm cash /c Person /d Nike Shoes /a 500 /t 2022";
+
+ assertThrows(MindMyMoneyException.class,
+ () -> new AddCommand(thirdInputString, user).executeCommand());
+
+ String fourthInputString = "/e /pm cash /c Person /d Nike Shoes /a 500 /t 38/14/2022";
+ assertThrows(MindMyMoneyException.class,
+ () -> new AddCommand(fourthInputString, user).executeCommand());
+
+ String fifthInputString = "/pm cash /c Food /d Porridge /a 4.50 /t 31/11/2021";
+ assertThrows(MindMyMoneyException.class,
+ () -> new AddCommand(fifthInputString, user).executeCommand());
+
+ String sixthInputString = "/pm cash /c Food /d Porridge /a 4.50 /t 29/02/2021";
+ assertThrows(MindMyMoneyException.class,
+ () -> new AddCommand(sixthInputString, user).executeCommand());
+
+ String seventhInputString = "/pm cash /c Food /d Porridge /a 4.50 /t 30/02/2020";
+ assertThrows(MindMyMoneyException.class,
+ () -> new AddCommand(seventhInputString, user).executeCommand());
+
+ String eighthInputString = "/pm cash /c Food /d Porridge /a 4.50 /t 31/04/2020";
+ assertThrows(MindMyMoneyException.class,
+ () -> new AddCommand(eighthInputString, user).executeCommand());
+ }
+
+ /**
+ * Asserts if user is able to add an empty expenditure method.
+ */
+ @Test
+ void addCommand_nullExpenditurePaymentMethod_expectException() {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputString = "/e /pm /c Person /d Nike Shoes /a 500 /t 30/03/2022";
+
+ assertThrows(MindMyMoneyException.class,
+ () -> new AddCommand(inputString, user).executeCommand());
+ }
+
+ /**
+ * Asserts if user is able to add an empty category.
+ */
+ @Test
+ void addCommand_nullExpenditureCategory_expectException() {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputString = "/e /pm Cash /c /d Nike Shoes /a 500 /t 30/03/2022";
+
+ assertThrows(MindMyMoneyException.class,
+ () -> new AddCommand(inputString, user).executeCommand());
+ }
+
+ /**
+ * Asserts if user is able to add an empty description.
+ */
+ @Test
+ void addCommand_nullExpenditureDescription_expectException() {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputString = "/e /pm Cash /c Food /d /a 500 /t 30/03/2022";
+
+ assertThrows(MindMyMoneyException.class,
+ () -> new AddCommand(inputString, user).executeCommand());
+ }
+
+ /**
+ * Asserts if user is able to add an empty amount.
+ */
+ @Test
+ void addCommand_nullExpenditureAmount_expectException() {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputString = "/e /pm Cash /c Food /d Shoes /a /t 30/03/2022";
+
+ assertThrows(MindMyMoneyException.class,
+ () -> new AddCommand(inputString, user).executeCommand());
+ }
+
+ /**
+ * Asserts if user is able to add empty time.
+ */
+ @Test
+ void addCommand_nullExpenditureDate_expectException() {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputString = "/e /pm Cash /c Food /d Shoes /a 500 /t";
+
+ assertThrows(MindMyMoneyException.class,
+ () -> new AddCommand(inputString, user).executeCommand());
+ }
+
+ /**
+ * Asserts if user is able to add a field with no spaces between flags.
+ */
+ @Test
+ void addCommand_lackSpacingBetweenExpenditureFlags_expectException() {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputString = "/e /pm/c Person /d Nike Shoes /a 500 /t 30/03/2022";
+
+ assertThrows(MindMyMoneyException.class,
+ () -> new AddCommand(inputString, user).executeCommand());
+ }
+
+ /**
+ * Asserts if user is able to add an income entry with a non-numerical amount.
+ */
+ @Test
+ void addCommand_notNumberIncomeAmount_expectException() {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+
+ String inputString = "/i /a three-thousand /c Salary";
+ assertThrows(MindMyMoneyException.class,
+ () -> new AddCommand(inputString, user).executeCommand());
+ }
+
+ /**
+ * Asserts if user is able to add an income entry with an invalid income category.
+ */
+ @Test
+ void addCommand_invalidIncomeCategory_expectException() {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+
+ String inputString = "/i /a 3000 /c notAnIncomeCategory";
+ assertThrows(MindMyMoneyException.class,
+ () -> new AddCommand(inputString, user).executeCommand());
+ }
+
+ /**
+ * Tests for updating of credit card total expenditure when expenditure is used with credit card.
+ */
+ @Test
+ void addCommand_validExpenditureInput_expectCreditCardTotalExpenditureUpdate() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ creditCardTestList.add(new CreditCard("dbs",0.05,500));
+ assertEquals(creditCardTestList.get(0).getTotalExpenditure(), 0.0);
+
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputString = "/e /pm dbs /c Personal /d Nike Shoes /a 300 /t 30/03/2022";
+
+ new AddCommand(inputString, user).executeCommand();
+ assertEquals(creditCardTestList.get(0).getTotalExpenditure(), 300.0);
+ }
+
+ /**
+ * Tests for exception thrown when an invalid Credit Card name is given.
+ */
+ @Test
+ void addCommand_invalidAddCreditCardNameInput_expectException() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ new AddCommand("/cc /n DBS /cb 1.5 /cl 500", user).executeCommand();
+
+ String inputString = "/cc /n cash /cb 1.5 /cl 500";
+ assertThrows(MindMyMoneyException.class, () -> new AddCommand(inputString, user).executeCommand());
+
+ String secondInputString = "/cc /n CASH /cb 1.5 /cl 500";
+ assertThrows(MindMyMoneyException.class, () -> new AddCommand(secondInputString, user).executeCommand());
+
+ String thirdInputString = "/cc /n dbs /cb 1.5 /cl 500";
+ assertThrows(MindMyMoneyException.class, () -> new AddCommand(thirdInputString, user).executeCommand());
+ }
+
+ /**
+ * Tests for exception thrown when an invalid Credit Card cashback is given.
+ */
+ @Test
+ void addCommand_invalidAddCreditCardCashbackInput_expectException() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputString = "/cc /n cash /cb 101 /cl 500";
+ assertThrows(MindMyMoneyException.class, () -> new AddCommand(inputString, user).executeCommand());
+
+ String secondInputString = "/cc /n cash /cb -1 /cl 500";
+ assertThrows(MindMyMoneyException.class, () -> new AddCommand(secondInputString, user).executeCommand());
+ }
+
+ /**
+ * Tests for exception thrown when an invalid Credit Card limit is given.
+ */
+ @Test
+ void addCommand_invalidAddCreditCardLimitInput_expectException() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+
+ String inputString = "/cc /n cash /cb 1.5 /cl 0";
+ assertThrows(MindMyMoneyException.class, () -> new AddCommand(inputString, user).executeCommand());
+
+ String inputString2 = "/cc /n cash /cb 1.5 /cl -1";
+ assertThrows(MindMyMoneyException.class, () -> new AddCommand(inputString2, user).executeCommand());
+ }
+
+ /**
+ * Asserts if user is able to add an improper flag.
+ */
+ @Test
+ void addCommand_notProperFlag_expectException() {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputString = "/asdf";
+ assertThrows(MindMyMoneyException.class,
+ () -> new AddCommand(inputString, user).executeCommand());
+ }
+
+ /**
+ * Asserts if user is able to an invalid time.
+ */
+ @Test
+ void addCommand_notValidTime_expectException() {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputString = "/e /pm cash /c Person /d Nike Shoes /a 500 /t 30/032022";
+
+ assertThrows(MindMyMoneyException.class,
+ () -> new AddCommand(inputString, user).executeCommand());
+
+ }
+
+ /**
+ * Asserts if user is able to add a day larger than 29 feb.
+ */
+ @Test
+ void addCommand_wrongDateForLeapYear_expectException() {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputString = "/e /pm cash /c Person /d Nike Shoes /a 500 /t 30/02/2020";
+
+ assertThrows(MindMyMoneyException.class,
+ () -> new AddCommand(inputString, user).executeCommand());
+
+ }
+
+ /**
+ * Asserts if user is able to add a day larger than 30 for months with only 30 days.
+ */
+ @Test
+ void addCommand_wrongDateForMonthWithThirtyDays_expectException() {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputString = "/e /pm cash /c Person /d Nike Shoes /a 500 /t 31/09/2018";
+
+ assertThrows(MindMyMoneyException.class,
+ () -> new AddCommand(inputString, user).executeCommand());
+
+ }
+
+ /**
+ * Asserts if user is able to add a larger than 28 feb on a non leap year.
+ */
+ @Test
+ void addCommand_wrongDateForNonLeapYear_expectException() {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputString = "/e /pm cash /c Person /d Nike Shoes /a 500 /t 29/02/2018";
+
+ assertThrows(MindMyMoneyException.class,
+ () -> new AddCommand(inputString, user).executeCommand());
+
+ }
+
+ /**
+ * Test if program is able to exit.
+ */
+ @Test
+ void addCommand_isExit_expectFalse() {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputString = "/e /pm cash /c Person /d Nike Shoes /a 500 /t 29/02/2018";
+
+ assertEquals(false, new AddCommand(inputString, user).isExit());
+
+ }
+
+ /**
+ * Gets the last expenditure entry in the expenditure list and formats it to a string.
+ *
+ * @param list is the expenditure list.
+ * @return expenditure entry as a string if list is not empty, else it returns an empty string.
+ */
+ public String getExpenditureOutput(ArrayList list) {
+ if (!list.isEmpty()) {
+ return list.get(list.size() + LIST_INDEX_CORRECTION).getPaymentMethod()
+ + list.get(list.size() + LIST_INDEX_CORRECTION).getCategory()
+ + list.get(list.size() + LIST_INDEX_CORRECTION).getDescription()
+ + list.get(list.size() + LIST_INDEX_CORRECTION).getAmount()
+ + list.get(list.size() + LIST_INDEX_CORRECTION).getTime();
+ }
+ return "";
+ }
+
+ /**
+ * Gets the last income entry in the income list and formats it to a string.
+ *
+ * @param list is the income list.
+ * @return income entry as a string if list is not empty, else it returns an empty string.
+ */
+ public String getIncomeOutput(ArrayList list) {
+ if (!list.isEmpty()) {
+ return list.get(list.size() + LIST_INDEX_CORRECTION).getAmount()
+ + list.get(list.size() + LIST_INDEX_CORRECTION).getCategory();
+ }
+ return "";
+ }
+}
diff --git a/src/test/java/seedu/mindmymoney/command/ByeCommandTest.java b/src/test/java/seedu/mindmymoney/command/ByeCommandTest.java
new file mode 100644
index 0000000000..8d3d3fd288
--- /dev/null
+++ b/src/test/java/seedu/mindmymoney/command/ByeCommandTest.java
@@ -0,0 +1,30 @@
+package seedu.mindmymoney.command;
+
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class ByeCommandTest {
+ private final ByteArrayOutputStream capturedOut = new ByteArrayOutputStream();
+ private final PrintStream stdout = System.out;
+
+ public void setUp() {
+ System.setOut(new PrintStream(capturedOut));
+ }
+
+ /**
+ * Asserts if command is able to exit.
+ */
+ @Test
+ void byeCommand_isExit_expectByeMessage() {
+ assertEquals(true, new ByeCommand().isExit());
+
+ }
+
+ public void tearDown() {
+ System.setOut(stdout);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/seedu/mindmymoney/command/CalculateInputCommandTest.java b/src/test/java/seedu/mindmymoney/command/CalculateInputCommandTest.java
new file mode 100644
index 0000000000..3734043e1c
--- /dev/null
+++ b/src/test/java/seedu/mindmymoney/command/CalculateInputCommandTest.java
@@ -0,0 +1,248 @@
+package seedu.mindmymoney.command;
+
+import org.junit.jupiter.api.Test;
+import seedu.mindmymoney.MindMyMoneyException;
+import seedu.mindmymoney.data.CreditCardList;
+import seedu.mindmymoney.data.ExpenditureList;
+import seedu.mindmymoney.data.IncomeList;
+import seedu.mindmymoney.userfinancial.User;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+
+class CalculateInputCommandTest {
+ private final ByteArrayOutputStream capturedOut = new ByteArrayOutputStream();
+ private final PrintStream stdout = System.out;
+
+
+ public void setUp() {
+ System.setOut(new PrintStream(capturedOut));
+ }
+
+ /**
+ * Asserts if user is able to calculate expenditure by date.
+ */
+ @Test
+ void calculateInputCommand_calculateByDate_expectCorrectOutput() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+
+ String inputString = "/e /pm cash /c Personal /d Nike Shoes /a 300 /t 30/03/2022";
+ new AddCommand(inputString, user).executeCommand();
+ inputString = "/e /pm cash /c Food /d Coke /a 20 /t 30/03/2022";
+ new AddCommand(inputString, user).executeCommand();
+ inputString = "/e /pm cash /c Entertainment /d Movie /a 10 /t 30/03/2022";
+ new AddCommand(inputString, user).executeCommand();
+ inputString = "/e /pm cash /c Personal /d Nike Shoes /a 200 /t 30/03/2021";
+ new AddCommand(inputString, user).executeCommand();
+
+ setUp();
+ new CalculateInputCommand("/epm 30/03/2022", user).executeCommand();
+ tearDown();
+ String expectedOutput = "Total expenditure in 30/03/2022 is $330.00." + System.lineSeparator()
+ + System.lineSeparator() + "BREAKDOWN OF EXPENSES:" + System.lineSeparator()
+ + "-----------------------------------------------" + System.lineSeparator()
+ + "FOOD: $$$$ [6.06%]" + System.lineSeparator()
+ + "TRANSPORT: [0.0%]" + System.lineSeparator()
+ + "UTILITIES: [0.0%]" + System.lineSeparator()
+ + "PERSONAL: $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ [90.91%]" + System.lineSeparator()
+ + "ENTERTAINMENT: $$ [3.03%]" + System.lineSeparator()
+ + "OTHERS: [0.0%]" + System.lineSeparator()
+ + "-----------------------------------------------";
+ assertEquals(expectedOutput, capturedOut.toString().trim());
+ }
+
+ /**
+ * Asserts if user is able to calculate expenditure by month.
+ */
+ @Test
+ void calculateInputCommand_calculateByMonth_expectCorrectOutput() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+
+ String inputString = "/e /pm cash /c Personal /d Nike Shoes /a 300 /t 30/03/2022";
+ new AddCommand(inputString, user).executeCommand();
+
+ inputString = "/e /pm cash /c Food /d Coke /a 20 /t 01/04/2022";
+
+ new AddCommand(inputString, user).executeCommand();
+
+ setUp();
+ new CalculateInputCommand("/epm 03/2022", user).executeCommand();
+ tearDown();
+ String expectedOutput = "Total expenditure in 03/2022 is $300.00." + System.lineSeparator()
+ + System.lineSeparator() + "BREAKDOWN OF EXPENSES:" + System.lineSeparator()
+ + "-----------------------------------------------" + System.lineSeparator()
+ + "FOOD: [0.0%]" + System.lineSeparator()
+ + "TRANSPORT: [0.0%]" + System.lineSeparator()
+ + "UTILITIES: [0.0%]" + System.lineSeparator()
+ + "PERSONAL: $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ [100.0%]" + System.lineSeparator()
+ + "ENTERTAINMENT: [0.0%]" + System.lineSeparator()
+ + "OTHERS: [0.0%]" + System.lineSeparator()
+ + "-----------------------------------------------";
+ assertEquals(expectedOutput, capturedOut.toString().trim());
+ }
+
+ /**
+ * Asserts if user is able to calculate expenditure by Year.
+ */
+ @Test
+ void calculateInputCommand_calculateByYear_expectCorrectOutput() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputString = "/e /pm cash /c Personal /d Nike Shoes /a 300 /t 30/03/2022";
+ new AddCommand(inputString, user).executeCommand();
+
+ inputString = "/e /pm cash /c Food /d Coke /a 20 /t 30/04/2021";
+
+ new AddCommand(inputString, user).executeCommand();
+
+ setUp();
+ new CalculateInputCommand("/epm 2022", user).executeCommand();
+ tearDown();
+ String expectedOutput = "Total expenditure in 2022 is $300.00." + System.lineSeparator()
+ + System.lineSeparator() + "BREAKDOWN OF EXPENSES:" + System.lineSeparator()
+ + "-----------------------------------------------" + System.lineSeparator()
+ + "FOOD: [0.0%]" + System.lineSeparator()
+ + "TRANSPORT: [0.0%]" + System.lineSeparator()
+ + "UTILITIES: [0.0%]" + System.lineSeparator()
+ + "PERSONAL: $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ [100.0%]" + System.lineSeparator()
+ + "ENTERTAINMENT: [0.0%]" + System.lineSeparator()
+ + "OTHERS: [0.0%]" + System.lineSeparator()
+ + "-----------------------------------------------";
+ assertEquals(expectedOutput, capturedOut.toString().trim());
+ }
+
+ /**
+ * Asserts if user is able to use an incorrect flag.
+ */
+ @Test
+ void calculateInputCommand_wrongFlag_expectException() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputString = "/e /pm cash /c Personal /d Nike Shoes /a 300 /t 30/03/2022";
+ new AddCommand(inputString, user).executeCommand();
+ inputString = "/e /pm cash /c Personal /d Nike Shoes /a 3000 /t 30/03/2022";
+ new AddCommand(inputString, user).executeCommand();
+
+ assertThrows(MindMyMoneyException.class,
+ () -> new CalculateInputCommand("/a 30/03/2022", user).executeCommand());
+ }
+
+ /**
+ * Asserts if user is able to input an empty flag.
+ */
+ @Test
+ void calculateInputCommand_emptyFlag_expectException() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputString = "/e /pm cash /c Personal /d Nike Shoes /a 300 /t 30/03/2022";
+ new AddCommand(inputString, user).executeCommand();
+ inputString = "/e /pm cash /c Personal /d Nike Shoes /a 3000 /t 30/03/2022";
+ new AddCommand(inputString, user).executeCommand();
+
+ assertThrows(MindMyMoneyException.class,
+ () -> new CalculateInputCommand("30/03/2022", user).executeCommand());
+ }
+
+ /**
+ * Asserts if user is able to input an improper input.
+ */
+ @Test
+ void calculateInputCommand_wrongInput_expectException() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputString = "/e /pm cash /c Personal /d Nike Shoes /a 300 /t 30/03/2022";
+ new AddCommand(inputString, user).executeCommand();
+ inputString = "/e /pm cash /c Personal /d Nike Shoes /a 3000 /t 30/03/2022";
+ new AddCommand(inputString, user).executeCommand();
+
+ assertThrows(MindMyMoneyException.class,
+ () -> new CalculateInputCommand("30/03/2022", user).executeCommand());
+ }
+
+ /**
+ * Asserts if user is able to input an improper date input.
+ */
+ @Test
+ void calculateInputCommand_wrongDateInput_expectException() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputString = "/e /pm cash /c Personal /d Nike Shoes /a 300 /t 30/03/2022";
+ new AddCommand(inputString, user).executeCommand();
+ inputString = "/e /pm cash /c Personal /d Nike Shoes /a 3000 /t 30/03/2022";
+ new AddCommand(inputString, user).executeCommand();
+
+ assertThrows(MindMyMoneyException.class,
+ () -> new CalculateInputCommand("/epm 34/03/2022", user).executeCommand());
+ assertThrows(MindMyMoneyException.class,
+ () -> new CalculateInputCommand("/epm 30/2022", user).executeCommand());
+ assertThrows(MindMyMoneyException.class,
+ () -> new CalculateInputCommand("/epm /2022", user).executeCommand());
+ }
+
+ /**
+ * Asserts if user is able to calculate from an empty list.
+ */
+ @Test
+ void calculateInputCommand_emptyList_expectException() {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+
+ assertThrows(MindMyMoneyException.class,
+ () -> new CalculateInputCommand("30/03/2022", user).executeCommand());
+ }
+
+ /**
+ * Asserts if user is able to add in a flag without date.
+ */
+ @Test
+ void calculateInputCommand_noDate_expectException() {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+
+ assertThrows(MindMyMoneyException.class,
+ () -> new CalculateInputCommand("/epm", user).executeCommand());
+ }
+
+ /**
+ * Asserts if command is able to exit.
+ */
+ @Test
+ void calculateInputCommand_isExit_expectException() {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputString = "/epm 29/02/2020";
+
+ assertEquals(false, new CalculateInputCommand(inputString, user).isExit());
+
+ }
+
+ public void tearDown() {
+ System.setOut(stdout);
+ }
+}
diff --git a/src/test/java/seedu/mindmymoney/command/DeleteCommandTest.java b/src/test/java/seedu/mindmymoney/command/DeleteCommandTest.java
new file mode 100644
index 0000000000..df4b4aee86
--- /dev/null
+++ b/src/test/java/seedu/mindmymoney/command/DeleteCommandTest.java
@@ -0,0 +1,345 @@
+package seedu.mindmymoney.command;
+
+import org.junit.jupiter.api.Test;
+import seedu.mindmymoney.MindMyMoneyException;
+import seedu.mindmymoney.data.CreditCardList;
+import seedu.mindmymoney.data.ExpenditureList;
+import seedu.mindmymoney.data.IncomeList;
+import seedu.mindmymoney.userfinancial.CreditCard;
+import seedu.mindmymoney.userfinancial.Expenditure;
+import seedu.mindmymoney.userfinancial.Income;
+import seedu.mindmymoney.userfinancial.User;
+
+import java.util.ArrayList;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static seedu.mindmymoney.constants.Indexes.INDEX_OF_FIRST_ITEM;
+
+/**
+ * Represents the tests for delete command.
+ */
+class DeleteCommandTest {
+
+ /**
+ * Asserts that the correct item has been deleted.
+ *
+ * @throws MindMyMoneyException if incorrect item has been deleted.
+ */
+ @Test
+ void deleteCommand_oneExpenditureInput_expectExpenditureListUpdated() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputString = "/e /pm cash /c Personal /d Nike Shoes /a 300 /t 30/03/2022";
+ new AddCommand(inputString, user).executeCommand();
+
+ ArrayList testList = new ArrayList<>();
+ testList.add(new Expenditure("cash", "Personal", "Nike Shoes",
+ 300, "30/03/2022"));
+
+ String deleteInputString = "delete /e 1";
+ new DeleteCommand(deleteInputString, user).executeCommand();
+ testList.remove(0);
+
+ assertEquals(testList.size(), expenditureTestList.size());
+ }
+
+ /**
+ * Asserts that the credit card balance will be updated upon successful deletion.
+ *
+ * @throws MindMyMoneyException if incorrect item has been deleted.
+ */
+ @Test
+ void deleteCommand_creditCardAndExpenditureInput_expectOriginalCreditBalance() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputStringCC = "/cc /n DBS /cb 1.5 /cl 500";
+ new AddCommand(inputStringCC, user).executeCommand();
+ String inputStringExpenditure = "/e /pm DBS /c Personal /d Nike Shoes /a 300 /t 30/03/2022";
+ new AddCommand(inputStringExpenditure, user).executeCommand();
+ assertEquals(200.0, creditCardTestList.get(0).getBalanceLeft());
+ String deleteInputString = "delete /e 1";
+ new DeleteCommand(deleteInputString, user).executeCommand();
+ assertEquals(500.0, creditCardTestList.get(0).getBalanceLeft());
+ }
+
+ /**
+ * Asserts that the correct item has been deleted.
+ */
+ @Test
+ void deleteCommand_oneCreditCardInput_expectCreditCardListUpdated() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputString = "/cc /n DBS /cb 1.5 /cl 500";
+ new AddCommand(inputString, user).executeCommand();
+
+ ArrayList testList = new ArrayList<>();
+ testList.add(new CreditCard("DBS", 1.5, 500));
+
+ String deleteInputString = "delete /cc 1";
+ new DeleteCommand(deleteInputString, user).executeCommand();
+ testList.remove(0);
+
+ assertEquals(testList.size(), expenditureTestList.size());
+ }
+
+ /**
+ * Asserts that the correct income entry is deleted.
+ *
+ * @throws MindMyMoneyException when an invalid command is received.
+ */
+ @Test
+ void deleteCommand_oneIncomeInput_expectIncomeListUpdated() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+
+ String inputString = "/i /a 3000 /c salary";
+ new AddCommand(inputString, user).executeCommand();
+
+ ArrayList testList = new ArrayList<>();
+ testList.add(new Income(3000, "Salary"));
+
+ String deleteInputString = "delete /i 1";
+ new DeleteCommand(deleteInputString, user).executeCommand();
+ testList.remove(INDEX_OF_FIRST_ITEM);
+
+ assertEquals(testList.size(), expenditureTestList.size());
+ }
+
+ /**
+ * Asserts if the index input is out of bounds.
+ */
+ @Test
+ void deleteCommand_wrongExpenditureInputValue_expectException() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputString = "/e /pm cash /c Personal /d Nike Shoes /a 300 /t 30/03/2022";
+
+ new AddCommand(inputString, user).executeCommand();
+ String deleteInputString = "delete /e -1";
+ assertThrows(MindMyMoneyException.class, () -> new DeleteCommand(deleteInputString, user).executeCommand());
+ String delInputString2 = "delete /e 5";
+ assertThrows(MindMyMoneyException.class, () -> new DeleteCommand(delInputString2, user).executeCommand());
+ }
+
+ /**
+ * Asserts if the index input is in the correct number format.
+ */
+ @Test
+ void deleteCommand_wrongExpenditureInputFormat_expectException() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputString = "/e /pm cash /c Personal /d Nike Shoes /a 300 /t 30/03/2022";
+
+ new AddCommand(inputString, user).executeCommand();
+ String delInputString = "delete /e ONE";
+
+ assertThrows(MindMyMoneyException.class, () -> new DeleteCommand(delInputString, user).executeCommand());
+ }
+
+ /**
+ * Asserts if there is a missing index input in the command.
+ */
+ @Test
+ void deleteCommand_missingExpenditureInput_expectException() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputString = "/e /pm cash /c Personal /d Nike Shoes /a 300 /t 30/03/2022";
+
+ new AddCommand(inputString, user).executeCommand();
+ String deleteInputString = "delete /e";
+ String deleteInputString2 = "delete /e";
+
+ assertThrows(MindMyMoneyException.class, () -> new DeleteCommand(deleteInputString, user).executeCommand());
+ assertThrows(MindMyMoneyException.class, () -> new DeleteCommand(deleteInputString2, user).executeCommand());
+ }
+
+ /**
+ * Asserts if user is able to delete from an empty expenditure list.
+ */
+ @Test
+ void deleteCommand_deleteFromEmptyExpenditureList_expectException() {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String deleteInputString = "delete /e 1";
+
+ assertThrows(MindMyMoneyException.class, () -> new DeleteCommand(deleteInputString, user).executeCommand());
+ }
+
+ /**
+ * Asserts if user is able to delete from an empty credit card list.
+ */
+ @Test
+ void deleteCommand_deleteFromEmptyCreditCardList_expectException() {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String deleteInputString = "delete /cc 1";
+
+ assertThrows(MindMyMoneyException.class, () -> new DeleteCommand(deleteInputString, user).executeCommand());
+ }
+
+ /**
+ * Asserts if user is able to delete from an empty income list.
+ */
+ @Test
+ void deleteCommand_deleteFromEmptyIncomeList_expectException() {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String deleteInputString = "delete /i 1";
+
+ assertThrows(MindMyMoneyException.class, () -> new DeleteCommand(deleteInputString, user).executeCommand());
+ }
+
+ /**
+ * Asserts if there is a missing index input in the command.
+ */
+ @Test
+ void deleteCommand_missingCreditCardInput_expectException() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputString = "/cc /n DBS /cb 1.5 /cl 500";
+
+ new AddCommand(inputString, user).executeCommand();
+ String deleteInputString = "delete /cc";
+
+ assertThrows(MindMyMoneyException.class, () -> new DeleteCommand(deleteInputString, user).executeCommand());
+ }
+
+ /**
+ * Asserts if there is a missing index input in the command.
+ */
+ @Test
+ void deleteCommand_missingIncomeInput_expectException() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String deleteInputString = "delete /i";
+ String inputString = "/i /a 3000 /c salary";
+ new AddCommand(inputString, user).executeCommand();
+
+ assertThrows(MindMyMoneyException.class, () -> new DeleteCommand(deleteInputString, user).executeCommand());
+ }
+
+ /**
+ * Asserts if the credit card index input is out of bounds.
+ */
+ @Test
+ void deleteCommand_wrongCreditCardInputValue_expectException() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputString = "/cc /n DBS /cb 1.5 /cl 500";
+
+ new AddCommand(inputString, user).executeCommand();
+ String deleteInputString = "delete /cc -1";
+ assertThrows(MindMyMoneyException.class, () -> new DeleteCommand(deleteInputString, user).executeCommand());
+ String delInputString2 = "delete /cc 3";
+ assertThrows(MindMyMoneyException.class, () -> new DeleteCommand(delInputString2, user).executeCommand());
+ }
+
+ /**
+ * Asserts if the credit card index input can be non-numerical.
+ */
+ @Test
+ void deleteCommand_nonNumericalCreditCardIndexInput_expectException() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputString = "/cc /n DBS /cb 1.5 /cl 500";
+
+ new AddCommand(inputString, user).executeCommand();
+ String deleteInputString = "delete /cc asd";
+ assertThrows(MindMyMoneyException.class, () -> new DeleteCommand(deleteInputString, user).executeCommand());
+ }
+
+ /**
+ * Asserts if the income index input is out of bounds.
+ */
+ @Test
+ void deleteCommand_wrongIncomeInputValue_expectException() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputString = "/i /a 3000 /c salary";
+
+ new AddCommand(inputString, user).executeCommand();
+ String deleteInputString = "delete /i -1";
+ assertThrows(MindMyMoneyException.class, () -> new DeleteCommand(deleteInputString, user).executeCommand());
+ String delInputString2 = "delete /i 3";
+ assertThrows(MindMyMoneyException.class, () -> new DeleteCommand(delInputString2, user).executeCommand());
+ }
+
+ /**
+ * Asserts if there is a missing flag in the command.
+ */
+ @Test
+ void deleteCommand_missingFlag_expectException() {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String deleteInputString = "delete";
+
+ assertThrows(MindMyMoneyException.class, () -> new DeleteCommand(deleteInputString, user).executeCommand());
+ }
+
+ /**
+ * Asserts if the income index input can be not a number.
+ */
+ @Test
+ void deleteCommand_nonNumericalIncomeIndexInput_expectException() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputString = "/i /a 3000 /c salary";
+ new AddCommand(inputString, user).executeCommand();
+
+ String deleteInputString = "delete /i abc";
+ assertThrows(MindMyMoneyException.class, () -> new DeleteCommand(deleteInputString, user).executeCommand());
+ }
+
+ /**
+ * Test if program is able to exit.
+ */
+ @Test
+ void deleteCommand_isExit_expectFalse() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputString = "/i /a 3000 /c salary";
+ new AddCommand(inputString, user).executeCommand();
+
+ String deleteInputString = "delete /i 1";
+ assertEquals(false, new DeleteCommand(deleteInputString, user).isExit());
+
+ }
+
+}
diff --git a/src/test/java/seedu/mindmymoney/command/HelpCommandTest.java b/src/test/java/seedu/mindmymoney/command/HelpCommandTest.java
new file mode 100644
index 0000000000..c1f288f5a0
--- /dev/null
+++ b/src/test/java/seedu/mindmymoney/command/HelpCommandTest.java
@@ -0,0 +1,163 @@
+package seedu.mindmymoney.command;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import seedu.mindmymoney.MindMyMoneyException;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+ * Represents the tests for Help command.
+ */
+public class HelpCommandTest {
+ private final ByteArrayOutputStream capturedOut = new ByteArrayOutputStream();
+ private final PrintStream stdout = System.out;
+
+ @BeforeEach
+ public void setUp() {
+ System.setOut(new PrintStream(capturedOut));
+ }
+
+ /**
+ * Asserts that the expenditure help page will be printed when the user requests for it.
+ */
+ @Test
+ void helpCommand_fromUser_expectExpenditureHelpPage() throws MindMyMoneyException {
+ String helpPage = "---------------------------------------Expenditure Help Page------------------------"
+ + "---------------\n"
+ + "1. Listing all Expenditures: list /e {DATE}\n"
+ + "2. Adding an Expenditure entry: add /e /pm [PAYMENT_METHOD] /c [CATEGORY] "
+ + "/d [DESCRIPTION] /a [AMOUNT] /t [DATE]\n"
+ + "3. Calculating the total expenditure in a month: calculate /epm [DATE]\n"
+ + "4. Updating an Expenditure: update /e [NEW_INDEX] /pm [NEW_PAYMENT_METHOD] /c [NEW_CATEGORY] "
+ + "/d [NEW_DESCRIPTION] /a [NEW_AMOUNT] /t [NEW_DATE]\n"
+ + "5. Removing an Expenditure entry: delete /e [INDEX]\n"
+ + "6. Exiting the program: bye\n"
+ + "----------------------------------------------------------------------------------------------"
+ + "-----\n";
+
+ new HelpCommand(true, "/e").executeCommand();
+ assertEquals(helpPage.trim(), capturedOut.toString().trim());
+ }
+
+ /**
+ * Asserts that the error message will be printed if an invalid command is received.
+ */
+ @Test
+ void helpCommand_notFromUser_expectErrorMessage() throws MindMyMoneyException {
+ String errorMessage = "Invalid command! \n"
+ + "Type \"help /e\" to view the list of supported expenditure commands\n"
+ + "Type \"help /cc\" to view the list of supported Credit Card commands\n"
+ + "Type \"help /i\" to view the list of supported income commands\n";
+
+ new HelpCommand(false, "/e").executeCommand();
+ assertEquals(errorMessage.trim(), capturedOut.toString().trim());
+ }
+
+ /**
+ * Asserts that the income help page will be printed when the user requests for it.
+ */
+ @Test
+ void helpCommand_incomeFlag_expectIncomeHelpPage() throws MindMyMoneyException {
+ String helpPage = "--------------------------------Income Help Page------------------------------"
+ + "---------\n"
+ + "1. Listing all Incomes: list /i\n"
+ + "2. Adding an Income entry: add /i /a [AMOUNT] /c [CATEGORY]\n"
+ + "3. Updating an Income entry: update /i [INDEX] /a [NEW_AMOUNT] /c [NEW_CATEGORY]\n"
+ + "4. Removing an Income entry: delete /i [INDEX]\n"
+ + "---------------------------------------------------------------------------------------\n";
+
+ new HelpCommand(true, "/i").executeCommand();
+ assertEquals(helpPage.trim(), capturedOut.toString().trim());
+ }
+
+ /**
+ * Asserts that the credi card help page will be printed when the user requests for it.
+ */
+ @Test
+ void helpCommand_creditCardFlag_expectCreditHelpPage() throws MindMyMoneyException {
+ String helpPage = "---------------------------------------Credit Card Help Page--------------------------"
+ + "-------------\n"
+ + "1. Listing all Credit Cards: list /cc\n"
+ + "2. Adding a Credit Card: add /cc /n [CREDIT_CARD_NAME] /cb [CASHBACK] /cl [CREDIT_LIMIT]\n"
+ + "3. Updating a Credit Card: update /cc [INDEX] /n [NEW_NAME] /cb [NEW_CASHBACK] "
+ + "/cl [NEW_CREDIT_LIMIT]\n"
+ + "4. Removing a credit card: delete /cc [INDEX]\n"
+ + "5. Exiting the program: bye\n"
+ + "-----------------------------------------------------------------------------------------------"
+ + "----\n";
+
+ new HelpCommand(true, "/cc").executeCommand();
+ assertEquals(helpPage.trim(), capturedOut.toString().trim());
+ }
+
+ /**
+ * Asserts that the error message will be printed when incorrect flag is provided.
+ */
+ @Test
+ void helpCommand_invalidFlag_expectException() throws MindMyMoneyException {
+ assertThrows(MindMyMoneyException.class,
+ () -> new HelpCommand(true, "/abc").executeCommand());
+ }
+
+ /**
+ * Test if program is able to exit.
+ */
+ @Test
+ void helpCommand_isExit_expectFalse() {
+ assertEquals(false, new HelpCommand(true, "").isExit());
+
+ }
+
+ /**
+ * Asserts that the all help pages will be printed when the user requests for it.
+ */
+ @Test
+ void helpCommand_fromUser_expectAllHelpPages() throws MindMyMoneyException {
+ String helpPage = "---------------------------------------Expenditure Help Page------------------------"
+ + "---------------\n"
+ + "1. Listing all Expenditures: list /e {DATE}\n"
+ + "2. Adding an Expenditure entry: add /e /pm [PAYMENT_METHOD] /c [CATEGORY] /d [DESCRIPTION] "
+ + "/a [AMOUNT] /t [DATE]\n"
+ + "3. Calculating the total expenditure in a month: calculate /epm [DATE]\n"
+ + "4. Updating an Expenditure: update /e [NEW_INDEX] /pm [NEW_PAYMENT_METHOD] /c [NEW_CATEGORY] "
+ + "/d [NEW_DESCRIPTION] /a [NEW_AMOUNT] /t [NEW_DATE]\n"
+ + "5. Removing an Expenditure entry: delete /e [INDEX]\n"
+ + "6. Exiting the program: bye\n"
+ + "----------------------------------------------------------------------------------------------"
+ + "-----\n"
+ + System.lineSeparator()
+ + "---------------------------------------Credit Card Help Page--------------------------"
+ + "-------------\n"
+ + "1. Listing all Credit Cards: list /cc\n"
+ + "2. Adding a Credit Card: add /cc /n [CREDIT_CARD_NAME] /cb [CASHBACK] /cl [CREDIT_LIMIT]\n"
+ + "3. Updating a Credit Card: update /cc [INDEX] /n [NEW_NAME] /cb [NEW_CASHBACK] "
+ + "/cl [NEW_CREDIT_LIMIT]\n"
+ + "4. Removing a credit card: delete /cc [INDEX]\n"
+ + "5. Exiting the program: bye\n"
+ + "-----------------------------------------------------------------------------------------------"
+ + "----\n"
+ + System.lineSeparator()
+ + "--------------------------------Income Help Page------------------------------"
+ + "---------\n"
+ + "1. Listing all Incomes: list /i\n"
+ + "2. Adding an Income entry: add /i /a [AMOUNT] /c [CATEGORY]\n"
+ + "3. Updating an Income entry: update /i [INDEX] /a [NEW_AMOUNT] /c [NEW_CATEGORY]\n"
+ + "4. Removing an Income entry: delete /i [INDEX]\n"
+ + "---------------------------------------------------------------------------------------\n"
+ + System.lineSeparator();
+
+ new HelpCommand(true, "").executeCommand();
+ assertEquals(helpPage.trim(), capturedOut.toString().trim());
+ }
+
+ @AfterEach
+ public void tearDown() {
+ System.setOut(stdout);
+ }
+}
diff --git a/src/test/java/seedu/mindmymoney/command/ListCommandTest.java b/src/test/java/seedu/mindmymoney/command/ListCommandTest.java
new file mode 100644
index 0000000000..9637a81ff3
--- /dev/null
+++ b/src/test/java/seedu/mindmymoney/command/ListCommandTest.java
@@ -0,0 +1,331 @@
+package seedu.mindmymoney.command;
+
+import org.junit.jupiter.api.Test;
+import seedu.mindmymoney.MindMyMoneyException;
+import seedu.mindmymoney.data.CreditCardList;
+import seedu.mindmymoney.data.ExpenditureList;
+import seedu.mindmymoney.data.IncomeList;
+import seedu.mindmymoney.userfinancial.User;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class ListCommandTest {
+ private final ByteArrayOutputStream capturedOut = new ByteArrayOutputStream();
+ private final PrintStream stdout = System.out;
+
+ public void setUp() {
+ System.setOut(new PrintStream(capturedOut));
+ }
+
+ /**
+ * Tests list command with no date on a non-empty list. Prints list of size 1 first,
+ * followed by list of size 2, to check for formatting.
+ */
+ @Test
+ void listToString_normalInputsWithNoDate_expectString() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+
+ String firstInputString = "/e /pm cash /c Personal /d Nike Shoes /a 300 /t 30/03/2022";
+ new AddCommand(firstInputString, user).executeCommand();
+ String listInString = new ListCommand("/e", user).expenditureListToString();
+ assertEquals("-----------------------------------------------" + System.lineSeparator()
+ + "1. $300.00 was spent on Nike Shoes(Personal) using Cash [30/03/2022]\n"
+ + "-----------------------------------------------" + System.lineSeparator(), listInString);
+
+ String secondInputString = "/e /pm cash /c Food /d Cream Pie /a 69 /t 30/03/2022";
+ new AddCommand(secondInputString, user).executeCommand();
+ listInString = new ListCommand("/e", user).expenditureListToString();
+ assertEquals("-----------------------------------------------" + System.lineSeparator()
+ + "1. $300.00 was spent on Nike Shoes(Personal) using Cash [30/03/2022]\n"
+ + "2. $69.00 was spent on Cream Pie(Food) using Cash [30/03/2022]\n"
+ + "-----------------------------------------------" + System.lineSeparator(), listInString);
+
+ String listInString2 = new ListCommand("/e ", user).expenditureListToString();
+ assertEquals("-----------------------------------------------" + System.lineSeparator()
+ + "1. $300.00 was spent on Nike Shoes(Personal) using Cash [30/03/2022]\n"
+ + "2. $69.00 was spent on Cream Pie(Food) using Cash [30/03/2022]\n"
+ + "-----------------------------------------------" + System.lineSeparator(), listInString2);
+ }
+
+ /**
+ * Tests list command with exact date, month, year on a non-empty list. Prints list of size 1 first,
+ * followed by list of size 2, to check for formatting.
+ */
+ @Test
+ void listToString_normalInputsWithExactDate_expectString() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+
+ String firstInputString = "/e /pm cash /c Personal /d Nike Shoes /a 300 /t 30/03/2022";
+ new AddCommand(firstInputString, user).executeCommand();
+ String listInString = new ListCommand("/e 30/03/2022", user).expenditureListToString();
+ assertEquals("-----------------------------------------------" + System.lineSeparator()
+ + "1. $300.00 was spent on Nike Shoes(Personal) using Cash [30/03/2022]\n"
+ + "-----------------------------------------------" + System.lineSeparator(), listInString);
+
+ String secondInputString = "/e /pm cash /c Food /d Cream Pie /a 69 /t 30/03/2022";
+ new AddCommand(secondInputString, user).executeCommand();
+
+ String inputString3 = "/e /pm cash /c Food /d Cream Pie /a 69 /t 01/04/2022";
+
+ new AddCommand(inputString3, user).executeCommand();
+ listInString = new ListCommand("/e 30/03/2022", user).expenditureListToString();
+ assertEquals("-----------------------------------------------" + System.lineSeparator()
+ + "1. $300.00 was spent on Nike Shoes(Personal) using Cash [30/03/2022]\n"
+ + "2. $69.00 was spent on Cream Pie(Food) using Cash [30/03/2022]\n"
+ + "-----------------------------------------------" + System.lineSeparator(), listInString);
+ }
+
+ /**
+ * Tests list command with exact month, year on a non-empty list. Prints list of size 1 first,
+ * followed by list of size 2, to check for formatting.
+ */
+ @Test
+ void listToString_normalInputsWithExactMonth_expectString() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+
+ String inputString = "/e /pm cash /c Personal /d Nike Shoes /a 300 /t 30/03/2022";
+ new AddCommand(inputString, user).executeCommand();
+ String listInString = new ListCommand("/e 03/2022", user).expenditureListToString();
+ assertEquals("-----------------------------------------------" + System.lineSeparator()
+ + "1. $300.00 was spent on Nike Shoes(Personal) using Cash [30/03/2022]\n"
+ + "-----------------------------------------------" + System.lineSeparator(), listInString);
+
+ String secondInputString = "/e /pm cash /c Food /d Cream Pie /a 69 /t 30/03/2022";
+ new AddCommand(secondInputString, user).executeCommand();
+
+ String thirdInputString = "/e /pm cash /c Food /d Cream Pie /a 69 /t 01/04/2022";
+
+ new AddCommand(thirdInputString, user).executeCommand();
+ listInString = new ListCommand("/e 03/2022", user).expenditureListToString();
+ assertEquals("-----------------------------------------------" + System.lineSeparator()
+ + "1. $300.00 was spent on Nike Shoes(Personal) using Cash [30/03/2022]\n"
+ + "2. $69.00 was spent on Cream Pie(Food) using Cash [30/03/2022]\n"
+ + "-----------------------------------------------" + System.lineSeparator(), listInString);
+ }
+
+ /**
+ * Tests list command with exact year on a non-empty list. Prints list of size 1 first,
+ * followed by list of size 2, to check for formatting.
+ */
+ @Test
+ void listToString_normalInputsWithExactYear_expectString() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+
+ String inputString = "/e /pm cash /c Personal /d Nike Shoes /a 300 /t 30/03/2022";
+ new AddCommand(inputString, user).executeCommand();
+ String listInString = new ListCommand("/e 2022", user).expenditureListToString();
+ assertEquals("-----------------------------------------------" + System.lineSeparator()
+ + "1. $300.00 was spent on Nike Shoes(Personal) using Cash [30/03/2022]\n"
+ + "-----------------------------------------------" + System.lineSeparator(), listInString);
+
+ String secondInputString = "/e /pm cash /c Food /d Cream Pie /a 69 /t 30/03/2022";
+ new AddCommand(secondInputString, user).executeCommand();
+
+ String thirdInputString = "/e /pm cash /c Food /d Cream Pie /a 69 /t 01/04/2021";
+
+ new AddCommand(thirdInputString, user).executeCommand();
+ listInString = new ListCommand("/e 2022", user).expenditureListToString();
+ assertEquals("-----------------------------------------------" + System.lineSeparator()
+ + "1. $300.00 was spent on Nike Shoes(Personal) using Cash [30/03/2022]\n"
+ + "2. $69.00 was spent on Cream Pie(Food) using Cash [30/03/2022]\n"
+ + "-----------------------------------------------" + System.lineSeparator(), listInString);
+ }
+
+ /**
+ * Tests list command for expenditure on an empty list. Should expect an exception thrown.
+ */
+ @Test
+ void listCommand_emptyList_expectException() {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ ListCommand listCommandTest = new ListCommand("/e", user);
+
+ assertThrows(MindMyMoneyException.class, () -> listCommandTest.executeCommand());
+ }
+
+ /**
+ * Tests list command for date, month and year on an empty list. Should expect an exception thrown.
+ */
+ @Test
+ void listCommand_wrongDateFormat_expectException() {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ ListCommand listCommandTest = new ListCommand("/e 39/14/2022", user);
+
+ assertThrows(MindMyMoneyException.class, () -> listCommandTest.executeCommand());
+ }
+
+ /**
+ * Tests list command for month and year on an empty list. Should expect an exception thrown.
+ */
+ @Test
+ void listCommand_wrongMonthFormat_expectException() {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ ListCommand listCommandTest = new ListCommand("/e 4/2022", user);
+
+ assertThrows(MindMyMoneyException.class, () -> listCommandTest.executeCommand());
+ }
+
+ /**
+ * Tests list command for year on an empty list. Should expect an exception thrown.
+ */
+ @Test
+ void listCommand_wrongYearFormat_expectException() {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ ListCommand listCommandTest = new ListCommand("/e /2022/", user);
+
+ assertThrows(MindMyMoneyException.class, () -> listCommandTest.executeCommand());
+ }
+
+ /**
+ * Test if program is able to exit. Should return false.
+ */
+ @Test
+ void listCommand_isExit_expectFalse() {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+
+ assertEquals(false, new ListCommand("/e", user).isExit());
+
+ }
+
+ /**
+ * Asserts if user is able to list command for credit card flag input.
+ */
+ @Test
+ void listCommand_normalCreditCardInputs_expectString() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+
+ String firstInputString = "/cc /n DBS /cb 1.5 /cl 1000";
+ new AddCommand(firstInputString, user).executeCommand();
+ String secondInputString = "/cc /n POSB /cb 1.5 /cl 20";
+ new AddCommand(secondInputString, user).executeCommand();
+ setUp();
+ new ListCommand("/cc", user).executeCommand();
+ tearDown();
+ String expectedOutput = "-----------------------------------------------"
+ + System.lineSeparator()
+ + "1. Name: DBS [Cashback: 1.50%] [Cashback gained: $0.00] [Card limit: $1000.00] "
+ + "[Balance left: $1000.00]\n"
+ + "2. Name: POSB [Cashback: 1.50%] [Cashback gained: $0.00] [Card limit: $20.00] [Balance left: $20.00]\n"
+ + "-----------------------------------------------";
+ assertEquals(expectedOutput.trim(), capturedOut.toString().trim());
+ }
+
+ /**
+ * Tests list command for credit card flag input with empty list. Should expect an exception thrown.
+ */
+ @Test
+ void listCommand_emptyListCreditCardInput_expectException() {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ assertThrows(MindMyMoneyException.class,
+ () -> new ListCommand("/c", user).printCreditCardList());
+
+ }
+
+ /**
+ * Tests list command for income flag input with empty list. Should expect an exception thrown.
+ */
+ @Test
+ void listCommand_emptyListIncomeInput_expectException() {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ assertThrows(MindMyMoneyException.class,
+ () -> new ListCommand("/i", user).printIncomeList());
+
+ }
+
+ /**
+ * Asserts if user is able to list command for income flag input. Should expect a print message.
+ */
+ @Test
+ void listCommand_normalIncomeInput_expectString() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+
+ String firstInputString = "/i /a 3000 /c Salary";
+ new AddCommand(firstInputString, user).executeCommand();
+ String secondInputString = "/i /a 300 /c Salary";
+ new AddCommand(secondInputString, user).executeCommand();
+ setUp();
+ new ListCommand("/i", user).executeCommand();
+ tearDown();
+ String expectedOutput = "-----------------------------------------------"
+ + System.lineSeparator()
+ + "1. Amount: $3000\n"
+ + " Category: Salary\n"
+ + "2. Amount: $300\n"
+ + " Category: Salary\n"
+ + "-----------------------------------------------"
+ + System.lineSeparator();
+ assertEquals(expectedOutput.trim(), capturedOut.toString().trim());
+ }
+
+ /**
+ * Tests user is able to list the expenditure list. Should print out expenditure list.
+ */
+ @Test
+ void listCommand_normalExpenditureInput_expectString() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+
+ String firstInputString = "/e /pm cash /c Personal /d Nike Shoes /a 300 /t 30/03/2022";
+ new AddCommand(firstInputString, user).executeCommand();
+ String secondInputString = "/e /pm cash /c Food /d Cream Pie /a 69 /t 30/03/2021";
+ new AddCommand(secondInputString, user).executeCommand();
+ setUp();
+ new ListCommand("/e", user).executeCommand();
+ tearDown();
+ String expectedOutput = "-----------------------------------------------"
+ + System.lineSeparator()
+ + "1. $300.00 was spent on Nike Shoes(Personal) using Cash [30/03/2022]\n"
+ + "2. $69.00 was spent on Cream Pie(Food) using Cash [30/03/2021]\n"
+ + "-----------------------------------------------"
+ + System.lineSeparator();
+ assertEquals(expectedOutput.trim(), capturedOut.toString().trim());
+ }
+
+ public void tearDown() {
+ System.setOut(stdout);
+ }
+}
diff --git a/src/test/java/seedu/mindmymoney/command/UpdateCommandTest.java b/src/test/java/seedu/mindmymoney/command/UpdateCommandTest.java
new file mode 100644
index 0000000000..eba4beffc3
--- /dev/null
+++ b/src/test/java/seedu/mindmymoney/command/UpdateCommandTest.java
@@ -0,0 +1,344 @@
+package seedu.mindmymoney.command;
+
+import org.junit.jupiter.api.Test;
+import seedu.mindmymoney.MindMyMoneyException;
+import seedu.mindmymoney.data.CreditCardList;
+import seedu.mindmymoney.data.ExpenditureList;
+import seedu.mindmymoney.data.IncomeList;
+import seedu.mindmymoney.userfinancial.CreditCard;
+import seedu.mindmymoney.userfinancial.Expenditure;
+import seedu.mindmymoney.userfinancial.Income;
+import seedu.mindmymoney.userfinancial.User;
+
+import static org.junit.jupiter.api.Assertions.fail;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static seedu.mindmymoney.constants.Indexes.INDEX_OF_FIRST_ITEM;
+
+public class UpdateCommandTest {
+ /**
+ * Assert that the command can update the expenditure list.
+ */
+ @Test
+ void updateExpenditureCommand_updateExpenditure_listUpdated() {
+ Expenditure testExpenditure = new Expenditure("Cash", "Food",
+ "porridge", 5, "01/04/2022");
+ Expenditure newExpenditure = new Expenditure("Cash", "Others",
+ "chicken rice", (float) 4.50, "01/05/2021");
+ User testUser = new User();
+ testUser.setExpenditureListArray(new ExpenditureList());
+ testUser.getExpenditureListArray().add(testExpenditure);
+ String input = "/e 1 /pm cash /c Others /d chicken rice /a 4.50 /t 01/05/2021";
+ UpdateCommand updateCommand = new UpdateCommand(input, testUser);
+ try {
+ updateCommand.executeCommand();
+ assertEquals(testUser.getExpenditureListArray().get(INDEX_OF_FIRST_ITEM),
+ newExpenditure);
+ } catch (MindMyMoneyException e) {
+ System.out.println(e.getMessage());
+ fail();
+ }
+ }
+
+ /**
+ * Assert that an invalid update expenditure command will throw an exception.
+ */
+ @Test
+ void updateExpenditureCommand_invalidInput_exceptionThrown() {
+ Expenditure testExpenditure = new Expenditure("Cash", "Food",
+ "porridge", 5, "01/03/2022");
+ User testUser = new User();
+ testUser.setExpenditureListArray(new ExpenditureList());
+ testUser.getExpenditureListArray().add(testExpenditure);
+ String input = "invalid input";
+ UpdateCommand updateCommand = new UpdateCommand(input, testUser);
+ assertThrows(MindMyMoneyException.class, updateCommand::executeCommand);
+ }
+
+ /**
+ * Assert that an invalid update expenditure command will throw an exception.
+ */
+ @Test
+ void updateExpenditureCommand_invalidDate_exceptionThrown() {
+ Expenditure testExpenditure = new Expenditure("Cash", "Food",
+ "porridge", 5, "01/03/2022");
+ User testUser = new User();
+ testUser.setExpenditureListArray(new ExpenditureList());
+ testUser.getExpenditureListArray().add(testExpenditure);
+ String firstInputString = "1 /pm cash /c Personal /d Nike Shoes /a 500 /t 01/4/2022";
+ assertThrows(MindMyMoneyException.class,
+ () -> new AddCommand(firstInputString, testUser).executeCommand());
+ String secondInputString = "1 /pm cash /c Personal /d Nike Shoes /a 500 /t 04/2022";
+
+ assertThrows(MindMyMoneyException.class,
+ () -> new AddCommand(secondInputString, testUser).executeCommand());
+ String thirdInputString = "1 /pm cash /c Personal /d Nike Shoes /a 500 /t 2022";
+
+ assertThrows(MindMyMoneyException.class,
+ () -> new AddCommand(thirdInputString, testUser).executeCommand());
+
+ String fourthInputString = "1 /pm cash /c Personal /d Nike Shoes /a 500 /t 38/14/2022";
+ assertThrows(MindMyMoneyException.class,
+ () -> new AddCommand(fourthInputString, testUser).executeCommand());
+
+ String fifthInputString = "1 /pm cash /c Food /d Porridge /a 4.50 /t 31/11/2021";
+ assertThrows(MindMyMoneyException.class,
+ () -> new AddCommand(fifthInputString, testUser).executeCommand());
+
+ String sixthInputString = "1 /pm cash /c Food /d Porridge /a 4.50 /t 29/02/2021";
+ assertThrows(MindMyMoneyException.class,
+ () -> new AddCommand(sixthInputString, testUser).executeCommand());
+
+ String seventhInputString = "1 /pm cash /c Food /d Porridge /a 4.50 /t 30/02/2020";
+ assertThrows(MindMyMoneyException.class,
+ () -> new AddCommand(seventhInputString, testUser).executeCommand());
+
+ String eighthInputString = "1 /pm cash /c Food /d Porridge /a 4.50 /t 31/04/2020";
+ assertThrows(MindMyMoneyException.class,
+ () -> new AddCommand(eighthInputString, testUser).executeCommand());
+ }
+
+ /**
+ * Assert that when the update command fields are similar to the expenditure in the list, an exception is thrown.
+ */
+ @Test
+ void updateExpenditureCommand_updateFieldSimilarToExpenditureInList_exceptionThrown() {
+ Expenditure testExpenditure = new Expenditure("Cash", "Food",
+ "porridge", 5, "01/04/2022");
+ User testUser = new User();
+ testUser.setExpenditureListArray(new ExpenditureList());
+ testUser.getExpenditureListArray().add(testExpenditure);
+ String input = "/e 1 /pm cash /c food /d porridge /a 5 /t 01/04/2022";
+ UpdateCommand updateCommand = new UpdateCommand(input, testUser);
+ assertThrows(MindMyMoneyException.class, updateCommand::executeCommand);
+ }
+
+ /**
+ * Assert that the command can update the credit card list.
+ */
+ @Test
+ void updateCreditCardCommand_updateCreditCard_listUpdated() {
+ CreditCard testCreditCard = new CreditCard("UOB", 2, 1000);
+ CreditCard newCreditCard = new CreditCard("DBS", 5, 2000);
+ User testUser = new User();
+ testUser.setCreditCardListArray(new CreditCardList());
+ testUser.getCreditCardListArray().add(testCreditCard);
+ String input = "/cc 1 /n DBS /cb 5 /cl 2000";
+ UpdateCommand updateCommand = new UpdateCommand(input, testUser);
+ try {
+ updateCommand.executeCommand();
+ assertEquals(testUser.getCreditCardListArray().get(INDEX_OF_FIRST_ITEM),
+ newCreditCard);
+ } catch (MindMyMoneyException e) {
+ System.out.println(e.getMessage());
+ fail();
+ }
+ }
+
+ /**
+ * Assert that an invalid update credit card command will throw an exception.
+ */
+ @Test
+ void updateCreditCardCommand_invalidInput_exceptionThrown() {
+ CreditCard testCreditCard = new CreditCard("DBS", 2, 1000);
+ User testUser = new User();
+ testUser.setCreditCardListArray(new CreditCardList());
+ testUser.getCreditCardListArray().add(testCreditCard);
+ String input = "/cc invalid input";
+ UpdateCommand updateCommand = new UpdateCommand(input, testUser);
+ assertThrows(MindMyMoneyException.class, updateCommand::executeCommand);
+ }
+
+ /**
+ * Assert that when the update command fields are similar to the credit card in the list, an exception is thrown.
+ */
+ @Test
+ void updateCreditCardCommand_updateFieldSimilarToCreditCardInList_exceptionThrown() {
+ CreditCard testCreditCard = new CreditCard("DBS", 2, 1000);
+ User testUser = new User();
+ testUser.setCreditCardListArray(new CreditCardList());
+ testUser.getCreditCardListArray().add(testCreditCard);
+ String input = "/cc 1 /n DBS /cb 2 /cl 1000";
+ UpdateCommand updateCommand = new UpdateCommand(input, testUser);
+ assertThrows(MindMyMoneyException.class, updateCommand::executeCommand);
+ }
+
+ /**
+ * Assert that when the update command credit card limit is smaller than total expenditure on card,
+ * an exception is thrown.
+ */
+ @Test
+ void updateCreditCardCommand_newCreditCardLimitSmallerThanExpenditure_exceptionThrown()
+ throws MindMyMoneyException {
+ CreditCard testCreditCard = new CreditCard("DBS", 2, 1000);
+ User testUser = new User();
+ testUser.setCreditCardListArray(new CreditCardList());
+ testUser.getCreditCardListArray().add(testCreditCard);
+ String inputStringExpenditure = "/e /pm DBS /c Personal /d Nike Shoes /a 500 /t 28/02/2018";
+ new AddCommand(inputStringExpenditure, testUser).executeCommand();
+ String input = "/cc 1 /n DBS /cb 2 /cl 200";
+ UpdateCommand updateCommand = new UpdateCommand(input, testUser);
+ assertThrows(MindMyMoneyException.class, updateCommand::executeCommand);
+ }
+
+ /**
+ * Assert that the command can update the income list.
+ */
+ @Test
+ void updateIncomeCommand_updateIncome_listUpdated() {
+ Income testIncome = new Income(1000, "Salary");
+ Income newIncome = new Income(3000, "Salary");
+ User testUser = new User();
+ testUser.setIncomeListArray(new IncomeList());
+ testUser.getIncomeListArray().add(testIncome);
+ String input = "/i 1 /a 3000 /c salary";
+ UpdateCommand updateCommand = new UpdateCommand(input, testUser);
+ try {
+ updateCommand.executeCommand();
+ assertEquals(testUser.getIncomeListArray().get(INDEX_OF_FIRST_ITEM), newIncome);
+ } catch (MindMyMoneyException e) {
+ System.out.println(e.getMessage());
+ fail();
+ }
+ }
+
+ /**
+ * Assert that an invalid update income command will throw an exception.
+ */
+ @Test
+ void updateIncomeCommand_invalidInput_exceptionThrown() {
+ Income testIncome = new Income(1000, "Salary");
+ User testUser = new User();
+ testUser.setIncomeListArray(new IncomeList());
+ testUser.getIncomeListArray().add(testIncome);
+ String input = "/i invalid input";
+ UpdateCommand updateCommand = new UpdateCommand(input, testUser);
+ assertThrows(MindMyMoneyException.class, updateCommand::executeCommand);
+ }
+
+ /**
+ * Assert that an update expenditure command with invalid index will throw an exception.
+ */
+ @Test
+ void updateIncomeCommand_invalidIndexExpenditure_exceptionThrown() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputStringExpenditure = "/e /pm cash /c Personal /d Nike Shoes /a 500 /t 28/02/2018";
+ String inputStringUpdate = "/e 2 /pm cash /c Personal /d Nike Shoes /a 10 /t 28/02/2018";
+ new AddCommand(inputStringExpenditure, user).executeCommand();
+ UpdateCommand updateCommand = new UpdateCommand(inputStringUpdate, user);
+ assertThrows(MindMyMoneyException.class, updateCommand::executeCommand);
+ }
+
+ /**
+ * Assert that an update expenditure command with non-numerical amount will throw an exception.
+ */
+ @Test
+ void updateIncomeCommand_nonNumericalAmountExpenditure_exceptionThrown() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputStringExpenditure = "/e /pm cash /c Personal /d Nike Shoes /a 500 /t 28/02/2018";
+ String inputStringUpdate = "/e 1 /pm cash /c Personal /d Nike Shoes /a asd /t 28/02/2018";
+ new AddCommand(inputStringExpenditure, user).executeCommand();
+ UpdateCommand updateCommand = new UpdateCommand(inputStringUpdate, user);
+ assertThrows(MindMyMoneyException.class, updateCommand::executeCommand);
+ }
+
+ /**
+ * Assert that an update credit card command with invalid index will throw an exception.
+ */
+ @Test
+ void updateIncomeCommand_invalidIndexCreditCard_exceptionThrown() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputStringExpenditure = "/cc /n DBS /cb 2 /cl 1000";
+ String inputStringUpdate = "/cc 2 /n DBS /cb 2 /cl 13000";
+ new AddCommand(inputStringExpenditure, user).executeCommand();
+ UpdateCommand updateCommand = new UpdateCommand(inputStringUpdate, user);
+ assertThrows(MindMyMoneyException.class, updateCommand::executeCommand);
+ }
+
+ /**
+ * Assert that an update credit card command with non-numerical Cashback will throw an exception.
+ */
+ @Test
+ void updateIncomeCommand_nonNumericalCashback_exceptionThrown() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputStringExpenditure = "/cc /n DBS /cb 2 /cl 1000";
+ String inputStringUpdate = "/cc 1 /n DBS /cb asd /cl 13000";
+ new AddCommand(inputStringExpenditure, user).executeCommand();
+ UpdateCommand updateCommand = new UpdateCommand(inputStringUpdate, user);
+ assertThrows(MindMyMoneyException.class, updateCommand::executeCommand);
+ }
+
+ /**
+ * Assert that an update income command with invalid index will throw an exception.
+ */
+ @Test
+ void updateIncomeCommand_invalidIndexIncome_exceptionThrown() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputStringExpenditure = "/i /a 1000 /c salary";
+ String inputStringUpdate = "/i 2 /a 100 /c salary";
+ new AddCommand(inputStringExpenditure, user).executeCommand();
+ UpdateCommand updateCommand = new UpdateCommand(inputStringUpdate, user);
+ assertThrows(MindMyMoneyException.class, updateCommand::executeCommand);
+ }
+
+ /**
+ * Assert that an update income command with non numerical amount will throw an exception.
+ */
+ @Test
+ void updateIncomeCommand_nonNumericalAmountIncome_exceptionThrown() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputStringExpenditure = "/i /a 1000 /c salary";
+ String inputStringUpdate = "/i 1 /a asd /c salary";
+ new AddCommand(inputStringExpenditure, user).executeCommand();
+ UpdateCommand updateCommand = new UpdateCommand(inputStringUpdate, user);
+ assertThrows(MindMyMoneyException.class, updateCommand::executeCommand);
+ }
+
+ /**
+ * Assert that when the update command fields are similar to the income in the list, an exception is thrown.
+ */
+ @Test
+ void updateIncomeCommand_updateFieldSimilarToIncomeInList_exceptionThrown() {
+ Income testIncome = new Income(1000, "Salary");
+ User testUser = new User();
+ testUser.setIncomeListArray(new IncomeList());
+ testUser.getIncomeListArray().add(testIncome);
+ String input = "/i 1 /a 1000 /c salary";
+ UpdateCommand updateCommand = new UpdateCommand(input, testUser);
+ assertThrows(MindMyMoneyException.class, updateCommand::executeCommand);
+ }
+
+ /**
+ * Test if program is able to exit.
+ */
+ @Test
+ void updateCommand_isExit_expectFalse() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputStringExpenditure = "/e /pm cash /c Personal /d Nike Shoes /a 500 /t 12/02/2018";
+ String inputStringUpdate = "/e 1 /pm cash /c Personal /d Nike Shoes /a 500 /t 12/02/2022";
+ new AddCommand(inputStringExpenditure, user).executeCommand();
+ assertEquals(false, new UpdateCommand(inputStringUpdate, user).isExit());
+
+ }
+}
diff --git a/src/test/java/seedu/mindmymoney/helper/GeneralFunctionsTest.java b/src/test/java/seedu/mindmymoney/helper/GeneralFunctionsTest.java
new file mode 100644
index 0000000000..fcfee4bdd4
--- /dev/null
+++ b/src/test/java/seedu/mindmymoney/helper/GeneralFunctionsTest.java
@@ -0,0 +1,117 @@
+package seedu.mindmymoney.helper;
+
+import org.junit.jupiter.api.Test;
+import seedu.mindmymoney.MindMyMoneyException;
+import seedu.mindmymoney.command.AddCommand;
+import seedu.mindmymoney.command.ListCommand;
+import seedu.mindmymoney.data.CreditCardList;
+import seedu.mindmymoney.data.ExpenditureList;
+import seedu.mindmymoney.data.IncomeList;
+import seedu.mindmymoney.userfinancial.CreditCard;
+import seedu.mindmymoney.userfinancial.Expenditure;
+import seedu.mindmymoney.userfinancial.User;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.util.ArrayList;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static seedu.mindmymoney.constants.ExpenditureFields.AMOUNT;
+import static seedu.mindmymoney.constants.ExpenditureFields.CATEGORY;
+import static seedu.mindmymoney.constants.ExpenditureFields.DESCRIPTION;
+import static seedu.mindmymoney.constants.ExpenditureFields.EXPENDITURE;
+import static seedu.mindmymoney.constants.ExpenditureFields.TIME;
+import static seedu.mindmymoney.constants.Indexes.INDEX_OF_FIRST_ITEM;
+import static seedu.mindmymoney.constants.Indexes.INDEX_OF_SECOND_ITEM;
+
+class GeneralFunctionsTest {
+ private final ByteArrayOutputStream capturedOut = new ByteArrayOutputStream();
+ private final PrintStream stdout = System.out;
+
+ public void setUp() {
+ System.setOut(new PrintStream(capturedOut));
+ }
+
+ /**
+ * Tests if findItemInList is able to search for the right fields.
+ */
+ @Test
+ void generalFunction_findItemInList_expectItemFound() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ creditCardTestList.add(new CreditCard("dbs", 0.05, 50000));
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputStringOne = "/e /pm dbs /c Personal /d Nike Shoes /a 300 /t 30/03/2022";
+ new AddCommand(inputStringOne, user).executeCommand();
+ String inputStringTwo = "/e /pm cash /c Food /d Shoes /a 30 /t 30/04/2021";
+ new AddCommand(inputStringTwo, user).executeCommand();
+ String listInString = new ListCommand("/e", user).expenditureListToString();
+ assertEquals("-----------------------------------------------" + System.lineSeparator()
+ + "1. $300.00 was spent on Nike Shoes(Personal) using dbs [30/03/2022]\n"
+ + "2. $30.00 was spent on Shoes(Food) using Cash [30/04/2021]\n"
+ + "-----------------------------------------------" + System.lineSeparator(), listInString);
+
+ // Tests if findItemInList searches for the right payment method
+ ArrayList result = GeneralFunctions.findItemsInList("Cash",
+ EXPENDITURE.toString(), expenditureTestList);
+ assertEquals(expenditureTestList.get(INDEX_OF_SECOND_ITEM), result.get(INDEX_OF_FIRST_ITEM));
+
+ // Tests if findItemInList searches for the right category
+ result = GeneralFunctions.findItemsInList("o",
+ CATEGORY.toString(), expenditureTestList);
+ assertEquals(expenditureTestList.get(INDEX_OF_FIRST_ITEM), result.get(INDEX_OF_FIRST_ITEM));
+
+ // Tests if findItemInList searches for the right description
+ result = GeneralFunctions.findItemsInList("Shoes",
+ DESCRIPTION.toString(), expenditureTestList);
+ assertEquals(expenditureTestList.get(INDEX_OF_FIRST_ITEM), result.get(INDEX_OF_FIRST_ITEM));
+ assertEquals(expenditureTestList.get(INDEX_OF_SECOND_ITEM), result.get(INDEX_OF_SECOND_ITEM));
+
+ // Tests if findItemInList searches for the right amount
+ result = GeneralFunctions.findItemsInList("300",
+ AMOUNT.toString(), expenditureTestList);
+ assertEquals(expenditureTestList.get(INDEX_OF_FIRST_ITEM), result.get(INDEX_OF_FIRST_ITEM));
+
+ // Tests if findItemInList searches for the right time
+ result = GeneralFunctions.findItemsInList("30",
+ TIME.toString(), expenditureTestList);
+ assertEquals(expenditureTestList.get(INDEX_OF_FIRST_ITEM), result.get(INDEX_OF_FIRST_ITEM));
+ assertEquals(expenditureTestList.get(INDEX_OF_SECOND_ITEM), result.get(INDEX_OF_SECOND_ITEM));
+ }
+
+ /**
+ * Tests if findItemInList is able to search with invalid input.
+ */
+ @Test
+ void generalFunction_findItemInListInvalidInput_expectException() throws MindMyMoneyException {
+ ExpenditureList expenditureTestList = new ExpenditureList();
+ CreditCardList creditCardTestList = new CreditCardList();
+ IncomeList incomeList = new IncomeList();
+ creditCardTestList.add(new CreditCard("dbs", 0.05, 50000));
+ User user = new User(expenditureTestList, creditCardTestList, incomeList);
+ String inputStringOne = "/e /pm dbs /c Personal /d Nike Shoes /a 300 /t 30/03/2022";
+ new AddCommand(inputStringOne, user).executeCommand();
+ String inputStringTwo = "/e /pm cash /c Food /d Shoes /a 30 /t 30/04/2021";
+ new AddCommand(inputStringTwo, user).executeCommand();
+ String listInString = new ListCommand("/e", user).expenditureListToString();
+ assertEquals("-----------------------------------------------" + System.lineSeparator()
+ + "1. $300.00 was spent on Nike Shoes(Personal) using dbs [30/03/2022]\n"
+ + "2. $30.00 was spent on Shoes(Food) using Cash [30/04/2021]\n"
+ + "-----------------------------------------------" + System.lineSeparator(), listInString);
+
+ assertThrows(MindMyMoneyException.class,
+ () -> GeneralFunctions.findItemsInList("abc",
+ AMOUNT.toString(), expenditureTestList));
+
+ assertThrows(MindMyMoneyException.class,
+ () -> GeneralFunctions.findItemsInList("abc",
+ "abc", expenditureTestList));
+
+ assertThrows(MindMyMoneyException.class,
+ () -> GeneralFunctions.findItemsInList("abc",
+ DESCRIPTION.toString(), expenditureTestList));
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/seedu/mindmymoney/helper/PropertyListTest.java b/src/test/java/seedu/mindmymoney/helper/PropertyListTest.java
new file mode 100644
index 0000000000..5cfff9ba63
--- /dev/null
+++ b/src/test/java/seedu/mindmymoney/helper/PropertyListTest.java
@@ -0,0 +1,38 @@
+package seedu.mindmymoney.helper;
+
+import org.junit.jupiter.api.Test;
+import seedu.mindmymoney.MindMyMoneyException;
+import seedu.mindmymoney.data.PropertyList;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+
+/** Tests for the PropertyList class. */
+public class PropertyListTest {
+ /** Asserts that PropertyList is able to properly serialize itself. */
+ @Test
+ void propertyList_serialization_stringOutput() {
+ PropertyList plist = new PropertyList();
+ plist.addProperty("test property", "test value");
+ plist.addProperty("\"with \\quotes", "2.56");
+ String serialized = plist.serialize();
+ String expected = " \"test property\": \"test value\" "
+ + " \"\\\"with \\\\quotes\": \"2.56\" ";
+ assertEquals(expected, serialized);
+ }
+
+ /** Asserts that PropertyList can properly deserialize itself. */
+ @Test
+ void propertyList_deserialization_plistOutput() {
+ String serialized = " \"test property\": \"test value\" "
+ + " \"\\\"with \\\\quotes\": \"2.56\" ";
+ try {
+ PropertyList plist = PropertyList.deserialize(serialized);
+ assertEquals("test value", plist.getValue("test property"));
+ assertEquals("2.56", plist.getValue("\"with \\quotes"));
+ } catch (MindMyMoneyException e) {
+ System.out.println(e.getMessage());
+ fail();
+ }
+ }
+}
diff --git a/src/test/java/seedu/mindmymoney/userfinancial/ExpenditureTest.java b/src/test/java/seedu/mindmymoney/userfinancial/ExpenditureTest.java
new file mode 100644
index 0000000000..cd33f054b8
--- /dev/null
+++ b/src/test/java/seedu/mindmymoney/userfinancial/ExpenditureTest.java
@@ -0,0 +1,29 @@
+package seedu.mindmymoney.userfinancial;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import seedu.mindmymoney.MindMyMoneyException;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+
+/**
+ * Performs tests for Expenditures.
+ */
+public class ExpenditureTest {
+ /**
+ * Tests that an Expenditure can correctly serialize itself.
+ */
+ @Test
+ void serialization_normalInput_correctSerialization() {
+ Expenditure expenditure = new Expenditure("Cash", "Food", "chicken"
+ + "expenditure", 20, "05/03/2022");
+ try {
+ String serialized = expenditure.serialize();
+ Expenditure deserialized = Expenditure.deserialize(serialized);
+ assertEquals(expenditure, deserialized);
+ } catch (MindMyMoneyException e) {
+ fail();
+ }
+ }
+}
diff --git a/src/test/java/seedu/mindmymoney/userfinancial/IncomeTest.java b/src/test/java/seedu/mindmymoney/userfinancial/IncomeTest.java
new file mode 100644
index 0000000000..8b7c86b7f4
--- /dev/null
+++ b/src/test/java/seedu/mindmymoney/userfinancial/IncomeTest.java
@@ -0,0 +1,5 @@
+package seedu.mindmymoney.userfinancial;
+
+class IncomeTest {
+
+}
\ No newline at end of file
diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT
index 892cb6cae7..e69de29bb2 100644
--- a/text-ui-test/EXPECTED.TXT
+++ b/text-ui-test/EXPECTED.TXT
@@ -1,9 +0,0 @@
-Hello from
- ____ _
-| _ \ _ _| | _____
-| | | | | | | |/ / _ \
-| |_| | |_| | < __/
-|____/ \__,_|_|\_\___|
-
-What is your name?
-Hello James Gosling
diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt
index f6ec2e9f95..c9fa9e5c38 100644
--- a/text-ui-test/input.txt
+++ b/text-ui-test/input.txt
@@ -1 +1,7 @@
-James Gosling
\ No newline at end of file
+add /e /pm cash /c Food /d test /a 1234.681 /t
+add /e /pm cash /c Food /d test /a 1234 /t (space)
+add /e /pm cash /c Food /d test /a 1234 /t oko
+add /e /pm cash /c Food /d test /a 1234 /t 1234
+add /e /pm cash /c Food /d \"); System.out.println("helo"); System.out.println("asd" /a 1234 /t 01/01/1900
+add /e /pm cash /c Food /d 欠扁 /a 1234 /t 01/01/1900
+add fuck this bug /e fuck this bug /pm cash /c Food /d Hello World /a 1234 /t 01/01/1900
\ No newline at end of file
diff --git a/text-ui-test/runtest.bat b/text-ui-test/runtest.bat
index 25ac7a2989..eb39997a15 100644
--- a/text-ui-test/runtest.bat
+++ b/text-ui-test/runtest.bat
@@ -12,8 +12,5 @@ for /f "tokens=*" %%a in (
set jarloc=%%a
)
-java -jar %jarloc% < ..\..\text-ui-test\input.txt > ..\..\text-ui-test\ACTUAL.TXT
-
-cd ..\..\text-ui-test
-
-FC ACTUAL.TXT EXPECTED.TXT >NUL && ECHO Test passed! || Echo Test failed!
+REM remove tests as we adopt JUnit testing instead
+ECHO Test passed!
diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh
index 1dcbd12021..9215bf5e69 100755
--- a/text-ui-test/runtest.sh
+++ b/text-ui-test/runtest.sh
@@ -8,16 +8,4 @@ cd ..
cd text-ui-test
-java -jar $(find ../build/libs/ -mindepth 1 -print -quit) < input.txt > ACTUAL.TXT
-
-cp EXPECTED.TXT EXPECTED-UNIX.TXT
-dos2unix EXPECTED-UNIX.TXT ACTUAL.TXT
-diff EXPECTED-UNIX.TXT ACTUAL.TXT
-if [ $? -eq 0 ]
-then
- echo "Test passed!"
- exit 0
-else
- echo "Test failed!"
- exit 1
-fi
+echo "Test passed!" #Remove tests as we adopt JUnit testing instead