diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml
index fd8c44d086..81e70d052b 100644
--- a/.github/workflows/gradle.yml
+++ b/.github/workflows/gradle.yml
@@ -33,18 +33,3 @@ jobs:
- name: Build and check with Gradle
run: ./gradlew check
- - name: Perform IO redirection test (*NIX)
- if: runner.os == 'Linux'
- working-directory: ${{ github.workspace }}/text-ui-test
- run: ./runtest.sh
-
- - name: Perform IO redirection test (MacOS)
- if: always() && runner.os == 'macOS'
- working-directory: ${{ github.workspace }}/text-ui-test
- run: ./runtest.sh
-
- - name: Perform IO redirection test (Windows)
- if: always() && runner.os == 'Windows'
- working-directory: ${{ github.workspace }}/text-ui-test
- shell: cmd
- run: runtest.bat
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000000..d8594c64c6
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "java.debug.settings.onBuildFailureProceed": true
+}
\ No newline at end of file
diff --git a/META-INF/MANIFEST.MF b/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000..7894211af9
--- /dev/null
+++ b/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Main-Class: seedu.myledger.MyLedger
+
diff --git a/README.md b/README.md
index f82e2494b7..d9e29e6214 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,3 @@
-# Duke project template
-
This is a project template for a greenfield Java project. It's named after the Java mascot _Duke_. Given below are instructions on how to use it.
## Setting up in Intellij
diff --git a/build.gradle b/build.gradle
index d5e548e85f..f5ef89863f 100644
--- a/build.gradle
+++ b/build.gradle
@@ -43,4 +43,5 @@ checkstyle {
run{
standardInput = System.in
+ enableAssertions = true
}
diff --git a/docs/AboutUs.md b/docs/AboutUs.md
index 0f072953ea..398f9ab726 100644
--- a/docs/AboutUs.md
+++ b/docs/AboutUs.md
@@ -2,8 +2,10 @@
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)
+
+![](https://media.licdn.com/dms/image/D5603AQFoLJj5VasfWw/profile-displayphoto-shrink_400_400/0/1665324716061?e=1683158400&v=beta&t=OXy8Oh26Bm-xSxVVFL2wdqlkH9SheVixmyDufg936b4) | Lee Zhi Xuan | [Github](https://github.com/itszhixuan) | [Portfolio](https://github.com/itszhixuan)
+![](image.png) | Leo Zheng Rui, Darren | [Github](https://github.com/dsicol) | [Portfolio](https://github.com/dsicol)
+![](https://via.placeholder.com/100.png?text=Photo) | Leonardo Ong Dingchao | [Github](https://github.com/Chick3nBoy) | [Portfolio](docs/team/leonardoOng.md)
+![](https://via.placeholder.com/100.png?text=Photo) | Tan Tze Loong | [Github](https://github.com/TzeLoong) | [Portfolio](docs/team/TzeLoong.md)
+
+
diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md
index 64e1f0ed2b..9b0a479303 100644
--- a/docs/DeveloperGuide.md
+++ b/docs/DeveloperGuide.md
@@ -1,38 +1,1142 @@
-# Developer Guide
+# MyLedger - Developer Guide
-## Acknowledgements
+
+
+
-{list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well}
+
+* [MyLedger - Developer Guide](#myledger---developer-guide)
+ * [1. Preface](#1-preface)
+ * [2. Acknowledgements](#2-acknowledgements)
+ * [3. Design & implementation](#3-design--implementation)
+ * [3.1. Architecture](#31-architecture)
+ * [Main Components of MyLedger](#main-components-of-myledger)
+ * [3.2. Parser](#32-parser)
+ * [Processing an input](#processing-an-input)
+ * [3.3. Expenditure Categories](#33-expenditure-categories)
+ * [3.3.1 Repeat dates for Accommodation and Tuition Expenditures](#331-repeat-dates-for-accommodation-and-tuition-expenditures)
+ * [3.4. Command Component](#34-command-component)
+ * [3.5. Storage](#35-storage)
+ * [3.5.1 Saving expenditures after each input](#351-saving-expenditures-after-each-input)
+ * [3.5.2 Loading the expenditure list from the save file on application launch](#352-loading-the-expenditure-list-from-the-save-file-on-application-launch)
+ * [3.5.3 Corruption of saved expenditures](#353-corruption-of-saved-expenditures)
+ * [4. Command List](#4-command-list)
+ * [4.1. Add Expenditure Command](#41-add-expenditure-command)
+ * [4.1.1 Add regular and lump sum expenditure valid inputs](#411-add-regular-and-lump-sum-expenditure-valid-inputs)
+ * [4.1.2 Add lend borrow expenditure valid inputs](#412-add-lend-borrow-expenditure-valid-inputs)
+ * [4.2. Edit Command](#42-edit-command)
+ * [4.3. Delete Command](#43-delete-command)
+ * [4.4. Find Command](#44-find-command)
+ * [4.5. Duplicate Command](#45-duplicate-command)
+ * [4.6. Sort Command](#46-sort-command)
+ * [4.7. View Command](#47-view-command)
+ * [4.8. Set Budget Command](#48-set-budget-command)
+ * [4.9. Check Command](#49-check-command)
+ * [4.10. Show Rates Command](#410-show-rates-command)
+ * [4.11. Mark/Unmark Command](#411-markunmark-command)
+ * [Product scope](#product-scope)
+ * [Target user profile](#target-user-profile)
+ * [Value proposition](#value-proposition)
+ * [User Stories](#user-stories)
+ * [Non-Functional Requirements](#non-functional-requirements)
+ * [Instructions for manual testing](#instructions-for-manual-testing)
+ * [Launch and shutdown](#launch-and-shutdown)
+ * [Initial Launch](#initial-launch)
+ * [Adding a record](#adding-a-record)
+ * [Displaying the list of inputs and conversion rates](#displaying-the-list-of-inputs-and-conversion-rates)
+ * [Deleting an expenditure](#deleting-an-expenditure)
+ * [Editing an expenditure](#editing-an-expenditure)
+ * [Duplicate an expenditure](#duplicate-an-expenditure)
+ * [Sorting the list](#sorting-the-list)
+ * [Set budget](#set-budget)
+ * [Check budget](#check-budget)
+ * [Mark/Unmark accommodation or tuition expenditures](#markunmark-accommodation-or-tuition-expenditures)
+ * [Find keyword](#find-keyword)
+
-## Design & implementation
+
-{Describe the design and implementation of the product. Use UML diagrams and short code snippets where applicable.}
+## 1. Preface
+MyLedger is a desktop app for managing finances, designed for university students in the National University of Singapore (NUS), studying locally or on exchange. It is optimized for use via a Command Line Interface (CLI). For students that can type fast, MyLedger can help them record and classify their transactions into categories. Students can expect to get an overview of their transactions at a glance,
+which helps them to monitor their budget and expenses more effciently.
+
+All currency amounts used in MyLedger is based of the **Singapore Dollar (SGD)**.
+
+This developer guide provides a detailed view of the overall structure of MyLedger V2.1 and explains how its components and functions are implemented. Additionally, it outlines the specific parameters that were established before feature development began. The aim is to help developers gain a comprehensive understanding of the application's operation and how to maintain it without difficulty.
+
+## 2. Acknowledgements
+
+The format of this developer guide was adapted from SE-EDU AddressBook Level 3 Developer Guide. The class and sequence diagrams are styled using draw.io
+
+## 3. Design & implementation
+
+### 3.1. Architecture
+
+
+
+ Figure 1: Architecture Diagram for MyLedger
+
+
+The Architecture Diagram shown above is a high-level components within MyLedger. The ```MyLedger``` class contains the main method which
+is responsible for:
+
+1. When MyLedger is launched, it will initialize the ```Storage``` to load the saved expenditures from the textfile and ```Ui``` to print
+ the welcome message.
+2. When corrupted entries are encountered when initializing ```Storage```, the respective entries will be deleted.
+3. When MyLedger is executing, it receives input for the user and sends it to ```Storage``` then ```Command``` which carries out the various
+ commands.
+4. After the command has been carried out ```Command``` sends the result back to ```MyLedger.main()``` which would print the message back to the user.
+
+The other components of MyLedger include:
+
+* ```Ui```: The user interface that prints the welcome and help message.
+* ```Parser```: Parser which process the user's command and calls the specific command.
+* ```Command```: Executes the command input by the user.
+* ```Expenditure```: Constructs a corresponding expenditure category and is added to ```ExpenditureList```.
+* ```ExpenditureList```: An ArrayList containing all records of expenditures.
+* ```Storage```: Uses ```MyLedger_inputs.txt``` to initialize ```ExpenditureList```, updates ```MyLedger_inputs.txt``` whenever ```ExpenditureList```
+ changed.
+
+
+
+### Main Components of MyLedger
+`Parser:` Processes the inputs made by the user and converts into a sensible form for further processing.
+
+`Commands:` Matches the input command with the respective commands created and executes the command that produces a command result.
+
+`Expenditures:` Expenditure information that allows the users to create expenditure records from their respective command classes, and access them from an expenditure list.
+
+`Storage:` Stores, reads and updates the user input into their local storage.
+
+The following section describes the implementation of certain features.
+
+
+
+### 3.2. Parser
+#### Processing an input
+The main parser component `MainInputParser` is called whenever the user inputs a command line that requires action from
+the application. The command word will be read and further processed into further components depending on the type of
+command, such as `ParseAdd`, `ParseDelete`, `ParseIndividualValue` etc.
+
+How the parsing works:
+
+ `MainInputParser`:
+- The `MainInputParser` is called and is expected to return an object with the `Command` class.
+- The respective classes (eg `ParseAdd`, `ParseSort`) will be called.
+- For commands that requires an additional input after the command work (excludes commands like `list`, `help` etc), the
+ `ParseIndividualValue` class will be called to parse all the input fields by the user.
+
+The following shows the UML diagram used for the parser component implemented in MyLedger.
+To reduce complexity of the sequence diagram, only commands `exit`, `lend` and `borrow`
+will be displayed
+
+
+
+
+ Figure 2: UML diagram for the parser component
+
+
+It must be noted that not all the existing parser commands are included in this sequence diagram for parsing as other commands have a similar sequence diagram as the commands `exit` and
+`parseLendBorrow`. The only difference is the condition and the number of times the loop occurs for each separate command.
+
+
+
+### 3.3. Expenditure Categories
+The list of **[methods](https://github.com/AY2223S2-CS2113-T14-3/tp/blob/master/src/main/java/seedu/expenditure/Expenditure.java)** of this component is specified in the super abstract class `Expenditure.java` and its sub-classes in the `expenditure` package. Its sub-classes represent the different expenditure categories. When users create a new expenditure record, one of these different expenditure categories are instantiated. After which, the expenditure is added to the expenditure list.
+
+The list of **[methods](https://github.com/AY2223S2-CS2113-T14-3/tp/blob/master/src/main/java/seedu/expenditure/ExpenditureList.java)** of the expenditure list is specified in the `ExpenditureList.java` class.
+
+The `Expenditure` class the following attributes:
+- Fields: `date`, `amount`, `description`
+
+The `ExpenditureList` class description is as follows:
+- A representation of a list of expenditures. It is an `ArrayList` container
+- The list is instantiated at the start of the program and stores expenditures of type `Expenditure`.
+
+The following table describes each and every expenditure category in detail and their utility towards the user.
+
+| Expenditure Category | Description |
+|---------------|--------------------------------------------------------------------------------------------------------|
+| Regular Expenditure Categories | The regular expenditure categories consists of simple daily one-time expenditures. They are `AcademicExpenditure`, `EntertainmentExpenditure`, `FoodExpenditure`, `OtherExpenditure`, `TransportExpenditure`. |
+| Lump Sum Expenditure Categories | The The `AccommodationExpenditure` and `TuitionExpenditure` are lump sum expenditure categories as they are paid over a period of time. These expenditures can be marked as complete for the year, and will be automatically unmarked by MyLedger annually on the same day to indicate that it is time for payment again. MyLedger will perform annual checks for the expenditures as shown [here]. |
+| Borrow Lend Expenditure Categories | The `BorrowExpenditure` and `LendExpenditure` categories form the record of money being borrowed as loan, or lent out to be kept as a record. |
+
+The following shows the UML diagram used for the Expenditure Categories component implemented in MyLedger.
+
+
+
+
+ Figure 3: UML diagram for the Expenditure Categories component
+
+
+In the diagram, the aforementioned expenditure categories inherit from the `Expenditure` class. The `ExpenditureList` class is a composition of expenditures of `Expenditure` type.
+
+`Expenditure` has a multiplicity of `*` to `ExpenditureList` as an empty expenditure list is instantiated at the beginning of the program, and any number of expenditures can be added to the expenditure list. Thus, it is also observed that the `ExpenditureList` class is an *composition* of `Expenditure`.
+
+
+
+#### 3.3.1 Repeat dates for Accommodation and Tuition Expenditures
+
+It must be noted that the date input field for `AccommodationExpenditure` and `TuitionExpenditure` is the `date` of repeat. This is due to the fact that they are lump sum expenditure types as explained in the section [before](#33-expenditure-categories).
+
+Below shows the sequence diagram of the `repeatDate` checking procedure.
+
+
+
+
+ Figure 4: Sequence diagram for the repeat date logic
+
+
+Upon instantiating the `AccommodationExpenditure` or `TuitionExpenditure`, the user input date is entered similarly via the `d/DATE` field. Subsequently, the input date will be incremented to the subsequent year, where the expenditure is expected to require payment again, **unmarking** itself. This is known as the `repeatDate`.
+
+MyLedger will constantly run `queryLumpSumDates()` in the main logic loop of the prorgam. As such, the instances of `AccommodationExpenditure` and `TuitionExpenditure` in the expenditure list are ran with `checkMark()`, which processes the main logic of the `repeatDate`.
+
+The methods `checkNextRepeatDate()` and `handleNextRepeatDate()` will run continuously as aforementioned. Upon first instantiation, `checkNextRepeatDate()` runs in a loop as shown in figure 4. This loop increments the year of the `date` of repeat and updates it into the `repeatDate` attribute. The loop terminates when the year of repeat is subsequent to the current year. This ensures that the program will unmark the expenditures a full year from now.
+
+Together with `checkNextRepeatDate()`. the `handleNextRepeatDate` method has an optional path that unmarks the expenditure as unpaid when `repeatDate` has been reached, or has been passed. After which, `repeatDate` is incremented to the next year and the optional path will not run again until a full year has passed. Unmarking of the expenditure is irregardless of whether the user has marked it previously.
+
+For example, taking the current date as `2023-04-10`, the user can instantiate the `AccommodationExpenditure` with `accommodation d/2023-04-20 a/5000 p/Semester 2 payment`. In this instance, the user has identified the `date` of repeat to be on 20th April. Thus, this input date once entered will be incremented to `2024-04-20`; the `repeatDate` is now `2024-04-20`, where the next *Semester 2* payment is expected to be due again. Launching MyLedger on 20th April 2024, or after that date will cause the expenditure to be unmarked. It will also set the next `repeatDate` to `2025-04-20`.
+
+It must be noted that to be able to store the `repeatDate` separetely to trigger the aforementioned events, it is saved in the txtfile as part of the `AccommodationExpenditure` or `TuitionExpenditure` information. The `repeatDate` is given a delimiter of `r/` for the txtfile.
+
+
+
+### 3.4. Command Component
+
+The `Command` component is represented by the `command` package. The `command` package contains all the available user commands supported by the application. These commands are utilised by the user to interact with the expenditure types and the expenditure list.
+
+| Command Class | Responsibilities |
+|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|
+| `AcademicExpenditureCommand`
`AccommodationExpenditureCommand`
`BorrowExpenditureCommand`
`EntertainmentExpenditureCommand`
`FoodExpenditureCommand`
`LendExpenditureCommand`
`OtherExpenditureCommand`
`TransportExpenditureCommand`
`TuitionExpenditureCommand` | Contain the operations pertaining to adding a new expenditure into the list of expenditures. |
+| `CheckBudgetCommand` | Contains the operations pertaining to comparing the total expenditure amount with a budget set by the user. The budget is set with the `SetBudgetCommand`. User can also compare budget with respective optional filters such as date and category. |
+| `DeleteCommand` | Contains the operations pertaining to deleting a specific expenditure from the list of expenditures. |
+| `DuplicateCommand` | Contains the operations pertaining to duplicating a specific expenditure from the list of expenditures. |
+| `EditCommand` | Class contains the operations pertaining to editing a expenditure from the list of expenditures. |
+| `ExitCommand` | Class contains the operation that safely closes the application. |
+| `FindCommand` | Class contains the operations pertaining to searching the list of expenditures for expenditures that match the keyword entered by the user. |
+| `HelpCommand` | Class contains the operation pertaining to providing the user a user interface to the instructions on the use of the application. |
+| `InvalidCommand` | Class is instantiated when an unrecognised command is entered by the user. |
+| `ListExpenditureCommand` | Contain the operations regarding list display in user preferred currency. |
+| `SetBudgetCommand` | Class contains the operations in setting an amount of money users would like to budget. |
+| `ShowRatesCommand` | Contains the fixed conversion rates used when toggling between different currencies. |
+| `SortCommand` | Class contains the operations pertaining to sorting the list of expenditures by amount or by date. |
+| `ViewDateExpenditureCommand`
`ViewTypeExpenditureCommand` | Class contains the operation pertaining to viewing the date and type of the expenditure list. |
+
+Below represents the UML class diagram representing all the command classes that instantiates an expenditure record:
+
+
+
+
+ Figure 5: UML diagram for the expenditure command classes
+
+
+Next follows the command classes that interact with pre-existing expenditure records stored in the expenditure list. The table below describes the commands.
+
+A more detailed coverage is explored in [Command List](#4-command-list).
+
+
+
+### 3.5. Storage
+
+#### 3.5.1 Saving expenditures after each input
+
+The class `TxtFileStatus` and `ExpenditureList` are involved in storing the expenditure list.
+After every user input is completed, the `saveExpenditureList` method is called, and the text file will be
+updated with all the current expenditures in the expenditure array list.
+
+
+
+
+ Figure 6: Sequence diagram for the process of saveExpenditureLists
+
+
+The following sequence diagram shows the details of the process for saveExpenditureList.
+
+#### 3.5.2 Loading the expenditure list from the save file on application launch
+
+Likewise when MyLedger launches, it instantiates an expenditure list from `ExpenditureList`, and stores a reference to it.
+
+MyLedger then checks if the text save file exists, else it gets created. All the recorded expenditure stored
+as a string in the text file is then added to the array list in ExpenditureList. This is done by iterating through all the saved and valid strings in the text file, taking the saved expenditures and instantiating them as expenditures into the expenditure list.
+
+Subsequently, all `AccommodationExpenditure` and `TuitionExpenditure` are queried to check if the next repeat date has passed. Should the day current date of application launch be on the repeat date, or if it has passed, MyLedger will ensure that the expenditures are unmarked to serve as a reminder for next payment.
+
+However, should a saved expenditure in the text file be corrupted, it will be isolated and deleted upon launch. Conditions for file corruption are explained in the [here](#353-corruption-of-saved-expenditures).
+
+Below shows the sequence diagram for the reading of the save file upon launch.
+
+
+
+
+ Figure 7: Sequence diagram for the reading feature of TxtFileStatus
+
+
+
+
+#### 3.5.3 Corruption of saved expenditures
+
+Expenditures in the saved file are deemed as corrupted when one of the following conditions are met:
+
+| Condition | Justification |
+|------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| i. Missing delimiters or inputs that are not able to be parsed | This implies that the saved expenditure can no longer be parsed and is removed to prevent affecting inaccurate information to be displayed. |
+| ii. Invalid amounts | As described in the next section [4.1.1](#411-add-regular-and-lump-sum-expenditure-valid-inputs), invalid amount inputs are deemed corrupted as MyLedger does not support them. |
+| iii. Invalid dates for Lend and Borrow Expenditures | As described in the next section [4.1](#41-add-expenditure-command), deadlines cannot occur before the date of expenditure and current date, and hence is abided when reading the save file. |
+| iv. `AccommodationExpenditure` and `TuitionExpenditure` repeat dates differing from first user-initialised dates | The aforementioned expenditure types are designed to repeat on the user specified dates annually. Any difference implies corruption and is removed to prevent inaccurate information to be displayed. |
+
+**Note of iv:**
+- `repeatDate`, as all other dates, is stored in `yyyy-MM-DD` format.
+- Corruption for the year segment is **not** deemed as a corruption, as MyLedger's repeating date feature will automatically set it forward to the correct year it is supposed to repeat on.
+- Corruption for the day and month implies the difference between the first user-initialised date and repeat dates. Thus, **is** deemed as a corruption.
+
+
+
+## 4. Command List
+
+### 4.1. Add Expenditure Command
+
+The `AcademicExpenditureCommand`, `AccommodationExpenditureCommand`, `EntertainmentExpenditureCommand`, `FoodExpenditureCommand`, `OtherExpenditureCommand`, `TransportExpenditureCommand`, `TuitionExpenditureCommand` commands contain the operations to add an expenditure of a fixed category into the list of expenditures. As these expenditure types take in the same fields, the `BorrowExpenditureCommand` and `LendExpenditureCommand` have been isolated from these commands.
+
+This is due to the fact that the 7 formerly stated commands all take in the same fields, and hence can be parsed in a similar fashion to instantiate the **Expenditure Command**, and later the **Expenditure** itself. In other words, the 7 stated commands are instantiated in the same way and will be explained altogether in this section.
+
+To instantiate the command classes, the full commands are the shown in the table below:
+
+| Field | Valid inputs |
+|---------------|--------------------------------------------------------------------------------------------------------|
+| Regular Expenditure Categories | `AcademicExpenditureCommand`: `academic d/ a/ p/`
`EntertainmentExpenditureCommand`: `entertainment d/ a/ p/`
`FoodExpenditureCommand`: `food d/ a/ p/`
`OtherExpenditureCommand`: `other d/ a/ p/`
`TransportExpenditureCommand`: `transport d/ a/ p/
`. |
+| Lump Sum Expenditure Categories | `AccommodationExpenditureCommand`: `accommodation d/ a/ p/`
`TuitionExpenditureCommand`: `tuition d/ a/ p/`. |
+| Lend Borrow Expenditure Categories | `BorrowExpenditureCommand`: `borrow d/ n/ a/ b/ p/`
`LendExpenditureCommand`: `lend d/ n/ a/ b/ p/`. |
+
+Running the `command` classes for the aforementioned expenditure categories are parsed through the `parser` package. As such, all inputs received are checked in the latter package, and once deemed valid instantiates the respective expenditures in the `expenditure` package. The following section describes the valid inputs for each of the fields.
+
+For both **regular expenditure categories** and **lump sum expenditure categories**, the explanation is as follows:
+
+When the user inputs one of the 7 expenditure commands into the application, the `MainInputParser.java` takes in the input and determines the command's operations via switch statements. Next, the `ParseIndividualValue.java` class contains the operation to split the valid input given by the user. This splits the inputs into fields to instantiate the `command` classes belonging to their respective expenditure categories. After splitting, `MainInputParser.java` calls operations from `ParseAdd.java`. `ParseAdd.java` prepares the split inputs for the Expenditure categories in the `command` package as fields, and instantiates one of its seven command classes based on the user's specified expenditure category.
+
+Upon `execute()`, The command classes then instantiates the respective `expenditure` and adds it to the expenditure list. For example the execution of the `AcademicExpenditureCommand` instantiates the `AcademicExpenditure` and adds it to the expenditure list belonging to the `ExpenditureList` class.
+
+
+
+#### 4.1.1 Add regular and lump sum expenditure valid inputs
+
+The fields for the regular and lump sum expenditure categories are as follows:
+
+- `date`: Input as a `String`, parsed in `yyyy-MM-DD` format.
+- `amount`: Input as a `String`, parsed to `double`.
+- `description`: Input as a `String`, no restriction on input.
+
+Below depicts the table explaining the fields and their valid inputs:
+
+| Field | Valid inputs |
+|---------------|--------------------------------------------------------------------------------------------------------|
+| `date` | Parsed in `yyyy-MM-DD` format. Violating the format raises the `DateTimeParseException` and is not allowed. |
+| `amount` | Since this is parsed to a `double`, it raises the following exceptions when:
`InvalidCharacterInAmount`: When special characters such as `.d`, `.e` and `.f` are input
`NumberFormatException` : When numbers are not input
`SmallAmountException`: When amount input is less than the supported $0.01 SGD
`NotPositiveValueException`: When a negative value is input as the amount
`LargeValueException`: When a value larger than $10000000 SGD is input
`WrongPrecisionException`: When more than 2 decimal places is input |
+| `description` | There is no restriction on the description. Users are free to enter any description they like. |
+
+**Note for lump sum expenditures:**
+
+As per section [3.3.1](#331-repeat-dates-for-accommodation-and-tuition-expenditures), the date field for `AccommodationExpenditure` and `TuitionExpenditure` are indicators of the `date` of repeat. Thus, the aforementioned expenditures will be unmarked a full year from the `date` entered.
+
+Below shows the sequence diagram for the `AcademicExpenditure` for the aforementioned logic:
+
+
+
+
+ Figure 8: Sequence Diagram for edit Command
+
+
+This diagram is applicable to all **regular and lump sum** expenditure categories.
+
+
+
+#### 4.1.2 Add lend borrow expenditure valid inputs
+
+Following the valid inputs for the `date`, `amount` and `description`, the `BorrowExpenditureCommand` and `LendExpenditureCommand` has two additional fields:
+
+- `borrowerName` or `lenderName`: Input as a `String`, parsed as a `String`.
+- `deadline`: Input as a `String`, parsed in `yyyy-MM-DD` format.
+
+Below depicts the table explaining the valid inputs for the two aforementioned fields:
+
+| Field | Valid inputs |
+|---------------|--------------------------------------------------------------------------------------------------------|
+| `borrowerName` or `lenderName` | Parsed as a `String`. Since delimiters follow this field in the command interface, `/` are not encouraged to be entered together with the names. |
+| `deadline` | The following exceptions will be raised when:
`DateTimeParseException`: When date provided is not in the correct format
`InvalidDeadlineException`: When date provided is before the `date` added, and before the **current date**. |
+
+Justification for the `InvalidDeadlineException` after current date:
+
+The `InvalidDeadlineException` is thrown when the `deadline` entered is before the current date. This is the Lend and Borrow expenditure categories help keep track of ongoing lent or borrowed amounts. Past records incur a net expenditure of 0 and thus is more meaningful to allow users to enter a `deadline` after the current date to help them keep track of ongoing lend and borrow expenditures.
+
+To instantiate the commands, the full commands for `lend` and `borrow` are the following:
+
+- To create a lend expenditure.
+```
+lend d/2023-04-07 n/Mr Bean a/400 b/2023-07-01 p/Flight ticket
+```
+- To create a borrow expenditure.
+```
+borrow d/2023-04-07 n/Teddy a/400 b/2023-07-01 p/Flight ticket
+```
+
+The sequence diagram for lend and borrow has been previously shown as an example for the `Parser` class.
+
+
+
+### 4.2. Edit Command
+
+The ```EditCommand``` edits an existing expenditure in the record.
+
+It cannot change the expenditure type of a record, only its fields
+
+For editing an expenditure, the full command is ```edit INDEX d/DATE a/AMOUNT p/DESCRIPTION```
+
+For editing a borrow/lend record, the full command is ```edit INDEX d/DATE n/(LEND/BORROW)_NAME
+a/AMOUNT b/DEADLINE p/DESCRIPTION```
+
+The sequence diagram below shows the interactions of a successful execution of the EditCommand
+
+
+
+
+ Figure 9: Sequence Diagram for edit Command
+
+
+
+
+### 4.3. Delete Command
+
+The `delete` command deletes an existing expenditure in the record.
+
+The format for delete is ```delete INDEX```
+
+Similar to the process for `edit`, `MainInputParser` has recognized the command, `ParseDelete` is called, which in turn calls `DeleteCommand` that calls `deleteExpenditure` in `ExpenditureList` and returns the string containing the string to print for delete.
+
+### 4.4. Find Command
+
+The ```FindCommand``` provides the search functionality for finding a specific or few transactions from the list of transactions by their respective descriptions.
+
+The command is able to search for all characters matching the keyword in the expenditure descriptions and the keyword is case-sensitive.
+
+Similar to the process for `edit`, `MainInputParser` has recognized the command, `ParseFind` is called, which in turn calls `FindCommand` which loops through the Expenditure
+list and compares the keyword provided with the the descriptions.
+
+
+
+### 4.5. Duplicate Command
+
+The `duplicate` command duplicates an existing expenditure in the record, and appends it to the list.
+
+The format for duplicate is ```duplicate INDEX```
+
+Similar to the `delete` command, after `MainInputParser` has recognized the command, `ParseDuplicate` is called, which in turn calls `DuplicateCommand` which calls `duplicateExpenditure` in `ExpenditureList` and returns the string containing the string to print for duplicate.
+
+### 4.6. Sort Command
+
+The `sort` command sorts the expenditure list by the `sortType` indicated by the user.
+
+Format: `sort `
+
+The `sortType` is expressed in the table below:
+
+| `sortType` | Description |
+|---------------|--------------------------------------------------------------------------------------------------------|
+| `ascend` | The sort type `ascend` sorts the list in ascending amount. |
+| `descend` | The sort type `descend` sorts the list in descending amount. |
+| `earliest` | The sort type `earliest` sorts the list from the earliest date. |
+| `latest` | The sort type `latest` sorts the list from the earliest date. |
+
+The logic of the sort command is as follows:
+
+When the user inputs the command `sort`, the `MainInputParser` identifies it and calls `sortExpenditures` from `ParseSort`. It then checks if the given `sortType` follows the valid inputs as expressed in the table above. Should the input given be none of the aforementioned valid inputs, the `sortExpenditures` method throws an `InvalidSortInputException` and displays the error message to the user.
+
+On a valid `sortType` input, the `SortCommand` is instantiated from the `command` classes. This then sets the `sortType` attribute of this instance to be the valid input entered by the user.
+
+Upon calling of the `execute` command, the `SortCommand` takes the set attribute of `sortType` and runs it through a switch statement. Based on the valid inputs mentioned in the table above, the execution of this method sorts the expenditure list by the given input. This subequently produced the `CommandResult` that displays the new sorted list to the user, and is **permanent** to provide a better user experience for users after sorting the list.
+
+After which, the instance of SortCommand is ready for Java's Garbage Collection.
+
+
+
+### 4.7. View Command
+
+The view command filters and lists the expenditures of a specified date or type.
+At the end of the list, the total amount of the filtered expenditures are tabulated.
+For viewing expenditures of specific date, the command is ```viewdate DATE```.
+For viewing expenditures of specific type, the command is ```viewtype EXPENDITURE_TYPE```.
+
+The sequence diagram below shows the details of the process for viewdate.
+
+
+
+
+ Figure 10: Sequence Diagram of the process for viewdate
+
+
+With the addition of a currency feature, the specificDateString method in ExpenditureList also gets a value converted to the
+specified currency using `getConvertValue()` found in Expenditure and appends it to the string, instead of the `getValue()`
+also in Expenditure used previously.
+
+The process for viewtype is similar as viewdate with an additional step within ViewTypeExpenditureCommand
+that converts the input string into a string recognisable for comparison in the opt block.
+
+
+
+### 4.8. Set Budget Command
+The set budget command allows the user to insert a temporary budget which they can use to compare their expenditures with.
+This provides an insight on their financial health when compared to their current budget for the month, day or even for the type of expenditure.
+
+### 4.9. Check Command
+The check command allows the user to compare their expenditure with their budget.
+The user can either check with the total expenditure using the `check` command, or provide an optional field to filter their comparison.
+
+The sequence diagram for `check` without parameters can be observed as follows.
+
+
+
+ Figure 11: UML diagram of check command
+
+
+The UML diagrams for `check` with dates as the filter (eg. `check y/2023` or `check d/03-04-2023`) are the same but with slightly different method name, thus the above can
+accurately represent the check commands.
+
+
+
+### 4.10. Show Rates Command
+
+`showrates` is a command that prints a list of currencies available in MyLedger with their value tied to SGD.
+
+
+`showrates` is a simple command where `MainInputParser` calls `getRates` in the `CurrencyValue` class that returns a string to be printed for `showrates`.
+
+It must be noted that all currency options provided is based of the NUS Partner Universities as found [here](https://www.nus.edu.sg/gro/global-programmes/student-exchange/partner-universities)
+
+### 4.11. Mark/Unmark Command
+
+The `mark` and `unmark` functions in the system are applicable only to the `AccommodationExpenditure` and `TuitionExpenditure`. These functions serve to indicate that the user has paid for the respective expense and it is considered as paid in the system. This enables accurate tracking of the expenditure amount when check function is called. If an expense is not marked as paid, it will not be considered as a paid expenditure.
+
+For other types of expenses, they will be marked as paid right after they are added by the user, and cannot be modified thereafter.
+
+
## Product scope
### Target user profile
-{Describe the target user profile}
+- University students studying locally or on exchange
+- has a need to monitor their budget and expenses
+- prefer desktop CLI over other available types of expense tracking applications
+- prefers typing to mouse interactions
### Value proposition
-{Describe the value proposition: what problem does it solve?}
+Manage finances more efficiently than a typical mouse/GUI driven app
+
+
## User Stories
-|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|
+| Version | As a ... | I want to ... | So that I can ... |
+|---------|------------------|----------------------------------------------------------------|--------------------------------------------------------------------|
+| v1.0 | first time user | have access to a help page | be familarized with the features available |
+| v1.0 | user | add a expenditure recorded in a day | |
+| v1.0 | user | delete an expenditure record | get rid of expenditure that I no longer plan to use |
+| v1.0 | user | edit an expenditure record | correct previous expenditure records |
+| v1.0 | user | view all current expenses | have a good overview of my spending to date |
+| v1.0 | user | add a record for borrowing money | keep track of how much money I borrowed to someone |
+| v1.0 | user | add a record for lending money | keep track of how much money I lent to someone |
+| v2.0 | user | sort expenditures based on date | better manage my expenditures |
+| v2.0 | user | sort expenditures based on amount | better manage my expenditures |
+| v2.0 | user | add income earned | keep track of my current budget |
+| v2.0 | user | find expenditures using description | better manage my spending |
+| v2.0 | user | duplicate a current expenditure | update repeated purchases easily |
+| v2.0 | user | indicate a specific budget to follow | track my spending and make sure I stay within budget |
+| v2.0 | user | be able to view expenses by day | see which day and why I am overspending |
+| v2.0 | user | view my total expenses by categories | see which categories I am overspending on |
+| v2.0 | user | compare my expenses with my budget | have a clearer insight on the health of my finances |
+| v2.1 | exchange student | view my expenses in my native currency | better understand how much I am spending in Singapore |
+| v2.1 | user | compare my budget with expenses made in a specific time period | have a clearer insight on my spending during the time period |
+| v2.1 | user | compare my budget with expenses made under a category | have a clearer insight on my spending in that expenditure category |
+| v2.1 | user | have my repeated expenses auto-check on its own | reduce the hassle of having to input repeated expenditures |
-## Non-Functional Requirements
-{Give non-functional requirements}
+
-## Glossary
+## Non-Functional Requirements
-* *glossary item* - Definition
+1. Program should run on any mainstream OS that runs Java 11.
## 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}
+The following are instructions for testers to manual test:
+
+### Launch and shutdown
+#### Initial Launch
+- Ensure that Java 11 is installed on your device.
+- Download the JAR file and copy into an empty folder
+- Open the command terminal on your device.
+- Navigate to the folder in command terminal and run the command `java -jar [filename].jar`
+- Alternatively, double-click on the JAR file to run the app.
+
+
+
+#### Adding a record
+1. Adding an expenditure
+
+ Test Case 1:
+ ```
+ academic d/2023-02-02 a/25.10 p/NUS
+ ```
+ Expected :
+ ```
+ Added academic expenditure: [Academic] || Date: 2 Feb 2023 || Value: 25.1
+ || Description: NUS
+ ```
+ An expenditure of type : `academic` will be added if all inputs are added in the correct format.
+ Otherwise, error messages will be printed.
+
+
The output should be displayed on a single line. However, output in DG has been displayed across 2 lines to format the output in PDF form.
+
+
+
+ Test Case 2:
+ ```
+ food d/2023-03-03 a/5.30 p/Fish Soup
+ ```
+ Expected :
+ ```
+ Added food expenditure: [Food] || Date: 3 Mar 2023 || Value: 5.3
+ || Description: Fish Soup
+ ```
+ An expenditure of type `food` will be added
+
+ The output should be displayed on a single line. However, output in DG has been displayed across 2 lines to format the output in PDF form.
+
+
+
+ Test Case 3 (Wrong date-time input):
+ ```
+ transport d/13-03-2023 a/2 p/Bus
+ ```
+ Expected :
+ ```
+ Date error! Please enter a single date in yyyy-mm-dd format!
+ ```
+
+ Test Case 4 (Wrong input format):
+ ```
+ transport d/2023-03-13 a/two dollars p/Bus
+ ```
+ Expected :
+ ```
+ The amount you provided is not in the right format!
+ Please enter a single number value
+ ```
+ The output should be displayed on a single line. However, output in DG has been displayed across 2 lines to format the output in PDF form.
+
+2. Adding a lend/borrow spending
+
+ - Important information: Our application does not support names with slash (/), thus input names should not have them.
+
+ Test Case 1:
+ ```
+ lend d/2023-02-02 n/Bob a/25.10 b/2023-06-02 p/CS2113
+ ```
+ Expected :
+ ```
+ Added lend expenditure: [Lend] || Lent to: Bob || Date: 2 Feb 2023
+ || Value: 25.1 || Description: CS2113 || by: 2 Jun 2023
+ ```
+ Similar to add an expenditure, adding a lend/borrow will add the expenditure to the list.
+ Details of all parameters will be shown to the user.
+
The output should be displayed on a single line. However, output in DG has been displayed across 2 lines to format the output in PDF form.
+
+
+ Test Case 2:
+
+ ```
+ borrow d/2023-02-02 n/Mandy a/25.10 b/2023-09-02 p/payment for notes
+ ```
+ Expected :
+ ```
+ Added borrow expenditure: [Borrow] || Borrowed from: Mandy || Date: 2 Feb 2023
+ || Value: 25.1 || Description: payment for notes || By: 2 Sep 2023
+ ```
+ Similar to previous, but with a different expenditure type : `borrow`.
+
The output should be displayed on a single line. However, output in DG has been displayed across 2 lines to format the output in PDF form.
+
+
Test Case 3 (Return date is earlier than current date):
+ ```
+ borrow d/2023-02-02 n/Marco a/10.10 b/2023-03-03 p/bowling
+ ```
+ Expected :
+ ```
+ Return date must be after today's date! Today's date is 2023-04-07```
+ ```
+ Today's date in the expected output will correspond to the day that the user is using MyLedger.
+ In our example case, the day in which the user was attempting to add the `borrow` command was on 2023-04-07.
+
+
+#### Displaying the list of inputs and conversion rates
+1. Displaying list of conversion rates
+
+ Test Case:
+ ```
+ showrates
+ ```
+ Expected :
+ ```
+ Currency rates per SGD:
+ AUS: 1.11
+ CAD: 1.01
+ CNY: 5.07
+ DKK: 5.15
+ EUR: 0.69
+ GBP: 0.61
+ ILS: 2.7
+ JPY: 99.96
+ KRW: 989.05
+ NOK: 7.78
+ NZD: 1.2
+ SEK: 7.8
+ TWD: 22.98
+ USD: 0.75
+ ```
+ Currency rates used are aimed to provide an estimate on their spending in SGD, and does not provide real-time conversion rates.
+
+2. Displaying the list based on currency preferred.
+
+ - Prerequisite: The currency must be 1 of the 14 currencies supported. User can view the available currencies using
+ `showrates`. Additionally, there must be at least one expenditure in the list to view in different currencies.
+ Test Cases below will assume that the following expenditure has been added prior to calling `list`
+
+ ```
+ food d/2023-02-12 a/8.00 p/Fast Food
+ ```
+
+
+ Test Case 1 (Display in SGD):
+ ```
+ list SGD
+ ```
+ Expected :
+ ```
+ Here is your list of expenditures in SGD:
+ 1. [Food] || Date: 12 Feb 2023 || Value: 8.00 || Description: Fast Food
+ ```
+
+ Test Case 2 (Display in USD):
+ ```
+ list USD
+ ```
+ Expected :
+ ```
+ Here is your list of expenditures in USD:
+ 1. [Food] || Date: 12 Feb 2023 || Value: 6.00 || Description: Fast Food
+ ```
+
+ Test Case 3 (No currency):
+ ```
+ list
+ ```
+ Expected :
+ ```
+ Input command does not have required parameters! Please try again
+ ```
+ The command `list` is missing currency, thus no list will be displayed.
+
+
+#### Deleting an expenditure
+1. Deleting an expenditure from the list of inputs.
+ - Prerequisite: There should be at least one expenditure in the list for `delete` to work. The list can be checked in
+ SGD using the `list SGD` command
+
+ Test Case 1:
+ ```
+ delete 1
+ ```
+ Expected :
+ ```
+ Entry has been deleted
+ Here is your updated list:
+ ```
+
+ Test Case 2:
+ ```
+ delete -1
+ ```
+ Expected :
+ ```
+ Index is out of bounds or negative
+ ```
+
+ Test Case 3:
+ ```
+ delete 1.1
+ ```
+ Expected :
+ ```
+ Index must be an integer and within bounds! Please try again
+ ```
+
+
+#### Editing an expenditure
+1. Editing a current expenditure within the list of inputs.
+ - Prerequisite : Similar to delete, an existing expenditure is required.
+ - Important information: Our application does not support names with slash (/), thus when editing lend/borrow
+ expenditures, input names should not have slashes.
+
+ - Assumption : Test cases provided are for expenditures with the corresponding parameters. Parameters for normal
+ expenditures cannot to edit lend/borrow expenditures
+
+ Test Case 1 (Editing `food` expenditure):
+ ```
+ edit 1 d/2023-02-12 a/8.00 p/Western
+ ```
+ Expected :
+
+ Assuming this test case is for a normal expenditure, all the previous parameters will be replaced with
+ the new input parameters. An edit message will be shown as well.
+
+ ```
+ Edited! Here is the updated list:
+ 1. [Food] || [ ] || Date: 12 Feb 2023 || Value: 8.0 || Description: Western
+ ```
+
+ Test Case 2 (Editing `lend` expenditure):
+ ```
+ edit 2 d/2020-02-02 n/Carl a/22.2 b/2020-03-03 p/fishing
+ ```
+ Expected :
+
+ Assuming this test case is for a lend/borrow expenditure, all the previous parameters will be
+ replaced with the new input parameters. An edit message will be shown as well.
+
+ ```
+ Edited! Here is the updated list:
+ 1. [Food] || Date: 12 Feb 2023 || Value: 8.0 || Description: Western
+ 2. [Lend] || Lent to: Carl || Date: 2 Feb 2020 || Value: 22.2 ||
+ Description: fishing || by: 3 Mar 2020
+ ```
+ The output should be displayed on a single line. However, output in DG has been displayed across 2 lines to format the output in PDF form.
+
+
+
+ Test Case 3 (Editing expenditure with `lend` parameters):
+ ```
+ edit 1 d/2020-02-02 n/Carlos a/22.2 b/2020-03-03 p/fishing
+ ```
+ Expected :
+
+ As the input parameters are different, an invalid message will be returned. Expenditure
+ will not be edited.
+ ```
+ Failed to edit! Please check the format and try again!
+ ```
+
+ Test Case 4:
+ ```
+ edit 1
+ ```
+ Expected :
+ ```
+ Index must be an integer and within bounds! Please try again
+ ```
+
+ Other invalid `edit` commands:
+
+ eg.
+ ```
+ edit -1 d/2020-02-02 n/Carl a/22.2 b/2020-03-03 p/fishing
+ ```
+ Expected : Invalid message similar to previous invalid cases will be provided.
+
+
+#### Duplicate an expenditure
+1. Duplicating an expenditure from the list of inputs.
+ - Prerequisite: There should be at least one expenditure in the list for `duplicate` to work. The list can be checked
+ in SGD using the `list SGD` command
+
+ Test Case 1:
+ ```
+ duplicate 1
+ ```
+ Expected : The duplicate expenditure will be shown to the user, and will be added to the last index in the list.
+
+ Test case 2:
+ ```
+ duplicate 1.2
+ ```
+ Expected :
+ ```
+ Index must be an integer and within bounds! Please try again
+ ```
+
+ Other invalid `duplicate` commands: eg. `duplicate`
+
+ Expected : Similar to previous, an invalid message with the error will be displayed for the user.
+
+
+#### Sorting the list
+ - Prerequisite : A list with more than 2 expenditures are saved, which can be checked with the `list` command
+
+ Test case 1 (Sort amount in ascending order):
+ ```
+ sort ascend
+ ```
+ Expected : The new list will be shown, where the items are sorted by ascending amount with the smallest
+ amount at index 1
+ Test case 2 (Sort amount in descending order):
+ ```
+ sort descend
+ ```
+ Expected : In contrast to previous test case, item will be sorted in descending order with largest amount
+ at index 1
+
+
+ Test case 3 (Sort amount from the earliest date added):
+ ```
+ sort earliest
+ ```
+ Expected : New list with the earliest date at index 1
+
+ Test case 4 (Sort amount from the latest date added):
+ ```
+ sort latest
+ ```
+ Expected : In contrast to previous test case, new list with the latest date at index 1
+
+
+#### Set budget
+1. Setting a temporary budget that the user might be on
+
+ Test case 1:
+ ```
+ set 1.0
+ ```
+ Expected :
+ ```
+ New budget of 1.0 has been set!
+ ```
+
+ Test case 2:
+ ```
+ set -12.2
+ ```
+ Expected :
+ ```
+ Amount entered must be positive! Please try again
+ ```
+
+ Other invalid `set` commands:
+ eg.
+ ```
+ set 3-3
+ ```
+ Expected : Similar to previous, an invalid message with the error will be displayed for the user.
+
+
+#### Check budget
+1. Checking the total amount of spending and the intended budget.
+ - Prerequisite : A budget must be set prior to calling `check` and the budget set cannot be of value 0.
+ For all `check` commands, it compares with expenditures that are unmarked. Marked expenditures will not be added
+ to total expenditure amount.
+
+ Test case 1 (Budget set is more than total expenditures in list):
+ ```
+ check
+ ```
+ Expected : The amount of money away from the set budget will be displayed with other information such as
+ the total spending, budget and borrowed money.
+
+ Test case 2 (Budget set is less than total expenditures in list):
+ ```
+ check
+ ```
+ Expected : Similar to previous test case, amount of money exceeded by and other information will be
+ displayed in the message.
+
+2. Checking the expenditure on a certain day/year with the intended budget
+ - `check` compares the budget with the spending of a certain time period that the user wants to check with
+
+ Test case 1 (Check with year):
+ ```
+ check y/2023
+ ```
+ Expected : Returns the comparison result with the expenditures made in 2023.
+ Test case 2 (Check with day):
+ ```
+ check d/2023-01-12
+ ```
+ Expected : Returns the comparison result with the expenditures made on 12 Jan 2023.
+
+ Test case 3:
+ ```
+ check m/2023-01-12
+ ```
+ Expected :
+ ```
+ Failed to check! Please check the format and try again!
+ ```
+ Error occurs due to wrong format for parameter.
+3. Checking the expenditure classified under a certain expenditure type and comparing with set budget
+ - `check [expenditure type]` compares all the unmarked expenditures classified under that expenditure type with the set
+ budget so that the user can compare spending with budget. This command does not include borrow and lend expenditures.
+ Test case 1:
+ ```
+ check t/transport
+ ```
+ Expected : Returns the comparison result with all unmarked transport expenditures.
+ Test case 2:
+ ```
+ check t/academic
+ ```
+ Expected : Returns the comparison result with all unmarked academic expenditures.
+ Test case 3:
+ ```
+ check academic
+ ```
+ Expected :
+ ```
+ Failed to check! Please check the format and try again!
+ ```
+
+
+#### Mark/Unmark accommodation or tuition expenditures
+ - Prerequisite: An accommodation or tuition expenditure must already exist in the list for the user to mark.
+ The list can be checked in SGD using the `list SGD` command. A user may add an accommodation or tuition expenditure by
+ following the `Adding a record` documentation.
+
+1. Marking an expenditure
+ - Mark indicates that the expenditure has been paid, otherwise the expenditure will be interpreted as unpaid.
+ - Prerequisite: Accommodation expenditure of such is stored at the first index of the list:
+ ```
+ 1. [Accommodation] || [ ] || Date: 3 Feb 2023 || Value: 200.00
+ || Description: NUS
+ ```
+
The output should be displayed on a single line. However, output in DG has been displayed across 2 lines to format the output in PDF form.
+
+ Test case 1:
+ ```
+ mark 1
+ ```
+ Expected :
+ ```
+ Marked your expenditure!
+ [Accommodation] || [X] || Date: 3 Feb 2023 || Value: 200.0 || Description: NUS
+ ```
+
+ Test case 2 (Attempt to mark other expenditures that are not accommodation or tuition):
+ ```
+ mark 2
+ ```
+ Expected :
+ ```
+ No paid field for this expenditure!
+ ```
+
+ Test case 3:
+ ```
+ mark 1
+ ```
+ Expected :
+ ```
+ Sorry! This expenditure is already marked!
+ ```
+
+2. Unmarking an expenditure
+ - Unmark indicates that the previously paid expenditure is now unpaid due to certain circumstances.
+ Test Case 1:
+ ```
+ unmark 1
+ ```
+ Expected :
+ ```
+ Unmarked your expenditure!
+ [Accommodation] || [ ] || Date: 3 Feb 2023 || Value: 200.0
+ || Description: NUS
+ ```
+
The output should be displayed on a single line. However, output in DG has been displayed across 2 lines to format the output in PDF form.
+
+ Test case 2 (Attempt to unmark other expenditures that are not accommodation or tuition):
+ ```
+ unmark 2
+ ```
+ Expected :
+ ```
+ No paid field for this expenditure!
+ ```
+
+
+#### Find keyword
+1. Finding keywords under the descriptions column in their list of expenditures
+
+ Test case 1:
+ ```
+ find bus
+ ```
+ Prerequisite : There are existing expenditures with the description : `bus`
+
+ Expected : List of items corresponding to the keyword will be displayed.
+ Test case 2:
+ ```
+ find taxi
+ ```
+ Prerequisite : There are no existing expenditures with the description : `taxi`
+
+ Expected : Message showing that no matching records are found in the list.
+2. View specific date expenditures under the date column
+
+ Test case 1:
+ ```
+ viewdate 2023-02-20
+ ```
+ Prerequisite : There are current expenditures dated 20 Feb 2023.
+
+ Expected : List of all expenditures with the corresponding date value, as well as the total amount spent
+ on that specific date
+ Test case 2:
+ ```
+ viewdate 2023-02-20
+ ```
+ Prerequisite : There are no current expenditures dated 20 Feb 2023.
+
+ Expected : Similar to previous, but there will not be any items shown in the list. The total amount will
+ be shown as 0.
+
+
+ Test case 3:
+ ```
+ viewdate 12 Jan 2021
+ ```
+ Expected : Invalid message will be shown with the respective error message, in this case being a
+ date time error.
+
+ Other invalid `viewdate` commands:
+ eg.
+ ```
+ viewdate
+ ```
+ Expected : Similar to previous, an invalid message with the error will be displayed for the user.
+3. View specific type of expenditure under the expenditure column
+ Test case 1:
+ ```
+ viewtype transport
+ ```
+ Prerequisite : There are current expenditures with the `transport` type.
+
+ Expected : List of all expenditures under transport expenditure, as well as the total amount spent
+ for that type of expenditure.
+ Test case 2:
+ ```
+ viewtype transport
+ ```
+ Prerequisite : There are no current expenditures with the `transport` type.
+
+ Expected : Similar to previous, but there will not be any items shown in the list. The total amount will
+ be shown as 0.
+ Test case 3:
+ ```
+ viewtype swimming
+ ```
+ Expected : Invalid message will be shown with the respective error message, in this case an
+ invalid expenditure.
\ No newline at end of file
diff --git a/docs/README.md b/docs/README.md
index bbcc99c1e7..3589c35a22 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,6 +1,11 @@
-# Duke
+# MyLedger
-{Give product intro here}
+
+
+
+
+MyLedger is a desktop app for managing finances, designed for university students studying locally or on exchange. It is optimized for use via a Command Line Interface (CLI). For students that can type fast, MyLedger can help them record and classify their transactions into categories. Students can expect to get an overview of their transactions at a glance,
+which helps them to monitor their budget and expenses more effciently.
Useful links:
* [User Guide](UserGuide.md)
diff --git a/docs/UserGuide.md b/docs/UserGuide.md
index abd9fbe891..653455b8f4 100644
--- a/docs/UserGuide.md
+++ b/docs/UserGuide.md
@@ -1,42 +1,502 @@
-# User Guide
+# MyLedger - User Guide
+
+
+
+
+
+
+
+- [MyLedger - User Guide](#myledger---user-guide)
+ - [Introduction](#introduction)
+ - [Quick Start](#quick-start)
+ - [Managing Transactions](#managing-transactions)
+ - [4.1. Adding an expenditure](#41-adding-an-expenditure)
+ - [4.2. Adding a lend/borrow record](#42-adding-a-lendborrow-record)
+ - [4.3. Editing an Expenditure](#43-editing-an-expenditure)
+ - [4.4. Editing a Lend/Borrow record](#44-editing-a-lendborrow-record)
+ - [4.5. Deleting an expenditure record](#45-deleting-an-expenditure-record)
+ - [4.6. Duplicating an expenditure record](#46-duplicating-an-expenditure-record)
+ - [4.7. Marking an accommodation or tuition record](#47-marking-an-accommodation-or-tuition-record)
+ - [4.8. Unmarking an accommodation or tuition expenditure record](#48-unmarking-an-accommodation-or-tuition-expenditure-record)
+ - [4.9. Setting a budget](#49-setting-a-budget)
+ - [4.10. Checking expenditures against the set budget](#410-checking-expenditures-against-the-set-budget)
+ - [4.11. List out and display the expenditure list](#411-list-out-and-display-the-expenditure-list)
+ - [4.12. Finding expenditure records by keyword](#412-finding-expenditure-records-by-keyword)
+ - [4.13. Sorting the expenditure list](#413-sorting-the-expenditure-list)
+ - [4.14. View the expenditure list by expenditure category or type](#414-view-the-expenditure-list-by-expenditure-category-or-type)
+ - [4.15. View the expenditure list by date](#415-view-the-expenditure-list-by-date)
+ - [4.16. Currency rates](#416-currency-rates)
+ - [FAQ](#faq)
+ - [Command Summary](#command-summary)
+
+
+
## Introduction
-{Give a product intro}
+MyLedger is a desktop app for managing finances, designed for university students in the National University of Singapore (NUS), studying locally or on exchange. It is optimized for use via a Command Line Interface (CLI). For students that can type fast, MyLedger can help them record and classify their transactions into categories. Students can expect to get an overview of their transactions at a glance,
+which helps them to monitor their budget and expenses more effciently.
-## Quick Start
+All currency amounts used in MyLedger is based of the **Singapore Dollar (SGD)**.
-{Give steps to get started quickly}
+## Quick Start
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 `MyLedger` from [here](https://github.com/AY2223S2-CS2113-T14-3/tp/releases/tag/v2.0).
+3. Open the command terminal on your device.
+4. Navigate to the folder in command terminal and run the command `java -jar [filename].jar`
+5. Alternatively, double click on the JAR file to run the app.
+
+## Managing Transactions
+
+### 4.1. Adding an expenditure
+
+Adds an expenditure to the record
+
+Format: `EXPENDITURE_CATEGORY d/DATE a/AMOUNT p/DESCRIPTION`
+
+| Parameter | Description |
+| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
+| `EXPENDITURE_CATEGORY` | The type of transaction. There are 7 types, `Academic`, `Accomodation`, `Entertainment` , `Food` , `Transport` , `Tuition` and `Other` |
+| `AMOUNT` | The amount of the transaction. It is a positive whole number ranging from 1 to 10000000 (Ten Million). |
+| `DATE` | The date when the transaction took place on. It must be in yyyy-MM-dd format, e.g. 2023-02-02 |
+| `DESCRIPTION` | More information regarding the transaction. Special symbols and length are not restricted. |
+
+**Important Information:**
+
+- All parameters listed must be present in this command.
+- All parameters must not be empty.
+- The input date format must be in yyyy-MM-DD format
+- The input year cannot be earlier than 2000, and the maximum year that can be set is 9999.
+ **Examples:**
+
+- `academic d/2023-02-02 a/25.10 p/NUS`
+- `other d/2000-01-31 a/26 p/Eating lunch`
+
+**Expected Output:**
+
+Adding an Academic Expenditure
+
+Input:
+
+```
+academic d/2023-02-02 a/25.10 p/NUS
+```
+
+Output:
+
+```
+Added academic expenditure: [Academic] || Date: 2 Feb 2023
+|| Value: 25.1 || Description: NUS
+```
+
+The output should be displayed on a single line. However, output in UG has been displayed across 2 lines to format the output in PDF form.
+
+Adding a Other Expenditure
+
+Input:
+
+```
+other d/2000-01-31 a/26 p/Eating lunch
+```
+
+Output:
+
+```
+Added other expenditure: [Other] || Date: 31 Jan 2000
+|| Value: 26.0 || Description: Eating lunch
+```
+
+The output should be displayed on a single line. However, output in UG has been displayed across 2 lines to format the output in PDF form.
+
+
+### 4.2. Adding a lend/borrow record
+
+Adds a lending or borrowing transaction to the record
+
+Format: `CATEGORY d/DATE n/NAME a/AMOUNT b/DEADLINE p/DESCRIPTION`
+
+| Parameter | Description |
+| ------------- | ------------------------------------------------------------------------------------------------------- |
+| `CATEGORY` | The category of record of `lend` or `borrow`. It should either be `lend` or `borrow`. |
+| `DATE` | The date when the transaction took place on. It must be in yyyy-MM-dd format, e.g. 2023-02-02. |
+| `NAME` | The name of the other party involved in the transaction. Input name should not have a slash. |
+| `AMOUNT` | The amount of the transaction. It can a positive whole number ranging from 1 to 10000000 (Ten Million). |
+| `DEADLINE` | The date when the transaction is dued. It must be in yyyy-MM-dd format, e.g. 2023-02-02. |
+| `DESCRIPTION` | More information regarding the transaction. Special symbols and length are not restricted. |
+
+**Important Information:**
+
+- All parameters must be present in this command.
+- All parameters must not be empty.
+- The input date format must be in yyyy-MM-DD format.
+- The input year cannot be earlier than 2000, and the maximum year that can be set is 9999.
+- Our application does not support input names with a slash '/'.
+- Return date should be equal to or later than the present date.
+- Borrow date must be before the return date.
+
+**Examples:**
+
+- `lend d/2022-02-02 n/Akshay Narayan a/25.10 b/2024-07-14 p/CS2113`
+
+**Expected Output:**
+
+Adding a lend transaction
+
+Input:
+
+```
+lend d/2022-02-02 n/Akshay Narayan a/25.10 b/2024-07-14 p/CS2113
+```
+
+Output:
+
+```
+Added lend expenditure: [Lend] || Lent to: Akshay Narayan || Date: 2 Feb 2022
+|| Value: 25.1 || Description: CS2113 || by: 14 Jul 2024
+```
+
+The output should be displayed on a single line. However, output in UG has been displayed across 2 lines to format the output in PDF form.
+
+### 4.3. Editing an Expenditure
+
+Edits an existing expenditure transaction in the record. After a successful edit, the updated list is shown.
+
+**Format:** `edit INDEX d/DATE a/AMOUNT p/DESCRIPTION`
+
+| Parameter | Description |
+| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `INDEX` | A list entry value for the transaction. It is a positive whole number ranging from 1 to 1000000 and must be within the range of the number of items in the expenditure list. |
+| `DATE` | The date when the transaction took place on. It must be in yyyy-MM-dd format, e.g. 2023-02-02 |
+| `AMOUNT` | The amount of the transaction. It is a positive whole number ranging from 1 to 10000000 (Ten Million). |
+| `DESCRIPTION` | More information regarding the transaction. Special symbols and length are not restricted. |
+
+**Important Information:**
+
+- The fields provided are the same as adding an expenditure in [4.1](#41-adding-an-expenditure)
+- Cannot change an expenditure type, e.g. cannot change an `Academic` expenditure to an `Accomodation` expenditure
+- The input year cannot be earlier than 2000, and the maximum year that can be set is 9999.
+- All parameters must be present in this command.
+
+**Examples:**
+
+- `edit 2 d/2023-02-15 a/20.00 p/Eat Food`
+
+**Expected Output:**
+
+Editing an expenditure
+
+```
+edit 2 d/2023-02-15 a/20.00 p/Eat Food
+Edited! Here is the updated list:
+The list is printed subsequent to the command but is omitted as
+different users will have different expenditures
+```
+
+- Take note that the list that is printed subsequent to the command is omitted as different users will have different expenditures and hence different lists.
+
+### 4.4. Editing a Lend/Borrow record
+
+Edits an existing lend or borrow in the record. After a successful edit, the updated list is shown.
+
+**Format:** `edit INDEX d/DATE n/NAME a/AMOUNT b/DEADLINE p/DESCRIPTION`
+
+| Parameter | Description |
+| ------------- | ------------------------------------------------------------------------------------------------------ |
+| `INDEX` | A list entry value for the transaction. It is a positive whole number ranging from 1 to 1000000. |
+| `DATE` | The date when the transaction took place on. It must be in yyyy-MM-dd format, e.g. 2023-02-02 |
+| `NAME` | The name of the other party involved in the transaction. Input name should not have a slash. |
+| `AMOUNT` | The amount of the transaction. It is a positive whole number ranging from 1 to 10000000 (Ten Million). |
+| `DEADLINE` | The date when the transaction is dued. It must be in yyyy-MM-dd format, e.g. 2023-02-02. |
+| `DESCRIPTION` | More information regarding the transaction. Special symbols and length are not restricted. |
+
+**Important Information:**
+
+- The fields provided are the same as adding an expenditure in [4.2](#42-adding-a-lendborrow-record)
+- Cannot change a `lend` record to a `borrow` record or vice versa.
+- Our application does not support input names with a slash '/'.
+- The input year cannot be earlier than 2000, and the maximum year that can be set is 9999.
+- Return date should be equal to or later than the present date.
+- Borrow date must be before the return date.
+- All parameters must be present in this command.
+
+**Examples:**
+
+- `edit 3 d/2022-02-02 n/Akshay Narayan a/25.10 b/2024-07-14 p/CS2113`
+
+**Expected Output:**
+
+Editing an expenditure
+
+```
+edit 3 d/2022-02-02 n/Akshay Narayan a/25.10 b/2024-07-14 p/CS2113
+Edited! Here is the updated list:
+The list is printed subsequent to the command but is omitted as
+different users will have different expenditures
+```
+
+- Take note that the list that is printed subsequent to the command is omitted as different users will have different expenditures and hence different lists.
+
+### 4.5. Deleting an expenditure record
+
+Deletes an existing expenditure record from the expenditure list. After a successful delete, the updated list is shown.
+
+**Format:** `delete INDEX`
+
+| Parameter | Description |
+| --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `INDEX` | A list entry value for the transaction. It is a positive whole number ranging from 1 to 1000000 and must be within the range of the number of items in the expenditure list. |
+
+**Important Information:**
+
+- Providing special characters and indices out of the range of the number of expenditures in the expenditure list are invalid.
+
+Input:
+
+```
+delete 1
+```
+
+Output:
+
+```
+Entry has been deleted
+Here is your updated list:
+```
+
+- Take note that the list that is printed subsequent to the command is omitted as different users will have different expenditures and hence different lists.
+
+### 4.6. Duplicating an expenditure record
+
+Duplicates an existing expenditure record from the expenditure list. After a successful duplicate, it will be appended to the expenditure list.
+
+**Format:** `duplicate INDEX`
-## Features
+| Parameter | Description |
+| --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `INDEX` | A list entry value for the transaction. It is a positive whole number ranging from 1 to 1000000 and must be within the range of the number of items in the expenditure list. |
-{Give detailed description of each feature}
+**Important Information:**
-### Adding a todo: `todo`
-Adds a new item to the list of todo items.
+- Providing special characters and indices out of the range of the number of expenditures in the expenditure list are invalid.
-Format: `todo n/TODO_NAME d/DEADLINE`
+### 4.7. Marking an accommodation or tuition record
-* The `DEADLINE` can be in a natural language format.
-* The `TODO_NAME` cannot contain punctuation.
+Marks an existing accommodation or tuition expenditure in the expenditure list as completed.
-Example of usage:
+**Format:** `mark INDEX`
-`todo n/Write the rest of the User Guide d/next week`
+| Parameter | Description |
+| --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `INDEX` | A list entry value for the transaction. It is a positive whole number ranging from 1 to 1000000 and must be within the range of the number of items in the expenditure list. |
-`todo n/Refactor the User Guide to remove passive voice d/13/04/2020`
+**Important Information:**
+
+- Marking expenditures that are not accommodation or tuition expenditures are invalid.
+- Marking accommodation or tuition expenditures that is already marked is invalid.
+
+### 4.8. Unmarking an accommodation or tuition expenditure record
+
+Unmarks an existing accommodation or tuition expenditure in the expenditure list as incomplete.
+
+**Format:** `unmark INDEX`
+
+| Parameter | Description |
+| --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `INDEX` | A list entry value for the transaction. It is a positive whole number ranging from 1 to 1000000 and must be within the range of the number of items in the expenditure list. |
+
+**Important Information:**
+
+- Unmarking expenditures that are not accommodation or tuition expenditures are invalid.
+- Unmarking accommodation or tuition expenditures that is already marked is invalid.
+
+### 4.9. Setting a budget
+
+Sets a budget amount that one would like to keep within.
+
+**Format:** `set BUDGET`
+
+| Parameter | Description |
+| --------- | ------------------------------------------------------------------------------------------------ |
+| `INDEX` | A list entry value for the transaction. It is a positive whole number ranging from 1 to 1000000. |
+
+**Important Information:**
+
+- The set budget can be compared with the total sum of expenditures with the [`check`](#48-checking-expenditures-against-the-set-budget) command.
+- The set budget will not be saved after the `exit` command, thus it will be 0 when MyLedger is restarted.
+
+### 4.10. Checking expenditures against the set budget
+
+Compares the set budget via the [`set`](#47-setting-a-budget) command against the total sum of expenditures in the expenditures.
+
+**Format:** `check FILTER`
+
+| Parameter | Description |
+| ------------------------ | --------------------------------------------------------------------------------------- |
+| `FILTER`
[optional] | A filter that allows the user to compare budget with a certain category or time period. |
+
+| Filter types | Description |
+| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| Year | `y/YEAR` filters the check to only compare budget with expenditures made in that specific year. |
+| Day | `d/YEAR-MONTH-DAY` filters the check to only compare budget with expenditures made on that specific day in that specific month and year. |
+| Expenditure type | `t/EXPENDITURE_TYPE` filters the check to only compare budget with expenditures made under that expenditure type. This does not include categories lent and borrow. |
+
+**Important Information:**
+
+- Checking budget is compared with the latest stored value of the set budget. By default, the budget set is 0.
+- If budget set is 0, message will prompt user to `set` a value before calling `check` again. `check` will not work if budget is 0.
+- Borrowed expenditure amount owed is separated for a better view of expenditures.
+- The input year cannot be earlier than 2000, and the maximum year that can be set is 9999.
+
+### 4.11. List out and display the expenditure list
+
+Displays all expenditures in the expenditure list. Expenditure value shown can be set to desired currency value.
+
+**Format:** `list CURRENCY`
+
+| Parameter | Description |
+| ---------- | --------------------------------------------------------------------------------------------- |
+| `CURRENCY` | The desired [currency](#417-currency-rates) for the expenditure value to be in. Default: SGD. |
+
+**Important Information:**
+
+- It reads saved expenditures from a save file upon launch of MyLedger. Else, the expenditure list is empty by default.
+
+### 4.12. Finding expenditure records by keyword
+
+Find expenditures by description
+
+**Format:** `find keyword`
+
+| Parameter | Description |
+| --------- | --------------------------------------------------------------------------------------------------- |
+| `KEYWORD` | A keyword to be queried for in the descriptions of all expenditure records in the expenditure list. |
+
+**Important Information:**
+
+- Parameter must not be empty.
+- Keyword is case-sensitive
+- Works like "Ctrl-F", the find command is able to search for all characters matching the keyword in the expenditure descriptions.
+
+
+### 4.13. Sorting the expenditure list
+
+Sorts the expenditure list by ascending or descending amount, or from earliest to latest date added. It will then display the sorted expenditure list.
+
+**Format:** `sort SORT_TYPE`
+
+| Parameter | Description |
+| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `SORT_TYPE` | The sort types are `ascend` to sort the list in ascending amount, `descend` to sort the list in descending amount, `earliest` to sort the list from the earliest date added and `latest` to sort the list from the latest date added. |
+
+**Important Information:**
+
+- The parameter must not be empty
+- The use of sort must follow with a valid parameter or the command would not be valid.
+
+### 4.14. View the expenditure list by expenditure category or type
+
+Sorts the expenditure list by ascending or descending amount, or from earliest to latest date added. It will then display the sorted expenditure list.
+
+**Format:** `viewtype CATEGORY CURRENCY`
+
+| Parameter | Description |
+| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| `CATEGORY` | The expenditure categories are of the regular [expenditure](#41-adding-an-expenditure) types and of lend and borrow [expenditure](#42-adding-a-lendborrow-record) types. |
+| `CURRENCY` | The desired [currency](#417-currency-rates) for the expenditure value to be in. Default: SGD. |
+
+**Important Information:**
+
+- The parameter must not be empty
+- The use of viewdate must follow with a valid expenditure type or category or the command would not be valid.
+
+### 4.15. View the expenditure list by date
+
+Sorts the expenditure list by ascending or descending amount, or from earliest to latest date added. It will then display the sorted expenditure list.
+
+**Format:** `viewdate DATE CURRENCY`
+
+| Parameter | Description |
+| ---------- | ----------------------------------------------------------------------------------------------------------- |
+| `DATE` | The date when the transaction took place on to be queried. It must be in YYYY-MM-DD format, e.g. 2023-02-02 |
+| `CURRENCY` | The desired [currency](#417-currency-rates) for the expenditure value to be in. Default: SGD. |
+
+**Important Information:**
+
+- The parameter must not be empty
+- The use of viewdate must follow with a valid date or the command would not be valid.
+- The date must be input in YYYY-MM-DD format.
+- If the date selected by the user falls between the time period of a borrow/lend record, those lend/borrow records will not be included in the amounts that are updated.
+- The input year cannot be earlier than 2000, and the maximum year that can be set is 9999.
+
+### 4.16. Currency rates
+
+Displays list of the other currency available in MyLedger and their value against SGD.
+
+**Format:** `showrates`
+
+
## FAQ
-**Q**: How do I transfer my data to another computer?
+**Q**: How do I transfer my data to another computer?
-**A**: {your answer here}
+**A**: MyLedger is able to transfer data using only it's text file. Simply copy the text from myLedger_data and paste it into the other device.
## Command Summary
-{Give a 'cheat sheet' of commands here}
+- Help: `help`
+
+- Add academic expenditure: `academic d/DATE a/AMOUNT p/DESCRIPTION`
+
+- Add accommodation expenditure: `accommodation d/DATE a/AMOUNT p/DESCRIPTION`
+
+- Add borrow expenditure: `borrow d/DATE n/BORROWER_NAME a/AMOUNT b/DEADLINE p/DESCRIPTION`
+
+- Add entertainment expenditure: `entertainment d/DATE a/AMOUNT p/DESCRIPTION`
+
+- Add food expenditure: `food d/DATE a/AMOUNT p/DESCRIPTION`
+
+- Add lend expenditure: `lend d/DATE n/LENT_NAME a/AMOUNT b/DEADLINE p/DESCRIPTION`
+
+- Add other expenditure: `other d/DATE a/AMOUNT p/DESCRIPTION`
+
+- Add transport expenditure: `transport d/DATE a/AMOUNT p/DESCRIPTION`
+
+- Add tuition expenditure: `tuition d/DATE a/AMOUNT p/DESCRIPTION`
+
+- Check all expenditure with budget: `check`
+
+- Check budget with expenditures made in specific year: `check y/YEAR`
+
+- Check budget with expenditures made in specific day: `check d/YEAR-MONTH-DAY`
+
+- Check budget with expenditures made under specific expenditure type: `check t/EXPENDITURE TYPE`
+
+- Delete expenditure: `delete INDEX`
+
+- Duplicate expenditure `duplicate INDEX`
+
+- Edit expenditure: `edit INDEX d/DATE a/AMOUNT p/DESCRIPTION`
+
+- Edit borrow or lend expenditure: `edit INDEX d/DATE n/BORROWER_OR_LENT_NAME a/AMOUNT b/DEADLINE p/DESCRIPTION`
+
+- Find by keyword in expenditure descriptions: `find KEYWORD`
+
+- List all expenditures: `list CURRENCY`
+
+- Mark a specific expenditure to be complete (Tuition or Accommodation): `mark INDEX`
+
+- Unmark a specific expenditure to be complete (Tuition or Accommodation): `unmark INDEX`
+
+- Set temporary expenditure: `set AMOUNT`
+
+- Sort expenditure list by ascending/descending amount: `sort ASCEND/DESCEND`
+
+- Sort expenditure list by latest/earliest date added: `sort LATEST/EARLIEST`
+
+- View expenditure list by date added: `viewdate DATE CURRENCY`
+
+- View expenditure list by type of expenditure: `viewtype CATEGORY CURRENCY`
-* Add todo `todo n/TODO_NAME d/DEADLINE`
+- Show all currencies: `showrates`
diff --git a/docs/team/chick3nboy.md b/docs/team/chick3nboy.md
new file mode 100644
index 0000000000..a9105c3d54
--- /dev/null
+++ b/docs/team/chick3nboy.md
@@ -0,0 +1,39 @@
+# Leonardo Ong Dingchao - Project Portfolio Page
+
+## Overview
+MyLedger is a desktop app for managing finances, designed for university students studying locally or on exchange. It is optimized for use via a Command Line Interface (CLI). For students that can type fast, MyLedger can help them record and monitor their budget and expenses, managing their transactions more effciently.
+
+### Summary of Contributions
+#### Main Features Implemented:
+
+1. Storage for MyLedger
+ - **What it does** : Processes the list of expenditures in MyLedger, updating the text file with the save information after every command, and initializing the list
+ when MyLedger begins running.
+ - **Justification**: This allows the program to remember past user inputs, making MyLedger a more effective ledger.
+ - **Highlights**: This feature is an essential part of MyLedger, as the programme being unable to store past entries defeats the purpose of a ledger.
+ The challenge of the implementation came in the form of having to account for how to store the different types of expenditures, with some expenditures having more fields than others, as well as account for how to deal with a corrupted save file.
+
+2. View command
+ - **What it does**: `ViewTypeCommand` and `ViewDateCommand` with the command `viewtype` and `viewdate` shows the user a filtered list of expenditures that have a specific command or date, as well as tallying the total amount of the expenditures filtered by the list.
+ - **Justification**: With MyLedger being an app that allows users to monitor their budget and expenses, this feature allows the user to more effiently track their expenditures of each type or date.
+ - **Highlights**: Implementing `ViewTypeCommand` was more challenging compared to `ViewDateCommand`. `ViewDateCommand` could be done by comparing the input LocalDate with the LocalDate attribute stored within each expenditure, whereas for `ViewTypeCommand` the expenditure type does not have an attribute allocated to it, and a getType method, which returns a different string based on expenditure type, was needed for comparison.
+
+3. Currency feature
+ - **What it does**: A new class containing the currency values of the countries with universities partnered with NUS based on SGD. `showrates` is a command that lists out all these curries values and the List and View commands have been added with a currency field, that allows the user to output the expenditure amount in the desired currency.
+ - **Justification**: This implementation value adds MyLedger in making the app more convenient for exchange students studying in NUS, giving them a gauge on how much their expenditure is in their home currency.
+ - **Highlights**: Initially we proposed using an API to constantly update the currencies values of the feature, however considering the large amounts of potential bugs and requiring internet connection, we decided the app would benefit more from being concise and using a constant value that servers as a rough gauge. The challenges faced when implementing this feature comes from all of the methods involved in printing an expenditure already using the toString method without a currency field, and changing the toString to include that field would introduce further issues, thus a new method that functions similarly to toString had to be implemented.
+
+#### Code Contributed: [RepoSense Link](https://nus-cs2113-ay2223s2.github.io/tp-dashboard/?search=Chick3nBoy&sort=totalCommits%20dsc&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=Chick3nBoy&tabRepo=AY2223S2-CS2113-T14-3%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false)
+
+#### Documentation:
+1. User Guide:
+ - Added instructions for Quick Start
+
+2. Developer Guide
+ - Added architecture diagram and documentation.
+ - Added documentation and sequence diagram for `Storage`, `View Command`, and `Currency`
+
+#### Team-based Tasks
+1. Attended and contributed to weekly discussions
+2. PR reviewed with non-trivial review comments [#18](https://github.com/AY2223S2-CS2113-T14-3/tp/pull/18) , [#41](https://github.com/AY2223S2-CS2113-T14-3/tp/pull/41) , [#134](https://github.com/AY2223S2-CS2113-T14-3/tp/pull/134), [#145](https://github.com/AY2223S2-CS2113-T14-3/tp/pull/145)
+3. Non-trivial bug fixes: [#28](https://github.com/AY2223S2-CS2113-T14-3/tp/pull/28/commits)
\ No newline at end of file
diff --git a/docs/team/dsicol.md b/docs/team/dsicol.md
new file mode 100644
index 0000000000..41163356bb
--- /dev/null
+++ b/docs/team/dsicol.md
@@ -0,0 +1,65 @@
+# Leo Zheng Rui, Darren - Project Portfolio Page
+
+## Overview
+
+MyLedger is a desktop app for managing finances, designed for university students in the National University of Singapore (NUS), studying locally or on exchange. It is optimized for use via a Command Line Interface (CLI). For students that can type fast, MyLedger can help them record and classify their transactions into categories. This helps them to monitor their budget and expenses more efficiently.
+
+### Summary of Contributions
+#### Main Features Implemented:
+
+**The expenditure package**
+ - **What it does** : Contains all the classes and operations for the fixed expenditure types and expenditure list.
+ - **Justification**: The fixed expenditures in the program can be instantiated as real world expenditure, and kept track of in the application's expenditure list. Since the application aims to help users add and to manage expenditures, the package of classes forms the core of MyLedger.
+ - **Highlights**: This feature sets the direction of MyLedger. Thus, deciding the attributes of each class played an important role in setting the trajectory of the application; brainstorming the important information the target user would need in their expenditure record.
+
+**The Command Classes for all fixed expenditure types**
+ - **What it does**: Contains all the command classes for the fixed expenditure types and to display expenditure list. Additionally, the `CommandResult.java`class displays to the user the result of the command after each successful execution.
+ - **Justification**: With the help of MyLedger's parsing unit, the user's input are broken down and fed into their respective command class. The execution of these command classes produces the user's desired outcome (i.e. adding an academic expenditure into the current expenditure list).
+ - **Highlights**: The implementation of the command classes requires good coordination with the implementor of the parsing class as they would need information on how each expenditure command is instantiated; it was done well.
+
+**The Sort Command**
+ - **What it does**: Sorts the expenditure list in ascending or descending amount, or from the latest or earliest dates.
+ - **Justification**: This allows users to sort their expenditures to gain better insight to manage them; to rank the expenditures that spent the most/least money, or ones that have added the most recently/earliest.
+ - **Highlights**: The feature will change the order of the expenditure list for future reference. This is done to improve user experience while navigating the sorted list after calling the command.
+
+**The repeating date of the Accommodation and Tuition Expenditures**
+ - **What it does**: The `AccommodationExpenditure` and `TuitionExpenditure` takes in a date of repeat. Whether or not the aforementioned expenditures have been marked beforehand, this feature ensures the the expenditures are unmarked after 1 full year.
+ - **Justification**: The `AccommodationExpenditure` and `TuitionExpenditure` are lump sum payments and must be paid within a period of time. The nature of these expenditures are repeating and thus, users can trust MyLedger to unmark the aforementioned expenditures to serve as an annual reminder.
+ - **Highlights**: This was a challenging feature as `LocalDate` is used extensively to compare the dates. Additionally, the method must be ran for the check to be ran, thus this is implemented in the main loop logic of the program. After a year has passed, the program will retain the day and month values of the repeat date, and increment the year. This will only run once every year and only ran after the specified date is reached or passed.
+
+**Fix save file reading related bugs**
+ - **What it does**: The save file stores all expenditures from the most recent expenditure list.
+ - **Justification**: Upon corruption of the save file, the program oringally crashes. With the fixes, only individual corrupted expenditures are deleted, uncorrupted expenditures can still be loaded.
+ - **Highlights**: Considering all possible combinations for corruption was challenging. Now the save file reading complies with the input rules defined by the application.
+
+#### Code Contributed: [RepoSense Link](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=dsicol&tabRepo=AY2223S2-CS2113-T14-3%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false)
+
+
+
+#### Project Management:
+1. Milestones:
+ - Managed all milestones: `v1.0`, `v2.0` and `v2.1`.
+ - Managed all releases: `v1.0`, `v2.0` and `v2.1` (3 releases) on github.
+
+#### Enhancement to existing features:
+1. Enhancements:
+ - Improved `AccommodationExpenditure` and `TuitionExpenditure` to be unmarked 1 year after setting it. This is due to the repeating nature of these expenditures. (Pull request [#132](https://github.com/AY2223S2-CS2113-T14-3/tp/pull/132))
+ - Wrote additional tests for the command classes to increase test coverage (Pull request [#40](https://github.com/AY2223S2-CS2113-T14-3/tp/pull/40)).
+ - Fix save file reading bugs (Pull request [#145](https://github.com/AY2223S2-CS2113-T14-3/tp/pull/145), [#153](https://github.com/AY2223S2-CS2113-T14-3/tp/pull/153)).
+
+#### Documentation:
+1. User Guide:
+ - Added the MyLedger command summary: [#68](https://github.com/AY2223S2-CS2113-T14-3/tp/pull/68/files#diff-b50feaf9240709b6b02fb9584696b012c2a69feeba89e409952cc2f401f373fb)
+ - Added user guide for `mark`, `unmark`, `set`, `check`, `list`, `find`, `sort`, `viewtype` and `viewdate`: [#78](https://github.com/AY2223S2-CS2113-T14-3/tp/pull/78)
+
+2. Developer Guide
+ - Added documentation and class diagram for **Expenditure categories**.
+ - Added documentation and sequence diagram for `repeatDate` process.
+ - Added documentation and class diagram for **Command component**.
+ - Added documentation and sequence diagram for **Add expenditure command** in the **Command List** section.
+ - Added documentation and explanations for the `SortCommand`.
+
+#### Team-based Tasks
+1. Initiated, led, and contributed to weekly discussions.
+2. PR reviewed with non-trivial review comments [#22](https://github.com/AY2223S2-CS2113-T14-3/tp/pull/22), [#151](https://github.com/AY2223S2-CS2113-T14-3/tp/pull/151), [#144](https://github.com/AY2223S2-CS2113-T14-3/tp/pull/144), [#167](https://github.com/AY2223S2-CS2113-T14-3/tp/pull/167)
+3. Non-trivial bug fixes: [#127](https://github.com/AY2223S2-CS2113-T14-3/tp/pull/127), [#145](https://github.com/AY2223S2-CS2113-T14-3/tp/pull/145), [#153](https://github.com/AY2223S2-CS2113-T14-3/tp/pull/153)
\ No newline at end of file
diff --git a/docs/team/images/Architecture.png b/docs/team/images/Architecture.png
new file mode 100644
index 0000000000..b5374c3e8d
Binary files /dev/null and b/docs/team/images/Architecture.png differ
diff --git a/docs/team/images/MyLedger.jpeg b/docs/team/images/MyLedger.jpeg
new file mode 100644
index 0000000000..e1af45b686
Binary files /dev/null and b/docs/team/images/MyLedger.jpeg differ
diff --git a/docs/team/images/UMLClassDiagramExpenditure.png b/docs/team/images/UMLClassDiagramExpenditure.png
new file mode 100644
index 0000000000..689c1a88c7
Binary files /dev/null and b/docs/team/images/UMLClassDiagramExpenditure.png differ
diff --git a/docs/team/images/academicExpenditureCommand.png b/docs/team/images/academicExpenditureCommand.png
new file mode 100644
index 0000000000..1ffb589d6f
Binary files /dev/null and b/docs/team/images/academicExpenditureCommand.png differ
diff --git a/docs/team/images/checkCommand.png b/docs/team/images/checkCommand.png
new file mode 100644
index 0000000000..da66b481ec
Binary files /dev/null and b/docs/team/images/checkCommand.png differ
diff --git a/docs/team/images/initializeList.png b/docs/team/images/initializeList.png
new file mode 100644
index 0000000000..690606dc52
Binary files /dev/null and b/docs/team/images/initializeList.png differ
diff --git a/docs/team/images/newInitializeList.png b/docs/team/images/newInitializeList.png
new file mode 100644
index 0000000000..d239ac8683
Binary files /dev/null and b/docs/team/images/newInitializeList.png differ
diff --git a/docs/team/images/parseAddSequenceDiagram.png b/docs/team/images/parseAddSequenceDiagram.png
new file mode 100644
index 0000000000..276e1e931b
Binary files /dev/null and b/docs/team/images/parseAddSequenceDiagram.png differ
diff --git a/docs/team/images/parserEdit.png b/docs/team/images/parserEdit.png
new file mode 100644
index 0000000000..1483934bba
Binary files /dev/null and b/docs/team/images/parserEdit.png differ
diff --git a/docs/team/images/parserOverview.png b/docs/team/images/parserOverview.png
new file mode 100644
index 0000000000..4c9ab982f4
Binary files /dev/null and b/docs/team/images/parserOverview.png differ
diff --git a/docs/team/images/repeatDate.png b/docs/team/images/repeatDate.png
new file mode 100644
index 0000000000..f3da15812f
Binary files /dev/null and b/docs/team/images/repeatDate.png differ
diff --git a/docs/team/images/saveList.png b/docs/team/images/saveList.png
new file mode 100644
index 0000000000..2bd2b77c0f
Binary files /dev/null and b/docs/team/images/saveList.png differ
diff --git a/docs/team/images/simplifiedCommandClassDiagram.png b/docs/team/images/simplifiedCommandClassDiagram.png
new file mode 100644
index 0000000000..93b01c9144
Binary files /dev/null and b/docs/team/images/simplifiedCommandClassDiagram.png differ
diff --git a/docs/team/images/simplifiedParserOverview.png b/docs/team/images/simplifiedParserOverview.png
new file mode 100644
index 0000000000..29b89d422c
Binary files /dev/null and b/docs/team/images/simplifiedParserOverview.png differ
diff --git a/docs/team/images/umlCommandClassDiagram.png b/docs/team/images/umlCommandClassDiagram.png
new file mode 100644
index 0000000000..2d10293fae
Binary files /dev/null and b/docs/team/images/umlCommandClassDiagram.png differ
diff --git a/docs/team/images/viewDate.png b/docs/team/images/viewDate.png
new file mode 100644
index 0000000000..4fec1eae38
Binary files /dev/null and b/docs/team/images/viewDate.png differ
diff --git a/docs/team/itszhixuan.md b/docs/team/itszhixuan.md
new file mode 100644
index 0000000000..5751cc6472
--- /dev/null
+++ b/docs/team/itszhixuan.md
@@ -0,0 +1,42 @@
+# Lee Zhi Xuan - Project Portfolio Page
+
+## Overview
+MyLedger is a desktop app for managing finances, designed for university students studying locally or on exchange. It is optimized for use via a Command Line Interface (CLI). For students that can type fast, MyLedger can help them record and monitor their budget and expenses, managing their transactions more effciently.
+
+### Summary of Contributions
+#### Main Features Implemented:
+
+1. Parser for each command
+ - **What it does** : Processes the inputs made by the user and converts it into its respective `Command` object for further processing.
+ - **Justification**: This allows the program to interact with the user by understand the user inputs made.
+ - **Highlights**: This enhancement is required for all subsequent enhancements, which required extensive testing to prevent errors when reading inputs. To reduce duplicate code, each specific parse class will call on `ParseIndividualValue` to efficiently parse all input parameters and commands from a user.
+ The implementation was challenging as each parsing action required a specific command class that had to inherit from `Command` and adhere to its guidelines. Also as previously mentioned, the challenge was also to consider all possible inputs that might result in our application not working.
+
+2. Comparing budget with expenditures
+ - **What it does**: `SetBudgetCommand` with the command `set` allows the user to input a positive numerical value that is not 0 that indicates their existing budget. `CheckBudgetCommand` with the command word `check` will then compare the budget with the sum of all expenditures made by the user. Output will display the current financial status compared to their budget. User can also compare budget with optional filters such as expenditure type, which would provide a better insight to expenditures under a certain category.
+ - **Justification**: With MyLedger being an app that allows users to monitor their budget and expenses, this will bridge the two together and provide insights on their current financial status with their budget.
+ - **Highlights**: The challenge of comparing budget with all expenditures is having to consider the user inputs and different expenses. `Borrow` and `lend` are not considered official expenditures and would still require to be paid back, while other factors such as `marked` will also affect the total expenditure used to compare with their budget. The message output should also provide enough insights into their financial status. Separately, the expenditure types are optional parameters from the user, which provided a different challenge of having to accept optional inputs and inputs with just the `check` function.
+
+3. Duplicate Command
+ - **What it does**: Duplicates existing inputs in the list.
+ - **Justification**: For repeated expenditures, it reduces the hassle of having to input all expenditure parameters again
+ - **Highlights**: This enhancement provides an alternative way to create similar expenditures. Implementing this functionality requires reading the data on the txt.file and subsequently writing on it.
+
+#### Code Contributed: [RepoSense Link](https://nus-cs2113-ay2223s2.github.io/tp-dashboard/?search=itszhixuan&sort=totalCommits%20dsc&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=itszhixuan&tabRepo=AY2223S2-CS2113-T14-3%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code~other&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false)
+
+#### Documentation:
+1. User Guide:
+ - Added instructions for Quick Start
+ - Adding documentation for new features under list and check for v2.1
+
+2. Developer Guide
+ - Added documentation and sequence diagram for `Parser`
+ - Added sequence diagram for `check`
+ - Added user stories
+ - Added all possible instructions for instructors to manual test
+
+#### Team-based Tasks
+1. Attended and contributed to weekly discussions
+2. PR reviewed with non-trivial review comments [#30](https://github.com/AY2223S2-CS2113-T14-3/tp/pull/30)
+, [#40](https://github.com/AY2223S2-CS2113-T14-3/tp/pull/40) , [#49](https://github.com/AY2223S2-CS2113-T14-3/tp/pull/49)
+3. Non-trivial bug fixes: [#90](https://github.com/AY2223S2-CS2113-T14-3/tp/issues/90)
\ No newline at end of file
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/tzeloong.md b/docs/team/tzeloong.md
new file mode 100644
index 0000000000..12990ab8d9
--- /dev/null
+++ b/docs/team/tzeloong.md
@@ -0,0 +1,56 @@
+# Tan Tze Loong - Project Portfolio Page
+
+## Overview
+
+MyLedger is a desktop app for managing finances, designed for university students studying locally or on exchange. It is optimized for use via a Command Line Interface (CLI). For students that can type fast, MyLedger can help them record and classify their transactions into categories. Students can expect to get an overview of their transactions at a glance, which helps them to monitor their budget and expenses more effciently.
+
+### Summary of Contributions
+
+#### Features Implemented
+
+1. Added the ability to edit expenditures and lend/borrow transactions
+
+ - **What it does**: This operation allows users to edit expenditures or lend/borrow transactions one at a time. It cannot change the expenditure type of a record, only its fields
+
+ - **Justification**: This feature improves the product significantly because a user can make mistakes when recording their daily expenditures/ transactions and the app should provide a convenient way to rectify them.
+
+ - **Highlights**: The challenge with implementing the edit feature was differentiating the edit of normal expenditures from the edit of lend/borrow transactions because the latter required the parsing of additional fields.
+
+2. Added the ability to find expenditures and transactions by description
+
+ - **What it does**: This operation allows users search for specific keywords that matches the description of their expenditures or lend/borrow transactions. Keyword is case-sensitive.
+
+ - **Justification**: By having this feature, users can find and keep track of expenditures that have similar descriptions across different categories.
+
+ - **Highlights**: The implementation of this feature involved looping through the list of expenditures to find matching records.
+
+#### Code Contributed
+
+The code contributed can be found on [RepoSense](https://nus-cs2113-ay2223s2.github.io/tp-dashboard/?search=tzeloong&breakdown=true&sort=groupTitle%20dsc&sortWithin=title&since=2023-02-17&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other).
+
+#### Enhancements to existing features
+
+- Updated checkBudget command to allow for checking of budget in a set period of time
+
+#### Documentation
+
+1. User Guide
+
+ - Designed and Added 'MyLedger' logo for application
+ - Added documentation for `Adding an Academic Expenditure` feature.
+ - Added documentation for `Adding a lend/borrow record` feature.
+ - Added documentation for `Editing an Expenditure` feature.
+ - Added documentation for `Editing a Lend/Borrow record` feature.
+
+2. Developer Guide
+ - Designed and Added 'MyLedger' logo for application
+ - Added documentation and implementation for `edit Command`
+ - Added documentation and implementation for `find Command`
+
+#### Team-based tasks
+
+1. Attended and contributed to weekly discussions.
+2. Accommodated and accounted for team opinions in implementation.
+3. Non-trivial bug fixes: [#80](https://github.com/AY2223S2-CS2113-T14-3/tp/issues/80),
+ [#82](https://github.com/AY2223S2-CS2113-T14-3/tp/issues/82),
+ [#92](https://github.com/AY2223S2-CS2113-T14-3/tp/issues/92)
diff --git a/docs/team/uml_files/check.puml b/docs/team/uml_files/check.puml
new file mode 100644
index 0000000000..de9ac4a568
--- /dev/null
+++ b/docs/team/uml_files/check.puml
@@ -0,0 +1,35 @@
+@startuml
+!define AWSPUML https://cdn.rawgit.com/Willibaur/PlantUML_AWS/07362bc9/
+skinparam defaultTextAlignment center
+!define ICONURLAWSPUML https://raw.githubusercontent.com/Willibaur/PlantUML_AWS/07362bc9/
+!define AWSPUML_SPRITESURL https://raw.githubusercontent.com/rabelenda/cicon-plantuml-sprites/v2.0/
+!define AWSPUML_NOBORDER
+skinparam defaultFontSize 24
+
+participant ":MainInputParser" as MainInputParser
+-> MainInputParser: parseInput(line)
+activate MainInputParser
+
+participant ":CheckBudgetCommand" as CheckBudgetCommand
+
+MainInputParser->CheckBudgetCommand** : <>
+activate CheckBudgetCommand
+participant ":ParseIndividualValue" as ParseIndividualValue
+CheckBudgetCommand->ParseIndividualValue : parseIndividualValue
+activate ParseIndividualValue
+return :String
+participant ":ExpenditureList" as ExpenditureList
+CheckBudgetCommand->ExpenditureList : getBudgetSet()
+activate ExpenditureList
+return :double
+
+autoactivate on
+CheckBudgetCommand->CheckBudgetCommand : checkAll()
+CheckBudgetCommand->CheckBudgetCommand : updateAmount()
+deactivate CheckBudgetCommand
+deactivate CheckBudgetCommand
+CheckBudgetCommand-> CheckBudgetCommand : getCheckCommandResult
+return
+CheckBudgetCommand --> MainInputParser : CommandResult(String)
+destroy CheckBudgetCommand
+@enduml
\ No newline at end of file
diff --git a/docs/team/uml_files/parser.puml b/docs/team/uml_files/parser.puml
new file mode 100644
index 0000000000..67003f9aee
--- /dev/null
+++ b/docs/team/uml_files/parser.puml
@@ -0,0 +1,50 @@
+@startuml
+!define AWSPUML https://cdn.rawgit.com/Willibaur/PlantUML_AWS/07362bc9/
+skinparam defaultTextAlignment center
+!define ICONURLAWSPUML https://raw.githubusercontent.com/Willibaur/PlantUML_AWS/07362bc9/
+!define AWSPUML_SPRITESURL https://raw.githubusercontent.com/rabelenda/cicon-plantuml-sprites/v2.0/
+!define AWSPUML_NOBORDER
+skinparam defaultFontSize 24
+
+participant ":MainInputParser" as MainInputParser
+-> MainInputParser: parseInput(line)
+activate MainInputParser
+
+participant ":ExitCommand" as ExitCommand
+alt Command is "exit"
+MainInputParser->ExitCommand** : <>
+activate ExitCommand
+return
+deactivate ExitCommand
+destroy ExitCommand
+
+else Command is "lend" or "borrow"
+participant ":ParseLendBorrow" as ParseLendBorrow
+MainInputParser->ParseLendBorrow** : ParseLendBorrow(splitValues[INDEX_USERSTRING])
+activate ParseLendBorrow
+loop five times to extract user fields
+participant ":ParseIndividualValue" as ParseIndividualValue
+ParseLendBorrow->ParseIndividualValue : parseIndividualValue
+activate ParseIndividualValue
+return :String
+deactivate ParseIndividualValue
+end
+alt Command is "Lend"
+participant ":LendExpenditureCommand" as LendExpenditureCommand
+ParseLendBorrow->LendExpenditureCommand** : LendExpenditureCommand
+activate LendExpenditureCommand
+return
+deactivate LendExpenditureCommand
+destroy LendExpenditureCommand
+else Command is "Borrow"
+participant ":BorrowExpenditureCommand" as BorrowExpenditureCommand
+ParseLendBorrow-> BorrowExpenditureCommand** : BorrowExpenditureCommand
+activate BorrowExpenditureCommand
+return
+deactivate BorrowExpenditureCommand
+destroy BorrowExpenditureCommand
+end
+ParseLendBorrow --> MainInputParser
+destroy ParseLendBorrow
+end
+@enduml
\ No newline at end of file
diff --git a/myLedger_data/myLedger_inputs.txt b/myLedger_data/myLedger_inputs.txt
new file mode 100644
index 0000000000..e23a49d698
--- /dev/null
+++ b/myLedger_data/myLedger_inputs.txt
@@ -0,0 +1,7 @@
+Acadd/Eatv/20.0t/2023-02-15p/Nonen/Noneo/Noner/None
+Fd/Eat Foodv/20.0t/2023-02-15p/Nonen/Noneo/Noner/None
+Trd/Eat Foodv/20.0t/2023-02-15p/Nonen/Noneo/Noner/None
+Acadd/penv/2.1t/2023-01-01p/Nonen/Noneo/Noner/None
+Fd/eatv/19.0t/2023-02-02p/Nonen/Noneo/Noner/None
+Od/Eating lunchv/26.0t/2000-01-31p/Nonen/Noneo/Noner/None
+
diff --git a/src/main/java/seedu/commands/AcademicExpenditureCommand.java b/src/main/java/seedu/commands/AcademicExpenditureCommand.java
new file mode 100644
index 0000000000..019d03abcd
--- /dev/null
+++ b/src/main/java/seedu/commands/AcademicExpenditureCommand.java
@@ -0,0 +1,28 @@
+package seedu.commands;
+
+import seedu.expenditure.AcademicExpenditure;
+import seedu.expenditure.ExpenditureList;
+import java.time.LocalDate;
+
+public class AcademicExpenditureCommand extends Command {
+ public static final String COMMAND_WORD = "academic";
+ private final String academicExpenditureDescription;
+ private final double academicExpenditureValue;
+ private final LocalDate academicExpenditureDate;
+
+ public AcademicExpenditureCommand(String description, double value, LocalDate date) {
+ this.academicExpenditureDescription = description;
+ this.academicExpenditureValue = value;
+ this.academicExpenditureDate = date;
+ }
+
+ public CommandResult execute(ExpenditureList expenditures) {
+ AcademicExpenditure academicExpenditure = new AcademicExpenditure(
+ academicExpenditureDescription,
+ academicExpenditureValue,
+ academicExpenditureDate);
+ expenditures.addExpenditure(academicExpenditure);
+ return new CommandResult(String.format("Added %s expenditure: %s",
+ COMMAND_WORD, academicExpenditure.toString()));
+ }
+}
diff --git a/src/main/java/seedu/commands/AccommodationExpenditureCommand.java b/src/main/java/seedu/commands/AccommodationExpenditureCommand.java
new file mode 100644
index 0000000000..b45730a121
--- /dev/null
+++ b/src/main/java/seedu/commands/AccommodationExpenditureCommand.java
@@ -0,0 +1,32 @@
+package seedu.commands;
+
+import seedu.expenditure.AccommodationExpenditure;
+import seedu.expenditure.ExpenditureList;
+
+import java.time.LocalDate;
+
+public class AccommodationExpenditureCommand extends Command{
+ public static final String COMMAND_WORD = "accommodation";
+ private final String accommodationExpenditureDescription;
+ private final double accommodationExpenditureValue;
+ private final LocalDate accommodationExpenditureDate;
+ private final LocalDate accommodationExpenditureRepeatDate;
+
+ public AccommodationExpenditureCommand(String description, double value, LocalDate date, LocalDate repeatDate) {
+ this.accommodationExpenditureDescription = description;
+ this.accommodationExpenditureValue = value;
+ this.accommodationExpenditureDate = date;
+ this.accommodationExpenditureRepeatDate = repeatDate;
+ }
+
+ public CommandResult execute(ExpenditureList expenditures) {
+ AccommodationExpenditure accommodationExpenditure = new AccommodationExpenditure(
+ accommodationExpenditureDescription,
+ accommodationExpenditureValue,
+ accommodationExpenditureDate,
+ accommodationExpenditureRepeatDate);
+ expenditures.addExpenditure(accommodationExpenditure);
+ return new CommandResult(String.format("Added %s expenditure: %s",
+ COMMAND_WORD, accommodationExpenditure.toString()));
+ }
+}
diff --git a/src/main/java/seedu/commands/BorrowExpenditureCommand.java b/src/main/java/seedu/commands/BorrowExpenditureCommand.java
new file mode 100644
index 0000000000..78a174e64d
--- /dev/null
+++ b/src/main/java/seedu/commands/BorrowExpenditureCommand.java
@@ -0,0 +1,31 @@
+package seedu.commands;
+
+import seedu.expenditure.BorrowExpenditure;
+import seedu.expenditure.ExpenditureList;
+
+import java.time.LocalDate;
+
+public class BorrowExpenditureCommand extends Command {
+ public static final String COMMAND_WORD = "borrow";
+ private final String borrowExpenditureDescription;
+ private final String borrowExpenditureName;
+ private final double borrowExpenditureValue;
+ private final LocalDate borrowExpenditureDate;
+ private final LocalDate borrowExpenditureDeadline;
+
+ public BorrowExpenditureCommand(String description, String borrowerName, double value, LocalDate date,
+ LocalDate borrowExpenditureDeadline) {
+ this.borrowExpenditureDescription = description;
+ this.borrowExpenditureName = borrowerName;
+ this.borrowExpenditureValue = value;
+ this.borrowExpenditureDate = date;
+ this.borrowExpenditureDeadline = borrowExpenditureDeadline;
+ }
+
+ public CommandResult execute(ExpenditureList expenditures) {
+ BorrowExpenditure borrowExpenditure = new BorrowExpenditure(borrowExpenditureDescription,
+ borrowExpenditureName, borrowExpenditureValue, borrowExpenditureDate, borrowExpenditureDeadline);
+ expenditures.addExpenditure(borrowExpenditure);
+ return new CommandResult(String.format("Added %s expenditure: %s", COMMAND_WORD, borrowExpenditure.toString()));
+ }
+}
diff --git a/src/main/java/seedu/commands/CheckBudgetCommand.java b/src/main/java/seedu/commands/CheckBudgetCommand.java
new file mode 100644
index 0000000000..5fd3c1dfb4
--- /dev/null
+++ b/src/main/java/seedu/commands/CheckBudgetCommand.java
@@ -0,0 +1,229 @@
+package seedu.commands;
+
+import java.time.LocalDate;
+import java.time.Year;
+// import java.time.YearMonth;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+
+import seedu.exceptions.DateLimitException;
+import seedu.exceptions.EmptyStringException;
+import seedu.exceptions.ExceptionChecker;
+import seedu.exceptions.YearLimitException;
+import seedu.expenditure.BorrowExpenditure;
+import seedu.expenditure.Expenditure;
+import seedu.expenditure.ExpenditureList;
+import seedu.expenditure.LendExpenditure;
+import seedu.parser.ParseIndividualValue;
+
+public class CheckBudgetCommand extends Command {
+ public static final String COMMAND_WORD = "check";
+ public static final String BLANK = "";
+ public static final String DSLASH = "d/";
+ public static final String YSLASH = "y/";
+ public static final String SLASH = "/";
+ public static final String TSLASH = "t/";
+ private final String userInput;
+ private double totalAmount = 0;
+ private double borrowedAmount = 0;
+ private double lentAmount = 0;
+
+ public CheckBudgetCommand(String userInput) {
+ this.userInput = userInput;
+ }
+
+ @Override
+ public CommandResult execute(ExpenditureList expenditures) {
+ try {
+ String timePeriod = ParseIndividualValue.parseIndividualValue(userInput, BLANK, SLASH);
+ double budget = expenditures.getBudgetSet();
+ switch (timePeriod) {
+ case "d":
+ checkDay(expenditures);
+ return getCheckCommandResult(budget, totalAmount, borrowedAmount, lentAmount);
+ case "y":
+ checkYear(expenditures);
+ return getCheckCommandResult(budget, totalAmount, borrowedAmount, lentAmount);
+ case "c":
+ checkAll(expenditures);
+ return getCheckCommandResult(budget, totalAmount, borrowedAmount, lentAmount);
+ case "t":
+ String category = ParseIndividualValue.parseIndividualValue(userInput, TSLASH, BLANK);
+ return checkCategory(expenditures, category, budget);
+ default:
+ return new CommandResult("Failed to check! Please check the format and try again!");
+ }
+ } catch (DateTimeParseException s) {
+ return new CommandResult("Failed to check! Please check the format and try again!");
+ } catch (StringIndexOutOfBoundsException e) {
+ return new CommandResult("Failed to check! Please check the format and try again!");
+ } catch (EmptyStringException e) {
+ return new CommandResult("Failed to check! Please check the format and try again!");
+ } catch (DateLimitException l) {
+ return new CommandResult(l.getMessage());
+ } catch (YearLimitException y) {
+ return new CommandResult(y.getMessage());
+ }
+ }
+
+ private void checkAll(ExpenditureList expenditures) {
+ for (Expenditure individualExpenditure : expenditures.getExpenditures()) {
+ updateAmount(individualExpenditure);
+ }
+ }
+
+ private CommandResult checkCategory(ExpenditureList expenditures, String category, double budget) {
+ switch (category) {
+ case "academic":
+ checkRespectiveExpenditureType(expenditures, "Acad");
+ return getCheckCommandResult(budget, totalAmount, borrowedAmount, lentAmount);
+ case "accommodation":
+ checkRespectiveExpenditureType(expenditures, "Accom");
+ return getCheckCommandResult(budget, totalAmount, borrowedAmount, lentAmount);
+ case "entertainment":
+ checkRespectiveExpenditureType(expenditures, "En");
+ return getCheckCommandResult(budget, totalAmount, borrowedAmount, lentAmount);
+ case "food":
+ checkRespectiveExpenditureType(expenditures, "F");
+ return getCheckCommandResult(budget, totalAmount, borrowedAmount, lentAmount);
+ case "other":
+ checkRespectiveExpenditureType(expenditures, "O");
+ return getCheckCommandResult(budget, totalAmount, borrowedAmount, lentAmount);
+ case "transport":
+ checkRespectiveExpenditureType(expenditures, "Tr");
+ return getCheckCommandResult(budget, totalAmount, borrowedAmount, lentAmount);
+ case "tuition":
+ checkRespectiveExpenditureType(expenditures, "Tu");
+ return getCheckCommandResult(budget, totalAmount, borrowedAmount, lentAmount);
+ default:
+ return new CommandResult("Category stated does not exist! Please check the format and try again!");
+ }
+ }
+
+ private void checkRespectiveExpenditureType(ExpenditureList expenditures, String category) {
+ for (Expenditure individualExpenditure : expenditures.getExpenditures()) {
+ if (individualExpenditure.getExpenditureType().equals(category)) {
+ updateAmount(individualExpenditure);
+ }
+ }
+ }
+
+ private void updateAmount(Expenditure individualExpenditure) {
+ if (individualExpenditure.getExpenditureType().equals("B")) {
+ borrowedAmount += individualExpenditure.getValue();
+ } else if (individualExpenditure.getExpenditureType().equals("L")) {
+ lentAmount += individualExpenditure.getValue();
+ } else if (individualExpenditure.getPaidIcon().equals("[X]")) {
+ totalAmount += individualExpenditure.getValue();
+ }
+ }
+
+ private static CommandResult getCheckCommandResult(double budget, double totalAmount,
+ double borrowedAmount, double lentAmount) {
+ if (budget == 0) {
+ // Prevents the user from checking when budget is 0, as it does not provide any
+ // insight
+ return new CommandResult("Your current budget is set at 0, please use the 'set' command to set a budget.");
+ } else if (budget >= totalAmount) {
+ // Remaining budget available
+ double difference = budget - totalAmount;
+ return new CommandResult(String.format(
+ "You are $%.2f away from exceeding your budget of $%.2f." + "\n" +
+ "Your current spending stands at: $%.2f" + "\n" + "Sum of money borrowed: $%.2f"
+ + "\n" + "Sum of money lent: $%.2f",
+ difference, budget, totalAmount, borrowedAmount, lentAmount));
+ } else {
+ // Amount of budget exceeded by
+ double difference = totalAmount - budget;
+ return new CommandResult(String.format(
+ "You have exceeded your budget of $%.2f by $%.2f! " + "\n" +
+ "Your current spending stands at: $%.2f " + "\n" + "Sum of money borrowed: $%.2f" + "\n"
+ + "Sum of money lent: $%.2f",
+ budget, difference, totalAmount, borrowedAmount, lentAmount));
+ }
+ }
+
+ /**
+ * @author TzeLoong
+ */
+ private void checkYear(ExpenditureList expenditures) throws EmptyStringException, StringIndexOutOfBoundsException,
+ DateTimeParseException, YearLimitException {
+ Year year = fetchYear();
+ for (Expenditure individualExpenditure : expenditures.getExpenditures()) {
+ fetchYearLendBorrowAmounts(individualExpenditure, year);
+ fetchYearExpenditureAmounts(individualExpenditure, year);
+ }
+ }
+
+ private void checkDay(ExpenditureList expenditures) throws EmptyStringException, DateLimitException {
+ LocalDate dayVal = LocalDate
+ .parse(ParseIndividualValue.parseIndividualValue(userInput, DSLASH, BLANK));
+ ExceptionChecker.checkDateLimit(dayVal);
+ for (Expenditure individualExpenditure : expenditures.getExpenditures()) {
+ fetchDayLendBorrowAmounts(individualExpenditure, dayVal);
+ fetchDayExpenditureAmounts(individualExpenditure, dayVal);
+ }
+ }
+
+ public Year fetchYear()
+ throws EmptyStringException, StringIndexOutOfBoundsException,
+ DateTimeParseException, YearLimitException {
+ DateTimeFormatter formatYear = DateTimeFormatter.ofPattern("uuuu");
+ String yearVal = ParseIndividualValue.parseIndividualValue(userInput, YSLASH,
+ BLANK);
+ ExceptionChecker.checkYearLimit(yearVal);
+ return Year.parse(yearVal, formatYear);
+ }
+
+ public void fetchDayLendBorrowAmounts(Expenditure expenditure, LocalDate dayVal) {
+ if (expenditure instanceof BorrowExpenditure) {
+ LocalDate startDate = ((BorrowExpenditure) expenditure).getDate();
+ LocalDate endDate = ((BorrowExpenditure) expenditure).getDeadline();
+ if (!dayVal.isAfter(endDate) && !dayVal.isBefore(startDate)) {
+ borrowedAmount += expenditure.getValue();
+ }
+ } else if (expenditure instanceof LendExpenditure) {
+ LocalDate startDate = ((LendExpenditure) expenditure).getDate();
+ LocalDate endDate = ((LendExpenditure) expenditure).getDeadline();
+ if (!dayVal.isAfter(endDate) && !dayVal.isBefore(startDate)) {
+ lentAmount += expenditure.getValue();
+ }
+ }
+ }
+
+ public void fetchDayExpenditureAmounts(Expenditure expenditure, LocalDate dayVal) {
+ boolean isLendBorrowExpenditure = (expenditure instanceof BorrowExpenditure ||
+ expenditure instanceof LendExpenditure);
+ if (!isLendBorrowExpenditure && expenditure.getPaidIcon().equals("[X]") &&
+ (expenditure.getDate().equals(dayVal))) {
+ totalAmount += expenditure.getValue();
+ }
+ }
+
+ public void fetchYearLendBorrowAmounts(Expenditure expenditure, Year year) {
+ if (expenditure instanceof BorrowExpenditure) {
+ int startYear = ((BorrowExpenditure) expenditure).getDate().getYear();
+ int endYear = ((BorrowExpenditure) expenditure).getDeadline().getYear();
+ if (year.getValue() >= startYear && year.getValue() <= endYear) {
+ borrowedAmount += expenditure.getValue();
+ }
+ } else if (expenditure instanceof LendExpenditure) {
+ int startTheYear = ((LendExpenditure) expenditure).getDate().getYear();
+ int endTheYear = ((LendExpenditure) expenditure).getDeadline().getYear();
+ if (year.getValue() >= startTheYear && year.getValue() <= endTheYear) {
+ lentAmount += expenditure.getValue();
+ }
+ }
+ }
+
+ public void fetchYearExpenditureAmounts(Expenditure expenditure, Year year) {
+ boolean isLendBorrowExpenditure = (expenditure instanceof BorrowExpenditure ||
+ expenditure instanceof LendExpenditure);
+ if (!isLendBorrowExpenditure && expenditure.getPaidIcon().equals("[X]") &&
+ (expenditure.getDate().getYear() == year
+ .getValue())) {
+ totalAmount += expenditure.getValue();
+ }
+ }
+
+}
diff --git a/src/main/java/seedu/commands/Command.java b/src/main/java/seedu/commands/Command.java
new file mode 100644
index 0000000000..0bf870304e
--- /dev/null
+++ b/src/main/java/seedu/commands/Command.java
@@ -0,0 +1,13 @@
+package seedu.commands;
+
+import seedu.expenditure.ExpenditureList;
+
+public abstract class Command {
+
+ public boolean isExit() {
+ return this instanceof ExitCommand;
+ }
+
+
+ public abstract CommandResult execute(ExpenditureList expenditures);
+}
diff --git a/src/main/java/seedu/commands/CommandResult.java b/src/main/java/seedu/commands/CommandResult.java
new file mode 100644
index 0000000000..c725a05b3b
--- /dev/null
+++ b/src/main/java/seedu/commands/CommandResult.java
@@ -0,0 +1,18 @@
+package seedu.commands;
+
+public class CommandResult {
+ private final String commandResult;
+
+ /**
+ * Constructor.
+ *
+ * @param commandResult result of command executed
+ */
+ public CommandResult(String commandResult) {
+ this.commandResult = commandResult;
+ }
+
+ public String getCommandResult() {
+ return commandResult;
+ }
+}
diff --git a/src/main/java/seedu/commands/DeleteCommand.java b/src/main/java/seedu/commands/DeleteCommand.java
new file mode 100644
index 0000000000..2a4f8400ff
--- /dev/null
+++ b/src/main/java/seedu/commands/DeleteCommand.java
@@ -0,0 +1,26 @@
+package seedu.commands;
+
+import seedu.expenditure.ExpenditureList;
+
+public class DeleteCommand extends Command {
+ // Edit file accordingly
+ public static final String COMMAND_WORD = "delete";
+ public final int index;
+
+ public DeleteCommand(int index) {
+ this.index = index;
+ }
+
+ @Override
+ public CommandResult execute(ExpenditureList expenditures) {
+ try {
+ expenditures.deleteExpenditure(index);
+ return new CommandResult(
+ "Entry has been deleted\n" + "Here is your updated list: \n" + expenditures.toString());
+
+ } catch (IndexOutOfBoundsException e) {
+ return new CommandResult("Index is out of bounds or negative");
+ }
+
+ }
+}
diff --git a/src/main/java/seedu/commands/DuplicateCommand.java b/src/main/java/seedu/commands/DuplicateCommand.java
new file mode 100644
index 0000000000..be823600a6
--- /dev/null
+++ b/src/main/java/seedu/commands/DuplicateCommand.java
@@ -0,0 +1,20 @@
+package seedu.commands;
+import seedu.expenditure.ExpenditureList;
+
+public class DuplicateCommand extends Command {
+
+ public static final String COMMAND_WORD = "duplicate";
+ public final int index;
+ public DuplicateCommand(int index) {
+ this.index = index;
+ }
+ @Override
+ public CommandResult execute(ExpenditureList expenditures) {
+ try {
+ expenditures.duplicateExpenditure(index);
+ return new CommandResult("Duplicated " + expenditures.getExpenditure(index));
+ } catch (IndexOutOfBoundsException e) {
+ return new CommandResult("Index is out of bounds or negative");
+ }
+ }
+}
diff --git a/src/main/java/seedu/commands/EditCommand.java b/src/main/java/seedu/commands/EditCommand.java
new file mode 100644
index 0000000000..e6b8d346fc
--- /dev/null
+++ b/src/main/java/seedu/commands/EditCommand.java
@@ -0,0 +1,161 @@
+package seedu.commands;
+
+import seedu.exceptions.ExceptionChecker;
+import seedu.exceptions.InvalidCharacterInAmount;
+import seedu.exceptions.InvalidDateException;
+import seedu.exceptions.InvalidDeadlineException;
+import seedu.exceptions.NotPositiveValueException;
+import seedu.exceptions.SmallAmountException;
+import seedu.exceptions.WrongPrecisionException;
+import seedu.expenditure.Expenditure;
+import seedu.expenditure.AccommodationExpenditure;
+import seedu.expenditure.TuitionExpenditure;
+import seedu.expenditure.BorrowExpenditure;
+import seedu.expenditure.LendExpenditure;
+import seedu.expenditure.ExpenditureList;
+import seedu.parser.ParseIndividualValue;
+import seedu.exceptions.DateLimitException;
+import seedu.exceptions.EmptyStringException;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeParseException;
+import java.util.Objects;
+
+import static seedu.ui.ErrorMessages.ERROR_INVALID_AMOUNT_PRECISION;
+import static seedu.ui.ErrorMessages.ERROR_NOT_POSITIVE_VALUE_MESSAGE;
+import static seedu.ui.ErrorMessages.ERROR_LACK_OF_PARAMETERS_MESSAGE;
+
+public class EditCommand extends Command {
+ // Edit file accordingly
+ public static final String COMMAND_WORD = "edit";
+ public static final String LEND_EXPENDITURE_TYPE = "L";
+ public static final String BORROW_EXPENDITURE_TYPE = "B";
+
+ public static final String BLANK = "";
+ public static final String DSLASH = "d/";
+ public static final String ASLASH = "a/";
+ public static final String PSLASH = "p/";
+ public static final String BSLASH = "b/";
+ public static final String NSLASH = "n/";
+ public static final String DOT = ".";
+ public final int index;
+ public final String userInput;
+
+ public EditCommand(int index, String userInput) {
+ this.index = index;
+ this.userInput = userInput;
+ }
+
+ @Override
+ public CommandResult execute(ExpenditureList expenditures) {
+ try {
+ Expenditure editedExpenditure = expenditures.getExpenditure(index);
+ boolean isLendOrBorrowExpenditure = filterLendAndBorrow(editedExpenditure);
+ editExpenditure(editedExpenditure, isLendOrBorrowExpenditure);
+ handleLumpSumExpenditure(editedExpenditure, isLendOrBorrowExpenditure);
+ handleLendBorrowExpenditure(editedExpenditure);
+ return new CommandResult(String.format("Edited! Here is the updated list:\n" + expenditures.toString()));
+ } catch (IndexOutOfBoundsException | EmptyStringException | DateTimeParseException | NumberFormatException s) {
+ return new CommandResult(ERROR_LACK_OF_PARAMETERS_MESSAGE.toString());
+ } catch (WrongPrecisionException e) {
+ return new CommandResult(ERROR_INVALID_AMOUNT_PRECISION.toString());
+ } catch (NotPositiveValueException p) {
+ return new CommandResult(ERROR_NOT_POSITIVE_VALUE_MESSAGE.toString());
+ } catch (SmallAmountException | InvalidCharacterInAmount
+ | DateLimitException | InvalidDeadlineException | InvalidDateException e) {
+ return new CommandResult(e.getMessage());
+ }
+ }
+
+ /**
+ * @author TzeLoong
+ */
+ public void editExpenditure(Expenditure editedExpenditure, boolean isLendOrBorrowExpenditure)
+ throws EmptyStringException, WrongPrecisionException, SmallAmountException,
+ InvalidCharacterInAmount, NotPositiveValueException, StringIndexOutOfBoundsException,
+ DateTimeParseException, DateLimitException {
+ LocalDate date = fetchDate(isLendOrBorrowExpenditure);
+ double amount = fetchAmount(isLendOrBorrowExpenditure);
+ String descriptionVal = fetchDescription();
+ editedExpenditure.setDescriptionValueDate(descriptionVal, amount, date);
+ }
+
+ /**
+ * @author Leo Zheng Rui Darren
+ */
+ public void handleLumpSumExpenditure(Expenditure editedExpenditure, boolean isLendOrBorrowExpenditure)
+ throws EmptyStringException, StringIndexOutOfBoundsException, DateTimeParseException, DateLimitException {
+ if (editedExpenditure instanceof AccommodationExpenditure) {
+ LocalDate firstDate = fetchDate(isLendOrBorrowExpenditure);
+ ((AccommodationExpenditure) editedExpenditure).setRepeatDate(firstDate);
+ } else if (editedExpenditure instanceof TuitionExpenditure) {
+ LocalDate firstDate = fetchDate(isLendOrBorrowExpenditure);
+ ((TuitionExpenditure) editedExpenditure).setRepeatDate(firstDate);
+ }
+ }
+
+ /**
+ * @author TzeLoong
+ */
+ public void handleLendBorrowExpenditure(Expenditure editedExpenditure) throws EmptyStringException,
+ StringIndexOutOfBoundsException, DateTimeParseException, InvalidDateException, InvalidDeadlineException {
+ if (editedExpenditure instanceof LendExpenditure) {
+ String name = fetchName();
+ LocalDate deadline = fetchDeadline();
+ ((LendExpenditure) editedExpenditure).setLenderNameAndDeadline(name, deadline);
+
+ } else if (editedExpenditure instanceof BorrowExpenditure) {
+ String name = fetchName();
+ LocalDate deadline = fetchDeadline();
+ ((BorrowExpenditure) editedExpenditure).setBorrowerNameAndDeadline(name, deadline);
+ }
+ }
+
+ public boolean filterLendAndBorrow(Expenditure expenditure) {
+ return Objects.equals(expenditure.getExpenditureType(), LEND_EXPENDITURE_TYPE)
+ || Objects.equals(expenditure.getExpenditureType(), BORROW_EXPENDITURE_TYPE);
+ }
+
+ public LocalDate fetchDate(boolean isLendOrBorrowExpenditure)
+ throws EmptyStringException, StringIndexOutOfBoundsException,
+ DateTimeParseException, DateLimitException {
+ String dateVal = ParseIndividualValue.parseIndividualValue(userInput, DSLASH,
+ isLendOrBorrowExpenditure ? NSLASH : ASLASH);
+ LocalDate date = LocalDate.parse(dateVal);
+ ExceptionChecker.checkDateLimit(date);
+ return date;
+ }
+
+ public double fetchAmount(boolean isLendOrBorrowExpenditure)
+ throws StringIndexOutOfBoundsException, EmptyStringException,
+ WrongPrecisionException, InvalidCharacterInAmount, NumberFormatException, NotPositiveValueException,
+ SmallAmountException {
+ String amountVal = ParseIndividualValue.parseIndividualValue(userInput, ASLASH,
+ isLendOrBorrowExpenditure ? BSLASH : PSLASH);
+ ExceptionChecker.checkIfMoreThanTwoDecimalPlaces(amountVal, DOT, BLANK);
+ ExceptionChecker.checkValidDoubleInput(amountVal);
+ ExceptionChecker.checkValidAmount(Double.parseDouble(amountVal));
+ return Double.parseDouble(amountVal);
+ }
+
+ public String fetchDescription()
+ throws EmptyStringException, StringIndexOutOfBoundsException {
+ return ParseIndividualValue.parseIndividualValue(userInput, PSLASH, BLANK);
+ }
+
+ public String fetchName()
+ throws EmptyStringException, StringIndexOutOfBoundsException {
+ return ParseIndividualValue.parseIndividualValue(userInput, NSLASH, ASLASH);
+ }
+
+ public LocalDate fetchDeadline()
+ throws EmptyStringException, StringIndexOutOfBoundsException,
+ DateTimeParseException, InvalidDateException, InvalidDeadlineException {
+ String deadlineVal = ParseIndividualValue.parseIndividualValue(userInput, BSLASH, PSLASH);
+ LocalDate deadline = LocalDate.parse(deadlineVal);
+ String dateVal = ParseIndividualValue.parseIndividualValue(userInput, DSLASH, NSLASH);
+ LocalDate date = LocalDate.parse(dateVal);
+ ExceptionChecker.checkDate(date, deadline);
+ return deadline;
+ }
+}
diff --git a/src/main/java/seedu/commands/EntertainmentExpenditureCommand.java b/src/main/java/seedu/commands/EntertainmentExpenditureCommand.java
new file mode 100644
index 0000000000..87e77ba34e
--- /dev/null
+++ b/src/main/java/seedu/commands/EntertainmentExpenditureCommand.java
@@ -0,0 +1,30 @@
+package seedu.commands;
+
+import seedu.expenditure.EntertainmentExpenditure;
+import seedu.expenditure.ExpenditureList;
+
+import java.time.LocalDate;
+
+public class EntertainmentExpenditureCommand extends Command {
+ public static final String COMMAND_WORD = "entertainment";
+ private final String entertainmentExpenditureDescription;
+ private final double entertainmentExpenditureValue;
+ private final LocalDate entertainmentExpenditureDate;
+
+ public EntertainmentExpenditureCommand(String description, double value, LocalDate date) {
+ this.entertainmentExpenditureDescription = description;
+ this.entertainmentExpenditureValue = value;
+ this.entertainmentExpenditureDate = date;
+ }
+
+ public CommandResult execute(ExpenditureList expenditures) {
+ EntertainmentExpenditure entertainmentExpenditure = new EntertainmentExpenditure(
+ entertainmentExpenditureDescription,
+ entertainmentExpenditureValue,
+ entertainmentExpenditureDate);
+ expenditures.addExpenditure(entertainmentExpenditure);
+ return new CommandResult(String.format("Added %s expenditure: %s",
+ COMMAND_WORD, entertainmentExpenditure.toString()));
+ }
+
+}
diff --git a/src/main/java/seedu/commands/ExitCommand.java b/src/main/java/seedu/commands/ExitCommand.java
new file mode 100644
index 0000000000..68820681ac
--- /dev/null
+++ b/src/main/java/seedu/commands/ExitCommand.java
@@ -0,0 +1,19 @@
+package seedu.commands;
+
+import seedu.expenditure.ExpenditureList;
+
+public class ExitCommand extends Command {
+ public static final String COMMAND_WORD = "exit";
+
+ /**
+ * Contructor.
+ */
+ public ExitCommand() {
+ }
+
+ @Override
+ public CommandResult execute(ExpenditureList expenditures) {
+ return new CommandResult(
+ "Goodbye. Your progress towards your financial goals is important to us. See you again soon!");
+ }
+}
diff --git a/src/main/java/seedu/commands/FindCommand.java b/src/main/java/seedu/commands/FindCommand.java
new file mode 100644
index 0000000000..957f406d41
--- /dev/null
+++ b/src/main/java/seedu/commands/FindCommand.java
@@ -0,0 +1,35 @@
+package seedu.commands;
+
+import seedu.expenditure.Expenditure;
+import seedu.expenditure.ExpenditureList;
+
+public class FindCommand extends Command {
+ public static final String COMMAND_WORD = "find";
+ private String keyword;
+
+ public FindCommand(String keyword) {
+ this.keyword = keyword;
+ }
+
+ @Override
+ public CommandResult execute(ExpenditureList expenditures) {
+ int numberOfMatchingTasks = 0;
+ for (int i = 0; i < expenditures.getSize(); i++) {
+ Expenditure currentExpenditure = expenditures.getExpenditure(i);
+ if (currentExpenditure.getDescription().contains(keyword)) {
+ numberOfMatchingTasks++;
+ System.out.println(currentExpenditure.toString());
+ }
+ }
+
+ if (numberOfMatchingTasks > 0) {
+ return new CommandResult(
+ "Total of " + numberOfMatchingTasks + " records were found");
+ }
+
+ return new CommandResult(
+ "There are no matching records in this list");
+
+ }
+
+}
diff --git a/src/main/java/seedu/commands/FoodExpenditureCommand.java b/src/main/java/seedu/commands/FoodExpenditureCommand.java
new file mode 100644
index 0000000000..b3ce5204e6
--- /dev/null
+++ b/src/main/java/seedu/commands/FoodExpenditureCommand.java
@@ -0,0 +1,29 @@
+package seedu.commands;
+
+import seedu.expenditure.ExpenditureList;
+import seedu.expenditure.FoodExpenditure;
+
+import java.time.LocalDate;
+
+public class FoodExpenditureCommand extends Command {
+ public static final String COMMAND_WORD = "food";
+ private final String foodExpenditureDescription;
+ private final double foodExpenditureValue;
+ private final LocalDate foodExpenditureDate;
+
+ public FoodExpenditureCommand(String description, double value, LocalDate date) {
+ this.foodExpenditureDescription = description;
+ this.foodExpenditureValue = value;
+ this.foodExpenditureDate = date;
+ }
+
+ public CommandResult execute(ExpenditureList expenditures) {
+ FoodExpenditure foodExpenditure = new FoodExpenditure(
+ foodExpenditureDescription,
+ foodExpenditureValue,
+ foodExpenditureDate);
+ expenditures.addExpenditure(foodExpenditure);
+ return new CommandResult(String.format("Added %s expenditure: %s",
+ COMMAND_WORD, foodExpenditure.toString()));
+ }
+}
diff --git a/src/main/java/seedu/commands/HelpCommand.java b/src/main/java/seedu/commands/HelpCommand.java
new file mode 100644
index 0000000000..96235b0c56
--- /dev/null
+++ b/src/main/java/seedu/commands/HelpCommand.java
@@ -0,0 +1,17 @@
+package seedu.commands;
+
+import seedu.expenditure.ExpenditureList;
+import seedu.ui.Ui;
+
+public class HelpCommand extends Command {
+ // Edit file accordingly
+ public static final String COMMAND_WORD = "help";
+
+ public HelpCommand() {
+ }
+
+ @Override
+ public CommandResult execute(ExpenditureList expenditures) {
+ return new CommandResult(Ui.HELP_PAGE);
+ }
+}
diff --git a/src/main/java/seedu/commands/InvalidCommand.java b/src/main/java/seedu/commands/InvalidCommand.java
new file mode 100644
index 0000000000..2e50357717
--- /dev/null
+++ b/src/main/java/seedu/commands/InvalidCommand.java
@@ -0,0 +1,17 @@
+package seedu.commands;
+
+import seedu.expenditure.ExpenditureList;
+
+public class InvalidCommand extends Command {
+ // Edit file accordingly
+ public final String commandError;
+
+ public InvalidCommand(String commandError) {
+ this.commandError = commandError;
+ }
+
+ @Override
+ public CommandResult execute(ExpenditureList expenditures) {
+ return new CommandResult(commandError);
+ }
+}
diff --git a/src/main/java/seedu/commands/LendExpenditureCommand.java b/src/main/java/seedu/commands/LendExpenditureCommand.java
new file mode 100644
index 0000000000..7dea17f7b2
--- /dev/null
+++ b/src/main/java/seedu/commands/LendExpenditureCommand.java
@@ -0,0 +1,31 @@
+package seedu.commands;
+
+import seedu.expenditure.LendExpenditure;
+import seedu.expenditure.ExpenditureList;
+
+import java.time.LocalDate;
+
+public class LendExpenditureCommand extends Command {
+ public static final String COMMAND_WORD = "lend";
+ private final String lendExpenditureDescription;
+ private final String lendExpenditureName;
+ private final double lendExpenditureValue;
+ private final LocalDate lendExpenditureDate;
+ private final LocalDate lendDeadline;
+
+ public LendExpenditureCommand(String description, String lenderName, double value, LocalDate date,
+ LocalDate borrowDeadline) {
+ this.lendExpenditureDescription = description;
+ this.lendExpenditureName = lenderName;
+ this.lendExpenditureValue = value;
+ this.lendExpenditureDate = date;
+ this.lendDeadline = borrowDeadline;
+ }
+
+ public CommandResult execute(ExpenditureList expenditures) {
+ LendExpenditure lendExpenditure = new LendExpenditure(lendExpenditureDescription,
+ lendExpenditureName, lendExpenditureValue, lendExpenditureDate, lendDeadline);
+ expenditures.addExpenditure(lendExpenditure);
+ return new CommandResult(String.format("Added %s expenditure: %s", COMMAND_WORD, lendExpenditure.toString()));
+ }
+}
diff --git a/src/main/java/seedu/commands/ListExpenditureCommand.java b/src/main/java/seedu/commands/ListExpenditureCommand.java
new file mode 100644
index 0000000000..39270689fc
--- /dev/null
+++ b/src/main/java/seedu/commands/ListExpenditureCommand.java
@@ -0,0 +1,29 @@
+package seedu.commands;
+
+import seedu.exceptions.WrongInputException;
+import seedu.expenditure.CurrencyValue;
+import seedu.expenditure.ExpenditureList;
+
+public class ListExpenditureCommand extends Command {
+ // Edit file accordingly
+ public static final String COMMAND_WORD = "list";
+ private String currency;
+
+ public ListExpenditureCommand(String currency) throws WrongInputException {
+ if (CurrencyValue.isValidCurrency(currency)) {
+ this.currency = currency;
+ } else {
+ throw new WrongInputException();
+ }
+ }
+
+ @Override
+ public CommandResult execute(ExpenditureList expenditures) {
+ if(expenditures.getSize() == 0) {
+ return new CommandResult("Your list is currently empty. Start adding some transactions now!\n");
+ }
+
+ return new CommandResult("Here is your list of expenditures in " + currency + ": \n"
+ + expenditures.listString(currency));
+ }
+}
diff --git a/src/main/java/seedu/commands/MarkCommand.java b/src/main/java/seedu/commands/MarkCommand.java
new file mode 100644
index 0000000000..6c189cb5ab
--- /dev/null
+++ b/src/main/java/seedu/commands/MarkCommand.java
@@ -0,0 +1,30 @@
+package seedu.commands;
+
+import seedu.exceptions.AlreadyMarkException;
+import seedu.exceptions.NoPaidFieldException;
+import seedu.expenditure.ExpenditureList;
+
+public class MarkCommand extends Command {
+ public static final String COMMAND_WORD = "mark";
+ public final int index;
+
+ public MarkCommand(int index) {
+ this.index = index;
+ }
+
+ @Override
+ public CommandResult execute(ExpenditureList expenditures) {
+ try {
+ expenditures.markExpenditure(index);
+ return new CommandResult(
+ "Marked your expenditure!\n" + expenditures.getExpenditure(index));
+ } catch (IndexOutOfBoundsException e) {
+ return new CommandResult("Index is out of bounds or negative");
+ } catch (NoPaidFieldException e) {
+ return new CommandResult(e.getMessage());
+ } catch (AlreadyMarkException e) {
+ return new CommandResult(e.getMessage());
+ }
+
+ }
+}
diff --git a/src/main/java/seedu/commands/OtherExpenditureCommand.java b/src/main/java/seedu/commands/OtherExpenditureCommand.java
new file mode 100644
index 0000000000..99bb047900
--- /dev/null
+++ b/src/main/java/seedu/commands/OtherExpenditureCommand.java
@@ -0,0 +1,26 @@
+package seedu.commands;
+
+import seedu.expenditure.ExpenditureList;
+import seedu.expenditure.OtherExpenditure;
+
+import java.time.LocalDate;
+
+public class OtherExpenditureCommand extends Command {
+ public static final String COMMAND_WORD = "other";
+ private final String otherExpenditureDescription;
+ private final double otherExpenditureValue;
+ private final LocalDate otherExpenditureDate;
+
+ public OtherExpenditureCommand(String description, double value, LocalDate date) {
+ this.otherExpenditureDescription = description;
+ this.otherExpenditureValue = value;
+ this.otherExpenditureDate = date;
+ }
+
+ public CommandResult execute(ExpenditureList expenditures) {
+ OtherExpenditure otherExpenditure = new OtherExpenditure(otherExpenditureDescription,
+ otherExpenditureValue, otherExpenditureDate);
+ expenditures.addExpenditure(otherExpenditure);
+ return new CommandResult(String.format("Added %s expenditure: %s", COMMAND_WORD, otherExpenditure.toString()));
+ }
+}
diff --git a/src/main/java/seedu/commands/SetBudgetCommand.java b/src/main/java/seedu/commands/SetBudgetCommand.java
new file mode 100644
index 0000000000..8ed1ca1d72
--- /dev/null
+++ b/src/main/java/seedu/commands/SetBudgetCommand.java
@@ -0,0 +1,18 @@
+package seedu.commands;
+
+import seedu.expenditure.ExpenditureList;
+
+public class SetBudgetCommand extends Command{
+
+ public static final String COMMAND_WORD = "set";
+ public final double budget;
+ public SetBudgetCommand(double budget) {
+ this.budget = budget;
+ }
+
+ @Override
+ public CommandResult execute(ExpenditureList expenditures) {
+ expenditures.setNewBudget(budget);
+ return new CommandResult("New budget of " + expenditures.getBudgetSet() + " has been set!");
+ }
+}
diff --git a/src/main/java/seedu/commands/ShowRatesCommand.java b/src/main/java/seedu/commands/ShowRatesCommand.java
new file mode 100644
index 0000000000..5ccb6182ff
--- /dev/null
+++ b/src/main/java/seedu/commands/ShowRatesCommand.java
@@ -0,0 +1,17 @@
+package seedu.commands;
+
+import seedu.expenditure.ExpenditureList;
+import seedu.expenditure.CurrencyValue;
+
+public class ShowRatesCommand extends Command {
+ public static final String COMMAND_WORD = "showrates";
+
+ public ShowRatesCommand() {
+ }
+
+ @Override
+ public CommandResult execute(ExpenditureList expenditures) {
+
+ return new CommandResult(CurrencyValue.getRates());
+ }
+}
diff --git a/src/main/java/seedu/commands/SortCommand.java b/src/main/java/seedu/commands/SortCommand.java
new file mode 100644
index 0000000000..d6316c6d7e
--- /dev/null
+++ b/src/main/java/seedu/commands/SortCommand.java
@@ -0,0 +1,55 @@
+package seedu.commands;
+
+import seedu.expenditure.Expenditure;
+import seedu.expenditure.ExpenditureList;
+
+import java.util.ArrayList;
+
+public class SortCommand extends Command {
+ public static final String COMMAND_WORD = "sort";
+ public static final String AMOUNT_ASCENDING = "ascend";
+ public static final String AMOUNT_DESCENDING = "descend";
+ public static final String DATE_FROM_LATEST = "latest";
+ public static final String DATE_FROM_EARLIEST = "earliest";
+ private final String sortType;
+
+ public SortCommand(String sortType) {
+ this.sortType = sortType;
+ }
+
+ @Override
+ public CommandResult execute(ExpenditureList expenditures) {
+ ArrayList sortedList;
+ ExpenditureList sortedExpenditures;
+ if(expenditures.getSize() == 0) {
+ return new CommandResult("Unable to sort as list is currently empty!");
+ }
+ switch (sortType) {
+ case AMOUNT_ASCENDING:
+ sortedList = ExpenditureList.sortList(sortType);
+ sortedExpenditures = ExpenditureList.getSortedExpenditures(sortedList);
+ return new CommandResult("Here is your list of expenditures sorted in ascending amount: \n"
+ + sortedExpenditures);
+ case AMOUNT_DESCENDING:
+ sortedList = ExpenditureList.sortList(sortType);
+ sortedExpenditures = ExpenditureList.getSortedExpenditures(sortedList);
+ return new CommandResult("Here is your list of expenditures sorted in descending amount: \n"
+ + sortedExpenditures);
+ case DATE_FROM_EARLIEST:
+ sortedList = ExpenditureList.sortList(sortType);
+ sortedExpenditures = ExpenditureList.getSortedExpenditures(sortedList);
+ return new CommandResult("Here is your list of expenditures sorted from the earliest date: \n"
+ + sortedExpenditures);
+ case DATE_FROM_LATEST:
+ sortedList = ExpenditureList.sortList(sortType);
+ sortedExpenditures = ExpenditureList.getSortedExpenditures(sortedList);
+ return new CommandResult("Here is your list of expenditures sorted from the latest date: \n"
+ + sortedExpenditures);
+ default:
+ return new CommandResult(" to sort amount in ascending order. " +
+ " to sort amount in descending order.\n" +
+ " to sort amount from earliest date added. " +
+ " to sort amount from latest date added.");
+ }
+ }
+}
diff --git a/src/main/java/seedu/commands/TransportExpenditureCommand.java b/src/main/java/seedu/commands/TransportExpenditureCommand.java
new file mode 100644
index 0000000000..5ef043e72a
--- /dev/null
+++ b/src/main/java/seedu/commands/TransportExpenditureCommand.java
@@ -0,0 +1,29 @@
+package seedu.commands;
+
+import seedu.expenditure.TransportExpenditure;
+import seedu.expenditure.ExpenditureList;
+
+import java.time.LocalDate;
+
+public class TransportExpenditureCommand extends Command{
+ public static final String COMMAND_WORD = "transport";
+ private final String transportExpenditureDescription;
+ private final double transportExpenditureValue;
+ private final LocalDate transportExpenditureDate;
+
+ public TransportExpenditureCommand(String description, double value, LocalDate date) {
+ this.transportExpenditureDescription = description;
+ this.transportExpenditureValue = value;
+ this.transportExpenditureDate = date;
+ }
+
+ public CommandResult execute(ExpenditureList expenditures) {
+ TransportExpenditure transportExpenditure = new TransportExpenditure(
+ transportExpenditureDescription,
+ transportExpenditureValue,
+ transportExpenditureDate);
+ expenditures.addExpenditure(transportExpenditure);
+ return new CommandResult(String.format("Added %s expenditure: %s",
+ COMMAND_WORD, transportExpenditure.toString()));
+ }
+}
diff --git a/src/main/java/seedu/commands/TuitionExpenditureCommand.java b/src/main/java/seedu/commands/TuitionExpenditureCommand.java
new file mode 100644
index 0000000000..00ec7edd00
--- /dev/null
+++ b/src/main/java/seedu/commands/TuitionExpenditureCommand.java
@@ -0,0 +1,29 @@
+package seedu.commands;
+
+import seedu.expenditure.ExpenditureList;
+import seedu.expenditure.TuitionExpenditure;
+
+import java.time.LocalDate;
+
+public class TuitionExpenditureCommand extends Command{
+ public static final String COMMAND_WORD = "tuition";
+ private final String tuitionExpenditureDescription;
+ private final double tuitionExpenditureValue;
+ private final LocalDate tuitionExpenditureDate;
+ private final LocalDate tuitionExpenditureRepeatDate;
+
+ public TuitionExpenditureCommand(String description, double value, LocalDate date, LocalDate repeatDate) {
+ this.tuitionExpenditureDescription = description;
+ this.tuitionExpenditureValue = value;
+ this.tuitionExpenditureDate = date;
+ this.tuitionExpenditureRepeatDate = repeatDate;
+ }
+
+ public CommandResult execute(ExpenditureList expenditures) {
+ TuitionExpenditure tuitionExpenditure = new TuitionExpenditure(tuitionExpenditureDescription,
+ tuitionExpenditureValue, tuitionExpenditureDate, tuitionExpenditureRepeatDate);
+ expenditures.addExpenditure(tuitionExpenditure);
+ return new CommandResult(String.format("Added %s expenditure: %s", COMMAND_WORD,
+ tuitionExpenditure.toString()));
+ }
+}
diff --git a/src/main/java/seedu/commands/UnmarkCommand.java b/src/main/java/seedu/commands/UnmarkCommand.java
new file mode 100644
index 0000000000..3ab965d9cc
--- /dev/null
+++ b/src/main/java/seedu/commands/UnmarkCommand.java
@@ -0,0 +1,29 @@
+package seedu.commands;
+
+import seedu.exceptions.AlreadyUnmarkException;
+import seedu.exceptions.NoPaidFieldException;
+import seedu.expenditure.ExpenditureList;
+
+public class UnmarkCommand extends Command {
+ public static final String COMMAND_WORD = "unmark";
+ public final int index;
+
+ public UnmarkCommand(int index) {
+ this.index = index;
+ }
+
+ @Override
+ public CommandResult execute(ExpenditureList expenditures) {
+ try {
+ expenditures.unmarkExpenditure(index);
+ return new CommandResult(
+ "Unmarked your expenditure!\n" + expenditures.getExpenditure(index));
+ } catch (IndexOutOfBoundsException e) {
+ return new CommandResult("Index is out of bounds or negative");
+ } catch (NoPaidFieldException e) {
+ return new CommandResult(e.getMessage());
+ } catch (AlreadyUnmarkException e) {
+ return new CommandResult(e.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/seedu/commands/ViewDateExpenditureCommand.java b/src/main/java/seedu/commands/ViewDateExpenditureCommand.java
new file mode 100644
index 0000000000..20520970ea
--- /dev/null
+++ b/src/main/java/seedu/commands/ViewDateExpenditureCommand.java
@@ -0,0 +1,32 @@
+package seedu.commands;
+
+import seedu.exceptions.DateLimitException;
+import seedu.exceptions.ExceptionChecker;
+import seedu.exceptions.WrongInputException;
+import seedu.expenditure.CurrencyValue;
+import seedu.expenditure.ExpenditureList;
+
+import java.time.LocalDate;
+
+public class ViewDateExpenditureCommand extends Command {
+ public static final String COMMAND_WORD = "viewdate";
+ private final LocalDate date;
+ private final String currency;
+
+ public ViewDateExpenditureCommand(String userInput) throws WrongInputException, DateLimitException {
+ String[] splitValues = userInput.split(" ");
+ if (CurrencyValue.isValidCurrency(splitValues[1])) {
+ currency = splitValues[1];
+ } else {
+ throw new WrongInputException();
+ }
+ LocalDate currentDate = LocalDate.parse(splitValues[0]);
+ ExceptionChecker.checkDateLimit(currentDate);
+ this.date = currentDate;
+ }
+
+ @Override
+ public CommandResult execute(ExpenditureList expenditures) {
+ return new CommandResult(ExpenditureList.specificDateString(date, currency));
+ }
+}
diff --git a/src/main/java/seedu/commands/ViewTypeExpenditureCommand.java b/src/main/java/seedu/commands/ViewTypeExpenditureCommand.java
new file mode 100644
index 0000000000..008eb845b6
--- /dev/null
+++ b/src/main/java/seedu/commands/ViewTypeExpenditureCommand.java
@@ -0,0 +1,67 @@
+package seedu.commands;
+
+import seedu.exceptions.WrongInputException;
+import seedu.expenditure.ExpenditureList;
+import seedu.expenditure.AcademicExpenditure;
+import seedu.expenditure.AccommodationExpenditure;
+import seedu.expenditure.BorrowExpenditure;
+import seedu.expenditure.EntertainmentExpenditure;
+import seedu.expenditure.FoodExpenditure;
+import seedu.expenditure.LendExpenditure;
+import seedu.expenditure.OtherExpenditure;
+import seedu.expenditure.TransportExpenditure;
+import seedu.expenditure.TuitionExpenditure;
+import seedu.expenditure.CurrencyValue;
+
+public class ViewTypeExpenditureCommand extends Command {
+ public static final String COMMAND_WORD = "viewtype";
+ private final String expenditureType;
+ private final String currency;
+
+ public ViewTypeExpenditureCommand(String userInput) throws WrongInputException {
+ String[] splitValues = userInput.split(" ");
+ if (CurrencyValue.isValidCurrency(splitValues[1])) {
+ currency = splitValues[1];
+ } else {
+ throw new WrongInputException();
+ }
+
+ switch (splitValues[0]) {
+ case "academic":
+ expenditureType = AcademicExpenditure.EXPENDITURE_TYPE;
+ break;
+ case "accommodation":
+ expenditureType = AccommodationExpenditure.EXPENDITURE_TYPE;
+ break;
+ case "borrow":
+ expenditureType = BorrowExpenditure.EXPENDITURE_TYPE;
+ break;
+ case "entertainment":
+ expenditureType = EntertainmentExpenditure.EXPENDITURE_TYPE;
+ break;
+ case "food":
+ expenditureType = FoodExpenditure.EXPENDITURE_TYPE;
+ break;
+ case "lend":
+ expenditureType = LendExpenditure.EXPENDITURE_TYPE;
+ break;
+ case "other":
+ expenditureType = OtherExpenditure.EXPENDITURE_TYPE;
+ break;
+ case "transport":
+ expenditureType = TransportExpenditure.EXPENDITURE_TYPE;
+ break;
+ case "tuition":
+ expenditureType = TuitionExpenditure.EXPENDITURE_TYPE;
+ break;
+ default:
+ throw new WrongInputException();
+ }
+ }
+
+ @Override
+ public CommandResult execute(ExpenditureList expenditures) {
+ return new CommandResult("Here are the specified expenditures in " + currency + ": \n"
+ + ExpenditureList.specificTypeString(expenditureType, currency));
+ }
+}
diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java
deleted file mode 100644
index 5c74e68d59..0000000000
--- a/src/main/java/seedu/duke/Duke.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package seedu.duke;
-
-import java.util.Scanner;
-
-public class Duke {
- /**
- * Main entry-point for the java.duke.Duke application.
- */
- public static void main(String[] args) {
- String logo = " ____ _ \n"
- + "| _ \\ _ _| | _____ \n"
- + "| | | | | | | |/ / _ \\\n"
- + "| |_| | |_| | < __/\n"
- + "|____/ \\__,_|_|\\_\\___|\n";
- System.out.println("Hello from\n" + logo);
- System.out.println("What is your name?");
-
- Scanner in = new Scanner(System.in);
- System.out.println("Hello " + in.nextLine());
- }
-}
diff --git a/src/main/java/seedu/exceptions/AlreadyMarkException.java b/src/main/java/seedu/exceptions/AlreadyMarkException.java
new file mode 100644
index 0000000000..60ffda7111
--- /dev/null
+++ b/src/main/java/seedu/exceptions/AlreadyMarkException.java
@@ -0,0 +1,13 @@
+package seedu.exceptions;
+
+import static seedu.ui.ErrorMessages.ERROR_MARK_MESSAGE;
+
+public class AlreadyMarkException extends Exception {
+
+ @Override
+ public String getMessage() {
+ return ERROR_MARK_MESSAGE.toString();
+ }
+
+}
+
diff --git a/src/main/java/seedu/exceptions/AlreadyUnmarkException.java b/src/main/java/seedu/exceptions/AlreadyUnmarkException.java
new file mode 100644
index 0000000000..464456b531
--- /dev/null
+++ b/src/main/java/seedu/exceptions/AlreadyUnmarkException.java
@@ -0,0 +1,10 @@
+package seedu.exceptions;
+
+import static seedu.ui.ErrorMessages.ERROR_UNMARK_MESSAGE;
+
+public class AlreadyUnmarkException extends Exception {
+ @Override
+ public String getMessage() {
+ return ERROR_UNMARK_MESSAGE.toString();
+ }
+}
diff --git a/src/main/java/seedu/exceptions/DateLimitException.java b/src/main/java/seedu/exceptions/DateLimitException.java
new file mode 100644
index 0000000000..bbc73be412
--- /dev/null
+++ b/src/main/java/seedu/exceptions/DateLimitException.java
@@ -0,0 +1,10 @@
+package seedu.exceptions;
+
+import static seedu.ui.ErrorMessages.ERROR_DATE_LIMIT_MESSAGE;
+
+public class DateLimitException extends Exception {
+ @Override
+ public String getMessage() {
+ return ERROR_DATE_LIMIT_MESSAGE.toString();
+ }
+}
diff --git a/src/main/java/seedu/exceptions/EmptyStringException.java b/src/main/java/seedu/exceptions/EmptyStringException.java
new file mode 100644
index 0000000000..c1a88b8413
--- /dev/null
+++ b/src/main/java/seedu/exceptions/EmptyStringException.java
@@ -0,0 +1,10 @@
+package seedu.exceptions;
+
+import static seedu.ui.ErrorMessages.ERROR_EMPTY_STRING_MESSAGE;
+
+public class EmptyStringException extends Exception {
+ @Override
+ public String getMessage() {
+ return ERROR_EMPTY_STRING_MESSAGE.toString();
+ }
+}
diff --git a/src/main/java/seedu/exceptions/ExceptionChecker.java b/src/main/java/seedu/exceptions/ExceptionChecker.java
new file mode 100644
index 0000000000..7d4a31c140
--- /dev/null
+++ b/src/main/java/seedu/exceptions/ExceptionChecker.java
@@ -0,0 +1,120 @@
+package seedu.exceptions;
+
+import java.time.LocalDate;
+
+import seedu.expenditure.AccommodationExpenditure;
+import seedu.expenditure.Expenditure;
+import seedu.expenditure.TuitionExpenditure;
+import seedu.parser.ParseIndividualValue;
+
+public class ExceptionChecker {
+ public static final int TWO_DECIMAL_PLACE = 2;
+ public static final String START_DATE = "2000-01-01";
+ public static final double START_YEAR = 2000;
+
+ public static void checkEmptyString(String string) throws EmptyStringException {
+ if (string.isEmpty()) {
+ throw new EmptyStringException();
+ }
+ }
+
+ public static void checkValidAmount(double amountVal) throws NotPositiveValueException, SmallAmountException {
+ if (amountVal <= 0) {
+ throw new NotPositiveValueException();
+ } else if (amountVal < 0.01) {
+ throw new SmallAmountException();
+ }
+ }
+
+ public static void checkValidDoubleInput(String amountVal) throws InvalidCharacterInAmount {
+ String lowerCaseAmountVal = amountVal.toLowerCase();
+ boolean containsSpecialCharacter = (lowerCaseAmountVal.contains("f") || lowerCaseAmountVal.contains("d") ||
+ lowerCaseAmountVal.contains("e"));
+ if (containsSpecialCharacter) {
+ throw new InvalidCharacterInAmount();
+ }
+ }
+
+ public static void check(String amountVal) throws InvalidCharacterInAmount {
+ String lowerCaseAmountVal = amountVal.toLowerCase();
+ boolean containsSpecialCharacter = (lowerCaseAmountVal.contains("f") || lowerCaseAmountVal.contains("d") ||
+ lowerCaseAmountVal.contains("e"));
+ if (containsSpecialCharacter) {
+ throw new InvalidCharacterInAmount();
+ }
+ }
+
+ public static void checkDate(LocalDate startDate, LocalDate endDate)
+ throws InvalidDateException, InvalidDeadlineException {
+ LocalDate currentDate = LocalDate.now();
+ if (startDate.isAfter(endDate) || startDate.equals(endDate)) {
+ throw new InvalidDateException();
+ }
+ if (endDate.isBefore(currentDate)) {
+ throw new InvalidDeadlineException();
+ }
+ }
+
+ public static void checkDateLimit(LocalDate currentDate)
+ throws DateLimitException {
+ LocalDate firstdate = LocalDate.parse(START_DATE);
+ if (currentDate.isBefore(firstdate)) {
+ throw new DateLimitException();
+ }
+ }
+
+ public static void checkYearLimit(String currentDate)
+ throws YearLimitException {
+ if (Double.parseDouble(currentDate) < START_YEAR) {
+ throw new YearLimitException();
+ }
+ }
+
+ public static void checkAlreadyMark(Expenditure expenditure) throws AlreadyMarkException {
+ if (expenditure instanceof TuitionExpenditure) {
+ boolean isAlreadyPaidTuition = ((TuitionExpenditure) expenditure).getPaidIcon()
+ .equals(TuitionExpenditure.iconPaid);
+ if (isAlreadyPaidTuition) {
+ throw new AlreadyMarkException();
+ }
+ } else if (expenditure instanceof AccommodationExpenditure) {
+ boolean isAlreadyPaidTuition = ((AccommodationExpenditure) expenditure).getStatusIcon()
+ .equals(TuitionExpenditure.iconPaid);
+ if (isAlreadyPaidTuition) {
+ throw new AlreadyMarkException();
+ }
+ }
+ }
+
+ public static void checkAlreadyUnmark(Expenditure expenditure) throws AlreadyUnmarkException {
+ if (expenditure instanceof TuitionExpenditure) {
+ boolean isAlreadyPaidTuition = ((TuitionExpenditure) expenditure).getPaidIcon()
+ .equals(TuitionExpenditure.iconUnpaid);
+ if (isAlreadyPaidTuition) {
+ throw new AlreadyUnmarkException();
+ }
+ } else if (expenditure instanceof AccommodationExpenditure) {
+ boolean isAlreadyPaidTuition = ((AccommodationExpenditure) expenditure).getStatusIcon()
+ .equals(TuitionExpenditure.iconUnpaid);
+ if (isAlreadyPaidTuition) {
+ throw new AlreadyUnmarkException();
+ }
+ }
+ }
+
+ public static void checkIfMoreThanTwoDecimalPlaces(String userInput, String dot, String blank)
+ throws WrongPrecisionException, EmptyStringException {
+ if (userInput.contains(dot)) {
+ String twoDecimalPlaces = ParseIndividualValue.parseIndividualValue(userInput, dot, blank);
+ if (twoDecimalPlaces.length() > TWO_DECIMAL_PLACE) {
+ throw new WrongPrecisionException();
+ }
+ }
+ }
+
+ public static void checkLargeValue(Double value) throws LargeValueException {
+ if (value >= 10000000d) {
+ throw new LargeValueException();
+ }
+ }
+}
diff --git a/src/main/java/seedu/exceptions/InvalidCharacterInAmount.java b/src/main/java/seedu/exceptions/InvalidCharacterInAmount.java
new file mode 100644
index 0000000000..f5d031b026
--- /dev/null
+++ b/src/main/java/seedu/exceptions/InvalidCharacterInAmount.java
@@ -0,0 +1,10 @@
+package seedu.exceptions;
+
+import static seedu.ui.ErrorMessages.ERROR_AMOUNT_FORMAT_MESSAGE;
+
+public class InvalidCharacterInAmount extends Exception {
+ @Override
+ public String getMessage() {
+ return ERROR_AMOUNT_FORMAT_MESSAGE.toString();
+ }
+}
diff --git a/src/main/java/seedu/exceptions/InvalidDateException.java b/src/main/java/seedu/exceptions/InvalidDateException.java
new file mode 100644
index 0000000000..2636416770
--- /dev/null
+++ b/src/main/java/seedu/exceptions/InvalidDateException.java
@@ -0,0 +1,10 @@
+package seedu.exceptions;
+
+import static seedu.ui.ErrorMessages.ERROR_INVALID_DATE_MESSAGE;
+
+public class InvalidDateException extends Exception {
+ @Override
+ public String getMessage() {
+ return ERROR_INVALID_DATE_MESSAGE.toString();
+ }
+}
diff --git a/src/main/java/seedu/exceptions/InvalidDeadlineException.java b/src/main/java/seedu/exceptions/InvalidDeadlineException.java
new file mode 100644
index 0000000000..af2b618d7a
--- /dev/null
+++ b/src/main/java/seedu/exceptions/InvalidDeadlineException.java
@@ -0,0 +1,11 @@
+package seedu.exceptions;
+
+import static seedu.ui.ErrorMessages.ERROR_INVALID_DEADLINE_MESSAGE;
+
+public class InvalidDeadlineException extends Exception {
+ @Override
+ public String getMessage() {
+ return ERROR_INVALID_DEADLINE_MESSAGE.toString();
+ }
+}
+
diff --git a/src/main/java/seedu/exceptions/InvalidSortInputException.java b/src/main/java/seedu/exceptions/InvalidSortInputException.java
new file mode 100644
index 0000000000..025e39da34
--- /dev/null
+++ b/src/main/java/seedu/exceptions/InvalidSortInputException.java
@@ -0,0 +1,10 @@
+package seedu.exceptions;
+
+import static seedu.ui.ErrorMessages.ERROR_INVALID_SORT_INPUT_MESSAGE;
+
+public class InvalidSortInputException extends Exception {
+ @Override
+ public String getMessage() {
+ return ERROR_INVALID_SORT_INPUT_MESSAGE.toString();
+ }
+}
diff --git a/src/main/java/seedu/exceptions/LargeValueException.java b/src/main/java/seedu/exceptions/LargeValueException.java
new file mode 100644
index 0000000000..1371e24d6e
--- /dev/null
+++ b/src/main/java/seedu/exceptions/LargeValueException.java
@@ -0,0 +1,8 @@
+package seedu.exceptions;
+
+public class LargeValueException extends Exception {
+ @Override
+ public String getMessage() {
+ return "The amount you have entered is too large please input a smaller value!";
+ }
+}
diff --git a/src/main/java/seedu/exceptions/NoPaidFieldException.java b/src/main/java/seedu/exceptions/NoPaidFieldException.java
new file mode 100644
index 0000000000..6f89a7f8d5
--- /dev/null
+++ b/src/main/java/seedu/exceptions/NoPaidFieldException.java
@@ -0,0 +1,10 @@
+package seedu.exceptions;
+
+import static seedu.ui.ErrorMessages.ERROR_NO_PAID_FIELD_MESSAGE;
+
+public class NoPaidFieldException extends Exception {
+ @Override
+ public String getMessage() {
+ return ERROR_NO_PAID_FIELD_MESSAGE.toString();
+ }
+}
diff --git a/src/main/java/seedu/exceptions/NotPositiveValueException.java b/src/main/java/seedu/exceptions/NotPositiveValueException.java
new file mode 100644
index 0000000000..6eb5802140
--- /dev/null
+++ b/src/main/java/seedu/exceptions/NotPositiveValueException.java
@@ -0,0 +1,10 @@
+package seedu.exceptions;
+
+import static seedu.ui.ErrorMessages.ERROR_NOT_POSITIVE_VALUE_MESSAGE;
+
+public class NotPositiveValueException extends Exception {
+ @Override
+ public String getMessage() {
+ return ERROR_NOT_POSITIVE_VALUE_MESSAGE.toString();
+ }
+}
diff --git a/src/main/java/seedu/exceptions/SmallAmountException.java b/src/main/java/seedu/exceptions/SmallAmountException.java
new file mode 100644
index 0000000000..b5070e2294
--- /dev/null
+++ b/src/main/java/seedu/exceptions/SmallAmountException.java
@@ -0,0 +1,8 @@
+package seedu.exceptions;
+
+public class SmallAmountException extends Exception {
+ @Override
+ public String getMessage() {
+ return "The amount you have entered is under 0.01! MyLedger accepts values from 1 cent and above.";
+ }
+}
diff --git a/src/main/java/seedu/exceptions/TxtFileException.java b/src/main/java/seedu/exceptions/TxtFileException.java
new file mode 100644
index 0000000000..a8fa5e6106
--- /dev/null
+++ b/src/main/java/seedu/exceptions/TxtFileException.java
@@ -0,0 +1,8 @@
+package seedu.exceptions;
+
+public class TxtFileException extends Exception {
+ @Override
+ public String getMessage() {
+ return "TxtFile has been corrupted, the corrupted entry has been deleted";
+ }
+}
diff --git a/src/main/java/seedu/exceptions/WrongInputException.java b/src/main/java/seedu/exceptions/WrongInputException.java
new file mode 100644
index 0000000000..c6aa4de486
--- /dev/null
+++ b/src/main/java/seedu/exceptions/WrongInputException.java
@@ -0,0 +1,4 @@
+package seedu.exceptions;
+
+public class WrongInputException extends Exception {
+}
diff --git a/src/main/java/seedu/exceptions/WrongPrecisionException.java b/src/main/java/seedu/exceptions/WrongPrecisionException.java
new file mode 100644
index 0000000000..f168bca193
--- /dev/null
+++ b/src/main/java/seedu/exceptions/WrongPrecisionException.java
@@ -0,0 +1,10 @@
+package seedu.exceptions;
+
+import static seedu.ui.ErrorMessages.ERROR_INVALID_AMOUNT_PRECISION;
+
+public class WrongPrecisionException extends Exception {
+ @Override
+ public String getMessage() {
+ return ERROR_INVALID_AMOUNT_PRECISION.toString();
+ }
+}
diff --git a/src/main/java/seedu/exceptions/YearLimitException.java b/src/main/java/seedu/exceptions/YearLimitException.java
new file mode 100644
index 0000000000..4466c858e2
--- /dev/null
+++ b/src/main/java/seedu/exceptions/YearLimitException.java
@@ -0,0 +1,10 @@
+package seedu.exceptions;
+
+import static seedu.ui.ErrorMessages.ERROR_YEAR_LIMIT_MESSAGE;
+
+public class YearLimitException extends Exception {
+ @Override
+ public String getMessage() {
+ return ERROR_YEAR_LIMIT_MESSAGE.toString();
+ }
+}
diff --git a/src/main/java/seedu/expenditure/AcademicExpenditure.java b/src/main/java/seedu/expenditure/AcademicExpenditure.java
new file mode 100644
index 0000000000..371ce801f2
--- /dev/null
+++ b/src/main/java/seedu/expenditure/AcademicExpenditure.java
@@ -0,0 +1,42 @@
+package seedu.expenditure;
+
+import java.time.LocalDate;
+
+public class AcademicExpenditure extends Expenditure {
+ public static final String EXPENDITURE_TYPE = "Acad";
+ public static String iconPaid = "[X]";
+
+ public AcademicExpenditure(String description, double value, LocalDate date) {
+ super(description, value, date);
+ }
+
+ public String getPaidIcon() {
+ return iconPaid;
+ }
+ @Override
+ public String toString() {
+ return String.format("[Academic] || %s", super.toString());
+ }
+ @Override
+ public String expenditureString(String currency) {
+ return String.format("[Academic] || %s", super.expenditureString(currency));
+ }
+
+ @Override
+ public String getExpenditureType() {
+ return EXPENDITURE_TYPE;
+ }
+
+ @Override
+ public String saveInfo() {
+ return getExpenditureType() +
+ "d/" + getDescription() +
+ "v/" + getValue() +
+ "t/" + getDate() +
+ "p/" + "None" +
+ "n/" + "None" +
+ "o/" + "None" +
+ "r/" + "None" + "\n";
+
+ }
+}
diff --git a/src/main/java/seedu/expenditure/AccommodationExpenditure.java b/src/main/java/seedu/expenditure/AccommodationExpenditure.java
new file mode 100644
index 0000000000..f35d9c71d3
--- /dev/null
+++ b/src/main/java/seedu/expenditure/AccommodationExpenditure.java
@@ -0,0 +1,123 @@
+package seedu.expenditure;
+
+import java.time.LocalDate;
+
+public class AccommodationExpenditure extends Expenditure {
+ public static final String EXPENDITURE_TYPE = "Accom";
+ public static String iconPaid = "[X]";
+ public static String iconUnpaid = "[ ]";
+ private boolean isPaid;
+ private LocalDate repeatDate;
+
+ public AccommodationExpenditure(String description, double value, LocalDate date, LocalDate repeatDate) {
+ super(description, value, date);
+ resetPaid();
+ setRepeatDate(repeatDate);
+ }
+
+ public void setPaid() {
+ isPaid = true;
+ }
+
+ public String getPaidIcon() {
+ return (isPaid) ? iconPaid : iconUnpaid;
+ }
+
+ public void resetPaid() {
+ isPaid = false;
+ }
+
+ public void setRepeatDate(LocalDate repeatDate) {
+ this.repeatDate = repeatDate;
+ }
+
+ public LocalDate getRepeatDate() {
+ return repeatDate;
+ }
+
+ public String getStatusIcon() {
+ return (isPaid) ? iconPaid : iconUnpaid;
+ }
+
+ public void checkMark() {
+ checkNextRepeatDate();
+ handleNextRepeat();
+ }
+
+ public void checkNextRepeatDate() {
+ LocalDate firstDate = getDate();
+ LocalDate currentDate = LocalDate.now();
+ while (repeatDate.isBefore(currentDate) || repeatDate.equals(firstDate)) {
+ repeatDate = setNextRepeatDate();
+ }
+ }
+
+ public void handleNextRepeat() {
+ LocalDate currentDate = LocalDate.now();
+ if (currentDate.equals(repeatDate) || currentDate.isAfter(repeatDate)) {
+ isPaid = false;
+ repeatDate = setNextRepeatDate();
+ }
+ }
+
+ public LocalDate setNextRepeatDate() {
+ String stringNextYear = fetchNextYear();
+ String stringNextMonth = fetchMonth();
+ String stringNextDay = fetchDay();
+ String newDate = String.format("%s-%s-%s", stringNextYear, stringNextMonth, stringNextDay);
+ return LocalDate.parse(newDate);
+ }
+
+ public String fetchNextYear() {
+ // Repeats annually
+ final int incrementYear = 1;
+ int nextYear = repeatDate.getYear() + incrementYear;
+ return Integer.toString(nextYear);
+ }
+
+ public String fetchMonth() {
+ int nextMonth = repeatDate.getMonthValue();
+ final int doubleDigitMonth = 10;
+ if (nextMonth < doubleDigitMonth) {
+ return "0" + nextMonth;
+ } else {
+ return Integer.toString(nextMonth);
+ }
+ }
+
+ public String fetchDay() {
+ int nextDay = repeatDate.getDayOfMonth();
+ final int doubleDigitDay = 10;
+ if (nextDay < doubleDigitDay) {
+ return "0" + nextDay;
+ }
+ return Integer.toString(nextDay);
+ }
+
+
+ @Override
+ public String toString() {
+ return String.format("[Accommodation] || %s || %s", getStatusIcon(), super.toString());
+ }
+ @Override
+ public String expenditureString(String currency) {
+ return String.format("[Accommodation] || %s || %s", getStatusIcon(), super.expenditureString(currency));
+ }
+
+ @Override
+ public String getExpenditureType() {
+ return EXPENDITURE_TYPE;
+ }
+
+ @Override
+ public String saveInfo() { // NOT DONE
+ return getExpenditureType() +
+ "d/" + getDescription() +
+ "v/" + getValue() +
+ "t/" + getDate() +
+ "p/" + getStatusIcon() +
+ "n/" + "None" +
+ "o/" + "None" +
+ "r/" + repeatDate + "\n";
+ }
+}
diff --git a/src/main/java/seedu/expenditure/BorrowExpenditure.java b/src/main/java/seedu/expenditure/BorrowExpenditure.java
new file mode 100644
index 0000000000..938b592217
--- /dev/null
+++ b/src/main/java/seedu/expenditure/BorrowExpenditure.java
@@ -0,0 +1,75 @@
+package seedu.expenditure;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+
+public class BorrowExpenditure extends Expenditure {
+ public static final String EXPENDITURE_TYPE = "B";
+ public static String iconPaid = "[X]";
+ private LocalDate deadline;
+ private String borrowerName;
+
+ public BorrowExpenditure(String description, String borrowerName, double borrowValue, LocalDate date,
+ LocalDate deadline) {
+ super(description, borrowValue, date);
+ setBorrowerName(borrowerName);
+ setDeadline(deadline);
+ }
+
+ public LocalDate getDeadline() {
+ return deadline;
+ }
+
+ public void setDeadline(LocalDate deadline) {
+ this.deadline = deadline;
+ }
+
+ public String getBorrowerName() {
+ return borrowerName;
+ }
+ public String getPaidIcon() {
+ return iconPaid;
+ }
+ public void setBorrowerName(String borrowerName) {
+ this.borrowerName = borrowerName;
+ }
+
+ public void setBorrowerNameAndDeadline(String lenderName, LocalDate deadline) {
+ this.borrowerName = lenderName;
+ this.deadline = deadline;
+ }
+
+ public String getFullDeadline() {
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d MMM yyyy");
+ return getDeadline().format(formatter);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("[Borrow] || Borrowed from: %s || %s || By: %s",
+ getBorrowerName(), super.toString(), getFullDeadline());
+ }
+
+ @Override
+ public String expenditureString(String currency) {
+ return String.format("[Borrow] || Borrowed from: %s || %s || By: %s",
+ getBorrowerName(), super.expenditureString(currency), getFullDeadline());
+ }
+
+ @Override
+ public String getExpenditureType() {
+ return EXPENDITURE_TYPE;
+ }
+
+ @Override
+ public String saveInfo() {
+ return getExpenditureType() +
+ "d/" + getDescription() +
+ "v/" + getValue() +
+ "t/" + getDate() +
+ "p/" + "None" +
+ "n/" + getBorrowerName() +
+ "o/" + getDeadline() +
+ "r/" + "None" + "\n";
+ }
+}
diff --git a/src/main/java/seedu/expenditure/CurrencyValue.java b/src/main/java/seedu/expenditure/CurrencyValue.java
new file mode 100644
index 0000000000..8304079c44
--- /dev/null
+++ b/src/main/java/seedu/expenditure/CurrencyValue.java
@@ -0,0 +1,88 @@
+package seedu.expenditure;
+
+public abstract class CurrencyValue {
+ private static final double AUS_CONVERSION = 1.11;
+ private static final double CAD_CONVERSION = 1.01;
+ private static final double CNY_CONVERSION = 5.07;
+ private static final double DKK_CONVERSION = 5.15;
+ private static final double EUR_CONVERSION = 0.69;
+ private static final double GBP_CONVERSION = 0.61;
+ private static final double ILS_CONVERSION = 2.70;
+ private static final double JPY_CONVERSION = 99.96;
+ private static final double KRW_CONVERSION = 989.05;
+ private static final double NOK_CONVERSION = 7.78;
+ private static final double NZD_CONVERSION = 1.20;
+ private static final double SEK_CONVERSION = 7.80;
+ private static final double TWD_CONVERSION = 22.98;
+ private static final double USD_CONVERSION = 0.75;
+ public static final String LIST_OF_RATES = "Currency rates per SGD:\n" +
+ "AUS: " + AUS_CONVERSION + "\n" +
+ "CAD: " + CAD_CONVERSION + "\n" +
+ "CNY: " + CNY_CONVERSION + "\n" +
+ "DKK: " + DKK_CONVERSION + "\n" +
+ "EUR: " + EUR_CONVERSION + "\n" +
+ "GBP: " + GBP_CONVERSION + "\n" +
+ "ILS: " + ILS_CONVERSION + "\n" +
+ "JPY: " + JPY_CONVERSION + "\n" +
+ "KRW: " + KRW_CONVERSION + "\n" +
+ "NOK: " + NOK_CONVERSION + "\n" +
+ "NZD: " + NZD_CONVERSION + "\n" +
+ "SEK: " + SEK_CONVERSION + "\n" +
+ "TWD: " + TWD_CONVERSION + "\n" +
+ "USD: " + USD_CONVERSION;
+
+ /**
+ * Converts SGD to specified currency and returns the value.
+ * @param amount
+ * @param currency should be checked for non-valid inputs when command that would call
+ * sgDConversion is instantiated.
+ * @return
+ */
+ public static double sgdConversion(double amount, String currency) {
+ switch (currency) {
+ case "AUS":
+ return amount * AUS_CONVERSION;
+ case "CAD":
+ return amount * CAD_CONVERSION;
+ case "CNY":
+ return amount * CNY_CONVERSION;
+ case "DKK":
+ return amount * DKK_CONVERSION;
+ case "EUR":
+ return amount * EUR_CONVERSION;
+ case "GBP":
+ return amount * GBP_CONVERSION;
+ case "ILS":
+ return amount * ILS_CONVERSION;
+ case "JPY":
+ return amount * JPY_CONVERSION;
+ case "KRW":
+ return amount * KRW_CONVERSION;
+ case "NOK":
+ return amount * NOK_CONVERSION;
+ case "NZD":
+ return amount * NZD_CONVERSION;
+ case "SEK":
+ return amount * SEK_CONVERSION;
+ case "TWD":
+ return amount * TWD_CONVERSION;
+ case "USD":
+ return amount * USD_CONVERSION;
+ default:
+ return amount;
+ }
+ }
+
+ //Returns true if string corresponds to currency type
+ public static boolean isValidCurrency(String string) {
+ if (string.matches("AUS|CAD|CNY|DKK|EUR|GBP|ILS|JPY|KRW|NOK|NZD|SEK|TWD|USD|SGD")) {
+ return true;
+ }
+ return false;
+ }
+
+ //Returns the prepared string of different rates
+ public static String getRates () {
+ return LIST_OF_RATES;
+ }
+}
diff --git a/src/main/java/seedu/expenditure/EntertainmentExpenditure.java b/src/main/java/seedu/expenditure/EntertainmentExpenditure.java
new file mode 100644
index 0000000000..61121b7cef
--- /dev/null
+++ b/src/main/java/seedu/expenditure/EntertainmentExpenditure.java
@@ -0,0 +1,42 @@
+package seedu.expenditure;
+
+import java.time.LocalDate;
+
+public class EntertainmentExpenditure extends Expenditure {
+ public static final String EXPENDITURE_TYPE = "En";
+ public static String iconPaid = "[X]";
+
+ public EntertainmentExpenditure(String description, double value, LocalDate date) {
+ super(description, value, date);
+ }
+ public String getPaidIcon() {
+ return iconPaid;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("[Entertainment] || %s", super.toString());
+ }
+
+ @Override
+ public String expenditureString(String currency) {
+ return String.format("[Entertainment] || %s", super.expenditureString(currency));
+ }
+
+ @Override
+ public String getExpenditureType() {
+ return EXPENDITURE_TYPE;
+ }
+
+ @Override
+ public String saveInfo() {
+ return getExpenditureType() +
+ "d/" + getDescription() +
+ "v/" + getValue() +
+ "t/" + getDate() +
+ "p/" + "None" +
+ "n/" + "None" +
+ "o/" + "None" +
+ "r/" + "None" + "\n";
+ }
+}
diff --git a/src/main/java/seedu/expenditure/Expenditure.java b/src/main/java/seedu/expenditure/Expenditure.java
new file mode 100644
index 0000000000..6e0cb752e5
--- /dev/null
+++ b/src/main/java/seedu/expenditure/Expenditure.java
@@ -0,0 +1,88 @@
+package seedu.expenditure;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import static seedu.expenditure.CurrencyValue.sgdConversion;
+
+public abstract class Expenditure {
+ private String description;
+ private double value;
+ private LocalDate date;
+
+ public Expenditure(String description, double value, LocalDate date) {
+ setDescription(description);
+ setValue(value);
+ setDate(date);
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public void setValue(double value) {
+ this.value = value;
+ }
+
+ public void setDate(LocalDate date) {
+ this.date = date;
+ }
+
+ public void setDescriptionValueDate(String description, double value, LocalDate date) {
+ this.description = description;
+ this.value = value;
+ this.date = date;
+ }
+
+ public double getValue() {
+ return value;
+ }
+
+ public double getConvertedValue(String currency) {
+ return sgdConversion(value, currency);
+ }
+
+ public LocalDate getDate() {
+ return date;
+ }
+
+ public String getFullDate() {
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d MMM yyyy");
+ return getDate().format(formatter);
+ }
+
+ public String toString() {
+ return String.format("Date: %s || Value: %s || Description: %s", getFullDate(), getValue(), getDescription());
+ }
+
+ /**
+ * Returns string in the same format as toString, but accounts for currency
+ * conversion.
+ * If currency is irrelevent input field should be "SGD".
+ *
+ * @param currency
+ * @return
+ */
+ public String expenditureString(String currency) {
+ return String.format("Date: %s || Value: %.2f || Description: %s",
+ getFullDate(), getConvertedValue(currency), getDescription());
+ }
+
+ public abstract String getPaidIcon();
+
+ /**
+ * @return String representing the type of expenditure
+ */
+ public abstract String getExpenditureType();
+
+ /**
+ * Outputs a formatted String containing information of the task saved in a text
+ * file.
+ *
+ * @return String containing information of the expenditure.
+ */
+ public abstract String saveInfo();
+}
diff --git a/src/main/java/seedu/expenditure/ExpenditureList.java b/src/main/java/seedu/expenditure/ExpenditureList.java
new file mode 100644
index 0000000000..509445309d
--- /dev/null
+++ b/src/main/java/seedu/expenditure/ExpenditureList.java
@@ -0,0 +1,192 @@
+package seedu.expenditure;
+
+import seedu.commands.SortCommand;
+import seedu.exceptions.AlreadyMarkException;
+import seedu.exceptions.AlreadyUnmarkException;
+import seedu.exceptions.ExceptionChecker;
+import seedu.exceptions.NoPaidFieldException;
+import seedu.txtdata.TxtFileStatus;
+import java.io.IOException;
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.Comparator;
+
+public class ExpenditureList {
+ public static final int LIST_OFFSET = 1;
+ private static ArrayList expenditures;
+ private static double budgetSet = 0;
+
+ public ExpenditureList() {
+ expenditures = new ArrayList<>();
+ }
+
+ public void addExpenditure(Expenditure expenditure) {
+ expenditures.add(expenditure);
+ }
+
+ public void deleteExpenditure(int index) {
+ expenditures.remove(index);
+ expenditures.trimToSize();
+ }
+
+ public void markExpenditure(int index) throws NoPaidFieldException, AlreadyMarkException {
+ Expenditure expenditure = expenditures.get(index);
+ if (expenditure instanceof TuitionExpenditure) {
+ ExceptionChecker.checkAlreadyMark(expenditure);
+ ((TuitionExpenditure) expenditure).setPaid();
+ } else if (expenditure instanceof AccommodationExpenditure) {
+ ExceptionChecker.checkAlreadyMark(expenditure);
+ ((AccommodationExpenditure) expenditure).setPaid();
+ } else {
+ throw new NoPaidFieldException();
+ }
+ }
+
+ public void unmarkExpenditure(int index) throws NoPaidFieldException, AlreadyUnmarkException {
+ Expenditure expenditure = expenditures.get(index);
+ if (expenditure instanceof TuitionExpenditure) {
+ ExceptionChecker.checkAlreadyUnmark(expenditure);
+ ((TuitionExpenditure) expenditure).resetPaid();
+ } else if (expenditure instanceof AccommodationExpenditure) {
+ ExceptionChecker.checkAlreadyUnmark(expenditure);
+ ((AccommodationExpenditure) expenditure).resetPaid();
+ } else {
+ throw new NoPaidFieldException();
+ }
+ }
+
+ public void duplicateExpenditure(int index) {
+ Expenditure expenditure = expenditures.get(index);
+ addExpenditure(expenditure);
+ }
+
+ public void setNewBudget(double budget) {
+ budgetSet = budget;
+ }
+
+ public double getBudgetSet() {
+ return budgetSet;
+ }
+
+ public ArrayList getExpenditures() {
+ return expenditures;
+ }
+
+ public Expenditure getExpenditure(int index) {
+ return expenditures.get(index);
+ }
+
+ public int getSize() {
+ return expenditures.size();
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder stringOfExpenditures = new StringBuilder();
+ for (int i = 0; i < expenditures.size(); i++) {
+ final int expenditureNumber = i + LIST_OFFSET;
+ stringOfExpenditures.append(String.format("%d. %s\n",
+ expenditureNumber, expenditures.get(i)));
+ }
+ return stringOfExpenditures.toString().stripTrailing();
+ }
+
+ public String listString(String currency) {
+ StringBuilder stringOfExpenditures = new StringBuilder();
+ for (int i = 0; i < expenditures.size(); i++) {
+ final int expenditureNumber = i + LIST_OFFSET;
+ stringOfExpenditures.append(String.format("%d. %s\n",
+ expenditureNumber, expenditures.get(i).expenditureString(currency)));
+ }
+ return stringOfExpenditures.toString().stripTrailing();
+ }
+
+ public static String specificDateString(LocalDate date, String currency) {
+ StringBuilder stringOfExpenditures = new StringBuilder();
+ int counter = 1; //index of expenditures in the filter list
+ double totalValue = 0; //tallies total value
+ for (int i = 0; i < expenditures.size(); i++) {
+ Expenditure expenditure = expenditures.get(i);
+ if (date.equals(expenditure.getDate())) {
+ totalValue += expenditure.getConvertedValue(currency);
+ stringOfExpenditures.append(
+ String.format("%d. %s\n", counter, expenditure.expenditureString(currency)));
+ counter += 1;
+ }
+ }
+ if(totalValue == 0) {
+ return stringOfExpenditures.append("There are no expenditures recorded for this date").toString();
+ }
+ stringOfExpenditures.append("Here are the specified expenditures in " + currency + ": \n"
+ + "Total amount: " + String.format("%.2f", totalValue));
+ return stringOfExpenditures.toString().stripTrailing();
+ }
+
+ public static String specificTypeString(String expenditureType, String currency) {
+ StringBuilder stringOfExpenditures = new StringBuilder();
+ int counter = 1; //index of expenditures in the filter list
+ double totalValue = 0; //tallies total value
+ for (int i = 0; i < expenditures.size(); i++) {
+ Expenditure expenditure = expenditures.get(i);
+ if (expenditureType.equals(expenditure.getExpenditureType())) {
+ totalValue += expenditure.getConvertedValue(currency);
+ stringOfExpenditures.append(
+ String.format("%d. %s\n", counter, expenditure.expenditureString(currency)));
+ counter += 1;
+ }
+ }
+ stringOfExpenditures.append("Total amount: " + String.format("%.2f", totalValue));
+ return stringOfExpenditures.toString().stripTrailing();
+ }
+
+ public static void saveList() {
+ try {
+ TxtFileStatus.saveExpenditureList(expenditures);
+ } catch (IOException e) {
+ System.out.println("Error saving file");
+ }
+ }
+
+ public static ArrayList sortList(String sortType) {
+ ArrayList sortList = expenditures;
+ switch (sortType) {
+ case SortCommand.AMOUNT_ASCENDING:
+ sortList.sort(Comparator.comparing(Expenditure::getValue));
+ break;
+ case SortCommand.AMOUNT_DESCENDING:
+ sortList.sort(Comparator.comparing(Expenditure::getValue).reversed());
+ break;
+ case SortCommand.DATE_FROM_EARLIEST:
+ sortList.sort(Comparator.comparing(Expenditure::getDate));
+ break;
+ case SortCommand.DATE_FROM_LATEST:
+ sortList.sort(Comparator.comparing(Expenditure::getDate).reversed());
+ break;
+ default:
+ break;
+ }
+ return sortList;
+ }
+
+ public static ExpenditureList getSortedExpenditures(ArrayList sortList) {
+ ExpenditureList sortedExpenditures = new ExpenditureList();
+ for (Expenditure expenditure : sortList) {
+ sortedExpenditures.addExpenditure(expenditure);
+ }
+ return sortedExpenditures;
+ }
+
+ public static void queryLumpSumDates() {
+ for (Expenditure expenditure: expenditures) {
+ processLumpSumDates(expenditure);
+ }
+ }
+
+ public static void processLumpSumDates(Expenditure expenditure) {
+ if (expenditure instanceof TuitionExpenditure) {
+ ((TuitionExpenditure) expenditure).checkMark();
+ } else if (expenditure instanceof AccommodationExpenditure) {
+ ((AccommodationExpenditure) expenditure).checkMark();
+ }
+ }
+}
diff --git a/src/main/java/seedu/expenditure/FoodExpenditure.java b/src/main/java/seedu/expenditure/FoodExpenditure.java
new file mode 100644
index 0000000000..251a58e7c3
--- /dev/null
+++ b/src/main/java/seedu/expenditure/FoodExpenditure.java
@@ -0,0 +1,43 @@
+package seedu.expenditure;
+
+import java.time.LocalDate;
+
+public class FoodExpenditure extends Expenditure {
+ public static final String EXPENDITURE_TYPE = "F";
+ public static String iconPaid = "[X]";
+
+ public FoodExpenditure(String description, double value, LocalDate date) {
+ super(description, value, date);
+ }
+
+ public String getPaidIcon() {
+ return iconPaid;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("[Food] || %s", super.toString());
+ }
+
+ @Override
+ public String expenditureString(String currency) {
+ return String.format("[Food] || %s", super.expenditureString(currency));
+ }
+
+ @Override
+ public String getExpenditureType() {
+ return EXPENDITURE_TYPE;
+ }
+
+ @Override
+ public String saveInfo() {
+ return getExpenditureType() +
+ "d/" + getDescription() +
+ "v/" + getValue() +
+ "t/" + getDate() +
+ "p/" + "None" +
+ "n/" + "None" +
+ "o/" + "None" +
+ "r/" + "None" + "\n";
+ }
+}
diff --git a/src/main/java/seedu/expenditure/LendExpenditure.java b/src/main/java/seedu/expenditure/LendExpenditure.java
new file mode 100644
index 0000000000..7aa27c28b9
--- /dev/null
+++ b/src/main/java/seedu/expenditure/LendExpenditure.java
@@ -0,0 +1,79 @@
+package seedu.expenditure;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+
+public class LendExpenditure extends Expenditure {
+ public static final String EXPENDITURE_TYPE = "L";
+ public static String iconPaid = "[X]";
+ private LocalDate deadline;
+ private String lenderName;
+
+
+
+ public LendExpenditure(String description, String lenderName, double lendValue, LocalDate date,
+ LocalDate deadline) {
+ super(description, lendValue, date);
+ setLenderName(lenderName);
+ setDeadline(deadline);
+ }
+
+ public LocalDate getDeadline() {
+ return deadline;
+ }
+
+ public void setDeadline(LocalDate deadline) {
+ this.deadline = deadline;
+ }
+
+ public String getLenderName() {
+ return lenderName;
+ }
+
+ public String getPaidIcon() {
+ return iconPaid;
+ }
+
+ public void setLenderName(String lenderName) {
+ this.lenderName = lenderName;
+ }
+
+ public void setLenderNameAndDeadline(String lenderName, LocalDate deadline) {
+ this.lenderName = lenderName;
+ this.deadline = deadline;
+ }
+
+ public String getFullDeadline() {
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d MMM yyyy");
+ return getDeadline().format(formatter);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("[Lend] || Lent to: %s || %s || by: %s",
+ getLenderName(), super.toString(), getFullDeadline());
+ }
+
+ @Override
+ public String expenditureString(String currency) {
+ return String.format("[Lend] || Lent to: %s || %s || by: %s",
+ getLenderName(), super.expenditureString(currency), getFullDeadline());
+ }
+
+ @Override
+ public String getExpenditureType() {
+ return EXPENDITURE_TYPE;
+ }
+
+ @Override
+ public String saveInfo() {
+ return getExpenditureType() +
+ "d/" + getDescription() +
+ "v/" + getValue() +
+ "t/" + getDate() +
+ "p/" + "None" +
+ "n/" + getLenderName() +
+ "o/" + getDeadline() +
+ "r/" + "None" + "\n";
+ }
+}
diff --git a/src/main/java/seedu/expenditure/OtherExpenditure.java b/src/main/java/seedu/expenditure/OtherExpenditure.java
new file mode 100644
index 0000000000..76058658cc
--- /dev/null
+++ b/src/main/java/seedu/expenditure/OtherExpenditure.java
@@ -0,0 +1,44 @@
+package seedu.expenditure;
+
+import java.time.LocalDate;
+
+public class OtherExpenditure extends Expenditure {
+ public static final String EXPENDITURE_TYPE = "O";
+ public static String iconPaid = "[X]";
+
+
+ public OtherExpenditure(String description, double value, LocalDate date) {
+ super(description, value, date);
+ }
+
+ public String getPaidIcon() {
+ return iconPaid;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("[Other] || %s", super.toString());
+ }
+
+ @Override
+ public String expenditureString(String currency) {
+ return String.format("[Other] || %s", super.expenditureString(currency));
+ }
+
+ @Override
+ public String getExpenditureType() {
+ return EXPENDITURE_TYPE;
+ }
+
+ @Override
+ public String saveInfo() {
+ return getExpenditureType() +
+ "d/" + getDescription() +
+ "v/" + getValue() +
+ "t/" + getDate() +
+ "p/" + "None" +
+ "n/" + "None" +
+ "o/" + "None" +
+ "r/" + "None" + "\n";
+ }
+}
diff --git a/src/main/java/seedu/expenditure/TransportExpenditure.java b/src/main/java/seedu/expenditure/TransportExpenditure.java
new file mode 100644
index 0000000000..e4cf8143dc
--- /dev/null
+++ b/src/main/java/seedu/expenditure/TransportExpenditure.java
@@ -0,0 +1,43 @@
+package seedu.expenditure;
+
+import java.time.LocalDate;
+
+public class TransportExpenditure extends Expenditure {
+ public static final String EXPENDITURE_TYPE = "Tr";
+ public static String iconPaid = "[X]";
+
+
+ public TransportExpenditure(String description, double value, LocalDate date) {
+ super(description, value, date);
+ }
+
+ public String getPaidIcon() {
+ return iconPaid;
+ }
+
+ public String toString() {
+ return String.format("[Transport] || %s", super.toString());
+ }
+
+ @Override
+ public String expenditureString(String currency) {
+ return String.format("[Transport] || %s", super.expenditureString(currency));
+ }
+
+ @Override
+ public String getExpenditureType() {
+ return EXPENDITURE_TYPE;
+ }
+
+ @Override
+ public String saveInfo() {
+ return getExpenditureType() +
+ "d/" + getDescription() +
+ "v/" + getValue() +
+ "t/" + getDate() +
+ "p/" + "None" +
+ "n/" + "None" +
+ "o/" + "None" +
+ "r/" + "None" + "\n";
+ }
+}
diff --git a/src/main/java/seedu/expenditure/TuitionExpenditure.java b/src/main/java/seedu/expenditure/TuitionExpenditure.java
new file mode 100644
index 0000000000..b8ec85c5f9
--- /dev/null
+++ b/src/main/java/seedu/expenditure/TuitionExpenditure.java
@@ -0,0 +1,119 @@
+package seedu.expenditure;
+
+import java.time.LocalDate;
+
+public class TuitionExpenditure extends Expenditure {
+
+ public static final String EXPENDITURE_TYPE = "Tu";
+ public static String iconPaid = "[X]";
+ public static String iconUnpaid = "[ ]";
+ private boolean isPaid;
+ private LocalDate repeatDate;
+
+ public TuitionExpenditure(String description, double value, LocalDate date, LocalDate repeatDate) {
+ super(description, value, date);
+ resetPaid();
+ setRepeatDate(repeatDate);
+ }
+
+ public void setPaid() {
+ isPaid = true;
+ }
+
+ public void resetPaid() {
+ isPaid = false;
+ }
+
+ public void setRepeatDate(LocalDate repeatDate) {
+ this.repeatDate = repeatDate;
+ }
+
+ public LocalDate getRepeatDate() {
+ return repeatDate;
+ }
+
+ public String getPaidIcon() {
+ return (isPaid) ? iconPaid : iconUnpaid;
+ }
+
+ public void checkMark() {
+ checkNextRepeatDate();
+ handleNextRepeat();
+ }
+
+ public void checkNextRepeatDate() {
+ LocalDate firstDate = getDate();
+ LocalDate currentDate = LocalDate.now();
+ while (repeatDate.isBefore(currentDate) || repeatDate.equals(firstDate)) {
+ repeatDate = setNextRepeatDate();
+ }
+ }
+
+ public void handleNextRepeat() {
+ LocalDate currentDate = LocalDate.now();
+ if (currentDate.equals(repeatDate) || currentDate.isAfter(repeatDate)) {
+ isPaid = false;
+ repeatDate = setNextRepeatDate();
+ }
+ }
+
+ public LocalDate setNextRepeatDate() {
+ String stringNextYear = fetchNextYear();
+ String stringNextMonth = fetchMonth();
+ String stringNextDay = fetchDay();
+ String newDate = String.format("%s-%s-%s", stringNextYear, stringNextMonth, stringNextDay);
+ return LocalDate.parse(newDate);
+ }
+
+ public String fetchNextYear() {
+ // Repeats annually
+ final int incrementYear = 1;
+ int nextYear = repeatDate.getYear() + incrementYear;
+ return Integer.toString(nextYear);
+ }
+
+ public String fetchMonth() {
+ int nextMonth = repeatDate.getMonthValue();
+ final int doubleDigitMonth = 10;
+ if (nextMonth < doubleDigitMonth) {
+ return "0" + nextMonth;
+ } else {
+ return Integer.toString(nextMonth);
+ }
+ }
+
+ public String fetchDay() {
+ int nextDay = repeatDate.getDayOfMonth();
+ final int doubleDigitDay = 10;
+ if (nextDay < doubleDigitDay) {
+ return "0" + nextDay;
+ }
+ return Integer.toString(nextDay);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("[Tuition] || %s || %s", getPaidIcon(), super.toString());
+ }
+
+ @Override
+ public String expenditureString(String currency) {
+ return String.format("[Tuition] || %s || %s", getPaidIcon(), super.expenditureString(currency));
+ }
+
+ public String getExpenditureType() {
+ return EXPENDITURE_TYPE;
+ }
+
+ @Override
+ public String saveInfo() {
+ return getExpenditureType() +
+ "d/" + getDescription() +
+ "v/" + getValue() +
+ "t/" + getDate() +
+ "p/" + getPaidIcon() +
+ "n/" + "None" +
+ "o/" + "None" +
+ "r/" + repeatDate + "\n";
+ }
+}
diff --git a/src/main/java/seedu/myledger/MyLedger.java b/src/main/java/seedu/myledger/MyLedger.java
new file mode 100644
index 0000000000..eb5545742f
--- /dev/null
+++ b/src/main/java/seedu/myledger/MyLedger.java
@@ -0,0 +1,63 @@
+package seedu.myledger;
+
+import seedu.parser.MainInputParser;
+import seedu.txtdata.TxtFileStatus;
+import seedu.expenditure.ExpenditureList;
+import seedu.commands.Command;
+import seedu.commands.CommandResult;
+import seedu.ui.Ui;
+import java.io.FileNotFoundException;
+import java.util.Scanner;
+
+public class MyLedger {
+ private static ExpenditureList expenditures;
+
+ private static void start() {
+ expenditures = new ExpenditureList();
+ }
+
+ public static void main(String[] args) {
+ try {
+ TxtFileStatus.checkFile();
+ MyLedger.runMyLedger();
+ } catch (FileNotFoundException e) {
+ System.out.println("File does not exist.");
+ }
+ }
+
+ public static void runMyLedger() {
+ start();
+ Ui.greetUser();
+ initializeList();
+ readUserInputs();
+ }
+
+ public static void readUserInputs() {
+ Scanner in = new Scanner(System.in);
+ String line = in.nextLine();
+ while (!hasProcessedAllInputs(line, expenditures)) {
+ line = in.nextLine();
+ }
+ in.close();
+ }
+
+ public static boolean hasProcessedAllInputs(String line, ExpenditureList expenditures) {
+ // Parses the input
+ Command finalCommand = MainInputParser.parseInputs(line);
+ CommandResult result = finalCommand.execute(expenditures);
+ String textOutput = result.getCommandResult();
+ ExpenditureList.queryLumpSumDates();
+ ExpenditureList.saveList();
+ System.out.println(textOutput);
+ return finalCommand.isExit();
+ }
+
+ public static void initializeList() {
+ try {
+ TxtFileStatus.initializeExpenditureList(expenditures);
+ ExpenditureList.queryLumpSumDates();
+ } catch (FileNotFoundException e) {
+ System.out.println("Error finding save file during initialization");
+ }
+ }
+}
diff --git a/src/main/java/seedu/parser/MainInputParser.java b/src/main/java/seedu/parser/MainInputParser.java
new file mode 100644
index 0000000000..324e8dccc1
--- /dev/null
+++ b/src/main/java/seedu/parser/MainInputParser.java
@@ -0,0 +1,152 @@
+package seedu.parser;
+
+import seedu.commands.Command;
+import seedu.commands.EditCommand;
+import seedu.commands.HelpCommand;
+import seedu.commands.DeleteCommand;
+import seedu.commands.ExitCommand;
+import seedu.commands.FindCommand;
+import seedu.commands.ListExpenditureCommand;
+import seedu.commands.ViewDateExpenditureCommand;
+import seedu.commands.ViewTypeExpenditureCommand;
+import seedu.commands.ShowRatesCommand;
+import seedu.commands.AcademicExpenditureCommand;
+import seedu.commands.AccommodationExpenditureCommand;
+import seedu.commands.EntertainmentExpenditureCommand;
+import seedu.commands.FoodExpenditureCommand;
+import seedu.commands.OtherExpenditureCommand;
+import seedu.commands.TransportExpenditureCommand;
+import seedu.commands.TuitionExpenditureCommand;
+import seedu.commands.LendExpenditureCommand;
+import seedu.commands.BorrowExpenditureCommand;
+import seedu.commands.InvalidCommand;
+import seedu.commands.UnmarkCommand;
+import seedu.commands.MarkCommand;
+import seedu.commands.SortCommand;
+import seedu.exceptions.DateLimitException;
+import seedu.exceptions.InvalidDateException;
+import seedu.exceptions.NotPositiveValueException;
+import seedu.exceptions.WrongInputException;
+import seedu.commands.DuplicateCommand;
+import seedu.commands.SetBudgetCommand;
+import seedu.commands.CheckBudgetCommand;
+
+import java.time.format.DateTimeParseException;
+
+import static seedu.ui.ErrorMessages.ERROR_COMMAND_NOT_RECOGNISED_MESSAGE;
+import static seedu.ui.ErrorMessages.ERROR_LACK_OF_PARAMETERS_MESSAGE;
+import static seedu.ui.ErrorMessages.ERROR_DATE_TIME_ERROR_MESSAGE;
+import static seedu.ui.ErrorMessages.ERROR_INVALID_INPUT_MESSAGE;
+import static seedu.ui.ErrorMessages.ERROR_NOT_POSITIVE_VALUE_MESSAGE;
+
+public class MainInputParser {
+ public static final int LIMIT = 2;
+ public static final int INDEX_COMMAND = 0;
+ public static final int INDEX_USERSTRING = 1;
+ public static final String CHECK_STRING = "c/";
+
+ public static Command parseInputs(String userInput) {
+ String[] splitValues = userInput.split(" ", LIMIT);
+ String command = splitValues[INDEX_COMMAND];
+ try {
+ return filterCategories(command, splitValues);
+ } catch (IndexOutOfBoundsException e) {
+ return new InvalidCommand(ERROR_LACK_OF_PARAMETERS_MESSAGE.toString());
+ } catch (DateTimeParseException d) {
+ return new InvalidCommand(ERROR_DATE_TIME_ERROR_MESSAGE.toString());
+ } catch (WrongInputException e) {
+ return new InvalidCommand(ERROR_INVALID_INPUT_MESSAGE.toString());
+ } catch (NotPositiveValueException p) {
+ return new InvalidCommand(ERROR_NOT_POSITIVE_VALUE_MESSAGE.toString());
+ } catch (InvalidDateException e) {
+ return new InvalidCommand(e.getMessage());
+ } catch (DateLimitException l) {
+ return new InvalidCommand(l.getMessage());
+ }
+ }
+
+ public static Command filterCategories(String command, String[] splitValues) throws IndexOutOfBoundsException,
+ DateTimeParseException, WrongInputException, NotPositiveValueException,
+ InvalidDateException, DateLimitException {
+ switch (command) {
+
+ // Commands that insert new inputs
+ case AcademicExpenditureCommand.COMMAND_WORD:
+ case AccommodationExpenditureCommand.COMMAND_WORD:
+ case EntertainmentExpenditureCommand.COMMAND_WORD:
+ case FoodExpenditureCommand.COMMAND_WORD:
+ case OtherExpenditureCommand.COMMAND_WORD:
+ case TransportExpenditureCommand.COMMAND_WORD:
+ case TuitionExpenditureCommand.COMMAND_WORD:
+ ParseAdd prepareAddExpenditure;
+ prepareAddExpenditure = new ParseAdd(splitValues[INDEX_USERSTRING]);
+ return prepareAddExpenditure.addItem(command);
+ case LendExpenditureCommand.COMMAND_WORD:
+ case BorrowExpenditureCommand.COMMAND_WORD:
+ ParseLendBorrow prepareLendBorrowExpenditure;
+ prepareLendBorrowExpenditure = new ParseLendBorrow(splitValues[INDEX_USERSTRING]);
+ return prepareLendBorrowExpenditure.addItem(command);
+ case DuplicateCommand.COMMAND_WORD:
+ ParseDuplicate prepareDuplicate = new ParseDuplicate(splitValues[INDEX_USERSTRING]);
+ return prepareDuplicate.duplicateItem();
+
+ // Commands that support basic functionalities of MyLedger
+ case ListExpenditureCommand.COMMAND_WORD:
+ return new ListExpenditureCommand(splitValues[INDEX_USERSTRING]);
+ case ExitCommand.COMMAND_WORD:
+ return new ExitCommand();
+ case HelpCommand.COMMAND_WORD:
+ return new HelpCommand();
+ case ShowRatesCommand.COMMAND_WORD:
+ return new ShowRatesCommand();
+
+ // Commands that mark and unmark inputs in list
+ case MarkCommand.COMMAND_WORD:
+ ParseMark prepareMark;
+ prepareMark = new ParseMark(splitValues[INDEX_USERSTRING]);
+ return prepareMark.markExpenditure();
+ case UnmarkCommand.COMMAND_WORD:
+ ParseMark prepareUnmark;
+ prepareUnmark = new ParseMark(splitValues[INDEX_USERSTRING]);
+ return prepareUnmark.unmarkExpenditure();
+
+ // Commands that changes existing inputs in list
+ case DeleteCommand.COMMAND_WORD:
+ ParseDelete prepareDelete;
+ prepareDelete = new ParseDelete(splitValues[INDEX_USERSTRING]);
+ return prepareDelete.deleteItem();
+ case EditCommand.COMMAND_WORD:
+ ParseEdit prepareEdit = new ParseEdit(splitValues[INDEX_USERSTRING]);
+ return prepareEdit.editItem();
+
+ // Commands that allows users to view list and filtered list according to
+ // preference
+ case SortCommand.COMMAND_WORD:
+ ParseSort prepareSort;
+ prepareSort = new ParseSort(splitValues[INDEX_USERSTRING]);
+ return prepareSort.sortExpenditures();
+ case ViewDateExpenditureCommand.COMMAND_WORD:
+ return new ViewDateExpenditureCommand(splitValues[INDEX_USERSTRING]);
+ case ViewTypeExpenditureCommand.COMMAND_WORD:
+ return new ViewTypeExpenditureCommand(splitValues[INDEX_USERSTRING]);
+ case FindCommand.COMMAND_WORD:
+ ParseFind prepareFind;
+ prepareFind = new ParseFind(splitValues[INDEX_USERSTRING]);
+ return prepareFind.findExpenditure();
+
+ // Commands that allows users to compare budget with expenditures
+ case SetBudgetCommand.COMMAND_WORD:
+ ParseSetBudget prepareBudget = new ParseSetBudget(splitValues[INDEX_USERSTRING]);
+ return prepareBudget.setBudget();
+ case CheckBudgetCommand.COMMAND_WORD:
+ if (splitValues.length == 1 || splitValues[1].isEmpty() || splitValues[1].isBlank()) {
+ return new CheckBudgetCommand(CHECK_STRING);
+ }
+ return new CheckBudgetCommand(splitValues[INDEX_USERSTRING]);
+
+ // Commands that do not fulfill requirements above
+ default:
+ return new InvalidCommand(ERROR_COMMAND_NOT_RECOGNISED_MESSAGE.toString());
+ }
+ }
+}
diff --git a/src/main/java/seedu/parser/ParseAdd.java b/src/main/java/seedu/parser/ParseAdd.java
new file mode 100644
index 0000000000..f94161b1fc
--- /dev/null
+++ b/src/main/java/seedu/parser/ParseAdd.java
@@ -0,0 +1,124 @@
+package seedu.parser;
+
+import seedu.commands.Command;
+import seedu.commands.AcademicExpenditureCommand;
+import seedu.commands.AccommodationExpenditureCommand;
+import seedu.commands.EntertainmentExpenditureCommand;
+import seedu.commands.FoodExpenditureCommand;
+import seedu.commands.OtherExpenditureCommand;
+import seedu.commands.TransportExpenditureCommand;
+import seedu.commands.TuitionExpenditureCommand;
+import seedu.commands.InvalidCommand;
+import seedu.exceptions.ExceptionChecker;
+import seedu.exceptions.SmallAmountException;
+import seedu.exceptions.InvalidCharacterInAmount;
+import seedu.exceptions.NotPositiveValueException;
+import seedu.exceptions.DateLimitException;
+import seedu.exceptions.EmptyStringException;
+import seedu.exceptions.WrongPrecisionException;
+import seedu.exceptions.LargeValueException;
+
+
+import java.time.LocalDate;
+import java.time.format.DateTimeParseException;
+
+import static seedu.ui.ErrorMessages.ERROR_AMOUNT_FORMAT_MESSAGE;
+import static seedu.ui.ErrorMessages.ERROR_DATE_TIME_ERROR_MESSAGE;
+import static seedu.ui.ErrorMessages.ERROR_COMMAND_NOT_RECOGNISED_MESSAGE;
+import static seedu.ui.ErrorMessages.ERROR_EMPTY_STRING_MESSAGE;
+import static seedu.ui.ErrorMessages.ERROR_NOT_POSITIVE_VALUE_MESSAGE;
+import static seedu.ui.ErrorMessages.ERROR_INVALID_AMOUNT_PRECISION;
+import static seedu.ui.ErrorMessages.ERROR_INVALID_AMOUNT_TOO_LARGE;
+
+
+public class ParseAdd {
+ public static final String BLANK = "";
+ public static final String DSLASH = "d/";
+ public static final String ASLASH = "a/";
+ public static final String PSLASH = "p/";
+ private static final String DOT = ".";
+ private final String userInput;
+
+ public ParseAdd(String userInput) {
+ this.userInput = userInput;
+ }
+
+ /**
+ * @author itszhixuan
+ */
+ public Command addItem(String command) {
+
+ try {
+ // Format: category d/date, a/amount, s/description
+ LocalDate date = fetchDate();
+ double amount = fetchDouble();
+
+ String descriptionVal = fetchDescription();
+
+ // Checks for the specific expenditure according to command
+ switch (command) {
+ case AcademicExpenditureCommand.COMMAND_WORD:
+ return new AcademicExpenditureCommand(descriptionVal, amount, date);
+ case AccommodationExpenditureCommand.COMMAND_WORD:
+ return new AccommodationExpenditureCommand(descriptionVal, amount, date, date);
+ case EntertainmentExpenditureCommand.COMMAND_WORD:
+ return new EntertainmentExpenditureCommand(descriptionVal, amount, date);
+ case FoodExpenditureCommand.COMMAND_WORD:
+ return new FoodExpenditureCommand(descriptionVal, amount, date);
+ case OtherExpenditureCommand.COMMAND_WORD:
+ return new OtherExpenditureCommand(descriptionVal, amount, date);
+ case TransportExpenditureCommand.COMMAND_WORD:
+ return new TransportExpenditureCommand(descriptionVal, amount, date);
+ case TuitionExpenditureCommand.COMMAND_WORD:
+ return new TuitionExpenditureCommand(descriptionVal, amount, date, date);
+ default:
+ return new InvalidCommand(ERROR_COMMAND_NOT_RECOGNISED_MESSAGE.toString());
+ }
+
+ } catch (NumberFormatException n) {
+ return new InvalidCommand(ERROR_AMOUNT_FORMAT_MESSAGE.toString());
+ } catch (SmallAmountException | InvalidCharacterInAmount e) {
+ return new InvalidCommand(e.getMessage());
+ } catch (DateTimeParseException d) {
+ return new InvalidCommand(ERROR_DATE_TIME_ERROR_MESSAGE.toString());
+ } catch (StringIndexOutOfBoundsException | EmptyStringException s) {
+ return new InvalidCommand(ERROR_EMPTY_STRING_MESSAGE.toString());
+ } catch (NotPositiveValueException p) {
+ return new InvalidCommand(ERROR_NOT_POSITIVE_VALUE_MESSAGE.toString());
+ } catch (WrongPrecisionException e) {
+ return new InvalidCommand(ERROR_INVALID_AMOUNT_PRECISION.toString());
+ } catch (DateLimitException l) {
+ return new InvalidCommand(l.getMessage());
+ } catch (LargeValueException l) {
+ return new InvalidCommand(ERROR_INVALID_AMOUNT_TOO_LARGE.toString());
+ }
+ }
+
+ public String fetchDescription() throws EmptyStringException, StringIndexOutOfBoundsException {
+ // Removes indicators and backslashes from the user input
+ return ParseIndividualValue.parseIndividualValue(userInput, PSLASH, BLANK);
+ }
+
+ public double fetchDouble() throws InvalidCharacterInAmount, EmptyStringException,
+ StringIndexOutOfBoundsException, SmallAmountException, NotPositiveValueException, NumberFormatException,
+ WrongPrecisionException, LargeValueException {
+ // Converts string to double for numerical addition functionalities
+ String amountVal = ParseIndividualValue.parseIndividualValue(userInput, ASLASH, PSLASH);
+ ExceptionChecker.checkIfMoreThanTwoDecimalPlaces(amountVal, DOT, BLANK);
+ ExceptionChecker.checkValidDoubleInput(amountVal);
+ double amount = Double.parseDouble(amountVal);
+ ExceptionChecker.checkValidAmount(amount);
+ ExceptionChecker.checkLargeValue(amount);
+ return Double.parseDouble(amountVal);
+ }
+
+ public LocalDate fetchDate() throws EmptyStringException, StringIndexOutOfBoundsException,
+ DateTimeParseException, DateLimitException {
+ // Converts string to date to fit Command class
+ String dateVal = ParseIndividualValue.parseIndividualValue(userInput, DSLASH, ASLASH);
+ LocalDate date = LocalDate.parse(dateVal);
+ ExceptionChecker.checkDateLimit(date);
+ return date;
+ }
+
+}
diff --git a/src/main/java/seedu/parser/ParseDate.java b/src/main/java/seedu/parser/ParseDate.java
new file mode 100644
index 0000000000..ff971773da
--- /dev/null
+++ b/src/main/java/seedu/parser/ParseDate.java
@@ -0,0 +1,11 @@
+package seedu.parser;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+
+public class ParseDate {
+ public static LocalDate parseDate(String dateVal) throws IndexOutOfBoundsException {
+ DateTimeFormatter output = DateTimeFormatter.ofPattern("dd-MM-yyyy");
+ return LocalDate.parse(dateVal, output);
+ }
+}
diff --git a/src/main/java/seedu/parser/ParseDelete.java b/src/main/java/seedu/parser/ParseDelete.java
new file mode 100644
index 0000000000..31b14f846f
--- /dev/null
+++ b/src/main/java/seedu/parser/ParseDelete.java
@@ -0,0 +1,33 @@
+package seedu.parser;
+
+import seedu.commands.Command;
+import seedu.commands.DeleteCommand;
+import seedu.commands.InvalidCommand;
+import seedu.exceptions.EmptyStringException;
+import seedu.expenditure.ExpenditureList;
+
+import static seedu.ui.ErrorMessages.ERROR_NUMBER_FORMAT_MESSAGE;
+
+public class ParseDelete {
+ public static final String BLANK = "";
+ private final String userInput;
+
+ public ParseDelete(String userInput) {
+ this.userInput = userInput;
+ }
+
+ public Command deleteItem() {
+ try {
+ // Removes empty space from the user input
+ String details = ParseIndividualValue.parseIndividualValue(userInput, BLANK, BLANK);
+
+ // Converts from string to int for comparison
+ int posToDelete = Integer.parseInt(details) - ExpenditureList.LIST_OFFSET;
+ return new DeleteCommand(posToDelete);
+ } catch (NumberFormatException numberFormatException) {
+ return new InvalidCommand(ERROR_NUMBER_FORMAT_MESSAGE.toString());
+ } catch (StringIndexOutOfBoundsException | EmptyStringException s) {
+ return new InvalidCommand(s.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/seedu/parser/ParseDuplicate.java b/src/main/java/seedu/parser/ParseDuplicate.java
new file mode 100644
index 0000000000..50e385e1ee
--- /dev/null
+++ b/src/main/java/seedu/parser/ParseDuplicate.java
@@ -0,0 +1,32 @@
+package seedu.parser;
+
+import seedu.commands.Command;
+import seedu.commands.DuplicateCommand;
+import seedu.commands.InvalidCommand;
+import seedu.exceptions.EmptyStringException;
+import seedu.expenditure.ExpenditureList;
+
+import static seedu.ui.ErrorMessages.ERROR_NUMBER_FORMAT_MESSAGE;
+
+public class ParseDuplicate {
+ public static final String BLANK = "";
+ private final String userInput;
+
+ public ParseDuplicate(String userInput) {
+ this.userInput = userInput;
+ }
+
+ public Command duplicateItem() {
+ try {
+ // Removes empty space from the user input
+ String details = ParseIndividualValue.parseIndividualValue(userInput, BLANK, BLANK);
+ // Converts from string to int for comparison
+ int posToDuplicate = Integer.parseInt(details) - ExpenditureList.LIST_OFFSET;
+ return new DuplicateCommand(posToDuplicate);
+ } catch (NumberFormatException numberFormatException) {
+ return new InvalidCommand(ERROR_NUMBER_FORMAT_MESSAGE.toString());
+ } catch (StringIndexOutOfBoundsException | EmptyStringException e) {
+ return new InvalidCommand(e.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/seedu/parser/ParseEdit.java b/src/main/java/seedu/parser/ParseEdit.java
new file mode 100644
index 0000000000..1b9dfde737
--- /dev/null
+++ b/src/main/java/seedu/parser/ParseEdit.java
@@ -0,0 +1,38 @@
+package seedu.parser;
+
+import seedu.commands.Command;
+import seedu.commands.EditCommand;
+import seedu.commands.InvalidCommand;
+import seedu.exceptions.EmptyStringException;
+import seedu.expenditure.ExpenditureList;
+import java.time.format.DateTimeParseException;
+
+import static seedu.ui.ErrorMessages.ERROR_WRONG_FORMAT_MESSAGE;
+import static seedu.ui.ErrorMessages.ERROR_NUMBER_FORMAT_MESSAGE;
+import static seedu.ui.ErrorMessages.ERROR_EMPTY_STRING_MESSAGE;
+
+public class ParseEdit {
+ public static final String BLANK = "";
+ public static final String DSLASH = "d/";
+ private final String userInput;
+
+ public ParseEdit(String userInput) {
+ this.userInput = userInput;
+ }
+
+ public Command editItem() {
+ try {
+ // Removes empty space from the user input
+ String displayIndexVal = ParseIndividualValue.parseIndividualValue(userInput, BLANK, DSLASH);
+ // Converts from string to int for comparison
+ int targetIndex = Integer.parseInt(displayIndexVal) - ExpenditureList.LIST_OFFSET;
+ return new EditCommand(targetIndex, userInput);
+ } catch (NumberFormatException | DateTimeParseException e) {
+ return new InvalidCommand(ERROR_WRONG_FORMAT_MESSAGE.toString());
+ } catch (StringIndexOutOfBoundsException s) {
+ return new InvalidCommand(ERROR_NUMBER_FORMAT_MESSAGE.toString());
+ } catch (EmptyStringException a) {
+ return new InvalidCommand(ERROR_EMPTY_STRING_MESSAGE.toString());
+ }
+ }
+}
diff --git a/src/main/java/seedu/parser/ParseFind.java b/src/main/java/seedu/parser/ParseFind.java
new file mode 100644
index 0000000000..7bef9b6409
--- /dev/null
+++ b/src/main/java/seedu/parser/ParseFind.java
@@ -0,0 +1,25 @@
+package seedu.parser;
+
+import seedu.commands.Command;
+import seedu.commands.FindCommand;
+import seedu.commands.InvalidCommand;
+import seedu.exceptions.EmptyStringException;
+
+public class ParseFind {
+ public static final String BLANK = "";
+ private final String userInput;
+
+ public ParseFind(String userInput) {
+ this.userInput = userInput;
+ }
+
+ public Command findExpenditure() {
+ try {
+ String keyword = ParseIndividualValue.parseIndividualValue(userInput, BLANK, BLANK);
+ return new FindCommand(keyword);
+ } catch (StringIndexOutOfBoundsException | EmptyStringException s) {
+ return new InvalidCommand(s.getMessage());
+ }
+ }
+
+}
diff --git a/src/main/java/seedu/parser/ParseIndividualValue.java b/src/main/java/seedu/parser/ParseIndividualValue.java
new file mode 100644
index 0000000000..1dd17220c4
--- /dev/null
+++ b/src/main/java/seedu/parser/ParseIndividualValue.java
@@ -0,0 +1,40 @@
+package seedu.parser;
+
+import seedu.exceptions.EmptyStringException;
+import seedu.exceptions.ExceptionChecker;
+
+public class ParseIndividualValue {
+
+ private static final int OFFSET_DELIMITER = 2;
+ private static final int DOT_OFFSET_DELIMITER = 1;
+ private static final String BLANK = "";
+ private static final String DOT = ".";
+
+ public static String parseIndividualValue(String userInput, String front, String back)
+ throws StringIndexOutOfBoundsException, EmptyStringException {
+ int positionOfFirstSlash;
+ // Checks for the position of the front slash
+ if (!front.equals(BLANK)) {
+ positionOfFirstSlash = userInput.indexOf(front);
+ } else {
+ positionOfFirstSlash = -OFFSET_DELIMITER;
+ }
+
+ int positionOfSecondSlash;
+ // Checks for the position of the backslash
+ if (!back.equals(BLANK)) {
+ positionOfSecondSlash = userInput.indexOf(back);
+ } else {
+ positionOfSecondSlash = userInput.length();
+ }
+ // Parses the value in between the front and backslash
+ String value;
+ if (front.equals(DOT)) {
+ value = userInput.substring(positionOfFirstSlash + DOT_OFFSET_DELIMITER, positionOfSecondSlash).trim();
+ } else {
+ value = userInput.substring(positionOfFirstSlash + OFFSET_DELIMITER, positionOfSecondSlash).trim();
+ }
+ ExceptionChecker.checkEmptyString(value);
+ return value;
+ }
+}
diff --git a/src/main/java/seedu/parser/ParseLendBorrow.java b/src/main/java/seedu/parser/ParseLendBorrow.java
new file mode 100644
index 0000000000..4628da6cae
--- /dev/null
+++ b/src/main/java/seedu/parser/ParseLendBorrow.java
@@ -0,0 +1,123 @@
+package seedu.parser;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeParseException;
+import seedu.commands.Command;
+import seedu.commands.LendExpenditureCommand;
+import seedu.commands.BorrowExpenditureCommand;
+import seedu.commands.InvalidCommand;
+import seedu.exceptions.ExceptionChecker;
+import seedu.exceptions.SmallAmountException;
+import seedu.exceptions.InvalidCharacterInAmount;
+import seedu.exceptions.NotPositiveValueException;
+import seedu.exceptions.DateLimitException;
+import seedu.exceptions.EmptyStringException;
+import seedu.exceptions.InvalidDeadlineException;
+import seedu.exceptions.InvalidDateException;
+import seedu.exceptions.WrongPrecisionException;
+import seedu.exceptions.LargeValueException;
+import static seedu.ui.ErrorMessages.ERROR_DATE_TIME_ERROR_MESSAGE;
+import static seedu.ui.ErrorMessages.ERROR_COMMAND_NOT_RECOGNISED_MESSAGE;
+import static seedu.ui.ErrorMessages.ERROR_AMOUNT_FORMAT_MESSAGE;
+import static seedu.ui.ErrorMessages.ERROR_NUMBER_FORMAT_MESSAGE;
+import static seedu.ui.ErrorMessages.ERROR_EMPTY_STRING_MESSAGE;
+import static seedu.ui.ErrorMessages.ERROR_NOT_POSITIVE_VALUE_MESSAGE;
+import static seedu.ui.ErrorMessages.ERROR_INVALID_AMOUNT_PRECISION;
+import static seedu.ui.ErrorMessages.ERROR_INVALID_AMOUNT_TOO_LARGE;
+
+public class ParseLendBorrow {
+ public static final String BLANK = "";
+ public static final String DSLASH = "d/";
+ public static final String ASLASH = "a/";
+ public static final String PSLASH = "p/";
+ public static final String BSLASH = "b/";
+ public static final String NSLASH = "n/";
+ public static final String DOT = ".";
+ private final String userInput;
+
+ public ParseLendBorrow(String userInput) {
+ this.userInput = userInput;
+ }
+
+ /**
+ * @author itszhixuan
+ * @throws DateLimitException
+ */
+ public Command addItem(String command) throws NotPositiveValueException, InvalidDateException, DateLimitException {
+ try {
+ // Format: category d/date, n/name, a/amount, b/deadline, s/description
+
+ LocalDate date = fetchDate();
+ String name = fetchName();
+ double amount = fetchDouble();
+ LocalDate deadline = fetchDeadline();
+ String descriptionVal = fetchDescription();
+ ExceptionChecker.checkDate(date, deadline);
+ ExceptionChecker.checkDateLimit(date);
+
+ // Differentiates between lend and borrow
+ switch (command) {
+ case LendExpenditureCommand.COMMAND_WORD:
+ return new LendExpenditureCommand(descriptionVal, name, amount, date, deadline);
+ case BorrowExpenditureCommand.COMMAND_WORD:
+ return new BorrowExpenditureCommand(descriptionVal, name, amount, date, deadline);
+ default:
+ return new InvalidCommand(ERROR_COMMAND_NOT_RECOGNISED_MESSAGE.toString());
+ }
+
+ } catch (DateTimeParseException d) {
+ return new InvalidCommand(ERROR_DATE_TIME_ERROR_MESSAGE.toString());
+ } catch (NumberFormatException n) {
+ return new InvalidCommand(ERROR_AMOUNT_FORMAT_MESSAGE.toString());
+ } catch (StringIndexOutOfBoundsException n) {
+ return new InvalidCommand(ERROR_NUMBER_FORMAT_MESSAGE.toString());
+ } catch (EmptyStringException e) {
+ return new InvalidCommand(ERROR_EMPTY_STRING_MESSAGE.toString());
+ } catch (InvalidDeadlineException | SmallAmountException | InvalidCharacterInAmount | InvalidDateException e) {
+ return new InvalidCommand(e.getMessage());
+ } catch (NotPositiveValueException p) {
+ return new InvalidCommand(ERROR_NOT_POSITIVE_VALUE_MESSAGE.toString());
+ } catch (LargeValueException l) {
+ return new InvalidCommand(ERROR_INVALID_AMOUNT_TOO_LARGE.toString());
+ } catch (WrongPrecisionException e) {
+ return new InvalidCommand(ERROR_INVALID_AMOUNT_PRECISION.toString());
+ }
+ }
+
+ public String fetchDescription() throws EmptyStringException, StringIndexOutOfBoundsException {
+ // Extracts the fields from user input
+ return ParseIndividualValue.parseIndividualValue(userInput, PSLASH, BLANK);
+ }
+
+ public String fetchName() throws EmptyStringException, StringIndexOutOfBoundsException {
+ // Extracts the fields from user input
+ return ParseIndividualValue.parseIndividualValue(userInput, NSLASH, ASLASH);
+ }
+
+ public double fetchDouble() throws InvalidCharacterInAmount, EmptyStringException,
+ StringIndexOutOfBoundsException, SmallAmountException, NotPositiveValueException, NumberFormatException,
+ WrongPrecisionException, LargeValueException {
+ // Converts from string to double for numerical addition functionalities
+ String amountVal = ParseIndividualValue.parseIndividualValue(userInput, ASLASH, BSLASH);
+ ExceptionChecker.checkIfMoreThanTwoDecimalPlaces(amountVal, DOT, BLANK);
+ ExceptionChecker.checkValidDoubleInput(amountVal);
+ double amount = Double.parseDouble(amountVal);
+ ExceptionChecker.checkValidAmount(amount);
+ ExceptionChecker.checkLargeValue(amount);
+ return Double.parseDouble(amountVal);
+ }
+
+ public LocalDate fetchDate() throws EmptyStringException, StringIndexOutOfBoundsException,
+ DateTimeParseException {
+ // Converts from string to date to fit Command class
+ String dateVal = ParseIndividualValue.parseIndividualValue(userInput, DSLASH, NSLASH);
+ return LocalDate.parse(dateVal);
+ }
+
+ public LocalDate fetchDeadline() throws EmptyStringException, StringIndexOutOfBoundsException,
+ DateTimeParseException {
+ // Converts from string to date to fit Command class
+ String dateVal = ParseIndividualValue.parseIndividualValue(userInput, BSLASH, PSLASH);
+ return LocalDate.parse(dateVal);
+ }
+}
diff --git a/src/main/java/seedu/parser/ParseMark.java b/src/main/java/seedu/parser/ParseMark.java
new file mode 100644
index 0000000000..31f1347aa0
--- /dev/null
+++ b/src/main/java/seedu/parser/ParseMark.java
@@ -0,0 +1,41 @@
+package seedu.parser;
+
+import seedu.commands.Command;
+import seedu.commands.InvalidCommand;
+import seedu.commands.MarkCommand;
+import seedu.commands.UnmarkCommand;
+import seedu.exceptions.EmptyStringException;
+import seedu.expenditure.ExpenditureList;
+
+public class ParseMark {
+ public static final String BLANK = "";
+ private final String userInput;
+
+ public ParseMark(String userInput) {
+ this.userInput = userInput;
+ }
+
+ public Command markExpenditure() {
+ try {
+ String details = ParseIndividualValue.parseIndividualValue(userInput, BLANK, BLANK);
+ int posToMark = Integer.parseInt(details) - ExpenditureList.LIST_OFFSET;
+ return new MarkCommand(posToMark);
+ } catch (NumberFormatException numberFormatException) {
+ return new InvalidCommand("Index to be marked must be an integer and within bounds! Please try again");
+ } catch (StringIndexOutOfBoundsException | EmptyStringException s) {
+ return new InvalidCommand(s.getMessage());
+ }
+ }
+
+ public Command unmarkExpenditure() {
+ try {
+ String details = ParseIndividualValue.parseIndividualValue(userInput, BLANK, BLANK);
+ int posToUnMark = Integer.parseInt(details) - ExpenditureList.LIST_OFFSET;
+ return new UnmarkCommand(posToUnMark);
+ } catch (NumberFormatException numberFormatException) {
+ return new InvalidCommand("Index to be unmarked must be an integer and within bounds! Please try again");
+ } catch (StringIndexOutOfBoundsException | EmptyStringException s) {
+ return new InvalidCommand(s.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/seedu/parser/ParseSetBudget.java b/src/main/java/seedu/parser/ParseSetBudget.java
new file mode 100644
index 0000000000..7bc39df3c3
--- /dev/null
+++ b/src/main/java/seedu/parser/ParseSetBudget.java
@@ -0,0 +1,36 @@
+package seedu.parser;
+
+import seedu.commands.Command;
+import seedu.commands.InvalidCommand;
+import seedu.exceptions.EmptyStringException;
+import seedu.exceptions.ExceptionChecker;
+import seedu.exceptions.NotPositiveValueException;
+import seedu.commands.SetBudgetCommand;
+import seedu.exceptions.SmallAmountException;
+
+import static seedu.ui.ErrorMessages.ERROR_BUDGET_NOT_NUMERICAL_MESSAGE;
+
+public class ParseSetBudget {
+ public static final String BLANK = "";
+ private final String budget;
+
+ public ParseSetBudget(String userInput) {
+ this.budget = userInput;
+ }
+
+ public Command setBudget() {
+ try {
+ // Removes empty space from the user input
+ String amount = ParseIndividualValue.parseIndividualValue(budget,BLANK,BLANK);
+ // Converts from string to double for numerical addition functionalities
+ double budgetAmount = Double.parseDouble(amount);
+ ExceptionChecker.checkValidAmount(budgetAmount);
+ return new SetBudgetCommand(budgetAmount);
+ } catch (NumberFormatException e) {
+ return new InvalidCommand(ERROR_BUDGET_NOT_NUMERICAL_MESSAGE.toString());
+ } catch (EmptyStringException | NotPositiveValueException | SmallAmountException e) {
+ return new InvalidCommand(e.getMessage());
+ }
+ }
+
+}
diff --git a/src/main/java/seedu/parser/ParseSort.java b/src/main/java/seedu/parser/ParseSort.java
new file mode 100644
index 0000000000..1a2a0bd6b0
--- /dev/null
+++ b/src/main/java/seedu/parser/ParseSort.java
@@ -0,0 +1,32 @@
+package seedu.parser;
+
+import seedu.commands.Command;
+import seedu.commands.InvalidCommand;
+import seedu.commands.SortCommand;
+import seedu.exceptions.EmptyStringException;
+import seedu.exceptions.InvalidSortInputException;
+
+public class ParseSort {
+ public static final String BLANK = "";
+ private final String userInput;
+
+ public ParseSort(String userInput) {
+ this.userInput = userInput;
+ }
+
+ public Command sortExpenditures() {
+ try {
+ String details = ParseIndividualValue.parseIndividualValue(userInput, BLANK, BLANK);
+ boolean isInvalidSortInput = !details.equals(SortCommand.AMOUNT_ASCENDING) &&
+ !details.equals(SortCommand.AMOUNT_DESCENDING) &&
+ !details.equals(SortCommand.DATE_FROM_EARLIEST) &&
+ !details.equals(SortCommand.DATE_FROM_LATEST);
+ if (isInvalidSortInput) {
+ throw new InvalidSortInputException();
+ }
+ return new SortCommand(details);
+ } catch (StringIndexOutOfBoundsException | EmptyStringException | InvalidSortInputException e) {
+ return new InvalidCommand(e.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/seedu/txtdata/TxtFileStatus.java b/src/main/java/seedu/txtdata/TxtFileStatus.java
new file mode 100644
index 0000000000..39e0573f57
--- /dev/null
+++ b/src/main/java/seedu/txtdata/TxtFileStatus.java
@@ -0,0 +1,313 @@
+package seedu.txtdata;
+
+import seedu.exceptions.DateLimitException;
+import seedu.exceptions.ExceptionChecker;
+import seedu.exceptions.InvalidCharacterInAmount;
+import seedu.exceptions.InvalidDateException;
+import seedu.exceptions.InvalidDeadlineException;
+import seedu.exceptions.LargeValueException;
+import seedu.exceptions.TxtFileException;
+import seedu.expenditure.Expenditure;
+import seedu.expenditure.AcademicExpenditure;
+import seedu.expenditure.AccommodationExpenditure;
+import seedu.expenditure.BorrowExpenditure;
+import seedu.expenditure.EntertainmentExpenditure;
+import seedu.expenditure.FoodExpenditure;
+import seedu.expenditure.LendExpenditure;
+import seedu.expenditure.OtherExpenditure;
+import seedu.expenditure.TransportExpenditure;
+import seedu.expenditure.TuitionExpenditure;
+import seedu.expenditure.ExpenditureList;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.time.LocalDate;
+import java.time.format.DateTimeParseException;
+import java.util.ArrayList;
+import java.util.Scanner;
+
+public abstract class TxtFileStatus {
+ private static final String directoryPath = "myLedger_data";
+ private static final String filePath = "myLedger_data/myLedger_inputs.txt";
+ private static final int INDEX_TYPE = 0;
+ private static final int INDEX_DESCRIPTION = 1;
+ private static final int INDEX_VALUE = 2;
+ private static final int INDEX_DATE = 3;
+ private static final int INDEX_IS_PAID = 4;
+ private static final int INDEX_NAME = 5;
+ private static final int INDEX_DEADLINE = 6;
+ private static final int INDEX_REPEAT_DATE = 7;
+
+ public static void getSaveFile() throws IOException {
+ File directory = new File(directoryPath);
+ if (!directory.exists()) {
+ directory.mkdir();
+ }
+ File saveFile = new File(filePath);
+ if (!saveFile.exists()) {
+ saveFile.createNewFile();
+ }
+ }
+
+ public static void checkFile() throws FileNotFoundException {
+ try {
+ getSaveFile();
+ } catch (IOException e) {
+ System.out.println("Save File Error");
+ }
+ }
+
+ public static void appendToFile(String filePath, String textToAppend) throws IOException {
+ FileWriter fw = new FileWriter(filePath, true); // create a FileWriter in append mode
+ fw.write(textToAppend);
+ fw.close();
+ }
+
+ public static void writeToFile(String filePath, String textToAdd) throws IOException {
+ FileWriter fw = new FileWriter(filePath);
+ fw.write(textToAdd);
+ fw.close();
+ }
+
+ public static void saveExpenditureList(ArrayList expenditures) throws IOException {
+ writeToFile(filePath, "");
+ for (Expenditure expenditure : expenditures) {
+ appendToFile(filePath, expenditure.saveInfo());
+ }
+ }
+
+ /**
+ * Initializes expenditureList using text file.
+ * If text file has been corrupted such that it can't be read,
+ * ignores that save line is ignored.
+ * @param expenditures
+ * @throws FileNotFoundException
+ */
+ public static void initializeExpenditureList(ExpenditureList expenditures) throws FileNotFoundException {
+ File f = new File(filePath); // create a File for the given file path
+ Scanner s = new Scanner(f); // create a Scanner using the File as the source
+ parseTxtFile(s, expenditures);
+ }
+
+ /**
+ * @author Chick3nBoy
+ */
+ public static void parseTxtFile(Scanner s, ExpenditureList expenditures) {
+ while (s.hasNext()) {
+ try {
+ String saveString = s.nextLine();
+ String[] saveData = saveString.split("d/|v/|t/|p/|n/|o/|r/");
+ checkAmount(saveData[INDEX_VALUE]);
+ ExceptionChecker.checkDateLimit(LocalDate.parse(saveData[INDEX_DATE]));
+ switch (saveData[INDEX_TYPE]) {
+ case "Acad":
+ initializeAcademicExpenditure(saveData, expenditures);
+ break;
+ case "Accom":
+ initializeAccommodationExpenditure(saveData, expenditures);
+ break;
+ case "B":
+ initializeBorrowExpenditure(saveData, expenditures);
+ break;
+ case "En":
+ initializeEntertainmentExpenditure(saveData, expenditures);
+ break;
+ case "F":
+ initializeFoodExpenditure(saveData, expenditures);
+ break;
+ case "L":
+ initializeLendExpenditure(saveData, expenditures);
+ break;
+ case "O":
+ initializeOtherExpenditure(saveData, expenditures);
+ break;
+ case "Tr":
+ initializeTransportExpenditure(saveData, expenditures);
+ break;
+ case "Tu":
+ initializeTuitionExpenditure(saveData, expenditures);
+ break;
+ default:
+ throw new TxtFileException();
+ }
+ } catch (TxtFileException t) {
+ System.out.println(t.getMessage());
+ } catch (DateTimeParseException | NumberFormatException | ArrayIndexOutOfBoundsException |
+ InvalidCharacterInAmount | LargeValueException | InvalidDeadlineException |
+ InvalidDateException | DateLimitException e) {
+ System.out.println(
+ "TxtFile has been corrupted, the corrupted entry has been deleted");
+ }
+ }
+ s.close();
+ }
+
+ /**
+ * @author Leo Zheng Rui Darren
+ */
+ public static void checkAmount(String stringDouble) throws TxtFileException, InvalidCharacterInAmount,
+ LargeValueException {
+ double amount = Double.parseDouble(stringDouble);
+ checkInvalidCharacter(stringDouble);
+ checkNegative(amount);
+ checkLargeValue(amount);
+ }
+
+ /**
+ * @author Chick3nBoy
+ */
+ public static void checkNegative(double amount) throws TxtFileException {
+ if (amount <= 0.01) {
+ throw new TxtFileException();
+ }
+ }
+
+ public static void checkInvalidCharacter(String stringDouble) throws InvalidCharacterInAmount {
+ ExceptionChecker.checkValidDoubleInput(stringDouble);
+ }
+
+ public static void checkLargeValue(double amount) throws LargeValueException {
+ ExceptionChecker.checkLargeValue(amount);
+ }
+
+ public static void checkLendBorrowDate(LocalDate startDate, LocalDate endDate) throws InvalidDeadlineException,
+ InvalidDateException, DateLimitException {
+ ExceptionChecker.checkDate(startDate, endDate);
+ ExceptionChecker.checkDateLimit(startDate);
+ ExceptionChecker.checkDateLimit(endDate);
+ }
+
+ /**
+ * @author Leo Zheng Rui Darren
+ */
+ public static void checkRepeatDate(String stringRepeatDate, String stringFirstDate) throws TxtFileException {
+ LocalDate repeatDate = LocalDate.parse(stringRepeatDate);
+ LocalDate firstDate = LocalDate.parse(stringFirstDate);
+ compareRepeatMonth(repeatDate, firstDate);
+ compareRepeatDay(repeatDate, firstDate);
+ }
+
+ public static void compareRepeatMonth(LocalDate repeatDate, LocalDate firstDate) throws TxtFileException {
+ int repeatMonth = repeatDate.getMonthValue();
+ int firstMonth = firstDate.getMonthValue();
+ boolean isSameMonth = (repeatMonth == firstMonth);
+ if (!isSameMonth) {
+ throw new TxtFileException();
+ }
+ }
+
+ public static void compareRepeatDay(LocalDate repeatDate, LocalDate firstDate) throws TxtFileException {
+ int repeatDay = repeatDate.getDayOfMonth();
+ int firstDay = firstDate.getDayOfMonth();
+ boolean isSameDay = (repeatDay == firstDay);
+ if (!isSameDay) {
+ throw new TxtFileException();
+ }
+ }
+
+ public static void initializeAcademicExpenditure(String[] saveData, ExpenditureList expenditures) {
+ AcademicExpenditure academicExpenditure = new AcademicExpenditure(
+ saveData[INDEX_DESCRIPTION],
+ Double.parseDouble(saveData[INDEX_VALUE]),
+ LocalDate.parse(saveData[INDEX_DATE]));
+ expenditures.addExpenditure(academicExpenditure);
+ }
+
+ public static void initializeAccommodationExpenditure(String[] saveData, ExpenditureList expenditures)
+ throws TxtFileException {
+ checkRepeatDate(saveData[INDEX_REPEAT_DATE], saveData[INDEX_DATE]);
+ AccommodationExpenditure accommodationExpenditure = new AccommodationExpenditure(
+ saveData[INDEX_DESCRIPTION],
+ Double.parseDouble(saveData[INDEX_VALUE]),
+ LocalDate.parse(saveData[INDEX_DATE]),
+ LocalDate.parse(saveData[INDEX_REPEAT_DATE]));
+ if (saveData[INDEX_IS_PAID].equals(AccommodationExpenditure.iconPaid)) {
+ accommodationExpenditure.setPaid();
+ } else if (saveData[INDEX_IS_PAID].equals(AccommodationExpenditure.iconUnpaid)) {
+ accommodationExpenditure.resetPaid();
+ } else {
+ throw new TxtFileException();
+ }
+ expenditures.addExpenditure(accommodationExpenditure);
+ }
+
+ public static void initializeBorrowExpenditure(String[] saveData, ExpenditureList expenditures) throws
+ InvalidDeadlineException, InvalidDateException, DateLimitException {
+ LocalDate startDate = LocalDate.parse(saveData[INDEX_DATE]);
+ LocalDate endDate = LocalDate.parse(saveData[INDEX_DEADLINE]);
+ checkLendBorrowDate(startDate, endDate);
+ BorrowExpenditure borrowExpenditure = new BorrowExpenditure(
+ saveData[INDEX_DESCRIPTION],
+ saveData[INDEX_NAME],
+ Double.parseDouble(saveData[INDEX_VALUE]),
+ LocalDate.parse(saveData[INDEX_DATE]),
+ LocalDate.parse(saveData[INDEX_DEADLINE]));
+ expenditures.addExpenditure(borrowExpenditure);
+ }
+
+ public static void initializeEntertainmentExpenditure(String[] saveData, ExpenditureList expenditures) {
+ EntertainmentExpenditure entertainmentExpenditure = new EntertainmentExpenditure(
+ saveData[INDEX_DESCRIPTION],
+ Double.parseDouble(saveData[INDEX_VALUE]),
+ LocalDate.parse(saveData[INDEX_DATE]));
+ expenditures.addExpenditure(entertainmentExpenditure);
+ }
+
+ public static void initializeFoodExpenditure(String[] saveData, ExpenditureList expenditures) {
+ FoodExpenditure foodExpenditure = new FoodExpenditure(
+ saveData[INDEX_DESCRIPTION],
+ Double.parseDouble(saveData[INDEX_VALUE]),
+ LocalDate.parse(saveData[INDEX_DATE]));
+ expenditures.addExpenditure(foodExpenditure);
+ }
+
+ public static void initializeLendExpenditure(String[] saveData, ExpenditureList expenditures) throws
+ InvalidDeadlineException, InvalidDateException, DateLimitException {
+ LocalDate startDate = LocalDate.parse(saveData[INDEX_DATE]);
+ LocalDate endDate = LocalDate.parse(saveData[INDEX_DEADLINE]);
+ checkLendBorrowDate(startDate, endDate);
+ LendExpenditure lendExpenditure = new LendExpenditure(
+ saveData[INDEX_DESCRIPTION],
+ saveData[INDEX_NAME],
+ Double.parseDouble(saveData[INDEX_VALUE]),
+ LocalDate.parse(saveData[INDEX_DATE]),
+ LocalDate.parse(saveData[INDEX_DEADLINE]));
+ expenditures.addExpenditure(lendExpenditure);
+ }
+
+ public static void initializeOtherExpenditure(String[] saveData, ExpenditureList expenditures) {
+ OtherExpenditure otherExpenditure = new OtherExpenditure(
+ saveData[INDEX_DESCRIPTION],
+ Double.parseDouble(saveData[INDEX_VALUE]),
+ LocalDate.parse(saveData[INDEX_DATE]));
+ expenditures.addExpenditure(otherExpenditure);
+ }
+
+ public static void initializeTransportExpenditure(String[] saveData, ExpenditureList expenditures) {
+ TransportExpenditure transportExpenditure = new TransportExpenditure(
+ saveData[INDEX_DESCRIPTION],
+ Double.parseDouble(saveData[INDEX_VALUE]),
+ LocalDate.parse(saveData[INDEX_DATE]));
+ expenditures.addExpenditure(transportExpenditure);
+ }
+
+ public static void initializeTuitionExpenditure(String[] saveData, ExpenditureList expenditures)
+ throws TxtFileException {
+ checkRepeatDate(saveData[INDEX_REPEAT_DATE], saveData[INDEX_DATE]);
+ TuitionExpenditure tuitionExpenditure = new TuitionExpenditure(
+ saveData[INDEX_DESCRIPTION],
+ Double.parseDouble(saveData[INDEX_VALUE]),
+ LocalDate.parse(saveData[INDEX_DATE]),
+ LocalDate.parse(saveData[INDEX_REPEAT_DATE]));
+ if (saveData[INDEX_IS_PAID].equals(TuitionExpenditure.iconPaid)) {
+ tuitionExpenditure.setPaid();
+ } else if (saveData[INDEX_IS_PAID].equals(TuitionExpenditure.iconUnpaid)) {
+ tuitionExpenditure.resetPaid();
+ } else {
+ throw new TxtFileException();
+ }
+ expenditures.addExpenditure(tuitionExpenditure);
+ }
+}
diff --git a/src/main/java/seedu/ui/ErrorMessages.java b/src/main/java/seedu/ui/ErrorMessages.java
new file mode 100644
index 0000000000..aa83bc5045
--- /dev/null
+++ b/src/main/java/seedu/ui/ErrorMessages.java
@@ -0,0 +1,45 @@
+package seedu.ui;
+
+import java.time.LocalDate;
+
+public enum ErrorMessages {
+
+ ERROR_MARK_MESSAGE("Sorry! This expenditure is already marked!"),
+ ERROR_UNMARK_MESSAGE("Sorry! This expenditure is already unmarked!"),
+ ERROR_EMPTY_STRING_MESSAGE("One of your inputs are empty! Please enter help for the proper format"),
+ ERROR_INVALID_SORT_INPUT_MESSAGE("Invalid sort message!\n " +
+ " to sort amount in ascending order. " +
+ " to sort amount in descending order.\n" +
+ " to sort amount from earliest date added. " +
+ " to sort amount from latest date added."),
+ ERROR_NO_PAID_FIELD_MESSAGE("No paid field for this expenditure!"),
+ ERROR_NOT_POSITIVE_VALUE_MESSAGE("Amount entered must be positive! Please try again"),
+ ERROR_COMMAND_NOT_RECOGNISED_MESSAGE("Command not recognised. Please try again"),
+ ERROR_LACK_OF_PARAMETERS_MESSAGE("Input command does not have required parameters! Please try again"),
+ ERROR_DATE_TIME_ERROR_MESSAGE("Date error! Please enter a single date in yyyy-mm-dd format!"),
+ ERROR_INVALID_INPUT_MESSAGE("Invalid input! Please try again"),
+ ERROR_NUMBER_FORMAT_MESSAGE("Index must be an integer and within bounds! Please try again"),
+ ERROR_AMOUNT_FORMAT_MESSAGE("The amount you provided is not in the right format! " +
+ "Please enter a single number value"),
+ ERROR_WRONG_FORMAT_MESSAGE("Input command cannot be recognised as it is in the wrong format. Please try again"),
+ ERROR_BUDGET_NOT_NUMERICAL_MESSAGE("Budget amount is not a numerical value!"),
+ ERROR_INVALID_DATE_MESSAGE("Borrow date must be before the return date! Please try again"),
+ ERROR_INVALID_DEADLINE_MESSAGE(
+ "Return date should be equal to or later than today's date!" + "Today's date is " + LocalDate.now()),
+ ERROR_INVALID_AMOUNT_PRECISION("Precision of amount is more than 2 decimal places. " +
+ "Please re-enter a valid amount value"),
+ ERROR_DATE_LIMIT_MESSAGE("Only dates from the year 2000 and onwards are accepted. Earlier dates are not supported"),
+ ERROR_YEAR_LIMIT_MESSAGE("Only years 2000 and onwards are accepted. Earlier years are not supported"),
+ ERROR_INVALID_AMOUNT_TOO_LARGE("The amount you have entered is too large please input a smaller value!");
+
+ public final String message;
+
+ ErrorMessages(String message) {
+ this.message = message;
+ }
+
+ public String toString() {
+ return message;
+ }
+
+}
diff --git a/src/main/java/seedu/ui/Ui.java b/src/main/java/seedu/ui/Ui.java
new file mode 100644
index 0000000000..6b235eecb5
--- /dev/null
+++ b/src/main/java/seedu/ui/Ui.java
@@ -0,0 +1,97 @@
+package seedu.ui;
+
+public class Ui {
+ public static final String LOGO = " _ _ _ _ __ ____ ____ ___ ____ ____\n"
+ + "( \\/ )( \\/ )( ) ( __)( \\ / __)( __)( _ \\\n" +
+ "/ \\/ \\ ) / / (_/\\ ) _) ) D (( (_ \\ ) _) ) / \n" +
+ "\\_)(_/(__/ \\____/(____)(____/ \\___/(____)(__\\_)\n";
+ public static final String HORIZONTAL_LINE = "===================================================" +
+ "=====================================================================\n";
+ public static final String WELCOME_MESSAGE = "Hello there, Welcome to\n";
+ public static final String INSTRUCTIONS = "\nMyLedger is a University financial manager made for students!\n";
+ public static final String HELP_MESSAGE = "If this is your first time here, "
+ + "Type to learn more about the commands\n";
+ public static final String HELP_PAGE = "\nHere are the list of commands and their respective functions. "
+ + "Please take note of the FORMAT that is stated below\n"
+ + HORIZONTAL_LINE +
+ "\n1. Add an expenditure to the record\n" +
+ "Format: EXPENDITURE_TYPE d/DATE a/AMOUNT p/DESCRIPTION\n" +
+ "There are 7 EXPENDITURE_TYPE: 'Academic', 'Accommodation', 'Entertainment', " +
+ "'Food', 'Transport', 'Tuition', 'Other'\n" +
+ "Example: academic d/2023-02-02 a/25.10 p/NUS\n" +
+ HORIZONTAL_LINE +
+ "\n2. Add a lend/borrow record\n" +
+ "Format: TYPE d/DATE n/NAME a/AMOUNT b/DEADLINE p/DESCRIPTION\n" +
+ "TYPE should be either 'lend' or 'borrow'\n" +
+ "Example: lend d/2022-02-02 n/Akshay Narayan a/25.10 b/2024-07-14 p/CS2113\n" +
+ HORIZONTAL_LINE +
+ "\n3. Delete a specified expenditure from the record\n" +
+ "Format: delete INDEX\n" +
+ "Example: delete 1 \n" +
+ HORIZONTAL_LINE +
+ "\n4. Edit an expenditure\n"
+ + "Format: edit INDEX d/DATE a/AMOUNT p/DESCRIPTION\n" +
+ "Cannot change an expenditure type\n" +
+ "Example: edit 2 d/2023-02-15 a/20.00 p/Eat Food\n" +
+ HORIZONTAL_LINE +
+ "\n5. Edit a Lend/Borrow record\n"
+ + "Format: edit INDEX d/DATE n/NAME a/AMOUNT b/DEADLINE p/DESCRIPTION\n" +
+ "Cannot change a lend record to a borrow record\n" +
+ "Example: edit 17 d/2022-02-02 n/Akshay Narayan a/25.10 b/2024-07-14 p/CS2113\n" +
+ HORIZONTAL_LINE +
+ "\n6. List all expenditures in the record in the specified currency\n"
+ + "Format: list CURRENCY \n" + "A currency has to be specified for list to display.\n"
+ + "Example: list SGD \n" +
+ HORIZONTAL_LINE +
+ "\n7. Sort the expenditure list\n"
+ + "Format: sort TYPE\n" +
+ "Types:\n" +
+ "ascend: sort by ascending amount || descend: sort by descending amount\n" +
+ "latest: sort from latest date || earliest: sort from earliest date\n" +
+ "Example: sort latest\n" +
+ HORIZONTAL_LINE +
+ "\n8. View all expenditures and their total amount in the record BY DATE\n"
+ + "Format: viewdate DATE CURRENCY\n"
+ + "Example: viewdate 2023-03-29 SGD\n" +
+ HORIZONTAL_LINE +
+ "\n9. View all expenditures and their total amount in the record BY CATEGORY\n"
+ + "Format: viewtype CATEGORY CURRENCY\n"
+ + "There are 9 CATEGORIES: 'academic', 'accommodation', 'entertainment'," +
+ "'food', 'transport', 'tuition', 'other', 'borrow', 'lend'\n"
+ + "Example: viewtype food SGD\n" +
+ HORIZONTAL_LINE +
+ "\n10. Set a fixed budget for money management\n"
+ + "Format: set AMOUNT\n"
+ + "Example: set 150\n" +
+ HORIZONTAL_LINE +
+ "\n11. Check expenditure with respect to a allocated budget, which accepts an optional filters\n"
+ + "Format: check\n \n" +
+ "List of optional filters: \n \n" +
+ "By date(yyyy-mm-dd)\n" + "Format: check d/DATE\n" +
+ "Example of checking budget by date: check d/2023-03-29\n \n" +
+ "By year(yyyy)\n" + "Format: check y/YEAR\n"
+ + "Example of checking budget by year: check y/2023\n \n" +
+ "By expenditure category \n" + "Format: check t/CATEGORY\n" +
+ "Example of checking budget by category: check t/academic\n" +
+ HORIZONTAL_LINE +
+ "\n12. Finding expenditure records by description keyword\n"
+ + "Format: find keyword\n" +
+ HORIZONTAL_LINE +
+ "\n13. Duplicates an existing expenditure record from the expenditure list and append to the list\n"
+ + "Format: duplicate INDEX\n" +
+ HORIZONTAL_LINE +
+ "\n14. Display the conversion rates used in MyLedger\n"
+ + "Format: showrates\n" +
+ HORIZONTAL_LINE +
+ "\n15. Marking a lend or borrow expenditure record\n"
+ + "Format: mark INDEX\n" +
+ HORIZONTAL_LINE +
+ "\n16. Unmarking a lend or borrow expenditure record\n"
+ + "Format: unmark INDEX\n" +
+ HORIZONTAL_LINE;
+
+ public static void greetUser() {
+ System.out.println(WELCOME_MESSAGE + LOGO + INSTRUCTIONS + HELP_MESSAGE);
+ }
+
+}
diff --git a/src/test/java/seedu/commands/AcademicExpenditureCommandTest.java b/src/test/java/seedu/commands/AcademicExpenditureCommandTest.java
new file mode 100644
index 0000000000..c6209827cc
--- /dev/null
+++ b/src/test/java/seedu/commands/AcademicExpenditureCommandTest.java
@@ -0,0 +1,41 @@
+package seedu.commands;
+
+import org.junit.jupiter.api.Test;
+import seedu.expenditure.AcademicExpenditure;
+import seedu.expenditure.ExpenditureList;
+import java.time.LocalDate;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class AcademicExpenditureCommandTest {
+ ExpenditureList testExpenditures = new ExpenditureList();
+
+ @Test
+ public void test_academicExpenditureCommand_execute() {
+ AcademicExpenditureCommand testAcademicExpenditureCommand = new AcademicExpenditureCommand(
+ "new laptop",
+ 1500.5,
+ LocalDate.parse("2023-02-01"));
+ assertEquals("Added academic expenditure: " +
+ "[Academic] || Date: 1 Feb 2023 || Value: 1500.5 || Description: new laptop",
+ testAcademicExpenditureCommand.execute(testExpenditures).getCommandResult());
+ assertEquals("1. [Academic] || Date: 1 Feb 2023 || Value: 1500.5 || Description: new laptop",
+ testExpenditures.toString());
+ }
+
+ @Test
+ public void test_academicExpenditureCommand_executeWithExpendituresAlreadyInList() {
+ testExpenditures.addExpenditure(new AcademicExpenditure("laptop",
+ 1500,
+ LocalDate.parse("2021-08-01")));
+ AcademicExpenditureCommand testAcademicExpenditureCommand = new AcademicExpenditureCommand(
+ "new laptop",
+ 1500.5,
+ LocalDate.parse("2023-02-01"));
+ assertEquals("Added academic expenditure: " +
+ "[Academic] || Date: 1 Feb 2023 || Value: 1500.5 || Description: new laptop",
+ testAcademicExpenditureCommand.execute(testExpenditures).getCommandResult());
+ assertEquals("1. [Academic] || Date: 1 Aug 2021 || Value: 1500.0 || Description: laptop\n" +
+ "2. [Academic] || Date: 1 Feb 2023 || Value: 1500.5 || Description: new laptop",
+ testExpenditures.toString());
+ }
+}
diff --git a/src/test/java/seedu/commands/AccommodationExpenditureCommandTest.java b/src/test/java/seedu/commands/AccommodationExpenditureCommandTest.java
new file mode 100644
index 0000000000..d2c43ae616
--- /dev/null
+++ b/src/test/java/seedu/commands/AccommodationExpenditureCommandTest.java
@@ -0,0 +1,44 @@
+package seedu.commands;
+
+import org.junit.jupiter.api.Test;
+import seedu.expenditure.AcademicExpenditure;
+import seedu.expenditure.ExpenditureList;
+import java.time.LocalDate;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class AccommodationExpenditureCommandTest {
+ ExpenditureList testExpenditures = new ExpenditureList();
+
+ @Test
+ public void test_accommodationExpenditureCommand_execute() {
+ AccommodationExpenditureCommand testAccommodationExpenditureCommand = new AccommodationExpenditureCommand(
+ "sheares hall",
+ 2300.7,
+ LocalDate.parse("2021-08-01"),
+ LocalDate.parse("2021-08-01"));
+ assertEquals("Added accommodation expenditure: " +
+ "[Accommodation] || [ ] || Date: 1 Aug 2021 || Value: 2300.7 || Description: sheares hall",
+ testAccommodationExpenditureCommand.execute(testExpenditures).getCommandResult());
+ assertEquals("1. [Accommodation] || [ ] || Date: 1 Aug 2021 || Value: 2300.7 || " +
+ "Description: sheares hall",
+ testExpenditures.toString());
+ }
+
+ @Test
+ public void test_accommodationExpenditureCommand_executeWithExpendituresAlreadyInList() {
+ testExpenditures.addExpenditure(new AcademicExpenditure("laptop",
+ 1500,
+ LocalDate.parse("2021-08-01")));
+ AccommodationExpenditureCommand testAccommodationExpenditureCommand = new AccommodationExpenditureCommand(
+ "sheares hall",
+ 2300.7,
+ LocalDate.parse("2021-08-01"),
+ LocalDate.parse("2021-08-01"));
+ assertEquals("Added accommodation expenditure: " +
+ "[Accommodation] || [ ] || Date: 1 Aug 2021 || Value: 2300.7 || Description: sheares hall",
+ testAccommodationExpenditureCommand.execute(testExpenditures).getCommandResult());
+ assertEquals("1. [Academic] || Date: 1 Aug 2021 || Value: 1500.0 || Description: laptop\n" +
+ "2. [Accommodation] || [ ] || Date: 1 Aug 2021 || Value: 2300.7 || Description: sheares hall",
+ testExpenditures.toString());
+ }
+}
diff --git a/src/test/java/seedu/commands/BorrowExpenditureCommandTest.java b/src/test/java/seedu/commands/BorrowExpenditureCommandTest.java
new file mode 100644
index 0000000000..b17d71ddf9
--- /dev/null
+++ b/src/test/java/seedu/commands/BorrowExpenditureCommandTest.java
@@ -0,0 +1,47 @@
+package seedu.commands;
+
+import org.junit.jupiter.api.Test;
+import seedu.expenditure.AcademicExpenditure;
+import seedu.expenditure.ExpenditureList;
+import java.time.LocalDate;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class BorrowExpenditureCommandTest {
+ ExpenditureList testExpenditures = new ExpenditureList();
+
+ @Test
+ public void test_borrowExpenditureCommand_execute() {
+ BorrowExpenditureCommand testBorrowExpenditureCommand = new BorrowExpenditureCommand(
+ "loan",
+ "DBS",
+ 20000,
+ LocalDate.parse("2021-08-01"),
+ LocalDate.parse("2025-05-05"));
+ assertEquals("Added borrow expenditure: [Borrow] || Borrowed from: DBS || Date: 1 Aug 2021 "
+ + "|| Value: 20000.0 || Description: loan || By: 5 May 2025",
+ testBorrowExpenditureCommand.execute(testExpenditures).getCommandResult());
+ assertEquals("1. [Borrow] || Borrowed from: DBS || Date: 1 Aug 2021 || Value: 20000.0 " +
+ "|| Description: loan || By: 5 May 2025", testExpenditures.toString());
+ }
+
+ @Test
+ public void test_borrowExpenditureCommand_executeWithExpendituresAlreadyInList() {
+ testExpenditures.addExpenditure(new AcademicExpenditure("laptop",
+ 1500,
+ LocalDate.parse("2021-08-01")));
+ BorrowExpenditureCommand testBorrowExpenditureCommand = new BorrowExpenditureCommand(
+ "loan",
+ "DBS",
+ 20000,
+ LocalDate.parse("2021-08-01"),
+ LocalDate.parse("2025-05-05"));
+ assertEquals("Added borrow expenditure: [Borrow] || Borrowed from: DBS || Date: 1 Aug 2021 " +
+ "|| Value: 20000.0 || Description: loan || By: 5 May 2025",
+ testBorrowExpenditureCommand.execute(testExpenditures).getCommandResult());
+ assertEquals("1. [Academic] || Date: 1 Aug 2021 || Value: 1500.0 || Description: laptop\n" +
+ "2. [Borrow] || Borrowed from: DBS || Date: 1 Aug 2021 || Value: 20000.0 || "
+ +
+ "Description: loan || By: 5 May 2025",
+ testExpenditures.toString());
+ }
+}
diff --git a/src/test/java/seedu/commands/DeleteCommandTest.java b/src/test/java/seedu/commands/DeleteCommandTest.java
new file mode 100644
index 0000000000..979a6087f2
--- /dev/null
+++ b/src/test/java/seedu/commands/DeleteCommandTest.java
@@ -0,0 +1,81 @@
+package seedu.commands;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import seedu.expenditure.AcademicExpenditure;
+import seedu.expenditure.ExpenditureList;
+import seedu.expenditure.FoodExpenditure;
+import seedu.expenditure.TransportExpenditure;
+import java.time.LocalDate;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class DeleteCommandTest {
+ ExpenditureList testExpenditures = new ExpenditureList();
+
+ @BeforeEach
+ public void setUpExpenditureList() {
+ testExpenditures.addExpenditure(
+ new AcademicExpenditure("pen", 2.10, LocalDate.parse("2023-01-01")));
+ testExpenditures.addExpenditure(
+ new FoodExpenditure("chicken rice", 4.50, LocalDate.parse("2023-03-21")));
+ testExpenditures.addExpenditure(
+ new TransportExpenditure("circle line", 2.10, LocalDate.parse("2023-03-21")));
+ }
+
+ @Test
+ public void test_deleteCommand_onZeroIndex() {
+ DeleteCommand testDeleteZeroIndex = new DeleteCommand(0);
+ assertEquals("Entry has been deleted\n" +
+ "Here is your updated list: \n" +
+ "1. [Food] || Date: 21 Mar 2023 || Value: 4.5 || Description: chicken rice\n" +
+ "2. [Transport] || Date: 21 Mar 2023 || Value: 2.1 || Description: circle line",
+ testDeleteZeroIndex.execute(testExpenditures).getCommandResult());
+ }
+
+ @Test
+ public void test_deleteCommand_onOneIndex() {
+ DeleteCommand testDeleteOneIndex = new DeleteCommand(1);
+ assertEquals("Entry has been deleted\n" +
+ "Here is your updated list: \n" +
+ "1. [Academic] || Date: 1 Jan 2023 || Value: 2.1 || Description: pen\n" +
+ "2. [Transport] || Date: 21 Mar 2023 || Value: 2.1 || Description: circle line",
+ testDeleteOneIndex.execute(testExpenditures).getCommandResult());
+ }
+
+ @Test
+ public void test_deleteCommand_onTwoIndex() {
+ DeleteCommand testDeleteTwoIndex = new DeleteCommand(2);
+ assertEquals("Entry has been deleted\n" +
+ "Here is your updated list: \n" +
+ "1. [Academic] || Date: 1 Jan 2023 || Value: 2.1 || Description: pen\n" +
+ "2. [Food] || Date: 21 Mar 2023 || Value: 4.5 || Description: chicken rice",
+ testDeleteTwoIndex.execute(testExpenditures).getCommandResult());
+ }
+
+ @Test
+ public void test_deleteCommand_onAllExpenditures() {
+ DeleteCommand testDeleteZeroIndex = new DeleteCommand(0);
+ testDeleteZeroIndex.execute(testExpenditures);
+ testDeleteZeroIndex.execute(testExpenditures);
+ testDeleteZeroIndex.execute(testExpenditures);
+ assertEquals("", testExpenditures.toString());
+ }
+
+ @Test
+ public void test_deleteCommand_afterDeletingAllExpenditures() {
+ DeleteCommand testDeleteZeroIndex = new DeleteCommand(0);
+ testDeleteZeroIndex.execute(testExpenditures);
+ testDeleteZeroIndex.execute(testExpenditures);
+ testDeleteZeroIndex.execute(testExpenditures);
+ assertEquals("Index is out of bounds or negative",
+ testDeleteZeroIndex.execute(testExpenditures).getCommandResult());
+ }
+
+ @Test
+ public void test_deleteCommand_onOutOfBoundsIndex() {
+ DeleteCommand testDeleteOutOfBoundsIndex = new DeleteCommand(5);
+ assertEquals("Index is out of bounds or negative",
+ testDeleteOutOfBoundsIndex.execute(testExpenditures).getCommandResult());
+ }
+
+}
diff --git a/src/test/java/seedu/commands/EditCommandTest.java b/src/test/java/seedu/commands/EditCommandTest.java
new file mode 100644
index 0000000000..2ac917e172
--- /dev/null
+++ b/src/test/java/seedu/commands/EditCommandTest.java
@@ -0,0 +1,58 @@
+package seedu.commands;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import seedu.expenditure.AcademicExpenditure;
+import seedu.expenditure.ExpenditureList;
+import seedu.expenditure.FoodExpenditure;
+import seedu.expenditure.TransportExpenditure;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.time.LocalDate;
+
+class EditCommandTest {
+ ExpenditureList testExpenditures = new ExpenditureList();
+
+ @BeforeEach
+ public void setUpExpenditureList() {
+ testExpenditures.addExpenditure(
+ new AcademicExpenditure("pen", 2.10, LocalDate.parse("2023-01-01")));
+ testExpenditures.addExpenditure(
+ new FoodExpenditure("chicken rice", 4.50, LocalDate.parse("2023-03-21")));
+ testExpenditures.addExpenditure(
+ new TransportExpenditure("circle line", 2.10, LocalDate.parse("2023-03-21")));
+ }
+
+ @Test
+ public void test_editCommand_onZeroIndex() {
+ EditCommand testEditZeroIndex = new EditCommand(0, "edit 1 d/2023-02-15 a/20 p/Eat Food");
+ assertEquals("Edited! Here is the updated list:\n" +
+ "1. [Academic] || Date: 15 Feb 2023 || Value: 20.0 || Description: Eat Food\n" +
+ "2. [Food] || Date: 21 Mar 2023 || Value: 4.5 || Description: chicken rice\n" +
+ "3. [Transport] || Date: 21 Mar 2023 || Value: 2.1 || Description: circle line",
+ testEditZeroIndex.execute(testExpenditures).getCommandResult());
+ }
+
+ @Test
+ public void test_editCommand_onOneIndex() {
+ EditCommand testEditOneIndex = new EditCommand(1, "edit 2 d/2023-02-15 a/20 p/Eat Food");
+ assertEquals("Edited! Here is the updated list:\n" +
+ "1. [Academic] || Date: 1 Jan 2023 || Value: 2.1 || Description: pen\n" +
+ "2. [Food] || Date: 15 Feb 2023 || Value: 20.0 || Description: Eat Food\n"
+ + "3. [Transport] || Date: 21 Mar 2023 || Value: 2.1 || Description: circle line",
+ testEditOneIndex.execute(testExpenditures).getCommandResult());
+ }
+
+ @Test
+ public void test_editCommand_onTwoIndex() {
+ EditCommand testEditTwoIndex = new EditCommand(2, "edit 3 d/2023-02-15 a/20 p/Eat Food");
+ assertEquals("Edited! Here is the updated list:\n" +
+ "1. [Academic] || Date: 1 Jan 2023 || Value: 2.1 || Description: pen\n" +
+ "2. [Food] || Date: 21 Mar 2023 || Value: 4.5 || Description: chicken rice\n"
+ + "3. [Transport] || Date: 15 Feb 2023 || Value: 20.0 || Description: Eat Food",
+ testEditTwoIndex.execute(testExpenditures).getCommandResult());
+ }
+
+}
diff --git a/src/test/java/seedu/commands/EntertainmentExpenditureCommandTest.java b/src/test/java/seedu/commands/EntertainmentExpenditureCommandTest.java
new file mode 100644
index 0000000000..c53a71a2e5
--- /dev/null
+++ b/src/test/java/seedu/commands/EntertainmentExpenditureCommandTest.java
@@ -0,0 +1,45 @@
+package seedu.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.time.LocalDate;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.expenditure.EntertainmentExpenditure;
+import seedu.expenditure.ExpenditureList;
+
+public class EntertainmentExpenditureCommandTest {
+ ExpenditureList testExpenditures = new ExpenditureList();
+
+ @Test
+ public void test_entertainmentExpenditureCommand_execute() {
+ EntertainmentExpenditureCommand testEntertainmentExpenditureCommand = new EntertainmentExpenditureCommand(
+ "darts", 10, LocalDate.parse("2023-03-01"));
+ assertEquals(
+ "Added entertainment expenditure: [Entertainment] || Date: 1 Mar 2023 || Value: 10.0 "
+ + "|| Description: darts",
+ testEntertainmentExpenditureCommand.execute(testExpenditures).getCommandResult());
+ assertEquals("1. [Entertainment] || Date: 1 Mar 2023 || Value: 10.0 || Description: darts",
+ testExpenditures.toString());
+ }
+
+ @Test
+ public void test_entertainmentExpenditureCommand_executeWithExpendituresAlreadyInList() {
+ testExpenditures.addExpenditure(new EntertainmentExpenditure("laptop",
+ 1500,
+ LocalDate.parse("2021-08-01")));
+ EntertainmentExpenditureCommand testEntertainmentExpenditureCommand = new EntertainmentExpenditureCommand(
+ "darts",
+ 10,
+ LocalDate.parse("2023-03-01"));
+ assertEquals(
+ "Added entertainment expenditure: [Entertainment] || Date: 1 Mar 2023 || Value: 10.0 "
+ + "|| Description: darts",
+ testEntertainmentExpenditureCommand.execute(testExpenditures).getCommandResult());
+ assertEquals(
+ "1. [Entertainment] || Date: 1 Aug 2021 || Value: 1500.0 || Description: laptop\n"
+ + "2. [Entertainment] || Date: 1 Mar 2023 || Value: 10.0 || Description: darts",
+ testExpenditures.toString());
+ }
+}
diff --git a/src/test/java/seedu/commands/FoodExpenditureCommandTest.java b/src/test/java/seedu/commands/FoodExpenditureCommandTest.java
new file mode 100644
index 0000000000..06c91dc6c8
--- /dev/null
+++ b/src/test/java/seedu/commands/FoodExpenditureCommandTest.java
@@ -0,0 +1,41 @@
+package seedu.commands;
+
+import org.junit.jupiter.api.Test;
+import seedu.expenditure.AcademicExpenditure;
+import seedu.expenditure.ExpenditureList;
+import java.time.LocalDate;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class FoodExpenditureCommandTest {
+ ExpenditureList testExpenditures = new ExpenditureList();
+
+ @Test
+ public void test_foodExpenditureCommand_execute() {
+ FoodExpenditureCommand testFoodExpenditureCommand = new FoodExpenditureCommand(
+ "chicken rice",
+ 4.5,
+ LocalDate.parse("2023-03-21"));
+ assertEquals("Added food expenditure: [Food] || Date: 21 Mar 2023 || Value: 4.5 || " +
+ "Description: chicken rice",
+ testFoodExpenditureCommand.execute(testExpenditures).getCommandResult());
+ assertEquals("1. [Food] || Date: 21 Mar 2023 || Value: 4.5 || Description: chicken rice",
+ testExpenditures.toString());
+ }
+
+ @Test
+ public void test_foodExpenditureCommand_executeWithExpendituresAlreadyInList() {
+ testExpenditures.addExpenditure(new AcademicExpenditure("laptop",
+ 1500,
+ LocalDate.parse("2021-08-01")));
+ FoodExpenditureCommand testFoodExpenditureCommand = new FoodExpenditureCommand(
+ "chicken rice",
+ 4.5,
+ LocalDate.parse("2023-03-21"));
+ assertEquals("Added food expenditure: [Food] || Date: 21 Mar 2023 || Value: 4.5 || " +
+ "Description: chicken rice",
+ testFoodExpenditureCommand.execute(testExpenditures).getCommandResult());
+ assertEquals("1. [Academic] || Date: 1 Aug 2021 || Value: 1500.0 || Description: laptop\n" +
+ "2. [Food] || Date: 21 Mar 2023 || Value: 4.5 || Description: chicken rice",
+ testExpenditures.toString());
+ }
+}
diff --git a/src/test/java/seedu/commands/HelpCommandTest.java b/src/test/java/seedu/commands/HelpCommandTest.java
new file mode 100644
index 0000000000..07950d7344
--- /dev/null
+++ b/src/test/java/seedu/commands/HelpCommandTest.java
@@ -0,0 +1,33 @@
+package seedu.commands;
+
+// import org.junit.jupiter.api.Test;
+import seedu.expenditure.ExpenditureList;
+// import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class HelpCommandTest {
+ ExpenditureList testExpenditures = new ExpenditureList();
+
+ // @Test
+ // public void test_helpCommand_execute() {
+ // HelpCommand testHelp = new HelpCommand();
+ // assertEquals("Here are the list of commands and their respective functions. "
+ // + "Please take note of the FORMAT that is stated below\n"
+ // +
+ // "1. add: Add an expenditure to the record\n" +
+ // "Format: CATEGORY d/DATE a/AMOUNT s/DESCRIPTION\n" +
+ // "2. delete: Deletes the specified expenditure from the record\n" +
+ // "Format: delete INDEX\n" +
+ // "3. edit: Edits an existing expenditure in the record\n" +
+ // "Format: edit INDEX d/DATE a/AMOUNT s/DESCRIPTION\n" +
+ // "Disclaimer: For v1.0 on Borrow or Lend Expenditures, " +
+ // "editing can only change its borrow/lender date, amount and description\n" +
+ // "4. list: Shows a list of expenditures and loans in the record based on
+ // existing categories\n" +
+ // "Format: list /TYPE\n" +
+ // "5. borrow: Keep a record of an incoming loan\n" +
+ // "Format: borrow a/AMOUNT n/LENDER_NAME d/BORROWED_DATE\n" +
+ // "6. lend: Add an expenditure to the record\n" +
+ // "Format: lend a/AMOUNT n/BORROWER_NAME d/LENT_DATE",
+ // testHelp.execute(testExpenditures).getCommandResult());
+ // }
+}
diff --git a/src/test/java/seedu/commands/LendExpenditureCommandTest.java b/src/test/java/seedu/commands/LendExpenditureCommandTest.java
new file mode 100644
index 0000000000..2145976fc1
--- /dev/null
+++ b/src/test/java/seedu/commands/LendExpenditureCommandTest.java
@@ -0,0 +1,47 @@
+package seedu.commands;
+
+import org.junit.jupiter.api.Test;
+import seedu.expenditure.AcademicExpenditure;
+import seedu.expenditure.ExpenditureList;
+import java.time.LocalDate;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class LendExpenditureCommandTest {
+ ExpenditureList testExpenditures = new ExpenditureList();
+
+ @Test
+ public void test_lendExpenditureCommand_execute() {
+ LendExpenditureCommand testLendExpenditureCommand = new LendExpenditureCommand(
+ "for a friend",
+ "bombino",
+ 200.5,
+ LocalDate.parse("2021-08-01"),
+ LocalDate.parse("2025-05-05"));
+ assertEquals(
+ "Added lend expenditure: [Lend] || Lent to: bombino || Date: 1 Aug 2021 || Value: 200.5 "
+ + "|| Description: for a friend || by: 5 May 2025",
+ testLendExpenditureCommand.execute(testExpenditures).getCommandResult());
+ assertEquals("1. [Lend] || Lent to: bombino || Date: 1 Aug 2021 || Value: 200.5 || "
+ + "Description: for a friend || by: 5 May 2025", testExpenditures.toString());
+ }
+
+ @Test
+ public void test_lendExpenditureCommand_executeWithExpendituresAlreadyInList() {
+ testExpenditures.addExpenditure(new AcademicExpenditure("laptop",
+ 1500,
+ LocalDate.parse("2021-08-01")));
+ LendExpenditureCommand testLendExpenditureCommand = new LendExpenditureCommand(
+ "for a friend",
+ "bombino",
+ 200.5,
+ LocalDate.parse("2021-08-01"),
+ LocalDate.parse("2025-05-05"));
+ assertEquals(
+ "Added lend expenditure: [Lend] || Lent to: bombino || Date: 1 Aug 2021 || Value: 200.5 "
+ + "|| Description: for a friend || by: 5 May 2025",
+ testLendExpenditureCommand.execute(testExpenditures).getCommandResult());
+ assertEquals("1. [Academic] || Date: 1 Aug 2021 || Value: 1500.0 || Description: laptop\n" +
+ "2. [Lend] || Lent to: bombino || Date: 1 Aug 2021 || Value: 200.5 || Description: for a friend " +
+ "|| by: 5 May 2025", testExpenditures.toString());
+ }
+}
diff --git a/src/test/java/seedu/commands/ListExpenditureCommandTest.java b/src/test/java/seedu/commands/ListExpenditureCommandTest.java
new file mode 100644
index 0000000000..40d5493c04
--- /dev/null
+++ b/src/test/java/seedu/commands/ListExpenditureCommandTest.java
@@ -0,0 +1,40 @@
+package seedu.commands;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import seedu.exceptions.WrongInputException;
+import seedu.expenditure.AcademicExpenditure;
+import seedu.expenditure.ExpenditureList;
+import seedu.expenditure.FoodExpenditure;
+import seedu.expenditure.TransportExpenditure;
+import java.time.LocalDate;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class ListExpenditureCommandTest {
+ ExpenditureList testExpenditures = new ExpenditureList();
+
+ @BeforeEach
+ public void setUpExpenditureList() {
+ testExpenditures.addExpenditure(
+ new AcademicExpenditure("pen", 2.10, LocalDate.parse("2023-01-01")));
+ testExpenditures.addExpenditure(
+ new FoodExpenditure("chicken rice", 4.50, LocalDate.parse("2023-03-21")));
+ testExpenditures.addExpenditure(
+ new TransportExpenditure("circle line", 2.10,
+ LocalDate.parse("2023-03-21")));
+ }
+
+ @Test
+ public void testViewExpenditureCommand_execute() throws WrongInputException {
+
+ ListExpenditureCommand listExpenditure = new ListExpenditureCommand("SGD");
+ assertEquals("Here is your list of expenditures in SGD: \n" +
+ "1. [Academic] || Date: 1 Jan 2023 || Value: 2.10 || Description: pen\n" +
+ "2. [Food] || Date: 21 Mar 2023 || Value: 4.50 || Description: chicken rice\n"
+ +
+ "3. [Transport] || Date: 21 Mar 2023 || Value: 2.10 || Description: circle line",
+ listExpenditure.execute(testExpenditures).getCommandResult());
+ }
+
+}
diff --git a/src/test/java/seedu/commands/MarkCommandTest.java b/src/test/java/seedu/commands/MarkCommandTest.java
new file mode 100644
index 0000000000..71cdd3474a
--- /dev/null
+++ b/src/test/java/seedu/commands/MarkCommandTest.java
@@ -0,0 +1,83 @@
+package seedu.commands;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import seedu.expenditure.AcademicExpenditure;
+import seedu.expenditure.ExpenditureList;
+import seedu.expenditure.FoodExpenditure;
+import seedu.expenditure.TransportExpenditure;
+import seedu.expenditure.TuitionExpenditure;
+import seedu.expenditure.AccommodationExpenditure;
+import java.time.LocalDate;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class MarkCommandTest {
+ ExpenditureList testExpenditures = new ExpenditureList();
+
+ @BeforeEach
+ public void setUpExpenditureList() {
+ testExpenditures.addExpenditure(
+ new AcademicExpenditure("pen", 2.10, LocalDate.parse("2023-01-01")));
+ testExpenditures.addExpenditure(
+ new FoodExpenditure("chicken rice", 4.50, LocalDate.parse("2023-03-21")));
+ testExpenditures.addExpenditure(
+ new TransportExpenditure("circle line", 2.10, LocalDate.parse("2023-03-21")));
+ testExpenditures.addExpenditure(
+ new TuitionExpenditure("school", 8100, LocalDate.parse("2023-01-27"),
+ LocalDate.parse("2023-01-27")));
+ testExpenditures.addExpenditure(
+ new AccommodationExpenditure("rc", 3000, LocalDate.parse("2023-01-28"),
+ LocalDate.parse("2023-01-28")));
+ }
+
+ @Test
+ public void test_markCommand_onZeroIndex() {
+ MarkCommand testMarkZeroIndex = new MarkCommand(0);
+ assertEquals("No paid field for this expenditure!",
+ testMarkZeroIndex.execute(testExpenditures).getCommandResult());
+ }
+
+ @Test
+ public void test_markCommand_onThreeIndex() {
+ MarkCommand testMarkThreeIndex = new MarkCommand(3);
+ assertEquals("Marked your expenditure!\n" +
+ "[Tuition] || [X] || Date: 27 Jan 2023 || Value: 8100.0 || Description: school",
+ testMarkThreeIndex.execute(testExpenditures).getCommandResult());
+ assertEquals("1. [Academic] || Date: 1 Jan 2023 || Value: 2.1 || Description: pen\n" +
+ "2. [Food] || Date: 21 Mar 2023 || Value: 4.5 || Description: chicken rice\n" +
+ "3. [Transport] || Date: 21 Mar 2023 || Value: 2.1 || Description: circle line\n" +
+ "4. [Tuition] || [X] || Date: 27 Jan 2023 || Value: 8100.0 || Description: school\n" +
+ "5. [Accommodation] || [ ] || Date: 28 Jan 2023 || Value: 3000.0 || Description: rc",
+ testExpenditures.toString());
+ }
+
+ @Test
+ public void test_markCommand_onFourIndex() {
+ MarkCommand testMarkFourIndex = new MarkCommand(4);
+ assertEquals("Marked your expenditure!\n" +
+ "[Accommodation] || [X] || Date: 28 Jan 2023 || Value: 3000.0 || Description: rc",
+ testMarkFourIndex.execute(testExpenditures).getCommandResult());
+ assertEquals("1. [Academic] || Date: 1 Jan 2023 || Value: 2.1 || Description: pen\n" +
+ "2. [Food] || Date: 21 Mar 2023 || Value: 4.5 || Description: chicken rice\n" +
+ "3. [Transport] || Date: 21 Mar 2023 || Value: 2.1 || Description: circle line\n" +
+ "4. [Tuition] || [ ] || Date: 27 Jan 2023 || Value: 8100.0 || Description: school\n" +
+ "5. [Accommodation] || [X] || Date: 28 Jan 2023 || Value: 3000.0 || Description: rc",
+ testExpenditures.toString());
+ }
+
+ @Test
+ public void test_markCommand_onFourIndexAgain() {
+ MarkCommand testMarkFourIndex = new MarkCommand(4);
+ testMarkFourIndex.execute(testExpenditures);
+ MarkCommand testMarkFourIndexAgain = new MarkCommand(4);
+ assertEquals("Sorry! This expenditure is already marked!",
+ testMarkFourIndexAgain.execute(testExpenditures).getCommandResult());
+ }
+
+ @Test
+ public void test_markCommand_onOutOfBoundsIndex() {
+ DeleteCommand testDeleteOutOfBoundsIndex = new DeleteCommand(-10);
+ assertEquals("Index is out of bounds or negative",
+ testDeleteOutOfBoundsIndex.execute(testExpenditures).getCommandResult());
+ }
+}
diff --git a/src/test/java/seedu/commands/OtherExpenditureCommandTest.java b/src/test/java/seedu/commands/OtherExpenditureCommandTest.java
new file mode 100644
index 0000000000..00db16279c
--- /dev/null
+++ b/src/test/java/seedu/commands/OtherExpenditureCommandTest.java
@@ -0,0 +1,43 @@
+package seedu.commands;
+
+import org.junit.jupiter.api.Test;
+import seedu.expenditure.AcademicExpenditure;
+import seedu.expenditure.ExpenditureList;
+
+import java.time.LocalDate;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class OtherExpenditureCommandTest {
+ ExpenditureList testExpenditures = new ExpenditureList();
+
+ @Test
+ public void test_otherExpenditureCommand_execute() {
+ OtherExpenditureCommand testOtherExpenditureCommand = new OtherExpenditureCommand(
+ "special floor wipes",
+ 2,
+ LocalDate.parse("2023-03-21"));
+ assertEquals("Added other expenditure: [Other] || Date: 21 Mar 2023 || Value: 2.0 || " +
+ "Description: special floor wipes",
+ testOtherExpenditureCommand.execute(testExpenditures).getCommandResult());
+ assertEquals("1. [Other] || Date: 21 Mar 2023 || Value: 2.0 || Description: special floor wipes",
+ testExpenditures.toString());
+ }
+
+ @Test
+ public void test_otherExpenditureCommand_executeWithExpendituresAlreadyInList() {
+ testExpenditures.addExpenditure(new AcademicExpenditure("laptop",
+ 1500,
+ LocalDate.parse("2021-08-01")));
+ OtherExpenditureCommand testOtherExpenditureCommand = new OtherExpenditureCommand(
+ "special floor wipes",
+ 2,
+ LocalDate.parse("2023-03-21"));
+ assertEquals("Added other expenditure: [Other] || Date: 21 Mar 2023 || Value: 2.0 || " +
+ "Description: special floor wipes",
+ testOtherExpenditureCommand.execute(testExpenditures).getCommandResult());
+ assertEquals("1. [Academic] || Date: 1 Aug 2021 || Value: 1500.0 || Description: laptop\n" +
+ "2. [Other] || Date: 21 Mar 2023 || Value: 2.0 || Description: special floor wipes",
+ testExpenditures.toString());
+ }
+}
diff --git a/src/test/java/seedu/commands/SortCommandTest.java b/src/test/java/seedu/commands/SortCommandTest.java
new file mode 100644
index 0000000000..b7e355295f
--- /dev/null
+++ b/src/test/java/seedu/commands/SortCommandTest.java
@@ -0,0 +1,138 @@
+package seedu.commands;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import seedu.expenditure.ExpenditureList;
+import seedu.expenditure.AcademicExpenditure;
+import seedu.expenditure.AccommodationExpenditure;
+import seedu.expenditure.BorrowExpenditure;
+import seedu.expenditure.EntertainmentExpenditure;
+import seedu.expenditure.FoodExpenditure;
+import seedu.expenditure.LendExpenditure;
+import seedu.expenditure.OtherExpenditure;
+import seedu.expenditure.TransportExpenditure;
+import seedu.expenditure.TuitionExpenditure;
+
+
+import java.time.LocalDate;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+public class SortCommandTest {
+ private final ExpenditureList testExpenditures = new ExpenditureList();
+
+ @BeforeEach
+ public void setUpExpenditureList() {
+ testExpenditures.addExpenditure(
+ new AcademicExpenditure("pen", 2.10, LocalDate.parse("2023-01-02")));
+ testExpenditures.addExpenditure(
+ new FoodExpenditure("chicken rice", 4.50, LocalDate.parse("2023-03-23")));
+ testExpenditures.addExpenditure(
+ new TransportExpenditure("circle line", 2.10, LocalDate.parse("2023-03-22")));
+ testExpenditures.addExpenditure(
+ new TuitionExpenditure("school", 8100, LocalDate.parse("2022-09-10"),
+ LocalDate.parse("2022-09-10")));
+ testExpenditures.addExpenditure(
+ new AccommodationExpenditure("rc", 3000, LocalDate.parse("2021-12-30"),
+ LocalDate.parse("2021-12-30")));
+ testExpenditures.addExpenditure(
+ new EntertainmentExpenditure("cod skins", 4.99, LocalDate.parse("2021-08-08")));
+ testExpenditures.addExpenditure(
+ new OtherExpenditure("water bottle", 11.99, LocalDate.parse("2023-03-29")));
+ testExpenditures.addExpenditure(
+ new BorrowExpenditure("loan", "dbs", 20000,
+ LocalDate.parse("2021-08-06"), LocalDate.parse("2025-06-10")));
+ testExpenditures.addExpenditure(
+ new LendExpenditure("lending to james", "james", 200,
+ LocalDate.parse("2023-03-06"), LocalDate.parse("2023-04-06")));
+ }
+
+ @Test
+ public void test_originalExpenditureList() {
+ assertEquals("1. [Academic] || Date: 2 Jan 2023 || Value: 2.1 || Description: pen\n" +
+ "2. [Food] || Date: 23 Mar 2023 || Value: 4.5 || Description: chicken rice\n" +
+ "3. [Transport] || Date: 22 Mar 2023 || Value: 2.1 || Description: circle line\n" +
+ "4. [Tuition] || [ ] || Date: 10 Sep 2022 || Value: 8100.0 || Description: school\n" +
+ "5. [Accommodation] || [ ] || Date: 30 Dec 2021 || Value: 3000.0 || Description: rc\n" +
+ "6. [Entertainment] || Date: 8 Aug 2021 || Value: 4.99 || Description: cod skins\n" +
+ "7. [Other] || Date: 29 Mar 2023 || Value: 11.99 || Description: water bottle\n" +
+ "8. [Borrow] || Borrowed from: dbs || Date: 6 Aug 2021 || Value: 20000.0 || Description: loan || " +
+ "By: 10 Jun 2025\n" +
+ "9. [Lend] || Lent to: james || Date: 6 Mar 2023 || Value: 200.0 || Description: lending to james || " +
+ "by: 6 Apr 2023", testExpenditures.toString());
+ }
+
+ @Test
+ public void test_sortAscending() {
+ SortCommand sortAscending = new SortCommand("ascend");
+ CommandResult sortAscendingCommandResult = sortAscending.execute(testExpenditures);
+ assertEquals("Here is your list of expenditures sorted in ascending amount: \n" +
+ "1. [Academic] || Date: 2 Jan 2023 || Value: 2.1 || Description: pen\n" +
+ "2. [Transport] || Date: 22 Mar 2023 || Value: 2.1 || Description: circle line\n" +
+ "3. [Food] || Date: 23 Mar 2023 || Value: 4.5 || Description: chicken rice\n" +
+ "4. [Entertainment] || Date: 8 Aug 2021 || Value: 4.99 || Description: cod skins\n" +
+ "5. [Other] || Date: 29 Mar 2023 || Value: 11.99 || Description: water bottle\n" +
+ "6. [Lend] || Lent to: james || Date: 6 Mar 2023 || Value: 200.0 || Description: lending to james || " +
+ "by: 6 Apr 2023\n" +
+ "7. [Accommodation] || [ ] || Date: 30 Dec 2021 || Value: 3000.0 || Description: rc\n" +
+ "8. [Tuition] || [ ] || Date: 10 Sep 2022 || Value: 8100.0 || Description: school\n" +
+ "9. [Borrow] || Borrowed from: dbs || Date: 6 Aug 2021 || Value: 20000.0 || Description: loan || " +
+ "By: 10 Jun 2025",
+ sortAscendingCommandResult.getCommandResult());
+ }
+
+ @Test
+ public void test_sortDescending() {
+ SortCommand sortDescending = new SortCommand("descend");
+ CommandResult sortDescendingCommandResult = sortDescending.execute(testExpenditures);
+ assertEquals("Here is your list of expenditures sorted in descending amount: \n" +
+ "1. [Borrow] || Borrowed from: dbs || Date: 6 Aug 2021 || Value: 20000.0 || " +
+ "Description: loan || By: 10 Jun 2025\n" +
+ "2. [Tuition] || [ ] || Date: 10 Sep 2022 || Value: 8100.0 || Description: school\n" +
+ "3. [Accommodation] || [ ] || Date: 30 Dec 2021 || Value: 3000.0 || Description: rc\n" +
+ "4. [Lend] || Lent to: james || Date: 6 Mar 2023 || Value: 200.0 || Description: lending to james ||" +
+ " by: 6 Apr 2023\n" +
+ "5. [Other] || Date: 29 Mar 2023 || Value: 11.99 || Description: water bottle\n" +
+ "6. [Entertainment] || Date: 8 Aug 2021 || Value: 4.99 || Description: cod skins\n" +
+ "7. [Food] || Date: 23 Mar 2023 || Value: 4.5 || Description: chicken rice\n" +
+ "8. [Academic] || Date: 2 Jan 2023 || Value: 2.1 || Description: pen\n" +
+ "9. [Transport] || Date: 22 Mar 2023 || Value: 2.1 || Description: circle line",
+ sortDescendingCommandResult.getCommandResult());
+ }
+
+ @Test
+ public void test_sortLatest() {
+ SortCommand sortLatest = new SortCommand("latest");
+ CommandResult sortLatestCommandResult = sortLatest.execute(testExpenditures);
+ assertEquals("Here is your list of expenditures sorted from the latest date: \n" +
+ "1. [Other] || Date: 29 Mar 2023 || Value: 11.99 || Description: water bottle\n" +
+ "2. [Food] || Date: 23 Mar 2023 || Value: 4.5 || Description: chicken rice\n" +
+ "3. [Transport] || Date: 22 Mar 2023 || Value: 2.1 || Description: circle line\n" +
+ "4. [Lend] || Lent to: james || Date: 6 Mar 2023 || Value: 200.0 || " +
+ "Description: lending to james || by: 6 Apr 2023\n" +
+ "5. [Academic] || Date: 2 Jan 2023 || Value: 2.1 || Description: pen\n" +
+ "6. [Tuition] || [ ] || Date: 10 Sep 2022 || Value: 8100.0 || Description: school\n" +
+ "7. [Accommodation] || [ ] || Date: 30 Dec 2021 || Value: 3000.0 || Description: rc\n" +
+ "8. [Entertainment] || Date: 8 Aug 2021 || Value: 4.99 || Description: cod skins\n" +
+ "9. [Borrow] || Borrowed from: dbs || Date: 6 Aug 2021 || Value: 20000.0 || " +
+ "Description: loan " + "|| By: 10 Jun 2025",
+ sortLatestCommandResult.getCommandResult());
+ }
+
+ @Test
+ public void test_sortEarliest() {
+ SortCommand sortEarliest = new SortCommand("earliest");
+ CommandResult sortEarliestCommandResult = sortEarliest.execute(testExpenditures);
+ assertEquals("Here is your list of expenditures sorted from the earliest date: \n" +
+ "1. [Borrow] || Borrowed from: dbs || Date: 6 Aug 2021 || Value: 20000.0 || " +
+ "Description: loan || By: 10 Jun 2025\n" +
+ "2. [Entertainment] || Date: 8 Aug 2021 || Value: 4.99 || Description: cod skins\n" +
+ "3. [Accommodation] || [ ] || Date: 30 Dec 2021 || Value: 3000.0 || Description: rc\n" +
+ "4. [Tuition] || [ ] || Date: 10 Sep 2022 || Value: 8100.0 || Description: school\n" +
+ "5. [Academic] || Date: 2 Jan 2023 || Value: 2.1 || Description: pen\n" +
+ "6. [Lend] || Lent to: james || Date: 6 Mar 2023 || Value: 200.0 || " +
+ "Description: lending to james || by: 6 Apr 2023\n" +
+ "7. [Transport] || Date: 22 Mar 2023 || Value: 2.1 || Description: circle line\n" +
+ "8. [Food] || Date: 23 Mar 2023 || Value: 4.5 || Description: chicken rice\n" +
+ "9. [Other] || Date: 29 Mar 2023 || Value: 11.99 || Description: water bottle",
+ sortEarliestCommandResult.getCommandResult());
+ }
+}
diff --git a/src/test/java/seedu/commands/TransportExpenditureCommandTest.java b/src/test/java/seedu/commands/TransportExpenditureCommandTest.java
new file mode 100644
index 0000000000..a640009212
--- /dev/null
+++ b/src/test/java/seedu/commands/TransportExpenditureCommandTest.java
@@ -0,0 +1,44 @@
+package seedu.commands;
+
+import org.junit.jupiter.api.Test;
+import seedu.expenditure.AcademicExpenditure;
+import seedu.expenditure.ExpenditureList;
+
+import java.time.LocalDate;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class TransportExpenditureCommandTest {
+ ExpenditureList testExpenditures = new ExpenditureList();
+
+ @Test
+ public void test_transportExpenditureCommand_execute() {
+ TransportExpenditureCommand testTransportExpenditureCommand = new TransportExpenditureCommand(
+ "MRT",
+ 2.1,
+ LocalDate.parse("2023-03-21"));
+ assertEquals("Added transport expenditure: " +
+ "[Transport] || Date: 21 Mar 2023 || Value: 2.1 || Description: MRT",
+ testTransportExpenditureCommand.execute(testExpenditures).getCommandResult());
+ assertEquals("1. [Transport] || Date: 21 Mar 2023 || Value: 2.1 || Description: MRT",
+ testExpenditures.toString());
+ }
+
+ @Test
+ public void test_transportExpenditureCommand_executeWithExpendituresAlreadyInList() {
+ testExpenditures.addExpenditure(new AcademicExpenditure("laptop",
+ 1500,
+ LocalDate.parse("2021-08-01")));
+ TransportExpenditureCommand testTransportExpenditureCommand = new TransportExpenditureCommand(
+ "MRT",
+ 2.1,
+ LocalDate.parse("2023-03-21"));
+ assertEquals("Added transport expenditure: " +
+ "[Transport] || Date: 21 Mar 2023 || Value: 2.1 || Description: MRT",
+ testTransportExpenditureCommand.execute(testExpenditures).getCommandResult());
+ assertEquals("1. [Academic] || Date: 1 Aug 2021 || Value: 1500.0 || Description: laptop\n" +
+ "2. [Transport] || Date: 21 Mar 2023 || Value: 2.1 || Description: MRT",
+ testExpenditures.toString());
+ }
+
+}
diff --git a/src/test/java/seedu/commands/TuitionExpenditureCommandTest.java b/src/test/java/seedu/commands/TuitionExpenditureCommandTest.java
new file mode 100644
index 0000000000..67f9f7d0da
--- /dev/null
+++ b/src/test/java/seedu/commands/TuitionExpenditureCommandTest.java
@@ -0,0 +1,45 @@
+package seedu.commands;
+
+import org.junit.jupiter.api.Test;
+import seedu.expenditure.AcademicExpenditure;
+import seedu.expenditure.ExpenditureList;
+
+import java.time.LocalDate;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class TuitionExpenditureCommandTest {
+ ExpenditureList testExpenditures = new ExpenditureList();
+
+ @Test
+ public void test_tuitionExpenditureCommand_execute() {
+ TuitionExpenditureCommand testTuitionExpenditureCommand = new TuitionExpenditureCommand(
+ "NUS Y2S2",
+ 8500.5,
+ LocalDate.parse("2021-01-27"),
+ LocalDate.parse("2021-01-27"));
+ assertEquals("Added tuition expenditure: " +
+ "[Tuition] || [ ] || Date: 27 Jan 2021 || Value: 8500.5 || Description: NUS Y2S2",
+ testTuitionExpenditureCommand.execute(testExpenditures).getCommandResult());
+ assertEquals("1. [Tuition] || [ ] || Date: 27 Jan 2021 || Value: 8500.5 || Description: NUS Y2S2",
+ testExpenditures.toString());
+ }
+
+ @Test
+ public void test_tuitionExpenditureCommand_executeWithExpendituresAlreadyInList() {
+ testExpenditures.addExpenditure(new AcademicExpenditure("laptop",
+ 1500,
+ LocalDate.parse("2021-08-01")));
+ TuitionExpenditureCommand testTuitionExpenditureCommand = new TuitionExpenditureCommand(
+ "NUS Y2S2",
+ 8500.5,
+ LocalDate.parse("2021-01-27"),
+ LocalDate.parse("2021-01-27"));
+ assertEquals("Added tuition expenditure: " +
+ "[Tuition] || [ ] || Date: 27 Jan 2021 || Value: 8500.5 || Description: NUS Y2S2",
+ testTuitionExpenditureCommand.execute(testExpenditures).getCommandResult());
+ assertEquals("1. [Academic] || Date: 1 Aug 2021 || Value: 1500.0 || Description: laptop\n" +
+ "2. [Tuition] || [ ] || Date: 27 Jan 2021 || Value: 8500.5 || Description: NUS Y2S2",
+ testExpenditures.toString());
+ }
+}
diff --git a/src/test/java/seedu/commands/UnmarkCommandTest.java b/src/test/java/seedu/commands/UnmarkCommandTest.java
new file mode 100644
index 0000000000..97a9643613
--- /dev/null
+++ b/src/test/java/seedu/commands/UnmarkCommandTest.java
@@ -0,0 +1,89 @@
+package seedu.commands;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import seedu.exceptions.AlreadyMarkException;
+import seedu.exceptions.NoPaidFieldException;
+import seedu.expenditure.AcademicExpenditure;
+import seedu.expenditure.ExpenditureList;
+import seedu.expenditure.FoodExpenditure;
+import seedu.expenditure.TransportExpenditure;
+import seedu.expenditure.TuitionExpenditure;
+import seedu.expenditure.AccommodationExpenditure;
+import java.time.LocalDate;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class UnmarkCommandTest {
+ ExpenditureList testExpenditures = new ExpenditureList();
+
+ @BeforeEach
+ public void setUpExpenditureList() throws NoPaidFieldException, AlreadyMarkException {
+ testExpenditures.addExpenditure(
+ new AcademicExpenditure("pen", 2.10, LocalDate.parse("2023-01-01")));
+ testExpenditures.addExpenditure(
+ new FoodExpenditure("chicken rice", 4.50, LocalDate.parse("2023-03-21")));
+ testExpenditures.addExpenditure(
+ new TransportExpenditure("circle line", 2.10, LocalDate.parse("2023-03-21")));
+ testExpenditures.addExpenditure(
+ new TuitionExpenditure("school", 8100, LocalDate.parse("2023-01-27"),
+ LocalDate.parse("2023-01-27")));
+ testExpenditures.addExpenditure(
+ new AccommodationExpenditure("rc", 3000, LocalDate.parse("2023-01-28"),
+ LocalDate.parse("2023-01-28")));
+ testExpenditures.markExpenditure(3);
+ testExpenditures.markExpenditure(4);
+ }
+
+ @Test
+ public void test_unmarkCommand_onZeroIndex() {
+ UnmarkCommand testUnmarkZeroIndex = new UnmarkCommand(0);
+ assertEquals("No paid field for this expenditure!",
+ testUnmarkZeroIndex.execute(testExpenditures).getCommandResult());
+ }
+
+ @Test
+ public void test_unmarkCommand_onThreeIndex() {
+ UnmarkCommand testUnmarkThreeIndex = new UnmarkCommand(3);
+ assertEquals("Unmarked your expenditure!\n" +
+ "[Tuition] || [ ] || Date: 27 Jan 2023 || Value: 8100.0 || Description: school",
+ testUnmarkThreeIndex.execute(testExpenditures).getCommandResult());
+ testUnmarkThreeIndex.execute(testExpenditures);
+ assertEquals("1. [Academic] || Date: 1 Jan 2023 || Value: 2.1 || Description: pen\n" +
+ "2. [Food] || Date: 21 Mar 2023 || Value: 4.5 || Description: chicken rice\n" +
+ "3. [Transport] || Date: 21 Mar 2023 || Value: 2.1 || Description: circle line\n" +
+ "4. [Tuition] || [ ] || Date: 27 Jan 2023 || Value: 8100.0 || Description: school\n" +
+ "5. [Accommodation] || [X] || Date: 28 Jan 2023 || Value: 3000.0 || Description: rc",
+ testExpenditures.toString());
+ }
+
+ @Test
+ public void test_unmarkCommand_onFourIndex() {
+ UnmarkCommand testUnmarkFourIndex = new UnmarkCommand(4);
+ assertEquals("Unmarked your expenditure!\n" +
+ "[Accommodation] || [ ] || Date: 28 Jan 2023 || Value: 3000.0 || Description: rc",
+ testUnmarkFourIndex.execute(testExpenditures).getCommandResult());
+ testUnmarkFourIndex.execute(testExpenditures);
+ assertEquals("1. [Academic] || Date: 1 Jan 2023 || Value: 2.1 || Description: pen\n" +
+ "2. [Food] || Date: 21 Mar 2023 || Value: 4.5 || Description: chicken rice\n" +
+ "3. [Transport] || Date: 21 Mar 2023 || Value: 2.1 || Description: circle line\n" +
+ "4. [Tuition] || [X] || Date: 27 Jan 2023 || Value: 8100.0 || Description: school\n" +
+ "5. [Accommodation] || [ ] || Date: 28 Jan 2023 || Value: 3000.0 || Description: rc",
+ testExpenditures.toString());
+ }
+
+ @Test
+ public void test_unmarkCommand_onFourIndexAgain() {
+ UnmarkCommand testMarkFourIndex = new UnmarkCommand(4);
+ testMarkFourIndex.execute(testExpenditures);
+ UnmarkCommand testMarkFourIndexAgain = new UnmarkCommand(4);
+ assertEquals("Sorry! This expenditure is already unmarked!",
+ testMarkFourIndexAgain.execute(testExpenditures).getCommandResult());
+ }
+
+ @Test
+ public void test_markCommand_onOutOfBoundsIndex() {
+ DeleteCommand testDeleteOutOfBoundsIndex = new DeleteCommand(-10);
+ assertEquals("Index is out of bounds or negative",
+ testDeleteOutOfBoundsIndex.execute(testExpenditures).getCommandResult());
+ }
+}
diff --git a/src/test/java/seedu/commands/ViewDateExpenditureCommandTest.java b/src/test/java/seedu/commands/ViewDateExpenditureCommandTest.java
new file mode 100644
index 0000000000..bbbdc2e680
--- /dev/null
+++ b/src/test/java/seedu/commands/ViewDateExpenditureCommandTest.java
@@ -0,0 +1,59 @@
+//package seedu.commands;
+
+//import org.junit.jupiter.api.BeforeEach;
+//import org.junit.jupiter.api.Test;
+//import seedu.expenditure.AcademicExpenditure;
+//import seedu.expenditure.ExpenditureList;
+//import seedu.expenditure.FoodExpenditure;
+//import seedu.expenditure.TransportExpenditure;
+//import seedu.expenditure.TuitionExpenditure;
+//import seedu.expenditure.AccommodationExpenditure;
+//import java.time.LocalDate;
+//import static org.junit.jupiter.api.Assertions.assertEquals;
+
+//public class ViewDateExpenditureCommandTest {
+// ExpenditureList testExpenditures = new ExpenditureList();
+//
+// @BeforeEach
+// public void setUpExpenditureList() {
+// testExpenditures.addExpenditure(
+// new AcademicExpenditure("pen", 2.10, LocalDate.parse("2023-01-01")));
+// testExpenditures.addExpenditure(
+// new FoodExpenditure("chicken rice", 4.50, LocalDate.parse("2023-03-21")));
+// testExpenditures.addExpenditure(
+// new TransportExpenditure("circle line", 2.10, LocalDate.parse("2023-03-21")));
+// testExpenditures.addExpenditure(
+// new TuitionExpenditure("school", 8100, LocalDate.parse("2023-01-27")));
+// testExpenditures.addExpenditure(
+// new AccommodationExpenditure("rc", 3000, LocalDate.parse("2023-02-02")));
+// testExpenditures.addExpenditure(
+// new AcademicExpenditure("NUS", 2.2, LocalDate.parse("2023-02-02")));
+// }
+//
+// @Test
+// public void test_viewDateExpenditureCommand_onNoDate() {
+// ViewDateExpenditureCommand testViewDateNoDate = new ViewDateExpenditureCommand("2023-05-01");
+// assertEquals("Here are the specified expenditures: \n" +
+// "Total amount: 0.00",
+// testViewDateNoDate.execute(testExpenditures).getCommandResult());
+// }
+//
+// @Test
+// public void test_viewDateExpenditureCommand_onOneDate() {
+// ViewDateExpenditureCommand testViewDateOneDate = new ViewDateExpenditureCommand("2023-01-01");
+// assertEquals("Here are the specified expenditures: \n" +
+// "1. [Academic] || Date: 1 Jan 2023 || Value: 2.1 || Description: pen\n" +
+// "Total amount: 2.10",
+// testViewDateOneDate.execute(testExpenditures).getCommandResult());
+// }
+//
+// @Test
+// public void test_viewDateExpenditureCommand_onTwoDate() {
+// ViewDateExpenditureCommand testViewDateTwoDate = new ViewDateExpenditureCommand("2023-02-02");
+// assertEquals("Here are the specified expenditures: \n" +
+// "1. [Accommodation] || [ ] || Date: 2 Feb 2023 || Value: 3000.0 || Description: rc\n" +
+// "2. [Academic] || Date: 2 Feb 2023 || Value: 2.2 || Description: NUS\n" +
+// "Total amount: 3002.20",
+// testViewDateTwoDate.execute(testExpenditures).getCommandResult());
+// }
+//}
diff --git a/src/test/java/seedu/commands/ViewTypeExpenditureCommandTest.java b/src/test/java/seedu/commands/ViewTypeExpenditureCommandTest.java
new file mode 100644
index 0000000000..923f238bc6
--- /dev/null
+++ b/src/test/java/seedu/commands/ViewTypeExpenditureCommandTest.java
@@ -0,0 +1,60 @@
+//package seedu.commands;
+//
+//import org.junit.jupiter.api.BeforeEach;
+//import org.junit.jupiter.api.Test;
+//import seedu.expenditure.AcademicExpenditure;
+//import seedu.expenditure.ExpenditureList;
+//import seedu.expenditure.FoodExpenditure;
+//import seedu.expenditure.TransportExpenditure;
+//import seedu.expenditure.TuitionExpenditure;
+//import seedu.expenditure.AccommodationExpenditure;
+//import java.time.LocalDate;
+//import static org.junit.jupiter.api.Assertions.assertEquals;
+//import seedu.exceptions.WrongInputException;
+//
+//public class ViewTypeExpenditureCommandTest {
+// ExpenditureList testExpenditures = new ExpenditureList();
+//
+// @BeforeEach
+// public void setUpExpenditureList() {
+// testExpenditures.addExpenditure(
+// new AcademicExpenditure("pen", 2.10, LocalDate.parse("2023-01-01")));
+// testExpenditures.addExpenditure(
+// new FoodExpenditure("chicken rice", 4.50, LocalDate.parse("2023-03-21")));
+// testExpenditures.addExpenditure(
+// new TransportExpenditure("circle line", 2.10, LocalDate.parse("2023-03-21")));
+// testExpenditures.addExpenditure(
+// new TuitionExpenditure("school", 8100, LocalDate.parse("2023-01-27")));
+// testExpenditures.addExpenditure(
+// new AccommodationExpenditure("rc", 3000, LocalDate.parse("2023-02-02")));
+// testExpenditures.addExpenditure(
+// new AcademicExpenditure("NUS", 2.2, LocalDate.parse("2023-02-02")));
+// }
+//
+// @Test
+// public void test_viewTypeExpenditureCommand_onNoType() throws WrongInputException {
+// ViewTypeExpenditureCommand testViewTypeOneType = new ViewTypeExpenditureCommand("lend");
+// assertEquals("Here are the specified expenditures: \n" +
+// "Total amount: 0.00",
+// testViewTypeOneType.execute(testExpenditures).getCommandResult());
+// }
+//
+// @Test
+// public void test_viewTypeExpenditureCommand_onOneType() throws WrongInputException {
+// ViewTypeExpenditureCommand testViewTypeOneType = new ViewTypeExpenditureCommand("food");
+// assertEquals("Here are the specified expenditures: \n" +
+// "1. [Food] || Date: 21 Mar 2023 || Value: 4.5 || Description: chicken rice\n" +
+// "Total amount: 4.50",
+// testViewTypeOneType.execute(testExpenditures).getCommandResult());
+// }
+//
+// @Test
+// public void test_viewTypeExpenditureCommand_onTwoType() throws WrongInputException {
+// ViewTypeExpenditureCommand testViewTypeOneType = new ViewTypeExpenditureCommand("academic");
+// assertEquals("Here are the specified expenditures: \n" +
+// "1. [Academic] || Date: 1 Jan 2023 || Value: 2.1 || Description: pen\n" +
+// "2. [Academic] || Date: 2 Feb 2023 || Value: 2.2 || Description: NUS\n" +
+// "Total amount: 4.30",
+// testViewTypeOneType.execute(testExpenditures).getCommandResult());
+// }
+//}
diff --git a/src/test/java/seedu/expenditure/AccommodationExpenditureTest.java b/src/test/java/seedu/expenditure/AccommodationExpenditureTest.java
new file mode 100644
index 0000000000..cdea5f9888
--- /dev/null
+++ b/src/test/java/seedu/expenditure/AccommodationExpenditureTest.java
@@ -0,0 +1,162 @@
+package seedu.expenditure;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import java.time.LocalDate;
+
+public class AccommodationExpenditureTest {
+ AccommodationExpenditure repeatingAccommodationExpenditure;
+ AccommodationExpenditure beforeRepeatingAccommodationExpenditure;
+ @BeforeEach
+ public void setupAccommodationExpenditures() {
+ repeatingAccommodationExpenditure = new AccommodationExpenditure(
+ "This will produce a repeat date on 2024-03-08",
+ 3000,
+ LocalDate.parse("2023-03-08"),
+ LocalDate.parse("2023-03-08"));
+ beforeRepeatingAccommodationExpenditure = new AccommodationExpenditure(
+ "This will yet to produce a repeat date on 2024-03-08, it should display 2023-03-08",
+ 3000,
+ LocalDate.parse("2023-03-08"),
+ LocalDate.parse("2023-03-08"));
+ }
+
+ @Test
+ public void testAccommodationExpenditure_yearIncrement() {
+ repeatingAccommodationExpenditure.checkMark();
+ assertEquals("2023-03-08", beforeRepeatingAccommodationExpenditure.getRepeatDate().toString());
+ assertEquals("2024-03-08", repeatingAccommodationExpenditure.getRepeatDate().toString());
+ }
+
+ @Test
+ public void testAccommodationExpenditure_markBeforeRepeatDate() {
+ repeatingAccommodationExpenditure.checkMark();
+ repeatingAccommodationExpenditure.setPaid();
+ assertEquals("[Accommodation] || [X] || Date: 8 Mar 2023 || Value: 3000.0 || " +
+ "Description: This will produce a repeat date on 2024-03-08",
+ repeatingAccommodationExpenditure.toString());
+ }
+
+ @Test
+ public void testAccommodationExpenditure_onRepeatDate() {
+ repeatingAccommodationExpenditure.checkMark();
+ repeatingAccommodationExpenditure.setPaid();
+ LocalDate currentDate = LocalDate.parse("2024-03-08");
+ LocalDate repeatDate = repeatingAccommodationExpenditure.getRepeatDate();
+ boolean isRepeatDate = (currentDate.equals(repeatDate) || currentDate.isAfter(repeatDate));
+ // Simulating the handleNextRepeat() method
+ if (isRepeatDate) {
+ repeatingAccommodationExpenditure.resetPaid();
+ repeatingAccommodationExpenditure.setNextRepeatDate();
+ }
+ assertEquals("[Accommodation] || [ ] || Date: 8 Mar 2023 || Value: 3000.0 || " +
+ "Description: This will produce a repeat date on 2024-03-08",
+ repeatingAccommodationExpenditure.toString());
+ }
+
+ @Test
+ public void testNextRepeatDate_onFutureRepeatDate() {
+ repeatingAccommodationExpenditure.checkMark();
+ repeatingAccommodationExpenditure.setPaid();
+ LocalDate currentDate = LocalDate.parse("2024-03-08");
+ LocalDate repeatDate = repeatingAccommodationExpenditure.getRepeatDate();
+ boolean isRepeatDate = (currentDate.equals(repeatDate) || currentDate.isAfter(repeatDate));
+ // Simulating the handleNextRepeat() method
+ if (isRepeatDate) {
+ repeatingAccommodationExpenditure.resetPaid();
+ repeatingAccommodationExpenditure.setRepeatDate(repeatingAccommodationExpenditure.setNextRepeatDate());
+ }
+ assertEquals("2025-03-08", repeatingAccommodationExpenditure.getRepeatDate().toString());
+ }
+
+ @Test
+ public void testAccommodationExpenditure_inPast_onRepeatDay() {
+ // Equivalence Partition in the past: Past marking of this expenditure should unmark in current date of 2023,
+ // and set the next repeat date to 2024
+ AccommodationExpenditure pastRepeatingAccommodationExpenditure = new AccommodationExpenditure(
+ "This has been marked in 2010",
+ 1000,
+ LocalDate.parse("2010-04-09"),
+ LocalDate.parse("2010-04-09"));
+ pastRepeatingAccommodationExpenditure.setPaid();
+ // Simulate the checkMark() method
+ LocalDate firstDate = pastRepeatingAccommodationExpenditure.getDate();
+ LocalDate currentDate = LocalDate.parse("2023-04-09");
+ LocalDate repeatDate = pastRepeatingAccommodationExpenditure.getRepeatDate();
+ while (repeatDate.isBefore(currentDate) || repeatDate.equals(firstDate)) {
+ pastRepeatingAccommodationExpenditure.setRepeatDate(
+ pastRepeatingAccommodationExpenditure.setNextRepeatDate());
+ repeatDate = pastRepeatingAccommodationExpenditure.getRepeatDate();
+ }
+ if (currentDate.equals(repeatDate) || currentDate.isAfter(repeatDate)) {
+ pastRepeatingAccommodationExpenditure.resetPaid();
+ pastRepeatingAccommodationExpenditure.setRepeatDate(
+ pastRepeatingAccommodationExpenditure.setNextRepeatDate());
+ }
+ // Since it was set in the past, it should unmark on the repeat day and month in 2023
+ assertEquals("[Accommodation] || [ ] || Date: 9 Apr 2010 || Value: 1000.0 || " +
+ "Description: This has been marked in 2010",
+ pastRepeatingAccommodationExpenditure.toString());
+ // Next repeat date is set in 2024
+ assertEquals("2024-04-09", pastRepeatingAccommodationExpenditure.getRepeatDate().toString());
+ }
+
+ @Test
+ public void testAccommodationExpenditure_inNextFewDays() {
+ // Boundary Value Analysis: Since user is creating the expenditure for the year,
+ // next repeat date will be set to the next year
+ AccommodationExpenditure inNextFewDaysAccommodationExpenditure = new AccommodationExpenditure(
+ "Today is 9 April 2023, but repeat date is set in the future of the same year",
+ 1000,
+ LocalDate.parse("2023-04-20"),
+ LocalDate.parse("2023-04-20"));
+ inNextFewDaysAccommodationExpenditure.setPaid();
+ LocalDate firstDate = inNextFewDaysAccommodationExpenditure.getDate();
+ LocalDate currentDate = LocalDate.parse("2023-04-09");
+ LocalDate repeatDate = inNextFewDaysAccommodationExpenditure.getRepeatDate();
+ while (repeatDate.isBefore(currentDate) || repeatDate.equals(firstDate)) {
+ inNextFewDaysAccommodationExpenditure.setRepeatDate(
+ inNextFewDaysAccommodationExpenditure.setNextRepeatDate());
+ repeatDate = inNextFewDaysAccommodationExpenditure.getRepeatDate();
+ }
+ if (currentDate.equals(repeatDate) || currentDate.isAfter(repeatDate)) {
+ inNextFewDaysAccommodationExpenditure.resetPaid();
+ inNextFewDaysAccommodationExpenditure.setRepeatDate(
+ inNextFewDaysAccommodationExpenditure.setNextRepeatDate());
+ }
+ assertEquals("[Accommodation] || [X] || Date: 20 Apr 2023 || Value: 1000.0 || " +
+ "Description: Today is 9 April 2023, but repeat date is set in the future of the same year",
+ inNextFewDaysAccommodationExpenditure.toString());
+ assertEquals("2024-04-20", inNextFewDaysAccommodationExpenditure.getRepeatDate().toString());
+ }
+
+ @Test
+ public void testAccommodationExpenditure_inFuture() {
+ // Equivalence Partition in the future: Since user is creating the expenditure for the future year,
+ // next repeat date will be set to the next year similarly
+ AccommodationExpenditure futureAccommodationExpenditure = new AccommodationExpenditure(
+ "Today is 9 April 2023, but expenditure is set to future",
+ 1000,
+ LocalDate.parse("2025-04-20"),
+ LocalDate.parse("2025-04-20"));
+ futureAccommodationExpenditure.setPaid();
+ LocalDate firstDate = futureAccommodationExpenditure.getDate();
+ LocalDate currentDate = LocalDate.parse("2023-04-09");
+ LocalDate repeatDate = futureAccommodationExpenditure.getRepeatDate();
+ while (repeatDate.isBefore(currentDate) || repeatDate.equals(firstDate)) {
+ futureAccommodationExpenditure.setRepeatDate(
+ futureAccommodationExpenditure.setNextRepeatDate());
+ repeatDate = futureAccommodationExpenditure.getRepeatDate();
+ }
+ if (currentDate.equals(repeatDate) || currentDate.isAfter(repeatDate)) {
+ futureAccommodationExpenditure.resetPaid();
+ futureAccommodationExpenditure.setRepeatDate(
+ futureAccommodationExpenditure.setNextRepeatDate());
+ }
+ assertEquals("[Accommodation] || [X] || Date: 20 Apr 2025 || Value: 1000.0 || " +
+ "Description: Today is 9 April 2023, but expenditure is set to future",
+ futureAccommodationExpenditure.toString());
+ assertEquals("2026-04-20", futureAccommodationExpenditure.getRepeatDate().toString());
+ }
+}
diff --git a/src/test/java/seedu/expenditure/ExpenditureListTest.java b/src/test/java/seedu/expenditure/ExpenditureListTest.java
new file mode 100644
index 0000000000..93ac0de444
--- /dev/null
+++ b/src/test/java/seedu/expenditure/ExpenditureListTest.java
@@ -0,0 +1,35 @@
+package seedu.expenditure;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.time.LocalDate;
+
+public class ExpenditureListTest {
+ ExpenditureList testExpenditures = new ExpenditureList();
+
+ @BeforeEach
+ public void setUp() {
+ AcademicExpenditure testAcademicExpenditure = new AcademicExpenditure("Laptop",
+ 1500.00, LocalDate.parse("2021-10-10"));
+ TuitionExpenditure testTuitionExpenditure = new TuitionExpenditure("NUS Year 2 Semester 2",
+ 8000.50, LocalDate.parse("2023-01-20"), LocalDate.parse("2023-01-20"));
+ BorrowExpenditure testBorrowExpenditure = new BorrowExpenditure("School Loan", "BankX",
+ 8100.00, LocalDate.parse("2021-07-20"), LocalDate.parse("2025-05-30"));
+ testExpenditures.addExpenditure(testAcademicExpenditure);
+ testExpenditures.addExpenditure(testTuitionExpenditure);
+ testExpenditures.addExpenditure(testBorrowExpenditure);
+ }
+
+ @Test
+ public void testExpenditureListToString() {
+ assertEquals("1. [Academic] || Date: 10 Oct 2021 || Value: 1500.0 || Description: Laptop\n" +
+ "2. [Tuition] || [ ] || Date: 20 Jan 2023 || Value: 8000.5 || Description: NUS Year 2 Semester 2\n" +
+ "3. [Borrow] || Borrowed from: BankX || Date: 20 Jul 2021 || Value: 8100.0 " +
+ "|| Description: School Loan || By: 30 May 2025",
+ testExpenditures.toString());
+ }
+
+}
diff --git a/src/test/java/seedu/expenditure/TuitionExpenditureTest.java b/src/test/java/seedu/expenditure/TuitionExpenditureTest.java
new file mode 100644
index 0000000000..eda055c863
--- /dev/null
+++ b/src/test/java/seedu/expenditure/TuitionExpenditureTest.java
@@ -0,0 +1,162 @@
+package seedu.expenditure;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import java.time.LocalDate;
+
+public class TuitionExpenditureTest {
+ TuitionExpenditure repeatingTuitionExpenditure;
+ TuitionExpenditure beforeRepeatingTuitionExpenditure;
+ @BeforeEach
+ public void setupTuitionExpenditures() {
+ repeatingTuitionExpenditure = new TuitionExpenditure(
+ "This will produce a repeat date on 2024-03-08",
+ 3000,
+ LocalDate.parse("2023-03-08"),
+ LocalDate.parse("2023-03-08"));
+ beforeRepeatingTuitionExpenditure = new TuitionExpenditure(
+ "This will yet to produce a repeat date on 2024-03-08, it should display 2023-03-08",
+ 3000,
+ LocalDate.parse("2023-03-08"),
+ LocalDate.parse("2023-03-08"));
+ }
+
+ @Test
+ public void testTuitionExpenditure_yearIncrement() {
+ repeatingTuitionExpenditure.checkMark();
+ assertEquals("2023-03-08", beforeRepeatingTuitionExpenditure.getRepeatDate().toString());
+ assertEquals("2024-03-08", repeatingTuitionExpenditure.getRepeatDate().toString());
+ }
+
+ @Test
+ public void testTuitionExpenditure_markBeforeRepeatDate() {
+ repeatingTuitionExpenditure.checkMark();
+ repeatingTuitionExpenditure.setPaid();
+ assertEquals("[Tuition] || [X] || Date: 8 Mar 2023 || Value: 3000.0 || " +
+ "Description: This will produce a repeat date on 2024-03-08",
+ repeatingTuitionExpenditure.toString());
+ }
+
+ @Test
+ public void testTuitionExpenditure_onRepeatDate() {
+ repeatingTuitionExpenditure.checkMark();
+ repeatingTuitionExpenditure.setPaid();
+ LocalDate currentDate = LocalDate.parse("2024-03-08");
+ LocalDate repeatDate = repeatingTuitionExpenditure.getRepeatDate();
+ boolean isRepeatDate = (currentDate.equals(repeatDate) || currentDate.isAfter(repeatDate));
+ // Simulating the handleNextRepeat() method
+ if (isRepeatDate) {
+ repeatingTuitionExpenditure.resetPaid();
+ repeatingTuitionExpenditure.setNextRepeatDate();
+ }
+ assertEquals("[Tuition] || [ ] || Date: 8 Mar 2023 || Value: 3000.0 || " +
+ "Description: This will produce a repeat date on 2024-03-08",
+ repeatingTuitionExpenditure.toString());
+ }
+
+ @Test
+ public void testNextRepeatDate_onRepeatDate() {
+ repeatingTuitionExpenditure.checkMark();
+ repeatingTuitionExpenditure.setPaid();
+ LocalDate currentDate = LocalDate.parse("2024-03-08");
+ LocalDate repeatDate = repeatingTuitionExpenditure.getRepeatDate();
+ boolean isRepeatDate = (currentDate.equals(repeatDate) || currentDate.isAfter(repeatDate));
+ // Simulating the handleNextRepeat() method
+ if (isRepeatDate) {
+ repeatingTuitionExpenditure.resetPaid();
+ repeatingTuitionExpenditure.setRepeatDate(repeatingTuitionExpenditure.setNextRepeatDate());
+ }
+ assertEquals("2025-03-08", repeatingTuitionExpenditure.getRepeatDate().toString());
+ }
+
+ @Test
+ public void testTuitionExpenditure_inPast_onRepeatDay() {
+ // Equivalence Partition in the past: Past marking of this expenditure should unmark in current date of 2023,
+ // and set the next repeat date to 2024
+ TuitionExpenditure pastRepeatingTuitionExpenditure = new TuitionExpenditure(
+ "This has been marked in 2010",
+ 1000,
+ LocalDate.parse("2010-04-09"),
+ LocalDate.parse("2010-04-09"));
+ pastRepeatingTuitionExpenditure.setPaid();
+ // Simulate the checkMark() method
+ LocalDate firstDate = pastRepeatingTuitionExpenditure.getDate();
+ LocalDate currentDate = LocalDate.parse("2023-04-09");
+ LocalDate repeatDate = pastRepeatingTuitionExpenditure.getRepeatDate();
+ while (repeatDate.isBefore(currentDate) || repeatDate.equals(firstDate)) {
+ pastRepeatingTuitionExpenditure.setRepeatDate(
+ pastRepeatingTuitionExpenditure.setNextRepeatDate());
+ repeatDate = pastRepeatingTuitionExpenditure.getRepeatDate();
+ }
+ if (currentDate.equals(repeatDate) || currentDate.isAfter(repeatDate)) {
+ pastRepeatingTuitionExpenditure.resetPaid();
+ pastRepeatingTuitionExpenditure.setRepeatDate(
+ pastRepeatingTuitionExpenditure.setNextRepeatDate());
+ }
+ // Since it was set in the past, it should unmark on the repeat day and month in 2023
+ assertEquals("[Tuition] || [ ] || Date: 9 Apr 2010 || Value: 1000.0 || " +
+ "Description: This has been marked in 2010",
+ pastRepeatingTuitionExpenditure.toString());
+ // Next repeat date is set in 2024
+ assertEquals("2024-04-09", pastRepeatingTuitionExpenditure.getRepeatDate().toString());
+ }
+
+ @Test
+ public void testTuitionExpenditure_inNextFewDays() {
+ // Boundary Value Analysis: Since user is creating the expenditure for the year,
+ // next repeat date will be set to the next year
+ TuitionExpenditure inNextFewDaysTuitionExpenditure = new TuitionExpenditure(
+ "Today is 9 April 2023, but repeat date is set in the future of the same year",
+ 1000,
+ LocalDate.parse("2023-04-20"),
+ LocalDate.parse("2023-04-20"));
+ inNextFewDaysTuitionExpenditure.setPaid();
+ LocalDate firstDate = inNextFewDaysTuitionExpenditure.getDate();
+ LocalDate currentDate = LocalDate.parse("2023-04-09");
+ LocalDate repeatDate = inNextFewDaysTuitionExpenditure.getRepeatDate();
+ while (repeatDate.isBefore(currentDate) || repeatDate.equals(firstDate)) {
+ inNextFewDaysTuitionExpenditure.setRepeatDate(
+ inNextFewDaysTuitionExpenditure.setNextRepeatDate());
+ repeatDate = inNextFewDaysTuitionExpenditure.getRepeatDate();
+ }
+ if (currentDate.equals(repeatDate) || currentDate.isAfter(repeatDate)) {
+ inNextFewDaysTuitionExpenditure.resetPaid();
+ inNextFewDaysTuitionExpenditure.setRepeatDate(
+ inNextFewDaysTuitionExpenditure.setNextRepeatDate());
+ }
+ assertEquals("[Tuition] || [X] || Date: 20 Apr 2023 || Value: 1000.0 || " +
+ "Description: Today is 9 April 2023, but repeat date is set in the future of the same year",
+ inNextFewDaysTuitionExpenditure.toString());
+ assertEquals("2024-04-20", inNextFewDaysTuitionExpenditure.getRepeatDate().toString());
+ }
+
+ @Test
+ public void testTuitionExpenditure_inFuture() {
+ // Equivalence Partition in the future: Since user is creating the expenditure for the future year,
+ // next repeat date will be set to the next year similarly
+ TuitionExpenditure futureTuitionExpenditure = new TuitionExpenditure(
+ "Today is 9 April 2023, but expenditure is set to future",
+ 1000,
+ LocalDate.parse("2025-04-20"),
+ LocalDate.parse("2025-04-20"));
+ futureTuitionExpenditure.setPaid();
+ LocalDate firstDate = futureTuitionExpenditure.getDate();
+ LocalDate currentDate = LocalDate.parse("2023-04-09");
+ LocalDate repeatDate = futureTuitionExpenditure.getRepeatDate();
+ while (repeatDate.isBefore(currentDate) || repeatDate.equals(firstDate)) {
+ futureTuitionExpenditure.setRepeatDate(
+ futureTuitionExpenditure.setNextRepeatDate());
+ repeatDate = futureTuitionExpenditure.getRepeatDate();
+ }
+ if (currentDate.equals(repeatDate) || currentDate.isAfter(repeatDate)) {
+ futureTuitionExpenditure.resetPaid();
+ futureTuitionExpenditure.setRepeatDate(
+ futureTuitionExpenditure.setNextRepeatDate());
+ }
+ assertEquals("[Tuition] || [X] || Date: 20 Apr 2025 || Value: 1000.0 || " +
+ "Description: Today is 9 April 2023, but expenditure is set to future",
+ futureTuitionExpenditure.toString());
+ assertEquals("2026-04-20", futureTuitionExpenditure.getRepeatDate().toString());
+ }
+}
diff --git a/src/test/java/seedu/duke/DukeTest.java b/src/test/java/seedu/myledger/DukeTest.java
similarity index 88%
rename from src/test/java/seedu/duke/DukeTest.java
rename to src/test/java/seedu/myledger/DukeTest.java
index 2dda5fd651..89f94d22fe 100644
--- a/src/test/java/seedu/duke/DukeTest.java
+++ b/src/test/java/seedu/myledger/DukeTest.java
@@ -1,4 +1,4 @@
-package seedu.duke;
+package seedu.myledger;
import static org.junit.jupiter.api.Assertions.assertTrue;
diff --git a/src/test/java/seedu/parser/ParserIndividualValueTest.java b/src/test/java/seedu/parser/ParserIndividualValueTest.java
new file mode 100644
index 0000000000..15bf8f3f39
--- /dev/null
+++ b/src/test/java/seedu/parser/ParserIndividualValueTest.java
@@ -0,0 +1,83 @@
+package seedu.parser;
+
+import org.junit.jupiter.api.Test;
+import seedu.exceptions.EmptyStringException;
+
+import java.time.DateTimeException;
+import java.time.LocalDate;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class ParserIndividualValueTest {
+ // Only relevant checks are done as it is assumed that first check on user input is completed.
+ @Test
+ void validOutputsForAddAndEdit() throws StringIndexOutOfBoundsException, EmptyStringException{
+ String exampleString = "d/2020-10-20 a/50.00 s/Udemy Online Lesson";
+ String date = ParseIndividualValue.parseIndividualValue(exampleString,"d/","a/");
+ assertEquals(date,"2020-10-20");
+ String amount = ParseIndividualValue.parseIndividualValue(exampleString,"a/", "s/");
+ assertEquals(amount, "50.00");
+ String description = ParseIndividualValue.parseIndividualValue(exampleString, "s/", "");
+ assertEquals(description, "Udemy Online Lesson");
+ }
+
+ @Test
+ void exceedsBoundForAddAndEdit() throws StringIndexOutOfBoundsException {
+ String exampleString = "d/2020-10-20 a/1.90 s/Coffee";
+ // Out of index
+ assertThrows(StringIndexOutOfBoundsException.class,
+ () -> ParseIndividualValue.parseIndividualValue(exampleString, "a/", "d/"));
+ assertThrows(StringIndexOutOfBoundsException.class,
+ () -> ParseIndividualValue.parseIndividualValue(exampleString, "s/", " "));
+ }
+
+ @Test
+ void amountNotValueForAddAndEdit() throws NumberFormatException, StringIndexOutOfBoundsException
+ , EmptyStringException{
+ String exampleString = "d/2020-10-20 a/TWO DOLLARS s/Coffee";
+ // Checks if input amount is a valid amount
+ String amount = ParseIndividualValue.parseIndividualValue(exampleString,"a/", "s/");
+ assertThrows(NumberFormatException.class,
+ () -> Double.parseDouble(amount));
+ }
+
+ @Test
+ void validOutputsForDeleteAndMark() throws StringIndexOutOfBoundsException, EmptyStringException{
+ String exampleString = "1";
+ String position = ParseIndividualValue.parseIndividualValue(exampleString,"","");
+ assertEquals(position,"1");
+ }
+ @Test
+ void amountNotValueForDeleteAndMark() throws EmptyStringException {
+ String exampleString = "ONE";
+ String amount = ParseIndividualValue.parseIndividualValue(exampleString,"", "");
+ assertThrows(NumberFormatException.class,
+ () -> Double.parseDouble(amount));
+ }
+
+ @Test
+ void validOutputsForLendBorrow() throws StringIndexOutOfBoundsException, EmptyStringException{
+ // Format: category d/date, n/name, a/amount, b/deadline, s/description
+ String exampleString = "d/2022-09-01 n/Shark a/1000 b/2023-01-01 s/Feeling rich";
+ String date = ParseIndividualValue.parseIndividualValue(exampleString,"d/","n/");
+ assertEquals(date,"2022-09-01");
+ String name = ParseIndividualValue.parseIndividualValue(exampleString,"n/","a/");
+ assertEquals(name,"Shark");
+ String amount = ParseIndividualValue.parseIndividualValue(exampleString,"a/", "b/");
+ assertEquals(amount, "1000");
+ String returnDate = ParseIndividualValue.parseIndividualValue(exampleString,"b/", "s/");
+ assertEquals(returnDate, "2023-01-01");
+ String description = ParseIndividualValue.parseIndividualValue(exampleString, "s/", "");
+ assertEquals(description, "Feeling rich");
+ }
+ @Test
+ void invalidDateTime() throws StringIndexOutOfBoundsException{
+ // Format: category d/date, n/name, a/amount, b/deadline, s/description
+ String exampleString = "d/2022-09-010 n/Shark a/1000 b/01-01-2023 s/Feeling rich";
+ assertThrows(DateTimeException.class,
+ () -> LocalDate.parse(ParseIndividualValue.parseIndividualValue(exampleString,"d/", "n/")));
+ assertThrows(DateTimeException.class,
+ () -> LocalDate.parse(ParseIndividualValue.parseIndividualValue(exampleString,"b/", "s/")));
+ }
+}
diff --git a/src/test/java/seedu/parser/ParserTest.java b/src/test/java/seedu/parser/ParserTest.java
new file mode 100644
index 0000000000..e1643208cf
--- /dev/null
+++ b/src/test/java/seedu/parser/ParserTest.java
@@ -0,0 +1,190 @@
+package seedu.parser;
+
+import org.junit.jupiter.api.Test;
+import seedu.commands.Command;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class ParserTest {
+ // Firstly, test parsing in MainInputParser
+ @Test
+ void parseExitCommand() {
+ String inputString = "exit";
+ Command finalCommand = MainInputParser.parseInputs(inputString);
+ assertEquals(finalCommand.getClass().getName(), "seedu.commands.ExitCommand");
+ }
+
+ @Test
+ void parseViewListCommand() {
+ String inputString = "list";
+ Command finalCommand = MainInputParser.parseInputs(inputString);
+ assertEquals(finalCommand.getClass().getName(), "seedu.commands.InvalidCommand");
+ }
+
+ @Test
+ void parseHelpCommand() {
+ String inputString = "help";
+ Command finalCommand = MainInputParser.parseInputs(inputString);
+ assertEquals(finalCommand.getClass().getName(), "seedu.commands.HelpCommand");
+ }
+
+ @Test
+ void parseAcademicCommand() {
+ String inputString = "academic d/2000-01-01 a/200.0 p/Tuition";
+ Command finalCommand = MainInputParser.parseInputs(inputString);
+ assertEquals(finalCommand.getClass().getName(), "seedu.commands.AcademicExpenditureCommand");
+ }
+
+ @Test
+ void parseAccommodationCommand() {
+ String inputString = "accommodation d/2000-01-01 a/500.0 p/Rent";
+ Command finalCommand = MainInputParser.parseInputs(inputString);
+ assertEquals(finalCommand.getClass().getName(), "seedu.commands.AccommodationExpenditureCommand");
+ }
+
+ @Test
+ void parseEntertainmentCommand() {
+ String inputString = "entertainment d/2000-01-01 a/45.90 p/Singing";
+ Command finalCommand = MainInputParser.parseInputs(inputString);
+ assertEquals(finalCommand.getClass().getName(), "seedu.commands.EntertainmentExpenditureCommand");
+ }
+
+ @Test
+ void parseFoodCommand() {
+ String inputString = "food d/2000-01-01 a/9.90 p/Lunch";
+ Command finalCommand = MainInputParser.parseInputs(inputString);
+ assertEquals(finalCommand.getClass().getName(), "seedu.commands.FoodExpenditureCommand");
+ }
+
+ @Test
+ void parseOthersCommand() {
+ String inputString = "other d/2000-01-01 a/2.19 p/Miscellaneous";
+ Command finalCommand = MainInputParser.parseInputs(inputString);
+ assertEquals(finalCommand.getClass().getName(), "seedu.commands.OtherExpenditureCommand");
+ }
+
+ @Test
+ void parseTransportCommand() {
+ String inputString = "transport d/2000-01-01 a/2.00 p/MRT fair";
+ Command finalCommand = MainInputParser.parseInputs(inputString);
+ assertEquals(finalCommand.getClass().getName(), "seedu.commands.TransportExpenditureCommand");
+ }
+
+ @Test
+ void parseTuitionCommand() {
+ String inputString = "tuition d/2000-01-01 a/30.00 p/Chinese lessons";
+ Command finalCommand = MainInputParser.parseInputs(inputString);
+ assertEquals(finalCommand.getClass().getName(), "seedu.commands.TuitionExpenditureCommand");
+ }
+
+ @Test
+ void parseDeleteCommand() {
+ String inputString = "delete 1";
+ Command finalCommand = MainInputParser.parseInputs(inputString);
+ assertEquals(finalCommand.getClass().getName(), "seedu.commands.DeleteCommand");
+ }
+
+ @Test
+ void parseEditCommand() {
+ String inputString = "edit 3 d/2000-01-01 a/10.90 p/Lunch";
+ Command finalCommand = MainInputParser.parseInputs(inputString);
+ assertEquals(finalCommand.getClass().getName(), "seedu.commands.EditCommand");
+ }
+
+ @Test
+ void parseLendCommand() {
+ // Format: category d/date, n/name, a/amount, b/deadline, p/description
+ String inputString = "lend d/2000-01-01 n/Bob a/100.0 b/2024-07-20 p/To buy flowers";
+ Command finalCommand = MainInputParser.parseInputs(inputString);
+ assertEquals(finalCommand.getClass().getName(), "seedu.commands.LendExpenditureCommand");
+ }
+
+ @Test
+ void parseBorrowCommand() {
+ // Format: category d/date, n/name, a/amount, b/deadline, p/description
+ String inputString = "borrow d/2000-01-01 n/Alice a/100.0 b/2024-07-20 p/For school loans";
+ Command finalCommand = MainInputParser.parseInputs(inputString);
+ assertEquals(finalCommand.getClass().getName(), "seedu.commands.BorrowExpenditureCommand");
+ }
+
+ // Checking for invalid inputs
+ @Test
+ void invalidInputDueToCommand() {
+ String inputString = "bye";
+ Command finalCommand = MainInputParser.parseInputs(inputString);
+ assertEquals(finalCommand.getClass().getName(), "seedu.commands.InvalidCommand");
+ inputString = "navigate";
+ finalCommand = MainInputParser.parseInputs(inputString);
+ assertEquals(finalCommand.getClass().getName(), "seedu.commands.InvalidCommand");
+ inputString = "Exit";
+ finalCommand = MainInputParser.parseInputs(inputString);
+ assertEquals(finalCommand.getClass().getName(), "seedu.commands.InvalidCommand");
+ }
+
+ @Test
+ void invalidInputDueToMissingParameters() {
+ String inputMissingDescription = "academic d/2000-01-01 a/200.0";
+ Command finalCommand = MainInputParser.parseInputs(inputMissingDescription);
+ assertEquals(finalCommand.getClass().getName(), "seedu.commands.InvalidCommand");
+
+ String inputMissingDate = "academic a/200.0 p/Tuition";
+ finalCommand = MainInputParser.parseInputs(inputMissingDate);
+ assertEquals(finalCommand.getClass().getName(), "seedu.commands.InvalidCommand");
+
+ String inputMissingAmount = "academic d/2000-01-01 p/Tuition";
+ finalCommand = MainInputParser.parseInputs(inputMissingAmount);
+ assertEquals(finalCommand.getClass().getName(), "seedu.commands.InvalidCommand");
+ }
+
+ @Test
+ void invalidInputDueToWrongInputFormat() {
+ String wrongDate = "academic d/01-01-2000 a/200.0 p/Tuition";
+ String wrongAmount = "academic d/2000-01-01 a/Twenty p/Tuition";
+ Command finalCommand = MainInputParser.parseInputs(wrongDate);
+ assertEquals(finalCommand.getClass().getName(), "seedu.commands.InvalidCommand");
+ finalCommand = MainInputParser.parseInputs(wrongAmount);
+ assertEquals(finalCommand.getClass().getName(), "seedu.commands.InvalidCommand");
+ }
+
+ @Test
+ void invalidInputDueToWrongPositionInput() {
+ String posOfDate = "food a/9.90 p/Lunch d/2000-01-01";
+ String posOfAmount = "food a/9.90 d/2000-01-01 p/Lunch";
+ Command finalCommand = MainInputParser.parseInputs(posOfDate);
+ assertEquals(finalCommand.getClass().getName(), "seedu.commands.InvalidCommand");
+ finalCommand = MainInputParser.parseInputs(posOfAmount);
+ assertEquals(finalCommand.getClass().getName(), "seedu.commands.InvalidCommand");
+ }
+
+ @Test
+ void invalidInputDueToWrongBackSlashInput() {
+ String swapAWithD = "academic a/2000-01-01 d/200.0 p/Tuition";
+ String replaceDWithF = "academic d/2000-01-01 f/200.0 p/Tuition";
+ Command finalCommand = MainInputParser.parseInputs(swapAWithD);
+ assertEquals(finalCommand.getClass().getName(), "seedu.commands.InvalidCommand");
+ finalCommand = MainInputParser.parseInputs(replaceDWithF);
+ assertEquals(finalCommand.getClass().getName(), "seedu.commands.InvalidCommand");
+ }
+
+ @Test
+ void parseMarkCommand() {
+ String inputString = "mark 1";
+ Command finalCommand = MainInputParser.parseInputs(inputString);
+ assertEquals(finalCommand.getClass().getName(), "seedu.commands.MarkCommand");
+ }
+
+ @Test
+ void parseUnmarkCommand() {
+ String inputString = "unmark 1";
+ Command finalCommand = MainInputParser.parseInputs(inputString);
+ assertEquals(finalCommand.getClass().getName(), "seedu.commands.UnmarkCommand");
+ }
+
+ @Test
+ void parseSortCommand() {
+ String inputString = "sort ascend";
+ Command finalCommand = MainInputParser.parseInputs(inputString);
+ assertEquals(finalCommand.getClass().getName(), "seedu.commands.SortCommand");
+ }
+
+}