diff --git a/.gitignore b/.gitignore
index 2873e189e1..309e34f4c1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,12 +2,13 @@
/.idea/
/out/
/*.iml
-
# Gradle build files
/.gradle/
/build/
src/main/resources/docs/
-
+# Any other files that should not be committed
+/data
+*.jar
# MacOS custom attributes files created by Finder
.DS_Store
*.iml
diff --git a/META-INF/MANIFEST.MF b/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000..57eaf39ca6
--- /dev/null
+++ b/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Main-Class: seedu.brokeMan.BrokeMan
+
diff --git a/README.md b/README.md
index f82e2494b7..77ab65e4d5 100644
--- a/README.md
+++ b/README.md
@@ -1,64 +1,27 @@
-# Duke project template
+# BrokeMan
-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
+Welcome to BrokeMan!
-Prerequisites: JDK 11 (use the exact version), update Intellij to the most recent version.
+Your personal budget manager to prevent you to become broke like me...
-1. **Ensure Intellij JDK 11 is defined as an SDK**, as described [here](https://www.jetbrains.com/help/idea/sdk.html#set-up-jdk) -- this step is not needed if you have used JDK 11 in a previous Intellij project.
-1. **Import the project _as a Gradle project_**, as described [here](https://se-education.org/guides/tutorials/intellijImportGradleProject.html).
-1. **Verify the set up**: After the importing is complete, locate the `src/main/java/seedu/duke/Duke.java` file, right-click it, and choose `Run Duke.main()`. If the setup is correct, you should see something like the below:
- ```
- > Task :compileJava
- > Task :processResources NO-SOURCE
- > Task :classes
-
- > Task :Duke.main()
- Hello from
- ____ _
- | _ \ _ _| | _____
- | | | | | | | |/ / _ \
- | |_| | |_| | < __/
- |____/ \__,_|_|\_\___|
-
- What is your name?
- ```
- Type some word and press enter to let the execution proceed to the end.
+If you are a fast typer, this CLI based program will help you track your financial status much faster than the traditional UI based apps!
-## Build automation using Gradle
+---
-* This project uses Gradle for build automation and dependency management. It includes a basic build script as well (i.e. the `build.gradle` file).
-* If you are new to Gradle, refer to the [Gradle Tutorial at se-education.org/guides](https://se-education.org/guides/tutorials/gradle.html).
-
-## Testing
-
-### I/O redirection tests
-
-* To run _I/O redirection_ tests (aka _Text UI tests_), navigate to the `text-ui-test` and run the `runtest(.bat/.sh)` script.
-
-### JUnit tests
-
-* A skeleton JUnit test (`src/test/java/seedu/duke/DukeTest.java`) is provided with this project template.
-* If you are new to JUnit, refer to the [JUnit Tutorial at se-education.org/guides](https://se-education.org/guides/tutorials/junit.html).
-
-## Checkstyle
-
-* A sample CheckStyle rule configuration is provided in this project.
-* If you are new to Checkstyle, refer to the [Checkstyle Tutorial at se-education.org/guides](https://se-education.org/guides/tutorials/checkstyle.html).
-
-## CI using GitHub Actions
-
-The project uses [GitHub actions](https://github.com/features/actions) for CI. When you push a commit to this repo or PR against it, GitHub actions will run automatically to build and verify the code as updated by the commit/PR.
-
-## Documentation
-
-`/docs` folder contains a skeleton version of the project documentation.
-
-Steps for publishing documentation to the public:
-1. If you are using this project template for an individual project, go your fork on GitHub.
- If you are using this project template for a team project, go to the team fork on GitHub.
-1. Click on the `settings` tab.
-1. Scroll down to the `GitHub Pages` section.
-1. Set the `source` as `master branch /docs folder`.
-1. Optionally, use the `choose a theme` button to choose a theme for your documentation.
+Useful links:
+* [User Guide](docs/UserGuide.md)
+* [Developer Guide](docs/DeveloperGuide.md)
+* [About Us](docs/AboutUs.md)
+* [Yu Sichen Sistine PPP](docs/team/sistine-yu.md)
+* [Sangjun Nam PPP](docs/team/namsengi11.md)
+* [Samuel Tan PPP](docs/team/samueltansw.md)
+* [Liam PPP](docs/team/speciliam.md)
diff --git a/build.gradle b/build.gradle
index d5e548e85f..f7659eaa66 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,46 +1,47 @@
-plugins {
- id 'java'
- id 'application'
- id 'checkstyle'
- id 'com.github.johnrengelman.shadow' version '7.1.2'
-}
-
-repositories {
- mavenCentral()
-}
-
-dependencies {
- testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.5.0'
- testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.5.0'
-}
-
-test {
- useJUnitPlatform()
-
- testLogging {
- events "passed", "skipped", "failed"
-
- showExceptions true
- exceptionFormat "full"
- showCauses true
- showStackTraces true
- showStandardStreams = false
- }
-}
-
-application {
- mainClass = "seedu.duke.Duke"
-}
-
-shadowJar {
- archiveBaseName = "duke"
- archiveClassifier = null
-}
-
-checkstyle {
- toolVersion = '10.2'
-}
-
-run{
- standardInput = System.in
-}
+plugins {
+ id 'java'
+ id 'application'
+ id 'checkstyle'
+ id 'com.github.johnrengelman.shadow' version '7.1.2'
+}
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.5.0'
+ testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.5.0'
+}
+
+test {
+ useJUnitPlatform()
+
+ testLogging {
+ events "passed", "skipped", "failed"
+
+ showExceptions true
+ exceptionFormat "full"
+ showCauses true
+ showStackTraces true
+ showStandardStreams = false
+ }
+}
+
+application {
+ mainClass = "seedu.brokeMan.BrokeMan"
+}
+
+shadowJar {
+ archiveBaseName = "brokeMan"
+ archiveClassifier = null
+}
+
+checkstyle {
+ toolVersion = '10.2'
+}
+
+run{
+ standardInput = System.in
+ enableAssertions = true
+}
diff --git a/docs/AboutUs.md b/docs/AboutUs.md
index 0f072953ea..84936ce310 100644
--- a/docs/AboutUs.md
+++ b/docs/AboutUs.md
@@ -1,9 +1,8 @@
-# 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)
+# About us
+
+| Display | Name | Github Profile | Portfolio |
+|------------------------------|:------------------:|:-----------------------------:|:----------------------------------:|
+| | Yu Sichen | [Github](https://github.com/sistine-yu) | [Portfolio](./team/sistine-yu.md) |
+| | Liam Van | [Github](https://github.com/SpeciLiam) | [Portfolio](./team/speciliam.md) |
+| ![](./teamImages/samuel.jpg) | Samuel Tan Sze Wee | [Github](https://github.com/Samueltansw) | [Portfolio](./team/samueltansw.md) |
+| | Sangjun Nam | [Github](https://github.com/namsengi11) | [Portfolio](./team/namsengi11.md) |
diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md
index 64e1f0ed2b..68e469446f 100644
--- a/docs/DeveloperGuide.md
+++ b/docs/DeveloperGuide.md
@@ -1,38 +1,489 @@
# Developer Guide
+## Table of Contents
+
+1. [Acknowledgements](#acknowledgements)
+2. [Design](#design)
+ - [Architecture](#architecture)
+ - [UI component](#ui-component)
+ - [Parser component](#parser-component)
+ - [Command component](#command-component)
+ - [Save component](#save-component)
+ - [EntryList Component](#entrylist-component)
+ - [Budget Component](#budget-component)
+ - [Common class](#common-class)
+ - [Exception classes](#exception-classes)
+3. [Implementation](#implementation)
+ - [Entry](#entry)
+ - [EntryList](#entrylist)
+ - [ExpenseList, IncomeList](#expenselist-incomelist)
+ - [Budget](#budget)
+ - [SaveExpense, SaveIncome, SaveBudget](#saveexpense-saveincome-savebudget)
+ - [Wishlist (COMING SOON)](#wishlist-coming-soon)
+ - [Spending Advisor (COMING SOON)](#spending-advisor-coming-soon)
+4. [Appendix: Requirements](#appendix-requirements)
+ - [Product Scope](#product-scope)
+ - [User Stories](#user-stories)
+ - [Use cases](#use-cases)
+5. [Appendix: Instructions for manual testing](#appendix-instructions-for-manual-testing)
+
+---
+
## Acknowledgements
-{list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well}
+Below are links to the codes that this program has partly referenced and adapted from.
+
+- [addressbook-level2](https://github.com/se-edu/addressbook-level2)
+- [addressbook-level3](https://github.com/se-edu/addressbook-level3)
+
+[back to contents](#table-of-contents)
+
+---
+
+## Design
+
+### Architecture
+
+![Architecture Diagram](./images/ArchitectureDiagram.png)
+
+The ***architecture diagram*** given above explains the high level design of the program.
+
+Given below is a quick overview of the main components and how they interact with each other.
+
+**Main components of architecture**
+
+`BrokeMan` has one class [`Main`](https://github.com/AY2223S2-CS2113-F13-2/tp/blob/master/src/main/java/seedu/brokeMan/BrokeMan.java), which is responsible for:
+- At program launch: Initialises the components in the correct sequence, and connect them up with each other
+- At program termination: Shuts down the components and invokes cleanup methods where necessary.
+
+[`Common`](#common-class) represents a collection of messages used by multiple other components.
+[`Exception`](#exception-classes) represents a collection of custom exceptions used by multiple other components.
+
+The rest of the program consists of mainly 5 main components.
+- [`Ui`](#ui-component): The Ui of the program.
+- [`Parser`](#parser-component): The user input parser.
+- [`Command`](#command-component): The command executor.
+- [`Save`](#save-component): Reads data from, and writes data to hard disk.
+- [`EntryList`](#entrylist-component): The abstract class (with subclasses `IncomeList` and `ExpenseList`) that contains
+an `Entry` of linkedList which stores
+the list of entries (incomes / expenses) and its method when program is running.
+- [`Budget`](#budget-component) : Stores the list of budget amounts in `budgetEachMonth` and its methods when program is running.
+
+**How the architecture components interact with each other**
+
+The program will first load the content of the .txt files in the `Data` folder if exists,
+and populate the `incomeList`, `expenseList`, and `budgetEachMonth`. The `user`'s interactions with
+the `Ui` will be parsed to the `Command` using the `Parser`, and the `parser` will then return the `Command` object
+specified, which the `Ui` can execute and format the output to the `user`. When the `user` exits the program, the `Save`
+command will be executed and the content in the `entryList` (more specifically `incomeList`, `expenseList`) from `EntryList`, and `budgetEachMonth` from `Budget` will then be
+stored into their respective .txt files in the `Data` folder.
+
+[back to contents](#table-of-contents)
+
+---
+
+### Ui component
+
+The **API** of this component is specified in [`Ui.java`](https://github.com/AY2223S2-CS2113-F13-2/tp/blob/master/src/main/java/seedu/brokeMan/ui/Ui.java)
+
+Below is the UML diagram for Ui class:
+
+![UiClassDiagram](./images/UiClassDiagram.png)
+
+The Ui consists of methods to read inputs from user and to format the outputs to be displayed to the user.
+
+[back to contents](#table-of-contents)
+
+### Parser component
+
+The **API** of this component is specified in [`Parser.java`](https://github.com/AY2223S2-CS2113-F13-2/tp/blob/master/src/main/java/seedu/brokeMan/parser/Parser.java)
+
+Below is the UML diagram for Parser class:
+
+![ParserClassDiagram](./images/ParserClassDiagram.png)
+
+How the `Parser` component works:
+1. When the `BrokeMan` class calls the `Parser` class to execute a command using the `parseCommand` method.
+2. The `parseCommand` method uses `UserInput` class to split the user input in two, command input and description of command.
+3. Depending on the command input, the `parseCommand` method will call its respective `prepareXCommand` (i.e, `prepareDeleteExpenseCommand`).
+4. Whenever invalid inputs are given by the user, parser will throw appropriate exceptions, where it will catch itself and return a `InvalidCommand`.
+5. The `prepareXCommand` will then return the respective Command object (i.e, `deleteExpenseCommand`) back to the `parseCommand` method and then to the `BrokeMan` class.
+
+[back to contents](#table-of-contents)
+
+---
+
+### Command component
+
+The `Command` component consists of many `Command` classes that executes different commands of the program
+when called. It is stored in the [`seedu.brokeMan.command`](https://github.com/AY2223S2-CS2113-F13-2/tp/tree/master/src/main/java/seedu/brokeMan/command) package.
+
+The sequence diagram below shows how the components interact with each other for the scenarios where the user
+issues the commands `deleteExpense 1` and `exit`.
+
+![DeleteCommandSequenceDiagram](./images/DeleteCommandSequenceDiagram.png)
+
+When the `Ui` reads `"deleteExpense 1"` from `User`, it calls the `parseCommand()` method from `Parser` class
+to help parse the user input to `Command` (more specifically, `DeleteExpenseCommand`).
+In the `parseCommand()` method, it calls the `prepareDeleteExpenseCommand()` method to help convert the `String` "1" to
+`int` 1, and handle the necessary `exceptions`. It then returns a newly constructed `DeleteExpenseCommand(1)` object to `parseCommand()`
+method and back to `Ui`. The `Ui` executes the `excute()` method in `DeleteExpenseCommand` object which will call the
+`deleteExpense()` method in `EntryList` which will delete the entry of expense in its entryList. The `Ui` will
+display successful deletion of expense entry upon completion of delete command.
+
+When the `Ui` reads "exit" from the `User`, it similarly calls the `parseCommand()` method from
+`Parser` class to help parse the user input to `Command` (more specifically, `ExitCommand`).
+The `Parser` class will return a newly constructed `ExitCommand()` object back to the `Ui`.
+The `Ui` executes the `execute()` method of the `ExitCommand` which will call writeFile method in the `SaveBudget`,
+`SaveExpense`, and `SaveIncome` classes, in the `Save` folder, and write the content to its respective `.txt`
+files in the `Data` folder. Upon completion of the save command, the `Ui` will print the goodbye messages to the
+`User` and terminate the program.
+
+[back to contents](#table-of-contents)
+
+---
+
+### Save component
+
+The **API** of this component is specified in [`SaveBudget.java`](https://github.com/AY2223S2-CS2113-F13-2/tp/blob/master/src/main/java/seedu/brokeMan/save/SaveBudget.java), [`SaveExpense.java`](https://github.com/AY2223S2-CS2113-F13-2/tp/blob/master/src/main/java/seedu/brokeMan/save/SaveExpense.java), [`SaveIncome.java`](https://github.com/AY2223S2-CS2113-F13-2/tp/blob/master/src/main/java/seedu/brokeMan/save/SaveIncome.java)
+
+The `Save` component,
+
+- can save entryList data and budget data in the hard disk as .txt files, and read them back into corresponding objects.
+- depends on some classes in the EntryList component and Budget component(because the Save component’s job is to save/retrieve objects that belong to the EntryList and Budget)
+
+[back to contents](#table-of-contents)
+
+---
+
+### EntryList component
+
+The **API** of this component is specified in [`EntryList.java`](https://github.com/AY2223S2-CS2113-F13-2/tp/blob/master/src/main/java/seedu/brokeMan/entry/EntryList.java)
+
+The `EntryList` component,
+- stores the entry list data i.e., all `Entry` objects (which can inherit the behavior of `Expense` class or `Income` class)
+- stores the `EntryAmountComparator` and `EntryDateComparator` to filter the list. The UI can be bound to this list so that the UI automatically updates when the data in the list change.
+- does not depend on any of the other three components (as the `EntryList` represents data entities of the domain, they should make sense on their own without depending on other components).
+
+Here is the (partial) UML diagram of the `EntryList` component:
+
+![EntryListClassDiagram](./images/EntryListClassDiagram.png)
+
+[back to contents](#table-of-contents)
+
+---
+
+### Budget component
+
+The **API** of this component is specified in [`Budget.java`](https://github.com/AY2223S2-CS2113-F13-2/tp/blob/master/src/main/java/seedu/brokeMan/budget/Budget.java)
+
+The `Budget` component,
+- stores the monthly budget data in a hashmap
+- does not depend on any of the other three components (as the `Budget` represents data entities of the domain, they should make sense on their own without depending on other components).
+
+Here is a UML diagram of the `Budget` component:
+
+![BudgetClassDiagram](./images/BudgetClassUML.png)
+
+### Common class
+
+Messages used by multiple components are in the [`seedu.brokeMan.common`](https://github.com/AY2223S2-CS2113-F13-2/tp/tree/master/src/main/java/seedu/brokeMan/common) package.
+
+[back to contents](#table-of-contents)
+
+---
+
+### Exception classes
+
+Exceptions are used mainly by the parser to restrict commands from entering undesirable/unusable inputs provided by the user.
+
+Possible exceptions in multiple components are defined in the [`seedu.brokeMan.exception`](https://github.com/AY2223S2-CS2113-F13-2/tp/tree/master/src/main/java/seedu/brokeMan/exception) package.
+
+[back to contents](#table-of-contents)
+
+---
+
+## Implementation
+
+### Entry
+
+Below is the UML class diagram for Entry:
-## Design & implementation
+![EntryClassDiagram](./images/EntryClassDiagram.png)
-{Describe the design and implementation of the product. Use UML diagrams and short code snippets where applicable.}
+Entry class is the underlying superclass for Expense and Income classes. It establishes the common attributes and
+methods that are necessary to represent Expenses and Incomes. Abstract class is used to represent their common features
+to maximize code reusability and increase maintainability.
+**Methods**
-## Product scope
-### Target user profile
+The entry class implements getters for its attributes, as other classes may need to access information of the entry (ex. getTime())
-{Describe the target user profile}
+editDescription(), editAmount(), editTime(), editCategory()
-### Value proposition
+* Takes in corresponding parameters to edit the private attributes.
+* Used by EntryList to make edits
-{Describe the value proposition: what problem does it solve?}
+isSameMonth()
-## User Stories
+* Takes in Integer year and Month and returns if the entry is made in the date specified by parameters.
-|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|
+[back to contents](#table-of-contents)
-## Non-Functional Requirements
+---
-{Give non-functional requirements}
+### EntryList
+
+Below is the UML class diagram for EntryList:
+
+![EntryListFullClassDiagram](./images/EntryListFulllClassDiagram.png)
+
+The EntryList class represents a collection of Entry instances. It is an abstract class that serves as a superclass for
+ExpenseList and IncomeList classes, providing common functionalities to minimize repetitive code and easing code
+maintenance. It provides underlying methods for adding, removing, editing, and listing entries from the list, which can
+be expenses or incomes depending on the subclass.
+
+On top of the methods that provide basic functionality of expense/income lists, the EntryList class provides additional
+methods such as summing all entries or filtering the entries according to LocalDate provided to the method. Most methods
+of EntryList take in a List, which is because it has to operate on a list of expenses or incomes passed by the
+IncomeList or ExpenseList subclasses.
+
+**Methods**
+
+addEntry(), listEntry(), deleteEntry(), editEntry()
+
+* Underlying methods of the add, edit, view, and delete features of Expense and Income class.
+* Edit entry has different methods for each data stored in an entry. (E.g. amount, time...etc).
+ - Edit method makes use of the private edit methods of the Entry class.
+
+
+getTotalAmount(), sortEntriesByAmount(), sortEntriesByDate(), findEntriesByCategory(), selectEntryForDate(), getEntryListSum()
+
+* Underlying methods of subclasses of EntryList, which are used to implement features that extends beyond the CRUD features.
+* They all take in a list of entries, which are IncomeLists or ExpenseLists, then returns appropriate data back to the subclass.
+* Filter by date methods make use of java.stream and its filter method to choose out only the entries of appropriate date. To check if the entry is made in the date of interest, the isSameMonth() method of Entry class is used.
+* As mentioned above, custom comparators are implemented in the program to sort the entries by an attribute of an entry.
+
+[back to contents](#table-of-contents)
+
+---
+
+### ExpenseList, IncomeList
+
+Classes ExpenseList and IncomeList are responsible for keeping track of the corresponding entry instances added to the program by the user.
+At a class level, it keeps a **LinkedList** of corresponding entries.
+They both extend EntryList, the abstract class that represents a collection of Entry instances.
+It provides static functionalities of managing and viewing entry instances at a class level.
+Instances of ExpenseList and IncomeList are not created as all functionalities can be provided at the class level.
+
+**Methods**
+
+Most methods of the Income/ExpenseList make use of the underlying methods in the EntryList class.
+* For example, the addIncome method makes use of addEntry method of EntryList to add a Income instance in the class-level Linked-List named incomeList.
+
+
+listExpense() / listIncome()
+
+* Overloaded method, may take it no parameter or LocalDate parameter
+* If it has no passed parameters, it returns all entries in the list
+* If a LocalDate is passed, it returns all entries made in the month specified by LocalDate instance.
+
+[back to contents](#table-of-contents)
+
+---
+
+### Budget
+
+The Budget class represents the user’s monthly budget. The class utilize class-level hashmaps to represent the monthly
+budget, using outer key year and inner key month. It provides a methods setBudget and viewBudget to set and view budget for different months.
+
+It makes use of a static HashMap> to keep track of monthly budget. If the user tries to
+access budget using keys that are not entered in the HashMap, it will return a warning mentioning that the inquired
+budget has not been set yet.
+
+[back to contents](#table-of-contents)
+
+---
+
+### SaveExpense, SaveIncome, SaveBudget
+
+The SaveExpense, SaveIncome, SaveBudget classes deal with saving in the user inputted data locally.
+So that it can all be later accessed.
+They all save once the exit command is set.
+So assuming there are no bugs it should save.
+
+**Methods**
+
+writeFile(LinkedList expenses/incomes)
+* writes to the file in a similar format that is entered in to make a new Expense object within the constructor.
+
+readExpenseFile()
+
+* This method reads in from the saved file either ExpenseData.txt or IncomeData.txt.
+* Reads in and initialized each line as either expense or income.
+* Then adds to the respective list.
+
+
+writeFile(HashMap> budget)
+* This method writes to the file under ./data/BudgetData.txt file.
+* Will write upon exit.
+* organizes it based on how it the information is constructed for easy reading.
+* Iterates through the outer HashMap then inner.
+
+public static HashMap> readFile()
+* On start up reads through the file ./data/BudgetData.txt.
+* Reads in and the year then as a key then the monthly declared budget of the given months.
+* Returns this as the initialized budget on start.
+
+[back to contents](#table-of-contents)
+
+---
+
+### Wishlist (COMING SOON)
+
+The Wishlist class represents a good or a product the user wants to purchase in the future that are expensive enough, prompting the user to save up. Users can funnel their income entry to a specific wishlisted-product. Users can view how much percentage of the good's price they have saved up, which can give them further motivation to cut their spendings. Users will be able to add a list of wishlisted products.
+
+They can be implemented through a structure that is similar to Entry and EntryList. Each wishlisted product will be a separate class that stores the information of the good, such as name, price, and the date of wishlist created.
+
+The wishlist entry can then be arranged to a list, where users can easily navigate and compare between different wishlisted products. They can also give priority value to each wishlisted product, which can be used to sort them.
+
+[back to contents](#table-of-contents)
+
+---
+
+### Spending Advisor (COMING SOON)
+
+The spending advisor will be integrated to the product by assisting users to make best consumption choices. The advisor will help users compare prices from different food outlets and shops. As the target user for this program is students, the program will first implement food stalls and shops in NUS, which is where the developers for this program are enrolled. In subsequent iterations of the program, the advisor will expand into other regions and recommend users of the best-value purchases.
+
+[back to contents](#table-of-contents)
+
+---
+
+
+## Appendix: Requirements
+
+### Product scope
+
+**Target user profile**:
+
+- BrokeMan is suitable for students who have to work with tight budgets
+- students who want to use their money efficiently
+- students who want to minimize their spending.
+- students who can type fast
+- prefer typing to mouse interactions
+- is reasonably comfortable using CLI apps
+
+**Value proposition**:
+
+- Manage income and expenses faster than a typical mouse/GUI driven app
+- The program will offer visualization of the user's incomes, expenses and budget, allowing them to recap and be mindful
+ about their financial status. The project will allow division of budget into multiple subcategories of expenses. In
+ essence, the program sets students up for a better financial future.
+
+[back to contents](#table-of-contents)
+
+---
+
+### User Stories
+
+Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unlikely to have) - `*`
+
+| Priority | Version | As a ... | I want to ... | So that I can ... |
+|----------|---------|----------|------------------------------------------------|-----------------------------------------------------------------------------------|
+| `* * *` | v1.0 | user | add my income | add my new incomes and keep track of them |
+| `* * *` | v1.0 | user | delete my income entered | delete incorrectly added or unwantedincomes |
+| `* * *` | v1.0 | user | edit my income previously entered | edit a component of my income without having to delete and re-entering the income |
+| `* * *` | v1.0 | user | list my incomes | track all of my incomes and take a look at them at once |
+| `* * *` | v1.0 | user | add my expenses | add my new expenses and keep track of them |
+| `* * *` | v1.0 | user | delete my expenses entered | delete incorrectly added or unwanted expenses |
+| `* * *` | v1.0 | user | edit my expense previously entered | edit a component of my expense without having to delete and re-enter the expense |
+| `* * *` | v1.0 | user | list my expenses | track all my expenses and take a look at them at once |
+| `* * *` | v1.0 | user | set and view my budget | set expectation of how much money I should use |
+| `* * *` | v1.0 | user | view how much of the budget I spent | keep track of my spending status, as well as manage and change my spending habit as necessary |
+| `* *` | v1.0 | user | view all command that I can enter | get help on the features if necessary |
+| `* *` | v2.0 | user | list monthly expenses, income, and budget | refer to financial status in previous months |
+| `* *` | v2.0 | user | save all my income and expenses entered | so that I can refer to it next time I reopen the program |
+| `* *` | v3.0 | user | add goods on wishlist | so that I can save my incomes to purchase them |
+| `* ` | v3.0 | user | compare different spending options in the area | so that I can make the best-value purchases |
+
+[back to contents](#table-of-contents)
+
+---
+
+### Use cases
+
+(For all use cases below, the System is the `BrokeMan` and the Actor is the `user`,
+unless specified otherwise)
+
+#### Use case: Delete an expense
+
+#### MSS
+
+1. User requests to list expenses.
+2. BrokeMan shows a list of expenses.
+3. User requests to delete a specific person in the list.
+4. BrokeMan deletes the person.
+
+ Use case ends.
+
+#### Extensions
+* 2a. The list is empty.
+
+ Use case ends.
+* 3a. The given index is invalid.
+ * 3a1. BrokeMan shows an error message.
+
+ Use case resumes at step 2.
+
+[back to contents](#table-of-contents)
+
+---
+
+### Non-Functional Requirements
+
+1. Should work on any mainstream OS as long as it has Java 11 or above installed.
+2. Should be able to hold up to 1000 entries without a noticeable sluggishness in performance for typical usage.
+3. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse.
+
+[back to contents](#table-of-contents)
+
+---
## Glossary
-* *glossary item* - Definition
+- **Mainstream OS**: Windows, Linux, Unix, OS-X
+- Command Line Interface (CLI)
-## Instructions for manual testing
+[back to contents](#table-of-contents)
+
+---
+
+## Appendix: 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}
+
+1. Download the latest .jar file from [here](https://github.com/AY2223S2-CS2113-F13-2/tp/releases/tag/v2.1).
+2. Open the folder that the .jar file is in and run the program in your terminal using `java -jar CS2113-F13-2_BrokeMan.jar`.
+3. Read through the user guide to get the detailed instructions for the various features of the program.
+4. Additional, user can see all the available commands and a less detailed description of them by entering `help`.
+5. Add an expense at 2023/04/01 at 12:00 using the command `addExpense a/ 5.0 d/ lunch t/ 2023 04 01 12 00 c/ FOOD`.
+6. Delete expense using the command `deleteExpense 1`.
+7. Edit an expense using the command `editExpense i/ 1 t/ amount n/ 12.5`.
+8. List all expenses across the entire time period using the command `listExpense`.
+9. List all expenses for the month specified using the command `listExpense t/ 2023/04`.
+10. Add an income at 2023/04/01 at 12:00 using the command `addIncome a/ 4000 d/ salary t/ 2023 04 01 12 00 c/ SALARY`.
+11. Delete an income using the command `deleteIncome 1`.
+12. Edit an Income using the command `editIncome i/ 1 t/ info n/ stocks`.
+13. List all incomes across the entire time period using the command `listIncome`.
+14. List all incomes for the month specified using the command `listIncome t/ 2023/04`.
+15. Set a budget for current month using the command `setBudget 500`.
+16. Set a budget for a specific month using the command `setBudget 500 t/ 2023/05`.
+17. View budget for current month and amount of budget remaining using the command `viewBudget`.
+18. View budget for the month specified and the amount of budget remaining using the command `viewBudget t/ 2023/05`.
+19. View Expenses by decreasing amount using the command `sortExpenseByAmount`.
+20. View Income by decreasing amount using the command `sortIncomeByAmount`.
+21. View Expenses by dates from the latest to oldest using the command `sortExpenseByDate`.
+22. View incomes by dates from the latest to oldest using the command `sortIncomeByDate`.
+23. Exit and save the content of the program by using the command `exit`.
+
+[back to contents](#table-of-contents)
diff --git a/docs/README.md b/docs/README.md
index bbcc99c1e7..3166368d72 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,8 +1,25 @@
-# Duke
-
-{Give product intro here}
-
-Useful links:
-* [User Guide](UserGuide.md)
-* [Developer Guide](DeveloperGuide.md)
-* [About Us](AboutUs.md)
+# BrokeMan
+
+```
+$$$$$___ ______ _______ $$_____ _______ $$___$$_ _______ _______
+$$__$$__ ______ _______ $$__$$_ _$$$$__ $$$_$$$_ $$$$$__ _______
+$$$$$___ $$_$$_ _$$$$__ $$_$$__ $$__$$_ $$$$$$$_ ____$$_ $$$$$__
+$$___$$_ $$$_$_ $$__$$_ $$$$___ $$$$$$_ $$_$_$$_ _$$$$$_ $$__$$_
+$$___$$_ $$____ $$__$$_ $$_$$__ $$_____ $$___$$_ $$__$$_ $$__$$_
+$$$$$$__ $$____ _$$$$__ $$__$$_ _$$$$$_ $$___$$_ _$$$$$_ $$__$$_
+```
+
+Welcome to BrokeMan!
+
+Your personal budget manager to prevent you to become broke like me...
+
+---
+
+Useful links:
+* [User Guide](UserGuide.md)
+* [Developer Guide](DeveloperGuide.md)
+* [About Us](AboutUs.md)
+* [Yu Sichen Sistine PPP](team/sistine-yu.md)
+* [Sangjun Nam PPP](team/namsengi11.md)
+* [Samuel Tan PPP](team/samueltansw.md)
+* [Liam PPP](team/speciliam.md)
diff --git a/docs/UserGuide.md b/docs/UserGuide.md
index abd9fbe891..8005a57979 100644
--- a/docs/UserGuide.md
+++ b/docs/UserGuide.md
@@ -1,42 +1,741 @@
# User Guide
+## Table of Contents
+
+1. [Introduction](#introduction)
+2. [Quick Start](#quick-start)
+3. [Features](#features)
+ * [Adding an expense](#adding-an-expense-addexpense)
+ * [Adding an income](#adding-an-income-addincome)
+ * [Listing all expenses](#listing-all-expenses-listexpense)
+ * [Listing all income](#listing-all-income-listincome)
+ * [Editing an expense](#editing-an-expense-editexpense)
+ * [Editing an income](#editing-an-income-editincome)
+ * [Sorting an expense by amount](#sorting-an-expense-by-amount-sortexpensebyamount)
+ * [Sorting an income by amount](#sorting-an-income-by-amount-sortincomebyamount)
+ * [Sorting an expense by date](#sorting-an-expense-by-date-sortexpensebydate)
+ * [Sorting an income by date](#sorting-an-income-by-date-sortincomebydate)
+ * [Deleting an expense](#deleting-an-expense-deleteexpense)
+ * [Deleting an income](#deleting-an-income-deleteincome)
+ * [Set budget](#set-budget-setbudget)
+ * [View budget](#view-budget-viewbudget)
+ * [Help command](#help-command-help)
+ * [Exiting the program](#exiting-the-program-exit)
+ * [Saving the data](#saving-the-data)
+4. [FAQ](#faq)
+5. [Command Summary](#command-summary)
+
+
## Introduction
-{Give a product intro}
+
+BrokeMan is a desktop app for managing expenses and income, optimized for use via a Command Line Interface(CLI).
+If you can type fast, BrokeMan can get your expenses and income management tasks done faster than traditional GUI apps.
+
+[back to Contents](#table-of-contents)
+
+---
## Quick Start
-{Give steps to get started quickly}
1. Ensure that you have Java 11 or above installed.
-1. Down the latest version of `Duke` from [here](http://link.to/duke).
+2. Download the latest version of `BrokeMan` from [here](https://github.com/AY2223S2-CS2113-F13-2/tp/releases/tag/v2.1).
+3. Copy the file to the folder you want to use as the home folder for your BrokeMan.
+4. Open a command terminal, `cd` into the folder you put the jar file in, and use the `java -jar CS2113-F13-2_BrokeMan.jar` command to run the application.
+
+ A welcome message as shown below should appear.
+
+ ```
+ | -----------------------------------------------------------------------
+ | -----------------------------------------------------------------------
+ |
+ | $$$$$___ ______ _______ $$_____ _______ $$___$$_ _______ _______
+ | $$__$$__ ______ _______ $$__$$_ _$$$$__ $$$_$$$_ $$$$$__ _______
+ | $$$$$___ $$_$$_ _$$$$__ $$_$$__ $$__$$_ $$$$$$$_ ____$$_ $$$$$__
+ | $$___$$_ $$$_$_ $$__$$_ $$$$___ $$$$$$_ $$_$_$$_ _$$$$$_ $$__$$_
+ | $$___$$_ $$____ $$__$$_ $$_$$__ $$_____ $$___$$_ $$__$$_ $$__$$_
+ | $$$$$$__ $$____ _$$$$__ $$__$$_ _$$$$$_ $$___$$_ _$$$$$_ $$__$$_
+ | Welcome to BrokeMan!
+ | Your personal budget manager to prevent you to become broke like me...
+ |
+ | -----------------------------------------------------------------------
+ | -----------------------------------------------------------------------
+ |
+ | Enter command:
+ ```
+
+5. Type the command in the command box and press Enter to execute it. Refer to the Features below for details of each command. Type `help` to receive a list of commands that are available.
+
+[back to Contents](#table-of-contents)
+
+---
+
+## Features
+
+### Adding an expense: `addExpense`
+Format: `addExpense a/ d/ t/ c/ `
+Adds a new expense to the list of expenses.
+
+* The `amount` should be a double up to **2 decimal places** (dp). If amount has more than 2dp, it will round off to nearest 2dp.
+ * The largest amount that can be entered is `9999999999.99`.
+ * The amount entered must be greater `0`.
+* The `description` should be a String.
+* The `time` should follow `YYYY MM DD HH mm` format.
+ * (Note: entering 3 instead of 03 still works for March.
+ Users can remove preceding 0s, where apt, for simplicity sake)
+ * The oldest time that can be entered is 2000 01 01 00 00.
+ * The furthest time that can be entered is 9999 12 31 23 59.
+* The `category` should be one of the categories in the category list.
+
+**Category list**: FOOD, SHOPPING, GROCERIES, TRANSPORTATION, ENTERTAINMENT, TRAVEL, SALARY, INVESTMENT, and OTHERS
+
+Example of usage:
+`addExpense a/ 4.5 d/ lunch t/ 2023 03 22 20 12 c/ FOOD`
+
+Example output:
+
+```
+| -----------------------------------------------------------------------
+|
+| Enter command: addExpense a/ 4.5 d/ lunch t/ 2023 3 22 20 12 c/ FOOD
+| You have successfully added [$4.50 spent on lunch - 2023-03-22 @ 20:12 [FOOD]]
+|
+| -----------------------------------------------------------------------
+|
+| Enter command:
+```
+
+**Note**: Please do not leave the description of flags empty.
+
+[back to Contents](#table-of-contents)
+
+---
+
+### Adding an income: `addIncome`
+Format: `addIncome a/ d/ t/ c/ `
+Adds a new income to the list of incomes.
+
+* The `amount` should be a double up to **2dp**. If amount has more than 2dp, it will round off to nearest 2dp.
+ * The largest amount that can be entered is `9999999999.99`.
+ * The amount entered must be greater `0`.
+* The `description` should be a String.
+* The `time` should follow `YYYY MM DD HH mm` format.
+ * (Note: entering 3 instead of 03 still works for March.
+ Users can remove preceding 0s, where apt, for simplicity sake)
+ * The oldest time that can be entered is 2000 01 01 00 00.
+ * The furthest time that can be entered is 9999 12 31 23 59.
+* The `category` should be one of the categories in the category list.
+
+**Category list**: FOOD, SHOPPING, GROCERIES, TRANSPORTATION, ENTERTAINMENT, TRAVEL, SALARY, INVESTMENT, and OTHERS
+
+Example of usage:
+`addIncome a/ 400 d/ salary t/ 2023 03 12 15 01 c/ SALARY`
+
+Example output:
+
+```
+| -----------------------------------------------------------------------
+|
+| Enter command: addIncome a/ 400 d/ salary t/ 2023 03 12 15 01 c/ SALARY
+| You have successfully added [$400.00 earned on salary - 2023-03-12 @ 15:01 [SALARY]]
+|
+| -----------------------------------------------------------------------
+|
+| Enter command:
+```
+
+**Note**: Please do not leave the description of flags empty.
+
+[back to Contents](#table-of-contents)
+
+---
+
+### Listing all expenses: `listExpense`
+Format: `listExpense [t/ date]`
+Shows a list of all expenses in the list of expense.
+
+- The `date` should follow `YYYY/MM` format.
+ * The oldest date that can be entered is 2000/01.
+ * The furthest date that can be entered is 9999/12.
+- The time parameter is **optional**. If you add this optional parameter,
+it will show a list of all the expenses incurred in the month specified. If the optional time parameter is not given,
+it will show a list of all expenses across the entire time period.
+
+Example of usage: `listExpense t/ 2023/03` Shows a list of all expenses incurred in the month 2023 March
+
+Example output:
+
+```
+| -----------------------------------------------------------------------
+|
+| Enter command: listExpense t/ 2023/03
+| Here are the expenses you have made for 2023/MARCH.
+| 1. $4.50 spent on lunch - 2023-03-22 @ 20:12 [FOOD]
+| Total expenses: $4.5
+|
+| -----------------------------------------------------------------------
+|
+| Enter command:
+```
+
+[back to Contents](#table-of-contents)
+
+---
+
+### Listing all income: `listIncome`
+Format: `listIncome [t/ date]`
+Shows a list of all income in the list of income.
+
+- The `date` should follow `YYYY/MM` format.
+ * The oldest date that can be entered is 2000/01.
+ * The furthest date that can be entered is 9999/12.
+- The date parameter is **optional**. If you add this optional parameter,
+it will show a list of all income made in the specified month. If the optional date parameter is not given,
+it will show a list of all income across the entire time period.
+
+Example of usage: `listIncome` Shows a list of all income made across the entire time period.
+
+Example output:
+
+```
+| -----------------------------------------------------------------------
+|
+| Enter command: listIncome
+| Here are the income you have made.
+| 1. $3000.00 earned on salary - 2023-03-10 @ 10:10 [SALARY]
+| 2. $3000.00 earned on salary - 2023-02-10 @ 10:10 [SALARY]
+| 3. $3000.00 earned on salary - 2023-01-10 @ 10:10 [SALARY]
+| Total income: $9000.00
+|
+| -----------------------------------------------------------------------
+|
+| Enter command:
+```
+
+[back to Contents](#table-of-contents)
+
+---
+
+### Editing an expense: `editExpense`
+Format: `editExpense i/ t/ n/ `
+* Edits the expense at the specified `index`.
+The index refers to the index number shown in the displayed expense list.
+The index must be a positive integer 1, 2, 3, …
+* The `type` can be: amount, info, time, or category.
+ * amount (of type **double** up to **2dp**. If amount has more than 2dp, it will round off to nearest 2dp.): edits the expense
+ * The largest amount that can be entered is `9999999999.99`.
+ * The amount entered must be greater `0`.
+ * info (of type **String**): edits the description of expense
+ * time (in the format **YYYY MM DD HH mm**): edits the time at which user spent that expense.
+ * (Note: entering 3 instead of 03 still works for March, Users can remove preceding 0s, where apt, for simplicity sake)
+ * The oldest time that can be entered is 2000 01 01 00 00.
+ * The furthest time that can be entered is 9999 12 31 23 59.
+ * category (should be in the category list mentioned in [`addExpense`](#adding-an-expense-addexpense) feature): edits the category of that expense
+* You can only edit one type at a time.
+* Existing values will be updated to the input values.
+
+Example of usage:
+`editExpense i/ 1 t/ amount n/ 5`
+
+Example output:
+
+```
+| -----------------------------------------------------------------------
+|
+| Enter command: editExpense i/ 1 t/ amount n/ 5
+| Successfully edited amount.
+|
+| -----------------------------------------------------------------------
+|
+| Enter command:
+```
+
+**Note**: Please do not leave the description of flags empty.
+
+[back to Contents](#table-of-contents)
+
+---
+
+### Editing an income: `editIncome`
+Format: `editIncome i/ t/ n/ `
+* Edits the income at the specified `index`.
+ The index refers to the index number shown in the displayed income list.
+ The index must be a positive integer 1, 2, 3, ...
+* The `type` can be: amount, info, time, or category.
+ * amount (of type **double** up to **2dp**. If amount has more than 2dp, it will round off to nearest 2dp.): edits the income
+ * The largest amount that can be entered is `9999999999.99`.
+ * The amount entered must be greater `0`.
+ * info (of type **String**): edits the description of income
+ * time (in the format **YYYY MM DD HH mm**): edits the time at which income is earned.
+ * (Note: entering 3 instead of 03 still works for March, Users can remove preceding 0s, where apt, for simplicity sake)
+ * The oldest time that can be entered is 2000 01 01 00 00.
+ * The furthest time that can be entered is 9999 12 31 23 59.
+ * category (should be in the category list mentioned in [`addExpense`](#adding-an-expense-addexpense) feature): edits the category of that income
+* You can only edit one type at a time.
+* Existing values will be updated to the input values.
+
+Example of usage: `editIncome i/ 1 t/ category n/ FOOD`
+
+Example output:
+
+```
+| -----------------------------------------------------------------------
+|
+| Enter command: editIncome i/ 1 t/ category n/ FOOD
+| Successfully edited category.
+|
+| -----------------------------------------------------------------------
+|
+| Enter command:
+```
+
+**Note**: Please do not leave the description of flags empty.
+
+[back to Contents](#table-of-contents)
+
+---
+
+### Sorting an expense by amount: `sortExpenseByAmount`
+Format: `sortExpenseByAmount`
+Shows a list of all expenses in the order of higher amount to lower amount.
+
+Example of usage: `sortExpenseByAmount`
+
+Example output:
+
+```
+|
+| Enter command: sortExpenseByAmount
+| 1. $50.00 spent on dinner - 2023-03-10 @ 18:18 [FOOD]
+| 2. $20.00 spent on brunch - 2023-12-12 @ 12:12 [FOOD]
+| 3. $5.00 spent on lunch - 2023-03-22 @ 14:00 [FOOD]
+| Total expenses: $75.0
+|
+| -----------------------------------------------------------------------
+|
+| Enter command:
+```
+
+**Note**: As long as the input `sortExpenseByAmount` is correct,
+any trailing characters with a space after it will still call the function.
+
+I.e.,`sortExpenseByAmount random characters` will still work.
+
+[back to Contents](#table-of-contents)
+
+---
+
+### Sorting an income by amount: `sortIncomeByAmount`
+Format: `sortIncomeByAmount`
+Shows a list of all income in the order of higher amount to lower amount.
+
+Example of usage: `sortIncomeByAmount`
+
+Example output:
+
+```
+| -----------------------------------------------------------------------
+|
+| Enter command: sortIncomeByAmount
+| 1. $3000.00 earned on stocks - 2023-02-11 @ 01:01 [INVESTMENT]
+| 2. $1000.00 earned on grab - 2023-03-15 @ 15:15 [SALARY]
+| 3. $400.00 earned on salary - 2023-03-12 @ 15:01 [FOOD]
+| Total income: $4400.00
+|
+| -----------------------------------------------------------------------
+|
+| Enter command:
+```
-## Features
+**Note**: As long as the input `sortIncomeByAmount` is correct,
+any trailing characters with a space after it will still call the function.
-{Give detailed description of each feature}
+I.e.,`sortIncomeByAmount random characters` will still work.
-### Adding a todo: `todo`
-Adds a new item to the list of todo items.
+[back to Contents](#table-of-contents)
-Format: `todo n/TODO_NAME d/DEADLINE`
+---
-* The `DEADLINE` can be in a natural language format.
-* The `TODO_NAME` cannot contain punctuation.
+### Sorting an expense by date: `sortExpenseByDate`
+Format: `sortExpenseByDate`
+Shows a list of all expenses in a chronological order.
-Example of usage:
-`todo n/Write the rest of the User Guide d/next week`
-`todo n/Refactor the User Guide to remove passive voice d/13/04/2020`
+Example of usage: `sortExpenseByDate`
+
+Example output:
+
+```
+| -----------------------------------------------------------------------
+|
+| Enter command: sortExpenseByDate
+| 1. $20.00 spent on brunch - 2023-12-12 @ 12:12 [FOOD]
+| 2. $5.00 spent on lunch - 2023-03-22 @ 14:00 [FOOD]
+| 3. $50.00 spent on dinner - 2023-03-10 @ 18:18 [FOOD]
+| Total expenses: $75.0
+|
+| -----------------------------------------------------------------------
+|
+| Enter command:
+```
+
+**Note**: As long as the input `sortExpenseByDate` is correct,
+any trailing characters with a space after it will still call the function.
+
+I.e.,`sortExpenseByDate random characters` will still work.
+
+[back to Contents](#table-of-contents)
+
+---
+
+### Sorting an income by date: `sortIncomeByDate`
+Format: `sortIncomeByDate`
+Shows a list of all income in a chronological order.
+
+Example of usage: `sortIncomeByDate`
+
+Example output:
+
+```
+| -----------------------------------------------------------------------
+|
+| Enter command: sortIncomeByDate
+| 1. $1000.00 earned on grab - 2023-03-15 @ 15:15 [SALARY]
+| 2. $400.00 earned on salary - 2023-03-12 @ 15:01 [FOOD]
+| 3. $3000.00 earned on stocks - 2023-02-11 @ 01:01 [INVESTMENT]
+| Total income: $4400.00
+|
+| -----------------------------------------------------------------------
+|
+| Enter command:
+```
+
+**Note**: As long as the input `sortIncomeByDate` is correct,
+any trailing characters with a space after it will still call the function.
+
+I.e.,`sortIncomeByDate random characters` will still work.
+
+[back to Contents](#table-of-contents)
+
+---
+
+### Deleting an expense: `deleteExpense`
+Format: `deleteExpense `
+* Deletes the expense at the specified `index`.
+* The index refers to the index number shown in the displayed expense list.
+* The index must be a positive integer 1, 2, 3, ...
+
+Example of usage:
+`deleteExpense 2`
+
+Example output:
+
+```
+| -----------------------------------------------------------------------
+|
+| Enter command: deleteExpense 2
+| Successfully deleted.
+|
+| -----------------------------------------------------------------------
+|
+| Enter command:
+```
+
+**Note**: Please do not leave the index empty.
+
+[back to Contents](#table-of-contents)
+
+---
+
+### Deleting an income: `deleteIncome`
+Format: `deleteIncome `
+* Deletes the income at the specified `index`.
+* The index refers to the index number shown in the displayed income list.
+* The index must be a positive integer 1, 2, 3, ...
+
+Example of usage:
+`deleteIncome 2`
+
+Example output:
+
+```
+| -----------------------------------------------------------------------
+|
+| Enter command: deleteIncome 2
+| Successfully deleted.
+|
+| -----------------------------------------------------------------------
+|
+| Enter command:
+```
+
+**Note**: Please do not leave the index empty.
+
+[back to Contents](#table-of-contents)
+
+---
+
+### Set budget: `setBudget`
+Format: `setBudget [t/ date]`
+* The `amount` should be double up to **2dp**. If amount has more than 2dp, it will round off to nearest 2dp.
+ * The largest amount that can be entered is `9999999999.99`.
+ * The amount entered must be greater `0`.
+* The `date` should follow `YYYY/MM` format.
+ * The oldest date that can be entered is 2000/01.
+ * The furthest date that can be entered is 9999/12.
+* The date parameter is **optional**.
+If you add this optional parameter, the budget will only take expenses within the indicated month into consideration.
+If the optional date parameter is not given, it will set the budget for the current month.
+
+Example of usage:
+`setBudget 2000 t/ 2023/05` sets the budget of 2023/05 at 2000.
+`setBudget 2000` sets the budget for the current month.
+
+Example output with optional time flag:
+
+```
+| -----------------------------------------------------------------------
+|
+| Enter command: setBudget 2000 t/ 2023/05
+| You have successfully set $2000.00 as your budget for 2023/MAY.
+|
+| -----------------------------------------------------------------------
+|
+| Enter command:
+```
+
+Example output without optional time flag:
+
+```
+| -----------------------------------------------------------------------
+|
+| Enter command: setBudget 2000
+| You have successfully set $2000.00 as your budget for 2023/APRIL.
+|
+| -----------------------------------------------------------------------
+|
+| Enter command:
+```
+
+**Note**: Please do not leave the description of amount and / or description of optional time flag empty.
+
+[back to Contents](#table-of-contents)
+
+---
+
+### View budget: `viewBudget`
+Format: `viewBudget [t/ date]`
+* The `date` should follow `YYYY/MM` format.
+ * The oldest date that can be entered is 2000/01.
+ * The furthest date that can be entered is 9999/12.
+* The date parameter is **optional**.
+If you add this optional parameter, it shows the budget of the indicated month.
+If the optional date parameter is not given, it will show the budget for the current month.
+
+Example of usage:
+`viewBudget t/ 2023/05` shows the budget in 2023/05.
+`viewBudget` shows the budget in the current month.
+
+Example output with optional time flag:
+
+```
+| -----------------------------------------------------------------------
+|
+| Enter command: viewBudget t/ 2023/05
+| You have set your budget as $2000.00 for 2023/MAY.
+| The amount of budget left is $2000.00
+|
+| -----------------------------------------------------------------------
+|
+| Enter command:
+```
+
+Example output without optional time flag:
+
+```
+| -----------------------------------------------------------------------
+|
+| Enter command: viewBudget
+| You have set your budget as $2000.00 for 2023/APRIL.
+| The amount of budget left is $2000.00
+|
+| -----------------------------------------------------------------------
+|
+| Enter command:
+```
+
+**Note**: Please do not leave the description of time flag empty.
+
+[back to Contents](#table-of-contents)
+
+---
+
+### Help command: `help`
+Format: `help` Shows all the help messages for all features.
+
+**Help message will appear when invalid command is entered.**
+
+**This is to help users maximize their benefit from the application by utilizing it as designed.**
+
+Example of usage: `help`
+
+Example output:
+
+```
+| -----------------------------------------------------------------------
+|
+| Enter command: help
+| addExpense: add expense to the expense list.
+| Parameters: a/ d/ t/ c/
+| Valid categories are: FOOD, SHOPPING, GROCERIES, TRANSPORTATION, ENTERTAINMENT, TRAVEL, SALARY, INVESTMENT, and OTHERS
+| Example: addExpense a/ 4.5 d/ lunch t/ 2023 03 22 20 12 c/ FOOD
+|
+| addIncome: add income to the income list.
+| Parameters: a/ d/ t/ c/
+| Valid categories are: FOOD, SHOPPING, GROCERIES, TRANSPORTATION, ENTERTAINMENT, TRAVEL, SALARY, INVESTMENT, and OTHERS
+| Example: addIncome a/ 3000 d/ salary t/ 2023 03 10 10 10 c/ SALARY
+|
+| deleteExpense: deletes an expense from the list
+| Parameter:
+| Example: deleteExpense 1
+|
+| deleteIncome: deletes an income from the list
+| Parameter:
+| Example: deleteIncome 1
+|
+| editExpense: edits the expense from the list.
+| Parameter: i/ t/ n/
+| There are 4 types that can be changed, amount, info, time, category
+| Example: editExpense i/ 1 t/ amount n/ 5
+|
+| editIncome: edits the income from the list.
+| Parameter: i/ t/ n/
+| There are 4 types that can be changed, amount, info, time, category
+| Example: editIncome i/ 1 t/ info n/ stocks
+|
+| listExpense: lists expenses made in the current month.
+| listExpense t/ : : lists expenses made in the specified month
+| Optional Parameter: t/
+| Example: listExpense
+| Example: listExpense t/ 2023/03
+|
+| listIncome: lists incomes made in the current month.
+| listIncome t/ : : lists incomes made in the specified month
+| Optional Parameter: t/
+| Example: listIncome
+| Example: listIncome t/ 2023/03
+|
+| setBudget: sets your budget for current month.
+| setBudget t/ : sets your budget for specified month
+| Compulsory Parameter:
+| Optional Parameter: t/
+| Example: setBudget 500
+| Example: setBudget 500 t/ 2023/03
+|
+| viewBudget: view your budget for the current month and how much of it is left remaining.
+| viewBudget t/ : view your budget and how much of was left in the specified month
+| Optional Parameter: t/
+| Example: viewBudget
+| Example: viewBudget t/ 2023/03
+|
+| sortExpenseByAmount: shows the expenses made, sorted by amount of expense
+| Example: sortExpenseByAmount
+|
+| sortIncomeByAmount: shows the incomes made, sorted by amount of income
+| Example: sortIncomeByAmount
+|
+| sortExpenseByDate: shows the expenses made, sorted by date of expense
+| Example: sortExpenseByDate
+|
+| sortIncomeByDate: shows the incomes made, sorted by date of income
+| Example: sortIncomeByDate
+|
+| help: shows all the commands for the program.
+| Example: help
+|
+| exit: exits the program
+| Example: exit
+|
+| -----------------------------------------------------------------------
+|
+| Enter command:
+```
+
+[back to Contents](#table-of-contents)
+
+---
+
+### Exiting the program: `exit`
+Format: `exit` Exits the program, and save the data.
+
+Example of usage: `exit`
+
+Example output:
+
+```
+| -----------------------------------------------------------------------
+|
+| Enter command: exit
+| Exiting program...
+|
+| -----------------------------------------------------------------------
+| -----------------------------------------------------------------------
+|
+| ___ _ ___ _ _
+| / __| ___ ___ __| | o O O | _ ) | || | ___
+| | (_ | / _ \ / _ \ / _` | o | _ \ \_, | / -_)
+| \___| \___/ \___/ \__,_| TS__[O] |___/ _|__/ \___|
+| _|"""""|_|"""""|_|"""""|_|"""""| {======|_|"""""|_| """"|_|"""""|
+| "`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'./o--000'"`-0-0-'"`-0-0-'"`-0-0-'
+|
+| -----------------------------------------------------------------------
+| -----------------------------------------------------------------------
+```
+
+[back to Contents](#table-of-contents)
+
+---
+
+### Saving the data
+BrokeMan data are saved in text files ExpenseData, IncomeData and BudgetData upon exiting the program.
+
+**TRY NOT TO MAKE CHANGES TO THE .txt FILE GENERATED BY THE PROGRAM!**
+
+**INCORRECT ROW FORMAT OF .txt FILE WILL BE IGNORED DURING THE POPULATION OF DATA AT THE START OF THE PROGRAM!**
+
+[back to Contents](#table-of-contents)
+
+---
+
+### Get total expenses and income by category `[COMING SOON]`
+
+### Wishlist `[COMING SOON]`
+
+### Spending Advisor `[COMING SOON]`
+
## FAQ
**Q**: How do I transfer my data to another computer?
-**A**: {your answer here}
+**A**: Install the app in the other computer and overwrite the empty data file it creates with the file that contains the data of your previous BrokeMan home folder.
+
+[back to Contents](#table-of-contents)
## Command Summary
-{Give a 'cheat sheet' of commands here}
+| Action | Format, Examples |
+|-----------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| **Add** | `addExpense a/ d/ t/ c/ ` eg., `addExpense a/ 4.5 d/ lunch t/ 2023 03 22 20 12 c/ FOOD` `addIncome a/ d/ t/ c/ ` eg., `addIncome a/ 400 d/ salary t/ 2023 03 12 15 01 c/ SALARY` |
+| **List** | `listExpense [t/ time]` eg., `listIncome t/ 2023/03` `listIncome [t/ time]` eg., `listIncome` |
+| **Edit** | `editExpense i/ t/ n/ ` eg., `editExpense i/ 1 t/ amount n/ 7` `editIncome i/ t/ n/ ` e.g., `editIncome i/ 1 t/ info n/ pay` |
+| **Sort** | `sortExpenseByAmount` `sortExpenseByTime` `sortIncomeByAmount` `sortExpenseByAmount` |
+| **Delete** | `deleteExpense ` eg., `deleteExpense 2` `deleteIncome ` eg., `deleteIncome 2` |
+| **Set Budget** | `setBudget [t/ time]` eg., `setBudget 2000 t/ 2023/04` eg., `setBudget 4000` |
+| **View Budget** | `viewBudget [t/ time]` eg., `viewBudget t/ 2023/02` eg., `viewBudget` |
+| **Exit** | `exit` |
+| **Help** | `help` |
-* Add todo `todo n/TODO_NAME d/DEADLINE`
+[back to Contents](#table-of-contents)
diff --git a/docs/diagrams/ArchitectureDiagram.puml b/docs/diagrams/ArchitectureDiagram.puml
new file mode 100644
index 0000000000..43b50ce3e1
--- /dev/null
+++ b/docs/diagrams/ArchitectureDiagram.puml
@@ -0,0 +1,38 @@
+@startuml
+!include
+hide circle
+hide member
+
+
+
+Package "BrokeMan"<>{
+ class Ui
+ class Command
+ class Parser
+ class Save
+ class EntryList
+ class Budget
+ Package " " <> {
+ class Exception
+ class Common
+ }
+}
+
+class "<$user>" as User
+
+User <-> Ui
+Ui -> Parser
+Parser --> Command
+Command --> EntryList
+Command -left-> Budget
+Command -right-> Save
+Save <-> Data
+Save -left> EntryList
+Save --> Budget
+EntryList -> Ui
+Budget -> Ui
+Parser -[hidden]> Common
+Common -[hidden]> Exception
+
+
+@enduml
\ No newline at end of file
diff --git a/docs/diagrams/BudgetClassUML.puml b/docs/diagrams/BudgetClassUML.puml
new file mode 100644
index 0000000000..3719885091
--- /dev/null
+++ b/docs/diagrams/BudgetClassUML.puml
@@ -0,0 +1,35 @@
+@startuml
+skinparam classAttributeIconSize 0
+hide circle
+hide empty members
+
+class Budget {
+ - budgetEachMonth: HashMap>
+ + viewBudget(dateInString: Optional): void
+ + viewBudgetOfMonth(yearOfInterest: int, monthOfInterest: Month): void
+ + hasSetBudgetThisMonth(): boolean
+ + setBudget(budgetAmount: double, dateInString: Optional): void
+}
+
+class ViewBudgetCommand {
+ - date: Optional
+ + ViewBudgetCommand(date: String)
+ + ViewBudgetCommand()
+ + execute(): void
+}
+
+class SetBudgetCommand {
+ - budget: double
+ - date: Optional
+ + SetBudgetCommand(budget: double)
+ + SetBudgetCommand(budget: double, date: String)
+ + execute(): void
+}
+
+class hasNotSetBudgetException {
+}
+
+Budget <.. ViewBudgetCommand: uses <
+Budget <.. SetBudgetCommand: uses <
+ViewBudgetCommand ..> hasNotSetBudgetException: <>
+@enduml
diff --git a/docs/diagrams/DeleteCommandSequenceDiagram.puml b/docs/diagrams/DeleteCommandSequenceDiagram.puml
new file mode 100644
index 0000000000..4df8a319e1
--- /dev/null
+++ b/docs/diagrams/DeleteCommandSequenceDiagram.puml
@@ -0,0 +1,138 @@
+@startuml
+
+Actor User as user
+box execution of commands
+Participant ":Ui" as ui
+Participant ":Parser" as parser
+Participant "delete:Command" as deleteCommand
+Participant "exit:Command" as exitCommand
+Participant ":EntryList" as entryList
+Participant ":Save" as save
+Participant ":Budget" as budget
+Participant ":Data" as data
+end box
+
+
+user -> ui : "deleteExpense 1"
+activate ui
+
+ui -> parser : parseCommand("deleteExpense 1")
+activate parser
+
+parser -> parser : prepareDeleteExpenseCommand(1)
+activate parser
+
+create deleteCommand
+parser -> deleteCommand : DeleteExpenseCommand(1)
+activate deleteCommand
+
+deleteCommand --> parser : delete
+deactivate deleteCommand
+
+
+parser --> parser : delete
+deactivate parser
+
+parser --> ui : delete
+deactivate parser
+
+ui -> deleteCommand : execute()
+activate deleteCommand
+
+deleteCommand -> entryList : deleteExpense(1)
+activate entryList
+
+entryList --> deleteCommand
+deactivate entryList
+
+deleteCommand --> ui
+deactivate deleteCommand
+destroy deleteCommand
+
+
+ui --> user
+deactivate ui
+
+user -> ui : "exit"
+activate ui
+
+'ui -> parser : execute("exit")
+'activate parser
+
+
+ui -> parser : parseCommand("exit");
+activate parser
+
+create exitCommand
+parser -> exitCommand : ExitCommand()
+activate exitCommand
+
+exitCommand --> parser : exit
+deactivate exitCommand
+
+parser --> ui : exit
+deactivate parser
+destroy parser
+
+ui -> exitCommand : execute()
+activate exitCommand
+
+exitCommand -> save : writeFile(expenseList)
+activate save
+
+save -> entryList : expenseList
+activate entryList
+entryList --> save
+deactivate entryList
+save -> data : Save to file
+activate data
+data --> save
+deactivate data
+
+save --> exitCommand
+deactivate save
+
+exitCommand -> save : writeFile(incomeList)
+activate save
+
+
+save -> entryList : incomeList
+activate entryList
+entryList --> save
+deactivate entryList
+destroy entryList
+save -> data : Save to file
+activate data
+data --> save
+deactivate data
+
+save --> exitCommand
+deactivate save
+
+exitCommand -> save : writeFile(budgetEachMonth)
+activate save
+
+
+save -> budget : budgetEachMonth
+activate budget
+budget --> save
+deactivate budget
+destroy budget
+save -> data : Save to file
+activate data
+data --> save
+deactivate data
+
+save --> exitCommand
+deactivate save
+destroy save
+
+exitCommand --> ui
+deactivate exitCommand
+destroy exitCommand
+
+ui --> user
+deactivate ui
+destroy ui
+
+@enduml
\ No newline at end of file
diff --git a/docs/diagrams/EntryClassDiagram.puml b/docs/diagrams/EntryClassDiagram.puml
new file mode 100644
index 0000000000..0a3732fe85
--- /dev/null
+++ b/docs/diagrams/EntryClassDiagram.puml
@@ -0,0 +1,55 @@
+@startuml
+skinparam classAttributeIconSize 0
+hide circle
+hide empty members
+
+class "{abstract}\nEntry" as entry
+
+class entry {
+ #info: String
+ #amount: double
+ #time: LocalDateTime
+ #category: Category
+
+ #convertTimeToString(): String
+ #isSameMonth(year: int, month: Month): boolean
+ +Entry(amount: double, info: String, time: LocalDateTime, category: Category)
+ +editAmount(newAmount: double): void
+ +editDescription(newInfo: String): void
+ +editTime(newTime: LocalDateTime): void
+ +editCategory(newCategory: Category): void
+ +getAmount(): double
+ +getTime(): String
+ +getCategory(): Category
+ +getInfo(): String
+ {abstract} +toString(): String
+}
+
+class Expense {
+ +Expense(amount: double, info: String, time: LocalDateTime, category: Category)
+ +toString(): String
+}
+
+class Income {
+ +Income(amount: double, info: String, time: LocalDateTime, category: Category)
+ +toString(): String
+}
+
+class Category <> {
+ FOOD
+ SHOPPING
+ GROCERIES
+ TRANSPORTATION
+ ENTERTAINMENT
+ TRAVEL
+ SALARY
+ INVESTMENT
+ OTHERS
+}
+
+entry <|-- Expense
+entry <|-- Income
+
+entry .> Category
+
+@enduml
\ No newline at end of file
diff --git a/docs/diagrams/EntryListClassDiagram.puml b/docs/diagrams/EntryListClassDiagram.puml
new file mode 100644
index 0000000000..d23338dd17
--- /dev/null
+++ b/docs/diagrams/EntryListClassDiagram.puml
@@ -0,0 +1,28 @@
+@startuml
+'https://plantuml.com/class-diagram
+
+hide circle
+hide member
+
+class "{abstract}\nEntryList" as entryList
+
+entryList *-left-> "1" ExpenseList
+
+entryList *--> EntryXYZComparator
+note left of EntryXYZComparator: EntryXYZComparator = EntryAmountComparator, \nEntryDateComparator
+
+entryList *-left-> "1" IncomeList
+
+IncomeList -[hidden]down- ExpenseList
+
+
+ExpenseList -right-> "*" Expense
+IncomeList -right-> "*" Income
+
+
+Expense -down-> "1" category
+Income -down-> "1" category
+
+
+
+@enduml
\ No newline at end of file
diff --git a/docs/diagrams/EntryListFulllClassDiagram.puml b/docs/diagrams/EntryListFulllClassDiagram.puml
new file mode 100644
index 0000000000..7d807ca806
--- /dev/null
+++ b/docs/diagrams/EntryListFulllClassDiagram.puml
@@ -0,0 +1,74 @@
+@startuml
+hide circle
+skinparam classAttributeIconSize 0
+hide empty members
+
+class "{abstract}\nEntryList" as entryList
+
+class entryList {
+ +addEntry(newEntry: Entry, entryList: LinkedList): void
+ +listEntry(entryList: List): void
+ +deleteEntry(entryIndex: int, entryList, LinkedList): void
+ +editEntryCost(entryIndex: int, newAmount: double, entryList: LinkedList entryList): void
+ +editEntryDescription(entryIndex: int, newDescription: String, entryList: LinkedList): void
+ +editEntryTime(entryIndex: int, newTime, LocalDateTime, entryList: LinkedList): void
+ +editEntryCategory(entryIndex: int, newCategory: Category, entryList: LinkedList): void
+ +sortEntriesByAmount(entryList: LinkedList): void
+ +sortEntriesByDate(entryList: LinkedList): void
+ +findEntriesByCategory(category: Category, entryList: LinkedList): void
+ +selectEntryForDate(year: int, month: Month, entryList: LinkedList): Link
+ +getEntryListSum(entryList: List): double
+}
+
+class IncomeList {
+ +incomeList: LinkedList
+ +addIncome(newIncome Income): void
+ +deleteIncome(index: int): void
+ +listIncome(date: Optional): void
+ +listIncome(): void
+ +editIncome(index: int, newEntry: String): void
+ +editIncome(index: int, newEntry: Double): void
+ +editIncome(index: int, newEntry: LocalDateTime): void
+ +editIncome(index: int, newEntry: Category): void
+ +sortIncomeByAmount(): void
+ +sortIncomeByDate(): void
+ +findIncomeByCategory(category: Category):
+ +getIncomeMadeInMonth(year: int, month: Month): Link
+}
+
+class ExpenseList {
+ +expenseList: LinkedList
+ +addExpense(newExpense Expense): void
+ +deleteExpense(index: int): void
+ +listExpense(date: Optional): void
+ +ListExpense(): void
+ +editExpense(index: int, newEntry: String): void
+ +editExpense(index: int, newEntry: Double): void
+ +editExpense(index: int, newEntry: LocalDateTime): void
+ +editExpense(index: int, newEntry: Category): void
+ +sortExpenseByAmount(): void
+ +sortExpenseByDate(): void
+ +findExpenseByCategory(category: Category):
+ +getExpenseMadeInMonth(year: int, month: Month): Link
+}
+
+class Category <> {
+ FOOD
+ SHOPPING
+ GROCERIES
+ TRANSPORTATION
+ ENTERTAINMENT
+ TRAVEL
+ SALARY
+ INVESTMENT
+ OTHERS
+}
+
+entryList <|-- IncomeList
+entryList "1" .left.> "*" "{abstract}\nEntry"
+entryList <|-- ExpenseList
+entryList "1" .right> "*" Category
+IncomeList "1" -> "*" "{abstract}\nEntry"
+ExpenseList "1" -> "*" "{abstract}\nEntry"
+
+@enduml
\ No newline at end of file
diff --git a/docs/diagrams/ParserClassDiagram.puml b/docs/diagrams/ParserClassDiagram.puml
new file mode 100644
index 0000000000..56705f64af
--- /dev/null
+++ b/docs/diagrams/ParserClassDiagram.puml
@@ -0,0 +1,50 @@
+@startuml
+skinparam classAttributeIconSize 0
+hide circle
+
+class "{abstract}\nParser" as parser
+class "{abstract}\nCommand" as command
+class "{abstract}\nUserInput" as userInput
+
+class parser {
+ -prepareViewBudgetCommand(description: String): Command
+ -prepareSetBudgetCommand(description: String): Command
+ -prepareDeleteExpenseCommand(description: String): Command
+ -prepareDeleteIncomeCommand(description: String): Command
+ -prepareAddExpenseCommand(description: String): Command
+ -prepareAddIncomeCommand(description: String): Command
+ -prepareListExpenseCommand(description: String): Command
+ -prepareListIncomeCommand(description: String): Command
+ -prepareEditExpenseCommand(description: String): Command
+ -prepareEditIncomeCommand(description: String): Command
+ -checkAddCommandException(description: String): String[]
+ -checkExceedMaxCharForAmount(description: String): String
+ -checkValidOptionalTimeFlagException(description: String): String
+ -checkEmptyAddFlag(splitDescriptions: String[]): void
+ -checkSmallerThanMinTime(date: String): void
+ -checkEmptyFlag(splitDescriptions: String[]): void
+ -checkTimeException(timeToCheck: String): void
+ -checkDoubleException(cost: String): void
+ -checkEditCommandException(description: String): String[]
+ -checkContainFlagsException(newDescription: String): void
+ -checkIsIntegerIndex(index: String): void
+ -checkCorrectType(type: String): void
+ +parseCommand(userFullInput: String): Command
+}
+
+class command {
+ {abstract} +execute(): void
+}
+
+class userInput {
+ #userCommand: String
+ #userDescription: String
+
+ #UserInput(String, String)
+ #splitUserInput(): UserInput
+}
+
+parser --> "1" command
+parser .> "1" userInput : uses >
+
+@enduml
\ No newline at end of file
diff --git a/docs/diagrams/UiClassDiagram.puml b/docs/diagrams/UiClassDiagram.puml
new file mode 100644
index 0000000000..7e2759c3c8
--- /dev/null
+++ b/docs/diagrams/UiClassDiagram.puml
@@ -0,0 +1,37 @@
+@startuml
+skinparam classAttributeIconSize 0
+hide circle
+hide empty members
+
+class BrokeMan {
+ -run(): void
+ -runCommandUntilExit(): void
+ +main(args: String[]): void
+}
+
+class Ui <> {
+ -in: Scanner
+ -out: PrintStream
+ -LINE_DIVIDER: String
+ -LINE_PREFIX: String
+
+ +getUserCommand(): String
+ +showWelcomeMessages(): void
+ +showGoodByeMessages(): void
+ +showToUserWithLineBreak(messages: String...): void
+ +showToUser(messages: String...): void
+}
+
+class Scanner {
+ +nextLine()
+}
+
+class PrintStream {
+ +println()
+}
+
+BrokeMan .> "1" Ui
+Ui .. Scanner : gets input from >
+Ui .. PrintStream : prints to >
+
+@enduml
\ No newline at end of file
diff --git a/docs/images/ArchitectureDiagram.png b/docs/images/ArchitectureDiagram.png
new file mode 100644
index 0000000000..095a0b2cd0
Binary files /dev/null and b/docs/images/ArchitectureDiagram.png differ
diff --git a/docs/images/BudgetClassUML.png b/docs/images/BudgetClassUML.png
new file mode 100644
index 0000000000..8cffeb5a76
Binary files /dev/null and b/docs/images/BudgetClassUML.png differ
diff --git a/docs/images/DeleteCommandSequenceDiagram.png b/docs/images/DeleteCommandSequenceDiagram.png
new file mode 100644
index 0000000000..74321d5a31
Binary files /dev/null and b/docs/images/DeleteCommandSequenceDiagram.png differ
diff --git a/docs/images/EntryClassDiagram.png b/docs/images/EntryClassDiagram.png
new file mode 100644
index 0000000000..4cbe7e7cbd
Binary files /dev/null and b/docs/images/EntryClassDiagram.png differ
diff --git a/docs/images/EntryListClassDiagram.png b/docs/images/EntryListClassDiagram.png
new file mode 100644
index 0000000000..4d8ae31097
Binary files /dev/null and b/docs/images/EntryListClassDiagram.png differ
diff --git a/docs/images/EntryListFulllClassDiagram.png b/docs/images/EntryListFulllClassDiagram.png
new file mode 100644
index 0000000000..4da95f8bc4
Binary files /dev/null and b/docs/images/EntryListFulllClassDiagram.png differ
diff --git a/docs/images/ParserClassDiagram.png b/docs/images/ParserClassDiagram.png
new file mode 100644
index 0000000000..3772299abf
Binary files /dev/null and b/docs/images/ParserClassDiagram.png differ
diff --git a/docs/images/UiClassDiagram.png b/docs/images/UiClassDiagram.png
new file mode 100644
index 0000000000..64aac657e2
Binary files /dev/null and b/docs/images/UiClassDiagram.png differ
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/namsengi11.md b/docs/team/namsengi11.md
new file mode 100644
index 0000000000..63a6c5a463
--- /dev/null
+++ b/docs/team/namsengi11.md
@@ -0,0 +1,65 @@
+# Sangjun Nam - Project Portfolio Page
+
+### Summary of Contributions
+
+BrokeMan is a desktop app for managing expenses, incomes, and personal budget, optimized for use via a Command Line Interface(CLI). If you can type fast, BrokeMan can get your expenses and income management tasks done faster than traditional GUI apps.
+
+---
+
+### Code Contributed
+
+You can take a closer look at my code contribution in the link below:
+
+[Code Contribution Statistic](https://nus-cs2113-ay2223s2.github.io/tp-dashboard/?search=&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2023-02-17&tabOpen=true&tabType=authorship&tabAuthor=namsengi11&tabRepo=AY2223S2-CS2113-F13-2%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false)
+
+---
+
+### Enhancements implemented
+
+I have helped implement the backbone of the EntryList class that is used to functionalize the IncomeList and ExpenseList that controls the list of entries made by the user. I have came up with the ideas to how the Entry class can be implemented. I have implemented the Expense and ExpenseList class - which tracks the expenses made by the user - myself.
+
+I have also added the time component to the Expense, Income, and Budget class. I have made use of the java.time package, LocalDate and LocalDateTime for example. I have added them as attribute for the Entry class to add the time aspect to all entries made by the user. I made use of methods provided by the classes of java.time to add, sort, and find entries made at a certain time.
+
+I have also implemented how the budget data of different months are kept track. I utilized a double hashmap that uses years and months as a key to retrieve budget information of the specific week.
+
+I also contributed to commands that handle adding, editing, and sorting the expenses and incomes, as well as setting and viewing budget of specific months.
+
+I have also created and handled exceptions that might arise from users entering commands, such as error in entering the time information or parsing the time information. An example of this is handling exceptions that arises from date input that is outside the range set by the program.
+
+I developed JUnit tests for the Parser class, as well as the Classes in the Entry Package, which controls the input/output flow of the program. I wrote test cases to ensure that most, if not all, methods of the classes are tested. I also wrote testcases for io testing using runtest.bat and runtest.sh.
+
+I also resolved issues raised by the PE_Dry by throwing and catching new exceptions and adding comments on the error message.
+
+---
+
+### Contributions to the DG
+
+I added how features like EntryList and their subclasses, as well as the budget class are implemented to the program. I elaborated on how the abstract classes provide underlying methods for its subclasses.
+
+I collaborated with other teammates on the descriptions of the features and structure implemented.
+
+I added description of the features to be added in the future.
+
+I described the Project Scope and the User Story of the product.
+
+I added User Stories for BrokeMan, from currently implemented features to features coming soon.
+
+---
+
+### Contributions to the UG
+
+I added and edited descriptions of our commands to resolve the issues that arose from the PE-D.
+
+I also added UML diagram that represents the Budget Class and its dependencies.
+
+---
+
+### Contribution to team-tasks
+
+I led discussion on how the Entry package will be implemented in our program, from the super/sub class relationships and the data structure to store the information.
+
+I have initiated team meetings and proposed features to be implemented in each iterations.
+
+I have set up the github repository for our team, and managed the development goals of each iterations.
+
+I have led and created the release v1.0, which laid foundation to our program.
diff --git a/docs/team/samueltansw.md b/docs/team/samueltansw.md
new file mode 100644
index 0000000000..b1f4db17bc
--- /dev/null
+++ b/docs/team/samueltansw.md
@@ -0,0 +1,113 @@
+# Samuel Tan - Project Portfolio Page
+
+## Table of Contents
+
+1. [Overview](#overview)
+2. [Summary of Contributions](#summary-of-contributions)
+ - [Code Contributions](#code-contributions)
+ - [Enhancements to BrokeMan](#enhancements-to-brokeman)
+ - [Contributions to UG](#contributions-to-ug)
+ - [Contributions to DG](#contributions-to-dg)
+ - [Contribution to team-based tasks](#contribution-to-team-based-tasks)
+ - [Contributions beyond the project team](#contributions-beyond-the-project-team)
+
+## Overview
+
+Hello I'm Samuel Tan, an aspiring Computer Engineer studying at National University of Singapore.
+
+Together with my group members, we have created a product known as BrokeMan, which is a personal
+budget manager.
+
+[back to contents](#table-of-contents)
+
+---
+
+## Summary of Contributions
+
+I have contributed a **majority** of the enhancements to BrokeMan, instructions in User Guide (UG), and explanations in Developer Guide (DG).
+
+The following below is the breakdown of my contributions to the team project BrokeMan.
+
+### Code Contributions
+
+- Here is an in-depth look at my [code contribution](https://nus-cs2113-ay2223s2.github.io/tp-dashboard/?search=Samueltansw&breakdown=true&sort=groupTitle%20dsc&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other&since=2023-02-17&tabOpen=true&tabType=authorship&tabAuthor=Samueltansw&tabRepo=AY2223S2-CS2113-F13-2%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code~other&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false).
+
+[back to contents](#table-of-contents)
+
+---
+
+### Enhancements to BrokeMan
+
+1. Implemented the **entire** [BrokeMan](https://github.com/AY2223S2-CS2113-F13-2/tp/blob/master/src/main/java/seedu/brokeMan/BrokeMan.java) (main) class which determines how the program runs.
+2. Implemented the **entire** [Ui](https://github.com/AY2223S2-CS2113-F13-2/tp/blob/master/src/main/java/seedu/brokeMan/ui/Ui.java) class to read input from the users and format the outputs to the users.
+3. Implemented **almost all** the [Parser](https://github.com/AY2223S2-CS2113-F13-2/tp/blob/master/src/main/java/seedu/brokeMan/parser/Parser.java) Class to format and make sense of the user inputs. As well as to handle exceptions.
+4. Implemented **almost all** the [command](https://github.com/AY2223S2-CS2113-F13-2/tp/tree/master/src/main/java/seedu/brokeMan/command) classes and integrated the various command features of the product together,
+ensuring the working of the product.
+5. Helped with some parts of exception handling for SaveExpense and SaveBudget classes.
+6. Implemented **almost all** the [exception](https://github.com/AY2223S2-CS2113-F13-2/tp/tree/master/src/main/java/seedu/brokeMan/exception) classes to determine the error messages displayed to users when invalid inputs are entered.
+7. Implemented the **almost all** the [Messages](https://github.com/AY2223S2-CS2113-F13-2/tp/blob/master/src/main/java/seedu/brokeMan/common/Messages.java) class under the common package which contains the various messages to be displayed to user.
+8. Handled **almost all** the exceptions for the program to prevent crashing.
+Most of the exceptions handling are done in the Parser class.
+9. Implemented most of the JUnit tests for [ParserTest.java](https://github.com/AY2223S2-CS2113-F13-2/tp/blob/master/src/test/java/seedu/brokeMan/parser/ParserTest.java).
+10. Resolved **almost all** the PE-Dry run [issues](https://github.com/AY2223S2-CS2113-F13-2/tp/issues?q=is%3Aissue+is%3Aclosed) for BrokeMan.
+
+[back to contents](#table-of-contents)
+
+---
+
+### Contributions to UG
+
+1. Set up the Table of Contents (TOC) to work properly such that it can link to the appropriate sections,
+and added link back to the TOC at the end of every section.
+2. Added the example outputs for **all** features of the product as well as the welcome message.
+3. Contributed a **large portion** (more than half) of the instructions for **all** the [features](../UserGuide.md#features) in the UG such as `deleteExpense`, `deleteIncome`, `addExpense`, etc.
+4. Helped with the formatting of [Command Summary](../UserGuide.md#command-summary)
+
+[back to contents](#table-of-contents)
+
+---
+
+### Contributions to DG
+
+1. Set up the skeleton of DG with appropriate headers for everything.
+2. Set up the TOC to work properly such that it can link to the appropriate sections.
+3. Added the [Acknowledgements](../DeveloperGuide.md#acknowledgements) section which acknowledges the external libraries we have used
+4. Added the entire [Architecture](../DeveloperGuide.md#architecture)
+section of the DG to explain how the classes of our program interacts with each other.
+5. Added the entire [Ui](../DeveloperGuide.md#ui-component) section of the DG.
+6. Added most of the content in [Parser](../DeveloperGuide.md#parser-component) section of the DG, including the UML class diagram of parser.
+7. Added the entire [Command](../DeveloperGuide.md#command-component) section of the DG.
+7. Added the [Common class](../DeveloperGuide.md#common-class) section of the DG.
+8. Contributed to Target user profile under `Appendix: Requirements` of DG.
+9. Contributed to Value proposition under `Appendix: Requirements` of DG.
+10. Contributed to User Stories under `Appendix: Requirements` of DG.
+11. Contributed to User Stories under `Appendix: Requirements` of DG.
+12. Contributed to Glossary under `Appendix: Requirements` of DG.
+13. Added the entire [Appendix: Instructions for manual testing](../DeveloperGuide.md#appendix-instructions-for-manual-testing) of DG.
+14. Added the UML class diagram for [Entry](../DeveloperGuide.md#entry) under the implementation section of DG.
+15. Added the UML class diagram for [EntryList](../DeveloperGuide.md#entrylist) under the implementation section of DG.
+
+[back to contents](#table-of-contents)
+
+---
+
+### Contribution to team-based tasks
+
+- Contributes to team based tasks:
+ - Maintaining issue tracker
+ - Release Management (v2.0)
+
+[back to contents](#table-of-contents)
+
+---
+
+### Contributions beyond the project team
+
+- Helped solved bugs in the methods of IncomeLists, and ExpenseLists when I help to integrate the
+classes together for a working program. This happened at the early stage of our project when only the classes and methods for
+Expense and Income are implemented, without any main, Ui, Parser, or Command classes. I implemented the rest of the classes and
+made sure the features of expense and income are working as intended and that the program runs together with all the other classes.
+
+- Helped my group members linked their github accounts and portfolio in [AboutUs.md](../AboutUs.md), as well as updating the [Readme.md](../README.md).
+
+[back to contents](#table-of-contents)
diff --git a/docs/team/sistine-yu.md b/docs/team/sistine-yu.md
new file mode 100644
index 0000000000..fb0641d00d
--- /dev/null
+++ b/docs/team/sistine-yu.md
@@ -0,0 +1,40 @@
+# Yu Sichen (Sistine) - Project Portfolio Page
+
+## Overview
+
+### Project: BrokeMan
+
+BrokeMan is a desktop app for managing expenses, incomes, and personal budget, optimized for use via a Command Line Interface(CLI).
+It is suitable for students who have to work with tight budgets, use their money efficiently, and minimize their spendings.
+
+## Summary of contributions
+
+### Code contributed
+
+Click [here](https://nus-cs2113-ay2223s2.github.io/tp-dashboard/?search=&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2023-02-17&tabOpen=true&tabType=authorship&zFR=false&tabAuthor=sistine-yu&tabRepo=AY2223S2-CS2113-F13-2%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false) to see my code contribution.
+
+### Enhancements implemented
+
+- I made contributions to the back end of our team's application.
+I designed the income class and the list that stores incomes. After spotting the similarities between my work and my teammates' work, I worked on the refactoring of entry class and entrylist class to make the program structure more resilient and easier to test.
+- I intensively participated in implementing the features of listing, adding, editing, deleting entries.
+- I implemented the category class and handled the exception. I also designed the find category method though it's one of the incoming features.
+- I wrote part of the testing codes for entry packages.
+
+### Contributions to the UG
+
+- I established the foundational layout of User Guide including introduction, quick start, all the features, FAQ and command summary.
+
+### Contributions to the DG
+
+- I completed the component parts in DG and updated the class diagram and sequence diagram, except the UI component.
+- I scrutinized the DG texts and corrected some errors.
+
+### Contributions to team-based tasks
+
+- I was in charge of maintaining the issue trackers and milestone establishment.
+
+### Contributions beyond the project team
+
+- I reviewed the functional codes and developer guides of other teams and gave constructional feedback.
+- I tested out other teams' projects and produced informative bug reports.
\ No newline at end of file
diff --git a/docs/team/speciliam.md b/docs/team/speciliam.md
new file mode 100644
index 0000000000..29662088e0
--- /dev/null
+++ b/docs/team/speciliam.md
@@ -0,0 +1,39 @@
+# Liam - Project Portfolio Page
+
+## Overview
+
+### Project: BrokeMan
+
+BrokeMan is a desktop app for managing expenses, incomes, and personal budget, optimized for use via a Command Line Interface(CLI).
+This program works well for people who need to manage expenses in a very straight forward and intuitive manner.
+It is purposely built to display in a very easily readble way.
+Such users may include students, working professionals, and your average person.
+
+
+### Summary of Contributions
+
+### Code contributed
+
+To view the code I contributed click [here](https://nus-cs2113-ay2223s2.github.io/tp-dashboard/?search=&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2023-02-17&tabOpen=true&tabType=zoom&zFR=false&zA=SpeciLiam&zR=AY2223S2-CS2113-F13-2%2Ftp%5Bmaster%5D&zACS=154.9639945652174&zS=2023-02-17&zFS=&zU=2023-04-08&zMG=false&zFTF=commit&zFGS=groupByRepos).
+
+### Contributions to the UG
+
+- Implemented sections within the UG for clarity within the enhancements I integrated ino 'Brokeman'.
+
+### Contributions to the DG
+
+- I completed parts within the DG that relate to clarity in wording and specifics for what I integrated into 'Brokeman'.
+
+### Enhancements implemented
+
+- Implemented the initial base for the Budget and Target class files.
+- Added save feature for Expense class.
+- Added save feature for Income class.
+- Added read feature within main of Expense and Income class.
+- Worked on error handling with file tampering as well as exception handling.
+- Implemented a save and read feature for the budget class.
+- Implemented saving function for expenses.
+
+### Contributions beyond the project team
+- Bug tested other teams projects and gave informative reviews that could lead to a better final product.
+- Reviewed peers guides in comparison to own for collaborative feedback for our team and others.
\ No newline at end of file
diff --git a/docs/teamImages/A.jpg b/docs/teamImages/A.jpg
new file mode 100644
index 0000000000..a852a1a74c
Binary files /dev/null and b/docs/teamImages/A.jpg differ
diff --git a/docs/teamImages/B.jpg b/docs/teamImages/B.jpg
new file mode 100644
index 0000000000..e45c4b820c
Binary files /dev/null and b/docs/teamImages/B.jpg differ
diff --git a/docs/teamImages/Sangjun Nam.jpg b/docs/teamImages/Sangjun Nam.jpg
new file mode 100644
index 0000000000..0de7a0bb9c
Binary files /dev/null and b/docs/teamImages/Sangjun Nam.jpg differ
diff --git a/docs/teamImages/samuel.jpg b/docs/teamImages/samuel.jpg
new file mode 100644
index 0000000000..6cd640d9e7
Binary files /dev/null and b/docs/teamImages/samuel.jpg differ
diff --git a/duke.txt b/duke.txt
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/src/main/java/seedu/brokeMan/BrokeMan.java b/src/main/java/seedu/brokeMan/BrokeMan.java
new file mode 100644
index 0000000000..215509c51d
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/BrokeMan.java
@@ -0,0 +1,48 @@
+package seedu.brokeMan;
+
+import seedu.brokeMan.command.Command;
+import seedu.brokeMan.command.ExitCommand;
+import seedu.brokeMan.parser.Parser;
+import seedu.brokeMan.save.SaveBudget;
+import seedu.brokeMan.save.SaveExpense;
+import seedu.brokeMan.save.SaveIncome;
+import seedu.brokeMan.ui.Ui;
+
+import java.util.NoSuchElementException;
+
+public class BrokeMan {
+
+ /**
+ * Main entry-point for the java.duke.Duke application.
+ */
+
+ public static void main(String[] args) {
+ new BrokeMan().run();
+ }
+
+ private static void run() {
+ Ui.showWelcomeMessages();
+ runCommandUntilExitCommand();
+ Ui.showGoodByeMessages();
+ }
+
+ private static void runCommandUntilExitCommand() {
+ Command command = null;
+ SaveExpense.readExpenseFile();
+ SaveIncome.readIncomeFile();
+ SaveBudget.readFile();
+ String userFullInput;
+
+ do {
+ try {
+ userFullInput = Ui.getUserCommand();
+ command = Parser.parseCommand(userFullInput);
+ command.execute();
+ } catch (NoSuchElementException e) {
+ Ui.showToUser("Invalid input...");
+ command = new ExitCommand();
+ command.execute();
+ }
+ } while (!ExitCommand.isExit(command));
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/budget/Budget.java b/src/main/java/seedu/brokeMan/budget/Budget.java
new file mode 100644
index 0000000000..baf30c72f6
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/budget/Budget.java
@@ -0,0 +1,70 @@
+package seedu.brokeMan.budget;
+
+import seedu.brokeMan.entry.Entry;
+import seedu.brokeMan.entry.EntryList;
+import seedu.brokeMan.parser.StringToTime;
+import seedu.brokeMan.ui.Ui;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.Month;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Optional;
+
+import static seedu.brokeMan.entry.expense.ExpenseList.getExpensesMadeInMonth;
+import static seedu.brokeMan.parser.StringToTime.createDateString;
+
+public class Budget {
+ public static HashMap> budgetEachMonth = new HashMap<>();
+
+ public static void viewBudget(Optional dateInString) {
+ int year = StringToTime.createYearFromString(dateInString);
+ Month month = StringToTime.createMonthFromString(dateInString);
+
+ viewBudgetOfMonth(year, month);
+ }
+
+ public static void viewBudgetOfMonth(int yearOfInterest, Month monthOfInterest) {
+ try {
+ List expensesInMonth = getExpensesMadeInMonth(yearOfInterest, monthOfInterest);
+ double totalExpenses = EntryList.getEntryListSum(expensesInMonth);
+ double budgetThisMonth = budgetEachMonth.get(yearOfInterest).get(monthOfInterest);
+ double budgetLeft = budgetThisMonth - totalExpenses;
+ BigDecimal BigDecimalBudgetLeft = new BigDecimal(budgetThisMonth - totalExpenses);
+ Ui.showToUser(String.format("You have set your budget as $%.2f for %s.",
+ budgetThisMonth, createDateString(yearOfInterest, monthOfInterest)));
+ if (budgetLeft >= 0) {
+ Ui.showToUserWithLineBreak(
+ String.format("The amount of budget left is $%.2f", BigDecimalBudgetLeft), "");
+ } else if (budgetLeft < 0){
+ Ui.showToUserWithLineBreak(String.format(
+ "You have overspent your expenses by $%.2f", BigDecimalBudgetLeft), "");
+ }
+ } catch (NullPointerException npe) {
+ Ui.showToUserWithLineBreak("Budget information for the given month does not exist!", "");
+ }
+ }
+
+
+ public static boolean hasSetBudgetThisMonth() {
+ Integer thisYear = LocalDate.now().getYear();
+ if (!budgetEachMonth.containsKey(thisYear)) {
+ budgetEachMonth.put(thisYear, new HashMap<>());
+ }
+ Month thisMonth = LocalDate.now().getMonth();
+ return budgetEachMonth.get(thisYear).containsKey(thisMonth);
+ }
+
+ public static void setBudget(double budgetAmount, Optional dateInString) {
+ int year = StringToTime.createYearFromString(dateInString);
+ Month month = StringToTime.createMonthFromString(dateInString);
+
+ if (!budgetEachMonth.containsKey(year)) {
+ budgetEachMonth.put(year, new HashMap<>());
+ }
+ budgetEachMonth.get(year).put(month, budgetAmount);
+ Ui.showToUserWithLineBreak(String.format("You have successfully set $%.2f as your budget for %s.",
+ budgetAmount, StringToTime.createDateString(year, month)), "");
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/command/AddExpenseCommand.java b/src/main/java/seedu/brokeMan/command/AddExpenseCommand.java
new file mode 100644
index 0000000000..ef37116969
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/command/AddExpenseCommand.java
@@ -0,0 +1,25 @@
+package seedu.brokeMan.command;
+
+import seedu.brokeMan.entry.Category;
+import seedu.brokeMan.entry.expense.Expense;
+import seedu.brokeMan.entry.expense.ExpenseList;
+
+import java.time.LocalDateTime;
+
+public class AddExpenseCommand extends Command {
+ public static final String COMMAND_WORD = "addExpense";
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": add expense to the expense list.\n" +
+ "| Parameters: a/ d/ t/ c/ \n" +
+ "| Valid categories are: FOOD, SHOPPING, GROCERIES, " +
+ "TRANSPORTATION, ENTERTAINMENT, TRAVEL, SALARY, INVESTMENT, and OTHERS\n" +
+ "| Example: " + COMMAND_WORD + " a/ 4.5 d/ lunch t/ 2023 03 22 20 12 c/ FOOD";
+ private final Expense expense;
+
+ public AddExpenseCommand(double cost, String info, LocalDateTime time, Category category) {
+ this.expense = new Expense(cost, info, time, category);
+ }
+
+ public void execute() {
+ ExpenseList.addExpense(expense);
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/command/AddIncomeCommand.java b/src/main/java/seedu/brokeMan/command/AddIncomeCommand.java
new file mode 100644
index 0000000000..a6242bcf7a
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/command/AddIncomeCommand.java
@@ -0,0 +1,25 @@
+package seedu.brokeMan.command;
+
+import seedu.brokeMan.entry.Category;
+import seedu.brokeMan.entry.income.Income;
+import seedu.brokeMan.entry.income.IncomeList;
+
+import java.time.LocalDateTime;
+
+public class AddIncomeCommand extends Command {
+ public static final String COMMAND_WORD = "addIncome";
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": add income to the income list.\n" +
+ "| Parameters: a/ d/ t/ c/ \n" +
+ "| Valid categories are: FOOD, SHOPPING, GROCERIES, " +
+ "TRANSPORTATION, ENTERTAINMENT, TRAVEL, SALARY, INVESTMENT, and OTHERS\n" +
+ "| Example: " + COMMAND_WORD + " a/ 3000 d/ salary t/ 2023 03 10 10 10 c/ SALARY";
+ private final Income income;
+
+ public AddIncomeCommand(double amount, String info, LocalDateTime time, Category category) {
+ this.income = new Income(amount, info, time, category);
+ }
+
+ public void execute() {
+ IncomeList.addIncome(income);
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/command/Command.java b/src/main/java/seedu/brokeMan/command/Command.java
new file mode 100644
index 0000000000..24573dce9f
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/command/Command.java
@@ -0,0 +1,8 @@
+package seedu.brokeMan.command;
+
+public abstract class Command {
+ // protected static final String COMMAND_WORD = "";
+ // protected static final String MESSAGE_USAGE = "";
+
+ public abstract void execute();
+}
diff --git a/src/main/java/seedu/brokeMan/command/DeleteExpenseCommand.java b/src/main/java/seedu/brokeMan/command/DeleteExpenseCommand.java
new file mode 100644
index 0000000000..c55a80ada8
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/command/DeleteExpenseCommand.java
@@ -0,0 +1,19 @@
+package seedu.brokeMan.command;
+
+import seedu.brokeMan.entry.expense.ExpenseList;
+
+public class DeleteExpenseCommand extends Command {
+ public static final String COMMAND_WORD = "deleteExpense";
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": deletes an expense from the list\n" +
+ "| Parameter: \n" +
+ "| Example: " + COMMAND_WORD + " 1";
+ private final int index;
+
+ public DeleteExpenseCommand(int index) {
+ this.index = index;
+ }
+
+ public void execute() {
+ ExpenseList.deleteExpense(index);
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/command/DeleteIncomeCommand.java b/src/main/java/seedu/brokeMan/command/DeleteIncomeCommand.java
new file mode 100644
index 0000000000..ef7c0277ce
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/command/DeleteIncomeCommand.java
@@ -0,0 +1,19 @@
+package seedu.brokeMan.command;
+
+import seedu.brokeMan.entry.income.IncomeList;
+
+public class DeleteIncomeCommand extends Command {
+ public static final String COMMAND_WORD = "deleteIncome";
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": deletes an income from the list\n" +
+ "| Parameter: \n" +
+ "| Example: " + COMMAND_WORD + " 1";
+ private final int index;
+
+ public DeleteIncomeCommand(int index) {
+ this.index = index;
+ }
+
+ public void execute() {
+ IncomeList.deleteIncome(index);
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/command/EditExpenseCommand.java b/src/main/java/seedu/brokeMan/command/EditExpenseCommand.java
new file mode 100644
index 0000000000..0488e86f87
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/command/EditExpenseCommand.java
@@ -0,0 +1,50 @@
+package seedu.brokeMan.command;
+
+import seedu.brokeMan.entry.Category;
+import seedu.brokeMan.entry.expense.ExpenseList;
+import seedu.brokeMan.exception.CategoryNotCorrectException;
+import seedu.brokeMan.parser.StringToCategory;
+import seedu.brokeMan.parser.StringToTime;
+
+import java.time.LocalDateTime;
+import java.util.logging.Logger;
+
+public class EditExpenseCommand extends Command {
+ public static final String COMMAND_WORD = "editExpense";
+ private static final Logger logger = Logger.getLogger("EditExpenseCommandLogger");
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": edits the expense from the list.\n" +
+ "| Parameter: i/ t/ n/ \n" +
+ "| There are 4 types that can be changed, amount, info, time, category\n" +
+ "| Example: " + COMMAND_WORD + " i/ 1 t/ amount n/ 5";
+ private final int index;
+ private final String type;
+ private final String newEntry;
+
+ public EditExpenseCommand(int index, String type, String newEntry) {
+ this.index = index;
+ this.type = type;
+ this.newEntry = newEntry;
+ }
+
+ public void execute() {
+ if (type.equals("amount")) {
+ double newCost = Double.parseDouble(newEntry);
+ ExpenseList.editExpense(index, newCost);
+ } else if (type.equals("info")) {
+ ExpenseList.editExpense(index, newEntry);
+ } else if (type.equals("time")) {
+ LocalDateTime newTime = StringToTime.convertStringToTime(newEntry);
+ ExpenseList.editExpense(index, newTime);
+ } else if (type.equals("category")) {
+ Category newCategory;
+ try {
+ newCategory = StringToCategory.convertStringToCategory(newEntry);
+ } catch (CategoryNotCorrectException e) {
+ throw new RuntimeException(e);
+ }
+ ExpenseList.editExpense(index, newCategory);
+ }
+
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/command/EditIncomeCommand.java b/src/main/java/seedu/brokeMan/command/EditIncomeCommand.java
new file mode 100644
index 0000000000..efb0189cfe
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/command/EditIncomeCommand.java
@@ -0,0 +1,47 @@
+package seedu.brokeMan.command;
+
+import seedu.brokeMan.entry.Category;
+import seedu.brokeMan.entry.income.IncomeList;
+import seedu.brokeMan.exception.CategoryNotCorrectException;
+import seedu.brokeMan.parser.StringToCategory;
+import seedu.brokeMan.parser.StringToTime;
+
+import java.time.LocalDateTime;
+
+public class EditIncomeCommand extends Command {
+ public static final String COMMAND_WORD = "editIncome";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": edits the income from the list.\n" +
+ "| Parameter: i/ t/ n/ \n" +
+ "| There are 4 types that can be changed, amount, info, time, category\n" +
+ "| Example: " + COMMAND_WORD + " i/ 1 t/ info n/ stocks";
+ private int index;
+ private String type;
+ private String newEntry;
+
+ public EditIncomeCommand(int index, String type, String newEntry) {
+ this.index = index;
+ this.type = type;
+ this.newEntry = newEntry;
+ }
+
+ public void execute() {
+ if (type.equals("amount")) {
+ double newIncome = Double.parseDouble(newEntry);
+ IncomeList.editIncome(index, newIncome);
+ } else if (type.equals("info")) {
+ IncomeList.editIncome(index, newEntry);
+ } else if (type.equals("time")) {
+ LocalDateTime newTime = StringToTime.convertStringToTime(newEntry);
+ IncomeList.editIncome(index, newTime);
+ } else if (type.equals("category")) {
+ Category newCategory = null;
+ try {
+ newCategory = StringToCategory.convertStringToCategory(newEntry);
+ } catch (CategoryNotCorrectException e) {
+ throw new RuntimeException(e);
+ }
+ IncomeList.editIncome(index, newCategory);
+ }
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/command/ExitCommand.java b/src/main/java/seedu/brokeMan/command/ExitCommand.java
new file mode 100644
index 0000000000..e7ba39666f
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/command/ExitCommand.java
@@ -0,0 +1,25 @@
+package seedu.brokeMan.command;
+
+import seedu.brokeMan.save.SaveBudget;
+import seedu.brokeMan.save.SaveExpense;
+import seedu.brokeMan.save.SaveIncome;
+import seedu.brokeMan.ui.Ui;
+
+import static seedu.brokeMan.budget.Budget.budgetEachMonth;
+import static seedu.brokeMan.entry.expense.ExpenseList.expenseList;
+import static seedu.brokeMan.entry.income.IncomeList.incomeList;
+
+public class ExitCommand extends Command {
+ public static final String COMMAND_WORD = "exit";
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": exits the program\n" +
+ "| Example: " + COMMAND_WORD;
+ public void execute() {
+ SaveExpense.writeFile(expenseList);
+ SaveIncome.writeFile(incomeList);
+ SaveBudget.writeFile(budgetEachMonth);
+ Ui.showToUserWithLineBreak("Exiting program...", "");
+ }
+ public static boolean isExit(Command command) {
+ return command instanceof ExitCommand;
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/command/HelpCommand.java b/src/main/java/seedu/brokeMan/command/HelpCommand.java
new file mode 100644
index 0000000000..b6461c2cc7
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/command/HelpCommand.java
@@ -0,0 +1,20 @@
+package seedu.brokeMan.command;
+
+import seedu.brokeMan.ui.Ui;
+
+public class HelpCommand extends Command {
+ public static final String COMMAND_WORD = "help";
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": shows all the commands for the program.\n" +
+ "| Example: " + COMMAND_WORD;
+ public void execute() {
+ Ui.showToUserWithLineBreak(AddExpenseCommand.MESSAGE_USAGE, "",
+ AddIncomeCommand.MESSAGE_USAGE, "", DeleteExpenseCommand.MESSAGE_USAGE, "",
+ DeleteIncomeCommand.MESSAGE_USAGE, "",EditExpenseCommand.MESSAGE_USAGE, "",
+ EditIncomeCommand.MESSAGE_USAGE, "", ListExpenseCommand.MESSAGE_USAGE, "",
+ ListIncomeCommand.MESSAGE_USAGE, "", SetBudgetCommand.MESSAGE_USAGE, "",
+ ViewBudgetCommand.MESSAGE_USAGE, "", SortExpenseByAmountCommand.MESSAGE_USAGE, "",
+ SortIncomeByAmountCommand.MESSAGE_USAGE, "", SortExpenseByDateCommand.MESSAGE_USAGE, "",
+ SortIncomeByDateCommand.MESSAGE_USAGE, "",HelpCommand.MESSAGE_USAGE, "",
+ ExitCommand.MESSAGE_USAGE, "");
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/command/InvalidCommand.java b/src/main/java/seedu/brokeMan/command/InvalidCommand.java
new file mode 100644
index 0000000000..62cc49e3a9
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/command/InvalidCommand.java
@@ -0,0 +1,22 @@
+package seedu.brokeMan.command;
+
+import seedu.brokeMan.ui.Ui;
+
+import java.util.ArrayList;
+
+public class InvalidCommand extends Command {
+ private ArrayList invalidMessages = new ArrayList<>();
+
+ public InvalidCommand(String ... invalidMessages) {
+ for (String message : invalidMessages) {
+ this.invalidMessages.add(message);
+ }
+ }
+
+ public void execute() {
+ for (int i = 0; i < invalidMessages.size() - 1; i++) {
+ Ui.showToUser(invalidMessages.get(i), "");
+ }
+ Ui.showToUserWithLineBreak(invalidMessages.get(invalidMessages.size() - 1), "");
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/command/ListExpenseCommand.java b/src/main/java/seedu/brokeMan/command/ListExpenseCommand.java
new file mode 100644
index 0000000000..b277fdb9eb
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/command/ListExpenseCommand.java
@@ -0,0 +1,31 @@
+package seedu.brokeMan.command;
+
+import seedu.brokeMan.entry.expense.ExpenseList;
+
+import java.util.Optional;
+
+public class ListExpenseCommand extends Command {
+ public static final String COMMAND_WORD = "listExpense";
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": lists expenses made in the current month.\n" +
+ "| " + COMMAND_WORD + " t/ : : lists expenses made in the specified month\n" +
+ "| Optional Parameter: t/ \n" +
+ "| Example: " + COMMAND_WORD + "\n" +
+ "| Example: " + COMMAND_WORD + " t/ 2023/03";
+
+ private final Optional date;
+
+ public ListExpenseCommand() {
+ this.date = Optional.empty();
+ }
+
+ public ListExpenseCommand(String date) {
+ this.date = Optional.of(date);
+ }
+ public void execute() {
+ if (date.isEmpty()) {
+ ExpenseList.listExpense();
+ } else if (date.isPresent()) {
+ ExpenseList.listExpense(date);
+ }
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/command/ListIncomeCommand.java b/src/main/java/seedu/brokeMan/command/ListIncomeCommand.java
new file mode 100644
index 0000000000..00dab7f3c8
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/command/ListIncomeCommand.java
@@ -0,0 +1,32 @@
+package seedu.brokeMan.command;
+
+import seedu.brokeMan.entry.income.IncomeList;
+
+import java.util.Optional;
+
+public class ListIncomeCommand extends Command {
+ public static final String COMMAND_WORD = "listIncome";
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": lists incomes made in the current month.\n" +
+ "| " + COMMAND_WORD + " t/ : : lists incomes made in the specified month\n" +
+ "| Optional Parameter: t/ \n" +
+ "| Example: " + COMMAND_WORD + "\n" +
+ "| Example: " + COMMAND_WORD + " t/ 2023/03";
+
+ private final Optional date;
+
+ public ListIncomeCommand() {
+ this.date = Optional.empty();
+ }
+
+ public ListIncomeCommand(String date) {
+ this.date = Optional.of(date);
+ }
+
+ public void execute() {
+ if (date.isEmpty()) {
+ IncomeList.listIncome();
+ } else if (date.isPresent()) {
+ IncomeList.listIncome(date);
+ }
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/command/SetBudgetCommand.java b/src/main/java/seedu/brokeMan/command/SetBudgetCommand.java
new file mode 100644
index 0000000000..020411004e
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/command/SetBudgetCommand.java
@@ -0,0 +1,31 @@
+package seedu.brokeMan.command;
+
+import seedu.brokeMan.budget.Budget;
+
+import java.util.Optional;
+
+public class SetBudgetCommand extends Command {
+ public static final String COMMAND_WORD = "setBudget";
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": sets your budget for current month.\n" +
+ "| " + COMMAND_WORD + " t/ : sets your budget for specified month\n" +
+ "| Compulsory Parameter: \n" +
+ "| Optional Parameter: t/ \n" +
+ "| Example: " + COMMAND_WORD + " 500\n" +
+ "| Example: " + COMMAND_WORD + " 500 t/ 2023/03";
+ private final double budget;
+ private final Optional date;
+
+ public SetBudgetCommand(double budget) {
+ this.budget = budget;
+ date = Optional.empty();
+ }
+
+ public SetBudgetCommand(double budget, String date) {
+ this.budget = budget;
+ this.date = Optional.of(date);
+ }
+
+ public void execute() {
+ Budget.setBudget(budget, this.date);
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/command/SortExpenseByAmountCommand.java b/src/main/java/seedu/brokeMan/command/SortExpenseByAmountCommand.java
new file mode 100644
index 0000000000..469b02fae6
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/command/SortExpenseByAmountCommand.java
@@ -0,0 +1,14 @@
+package seedu.brokeMan.command;
+
+import seedu.brokeMan.entry.expense.ExpenseList;
+
+public class SortExpenseByAmountCommand extends Command {
+ public static final String COMMAND_WORD = "sortExpenseByAmount";
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": shows the expenses made, sorted by amount of expense\n"
+ + "| Example: " + COMMAND_WORD;
+
+ public void execute() {
+ ExpenseList.sortExpensesByAmount();
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/command/SortExpenseByDateCommand.java b/src/main/java/seedu/brokeMan/command/SortExpenseByDateCommand.java
new file mode 100644
index 0000000000..9a7179ed87
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/command/SortExpenseByDateCommand.java
@@ -0,0 +1,14 @@
+package seedu.brokeMan.command;
+
+import seedu.brokeMan.entry.expense.ExpenseList;
+
+public class SortExpenseByDateCommand extends Command {
+ public static final String COMMAND_WORD = "sortExpenseByDate";
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": shows the expenses made, sorted by date of expense\n"
+ + "| Example: " + COMMAND_WORD;
+
+ public void execute() {
+ ExpenseList.sortExpensesByDate();
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/command/SortIncomeByAmountCommand.java b/src/main/java/seedu/brokeMan/command/SortIncomeByAmountCommand.java
new file mode 100644
index 0000000000..37005205c6
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/command/SortIncomeByAmountCommand.java
@@ -0,0 +1,14 @@
+package seedu.brokeMan.command;
+
+import seedu.brokeMan.entry.income.IncomeList;
+
+public class SortIncomeByAmountCommand extends Command {
+ public static final String COMMAND_WORD = "sortIncomeByAmount";
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": shows the incomes made, sorted by amount of income\n"
+ + "| Example: " + COMMAND_WORD;
+
+ public void execute() {
+ IncomeList.sortIncomeByAmount();
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/command/SortIncomeByDateCommand.java b/src/main/java/seedu/brokeMan/command/SortIncomeByDateCommand.java
new file mode 100644
index 0000000000..25dfe01121
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/command/SortIncomeByDateCommand.java
@@ -0,0 +1,15 @@
+package seedu.brokeMan.command;
+
+
+import seedu.brokeMan.entry.income.IncomeList;
+
+public class SortIncomeByDateCommand extends Command {
+ public static final String COMMAND_WORD = "sortIncomeByDate";
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": shows the incomes made, sorted by date of income\n"
+ + "| Example: " + COMMAND_WORD;
+
+ public void execute() {
+ IncomeList.sortIncomeByDate();
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/command/ViewBudgetCommand.java b/src/main/java/seedu/brokeMan/command/ViewBudgetCommand.java
new file mode 100644
index 0000000000..9f1b845e00
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/command/ViewBudgetCommand.java
@@ -0,0 +1,35 @@
+package seedu.brokeMan.command;
+
+import seedu.brokeMan.budget.Budget;
+import seedu.brokeMan.exception.hasNotSetBudgetException;
+
+import java.util.Optional;
+
+public class ViewBudgetCommand extends Command {
+ public static final String COMMAND_WORD = "viewBudget";
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": view your budget for the current month " +
+ "and how much of it is left remaining.\n" +
+ "| " + COMMAND_WORD +
+ " t/ : view your budget and how much of was left in the specified month\n" +
+ "| Optional Parameter: t/ \n" +
+ "| Example: " + COMMAND_WORD + "\n" +
+ "| Example: " + COMMAND_WORD + " t/ 2023/03";
+
+ private final Optional date;
+
+ public ViewBudgetCommand(String date) {
+ this.date = Optional.of(date);
+ }
+
+ public ViewBudgetCommand() throws hasNotSetBudgetException {
+ if (!Budget.hasSetBudgetThisMonth()) {
+ throw new hasNotSetBudgetException();
+ }
+ this.date = Optional.empty();
+ }
+
+ @Override
+ public void execute() {
+ Budget.viewBudget(date);
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/common/Messages.java b/src/main/java/seedu/brokeMan/common/Messages.java
new file mode 100644
index 0000000000..d1a73b780a
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/common/Messages.java
@@ -0,0 +1,55 @@
+package seedu.brokeMan.common;
+
+public class Messages {
+
+ public static final String MESSAGE_LOGO =
+ "$$$$$___ ______ _______ $$_____ _______ $$___$$_ _______ _______\n" +
+ "| $$__$$__ ______ _______ $$__$$_ _$$$$__ $$$_$$$_ $$$$$__ _______\n" +
+ "| $$$$$___ $$_$$_ _$$$$__ $$_$$__ $$__$$_ $$$$$$$_ ____$$_ $$$$$__\n" +
+ "| $$___$$_ $$$_$_ $$__$$_ $$$$___ $$$$$$_ $$_$_$$_ _$$$$$_ $$__$$_\n" +
+ "| $$___$$_ $$____ $$__$$_ $$_$$__ $$_____ $$___$$_ $$__$$_ $$__$$_\n" +
+ "| $$$$$$__ $$____ _$$$$__ $$__$$_ _$$$$$_ $$___$$_ _$$$$$_ $$__$$_";
+
+ public static final String MESSAGE_GOODBYE =
+ " ___ _ ___ _ _\n" +
+ "| / __| ___ ___ __| | o O O | _ ) | || | ___\n" +
+ "| | (_ | / _ \\ / _ \\ / _` | o | _ \\ \\_, | / -_)\n" +
+ "| \\___| \\___/ \\___/ \\__,_| TS__[O] |___/ _|__/ \\___|\n" +
+ "| _|\"\"\"\"\"|_|\"\"\"\"\"|_|\"\"\"\"\"|_|\"\"\"\"\"| {======|_|\"\"\"\"\"|_| \"\"\"\"|_|\"\"\"\"\"|\n" +
+ "| \"`-0-0-'\"`-0-0-'\"`-0-0-'\"`-0-0-'./o--000'\"`-0-0-'\"`-0-0-'\"`-0-0-'";
+
+ public static final String MESSAGE_WELCOME = "Welcome to BrokeMan!\n" +
+ "| Your personal budget manager to prevent you to become broke like me...";
+ public static final String MESSAGE_INVALID_ADD_COMMAND = "Invalid add command format.";
+ public static final String MESSAGE_INVALID_EDIT_COMMAND = "Invalid edit command format.";
+ public static final String MESSAGE_AMOUNT_NOT_DOUBLE = "Amount is not a double.";
+ public static final String MESSAGE_INDEX_NOT_INTEGER = "Index is not an integer.";
+ public static final String MESSAGE_INDEX_NOT_SPECIFIED_EXCEPTION = "Index is not specified.";
+ public static final String MESSAGE_INCORRECT_TYPE = "Type specified is incorrect.";
+ public static final String MESSAGE_AMOUNT_LESS_THAN_OR_EQUALS_ZERO = "Amount cannot be less than or equals to 0.";
+ public static final String MESSAGE_ARGUMENTS_NOT_SPECIFIED = "Arguments not specified.";
+ public static final String MESSAGE_INVALID_TIME = "Invalid time information. " +
+ "Please present your time as 'YYYY MM DD HH mm'\n" +
+ "| Do not enter invalid dates, such as entering 14 for MM.\n" +
+ "| The earliest date available is 2000/01, and the latest date available is 9999/12.";
+ public static final String MESSAGE_INVALID_MONTH = "Invalid time information. " +
+ "Please present your time as [YYYY/MM]\n" +
+ "| Do not enter invalid dates, such as entering 14 for MM. Only months 1~12 are accepted.\n" +
+ "| The earliest date available is 2000/01, and the latest date available is 9999/12.";
+
+ public static final String MESSAGE_INVALID_OPTIONAL_TIME_FLAG = "Invalid optional time flag format.";
+ public static final String MESSAGE_WRONG_FLAG_ORDER = "Wrong flags order.";
+
+ public static final String MESSAGE_INVALID_CATEGORY = "Invalid category tag.\n| You can add category tags: " +
+ "FOOD, SHOPPING, GROCERIES, TRANSPORTATION, ENTERTAINMENT, TRAVEL, " +
+ "SALARY, INVESTMENT, " +
+ "OTHERS";
+
+ public static final String MESSAGE_CONTAIN_DUPLICATED_FLAGS = "Duplicated flags are not allowed.";
+
+ public static final String MESSAGE_NEW_DESCRIPTION_CONTAIN_FLAGS = "Edited description cannot contain any flags" +
+ " such as 'a/', 'd/', and 'c/'.";
+
+ public static final String MESSAGE_EXCEED_MAXIMUM_LENGTH_FOR_AMOUNT = "The maximum amount that can be" +
+ " entered is $9999999999.99";
+}
diff --git a/src/main/java/seedu/brokeMan/entry/Category.java b/src/main/java/seedu/brokeMan/entry/Category.java
new file mode 100644
index 0000000000..6a3f659e12
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/entry/Category.java
@@ -0,0 +1,7 @@
+package seedu.brokeMan.entry;
+
+public enum Category {
+ FOOD, SHOPPING, GROCERIES, TRANSPORTATION, ENTERTAINMENT, TRAVEL,
+ SALARY, INVESTMENT,
+ OTHERS
+}
diff --git a/src/main/java/seedu/brokeMan/entry/Entry.java b/src/main/java/seedu/brokeMan/entry/Entry.java
new file mode 100644
index 0000000000..b88da682d9
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/entry/Entry.java
@@ -0,0 +1,107 @@
+package seedu.brokeMan.entry;
+
+import java.time.LocalDateTime;
+import java.time.Month;
+
+public abstract class Entry {
+ protected String info;
+ protected double amount;
+ protected LocalDateTime time;
+ protected Category category;
+
+ public Entry(double amount, String info, LocalDateTime time, Category category) {
+ this.amount = amount;
+ this.info = info;
+ this.time = time;
+ this.category = category;
+ }
+
+ /**
+ * Edits amount of the entry
+ *
+ * @param newAmount New desired amount of the entry
+ */
+ public void editAmount(double newAmount) {
+ this.amount = newAmount;
+ }
+
+ /**
+ * Edits information entry of the entry
+ *
+ * @param newInfo New desired information of the entry
+ */
+ public void editDescription(String newInfo) {
+ this.info = newInfo;
+ }
+
+ /**
+ * Edits time entry of the entry
+ *
+ * @param newTime New desired time of the entry
+ */
+ public void editTime(LocalDateTime newTime) {
+ this.time = newTime;
+ }
+
+ /**
+ * Edits category entry of the entry
+ * @param newCategory
+ */
+ public void editCategory(Category newCategory) {this.category = newCategory; }
+
+ /**
+ * Returns the amount attribute of the entry
+ *
+ * @return double that represents the amount of the entry
+ */
+ public double getAmount() {
+ return this.amount;
+ }
+
+ /**
+ * Returns the time attribute of the entry
+ *
+ * @return String that represents the time of the entry
+ */
+ public String getTime() { return this.time.toString(); }
+
+ /**
+ * Returns the category attribute of the entry
+ *
+ * @return Category that represents the category of the entry
+ */
+ public Category getCategory() { return this.category; }
+
+
+ public String getInfo() {
+ return this.info;
+ }
+
+ /**
+ * Converts time attribute of the entry into a string that can be shown to the user
+ *
+ * @return String representation of the time attribute of the entry, shown in form 'date @ time'
+ */
+ protected String convertTimeToString() {
+ String timeAsString = this.time.toString();
+ int indexOfT = timeAsString.indexOf('T');
+ String dateString = timeAsString.substring(0, indexOfT);
+ String timeString = timeAsString.substring(indexOfT + 1);
+ return String.format("%s @ %s", dateString, timeString);
+ }
+
+ /**
+ * Checks if the entry is made at the same month specified by the parameter
+ *
+ * @param year Year of interest
+ * @param month Month of interest
+ * @return Boolean value that represents if the entry is made at the same date as specified by the parameter
+ */
+ protected boolean isSameMonth(int year, Month month) {
+ return (this.time.getYear() == year && this.time.getMonth().equals(month));
+ }
+
+ public abstract String toString();
+
+
+}
diff --git a/src/main/java/seedu/brokeMan/entry/EntryAmountComparator.java b/src/main/java/seedu/brokeMan/entry/EntryAmountComparator.java
new file mode 100644
index 0000000000..34f38c6984
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/entry/EntryAmountComparator.java
@@ -0,0 +1,16 @@
+package seedu.brokeMan.entry;
+
+
+import java.util.Comparator;
+
+public class EntryAmountComparator implements Comparator{
+ /**
+ * Compares two entries in terms of their amount
+ *
+ * @param e1 the first entry to be compared.
+ * @param e2 the second entry to be compared.
+ * @return integer value that shows which of the two entries has larger amount
+ */
+ public int compare(Entry e1, Entry e2) {
+ return (int)((e2.getAmount() - e1.getAmount()) * 100);}
+}
diff --git a/src/main/java/seedu/brokeMan/entry/EntryDateComparator.java b/src/main/java/seedu/brokeMan/entry/EntryDateComparator.java
new file mode 100644
index 0000000000..ceb145c53c
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/entry/EntryDateComparator.java
@@ -0,0 +1,16 @@
+package seedu.brokeMan.entry;
+
+import java.util.Comparator;
+
+public class EntryDateComparator implements Comparator {
+ /**
+ * Compares two entries in terms of their time
+ *
+ * @param e1 the first entry to be compared.
+ * @param e2 the second entry to be compared.
+ * @return integer value that shows which of the two entries is more recent in time
+ */
+ public int compare(Entry e1, Entry e2) {
+ return e2.time.compareTo(e1.time);
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/entry/EntryList.java b/src/main/java/seedu/brokeMan/entry/EntryList.java
new file mode 100644
index 0000000000..4b06767f9f
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/entry/EntryList.java
@@ -0,0 +1,203 @@
+package seedu.brokeMan.entry;
+
+import seedu.brokeMan.ui.Ui;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.time.Month;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public abstract class EntryList {
+
+ /**
+ * Adds new expense/income to the list
+ *
+ * @param newEntry new entry to be added
+ */
+ public static void addEntry(Entry newEntry, LinkedList entryList) {
+ entryList.add(newEntry);
+ Ui.showToUserWithLineBreak("You have successfully added [" + newEntry + "]", "");
+ }
+
+ /**
+ * Lists the entries in the entryList
+ *
+ * @param entryList LinkedList that contains the entries
+ */
+ public static void listEntry(List entryList) {
+ if (entryList.size() == 0) {
+ Ui.showToUser("The requested list is empty\n|");
+ } else if (entryList.size() > 0) {
+ int counter = 1;
+ for (Entry entryLog : entryList) {
+ String message = String.format("%d. %s", counter, entryLog.toString());
+ Ui.showToUser(message);
+ counter++;
+ }
+ }
+ }
+
+
+ /**
+ * deletes specific entry in the list
+ *
+ * @param entryIndex Index of the entry in the list
+ * @param entryList LinkedList that contains the entries
+ *
+ */
+ public static void deleteEntry(int entryIndex, LinkedList entryList) {
+ try {
+ entryList.remove(entryIndex - 1);
+ Ui.showToUserWithLineBreak("Successfully deleted.", "");
+ } catch (IndexOutOfBoundsException | IllegalArgumentException e) {
+ Ui.showToUserWithLineBreak("Invalid index! Please try again.", "");
+ }
+ }
+
+ /**
+ * Edits amount of money for an entry in the specific index in the list
+ *
+ * @param entryIndex index of the entry in the list
+ * @param newAmount new entry that will replace current entry
+ * @param entryList LinkedList that contains the entries
+ *
+ */
+ public static void editEntryCost(int entryIndex, double newAmount, LinkedList entryList) {
+ try {
+ Entry entryBeingEdited = entryList.get(entryIndex - 1);
+ entryBeingEdited.editAmount(newAmount);
+ Ui.showToUserWithLineBreak("Successfully edited amount.", "");
+ } catch (IndexOutOfBoundsException | IllegalArgumentException e) {
+ Ui.showToUserWithLineBreak("Invalid index! Please try again.", "");
+ }
+ }
+
+ /**
+ * Edits description of an entry in the specific index in the list
+ *
+ * @param entryIndex index of the entry in the list
+ * @param newDescription new description that will replace current description
+ * @param entryList LinkedList that contains the entries
+ *
+ */
+ public static void editEntryDescription(int entryIndex, String newDescription, LinkedList entryList) {
+ try {
+ Entry entryBeingEdited = entryList.get(entryIndex - 1);
+ entryBeingEdited.editDescription(newDescription);
+ Ui.showToUserWithLineBreak("Successfully edited description.", "");
+ } catch (IndexOutOfBoundsException | IllegalArgumentException e) {
+ Ui.showToUserWithLineBreak("Invalid index! Please try again.", "");
+ }
+ }
+
+ /**
+ * Edits time of an entry in the specific index in the list
+ *
+ * @param entryIndex index of the entry in the list
+ * @param newTime new time that will replace current time
+ * @param entryList LinkedList that contains the entries
+ *
+ */
+ public static void editEntryTime(int entryIndex, LocalDateTime newTime, LinkedList entryList) {
+ try {
+ Entry entryBeingEdited = entryList.get(entryIndex - 1);
+ entryBeingEdited.editTime(newTime);
+ Ui.showToUserWithLineBreak("Successfully edited time.", "");
+ } catch (IndexOutOfBoundsException | IllegalArgumentException e) {
+ Ui.showToUserWithLineBreak("Invalid index! Please try again.", "");
+ }
+ }
+
+ /**
+ * Edits category of an entry in the specific index in the list
+ * @param entryIndex index of the entry in the list
+ * @param newCategory new category that will replace current category
+ * @param entryList LinkedList that contains the entries
+ */
+ public static void editEntryCategory(int entryIndex, Category newCategory, LinkedList entryList) {
+ try {
+ Entry entryBeingEdited = entryList.get(entryIndex - 1);
+ entryBeingEdited.editCategory(newCategory);
+ Ui.showToUserWithLineBreak("Successfully edited category.", "");
+ } catch (IndexOutOfBoundsException | IllegalArgumentException e) {
+ Ui.showToUserWithLineBreak("Invalid index! Please try again.", "");
+ }
+ }
+
+ /**
+ * Sorts entries using Entry amount comparator
+ * @param entryList LinkedList that contains the entries
+ *
+ */
+ protected static void sortEntriesByAmount(LinkedList entryList) {
+ entryList.sort(new EntryAmountComparator());
+ listEntry(entryList);
+ }
+
+ /**
+ * Sorts entries using Entry date comparator
+ * @param entryList LinkedList that contains the entries
+ *
+ */
+ protected static void sortEntriesByDate(LinkedList entryList) {
+ entryList.sort(new EntryDateComparator());
+ listEntry(entryList);
+ }
+
+
+ /**
+ * Finds all entries that fit the specified category
+ *
+ * @param category Category of interest
+ * @param entryList List of entries that are registered under the category of interest
+ */
+ protected static void findEntriesByCategory(Category category, LinkedList entryList) {
+ LinkedList entriesByCategory = new LinkedList();
+ if (entryList.size() > 0) {
+ for (Entry entryLog : entryList) {
+ if (entryLog.getCategory() == category) {
+ entriesByCategory.add(entryLog);
+ }
+ }
+ }
+ if (entriesByCategory.size() > 0) {
+ listEntry(entriesByCategory);
+ Ui.showToUser("Total in this category: $" + getEntryListSum(entriesByCategory));
+ } else {
+ Ui.showToUser("No entries found under this category. ");
+ }
+ }
+
+ /**
+ * Returns list of entries that are made in the specified month
+ *
+ * @param year Year of interest
+ * @param month Month of interest
+ * @param entryList List of available entries
+ * @return List of entries that are made in the month specified by the parameters
+ */
+ protected static List selectEntryForDate(int year, Month month, LinkedList entryList) {
+ return entryList.stream().filter(x -> x.isSameMonth(year, month)).collect(Collectors.toList());
+ }
+
+
+ /**
+ * Returns the sum of the amount attribute of entries in the list
+ *
+ * @param entryList List of entries
+ * @return Double representation of the sum of entry amounts
+ */
+ public static double getEntryListSum(List entryList) {
+ double sum;
+ BigDecimal temp = new BigDecimal(0);
+ for (Entry entry : entryList) {
+ BigDecimal temp2 = new BigDecimal(entry.getAmount());
+ temp = temp.add(temp2);
+ }
+
+ sum = temp.doubleValue();
+ return sum;
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/entry/expense/Expense.java b/src/main/java/seedu/brokeMan/entry/expense/Expense.java
new file mode 100644
index 0000000000..dc3f135d5b
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/entry/expense/Expense.java
@@ -0,0 +1,17 @@
+package seedu.brokeMan.entry.expense;
+
+import seedu.brokeMan.entry.Entry;
+import seedu.brokeMan.entry.Category;
+
+import java.time.LocalDateTime;
+
+public class Expense extends Entry {
+ public Expense(double amount, String info, LocalDateTime time, Category category) {
+ super(amount, info, time, category);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("$%.2f spent on %s - %s [%s]", amount, info, super.convertTimeToString(), category.name());
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/entry/expense/ExpenseList.java b/src/main/java/seedu/brokeMan/entry/expense/ExpenseList.java
new file mode 100644
index 0000000000..5dd6c81131
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/entry/expense/ExpenseList.java
@@ -0,0 +1,145 @@
+package seedu.brokeMan.entry.expense;
+
+import seedu.brokeMan.entry.Category;
+import seedu.brokeMan.entry.Entry;
+import seedu.brokeMan.entry.EntryList;
+import seedu.brokeMan.parser.StringToTime;
+import seedu.brokeMan.ui.Ui;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.time.Month;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Optional;
+
+public class ExpenseList extends EntryList {
+ public static final LinkedList expenseList = new LinkedList<>();
+
+ /**
+ * Adds new expense to the list
+ *
+ * @param newExpense new expense to be added
+ */
+ public static void addExpense(Expense newExpense) {
+ addEntry(newExpense, expenseList);
+ }
+
+ /**
+ * Lists expenses saved
+ *
+ * @param date Optional of String that contains information about the date
+ */
+
+ public static void listExpense(Optional date) {
+ int year = StringToTime.createYearFromString(date);
+ Month month = StringToTime.createMonthFromString(date);
+
+ List expenseOfDate = getExpensesMadeInMonth(year, month);
+ String dateInString = StringToTime.createDateString(year, month);
+
+ Ui.showToUser("Here are the expenses you have made for " + dateInString + ".");
+ listEntry(expenseOfDate);
+ double totalDoubleExpenses = getEntryListSum(expenseOfDate);
+ BigDecimal totalExpenses = new BigDecimal(totalDoubleExpenses);
+ Ui.showToUser(String.format("Total expenses: $%.2f", totalExpenses));
+ Ui.showToUserWithLineBreak("");
+ }
+
+ /**
+ * Lists expenses saved
+ */
+ public static void listExpense() {
+ Ui.showToUser("Here are the expenses you have made.");
+ listEntry(expenseList);
+ Ui.showToUser("Total expenses: $" + getEntryListSum(expenseList));
+ Ui.showToUserWithLineBreak("");
+ }
+
+ /**
+ * deletes specific expense in the list
+ *
+ * @param expenseIndex Index of the expense in the list
+ */
+ public static void deleteExpense(int expenseIndex) {
+ deleteEntry(expenseIndex, expenseList);
+ }
+
+
+ /**
+ * Edits the description of the expense specified by the index in the list
+ *
+ * @param expenseIndex index of the expense in the list
+ * @param newEntry new description that will replace current description
+ */
+ public static void editExpense(int expenseIndex, String newEntry) {
+ editEntryDescription(expenseIndex, newEntry, expenseList);
+ }
+
+ /**
+ * Edits the amount of expense specified by the index in the list
+ * @param expenseIndex index of the expense in the list
+ * @param newEntry new amount that will replace the current amount
+ */
+ public static void editExpense(int expenseIndex, Double newEntry) {
+ editEntryCost(expenseIndex, newEntry, expenseList);
+ }
+
+ /**
+ * Edits the time of expense specified by the index in the list
+ *
+ * @param expenseIndex index of the expense in the list
+ * @param newEntry new time that will replace the current time
+ */
+ public static void editExpense(int expenseIndex, LocalDateTime newEntry) {
+ editEntryTime(expenseIndex, newEntry, expenseList);
+ }
+
+ /**
+ * Edits the category of expense specified by the index in the list
+ *
+ * @param expenseIndex index of the expense in the list
+ * @param newEntry new category of the expense
+ */
+ public static void editExpense(int expenseIndex, Category newEntry) {
+ editEntryCategory(expenseIndex, newEntry, expenseList);
+ }
+
+ /**
+ * Sorts expenses by amount, from largest to smallest
+ */
+ public static void sortExpensesByAmount() {
+ sortEntriesByAmount(expenseList);
+ Ui.showToUser("Total expenses: $" + getEntryListSum(expenseList));
+ Ui.showToUserWithLineBreak("");
+ }
+
+ /**
+ * Sorts expenses by date, from latest to oldest
+ */
+ public static void sortExpensesByDate() {
+ sortEntriesByDate(expenseList);
+ Ui.showToUser("Total expenses: $" + getEntryListSum(expenseList));
+ Ui.showToUserWithLineBreak("");
+ }
+
+ /**
+ * Finds list of all expenses that is under category specified
+ *
+ * @param category Category of the expense
+ */
+ public static void findExpenseByCategory(Category category) {
+ findEntriesByCategory(category, expenseList);
+ }
+
+ /**
+ * Returns list of all expenses made in the month specified
+ *
+ * @param year Year of the expense made
+ * @param month Month of the expense made
+ * @return List of entries that contain expenses made in the specified month
+ */
+ public static List getExpensesMadeInMonth(int year, Month month) {
+ return selectEntryForDate(year, month, expenseList);
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/entry/income/Income.java b/src/main/java/seedu/brokeMan/entry/income/Income.java
new file mode 100644
index 0000000000..ea100c743e
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/entry/income/Income.java
@@ -0,0 +1,16 @@
+package seedu.brokeMan.entry.income;
+
+import seedu.brokeMan.entry.Entry;
+import seedu.brokeMan.entry.Category;
+import java.time.LocalDateTime;
+
+public class Income extends Entry{
+ public Income(double amount, String info, LocalDateTime time, Category category) {
+ super(amount, info, time, category);
+ }
+
+ public String toString() {
+ return String.format("$%.2f earned on %s - %s [%s]", amount, info,
+ super.convertTimeToString(), category.name());
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/entry/income/IncomeList.java b/src/main/java/seedu/brokeMan/entry/income/IncomeList.java
new file mode 100644
index 0000000000..c422f9fc51
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/entry/income/IncomeList.java
@@ -0,0 +1,140 @@
+package seedu.brokeMan.entry.income;
+
+import seedu.brokeMan.entry.Category;
+import seedu.brokeMan.entry.Entry;
+import seedu.brokeMan.entry.EntryList;
+import seedu.brokeMan.parser.StringToTime;
+import seedu.brokeMan.ui.Ui;
+
+import java.time.LocalDateTime;
+import java.time.Month;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Optional;
+
+public class IncomeList extends EntryList {
+ public static final LinkedList incomeList = new LinkedList<>();
+
+ /**
+ * Adds new income to the list.
+ *
+ * @param newIncome the new income to be added
+ */
+ public static void addIncome(Income newIncome) {
+ addEntry(newIncome, incomeList);
+ }
+
+ /**
+ * delete specific income in the list
+ *
+ * @param index index of the income in the list
+ */
+ public static void deleteIncome(int index) {
+ deleteEntry(index, incomeList);
+ }
+
+ /**
+ * Lists incomes saved
+ *
+ * @param date Optional of String that contains information about the date
+ */
+ public static void listIncome(Optional date) {
+ int year = StringToTime.createYearFromString(date);
+ Month month = StringToTime.createMonthFromString(date);
+
+ List incomeOfDate = getIncomesMadeInMonth(year, month);
+ String dateInString = StringToTime.createDateString(year, month);
+
+ Ui.showToUser("Here are the income you have made for " + dateInString + ".");
+ listEntry(incomeOfDate);
+ Ui.showToUser("Total income: $" + getEntryListSum(incomeOfDate));
+ Ui.showToUserWithLineBreak("");
+ }
+
+ /**
+ * list out income in the list
+ */
+ public static void listIncome() {
+ Ui.showToUser("Here are the income you have made.");
+ listEntry(incomeList);
+ Ui.showToUser(String.format("Total income: $%.2f", getEntryListSum(incomeList)));
+ Ui.showToUserWithLineBreak("");
+ }
+
+ /**
+ * Edits the description of the income specified by the index in the list
+ *
+ * @param index index of the income in the list
+ * @param newEntry new description that will replace current description
+ */
+ public static void editIncome(int index, String newEntry) {
+ editEntryDescription(index, newEntry, incomeList);
+ }
+
+ /**
+ * Edits the amount of income specified by the index in the list
+ *
+ * @param index index of the income in the list
+ * @param newEntry new amount that will replace the current amount
+ */
+ public static void editIncome(int index, Double newEntry) {
+ editEntryCost(index, newEntry, incomeList);
+ }
+
+ /**
+ * Edits the time of income specified by the index in the list
+ *
+ * @param index index of the income in the list
+ * @param newEntry new time that will replace the current time
+ */
+ public static void editIncome(int index, LocalDateTime newEntry) {
+ editEntryTime(index, newEntry, incomeList);
+ }
+
+ /**
+ * Edits the category of income specified by the index in the list
+ *
+ * @param index index of the income in the list
+ * @param newEntry new category of the income
+ */
+ public static void editIncome(int index, Category newEntry) { editEntryCategory(index, newEntry, incomeList);}
+
+
+ /**
+ * Sorts incomes by amount, from largest to smallest
+ */
+ public static void sortIncomeByAmount() {
+ sortEntriesByAmount(incomeList);
+ Ui.showToUser(String.format("Total income: $%.2f", getEntryListSum(incomeList)));
+ Ui.showToUserWithLineBreak("");
+ }
+
+ /**
+ * Sorts incomes by date, from latest to oldest
+ */
+ public static void sortIncomeByDate() {
+ sortEntriesByDate(incomeList);
+ Ui.showToUser(String.format("Total income: $%.2f", getEntryListSum(incomeList)));
+ Ui.showToUserWithLineBreak("");
+ }
+
+ /**
+ * Finds list of all incomes that is under category specified
+ *
+ * @param category Category of the income
+ */
+ public static void findIncomeByCategory(Category category) {
+ findEntriesByCategory(category, incomeList);
+ }
+
+ /**
+ * Returns list of all incomes made in the month specified
+ *
+ * @param year Year of the income made
+ * @param month Month of the income made
+ * @return List of entries that contain incomes made in the specified month
+ */
+ public static List getIncomesMadeInMonth(int year, Month month) {
+ return selectEntryForDate(year, month, incomeList);
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/exception/AmountIsNotADoubleException.java b/src/main/java/seedu/brokeMan/exception/AmountIsNotADoubleException.java
new file mode 100644
index 0000000000..299cf654de
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/exception/AmountIsNotADoubleException.java
@@ -0,0 +1,9 @@
+package seedu.brokeMan.exception;
+
+import static seedu.brokeMan.common.Messages.MESSAGE_AMOUNT_NOT_DOUBLE;
+
+public class AmountIsNotADoubleException extends BrokeManException {
+ public String getMessage() {
+ return MESSAGE_AMOUNT_NOT_DOUBLE;
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/exception/BrokeManException.java b/src/main/java/seedu/brokeMan/exception/BrokeManException.java
new file mode 100644
index 0000000000..b42fd24c54
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/exception/BrokeManException.java
@@ -0,0 +1,5 @@
+package seedu.brokeMan.exception;
+
+public abstract class BrokeManException extends Exception {
+ public abstract String getMessage();
+}
diff --git a/src/main/java/seedu/brokeMan/exception/BudgetNotADoubleException.java b/src/main/java/seedu/brokeMan/exception/BudgetNotADoubleException.java
new file mode 100644
index 0000000000..d973eee36b
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/exception/BudgetNotADoubleException.java
@@ -0,0 +1,9 @@
+package seedu.brokeMan.exception;
+
+public class BudgetNotADoubleException extends BrokeManException {
+ @Override
+ public String getMessage() {
+ return "The budget you have set is not a double.\n" +
+ "| If you are trying to enter date information, please use the 't/' flag.";
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/exception/CategoryNotCorrectException.java b/src/main/java/seedu/brokeMan/exception/CategoryNotCorrectException.java
new file mode 100644
index 0000000000..3919d7cf61
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/exception/CategoryNotCorrectException.java
@@ -0,0 +1,8 @@
+package seedu.brokeMan.exception;
+import static seedu.brokeMan.common.Messages.MESSAGE_INVALID_CATEGORY;
+public class CategoryNotCorrectException extends BrokeManException{
+ @Override
+ public String getMessage() {
+ return MESSAGE_INVALID_CATEGORY;
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/exception/ContainDuplicatedFlagException.java b/src/main/java/seedu/brokeMan/exception/ContainDuplicatedFlagException.java
new file mode 100644
index 0000000000..ca855bbad6
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/exception/ContainDuplicatedFlagException.java
@@ -0,0 +1,10 @@
+package seedu.brokeMan.exception;
+
+import static seedu.brokeMan.common.Messages.MESSAGE_CONTAIN_DUPLICATED_FLAGS;
+
+public class ContainDuplicatedFlagException extends BrokeManException {
+ @Override
+ public String getMessage() {
+ return MESSAGE_CONTAIN_DUPLICATED_FLAGS;
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/exception/ContainsEmptyFlagException.java b/src/main/java/seedu/brokeMan/exception/ContainsEmptyFlagException.java
new file mode 100644
index 0000000000..9da1783619
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/exception/ContainsEmptyFlagException.java
@@ -0,0 +1,8 @@
+package seedu.brokeMan.exception;
+
+public class ContainsEmptyFlagException extends BrokeManException {
+ @Override
+ public String getMessage() {
+ return "Your flags description cannot be empty.";
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/exception/DateOutOfRangeException.java b/src/main/java/seedu/brokeMan/exception/DateOutOfRangeException.java
new file mode 100644
index 0000000000..1bfa5727fa
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/exception/DateOutOfRangeException.java
@@ -0,0 +1,9 @@
+package seedu.brokeMan.exception;
+
+public class DateOutOfRangeException extends BrokeManException {
+
+ @Override
+ public String getMessage() {
+ return "Date entered beyond the available date range, which is from year 2000 to 9999";
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/exception/ExceedMaximumLengthForAmountException.java b/src/main/java/seedu/brokeMan/exception/ExceedMaximumLengthForAmountException.java
new file mode 100644
index 0000000000..bc2f8f55d1
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/exception/ExceedMaximumLengthForAmountException.java
@@ -0,0 +1,10 @@
+package seedu.brokeMan.exception;
+
+import static seedu.brokeMan.common.Messages.MESSAGE_EXCEED_MAXIMUM_LENGTH_FOR_AMOUNT;
+
+public class ExceedMaximumLengthForAmountException extends BrokeManException {
+ @Override
+ public String getMessage() {
+ return MESSAGE_EXCEED_MAXIMUM_LENGTH_FOR_AMOUNT;
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/exception/IncorrectTypeException.java b/src/main/java/seedu/brokeMan/exception/IncorrectTypeException.java
new file mode 100644
index 0000000000..e9870c3955
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/exception/IncorrectTypeException.java
@@ -0,0 +1,10 @@
+package seedu.brokeMan.exception;
+
+import static seedu.brokeMan.common.Messages.MESSAGE_INCORRECT_TYPE;
+
+public class IncorrectTypeException extends BrokeManException {
+ @Override
+ public String getMessage() {
+ return MESSAGE_INCORRECT_TYPE;
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/exception/IndexNotAnIntegerException.java b/src/main/java/seedu/brokeMan/exception/IndexNotAnIntegerException.java
new file mode 100644
index 0000000000..25c3e12cf2
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/exception/IndexNotAnIntegerException.java
@@ -0,0 +1,9 @@
+package seedu.brokeMan.exception;
+
+import static seedu.brokeMan.common.Messages.MESSAGE_INDEX_NOT_INTEGER;
+
+public class IndexNotAnIntegerException extends BrokeManException {
+ public String getMessage() {
+ return MESSAGE_INDEX_NOT_INTEGER;
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/exception/InvalidAddCommandException.java b/src/main/java/seedu/brokeMan/exception/InvalidAddCommandException.java
new file mode 100644
index 0000000000..b090b15f5a
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/exception/InvalidAddCommandException.java
@@ -0,0 +1,10 @@
+package seedu.brokeMan.exception;
+
+import static seedu.brokeMan.common.Messages.MESSAGE_INVALID_ADD_COMMAND;
+
+public class InvalidAddCommandException extends BrokeManException {
+ @Override
+ public String getMessage() {
+ return MESSAGE_INVALID_ADD_COMMAND;
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/exception/InvalidDateTimeException.java b/src/main/java/seedu/brokeMan/exception/InvalidDateTimeException.java
new file mode 100644
index 0000000000..ed04cc03fc
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/exception/InvalidDateTimeException.java
@@ -0,0 +1,10 @@
+package seedu.brokeMan.exception;
+
+import static seedu.brokeMan.common.Messages.MESSAGE_INVALID_TIME;
+
+public class InvalidDateTimeException extends BrokeManException {
+ @Override
+ public String getMessage() {
+ return MESSAGE_INVALID_TIME;
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/exception/InvalidEditCommandException.java b/src/main/java/seedu/brokeMan/exception/InvalidEditCommandException.java
new file mode 100644
index 0000000000..d3458d19a7
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/exception/InvalidEditCommandException.java
@@ -0,0 +1,10 @@
+package seedu.brokeMan.exception;
+
+import static seedu.brokeMan.common.Messages.MESSAGE_INVALID_EDIT_COMMAND;
+
+public class InvalidEditCommandException extends BrokeManException {
+ @Override
+ public String getMessage() {
+ return MESSAGE_INVALID_EDIT_COMMAND;
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/exception/InvalidMonthTimeException.java b/src/main/java/seedu/brokeMan/exception/InvalidMonthTimeException.java
new file mode 100644
index 0000000000..b2206af620
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/exception/InvalidMonthTimeException.java
@@ -0,0 +1,10 @@
+package seedu.brokeMan.exception;
+
+import static seedu.brokeMan.common.Messages.MESSAGE_INVALID_MONTH;
+
+public class InvalidMonthTimeException extends BrokeManException {
+ @Override
+ public String getMessage() {
+ return MESSAGE_INVALID_MONTH;
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/exception/InvalidOptionalTimeFlagException.java b/src/main/java/seedu/brokeMan/exception/InvalidOptionalTimeFlagException.java
new file mode 100644
index 0000000000..624384b6d8
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/exception/InvalidOptionalTimeFlagException.java
@@ -0,0 +1,10 @@
+package seedu.brokeMan.exception;
+
+import static seedu.brokeMan.common.Messages.MESSAGE_INVALID_OPTIONAL_TIME_FLAG;
+
+public class InvalidOptionalTimeFlagException extends BrokeManException {
+ @Override
+ public String getMessage() {
+ return MESSAGE_INVALID_OPTIONAL_TIME_FLAG;
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/exception/NegativeAmountException.java b/src/main/java/seedu/brokeMan/exception/NegativeAmountException.java
new file mode 100644
index 0000000000..7bd4f4ca97
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/exception/NegativeAmountException.java
@@ -0,0 +1,10 @@
+package seedu.brokeMan.exception;
+
+import static seedu.brokeMan.common.Messages.MESSAGE_AMOUNT_LESS_THAN_OR_EQUALS_ZERO;
+
+public class NegativeAmountException extends BrokeManException {
+ @Override
+ public String getMessage() {
+ return MESSAGE_AMOUNT_LESS_THAN_OR_EQUALS_ZERO;
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/exception/NewDescriptionContainFlagsException.java b/src/main/java/seedu/brokeMan/exception/NewDescriptionContainFlagsException.java
new file mode 100644
index 0000000000..864777267f
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/exception/NewDescriptionContainFlagsException.java
@@ -0,0 +1,10 @@
+package seedu.brokeMan.exception;
+
+import static seedu.brokeMan.common.Messages.MESSAGE_NEW_DESCRIPTION_CONTAIN_FLAGS;
+
+public class NewDescriptionContainFlagsException extends BrokeManException {
+ @Override
+ public String getMessage() {
+ return MESSAGE_NEW_DESCRIPTION_CONTAIN_FLAGS;
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/exception/WrongFlagOrderException.java b/src/main/java/seedu/brokeMan/exception/WrongFlagOrderException.java
new file mode 100644
index 0000000000..c652c8355b
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/exception/WrongFlagOrderException.java
@@ -0,0 +1,10 @@
+package seedu.brokeMan.exception;
+
+import static seedu.brokeMan.common.Messages.MESSAGE_WRONG_FLAG_ORDER;
+
+public class WrongFlagOrderException extends BrokeManException {
+ @Override
+ public String getMessage() {
+ return MESSAGE_WRONG_FLAG_ORDER;
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/exception/hasNotSetBudgetException.java b/src/main/java/seedu/brokeMan/exception/hasNotSetBudgetException.java
new file mode 100644
index 0000000000..1c96fb5738
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/exception/hasNotSetBudgetException.java
@@ -0,0 +1,7 @@
+package seedu.brokeMan.exception;
+
+public class hasNotSetBudgetException extends BrokeManException {
+ public String getMessage() {
+ return "You have not set your budget for this month.";
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/parser/Parser.java b/src/main/java/seedu/brokeMan/parser/Parser.java
new file mode 100644
index 0000000000..1a8895c1c6
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/parser/Parser.java
@@ -0,0 +1,647 @@
+package seedu.brokeMan.parser;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import seedu.brokeMan.command.AddExpenseCommand;
+import seedu.brokeMan.command.AddIncomeCommand;
+import seedu.brokeMan.command.Command;
+import seedu.brokeMan.command.DeleteExpenseCommand;
+import seedu.brokeMan.command.DeleteIncomeCommand;
+import seedu.brokeMan.command.EditExpenseCommand;
+import seedu.brokeMan.command.EditIncomeCommand;
+import seedu.brokeMan.exception.ExceedMaximumLengthForAmountException;
+import seedu.brokeMan.command.ExitCommand;
+import seedu.brokeMan.command.HelpCommand;
+import seedu.brokeMan.command.InvalidCommand;
+import seedu.brokeMan.command.ListExpenseCommand;
+import seedu.brokeMan.command.ListIncomeCommand;
+import seedu.brokeMan.command.SetBudgetCommand;
+import seedu.brokeMan.command.SortExpenseByAmountCommand;
+import seedu.brokeMan.command.SortExpenseByDateCommand;
+import seedu.brokeMan.command.SortIncomeByAmountCommand;
+import seedu.brokeMan.command.SortIncomeByDateCommand;
+import seedu.brokeMan.command.ViewBudgetCommand;
+import seedu.brokeMan.exception.hasNotSetBudgetException;
+import seedu.brokeMan.exception.InvalidDateTimeException;
+import seedu.brokeMan.exception.InvalidMonthTimeException;
+import seedu.brokeMan.exception.DateOutOfRangeException;
+import seedu.brokeMan.entry.Category;
+import seedu.brokeMan.exception.AmountIsNotADoubleException;
+import seedu.brokeMan.exception.BrokeManException;
+import seedu.brokeMan.exception.BudgetNotADoubleException;
+import seedu.brokeMan.exception.CategoryNotCorrectException;
+import seedu.brokeMan.exception.ContainDuplicatedFlagException;
+import seedu.brokeMan.exception.ContainsEmptyFlagException;
+import seedu.brokeMan.exception.IncorrectTypeException;
+import seedu.brokeMan.exception.IndexNotAnIntegerException;
+import seedu.brokeMan.exception.InvalidAddCommandException;
+import seedu.brokeMan.exception.InvalidEditCommandException;
+import seedu.brokeMan.exception.InvalidOptionalTimeFlagException;
+import seedu.brokeMan.exception.NegativeAmountException;
+import seedu.brokeMan.exception.NewDescriptionContainFlagsException;
+import seedu.brokeMan.exception.WrongFlagOrderException;
+
+import java.math.RoundingMode;
+import java.text.DecimalFormat;
+import java.time.DateTimeException;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeParseException;
+
+import static seedu.brokeMan.common.Messages.MESSAGE_ARGUMENTS_NOT_SPECIFIED;
+import static seedu.brokeMan.common.Messages.MESSAGE_INDEX_NOT_SPECIFIED_EXCEPTION;
+import static seedu.brokeMan.parser.StringToCategory.convertStringToCategory;
+
+
+/*
+Some parts of the code are copied and adapted from TextUI.java of addressbook-level2
+with the link to the original code at:
+https://github.com/se-edu/addressbook-level2/blob/master/src/seedu/addressbook/parser/Parser.java
+ */
+
+public class Parser {
+
+ private static final DecimalFormat df = new DecimalFormat("0.00");
+
+ private static Logger logger = Logger.getLogger("Invalid Input Tracker");
+
+ /**
+ * parse the inputs entered by user into command for execution
+ *
+ * @param userFullInput full line of user input entered by user
+ * @return Command class based on the user's input
+ */
+ public static Command parseCommand(String userFullInput) {
+
+ UserInput userInput = UserInput.splitUserInput(userFullInput);
+
+ switch (userInput.userCommand) {
+ case AddExpenseCommand.COMMAND_WORD:
+ return prepareAddExpenseCommand(userInput.commandDescription);
+ case AddIncomeCommand.COMMAND_WORD:
+ return prepareAddIncomeCommand(userInput.commandDescription);
+ case ListExpenseCommand.COMMAND_WORD:
+ return prepareListExpenseCommand(userInput.commandDescription);
+ case ListIncomeCommand.COMMAND_WORD:
+ return prepareListIncomeCommand(userInput.commandDescription);
+ case EditExpenseCommand.COMMAND_WORD:
+ return prepareEditExpenseCommand(userInput.commandDescription);
+ case EditIncomeCommand.COMMAND_WORD:
+ return prepareEditIncomeCommand(userInput.commandDescription);
+ case DeleteExpenseCommand.COMMAND_WORD:
+ return prepareDeleteExpenseCommand(userInput.commandDescription);
+ case DeleteIncomeCommand.COMMAND_WORD:
+ return prepareDeleteIncomeCommand(userInput.commandDescription);
+ case SetBudgetCommand.COMMAND_WORD:
+ return prepareSetBudgetCommand(userInput.commandDescription);
+ case ViewBudgetCommand.COMMAND_WORD:
+ return prepareViewBudgetCommand(userInput.commandDescription);
+ case ExitCommand.COMMAND_WORD:
+ return new ExitCommand();
+ case SortExpenseByAmountCommand.COMMAND_WORD:
+ return new SortExpenseByAmountCommand();
+ case SortExpenseByDateCommand.COMMAND_WORD:
+ return new SortExpenseByDateCommand();
+ case SortIncomeByAmountCommand.COMMAND_WORD:
+ return new SortIncomeByAmountCommand();
+ case SortIncomeByDateCommand.COMMAND_WORD:
+ return new SortIncomeByDateCommand();
+ case HelpCommand.COMMAND_WORD: // fall through
+ default:
+ return new HelpCommand();
+ }
+ }
+
+ /**
+ * Prepares the view budget command
+ *
+ * @return the prepared command
+ */
+ private static Command prepareViewBudgetCommand(String description) {
+ String newDescription;
+ try {
+ if (description.equals("")) {
+ return new ViewBudgetCommand();
+ }
+ newDescription = checkValidOptionalTimeFlagException(description);
+ checkTimeWithInConstraint(newDescription);
+ return new ViewBudgetCommand(newDescription);
+ } catch (hasNotSetBudgetException e) {
+ return new InvalidCommand(e.getMessage(), SetBudgetCommand.MESSAGE_USAGE);
+ } catch (InvalidOptionalTimeFlagException | InvalidMonthTimeException | DateOutOfRangeException ex) {
+ return new InvalidCommand(ex.getMessage(), ViewBudgetCommand.MESSAGE_USAGE);
+ }
+ }
+
+ /**
+ * checks if the input string has a valid time flag and description
+ * @param description the input string to be checked
+ * @return the time string
+ * @throws InvalidOptionalTimeFlagException custom exception to indicate invalid time flag
+ * @throws InvalidMonthTimeException custom exception to indicate invalid time description
+ */
+ private static String checkValidOptionalTimeFlagException(String description)
+ throws InvalidOptionalTimeFlagException, InvalidMonthTimeException {
+ if (!description.startsWith("t/ ")) {
+ throw new InvalidOptionalTimeFlagException();
+ }
+ String newDescription = description.substring(3).trim();
+ try {
+ StringToTime.checkIfValidDateString(newDescription);
+ } catch (DateTimeParseException dtpe) {
+ throw new InvalidMonthTimeException();
+ }
+ return newDescription;
+ }
+
+ /**
+ * Parses description in the context of set budget command
+ *
+ * @param description full command description string
+ * @return the prepared command
+ */
+ private static Command prepareSetBudgetCommand(String description) {
+ if (description.equals("")) {
+ return new InvalidCommand("You did not specify your budget.",
+ SetBudgetCommand.MESSAGE_USAGE);
+ }
+
+ assert (!description.equals("")) : "You did not specify your budget\n";
+ String[] descriptionByWord = description.split(" t/ ");
+ if (descriptionByWord.length > 2 || descriptionByWord.length < 1) {
+ return new InvalidCommand("Invalid information entered", SetBudgetCommand.MESSAGE_USAGE);
+ }
+
+ double budget;
+ String budgetInString = descriptionByWord[0];
+
+ try {
+ budgetInString = checkExceedMaxCharForAmount(budgetInString);
+ budget = Double.parseDouble(budgetInString);
+ budget = Double.parseDouble(df.format(budget));
+ if (budget <= 0) {
+ String errorMessage = new NegativeAmountException().getMessage();
+ return new InvalidCommand(errorMessage, SetBudgetCommand.MESSAGE_USAGE);
+ }
+ } catch (ExceedMaximumLengthForAmountException e) {
+ logger.log(Level.WARNING, "Excessively Large Input");
+ return new InvalidCommand(e.getMessage(), SetBudgetCommand.MESSAGE_USAGE);
+
+ }catch (NumberFormatException nfe) {
+ String errorMessage = new BudgetNotADoubleException().getMessage();
+ return new InvalidCommand(errorMessage, SetBudgetCommand.MESSAGE_USAGE);
+ }
+
+ if (descriptionByWord.length == 2) {
+ try {
+ StringToTime.checkIfValidDateString(descriptionByWord[1]);
+ checkTimeWithInConstraint(descriptionByWord[1]);
+ } catch (DateTimeParseException dtpe) {
+ return new InvalidCommand(new InvalidMonthTimeException().getMessage(),
+ SetBudgetCommand.MESSAGE_USAGE);
+ } catch (DateOutOfRangeException sme) {
+ return new InvalidCommand(sme.getMessage(), SetBudgetCommand.MESSAGE_USAGE);
+ }
+ descriptionByWord[1] = descriptionByWord[1].trim();
+ return (descriptionByWord[1] == "" ? new SetBudgetCommand(budget)
+ : new SetBudgetCommand(budget, descriptionByWord[1]));
+ }
+ return new SetBudgetCommand(budget);
+ }
+
+ /**
+ * Parses the description in the context of delete expense command
+ *
+ * @param description full command description string
+ * @return the prepared command
+ */
+ private static Command prepareDeleteExpenseCommand(String description) {
+ if (description.equals("")) {
+ return new InvalidCommand(MESSAGE_INDEX_NOT_SPECIFIED_EXCEPTION,
+ DeleteExpenseCommand.MESSAGE_USAGE);
+ }
+
+ int index;
+ try {
+ index = Integer.parseInt(description);
+ } catch (NumberFormatException nfe) {
+ String errorMessage = new IndexNotAnIntegerException().getMessage();
+ return new InvalidCommand(errorMessage, DeleteExpenseCommand.MESSAGE_USAGE);
+ }
+
+ return new DeleteExpenseCommand(index);
+ }
+
+ /**
+ * Parses the command description in the context for delete income command
+ *
+ * @param description full command description string
+ * @return the prepared command
+ */
+ private static Command prepareDeleteIncomeCommand(String description) {
+ if (description.equals("")) {
+ return new InvalidCommand(MESSAGE_INDEX_NOT_SPECIFIED_EXCEPTION,
+ DeleteIncomeCommand.MESSAGE_USAGE);
+ }
+
+ int index;
+ try {
+ index = Integer.parseInt(description);
+ } catch (NumberFormatException nfe) {
+ String errorMessage = new IndexNotAnIntegerException().getMessage();
+ return new InvalidCommand(errorMessage, DeleteIncomeCommand.MESSAGE_USAGE);
+ }
+
+ return new DeleteIncomeCommand(index);
+ }
+
+ /**
+ * Parses the command description in the context for add expense command
+ *
+ * @param description full command description string
+ * @return the prepared command
+ */
+ private static Command prepareAddExpenseCommand(String description) {
+ // description in the form of "a/ d/ t/ time c/ category"
+
+ if (description.equals("")) {
+ return new InvalidCommand(MESSAGE_ARGUMENTS_NOT_SPECIFIED,
+ AddExpenseCommand.MESSAGE_USAGE);
+ }
+
+ String[] splitDescriptions;
+ try {
+ splitDescriptions = checkAddCommandException(description);
+ } catch (BrokeManException bme) {
+ return new InvalidCommand(bme.getMessage(), AddExpenseCommand.MESSAGE_USAGE);
+ }
+
+ double amount = Double.parseDouble(splitDescriptions[0]);
+ String newDescription = splitDescriptions[1];
+ Category category;
+ try {
+ category = convertStringToCategory(splitDescriptions[3]);
+ } catch (CategoryNotCorrectException e) {
+ logger.log(Level.INFO, "Unregistered Category:" + splitDescriptions[3]);
+ throw new RuntimeException(e);
+ }
+ LocalDateTime time = StringToTime.convertStringToTime(splitDescriptions[2]);
+
+ return new AddExpenseCommand(amount, newDescription, time, category);
+ }
+
+ /**
+ * Parses the command description in the context for add income command
+ *
+ * @param description full command description string
+ * @return the prepared command
+ */
+ private static Command prepareAddIncomeCommand(String description) {
+ // description in the form of "a/ d/ t/ time c/ category"
+
+ if (description.equals("")) {
+ return new InvalidCommand(MESSAGE_ARGUMENTS_NOT_SPECIFIED,
+ AddIncomeCommand.MESSAGE_USAGE);
+ }
+
+ String[] splitDescriptions;
+ try {
+ splitDescriptions = checkAddCommandException(description);
+ } catch (BrokeManException bme) {
+ logger.log(Level.WARNING, bme.getMessage());
+ return new InvalidCommand(bme.getMessage(), AddIncomeCommand.MESSAGE_USAGE);
+ }
+
+ double amount = Double.parseDouble(splitDescriptions[0]);
+ String newDescription = splitDescriptions[1];
+ Category category;
+ try {
+ category = convertStringToCategory(splitDescriptions[3]);
+ } catch (CategoryNotCorrectException e) {
+ throw new RuntimeException(e);
+ }
+ LocalDateTime time = StringToTime.convertStringToTime(splitDescriptions[2]);
+ return new AddIncomeCommand(amount, newDescription, time, category);
+ }
+
+ /**
+ * Parses the command description in the context for the context of add command and throws exceptions if found
+ *
+ * @param description full command description string
+ * @return the split command descriptions
+ * @throws BrokeManException the custom exception for specific exception case
+ */
+ public static String[] checkAddCommandException(String description) throws BrokeManException {
+ description = " " + description + " ";
+
+ boolean containsAllFlags = description.contains(" a/ ") &&
+ description.contains(" d/ ") && description.contains(" t/ ")
+ && description.contains(" c/");
+
+ if (!containsAllFlags) {
+ throw new InvalidAddCommandException();
+ }
+
+ boolean isFlagInCorrectOrder = description.indexOf("a/") < description.indexOf("d/") &&
+ description.indexOf("d/") < description.indexOf("t/") &&
+ description.indexOf("t/") < description.indexOf("c/");
+ if (!isFlagInCorrectOrder) {
+ throw new WrongFlagOrderException();
+ }
+
+ boolean hasDuplicatedFlags = (description.indexOf(" a/ ") != description.lastIndexOf(" a/ ")) ||
+ (description.indexOf(" d/ ") != description.lastIndexOf(" d/ ")) ||
+ (description.indexOf(" t/ ") != description.lastIndexOf(" t/ ")) ||
+ (description.indexOf(" c/ ") != description.lastIndexOf(" c/ "));
+ if (hasDuplicatedFlags) {
+ throw new ContainDuplicatedFlagException();
+ }
+
+ assert(isFlagInCorrectOrder) : "| flag is not in order";
+
+ String[] splitDescriptions = new String[4];
+ splitDescriptions[0] = description.substring(description.indexOf("a/") + 2,
+ description.indexOf("d/"));
+ splitDescriptions[1] = description.substring(description.indexOf("d/") + 2,
+ description.indexOf("t/"));
+ splitDescriptions[2] = description.substring(description.indexOf("t/") + 2,
+ description.indexOf("c/"));
+ splitDescriptions[3] = description.substring(description.indexOf("c/") + 2,
+ description.length());
+
+ splitDescriptions[0] = splitDescriptions[0].trim();
+ splitDescriptions[1] = splitDescriptions[1].trim();
+ splitDescriptions[2] = splitDescriptions[2].trim();
+ splitDescriptions[3] = splitDescriptions[3].trim();
+
+ checkEmptyAddFlag(splitDescriptions);
+ checkDoubleException(splitDescriptions[0]);
+ splitDescriptions[0] = checkExceedMaxCharForAmount(splitDescriptions[0]);
+ // 0.000001 will get pass the <= 0 check in checkDoubleException. checkExceedMaxCharForAmount will convert it to
+ // 0.00, which is why we call checkDoubleException again.
+ checkDoubleException(splitDescriptions[0]);
+ checkTimeException(splitDescriptions[2]);
+ checkTimeWithInConstraint(splitDescriptions[2]);
+ convertStringToCategory(splitDescriptions[3]);
+ return splitDescriptions;
+ }
+
+ private static void checkTimeWithInConstraint(String date) throws DateOutOfRangeException {
+ String[] dateByWord = new String[5];
+ if (date.contains("/")) {
+ dateByWord = date.split("/");
+ } else if (date.contains(" ")) {
+ dateByWord = date.split(" ");
+ }
+ if (Integer.parseInt(dateByWord[0]) < 2000 || Integer.parseInt(dateByWord[0]) > 9999) {
+ throw new DateOutOfRangeException();
+ }
+ }
+
+ private static String checkExceedMaxCharForAmount(String description) throws ExceedMaximumLengthForAmountException {
+ double amount = Double.parseDouble(description);
+ double maxAmountAllowed = 9999999999.99D;
+ if (amount > maxAmountAllowed) {
+ throw new ExceedMaximumLengthForAmountException();
+ }
+ df.setRoundingMode(RoundingMode.HALF_UP);
+ return df.format(amount);
+ }
+
+ private static Command prepareListExpenseCommand(String description) {
+ if (description.equals("")) {
+ return new ListExpenseCommand();
+ }
+ String newDescription;
+ try {
+ newDescription = checkValidOptionalTimeFlagException(description);
+ checkTimeWithInConstraint(newDescription);
+ } catch (InvalidOptionalTimeFlagException | InvalidMonthTimeException | DateOutOfRangeException e) {
+ return new InvalidCommand(e.getMessage(), ListExpenseCommand.MESSAGE_USAGE);
+ }
+ return new ListExpenseCommand(newDescription);
+ }
+
+ private static Command prepareListIncomeCommand(String description) {
+ if (description.equals("")) {
+ return new ListIncomeCommand();
+ }
+ String newDescription;
+ try {
+ newDescription = checkValidOptionalTimeFlagException(description);
+ checkTimeWithInConstraint(newDescription);
+ } catch (InvalidOptionalTimeFlagException | InvalidMonthTimeException | DateOutOfRangeException e) {
+ return new InvalidCommand(e.getMessage(), ListIncomeCommand.MESSAGE_USAGE);
+ }
+ return new ListIncomeCommand(newDescription);
+ }
+
+ /**
+ * Checks if the descriptions of the flags are empty
+ *
+ * @param splitDescriptions split command descriptions that contains the description of flags
+ * @throws ContainsEmptyFlagException custom exception to indicate flag descriptions is / are empty
+ */
+ private static void checkEmptyAddFlag(String[] splitDescriptions) throws ContainsEmptyFlagException {
+ assert (splitDescriptions.length == 4) : "Invalid input\n";
+ for (String description : splitDescriptions) {
+ if (description.isEmpty()) {
+ throw new ContainsEmptyFlagException();
+ }
+ }
+ }
+
+ private static void checkEmptyFlag(String[] splitDescriptions) throws ContainsEmptyFlagException {
+ assert (splitDescriptions.length == 3) : "Invalid input\n";
+ for (String description : splitDescriptions) {
+ if (description.isEmpty()) {
+ throw new ContainsEmptyFlagException();
+ }
+ }
+ }
+
+ /**
+ * Parses the command description in the context for edit expense command
+ *
+ * @param description full command description string
+ * @return the prepared edit expense command
+ */
+ private static Command prepareEditExpenseCommand(String description) {
+ if (description.equals("")) {
+ return new InvalidCommand(MESSAGE_ARGUMENTS_NOT_SPECIFIED,
+ EditExpenseCommand.MESSAGE_USAGE);
+ }
+
+ String[] splitDescriptions;
+
+ try {
+ splitDescriptions = checkEditCommandException(description);
+ if (splitDescriptions[1].equals("time")) {
+ checkTimeWithInConstraint(splitDescriptions[2]);
+ }
+ } catch (BrokeManException bme) {
+ return new InvalidCommand(bme.getMessage(), EditExpenseCommand.MESSAGE_USAGE);
+ }
+
+ return new EditExpenseCommand(Integer.parseInt(splitDescriptions[0]),
+ splitDescriptions[1], splitDescriptions[2]);
+ }
+
+ /**
+ * Parses the command description in the context for edit income command
+ *
+ * @param description full command description string
+ * @return the prepared edit income command
+ */
+ private static Command prepareEditIncomeCommand(String description) {
+ if (description.equals("")) {
+ return new InvalidCommand(MESSAGE_ARGUMENTS_NOT_SPECIFIED,
+ EditIncomeCommand.MESSAGE_USAGE);
+ }
+
+ String[] splitDescriptions;
+
+ try {
+ splitDescriptions = checkEditCommandException(description);
+ if (splitDescriptions[1].equals("time")) {
+ checkTimeWithInConstraint(splitDescriptions[2]);
+ }
+ } catch (BrokeManException bme) {
+ return new InvalidCommand(bme.getMessage(), EditIncomeCommand.MESSAGE_USAGE);
+ }
+
+ return new EditIncomeCommand(Integer.parseInt(splitDescriptions[0]),
+ splitDescriptions[1], splitDescriptions[2]);
+ }
+
+ /**
+ * Checks if the input is a valid date and time
+ * @param timeToCheck the input to be checked
+ * @throws InvalidDateTimeException custom exception to indicate invalid data and time input
+ */
+ private static void checkTimeException(String timeToCheck) throws InvalidDateTimeException {
+ try {
+ StringToTime.convertStringToTime(timeToCheck);
+ } catch (DateTimeException dte) {
+ throw new InvalidDateTimeException();
+ }
+ }
+
+
+ /**
+ * Checks if the input string to a double greater than 0
+ *
+ * @param cost input string to be converted
+ * @throws AmountIsNotADoubleException custom exception to indicate string input
+ * cannot be converted to a double
+ * @throws NegativeAmountException custom exception to indicate negative double value
+ */
+ private static void checkDoubleException(String cost) throws AmountIsNotADoubleException,
+ NegativeAmountException {
+ double amount;
+
+ try {
+ amount = Double.parseDouble(cost);
+ if (amount <= 0) {
+ throw new NegativeAmountException();
+ }
+ } catch (NumberFormatException nfe) {
+ throw new AmountIsNotADoubleException();
+ }
+ }
+
+
+ /**
+ * Parses the command description in the context for the context of
+ * delete command and throws exceptions if found
+ *
+ * @param description full command description string
+ * @return the split command descriptions
+ * @throws BrokeManException the custom exception that indicate the specific exception cases
+ */
+ private static String[] checkEditCommandException(String description) throws BrokeManException {
+ description = " " + description + " ";
+
+ boolean containsAllFlags = description.contains(" i/ ") &&
+ description.contains(" t/ ") && description.contains(" n/");
+ if (!containsAllFlags) {
+ throw new InvalidEditCommandException();
+ }
+ boolean isFlagInOrder = description.indexOf("i/") < description.indexOf("t/") &&
+ description.indexOf("t/") < description.indexOf("n/");
+ if (!isFlagInOrder) {
+ throw new WrongFlagOrderException();
+ }
+
+ boolean hasDuplicatedFlags = (description.indexOf(" i/ ") != description.lastIndexOf(" i/ ")) ||
+ (description.indexOf(" t/ ") != description.lastIndexOf(" t/ ")) ||
+ (description.indexOf(" n/ ") != description.lastIndexOf(" n/ "));
+ if (hasDuplicatedFlags) {
+ throw new ContainDuplicatedFlagException();
+ }
+
+ String[] splitDescriptions = new String[3];
+ splitDescriptions[0] = description.substring(description.indexOf("i/") + 2,
+ description.indexOf("t/"));
+ splitDescriptions[1] = description.substring(description.indexOf("t/") + 2,
+ description.indexOf("n/"));
+ splitDescriptions[2] = description.substring(description.indexOf("n/") + 2,
+ description.length());
+
+ splitDescriptions[0] = splitDescriptions[0].trim();
+ splitDescriptions[1] = splitDescriptions[1].trim();
+ splitDescriptions[2] = splitDescriptions[2].trim();
+
+ checkEmptyFlag(splitDescriptions);
+ checkIsIntegerIndex(splitDescriptions[0]);
+ checkCorrectType(splitDescriptions[1]);
+ if (splitDescriptions[1].equals("category")) {
+ convertStringToCategory(splitDescriptions[2]);
+ } else if (splitDescriptions[1].equals("amount")) {
+ checkDoubleException(splitDescriptions[2]);
+ splitDescriptions[2] = checkExceedMaxCharForAmount(splitDescriptions[2]);
+ } else if (splitDescriptions[1].equals("time")) {
+ checkTimeException((splitDescriptions[2]));
+ } else if (splitDescriptions[1].equals("info")) {
+ checkContainFlagsException(splitDescriptions[2]);
+ }
+
+ return splitDescriptions;
+ }
+
+ private static void checkContainFlagsException(String newDescription) throws NewDescriptionContainFlagsException {
+ newDescription = " " + newDescription + " ";
+ boolean isContainFlags = newDescription.contains(" c/ ") ||
+ newDescription.contains(" a/ ") || newDescription.contains(" d/ ");
+ if (isContainFlags) {
+ throw new NewDescriptionContainFlagsException();
+ }
+ }
+
+ /**
+ * Checks if string input is an integer
+ *
+ * @param index the input string to be checked
+ * @throws IndexNotAnIntegerException custom exception to indicate that
+ * input string cannot be converted to integer
+ */
+ private static void checkIsIntegerIndex(String index) throws IndexNotAnIntegerException {
+ try {
+ Integer.parseInt(index);
+ } catch (NumberFormatException nfe) {
+ throw new IndexNotAnIntegerException();
+ }
+ }
+
+ /**
+ * Checks if the type is correct (amount, info, or time)
+ * @param type input to be checked
+ * @throws IncorrectTypeException custom exception to indicate incorrect type exception
+ */
+ private static void checkCorrectType(String type) throws IncorrectTypeException {
+ if (!type.equals("amount") && !type.equals("info") &&
+ !type.equals("time") && !type.equals("category")) {
+ throw new IncorrectTypeException();
+ }
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/parser/StringToCategory.java b/src/main/java/seedu/brokeMan/parser/StringToCategory.java
new file mode 100644
index 0000000000..8b831313f0
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/parser/StringToCategory.java
@@ -0,0 +1,35 @@
+package seedu.brokeMan.parser;
+
+
+import seedu.brokeMan.entry.Category;
+import seedu.brokeMan.exception.CategoryNotCorrectException;
+
+
+public class StringToCategory {
+ public static Category convertStringToCategory(String categoryString) throws CategoryNotCorrectException {
+ Category category = null;
+ if (categoryString.equals("FOOD")) {
+ category = Category.FOOD;
+ } else if (categoryString.equals("SHOPPING")) {
+ category = Category.SHOPPING;
+ } else if (categoryString.equals("GROCERIES")) {
+ category = Category.GROCERIES;
+ } else if (categoryString.equals("TRANSPORTATION")) {
+ category = Category.TRANSPORTATION;
+ } else if (categoryString.equals("ENTERTAINMENT")) {
+ category = Category.ENTERTAINMENT;
+ } else if (categoryString.equals("TRAVEL")) {
+ category = Category.TRAVEL;
+ } else if (categoryString.equals("SALARY")) {
+ category = Category.SALARY;
+ } else if (categoryString.equals("INVESTMENT")) {
+ category = Category.INVESTMENT;
+ } else if (categoryString.equals("OTHERS")){
+ category = Category.OTHERS;
+ } else {
+ throw new CategoryNotCorrectException();
+ }
+ assert category != null : "Category should be one of the enum tags";
+ return category;
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/parser/StringToTime.java b/src/main/java/seedu/brokeMan/parser/StringToTime.java
new file mode 100644
index 0000000000..040f5ee280
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/parser/StringToTime.java
@@ -0,0 +1,71 @@
+package seedu.brokeMan.parser;
+
+import java.time.LocalDateTime;
+import java.time.LocalDate;
+import java.time.DateTimeException;
+import java.time.Month;
+import java.time.YearMonth;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.util.Optional;
+
+import static seedu.brokeMan.common.Messages.MESSAGE_INVALID_TIME;
+
+public class StringToTime {
+ public static LocalDateTime convertStringToTime(String timeInString) throws DateTimeException {
+ String[] timeByWord = timeInString.split(" ");
+ if (timeByWord.length != 5) {
+ throw new DateTimeException(MESSAGE_INVALID_TIME);
+ } else if (!areWordsValidTimeNumbers(timeByWord)) {
+ throw new DateTimeException(MESSAGE_INVALID_TIME);
+ }
+ // dummy assert
+ assert (timeByWord.length == 5) : MESSAGE_INVALID_TIME;
+
+ Integer[] timeByNumber = new Integer[5];
+ for (int i = 0; i < 5; i++) {
+ timeByNumber[i] = Integer.parseInt(timeByWord[i]);
+ }
+ return LocalDateTime.of(timeByNumber[0], timeByNumber[1], timeByNumber[2], timeByNumber[3], timeByNumber[4]);
+ }
+
+ private static boolean areWordsValidTimeNumbers(String[] timeByWord) throws DateTimeException {
+ try {
+ for (String time : timeByWord) {
+ if (Integer.parseInt(time) < 0) {
+ throw new DateTimeException(MESSAGE_INVALID_TIME);
+ }
+ }
+ return true;
+ } catch (NumberFormatException e) {
+ throw new DateTimeException(MESSAGE_INVALID_TIME);
+ }
+ }
+
+ public static String createDateString(int year, Month month) {
+ return Integer.toString(year) + "/" + month.toString();
+ }
+
+ public static int createYearFromString(Optional dateInString) {
+ int year = LocalDate.now().getYear();
+ if (dateInString.isPresent()) {
+ String[] yearAndMonth = dateInString.get().split("/");
+ year = Integer.parseInt(yearAndMonth[0]);
+ }
+ return year;
+ }
+
+ public static Month createMonthFromString(Optional dateInString) {
+ Month month = LocalDate.now().getMonth();
+ if (dateInString.isPresent()) {
+ String[] yearAndMonth = dateInString.get().split("/");
+ month = Month.of(Integer.parseInt(yearAndMonth[1]));
+ }
+ return month;
+ }
+
+ public static void checkIfValidDateString(String date) throws DateTimeParseException {
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM");
+ YearMonth yearMonth = YearMonth.parse(date, formatter);
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/parser/UserInput.java b/src/main/java/seedu/brokeMan/parser/UserInput.java
new file mode 100644
index 0000000000..af651a7512
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/parser/UserInput.java
@@ -0,0 +1,19 @@
+package seedu.brokeMan.parser;
+
+public class UserInput {
+ protected String userCommand;
+ protected String commandDescription;
+
+ protected UserInput(String userCommand, String commandDescription) {
+ this.userCommand = userCommand;
+ this.commandDescription = commandDescription;
+ }
+
+ protected static UserInput splitUserInput(String userFullInput) {
+ if (!userFullInput.contains(" ")) {
+ return new UserInput(userFullInput, "");
+ }
+ String[] userSplitInputs = userFullInput.split(" ", 2);
+ return new UserInput(userSplitInputs[0], userSplitInputs[1].trim());
+ }
+}
diff --git a/src/main/java/seedu/brokeMan/save/SaveBudget.java b/src/main/java/seedu/brokeMan/save/SaveBudget.java
new file mode 100644
index 0000000000..10591ef841
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/save/SaveBudget.java
@@ -0,0 +1,87 @@
+package seedu.brokeMan.save;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.time.Month;
+import java.util.HashMap;
+
+import static seedu.brokeMan.budget.Budget.budgetEachMonth;
+
+
+public class SaveBudget {
+ public static void writeFile(HashMap> budget) {
+ try {
+ FileWriter myWriter = new FileWriter("./data/BudgetData.txt");
+ myWriter.flush();
+ final String[] message = {""};
+ budget.forEach((key, value) -> {
+ message[0] += key + "_";
+ value.forEach((month, val) -> {
+ message[0] += month + ":" + val + ",";
+ });
+ message[0] += "\n";
+ });
+ myWriter.write(message[0]);
+ myWriter.close();
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ public static HashMap> readFile() {
+ try {
+ File directory = new File("./data");
+ if (!directory.exists()) {
+ directory.mkdir();
+ }
+ File file = new File("./data/BudgetData.txt");
+ if (!file.exists()) {
+ file.createNewFile();
+ }
+ BufferedReader reader = new BufferedReader(new FileReader(file));
+ String line;
+ while ((line = reader.readLine()) != null) {
+ String[] parts = line.split("_");
+ if (parts.length != 2) {
+ continue;
+ }
+ int key;
+ try {
+ key = Integer.parseInt(parts[0]);
+ } catch (NumberFormatException e) {
+ continue;
+ }
+ HashMap value = new HashMap<>();
+ String[] entries = parts[1].split(",");
+ for (String entry : entries) {
+ String[] pair = entry.split(":");
+ if (pair.length != 2) {
+ continue;
+ }
+ Month month;
+ try {
+ month = Month.valueOf(pair[0]);
+ } catch (IllegalArgumentException e) {
+ continue;
+ }
+ double val;
+ try {
+ val = Double.parseDouble(pair[1]);
+ } catch (NumberFormatException e) {
+ continue;
+ }
+ value.put(month, val);
+ }
+ budgetEachMonth.put(key, value);
+ }
+ reader.close();
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ }
+ return budgetEachMonth;
+ }
+
+}
diff --git a/src/main/java/seedu/brokeMan/save/SaveExpense.java b/src/main/java/seedu/brokeMan/save/SaveExpense.java
new file mode 100644
index 0000000000..f06e3ded73
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/save/SaveExpense.java
@@ -0,0 +1,83 @@
+package seedu.brokeMan.save;
+
+import seedu.brokeMan.entry.Entry;
+import seedu.brokeMan.entry.expense.Expense;
+import seedu.brokeMan.entry.expense.ExpenseList;
+import seedu.brokeMan.exception.BrokeManException;
+import seedu.brokeMan.parser.Parser;
+import seedu.brokeMan.parser.StringToCategory;
+import seedu.brokeMan.parser.StringToTime;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.LinkedList;
+
+public class SaveExpense {
+ /*
+ * saves all expenses under any change
+ */
+ public static void writeFile(LinkedList expenses) {
+ try {
+ FileWriter myWriter = new FileWriter("./data/ExpenseData.txt");
+ myWriter.flush();
+ String message;
+ for (Entry expense : expenses) {
+ message = "a/ " + expense.getAmount() +
+ " d/ " + expense.getInfo() +
+ " t/ " + expense.getTime().replaceAll("[T:-]", " ") +
+ " c/ " + expense.getCategory() +
+ "\n";
+ myWriter.write(message);
+ }
+ myWriter.close();
+ } catch (IOException foe) {
+ try {
+ Files.createDirectories(Path.of("./data"));
+ File myObj = new File("./data/ExpenseData.txt");
+ boolean newFile = myObj.createNewFile();
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ }
+ }
+ }
+
+ public static void readExpenseFile() {
+ try {
+ String filePath = "./data/ExpenseData.txt";
+ ArrayList expenseEntries;
+ expenseEntries = (ArrayList) Files.readAllLines(Paths.get(filePath),
+ StandardCharsets.UTF_8);
+ for (String expenseEntry : expenseEntries) {
+ String[] strExpense;
+ try {
+ strExpense = Parser.checkAddCommandException(expenseEntry);
+
+ Expense expense = new Expense(Double.parseDouble(strExpense[0]),
+ strExpense[1],
+ StringToTime.convertStringToTime(strExpense[2]),
+ StringToCategory.convertStringToCategory(strExpense[3]));
+ ExpenseList.expenseList.add(expense);
+ } catch (BrokeManException bme) {
+ continue;
+ }
+ }
+ } catch (IOException ioe) {
+ try {
+ Files.createDirectories(Path.of("./data"));
+ File myObj = new File("./data/ExpenseData.txt");
+ boolean newFile = myObj.createNewFile();
+ } catch (IOException fcIoe) {
+ ioe.printStackTrace();
+ }
+ }
+ }
+
+
+}
+
diff --git a/src/main/java/seedu/brokeMan/save/SaveIncome.java b/src/main/java/seedu/brokeMan/save/SaveIncome.java
new file mode 100644
index 0000000000..fa050a4468
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/save/SaveIncome.java
@@ -0,0 +1,82 @@
+package seedu.brokeMan.save;
+
+import seedu.brokeMan.entry.Entry;
+import seedu.brokeMan.entry.income.Income;
+import seedu.brokeMan.entry.income.IncomeList;
+import seedu.brokeMan.exception.BrokeManException;
+import seedu.brokeMan.parser.Parser;
+import seedu.brokeMan.parser.StringToCategory;
+import seedu.brokeMan.parser.StringToTime;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.LinkedList;
+
+
+public class SaveIncome {
+ /*
+ * saves all incomes under any change
+ */
+ public static void writeFile(LinkedList incomes) {
+ try {
+ FileWriter myWriter = new FileWriter("./data/IncomeData.txt");
+ myWriter.flush();
+ String message = "";
+ for (Entry incomeLog : incomes) {
+ message = "a/ " + incomeLog.getAmount() +
+ " d/ " + incomeLog.getInfo() +
+ " t/ " + incomeLog.getTime().replaceAll("[T:-]", " ") +
+ " c/ " + incomeLog.getCategory() +
+ "\n";
+ myWriter.write(message);
+ }
+ myWriter.close();
+ } catch (IOException FileNotFoundException) {
+ try {
+ Files.createDirectories(Path.of("./data"));
+ File myObj = new File("./data/IncomeData.txt");
+ boolean newFile = myObj.createNewFile();
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ }
+ }
+ }
+
+ public static void readIncomeFile() {
+ try {
+ String filePath = "./data/IncomeData.txt";
+ ArrayList incomeEntries;
+ incomeEntries = (ArrayList) Files.readAllLines(Paths.get(filePath),
+ StandardCharsets.UTF_8);
+ for (String incomeEntry : incomeEntries) {
+ String[] strIncome;
+ try {
+ strIncome = Parser.checkAddCommandException(incomeEntry);
+
+ Income income = new Income(Double.parseDouble(strIncome[0]),
+ strIncome[1],
+ StringToTime.convertStringToTime(strIncome[2]),
+ StringToCategory.convertStringToCategory(strIncome[3]));
+ IncomeList.incomeList.add(income);
+ } catch (BrokeManException bme) {
+ continue;
+ }
+ }
+ } catch (IOException ioe) {
+ try {
+ Files.createDirectories(Path.of("./data"));
+ File myObj = new File("./data/IncomeData.txt");
+ boolean newFile = myObj.createNewFile();
+ } catch (IOException fcIoe) {
+ ioe.printStackTrace();
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/seedu/brokeMan/ui/Ui.java b/src/main/java/seedu/brokeMan/ui/Ui.java
new file mode 100644
index 0000000000..105238d030
--- /dev/null
+++ b/src/main/java/seedu/brokeMan/ui/Ui.java
@@ -0,0 +1,66 @@
+package seedu.brokeMan.ui;
+
+import java.io.PrintStream;
+import java.util.NoSuchElementException;
+import java.util.Scanner;
+
+import static seedu.brokeMan.common.Messages.MESSAGE_GOODBYE;
+import static seedu.brokeMan.common.Messages.MESSAGE_LOGO;
+import static seedu.brokeMan.common.Messages.MESSAGE_WELCOME;
+
+/*
+Some parts of the code are copied and adapted from TextUI.java of addressbook-level2
+with the link to the original code at:
+https://github.com/se-edu/addressbook-level2/blob/master/src/seedu/addressbook/ui/TextUi.java
+ */
+public class Ui {
+ private static final Scanner in = new Scanner(System.in);
+ private static final PrintStream out = System.out;
+ private static final String LINE_DIVIDER =
+ "-----------------------------------------------------------------------";
+ private static final String LINE_PREFIX = "| ";
+
+
+ /**
+ * Gets user command from the terminal
+ * @return command entered by the user
+ */
+ public static String getUserCommand() throws NoSuchElementException {
+ showToUser("");
+ out.print(LINE_PREFIX + "Enter command: ");
+ String userInput;
+ userInput = in.nextLine().trim();
+ return userInput;
+ }
+
+ public static void showWelcomeMessages() {
+ showToUserWithLineBreak(LINE_DIVIDER, LINE_DIVIDER, "", MESSAGE_LOGO,
+ MESSAGE_WELCOME, "", LINE_DIVIDER);
+ }
+
+ public static void showGoodByeMessages() {
+ showToUserWithLineBreak(LINE_DIVIDER, "", MESSAGE_GOODBYE,
+ "", LINE_DIVIDER);
+ }
+
+ public static void showToUserWithLineBreak(String... messages) {
+ for (String message : messages) {
+ if (message.equals("")) {
+ out.println(LINE_PREFIX.trim() + message);
+ } else {
+ out.println(LINE_PREFIX + message);
+ }
+ }
+ out.println(LINE_PREFIX + LINE_DIVIDER);
+ }
+
+ public static void showToUser(String... messages) {
+ for (String message : messages) {
+ if (message.equals("")) {
+ out.println(LINE_PREFIX.trim() + message);
+ } else {
+ out.println(LINE_PREFIX + message);
+ }
+ }
+ }
+}
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/test/java/seedu/duke/DukeTest.java b/src/test/java/seedu/brokeMan/BrokeManTest.java
similarity index 79%
rename from src/test/java/seedu/duke/DukeTest.java
rename to src/test/java/seedu/brokeMan/BrokeManTest.java
index 2dda5fd651..2dff762973 100644
--- a/src/test/java/seedu/duke/DukeTest.java
+++ b/src/test/java/seedu/brokeMan/BrokeManTest.java
@@ -1,10 +1,10 @@
-package seedu.duke;
-
-import static org.junit.jupiter.api.Assertions.assertTrue;
+package seedu.brokeMan;
import org.junit.jupiter.api.Test;
-class DukeTest {
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class BrokeManTest {
@Test
public void sampleTest() {
assertTrue(true);
diff --git a/src/test/java/seedu/brokeMan/entry/EntryListTest.java b/src/test/java/seedu/brokeMan/entry/EntryListTest.java
new file mode 100644
index 0000000000..d8cea975b3
--- /dev/null
+++ b/src/test/java/seedu/brokeMan/entry/EntryListTest.java
@@ -0,0 +1,100 @@
+package seedu.brokeMan.entry;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import seedu.brokeMan.entry.expense.Expense;
+import seedu.brokeMan.entry.income.Income;
+
+import java.util.Arrays;
+import java.util.LinkedList;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static seedu.brokeMan.entry.EntryList.addEntry;
+import static seedu.brokeMan.entry.EntryList.deleteEntry;
+import static seedu.brokeMan.entry.EntryList.getEntryListSum;
+import static seedu.brokeMan.parser.StringToTime.convertStringToTime;
+import static seedu.brokeMan.parser.StringToCategory.convertStringToCategory;
+
+
+public class EntryListTest {
+
+ Entry entry1;
+ Entry entry2;
+ Entry entry3;
+ Entry entry4;
+ Entry entry5;
+ Entry entry6;
+ LinkedList expenseList;
+ LinkedList incomeList;
+
+ @BeforeEach
+ public void setUp() throws Exception {
+ entry1 = new Expense(4, "lunch", convertStringToTime("2022 09 08 12 14"),
+ convertStringToCategory("FOOD"));
+ entry2 = new Expense(6, "dinner", convertStringToTime("2023 12 12 20 01"),
+ convertStringToCategory("FOOD"));
+ entry3 = new Expense(5, "book", convertStringToTime("2023 09 08 20 14"),
+ convertStringToCategory("ENTERTAINMENT"));
+ expenseList = new LinkedList(Arrays.asList(entry1, entry2));
+ entry4 = new Income(5, "Stock", convertStringToTime("2023 03 29 10 11"),
+ convertStringToCategory("INVESTMENT"));
+ entry5 = new Income(10, "TA", convertStringToTime("2023 03 10 12 15"),
+ convertStringToCategory("SALARY"));
+ entry5 = new Income(20, "Allowance", convertStringToTime("2023 03 31 20 50"),
+ convertStringToCategory("OTHERS"));
+ incomeList = new LinkedList(Arrays.asList(entry4, entry5));
+ }
+
+ @Test
+ public void addEntryExpense_validExpense_success() {
+ addEntry(entry3, expenseList);
+ assertEquals(expenseList, new LinkedList<>(Arrays.asList(entry1, entry2, entry3)));
+ }
+ @Test
+ public void deleteEntryExpense_inBoundIndex_success() {
+ deleteEntry(2, expenseList);
+ assertEquals(expenseList, new LinkedList<>(Arrays.asList(entry1)));
+ }
+
+ @Test
+ public void deleteEntryExpense_outBoundIndex_exceptionThrown() {
+ try {
+ deleteEntry(3, expenseList);
+ } catch (IndexOutOfBoundsException | IllegalArgumentException e) {
+ assertThrows(IndexOutOfBoundsException.class, () -> deleteEntry(3,expenseList));
+ }
+
+ }
+
+ @Test
+ public void getTotalAmountExpense_inputList_success() {
+ assertEquals(10, getEntryListSum(expenseList));
+ }
+
+ @Test
+ public void addEntryIncome_validIncome_success() {
+ addEntry(entry6, incomeList);
+ assertEquals(incomeList, new LinkedList<>(Arrays.asList(entry4, entry5, entry6)));
+ }
+
+ @Test
+ public void deleteEntryIncome_inBoundIndex_success() {
+ deleteEntry(1, incomeList);
+ assertEquals(incomeList, new LinkedList<>(Arrays.asList(entry5)));
+ }
+
+ @Test
+ public void deleteEntryIncome_outBoundIndex_exceptionThrown() {
+ try {
+ deleteEntry(0, expenseList);
+ } catch (IndexOutOfBoundsException e) {
+ assertThrows(IndexOutOfBoundsException.class, () -> deleteEntry(4, incomeList));
+ }
+ }
+
+ @Test
+ public void getTotalAmountIncome_inputList_success() {
+ assertEquals(25, getEntryListSum(incomeList));
+ }
+}
diff --git a/src/test/java/seedu/brokeMan/entry/EntryTest.java b/src/test/java/seedu/brokeMan/entry/EntryTest.java
new file mode 100644
index 0000000000..ad05489960
--- /dev/null
+++ b/src/test/java/seedu/brokeMan/entry/EntryTest.java
@@ -0,0 +1,26 @@
+package seedu.brokeMan.entry;
+
+import org.junit.jupiter.api.Test;
+import seedu.brokeMan.entry.expense.Expense;
+
+import java.time.LocalDateTime;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class EntryTest {
+ @Test
+ public void testGetters() {
+ Entry entry = new Expense(10, "test", LocalDateTime.now(), Category.FOOD);
+ assertTrue(entry instanceof Entry);
+ entry.getTime();
+ entry.getInfo();
+ }
+
+ @Test
+ public void testComparator() {
+ Entry entry1 = new Expense(10, "test", LocalDateTime.now(), Category.FOOD);
+ Entry entry2 = new Expense(11, "test", LocalDateTime.of(2023, 10,
+ 10, 10, 10), Category.FOOD);
+ new EntryDateComparator().compare(entry1, entry2);
+ }
+}
diff --git a/src/test/java/seedu/brokeMan/entry/ExpenseListTest.java b/src/test/java/seedu/brokeMan/entry/ExpenseListTest.java
new file mode 100644
index 0000000000..c7e2b66a16
--- /dev/null
+++ b/src/test/java/seedu/brokeMan/entry/ExpenseListTest.java
@@ -0,0 +1,44 @@
+package seedu.brokeMan.entry;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import seedu.brokeMan.entry.expense.Expense;
+import seedu.brokeMan.entry.expense.ExpenseList;
+import java.time.LocalDateTime;
+import java.time.Month;
+import java.util.LinkedList;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class ExpenseListTest {
+ private static Expense Expense1;
+ private static Expense Expense2;
+
+ @BeforeAll
+ public static void setUp() {
+ Expense1 = new Expense(2000.0, "Food", LocalDateTime.of(2023, Month.APRIL,
+ 1, 10, 0), Category.FOOD);
+ Expense2 = new Expense(500.0, "Book", LocalDateTime.of(2023, Month.APRIL,
+ 15, 15, 30), Category.ENTERTAINMENT);
+ ExpenseList.addExpense(Expense1);
+ ExpenseList.addExpense(Expense2);
+ }
+
+ @Test
+ public void testAddExpense_validExpense_success() {
+ Expense Expense3 = new Expense(100.0, "Drink", LocalDateTime.of(2023, Month.APRIL,
+ 25, 12, 0), Category.ENTERTAINMENT);
+ ExpenseList.addExpense(Expense3);
+ LinkedList expectedExpenseList = new LinkedList<>();
+ expectedExpenseList.add(Expense1);
+ expectedExpenseList.add(Expense2);
+ expectedExpenseList.add(Expense3);
+ assertEquals(expectedExpenseList, ExpenseList.expenseList);
+ }
+
+ @Test
+ public void testFindExpenseByCategory_validCategory_success() {
+ ExpenseList.findExpenseByCategory(Category.FOOD);
+ }
+
+}
+
diff --git a/src/test/java/seedu/brokeMan/entry/IncomeListTest.java b/src/test/java/seedu/brokeMan/entry/IncomeListTest.java
new file mode 100644
index 0000000000..5a0affd668
--- /dev/null
+++ b/src/test/java/seedu/brokeMan/entry/IncomeListTest.java
@@ -0,0 +1,51 @@
+package seedu.brokeMan.entry;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import seedu.brokeMan.entry.income.Income;
+import seedu.brokeMan.entry.income.IncomeList;
+import java.time.LocalDateTime;
+import java.time.Month;
+import java.util.LinkedList;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class IncomeListTest {
+ private static Income income1;
+ private static Income income2;
+
+ @BeforeAll
+ public static void setUp() {
+ income1 = new Income(2000.0, "Salary", LocalDateTime.of(2023, Month.APRIL,
+ 1, 10, 0), Category.SALARY);
+ income2 = new Income(500.0, "Bonus", LocalDateTime.of(2023, Month.APRIL,
+ 15, 15, 30), Category.SALARY);
+ IncomeList.addIncome(income1);
+ IncomeList.addIncome(income2);
+ }
+
+ @Test
+ public void testAddIncome_validIncome_success() {
+ Income income3 = new Income(100.0, "Allowance", LocalDateTime.of(2023, Month.APRIL,
+ 25, 12, 0), Category.OTHERS);
+ IncomeList.addIncome(income3);
+ LinkedList expectedIncomeList = new LinkedList<>();
+ expectedIncomeList.add(income1);
+ expectedIncomeList.add(income2);
+ expectedIncomeList.add(income3);
+ assertEquals(expectedIncomeList, IncomeList.incomeList);
+ }
+
+ @Test
+ public void testDeleteIncome_validIncome_success() {
+ IncomeList.deleteIncome(1);
+ IncomeList.deleteIncome(1);
+ IncomeList.deleteIncome(1);
+ LinkedList expectedIncomeList = new LinkedList<>();
+ assertEquals(expectedIncomeList, IncomeList.incomeList);
+ }
+
+ @Test
+ public void testFindIncomeByCategory_validCategory_success() {
+ IncomeList.findIncomeByCategory(Category.SALARY);
+ }
+}
diff --git a/src/test/java/seedu/brokeMan/parser/ParserTest.java b/src/test/java/seedu/brokeMan/parser/ParserTest.java
new file mode 100644
index 0000000000..4927242a54
--- /dev/null
+++ b/src/test/java/seedu/brokeMan/parser/ParserTest.java
@@ -0,0 +1,607 @@
+package seedu.brokeMan.parser;
+
+import org.junit.jupiter.api.Test;
+import seedu.brokeMan.command.AddExpenseCommand;
+import seedu.brokeMan.command.Command;
+import seedu.brokeMan.command.DeleteExpenseCommand;
+import seedu.brokeMan.command.DeleteIncomeCommand;
+import seedu.brokeMan.command.EditExpenseCommand;
+import seedu.brokeMan.command.EditIncomeCommand;
+import seedu.brokeMan.command.ExitCommand;
+import seedu.brokeMan.command.HelpCommand;
+import seedu.brokeMan.command.InvalidCommand;
+import seedu.brokeMan.command.ListExpenseCommand;
+import seedu.brokeMan.command.ListIncomeCommand;
+import seedu.brokeMan.command.SetBudgetCommand;
+import seedu.brokeMan.command.SortExpenseByAmountCommand;
+import seedu.brokeMan.command.SortExpenseByDateCommand;
+import seedu.brokeMan.command.SortIncomeByAmountCommand;
+import seedu.brokeMan.command.SortIncomeByDateCommand;
+import seedu.brokeMan.command.ViewBudgetCommand;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class ParserTest {
+
+ @Test
+ void testViewBudget_currentMonthWithNoBudgetSet_success() {
+ final String userInput1 = "viewBudget";
+ Command command = Parser.parseCommand(userInput1);
+ assertTrue(command instanceof InvalidCommand);
+ command.execute();
+ }
+ @Test
+ void parseCommand_validViewBudget_success() {
+ final String userFirstInput = "setBudget 500";
+ Command firstCommand = Parser.parseCommand(userFirstInput);
+ firstCommand.execute();
+
+ final String userSecondInput = "viewBudget";
+ Command secondCommand = Parser.parseCommand(userSecondInput);
+ secondCommand.execute();
+
+ assertTrue(secondCommand instanceof ViewBudgetCommand);
+ }
+
+ @Test
+ void testViewBudget_validDateWithNoBudgetSet_success() {
+ final String userInput1 = "viewBudget t/ 2002/12";
+ Command command = Parser.parseCommand(userInput1);
+ assertTrue(command instanceof ViewBudgetCommand);
+ command.execute();
+ }
+
+ @Test
+ void testViewBudget_validDateWithOverspendBudget_success() {
+ final String userInput1 = "setBudget 1 t/ 2000/12";
+ Command firstCommand = Parser.parseCommand(userInput1);
+ firstCommand.execute();
+
+ final String userInput2 = "addExpense a/ 100 d/ eat t/ 2000 12 10 10 10 c/ FOOD";
+ Command secondCommand = Parser.parseCommand(userInput2);
+ secondCommand.execute();
+
+ final String userInput3 = "viewBudget t/ 2000/12";
+ Command thirdCommand = Parser.parseCommand(userInput3);
+ thirdCommand.execute();
+
+ assertTrue(firstCommand instanceof SetBudgetCommand);
+ assertTrue(secondCommand instanceof AddExpenseCommand);
+ assertTrue(thirdCommand instanceof ViewBudgetCommand);
+
+ final String cleanUp = "deleteExpense 1";
+ Command cleanUpCommand = Parser.parseCommand(cleanUp);
+ cleanUpCommand.execute();
+ }
+
+ @Test
+ void parseCommand_validViewBudgetWithDate_success() {
+ final String userFirstInput = "setBudget 600 t/ 2023/02";
+ Command firstCommand = Parser.parseCommand(userFirstInput);
+ firstCommand.execute();
+
+ final String userSecondInput = "viewBudget t/ 2023/02";
+ Command secondCommand = Parser.parseCommand(userSecondInput);
+ secondCommand.execute();
+
+ assertTrue(secondCommand instanceof ViewBudgetCommand);
+ }
+
+ @Test
+ void testSetBudget_invalidBudgetAmount_returnInvalidCommand() {
+ final String userFullInput = "setBudget five";
+ Command actualCommand = Parser.parseCommand(userFullInput);
+ actualCommand.execute();
+
+ assertTrue(actualCommand instanceof InvalidCommand);
+ }
+
+ @Test
+ void randomCommandShouldReturnHelpCommand() {
+ final String userFullInput = "random inputs";
+ Command actualCommand = Parser.parseCommand(userFullInput);
+ actualCommand.execute();
+ }
+
+ @Test
+ void parseCommand_invalidDateForViewBudget_returnInvalidCommand() {
+ final String userFullInput = "viewBudget t/ 203/02";
+ Command command = Parser.parseCommand(userFullInput);
+ command.execute();
+
+ assertTrue(command instanceof InvalidCommand);
+ }
+
+ @Test
+ void parseCommand_amountExceedLimit_returnInvalidCommand() {
+ final String userFullInput = "addExpense a/ 10000000000000000 d/ something t/ 2023 01 01 10 01 c/ FOOD";
+ Command command = Parser.parseCommand(userFullInput);
+
+ assertTrue(command instanceof InvalidCommand);
+ }
+
+ @Test
+ void parseCommand_stringBudget_returnInvalidCommand() {
+ final String userFullInput = "setBudget hello";
+ Command actualCommand = Parser.parseCommand(userFullInput);
+ actualCommand.execute();
+
+ assertTrue(actualCommand instanceof InvalidCommand);
+ }
+
+ @Test
+ void parseCommand_duplicatedFlags_returnInvalidCommand() {
+ final String userFullInput = "addIncome a/ a/ d/ t/ c/";
+ Command command = Parser.parseCommand(userFullInput);
+ assertTrue(command instanceof InvalidCommand);
+ }
+
+ @Test
+ void parseCommand_incorrectType_returnInvalidCommand() {
+ final String userFullInput = "editExpense i/ 1 t/ WRONG n/ hello";
+ Command command = Parser.parseCommand(userFullInput);
+ assertTrue(command instanceof InvalidCommand);
+ }
+
+ @Test
+ void parseCommand_negativeAmount_returnInvalidCommand() {
+ final String userFullInput = "addIncome a/ -303 d/ drugs?? t/ 2023 02 02 02 02 c/ OTHERS";
+ Command command = Parser.parseCommand(userFullInput);
+ assertTrue(command instanceof InvalidCommand);
+ }
+
+ @Test
+ void parseCommand_newDescriptionContainFlags_returnInvalidCommand() {
+ final String userFullInput = "editIncome i/ 1 t/ info n/ c/";
+ Command command = Parser.parseCommand(userFullInput);
+ assertTrue(command instanceof InvalidCommand);
+ }
+
+ @Test
+ void parseCommand_wrongFlagOrder_returnInvalidCommand() {
+ final String userFullInput = "addIncome a/ t/ d/ c/";
+ Command command = Parser.parseCommand(userFullInput);
+ assertTrue(command instanceof InvalidCommand);
+ }
+
+ @Test
+ void parseCommand_invalidOptionalTimeFlag_returnInvalidCommand() {
+ final String userFullInput = "viewBudget c/";
+ Command command = Parser.parseCommand(userFullInput);
+ assertTrue(command instanceof InvalidCommand);
+ }
+
+ @Test
+ void parseCommand_invalidTime_returnInvalidCommand() {
+ final String userFullInput = "editIncome i/ 1 t/ time n/ random";
+ Command command = Parser.parseCommand(userFullInput);
+ assertTrue(command instanceof InvalidCommand);
+ }
+
+ @Test
+ void parseCommand_randomInput_returnHelpCommand() {
+ final String userFullInput = "random inputs";
+ Command actualCommand = Parser.parseCommand(userFullInput);
+ actualCommand.execute();
+
+ assertTrue(actualCommand instanceof HelpCommand);
+ }
+
+ @Test
+ void parseCommand_emptyFlagDescription_returnInvalidCommand() {
+ final String userFullInput = "addIncome a/ d/ t/ c/";
+ Command actualCommand = Parser.parseCommand(userFullInput);
+ actualCommand.execute();
+
+ assertTrue(actualCommand instanceof InvalidCommand);
+ }
+
+ @Test
+ void parseCommand_invalidCategory_returnInvalidCommand() {
+ final String userFullInput = "addIncome a/ 200 d/ salary t/ 2023 02 02 02 02 c/ WRONG";
+ Command command = Parser.parseCommand(userFullInput);
+ assertTrue(command instanceof InvalidCommand);
+ }
+
+ @Test
+ void parseCommand_amountAsString_returnInvalidCommand() {
+ final String userFullInput = "addExpense a/ double d/ lunch t/ 2023 02 02 02 02 c/ FOOD";
+ Command command = Parser.parseCommand(userFullInput);
+ assertTrue(command instanceof InvalidCommand);
+ }
+
+ @Test
+ void parseCommand_wrongArgumentsForAddExpense_returnInvalidCommand() {
+ final String userFullInput = "addExpense wrong arguments";
+ Command actualCommand = Parser.parseCommand(userFullInput);
+ actualCommand.execute();
+
+ assertTrue(actualCommand instanceof InvalidCommand);
+ }
+
+ @Test
+ void parseCommand_stringIndexForDeleteExpense_returnInvalidCommand() {
+ final String userFullInput = "deleteExpense wrong";
+ Command actualCommand = Parser.parseCommand(userFullInput);
+ actualCommand.execute();
+
+ assertTrue(actualCommand instanceof InvalidCommand);
+ }
+
+ @Test
+ void parseCommand_emptyDeleteIncomeArguments_returnInvalidCommand() {
+ final String userFullInput = "deleteIncome ";
+ Command actualCommand = Parser.parseCommand(userFullInput);
+ actualCommand.execute();
+
+ assertTrue(actualCommand instanceof InvalidCommand);
+ }
+
+ @Test
+ void parseCommand_invalidEditExpenseCommand_returnInvalidCommand() {
+ final String userFullInput = "editExpense a/ a/ a/";
+ Command actualCommand = Parser.parseCommand(userFullInput);
+ actualCommand.execute();
+
+ final String cleanUp = "deleteExpense 1";
+ Command cleanUpCommand = Parser.parseCommand(cleanUp);
+ cleanUpCommand.execute();
+
+ assertTrue(actualCommand instanceof InvalidCommand);
+ }
+
+ @Test
+ void testAddExpense_invalidAmount_returnInvalidCommand() {
+ final String userFullInput = "addExpense a/ four d/ lunch t/ 2022 09 08 12 14 c/ FOOD";
+ Command actualCommand = Parser.parseCommand(userFullInput);
+ actualCommand.execute();
+
+ assertTrue(actualCommand instanceof InvalidCommand);
+ }
+
+ @Test
+ void testAddExpense_invalidCategory_returnInvalidCommand() {
+ final String userFullInput = "addExpense a/ 4 d/ lunch t/ 2022 09 08 12 14 c/ LUNCH";
+ Command actualCommand = Parser.parseCommand(userFullInput);
+ actualCommand.execute();
+
+ assertTrue(actualCommand instanceof InvalidCommand);
+ }
+
+ @Test
+ void testAddExpense_emptyFlag_returnInvalidCommand() {
+ final String userFullInput = "addExpense a/ 4 d/ lunch t/ 2022 09 08 12 14 c/";
+ Command actualCommand = Parser.parseCommand(userFullInput);
+ actualCommand.execute();
+
+ assertTrue(actualCommand instanceof InvalidCommand);
+ }
+
+ @Test
+ void validAddIncomeCommandShouldReturnAddIncomeCommand() {
+ final String userFullInput = "addIncome a/ 400 d/ stocks t/ 2023 12 11 12 41 c/ INVESTMENT";
+ Command actualCommand = Parser.parseCommand(userFullInput);
+ actualCommand.execute();
+
+ final String cleanUp = "deleteIncome 1";
+ Command cleanUpCommand = Parser.parseCommand(cleanUp);
+ cleanUpCommand.execute();
+ }
+
+ @Test
+ void parseCommand_invalidEditIncomeCommand_returnInvalidCommand() {
+ final String userFullInput = "editIncome wrong arguments";
+ Command actualCommand = Parser.parseCommand(userFullInput);
+ actualCommand.execute();
+ assertTrue(actualCommand instanceof InvalidCommand);
+ }
+
+ @Test
+ void testAddIncome_invalidAmount_returnInvalidCommand() {
+ final String userFullInput = "addIncome a/ four d/ stock earnings t/ 2022 09 08 12 14 c/ STOCK";
+ Command actualCommand = Parser.parseCommand(userFullInput);
+ actualCommand.execute();
+
+ assertTrue(actualCommand instanceof InvalidCommand);
+ }
+
+ @Test
+ void testAddIncome_invalidCategory_returnInvalidCommand() {
+ final String userFullInput = "addIncome a/ 4 d/ allowance t/ 2022 09 08 12 14 c/ ALLOWANCE";
+ Command actualCommand = Parser.parseCommand(userFullInput);
+ actualCommand.execute();
+
+ assertTrue(actualCommand instanceof InvalidCommand);
+ }
+
+ @Test
+ void testAddIncome_emptyFlag_returnInvalidCommand() {
+ final String userFullInput = "addIncome a/ 4 d/ allowance t/ 2022 09 08 12 14 c/";
+ Command actualCommand = Parser.parseCommand(userFullInput);
+ actualCommand.execute();
+
+ assertTrue(actualCommand instanceof InvalidCommand);
+ }
+
+ @Test
+ void testAddIncome_invalidDate_returnInvalidCommand() {
+ final String userFullInput = "addIncome a/ 4 d/ allowance t/ 1999 09 08 12 14 c/ OTHERS";
+ Command actualCommand = Parser.parseCommand(userFullInput);
+ actualCommand.execute();
+
+ assertTrue(actualCommand instanceof InvalidCommand);
+ }
+
+ @Test
+ void parseCommand_validDeleteExpenseCommand_success() {
+ final String userFirstInput = "addExpense a/ 4.0 d/ lunch t/ 2022 07 21 10 41 c/ FOOD";
+ Command addExpenseCommand = Parser.parseCommand(userFirstInput);
+ addExpenseCommand.execute();
+ final String userFullInput = "deleteExpense 1";
+ Command actualCommand = Parser.parseCommand(userFullInput);
+ actualCommand.execute();
+
+ assertTrue(actualCommand instanceof DeleteExpenseCommand);
+ }
+
+ @Test
+ void parseCommand_validDeleteIncomeCommand_success() {
+ final String userFirstInput = "addIncome a/ 4000 d/ salary t/ 2012 04 29 01 40 c/ SALARY";
+ Command addIncomeCommand = Parser.parseCommand(userFirstInput);
+ addIncomeCommand.execute();
+ final String userFullInput = "deleteIncome 1";
+ Command actualCommand = Parser.parseCommand(userFullInput);
+ actualCommand.execute();
+
+ assertTrue(actualCommand instanceof DeleteIncomeCommand);
+ }
+
+ @Test
+ void parseCommand_validEditExpenseCommand_success() {
+ final String userFirstInput = "addExpense a/ 4.0 d/ lunch t/ 2017 08 19 13 29 c/ FOOD";
+ Command addExpenseCommand = Parser.parseCommand(userFirstInput);
+ addExpenseCommand.execute();
+ final String userFullInput = "editExpense i/ 1 t/ info n/ brunch";
+ Command actualCommand = Parser.parseCommand(userFullInput);
+ actualCommand.execute();
+
+ final String cleanUp = "deleteExpense 1";
+ Command cleanUpCommand = Parser.parseCommand(cleanUp);
+ cleanUpCommand.execute();
+
+ assertTrue(actualCommand instanceof EditExpenseCommand);
+ }
+
+ @Test
+ void testEditExpense_editAmount_success() {
+ final String userFirstInput = "addExpense a/ 4.0 d/ lunch t/ 2017 08 19 13 29 c/ FOOD";
+ Command addExpenseCommand = Parser.parseCommand(userFirstInput);
+ addExpenseCommand.execute();
+ final String userFullInput = "editExpense i/ 1 t/ amount n/ 100";
+ Command actualCommand = Parser.parseCommand(userFullInput);
+ actualCommand.execute();
+
+ final String cleanUp = "deleteExpense 1";
+ Command cleanUpCommand = Parser.parseCommand(cleanUp);
+ cleanUpCommand.execute();
+
+ assertTrue(actualCommand instanceof EditExpenseCommand);
+ }
+
+ @Test
+ void testEditExpense_editTime_success() {
+ final String userFirstInput = "addExpense a/ 4.0 d/ lunch t/ 2017 08 19 13 29 c/ FOOD";
+ Command addExpenseCommand = Parser.parseCommand(userFirstInput);
+ addExpenseCommand.execute();
+ final String userFullInput = "editExpense i/ 1 t/ time n/ 2000 10 10 10 10";
+ Command actualCommand = Parser.parseCommand(userFullInput);
+ actualCommand.execute();
+
+ final String cleanUp = "deleteExpense 1";
+ Command cleanUpCommand = Parser.parseCommand(cleanUp);
+ cleanUpCommand.execute();
+
+ assertTrue(actualCommand instanceof EditExpenseCommand);
+ }
+
+ @Test
+ void testEditExpense_editCategory_success() {
+ final String userFirstInput = "addExpense a/ 4.0 d/ lunch t/ 2017 08 19 13 29 c/ FOOD";
+ Command addExpenseCommand = Parser.parseCommand(userFirstInput);
+ addExpenseCommand.execute();
+ final String userFullInput = "editExpense i/ 1 t/ category n/ ENTERTAINMENT";
+ Command actualCommand = Parser.parseCommand(userFullInput);
+ actualCommand.execute();
+
+ final String cleanUp = "deleteExpense 1";
+ Command cleanUpCommand = Parser.parseCommand(cleanUp);
+ cleanUpCommand.execute();
+
+ assertTrue(actualCommand instanceof EditExpenseCommand);
+ }
+
+ @Test
+ void testEditExpense_incorrectType_returnInvalidCommand() {
+ final String userFirstInput = "addExpense a/ 4.0 d/ lunch t/ 2017 08 19 13 29 c/ FOOD";
+ Command addExpenseCommand = Parser.parseCommand(userFirstInput);
+ addExpenseCommand.execute();
+ final String userFullInput = "editExpense i/ 1 t/ date n/ 2000 10 10 10 10";
+ Command actualCommand = Parser.parseCommand(userFullInput);
+ actualCommand.execute();
+
+ final String cleanUp = "deleteExpense 1";
+ Command cleanUpCommand = Parser.parseCommand(cleanUp);
+ cleanUpCommand.execute();
+
+ assertTrue(actualCommand instanceof InvalidCommand);
+ }
+
+ @Test
+ void parseCommand_validEditIncomeCommand_success() {
+ final String userFirstInput = "addIncome a/ 4000 d/ salary t/ 2000 10 19 12 42 c/ SALARY";
+ Command addExpenseCommand = Parser.parseCommand(userFirstInput);
+ addExpenseCommand.execute();
+ final String userFullInput = "editIncome i/ 1 t/ amount n/ 3000";
+ Command actualCommand = Parser.parseCommand(userFullInput);
+ actualCommand.execute();
+
+ final String cleanUp = "deleteExpense 1";
+ Command cleanUpCommand = Parser.parseCommand(cleanUp);
+ cleanUpCommand.execute();
+ assertTrue(actualCommand instanceof EditIncomeCommand);
+ }
+
+ @Test
+ void testEditIncome_editDescription_success() {
+ final String userFirstInput = "addIncome a/ 4.0 d/ bonus t/ 2017 08 19 13 29 c/ SALARY";
+ Command addIncomeCommand = Parser.parseCommand(userFirstInput);
+ addIncomeCommand.execute();
+ final String userFullInput = "editIncome i/ 1 t/ info n/ earning from stock";
+ Command actualCommand = Parser.parseCommand(userFullInput);
+ actualCommand.execute();
+
+ final String cleanUp = "deleteIncome 1";
+ Command cleanUpCommand = Parser.parseCommand(cleanUp);
+ cleanUpCommand.execute();
+
+ assertTrue(actualCommand instanceof EditIncomeCommand);
+ }
+
+ @Test
+ void testEditIncome_editTime_success() {
+ final String userFirstInput = "addIncome a/ 4.0 d/ bonus t/ 2017 08 19 13 29 c/ SALARY";
+ Command addIncomeCommand = Parser.parseCommand(userFirstInput);
+ addIncomeCommand.execute();
+ final String userFullInput = "editIncome i/ 1 t/ time n/ 2000 10 10 10 10";
+ Command actualCommand = Parser.parseCommand(userFullInput);
+ actualCommand.execute();
+
+ final String cleanUp = "deleteIncome 1";
+ Command cleanUpCommand = Parser.parseCommand(cleanUp);
+ cleanUpCommand.execute();
+
+ assertTrue(actualCommand instanceof EditIncomeCommand);
+ }
+
+ @Test
+ void testEditIncome_editCategory_success() {
+ final String userFirstInput = "addIncome a/ 4.0 d/ bonus t/ 2017 08 19 13 29 c/ SALARY";
+ Command addIncomeCommand = Parser.parseCommand(userFirstInput);
+ addIncomeCommand.execute();
+ final String userFullInput = "editIncome i/ 1 t/ category n/ OTHERS";
+ Command actualCommand = Parser.parseCommand(userFullInput);
+ actualCommand.execute();
+
+ final String cleanUp = "deleteIncome 1";
+ Command cleanUpCommand = Parser.parseCommand(cleanUp);
+ cleanUpCommand.execute();
+
+ assertTrue(actualCommand instanceof EditIncomeCommand);
+ }
+
+ @Test
+ void testEditIncome_incorrectType_returnInvalidCommand() {
+ final String userFirstInput = "addIncome a/ 4.0 d/ stock t/ 2017 08 19 13 29 c/ Microsoft";
+ Command addExpenseCommand = Parser.parseCommand(userFirstInput);
+ addExpenseCommand.execute();
+ final String userFullInput = "editIncome i/ 1 t/ date n/ 2000 10 10 10 10";
+ Command actualCommand = Parser.parseCommand(userFullInput);
+ actualCommand.execute();
+
+ final String cleanUp = "deleteIncome 1";
+ Command cleanUpCommand = Parser.parseCommand(cleanUp);
+ cleanUpCommand.execute();
+
+ assertTrue(actualCommand instanceof InvalidCommand);
+ }
+
+ @Test
+ void parseCommand_validListExpense_success() {
+ final String userFullInput = "listExpense";
+ Command actualCommand = Parser.parseCommand(userFullInput);
+ actualCommand.execute();
+
+ assertTrue(actualCommand instanceof ListExpenseCommand);
+ }
+
+ @Test
+ void parseCommand_validListExpenseWithDate_success() {
+ final String userFullInput = "listExpense t/ 2023/04";
+ Command command = Parser.parseCommand(userFullInput);
+ command.execute();
+
+ assertTrue(command instanceof ListExpenseCommand);
+ }
+
+ @Test
+ void parseCommand_validListIncomeWithDate_success() {
+ final String userFullInput = "listIncome t/ 2023/04";
+ Command command = Parser.parseCommand(userFullInput);
+ command.execute();
+
+ assertTrue(command instanceof ListIncomeCommand);
+ }
+
+ @Test
+ void parseCommand_validListIncome_success() {
+ final String userFullInput = "listIncome";
+ Command actualCommand = Parser.parseCommand(userFullInput);
+ actualCommand.execute();
+
+ assertTrue(actualCommand instanceof ListIncomeCommand);
+ }
+
+ @Test
+ void testIncomeList_validDate_success() {
+ final String userFullInput = "listIncome t/ 2022/01";
+ Command command = Parser.parseCommand(userFullInput);
+ command.execute();
+
+ assertTrue(command instanceof ListIncomeCommand);
+ }
+
+ @Test
+ void parseCommand_validSortExpenseByAmountCommand_success() {
+ final String userFullInput = "sortExpenseByAmount";
+ Command actualCommand = Parser.parseCommand(userFullInput);
+ actualCommand.execute();
+
+ assertTrue(actualCommand instanceof SortExpenseByAmountCommand);
+ }
+
+ @Test
+ void parseCommand_validSortIncomeByAmountCommand_success() {
+ final String userFullInput = "sortIncomeByAmount";
+ Command actualCommand = Parser.parseCommand(userFullInput);
+ actualCommand.execute();
+
+ assertTrue(actualCommand instanceof SortIncomeByAmountCommand);
+ }
+
+ @Test
+ void parseCommand_validSortExpenseByDateCommand_success() {
+ final String userFullInput = "sortExpenseByDate";
+ Command actualCommand = Parser.parseCommand(userFullInput);
+ actualCommand.execute();
+
+ assertTrue(actualCommand instanceof SortExpenseByDateCommand);
+ }
+
+ @Test
+ void parseCommand_validSortIncomeByDateCommand_success() {
+ final String userFullInput = "sortIncomeByDate";
+ Command actualCommand = Parser.parseCommand(userFullInput);
+ actualCommand.execute();
+
+ assertTrue(actualCommand instanceof SortIncomeByDateCommand);
+ }
+
+ @Test
+ public void testExit_validExit_success() {
+ final String userInput = "exit";
+ Command actualCommand = Parser.parseCommand(userInput);
+ actualCommand.execute();
+
+ assertTrue(actualCommand instanceof ExitCommand);
+ }
+}
diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT
index 892cb6cae7..bc1d85c26e 100644
--- a/text-ui-test/EXPECTED.TXT
+++ b/text-ui-test/EXPECTED.TXT
@@ -1,9 +1,303 @@
-Hello from
- ____ _
-| _ \ _ _| | _____
-| | | | | | | |/ / _ \
-| |_| | |_| | < __/
-|____/ \__,_|_|\_\___|
-
-What is your name?
-Hello James Gosling
+| -----------------------------------------------------------------------
+| -----------------------------------------------------------------------
+|
+| $$$$$___ ______ _______ $$_____ _______ $$___$$_ _______ _______
+| $$__$$__ ______ _______ $$__$$_ _$$$$__ $$$_$$$_ $$$$$__ _______
+| $$$$$___ $$_$$_ _$$$$__ $$_$$__ $$__$$_ $$$$$$$_ ____$$_ $$$$$__
+| $$___$$_ $$$_$_ $$__$$_ $$$$___ $$$$$$_ $$_$_$$_ _$$$$$_ $$__$$_
+| $$___$$_ $$____ $$__$$_ $$_$$__ $$_____ $$___$$_ $$__$$_ $$__$$_
+| $$$$$$__ $$____ _$$$$__ $$__$$_ _$$$$$_ $$___$$_ _$$$$$_ $$__$$_
+| Welcome to BrokeMan!
+| Your personal budget manager to prevent you to become broke like me...
+|
+| -----------------------------------------------------------------------
+| -----------------------------------------------------------------------
+|
+| Enter command: | You have successfully added [$4.00 spent on lunch - 2022-09-08 @ 12:14 [FOOD]]
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | You have successfully added [$6.00 spent on dinner - 2023-06-12 @ 20:01 [FOOD]]
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | Here are the expenses you have made.
+| 1. $4.00 spent on lunch - 2022-09-08 @ 12:14 [FOOD]
+| 2. $6.00 spent on dinner - 2023-06-12 @ 20:01 [FOOD]
+| Total expenses: $10.0
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | Successfully edited amount.
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | Successfully edited description.
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | Successfully edited time.
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | Successfully edited category.
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | Successfully deleted.
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | Here are the expenses you have made.
+| 1. $6.00 spent on dinner - 2023-06-10 @ 00:10 [OTHERS]
+| Total expenses: $6.0
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | You have successfully added [$400.00 earned on salary - 2023-03-12 @ 15:01 [SALARY]]
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | You have successfully added [$600.00 earned on stocks - 2023-03-15 @ 15:10 [INVESTMENT]]
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | Here are the income you have made.
+| 1. $400.00 earned on salary - 2023-03-12 @ 15:01 [SALARY]
+| 2. $600.00 earned on stocks - 2023-03-15 @ 15:10 [INVESTMENT]
+| Total income: $1000.00
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | Successfully edited amount.
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | Invalid optional time flag format.
+|
+| listExpense: lists expenses made in the current month.
+| listExpense t/ : : lists expenses made in the specified month
+| Optional Parameter: t/
+| Example: listExpense
+| Example: listExpense t/ 2023/03
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | Here are the expenses you have made for 2022/SEPTEMBER.
+| The requested list is empty
+|
+| Total expenses: $0.00
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | Invalid add command format.
+|
+| addIncome: add income to the income list.
+| Parameters: a/ d/ t/ c/
+| Valid categories are: FOOD, SHOPPING, GROCERIES, TRANSPORTATION, ENTERTAINMENT, TRAVEL, SALARY, INVESTMENT, and OTHERS
+| Example: addIncome a/ 3000 d/ salary t/ 2023 03 10 10 10 c/ SALARY
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | Invalid add command format.
+|
+| addIncome: add income to the income list.
+| Parameters: a/ d/ t/ c/
+| Valid categories are: FOOD, SHOPPING, GROCERIES, TRANSPORTATION, ENTERTAINMENT, TRAVEL, SALARY, INVESTMENT, and OTHERS
+| Example: addIncome a/ 3000 d/ salary t/ 2023 03 10 10 10 c/ SALARY
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | Here are the income you have made.
+| 1. $700.00 earned on salary - 2023-03-12 @ 15:01 [SALARY]
+| 2. $600.00 earned on stocks - 2023-03-15 @ 15:10 [INVESTMENT]
+| Total income: $1300.00
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | Invalid optional time flag format.
+|
+| listIncome: lists incomes made in the current month.
+| listIncome t/ : : lists incomes made in the specified month
+| Optional Parameter: t/
+| Example: listIncome
+| Example: listIncome t/ 2023/03
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | Here are the income you have made for 2023/MARCH.
+| 1. $700.00 earned on salary - 2023-03-12 @ 15:01 [SALARY]
+| 2. $600.00 earned on stocks - 2023-03-15 @ 15:10 [INVESTMENT]
+| Total income: $1300.0
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | Type specified is incorrect.
+|
+| editIncome: edits the income from the list.
+| Parameter: i/ t/ n/
+| There are 4 types that can be changed, amount, info, time, category
+| Example: editIncome i/ 1 t/ info n/ stocks
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | Successfully edited description.
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | Successfully edited time.
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | Successfully edited category.
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | Successfully deleted.
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | Here are the income you have made.
+| 1. $700.00 earned on pay - 2023-03-12 @ 15:01 [SALARY]
+| Total income: $700.00
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | You have not set your budget for this month.
+|
+| setBudget: sets your budget for current month.
+| setBudget t/ : sets your budget for specified month
+| Compulsory Parameter:
+| Optional Parameter: t/
+| Example: setBudget 500
+| Example: setBudget 500 t/ 2023/03
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | Amount cannot be less than or equals to 0.
+|
+| setBudget: sets your budget for current month.
+| setBudget t/ : sets your budget for specified month
+| Compulsory Parameter:
+| Optional Parameter: t/
+| Example: setBudget 500
+| Example: setBudget 500 t/ 2023/03
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | You have successfully set $500.00 as your budget for 2023/APRIL.
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | Invalid time information. Please present your time as [YYYY/MM]
+| Do not enter invalid dates, such as entering 14 for MM. Only months 1~12 are accepted.
+| The earliest date available is 2000/01, and the latest date available is 9999/12.
+|
+| setBudget: sets your budget for current month.
+| setBudget t/ : sets your budget for specified month
+| Compulsory Parameter:
+| Optional Parameter: t/
+| Example: setBudget 500
+| Example: setBudget 500 t/ 2023/03
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | The budget you have set is not a double.
+| If you are trying to enter date information, please use the 't/' flag.
+|
+| setBudget: sets your budget for current month.
+| setBudget t/ : sets your budget for specified month
+| Compulsory Parameter:
+| Optional Parameter: t/
+| Example: setBudget 500
+| Example: setBudget 500 t/ 2023/03
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | You have successfully set $2000.00 as your budget for 2023/APRIL.
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | You have set your budget as $2000.00 for 2023/APRIL.
+| The amount of budget left is $2000.00
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | Invalid optional time flag format.
+|
+| viewBudget: view your budget for the current month and how much of it is left remaining.
+| viewBudget t/ : view your budget and how much of was left in the specified month
+| Optional Parameter: t/
+| Example: viewBudget
+| Example: viewBudget t/ 2023/03
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | Invalid time information. Please present your time as [YYYY/MM]
+| Do not enter invalid dates, such as entering 14 for MM. Only months 1~12 are accepted.
+| The earliest date available is 2000/01, and the latest date available is 9999/12.
+|
+| viewBudget: view your budget for the current month and how much of it is left remaining.
+| viewBudget t/ : view your budget and how much of was left in the specified month
+| Optional Parameter: t/
+| Example: viewBudget
+| Example: viewBudget t/ 2023/03
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | Budget information for the given month does not exist!
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | Invalid add command format.
+|
+| addExpense: add expense to the expense list.
+| Parameters: a/ d/ t/ c/
+| Valid categories are: FOOD, SHOPPING, GROCERIES, TRANSPORTATION, ENTERTAINMENT, TRAVEL, SALARY, INVESTMENT, and OTHERS
+| Example: addExpense a/ 4.5 d/ lunch t/ 2023 03 22 20 12 c/ FOOD
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | Invalid add command format.
+|
+| addIncome: add income to the income list.
+| Parameters: a/ d/ t/ c/
+| Valid categories are: FOOD, SHOPPING, GROCERIES, TRANSPORTATION, ENTERTAINMENT, TRAVEL, SALARY, INVESTMENT, and OTHERS
+| Example: addIncome a/ 3000 d/ salary t/ 2023 03 10 10 10 c/ SALARY
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | 1. $6.00 spent on dinner - 2023-06-10 @ 00:10 [OTHERS]
+| Total expenses: $6.0
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | 1. $6.00 spent on dinner - 2023-06-10 @ 00:10 [OTHERS]
+| Total expenses: $6.0
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | 1. $700.00 earned on pay - 2023-03-12 @ 15:01 [SALARY]
+| Total income: $700.00
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | 1. $700.00 earned on pay - 2023-03-12 @ 15:01 [SALARY]
+| Total income: $700.00
+|
+| -----------------------------------------------------------------------
+|
+| Enter command: | Exiting program...
+|
+| -----------------------------------------------------------------------
+| -----------------------------------------------------------------------
+|
+| ___ _ ___ _ _
+| / __| ___ ___ __| | o O O | _ ) | || | ___
+| | (_ | / _ \ / _ \ / _` | o | _ \ \_, | / -_)
+| \___| \___/ \___/ \__,_| TS__[O] |___/ _|__/ \___|
+| _|"""""|_|"""""|_|"""""|_|"""""| {======|_|"""""|_| """"|_|"""""|
+| "`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'./o--000'"`-0-0-'"`-0-0-'"`-0-0-'
+|
+| -----------------------------------------------------------------------
+| -----------------------------------------------------------------------
diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt
index f6ec2e9f95..744a052e99 100644
--- a/text-ui-test/input.txt
+++ b/text-ui-test/input.txt
@@ -1 +1,43 @@
-James Gosling
\ No newline at end of file
+addExpense a/ 4.00 d/ lunch t/ 2022 09 08 12 14 c/ FOOD
+addExpense a/ 6.00 d/ dinner t/ 2023 06 12 20 01 c/ FOOD
+listExpense
+editExpense i/ 1 t/ amount n/ 7
+editExpense i/ 1 t/ info n/ breakfast
+editExpense i/ 2 t/ time n/ 2023 06 10 00 10
+editExpense i/ 2 t/ category n/ OTHERS
+deleteExpense 1
+listExpense
+addIncome a/ 400 d/ salary t/ 2023 03 12 15 01 c/ SALARY
+addIncome a/ 600 d/ stocks t/ 2023 03 15 15 10 c/ INVESTMENT
+listIncome
+editIncome i/ 1 t/ amount n/ 700
+listExpense t/
+listExpense t/ 2022/09
+addIncome a/ 400 d/ salary t/ 2023 03 12 15 01
+addIncome a/ 600 d/ stocks t/ 2023 03 15 15 10
+listIncome
+listIncome t/
+listIncome t/ 2023/03
+editIncome i/ 1 t/ income n/ 700
+editIncome i/ 1 t/ info n/ pay
+editIncome i/ 2 t/ time n/ 2023 06 10 00 10
+editIncome i/ 2 t/ category n/ OTHERS
+deleteIncome 2
+listIncome
+viewBudget
+setBudget -1
+setBudget 500
+setBudget 1000 t/ 2022/13
+setBudget 1000 t/
+setBudget 2000 t/ 2023/04
+viewBudget
+viewBudget t/
+viewBudget t/ 2022/13
+viewBudget t/ 2023/02
+addExpense a/ 10 d/ shopping t/ 2023 08 11 10 19
+addIncome a/ 800 d/ allowance t/ 2023 04 19 19 10
+sortExpenseByAmount
+sortExpenseByDate
+sortIncomeByAmount
+sortIncomeByDate
+exit
\ No newline at end of file