diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml
index fd8c44d086..e2062c4201 100644
--- a/.github/workflows/gradle.yml
+++ b/.github/workflows/gradle.yml
@@ -33,18 +33,18 @@ 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
+# - 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/.gitignore b/.gitignore
index 2873e189e1..81a9513463 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,3 +15,6 @@ bin/
/text-ui-test/ACTUAL.TXT
text-ui-test/EXPECTED-UNIX.TXT
+/data/accounts.txt
+/data/transactions.txt
+/logs/
diff --git a/build.gradle b/build.gradle
index ea82051fab..83f76abf5c 100644
--- a/build.gradle
+++ b/build.gradle
@@ -12,8 +12,10 @@ repositories {
dependencies {
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.10.0'
testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.10.0'
+ implementation group: 'org.knowm.xchart', name: 'xchart', version: '3.2.2'
}
+
test {
useJUnitPlatform()
@@ -29,11 +31,11 @@ test {
}
application {
- mainClass.set("seedu.duke.Duke")
+ mainClass.set("budgetbuddy.BudgetBuddy")
}
shadowJar {
- archiveBaseName.set("duke")
+ archiveBaseName.set("budgetbuddy")
archiveClassifier.set("")
}
@@ -42,5 +44,6 @@ checkstyle {
}
run{
+// enableAssertions = true
standardInput = System.in
}
diff --git a/docs/AboutUs.md b/docs/AboutUs.md
index 0f072953ea..d4f7682a4a 100644
--- a/docs/AboutUs.md
+++ b/docs/AboutUs.md
@@ -1,9 +1,9 @@
# About us
-Display | Name | Github Profile | Portfolio
---------|:----:|:--------------:|:---------:
-![](https://via.placeholder.com/100.png?text=Photo) | John Doe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
-![](https://via.placeholder.com/100.png?text=Photo) | Don Joe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
-![](https://via.placeholder.com/100.png?text=Photo) | Ron John | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
-![](https://via.placeholder.com/100.png?text=Photo) | John Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
-![](https://via.placeholder.com/100.png?text=Photo) | Don Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
+Display | Name | Github Profile | Portfolio
+--------|:-------------------------:|:-------------------------------------------:|:---------:
+![](images/vaibhav.png) | Vaibhav Dileep Pillai | [Github](https://github.com/vibes-863) | [Portfolio](./team/vibes-863)
+![](images/Shyam.jpg) | Shyam Krishna Arun Gandhi | [Github](https://github.com/ShyamKrishna33) | [Portfolio](./team/shyamkrishna33)
+![](images/vavinan.jpg) | Jeevanandham Vavinan | [Github](https://github.com/Vavinan) | [Portfolio](./team/vavinan)
+![](images/isaac.jpg) | Isaac Eng | [Github](https://github.com/isaaceng7) | [Portfolio](./team/isaaceng7)
+
diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md
index 64e1f0ed2b..ca61e19eea 100644
--- a/docs/DeveloperGuide.md
+++ b/docs/DeveloperGuide.md
@@ -1,38 +1,541 @@
# Developer Guide
+## Table of Contents
+- [Acknowledgements](#acknowledgements)
+- [Design & Implementation](#design--implementation)
+ - [Architecture](#architecture)
+ - [Add Account](#implemented-add-account)
+ - [Remove Account](#implemented-remove-account)
+ - [Category Feature](#implemented-category-feature)
+ - [Process Transction](#implemented-process-transaction)
+ - [Remove Transactiom](#implemented-remove-transaction)
+ - [Edit Transaction](#implemented-edit-transaction)
+ - [Search Transaction](#implemented-search-transactions)
+ - [List Feature](#implemented-list-transactions)
+ - [Insights](#implemented-insights)
+- [Product Scope](#product-scope)
+- [User Stories](#user-stories)
+- [Non-Functional Requirements](#non-functional-requirements)
+- [Glossary](#glossary)
+- [Instructions for manual testing](#instructions-for-manual-testing)
+
+
## Acknowledgements
-{list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well}
+* [XChart](https://knowm.org/open-source/xchart/) - open source library, has been used in this project
+as a data visualization tool.
## Design & implementation
-{Describe the design and implementation of the product. Use UML diagrams and short code snippets where applicable.}
+### Architecture
+
+The **Architecture Diagram** of BudgetBuddy is shown below. It explains the high-level design of the application.
+![](images/architectureDiagram.png)
+
+**Main components of the architecture:**
+
+The bulk of BudgetBuddy work is done by these following four components:
+
+* `BudgetBuddy` class: is the main class of the application, in charge of the app launch,
+ shut down and reading user's inputs. It invokes the loading and saving of data when the app is launched/shut down.
+* `ui` package: consists of `Ui` class, which deals with all the printing/output to the user,
+ and also some reading of user's inputs for additional data.
+* `parser` package: consists of the `Parser` class, makes sense of the data input by the user
+ to provide meaningful data for other methods.
+* `storage` package: consists of `DataStorage` class, in charge of saving and loading of the `data` files.
+
+These components help to manipulate the `Transaction`, `TransactionList`, `Account` and `AccountManager` classes
+which drives BudgetBuddy.
+
+### [Implemented] Add Account
+
+#### Description
+
+This method is used to add a new account to the list of accounts based on the user input provided. The input should
+include the account name and initial balance. After the account is successfully added, the account balance is
+initialized with the provided initial balance, and a unique account number is generated and assigned to the account. A
+message is displayed to the user indicating the success of the operation. This allows users to manage multiple accounts
+by adding new ones as needed.
+
+#### Parameters
+
+1. String input: A string containing the user input, which should include the account name and initial balance,
+ separated by specific delimiters.
+
+#### Design and Implementation
+
+The method starts by validating the syntax of the user input to ensure it contains the necessary delimiters for the
+account name and initial balance. If the input does not meet the expected format, it throws an
+InvalidArgumentSyntaxException. It then parses the input to extract the account name and initial balance. If either the
+name or initial balance is missing or empty, an EmptyArgumentException is thrown. Additionally, if the initial balance
+is not a valid double value, a NumberFormatException is thrown.
+
+After successfully parsing and validating the input, the method proceeds to generate a unique account number for the new
+account. It ensures that the generated number is not already in use by any existing account. Once a unique number is
+obtained, a new Account object is created with the generated account number, provided account name, and initial balance.
+This new account is then added to the list of accounts, and its number is added to the list of existing account numbers
+to ensure uniqueness.
+
+Finally, the method notifies the user of the successful addition of the account by displaying the details of the newly
+created account.
+
+The following class diagram shows the associations between classes involved in adding an account.
+
+![](images/AddAccountClassDiagram.png)
+
+The following sequence diagram shows how the add account process works:
+
+![](images/addAccountDiagram.png)
+
+### [Implemented] Remove Account
+
+#### Description
+
+This method is used to remove an existing account from the system based on the account number provided by the user
+input. It checks for the existence of the account in the system, and if found, it removes the account and any associated
+transactions, displaying a message indicating successful removal. This allows users to manage their accounts effectively
+by removing unnecessary or outdated accounts.
+
+#### Parameters
+
+1. String input: A string containing the user input, which should include the account number to be removed, parsed using
+ specific delimiters.
+
+#### Design and Implementation
+
+The method begins by validating the presence of the account number in the user input. If the format is incorrect, it
+throws an `InvalidArgumentSyntaxException`. If the account number is missing or the format is incorrect (not a valid
+integer), a `NumberFormatException` or `EmptyArgumentException` is thrown.
+
+After validating and parsing the account number, it checks if removing the account would result in no accounts left,
+which is not allowed. If valid, it proceeds to remove the account by the account number. It also removes any associated
+transactions with this account from the transaction list, ensuring no orphan transactions remain.
+
+Finally, the method updates the system's account list and transaction list to reflect the removal and notifies the user
+of the successful operation by displaying relevant details of the removed account and transactions.
+
+The following sequence diagram shows how the remove account process works:
+
+![](images/RemoveAccounrSequenceDiagram.png)
+
+**Additional Notes**
+The method ensures that the system's integrity is maintained by not allowing the last account to be removed, which is
+handled gracefully with appropriate user notifications. The removal process also involves updating various components of
+the system to reflect the changes accurately.
+
+### [Implemented] Category feature
+
+#### Description
+
+The Category feature empowers users to effectively categorize transactions based on their preferences. When initiating a
+new transaction through the `Add` command, users are prompted to select a category from a predefined list. This ensures
+organized and streamlined transaction management.
+
+#### Design and Implementation
+
+The implementation of the Category feature revolves around the integration of a `category` attribute within each
+transaction object. This attribute is defined as a member of the `Category` enum class.
+
+Upon invoking the `Add` command, users are presented with a selection prompt featuring the available categories. User
+input, typically in the form of a numerical identifier corresponding to a category within the enum class, facilitates
+the assignment of the appropriate enum object to the transaction's category attribute.
+
+### [Implemented] Process transaction
+
+#### Description
+
+This method adds a transaction to the list of transactions based on the necessary input details given by the user.
+
+#### Parameters
+
+1. String input: A string containing the user input, which should include the `NAME`, `AMOUNT`, `DATE` and `TYPE` of the
+ transaction.
+2. Account account: The account object associated with the transaction list.
+
+#### Design and Implementation
+
+1. ##### Syntax Validation:
+
+ The method first checks whether the input string contains all necessary arguments ("/a/", "/t/", "/n/", "/$/", "/d/")
+ required for adding a transaction. If any argument is missing, it throws an InvalidAddTransactionSyntax exception.
+
+2. #### Transaction Parsing:
+
+ It utilizes a parser object to parse the user input string into a Transaction object using the
+ parseUserInputToTransaction method.
+
+3. #### Assertion Checks:
+
+ Assertions are used for debugging purposes to ensure that the parsed transaction and added transaction are not null.
+ If they are null, assertion errors are thrown.
+
+4. #### Category Assignment:
+
+ If the category of the transaction is null, it prompts the user to choose a category from a list and assigns the
+ chosen category to the transaction.
+
+5. #### Transaction Addition:
+
+ After parsing and category assignment, the transaction is added to the account using the addTransaction method.
+
+6. #### Feedback to User:
+
+ Upon successful addition of the transaction, a message is printed to the user indicating the details of the added
+ transaction and the updated account balance.
+
+#### Exceptions:
+
+1. `InvalidTransactionTypeException`: This exception is thrown when the transaction type is not one of `income`
+ and `expense`.
+
+2. `InvalidAddTransactionSyntax`: This exception is thrown when the syntax of the add transaction is invalid.
+
+3. `EmptyArgumentException`: This exception is thrown when an empty argument is encountered.
+
+The following class diagram shows the associations between classes involved in processing a transaction.
+
+![](images/TransactionListDiagram.png)
+
+The following sequence diagram shows how an add transaction command works:
+![](images/addTransactionDiagram.png)
+
+### [Implemented] Remove transaction
+
+#### Description
+
+This method is used to remove a transaction from the list of transactions based on the transaction ID provided
+by the user. After the transaction is removed, the account balance is updated accordingly and a message is
+displayed to the user indicating the success of the operation. This helps user to remove the transaction
+from the list they added by mistake or those transactions they no longer need to keep track off.
+
+#### Parameters
+
+1. String input: A string containing the user input, which should include the transaction ID to be removed.
+2. Account account: The account object associated with the transaction list.
+
+#### Design and Implementation
+
+The method first validates the user input to ensure it's not empty or null. If the input is invalid, it throws
+an EmptyArgumentException. Next, it extracts the transaction ID from the input and verifies its integrity as a
+valid integer. If the ID is invalid, a NumberFormatException is thrown.
+
+Once a valid transaction ID is obtained, the method calculates its corresponding index in the transactions
+ArrayList by subtracting 1 from the provided ID, as ArrayList indices start from 0 . It then verifies
+if the calculated index falls within the bounds of the ArrayList. If the index is out of bounds, an
+InvalidIndexException is thrown.
+
+Upon successful validation, the method removes the transaction at the calculated index from the transactions
+ArrayList. Subsequently, it updates the account balance to reflect the removed transaction. Finally, it
+notifies the user of the successful removal along with displaying the details of the removed transaction.
+
+The following sequence diagram shows how a remove transaction goes works:
+
+![](images/removeTransactionDiagram.png)
+
+### [Implemented] Edit Transaction
+
+#### Description
+
+This method facilitates the editing of a transaction within the list of transactions associated with a
+specific account. Users can edit transactions by providing the index of the transaction they wish to modify
+along with the updated transaction details. After the edit operation is completed, the system updates the
+transaction accordingly and notifies the user of the successful operation. This feature enhances user
+flexibility by allowing them to correct erroneous transactions or update transaction details as needed.
+
+#### Parameters
+
+1. String input: A string representing user input, including the index of the transaction to be edited and
+ the updated transaction details.
+2. Account account: The account object associated with the transaction list.
+
+#### Design and Implementation
+
+The processEditTransaction method follows a structured approach to ensure the successful editing of
+transactions while maintaining data integrity:
+
+1. Input Validation: The method begins by validating the user input to ensure it is not empty or null. If
+ the input is invalid, an EmptyArgumentException is thrown to prompt the user to provide valid input.
+2. Transaction Index Extraction: After validating the input, the method extracts the index of the
+ transaction to be edited from the input string. It ensures the extracted data is a valid integer; otherwise, a
+ NumberFormatException is thrown to indicate invalid input.
+3. Index Calculation: Once a valid transaction index is obtained, the method calculates the corresponding
+ index in the transactions ArrayList. As ArrayList indices start from 0, the provided index is decremented
+ by 1 to align with the ArrayList index.
+4. Index Bounds Verification: The method verifies whether the calculated index falls within the bounds of
+ the transactions ArrayList. If the index is out of bounds, an InvalidIndexException is thrown to notify the user of
+ the invalid index provided.
+5. Transaction Editing: Upon successful validation, the method retrieves the transaction object at the
+ calculated index from the transactions ArrayList. It prompts the user to input the updated transaction details
+ through the UserInterface.getEditInformation() method. The edited transaction is then parsed using the
+ parser.parseTransactionType() method to ensure its validity and association with the provided account. Finally, the
+ edited transaction replaces the original transaction at the specified index in the transactions ArrayList using the
+ transactions.set() method.
+6. User Notification: After editing the transaction, the method notifies the user of the successful
+ operation by displaying a message through the UserInterface.printUpdatedTransaction() method.
+
+Sequence Diagram
+The following sequence diagram illustrates the sequence of interactions involved in the editing of a transaction:
+![](images/processEditTransactionDiagram.png)
+
+### [Implemented] Search Transactions
+
+### Description
+
+This method enables users to search for transactions based on a keyword. Users provide a keyword, and the system
+searches through transaction descriptions, amounts, categories, and dates to find matches. The search results,
+along with their corresponding indices in the transactions list, are displayed to the user.
+
+#### Design and Implementation
+
+1. **Keyword Extraction:** The method extracts the keyword from the user input.
+
+2. **Search Process:** It iterates through the list of transactions, checking if any transaction's
+ description, amount, category, or date contains the keyword. Matches are added to a list of search
+ results along with their corresponding indices in the transactions list.
+
+3. **Output Generation:** Once the search process is completed, the method generates output by displaying the
+ search results along with their indices to the user.
+
+4. **Exception Handling:** The method handles exceptions such as an empty keyword input or any unexpected
+ errors during the search process. Proper error messages are displayed to the user in case of exceptions.
+
+Example Algorithm:
+
+```
+searchTransactions(input)
+ 1. Extract the keyword from the user input.
+ 2. Initialize empty lists for search results and indices.
+ 3. For each transaction in transactions do:
+ 1. Convert transaction description to lowercase (description_lower).
+ 2. Convert transaction amount to string (amount_str).
+ 3. Convert category name to lowercase (category_name).
+ 4. Convert transaction date to string (date_str).
+ 5. If keyword is present in description_lower, amount_str, category_name_lower, or date_str then:
+ - Add the transaction to the search results list.
+ - Add the index of the transaction in transactions to the indices list.
+ 4. Display the search results along with their indices to the user.
+ 5. Catch ArrayIndexOutOfBoundsException:
+ - Print "Invalid search input."
+ 6. Catch Exception:
+ - Print the exception message.
+```
+
+### [Implemented] List Transactions
+
+### Description
+
+The list feature allows users to view their existing transactions. This feature includes viewing all the transactions,
+past week's transactions, past month's transactions, transactions from a specified date range, transactions from a
+specified account and transactions of a particular category.
+
+#### Design and Implementation
+
+This feature is facilitated through the `TransactionList#processList`, and it is designed to ensure successful viewing
+of the desired list as inputs are required in a bite-sized manner.
+
+This method first executes the `UserInterface#printListOptions` to show users the list options and their indexes which
+is needed for their inputs. The method will throw an InvalidIndexException if the input is out of the range (range 1-6).
+Depending on the list option chosen by the user, the case statement of the `TransactionList#processList` will run, and
+execute the method of the corresponding option. Different methods would have different prompts as more information is
+required from the user. For the example of custom date transactions, `TransactionList#getCustomDateTransactions` will
+call the methods: `UserInterface#getStartDate` and `UserInterface#getEndDate` in order to obtain the desired date range
+from the user. Once all the required information is gathered for the particular option, an ArrayList will be created and
+the desired transactions will be added into that ArrayList. Then, this ArrayList will be printed out, displaying the
+transactions of the chosen option.
+
+Sequence Diagram
+The following sequence diagram illustrates the sequence of interactions involved in the editing of a transaction:
+![](images/processList.png)
+
+### [Implemented] Insights
+
+#### Description
+
+This feature provides insights into the categorized expenses and incomes of the user. It utilizes the Insight
+class to calculate and display pie charts representing the distribution of expenses and incomes across
+different categories.
+
+#### Design and Implementation
+
+1. The displayCategoryInsight method iterates through the list of transactions and calculates the total income
+ and expense amounts for each category. It then calls the displayPieChart method to visualize these insights
+ using pie charts.
+
+2. The displayPieChart method creates separate pie charts for income and expense categories using the XChart
+ library. It customizes the appearance of the charts and adds series for each category with their respective
+ income or expense amounts.
+
+3. The indexOf method is a private helper function used to find the index of a specific category within an
+ array
+ of categories.
+
+4. The closeInsightFrames method is responsible for closing any open frames related to insights, specifically
+ targeting frames related to income and expense insights to ensure proper cleanup and resource management.
+
+The following is the class diagram for Insights class
+
+![](images/insightDiagram.png)
## Product scope
-### Target user profile
-{Describe the target user profile}
+### Target user profile:
-### Value proposition
+* has a need to manage significant number of day-to-day transactions
+* prefer desktop apps over other types
+* can type fast
+* prefers typing to mouse interactions
+* is reasonably comfortable using CLI apps
-{Describe the value proposition: what problem does it solve?}
+### Value proposition:
+
+* manage daily transactions faster 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 | user | Update my daily expense | Manage my transactions |
+| v1.0 | user | Exit from the interface | Stop the application |
+| v1.0 | user | Know how to communicate with the bot | Use it effectively |
+| v1.0 | user | View my past transactions | Keep track of them |
+| v1.0 | user | Delete a transaction | Remove a transaction I added by mistake |
+| v1.0 | user | Add income as well as expense transactions | Track my balance |
+| v2.0 | user | Keep track of my balance | Know how much money I have left |
+| v2.0 | user | Choose the date range to view my transactions | View transactions that I am interested in only |
+| v2.0 | user | Track multiple balances such as wallet, debit card | Know how much money I have left in all accounts |
+| v2.0 | user | Get a quick view of my past week's transactions | Obtain a quick overview of my recent spending history |
+| v2.0 | user | Categorize my transactions | Get insights on each category |
## Non-Functional Requirements
-{Give non-functional requirements}
+1. Should work on any *mainstream OS* as long as it has Java `11` or above installed.
+2. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be
+ able to accomplish most of the tasks faster using commands than using the mouse.
## Glossary
-* *glossary item* - Definition
+* **Mainstream OS**: Windows, Linux, Unix, macOS
## 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}
+### Add transaction
+With the help of help guide you can add a transaction (assuming account number is 5431)
+ * Run the following command: `add /a/5431 /t/Income /n/March Salary /$/10000 /d/01-03-2024 /c/8`
+ Expected : Received acknowledgement and transaction is added to the list.
+ * Run the following command: `add /a/5431 /t/new /n/March Salary /$/10000 /d/01-03-2024 /c/8`
+ Expected: Error occurred with a message. Transaction is not added
+ * Run the following command : `add /a/5431 /t/Income /n/March Salary /$/10000 /d/01-20-2024 /c/8`
+ Expected: Error occurred due to date format. Transaction is not added
+ * Run the following command : `add /a/5431 /t/Income /n/March Salary /$/10000 /d/01-03-2024 /c/10`
+ Expected: Invalid category. Transaction is not added.
+
+### Edit Transaction
+With the help of help guide you can edit a transaction (assuming there are only 2 transactions in the history)
+* Run the following command: `edit 3`
+ Expected: Error occurred due to index id is out of the range.
+* Run the following command : `edit 0`
+ Expected: Error occurred due to index id is out of the range.
+* Run the following command : `add-acc /n/DBS Savings /$/hundred`
+ Expected: Error occurred due to invalid initial balance. Initial balance should be double not a string.
+* Run the following command: `edit 1`
+ Expected : Details of the transaction is shown, and new input for each argument.
+
+
+### Add account
+With the help of help guide you can add a new account
+* Run the following command: `add-acc /n/DBS Savings /$/10000`
+ Expected : Received acknowledgement and account is added successfully.
+* Run the following command: `add-acc /n//$/10000`
+ Expected: Error occurred due to empty account name. Account not added.
+* Run the following command : `add-acc /n/DBS Savings /$/`
+ Expected: Error occurred due to missing initial balance. Account not added.
+* Run the following command : `add-acc /n/DBS Savings /$/hundred`
+ Expected: Error occurred due to invalid initial balance. Initial balance should be double not a string.
+ Account not added
+
+
+### Deleting a transaction
+1. Deleting a transaction with multiple existing transactions
+ 1. Prerequisites: List all transactions using the `list` command followed by `1`. **Multiple transactions** in the list.
+ 2. Test case: `delete 1`
+ Expected: First transaction is deleted from the transaction history. Account balance is updated. Details of the deleted transaction is shown in the status message.
+ 3. Test case: `delete 0`
+ Expected: No transaction is deleted. Error details shown in the status message.
+ 4. Test case: other incorrect delete commands: `delete abc`, `deleteee`
+ Expected: Similar to point 3.
+
+2. Deleting a transaction with only one existing transaction
+ 1. Prerequisites: List all transactions using the `list` command followed by `1`. **Only one** transaction in the list.
+ 2. Test case: `delete 1`
+ Expected: First transaction is deleted from the transaction history. Account balance is updated. Details of the deleted transaction is shown in the status message.
+ 3. Test case: `delete 2`
+ Expected: No transaction is deleted. Error details shown in the status message.
+ 4. Test case: `delete 0`
+ Expected: No transaction is deleted. Error details shown in the status message.
+
+3. Deleting a transaction with no existing transaction
+ 1. Prerequisites: List all transactions using the `list` command followed by `1`. **No** transaction in the list.
+ 2. Test case: `delete 1`
+ Expected: No transaction is deleted. Error details shown in the status message.
+ 3. Test case: `delete 0`
+ Expected: No transaction is deleted. Error details shown in the status message.
+
+
+### View transaction history
+1. Viewing transaction history of all transactions, past week transactions or past month transactions.
+ 1. Prerequisites: There is at least one existing transaction. View list options using the `list` command.
+ 2. Test case: `1`
+ Expected: All existing transactions is shown in the status message.
+ 3. Test case: `2`
+ Expected: Existing transactions from the past week is shown in the status message.
+ 4. Test case: `3`
+ Expected: Existing transactions from the past month is shown in the status message.
+ 5. Test case: integers of `4` to `6`
+ Expected: Contain prompts before generating different transaction lists in status message.
+ 6. Test case: other integers or incorrect inputs: `10`, `-1`, `abc`.
+ Expected: No list is generated. Error details shown in the status message.
+
+2. Viewing transaction from custom date range
+ 1. Prerequisites: There is at least one existing transaction that is within desired date range. View list options using the `list` command, followed by `4`
+ 2. Test case: `01-01-2024` followed by `01-03-2024`
+ Expected: All existing transactions within 01-01-2024 and 01-03-2024 shown in status message.
+ 3. Test case: `01012024` followed by `01032024`
+ Expected: No list is generated. Error details shown in the status message.
+ 4. Test case: other incorrect inputs: `01-01-24`, `01-Mar-2024`
+ Expected: No list is generated. Error details shown in the status message.
+
+### Delete an account
+1. Delete an existing account
+ 1. Prerequisites: List all accounts using the `list-acc`. There is at least one existing account.
+ 2. Test case: `delete-acc ACCOUNT_NUMBER`
+ Expected: Deletes the account and all existing transactions under it. Account details and transactions are shown in the status message.
+ 3. Test case: other incorrect inputs that is not the ACCOUNT_NUMBER: `delete-acc abc`, `delete-acc 2134321`, `delete-acc `.
+ Expected: No account is deleted. Error details shown in the status message.
+
+
+### Data Storage
+1. Saving the transactions and accounts
+ 1. Test case: Add a valid transaction, close the program and reopen the program
+ Expected: The previous transaction and the accounts are still available in the
+ transaction list to which the user can append more.
+2. Deleting transactions and accounts
+ 1. Prerequisite: Have at least 1 transaction in the transaction list.
+ 2. Test case: Delete a transaction, close the program and reopen it.
+ Expected: The previous transaction is not available in the
+ transaction list, and it has been permanently deleted.
+ 3. Test case: Delete an account, close the program and reopen it.
+ Expected: The previous account and all the transactions associated with it are
+ not available in the transaction list, and they have been permanently deleted.
+3. Dealing with corrupted data file.
+ 1. Prerequisite: Have some transactions in the transaction list.
+ 2. Test case: Remove any parameter of a transaction in the data file.
+ Expected: File Corrupted Error is triggered and all the data are
+ erased. The user is also notified of this error.
+ 3. Test case: Add a new delimiter ',' to the data file.
+ Expected: File Corrupted Error is triggered and all the data are
+ erased. The user is also notified of this error.
+ 4. Test case: An account (with at least 1 transaction) is deleted from the storage
+ Expected: File Corrupted Error is triggered and all the data are
+ erased. The user is also notified of this error.
+
diff --git a/docs/README.md b/docs/README.md
index bbcc99c1e7..704ffec9ea 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,6 +1,8 @@
-# Duke
+# BudgetBuddy
-{Give product intro here}
+BudgetBuddy is a **desktop app for managing personal finances, optimized for use via a Command Line Interface** (CLI).
+It offers the tracking of income and expenses of multiple accounts and even provides insights of your financial
+activities.
Useful links:
* [User Guide](UserGuide.md)
diff --git a/docs/UserGuide.md b/docs/UserGuide.md
index abd9fbe891..e508f4c2d7 100644
--- a/docs/UserGuide.md
+++ b/docs/UserGuide.md
@@ -1,42 +1,315 @@
# User Guide
+## Table of Contents
+- [Introduction](#introduction)
+- [Quick Start](#quick-start)
+- [Features](#features)
+ - [Viewing help: `help`](#viewing-help-help)
+ - [Adding a transaction: `add`](#adding-a-transaction-add)
+ - [View transaction history: `list`](#view-transaction-history-list)
+ - [Deleting a transaction: `delete`](#deleting-a-transaction-delete)
+ - [Edit a transaction: `edit`](#edit-a-transaction-edit)
+ - [Search for a transaction: `search`](#search-for-a-transaction-search)
+ - [Add an account: `add-acc`](#add-an-account-add-acc)
+ - [List all accounts: `list-acc`](#list-all-accounts-list-acc)
+ - [Delete an account: `delete-acc`](#delete-an-account-delete-acc)
+ - [Edit an account: `edit-acc`](#edit-an-account-edit-acc)
+ - [View transaction insights: `insights`](#view-transaction-insights-insights)
+ - [Exiting the program: `bye`](#exiting-the-program-bye)
+ - [Saving the data](#saving-the-data)
+ - [Editing the data file](#editing-the-data-file)
+- [FAQ](#faq)
+- [Command Summary](#command-summary)
+
## Introduction
-{Give a product intro}
+BudgetBuddy is a **desktop app for managing personal finances, optimized for use via a Command Line Interface** (CLI).
+It offers the tracking of income and expenses of multiple accounts and even provides insights of your financial
+activities.
## Quick Start
-{Give steps to get started quickly}
+1. Ensure that you have Java `11` or above installed.
+2. Down the latest version of `budgetbuddy.jar`
+ from [here](https://github.com/AY2324S2-CS2113-T15-2/tp/releases/latest).
+3. Copy the file to the folder you want to use as the _home folder_ for your BudgetBuddy.
+4. Open a command terminal, `cd` into the folder you added the jar file to, and use the `java -jar budgetbuddy.jar`
+ command to
+ run the application.
+5. When the application is **first run**, BudgetBuddy will prompt the user to **create a new account**, prompting the
+ user to
+ add an **account name** and **initial balance**. Type the details in the terminal and press Enter to confirm.
+6. Subsequently, users may type the command into the terminal and press Enter to execute it. e.g. typing **`help`** and
+ pressing
+ Enter will prompt the help feature.
+7. Refer to the [Features](#features) below for details of each command.
+
+## Features
+
+> [!NOTE]
+> **The following are notes about the command format:**
+* Words in `UPPER_CASE` are the parameters to be supplied by the user.
+ e.g `delete TRANSACTION_ID`, `TRANSACTION_ID` is a parameter which can be used as `delete 1`
+* Parameters can be in any order.
+ e.g if the command specifies `/n/ACCOUNT_NAME /$/INITIAL_BALANCE`, `/$/INITIAL_BALANCE /n/ACCOUNT_NAME` is also acceptable.
+* Extraneous parameters for commands that do not take in parameters (such as `help`, `list`, `list-acc` and `bye`) will be ignored.
+ e.g if the command specifies `help abc`, it will be interpreted as `help`.
+
+### Viewing help: `help`
+
+Shows the instructions for using BudgetBuddy.
+
+This command gives the list to search for commands, specific for each task. The commands `help all` and
+`help acc` covers the basic structure of all commands. Instructions for command-specific help will be
+provided in the `help all` and `help acc` accordingly.
+
+**Format:** `help`
+
+### Adding a transaction: `add`
+
+Adds a transaction into the transaction list of the specified account.
+
+**Parameters:** Account Number, Transaction Type, Name, Amount, Date, Category
+
+**Format:** `add /a/ACCOUNT_NUMBER /t/TRANSACTION_TYPE /n/NAME /$/AMOUNT /d/DATE /c/CATEGORY`
+
+* The `ACCOUNT_NUMBER` can be viewed using the command `list-acc`.
+* The `TRANSACTION_TYPE` includes **Expense** or **Income** ONLY.
+* The `AMOUNT` is in dollars ($).
+* The `DATE` should be in the format **DD-MM-YYYY**.
+* The `CATEGORY` is an integer. The categories are mapped to the following integers:
+ - 1 - Dining
+ - 2 - Groceries
+ - 3 - Utilities
+ - 4 - Transportation
+ - 5 - Healthcare
+ - 6 - Entertainment
+ - 7 - Rent
+ - 8 - Salary
+ - 9 - Others
+
+**Example of usage:**
+
+* `add /a/5431 /t/Income /n/March Salary /$/10000 /d/01-03-2024 /c/8`
+
+* `add /n/New iPhone /$/2000 /c/9 /t/Expense /a/5431 /d/20-03-2024`
+
+_Successful add feature output:_
+![](images/successful_add_feature.png)
+
+### View transaction history: `list`
+
+List the existing transactions. List feature includes options:
+
+1. All Transactions
+2. Past Week Transactions - list transactions from the past 7 days
+3. Past Month Transactions - list transactions from the past month
+4. Custom Date Transactions - list transactions between the specified dates(inclusive)
+5. Account Transactions - list all transactions in the specified account
+6. Category Transactions - list all transactions in the category type
+
+**Format:** `list`
+
+**Example of usage:**
+
+* `list` followed by `1` to view All Transactions.
+* `list` followed by `2` to view Past Week Transactions.
+* `list` followed by `3` to view Past Month Transactions.
+* `list` followed by `4` followed by the start date `01-01-2024` followed by the end date `31-03-2024`
+ to view Custom Date Transactions from 01-01-2024 to 31-03-2024.
+* `list` followed by `5` followed by account number `ACCOUNT_NUMBER` to view transactions from that account.
+* `list` followed by `6` followed by category number `CATEGORY_NUMBER` to view transactions of that category.
+
+_List feature options:_
+![](images/list_options.png)
+
+_Successful list feature (custom date) example:_
+![](images/successful_list_feature.png)
+
+### Deleting a transaction: `delete`
+
+Removes a transaction from transaction history.
+
+**Parameters:** Transaction ID
+
+**Format:** `delete TRANSACTION_ID`
+
+**_Note:_**
+
+* The `TRANSACTION_ID` is an integer value ranges from one to the size of the transaction history (index
+ ID of the last transaction).
+* The `TRANSACTION_ID` can be viewed using the command `list` followed by `1`.
+
+**Example of usage:**
+`delete 1`
+
+_Successful delete feature example:_
+![](images/successful_delete_transaction.png)
+
+### Edit a transaction: `edit`
+
+Edits the details of an existing transaction.
+
+**Parameters:** Transaction ID
+
+**Format:** `edit TRANSACTION_ID`
+
+**_Note:_**
+
+* The `TRANSACTION_ID` is an integer value ranges from one to the size of the transaction history (index
+ ID of the last transaction).
+* The `TRANSACTION_ID` can be viewed using the command `list` followed by `1`.
+* Edit transaction will only update the existing entry, so it won't change the index ID of that transaction.
+ The edited transaction will still be accessible from the same index ID.
+
+**Example of usage:**
+`edit 2`
+
+* Then the user will be asked to edit each information from that specific transaction one by one.
+
+_Successful edit feature example:_
+![](images/successful_edit_transaction.png)
-1. Ensure that you have Java 11 or above installed.
-1. Down the latest version of `Duke` from [here](http://link.to/duke).
+### Search for a transaction: `search`
-## Features
+Search for a list of transactions matching the keyword.
-{Give detailed description of each feature}
+**Parameters:** keyword
-### Adding a todo: `todo`
-Adds a new item to the list of todo items.
+**Format:** `search KEYWORD`
-Format: `todo n/TODO_NAME d/DEADLINE`
+**_Note:_**
-* The `DEADLINE` can be in a natural language format.
-* The `TODO_NAME` cannot contain punctuation.
+* The `KEYWORD` can be any value representing transaction description, category, transaction amount or
+ transaction date.
+* Search transaction will list out the matching transactions along with their true **index ID**. This can
+ be used in `edit` or `delete` command.
+* Keywords are case-insensitive so if there is no matching transactions, the user will be notified.
+* This feature will search from the whole transaction history rather than a specific account to ease the
+ usage of the BudgetBuddy.
-Example of usage:
+**Example of usage:**
+`search salary`
-`todo n/Write the rest of the User Guide d/next week`
+* Then the user will be asked to edit each information from that specific transaction one by one.
-`todo n/Refactor the User Guide to remove passive voice d/13/04/2020`
+_Successful edit feature example:_
+![](images/successful_search.png)
+
+### Add an account: `add-acc`
+
+Adds a new account with a specified initial balance.
+
+**Parameters:** Account Name, Initial Balance
+
+**Format:** `add-acc /n/ACCOUNT_NAME /$/INITIAL_BALANCE`
+
+**_Note:_**
+
+* The `INITIAL_AMOUNT` is in dollars ($).
+
+**Example of Usage:**
+`add-acc /n/DBS Savings /$/10000`
+
+_Successful add-acc feature output:_
+![](images/successful_add_acc_feature.png)
+
+### List all accounts: `list-acc`
+
+List all the existing accounts.
+
+**Format:** `list-acc`
+
+_Successful list-acc feature output:
_
+![](images/successful_list_acc_feature.png)
+
+### Delete an account: `delete-acc`
+
+Removes an account and removes all its transactions.
+
+**Parameters:** Account Number.
+
+**Format:** `delete ACCOUNT_NUMBER`
+
+**_Note:_**
+
+* The `ACCOUNT_NUMBER` can be viewed using the command `list-acc`.
+
+**Example of usage:**
+`delete-acc 5431`
+
+_Successful delete-acc feature output:_
+![](images/successful_delete_acc_feature.png)
+
+### Edit an account: `edit-acc`
+
+Edits the details of an existing account.
+
+**Parameters:** Account Number
+
+**Format:** `edit-acc ACCOUNT_NUMBER`
+
+**_Note:_**
+
+* The `ACCOUNT_NUMBER` can be viewed using the command `list-acc`.
+
+**Example of usage:**
+`edit-acc 5431`
+
+_Successful edit-acc feature output:_
+![](images/successful_edit_acc_feature.png)
+
+### View transaction insights: `insights`
+
+View the insights of all the transactions listed so far using a pie chart. Two pie charts are displayed,
+one for each type (i.e. `income` and `expense`). The pie charts show the percentage of total amount transferred
+in a particular category among all categories.
+
+**Format:** `insights`
+
+### Exiting the program: `bye`
+
+Exits BudgetBuddy.
+
+**Format:** `bye`
+
+### Saving the data
+
+BudgetBuddy data are saved in the hard disk automatically when the user exits the program. There is no need to save the
+data manually. The data will be loaded automatically when the user runs the program again.
+
+### Editing the data file
+
+BudgetBuddy data are saved as two txt files `[JAR file location]/data/accounts.txt`
+and `[JAR file location]/data/transactions.txt`. Advanced users are welcome to update the data directly by editing the
+data files.
+
+**_Caution!:_** If your changes to the data file makes its format invalid, BudgetBuddy will discard all data and start with
+an empty data file at the next run. Hence, it is recommended to take a backup of the file before editing it.
+Furthermore, certain edits can cause the BudgetBuddy to behave in unexpected ways (e.g., if a value entered is outside
+the acceptable range). Therefore, edit the data file only if you are confident that you can update it correctly.
## 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**: Install the app in the other computer and overwrite the empty data file it creates with the file that contains
+the data of your previous BudgetBuddy home folder.
## Command Summary
-{Give a 'cheat sheet' of commands here}
-
-* Add todo `todo n/TODO_NAME d/DEADLINE`
+* View help `help`
+* View help for all commands `help all`. This can be used to see all the commands related to transactions
+* View help for accounts `help acc`. This can be used to see all the commands related to account
+* Further help for each transaction command will be provided in the `help all`
+* Add transaction `add /a/ACCOUNT_NUMBER /t/TRANSACTION_TYPE /n/NAME /$/AMOUNT /d/DATE /c/CATEGORY`
+* List transactions `list`
+* Delete transaction `delete TRANSACTION_ID`
+* Edit transaction `edit TRANSACTION_ID`
+* Search transaction `search KEYWORD`
+* Add account `add-acc /n/ACCOUNT_NAME /$/INITIAL_BALANCE`
+* List accounts `list-acc`
+* Delete account `delete-acc ACCOUNT_NUMBER`
+* Edit account `edit-acc ACCOUNT_NUMBER`
+* View insights `insights`
+* Exit program `bye`
diff --git a/docs/diagrams/AddAccountClassDiagram.puml b/docs/diagrams/AddAccountClassDiagram.puml
new file mode 100644
index 0000000000..31d508e0f5
--- /dev/null
+++ b/docs/diagrams/AddAccountClassDiagram.puml
@@ -0,0 +1,76 @@
+@startuml
+skinparam classAttributeIconSize 0
+hide abstract circle
+hide class circle
+hide enum circle
+
+' Define the Account package and Account class
+package budgetbuddy.account {
+ class Account {
+ -balance: double
+ +Account()
+ +Account(balance: double): void
+ +getBalance(): double
+ +setBalance(balance: double): void
+ }
+}
+
+' Existing TransactionList package with new Account association
+package budgetbuddy.transaction {
+ class TransactionList {
+ -transactions: ArrayList
+ -parser: Parser
+ -dataStorage: DataStorage
+ +TransactionList(transactions: ArrayList): void
+ +processTransaction(input: String, account: Account): void
+ +updateBalance(account: Account): void ' Added method for updating balance
+ }
+}
+
+' Existing Parser package
+package budgetbuddy.parser {
+ object Parser {
+ +parseUserInputToTransaction(input: String, account: Account): Transaction
+ }
+}
+
+' Existing DataStorage package
+package budgetbuddy.datastorage {
+ object DataStorage {
+ +saveTransactions(transactionArrayList: ArrayList): void
+ +getBalance(): double ' Presumed method for getting balance for an account
+ }
+}
+
+' Existing Transaction Type and Category packages and classes
+package budgetbuddy.transaction.type {
+ abstract class Transaction {
+ -description: String
+ -amount: double
+ -category: Category
+ -date: LocalDate
+ +Transaction(accountNumber: int, accountName: String,
+ description: String, amount: double, date: String)
+ }
+}
+
+package budgetbuddy.categories {
+ enum Category <> {
+ +Category(categoryNum: int, categoryName: String)
+ +getCategoryName(): String
+ +getCategoryNum(): int
+ }
+}
+
+' Define relationships
+TransactionList -right-> "1" Parser: "uses "
+TransactionList -down-> "1" DataStorage: "uses "
+TransactionList ..> Transaction: "creates "
+Transaction --> "1" Category: "categorized by "
+Parser ..> Transaction: "parses into "
+Parser ..> Category: "uses "
+DataStorage ..> Account: "saves and retrieves account balance "
+Account <-- TransactionList: "updated by "
+
+' Additional relationships for Account management might be needed depending on the context
+@enduml
diff --git a/docs/diagrams/RemoveAccounrSequenceDiagram.puml b/docs/diagrams/RemoveAccounrSequenceDiagram.puml
new file mode 100644
index 0000000000..dadb44d0bb
--- /dev/null
+++ b/docs/diagrams/RemoveAccounrSequenceDiagram.puml
@@ -0,0 +1,30 @@
+@startuml
+'https://plantuml.com/sequence-diagram
+
+autonumber
+
+actor User
+participant "AccountManager" as AM
+participant "Parser" as P
+participant "TransactionList" as TL
+participant "UserInterface" as UI
+
+User -> AM: removeAccount(input, TL)
+AM -> AM: validateSyntax(input)
+AM -> P: parseRemoveAccount(input)
+activate P
+P --> AM: accountNumber
+deactivate P
+AM -> AM: getAccountByAccountNumber(accountNumber)
+AM -> TL: removeTransactionsByAccountNumber(accountNumber)
+activate TL
+TL --> AM: transactionsRemoved
+deactivate TL
+AM -> AM: removeAccount(accountNumber)
+AM -> UI: printDeleteAccountMessage(removedAccount, transactionsRemoved)
+activate UI
+UI --> AM: Display removed account message
+AM --> User: Confirmation message
+
+deactivate UI
+@enduml
diff --git a/docs/diagrams/TransactionListDiagram.puml b/docs/diagrams/TransactionListDiagram.puml
new file mode 100644
index 0000000000..b964603c52
--- /dev/null
+++ b/docs/diagrams/TransactionListDiagram.puml
@@ -0,0 +1,51 @@
+@startuml
+skinparam classAttributeIconSize 0
+hide abstract circle
+hide class circle
+hide enum circle
+package budgetbuddy.transaction{
+ class TransactionList {
+ -transactions: ArrayList
+ -parser: Parser
+ -dataStorage: DataStorage
+ +TransactionList(transactions: ArrayList): void
+ +processTransaction(input: String, account: Account): void
+ }
+}
+package budgetbuddy.parser{
+ object Parser {
+ +parseUserInputToTransaction(input: String, account: Account): Transaction
+ }
+}
+
+package budgetbuddy.datastorage{
+ object DataStorage {
+ +saveTransactions(transactionArrayList: ArrayList): void
+ }
+}
+
+package budgetbuddy.transaction.type {
+ abstract class Transaction{
+ -description: String
+ -amount: double
+ -category: Category
+ -date: LocalDate
+ +Transaction(accountNumber: int, accountName: String,
+ description: String, amount: double, date: String)
+ }
+}
+
+package budgetbuddy.categories {
+ enum Category <> {
+ +Category(categoryNum: int, categoryName: String)
+ +getCategoryName(): String
+ +getCategoryNum(): int
+ }
+}
+
+TransactionList -> "1" Parser: " {association} "
+TransactionList ---> "1" DataStorage: {association}
+TransactionList ..> Transaction: {dependency}
+Transaction --> "1" Category: {association}
+Parser ...> Category: <>
+@enduml
\ No newline at end of file
diff --git a/docs/diagrams/addAccountDiagram.puml b/docs/diagrams/addAccountDiagram.puml
new file mode 100644
index 0000000000..d724ac26d2
--- /dev/null
+++ b/docs/diagrams/addAccountDiagram.puml
@@ -0,0 +1,23 @@
+@startuml
+'https://plantuml.com/sequence-diagram
+
+autonumber
+
+actor User
+participant "AccountManager" as AM
+participant "Parser" as P
+participant "UserInterface" as UI
+
+User -> AM: processAddAccount(input)
+AM -> AM: validateSyntax(input)
+AM -> P: parseAddAccount(input)
+activate P
+P --> AM: parsedData
+deactivate P
+AM -> AM: addAccount(parsedData[0], parsedData[1])
+AM -> UI: printAddAccountMessage(newAccount)
+activate UI
+UI --> AM: Display added account message
+AM --> User : Confirmation message
+deactivate UI
+@enduml
\ No newline at end of file
diff --git a/docs/diagrams/addTransaction.puml b/docs/diagrams/addTransaction.puml
new file mode 100644
index 0000000000..85ad6f4e44
--- /dev/null
+++ b/docs/diagrams/addTransaction.puml
@@ -0,0 +1,36 @@
+@startuml
+'https://plantuml.com/sequence-diagram
+
+autonumber
+
+actor User
+participant ":TransactionManager" as TM
+participant ":Parser" as P
+participant "UserInterface" as UI
+
+User -> TM: processTransaction(input, account)
+TM -> TM: validateSyntax(input)
+TM -> P: parseUserInputToTransaction(input, account)
+activate P
+P -> P: checkCategory(transaction)
+
+alt Category is null
+ P -> UI: listCategories()
+ activate UI
+ UI --> User: Display category options
+ User --> UI: categoryNum
+ UI --> P: getCategoryNum()
+ deactivate UI
+end
+
+P --> TM: transaction
+deactivate P
+
+
+TM -> TM: addTransaction(transaction)
+TM -> UI: printAddMessage(transaction, account.balance)
+activate UI
+UI --> TM : Display added transaction message
+TM --> User: Confirmation message
+deactivate UI
+@enduml
\ No newline at end of file
diff --git a/docs/diagrams/insightDiagram.puml b/docs/diagrams/insightDiagram.puml
new file mode 100644
index 0000000000..c3e378c63e
--- /dev/null
+++ b/docs/diagrams/insightDiagram.puml
@@ -0,0 +1,55 @@
+@startuml
+skinparam classAttributeIconSize 0
+hide abstract circle
+hide class circle
+hide enum circle
+package budgetbuddy.insight {
+ class Insight {
+ +displayCategoryInsight(transactionArrayList: ArrayList): void
+ -displayPieChart(categoryArray: Category[], incomeArray: Double[], expenseArray: Double[]): void
+ -indexOf(array: Category[], target: Category): int
+ +closeInsightFrames(): void
+ }
+}
+
+package budgetbuddy.categories {
+ enum Category <> {
+ +Category(categoryNum: int, categoryName: String)
+ +getCategoryName(): String
+ +getCategoryNum(): int
+ }
+}
+
+package budgetbuddy.transaction.type {
+ abstract class Transaction{
+ -category: Category {dependency}
+ +Transaction(accountNumber: int, accountName: String,
+ description: String, amount: double, date: String)
+ +getAmount(): double
+ +getCategory(): Category
+ +setCategory(category: Category): void
+ {abstract} +getTransactionType() : String
+ }
+}
+
+
+class PieChart
+class PieChartBuilder
+class XChartPanel
+class PieStyler
+class JFrame {
+ +dispose(): void
+}
+class JPanel
+
+Insight ...> Category : " dependency "
+Insight ..> Transaction: dependency
+Transaction --> "*" Category: association
+Insight ..> JFrame :creates 2
+Insight ..> JPanel: creates 2
+Insight ..> PieChart: uses 2
+Insight ..> PieChartBuilder: uses 2
+Insight ..> XChartPanel :uses 2
+Insight ..> PieStyler: uses 2
+
+@enduml
diff --git a/docs/diagrams/processEditTransactionDiagram.puml b/docs/diagrams/processEditTransactionDiagram.puml
new file mode 100644
index 0000000000..4b7968dcb7
--- /dev/null
+++ b/docs/diagrams/processEditTransactionDiagram.puml
@@ -0,0 +1,49 @@
+@startuml
+'https://plantuml.com/sequence-diagram
+
+autonumber
+
+actor User
+participant "TransactionList" as TL
+participant "UserInterface" as UI
+participant ":AccountManager" as AM
+participant ":Parser" as P
+participant "EmptyArgumentException" as EAE
+participant "InvalidIndexException" as IIE
+participant "NumberFormatException" as NFE
+User -> TL: editTransaction [edit index]
+
+alt edit index is empty
+ TL -> EAE: throw EmptyArgumentException("edit index")
+end
+alt index is not an integer
+ TL -> NFE : throw NumberFormatException(edit_index)
+end
+TL -> TL: index = parseInt(data)
+alt index is within bounds
+ TL -> UI:
+ activate UI
+ UI --> TL: newTransaction
+ deactivate UI
+ TL -> P: parseEditTransaction(newTransaction, account)
+ activate P
+ P --> TL: updatedTransaction
+ deactivate P
+
+ TL -> AM: getAccountByAccountNumber(accountNumber)
+ activate AM
+ AM --> TL: account
+ deactivate AM
+
+ TL -> TL: setTransaction(updatedTransaction)
+
+ TL -> UI:
+ activate UI
+ UI --> TL: Display updated transaction
+ deactivate UI
+ TL --> User: Display Output
+else
+ TL -> IIE: throw InvalidIndexException(transaction_list_size)
+end
+
+@enduml
diff --git a/docs/diagrams/processList.puml b/docs/diagrams/processList.puml
new file mode 100644
index 0000000000..bdf6a761ba
--- /dev/null
+++ b/docs/diagrams/processList.puml
@@ -0,0 +1,107 @@
+@startuml
+'https://plantuml.com/sequence-diagram
+
+autonumber
+
+actor User
+participant ":TransactionManager" as TM
+participant ":UserInterface" as UI
+participant "LocalDate" as LD
+participant ":AccountManager" as AM
+
+User -> TM: processList()
+TM -> UI: printListOptions()
+activate UI
+UI --> User: Display list options
+deactivate UI
+User -> TM: selectOption(option)
+TM -> TM: determineAction(option)
+
+alt 1: All Transactions
+ TM -> TM: printTransactions()
+ TM -> UI: printTransactions()
+ activate UI
+ UI --> TM: Display all transactions
+ UI --> User: Confirmation message
+ deactivate UI
+else 2: Past Week Transactions
+ TM -> LD: now()
+ activate LD
+ LD --> TM: today
+ deactivate LD
+ TM -> TM: calculateStartDate("week")
+ TM -> TM: getPastTransactions(startDate)
+ TM -> UI: printPastTransactions(transactions, "week")
+ activate UI
+ UI --> TM: Display past week transactions
+ TM --> User: Confirmation message
+
+ deactivate UI
+else 3: Past Month Transactions
+ TM -> LD: now()
+ LD --> TM: today
+ TM -> TM: calculateStartDate("month")
+ TM -> TM: getPastTransactions(startDate)
+ TM -> UI: printPastTransactions(transactions, "month")
+ activate UI
+ UI --> TM: Display past month transactions
+ TM --> User: Confirmation message
+
+ deactivate UI
+else 4: Custom Date Transactions
+ TM -> UI: getStartDate()
+ activate UI
+ UI --> TM: startDate
+ deactivate UI
+ TM -> UI: getEndDate()
+ activate UI
+ UI --> TM: endDate
+ deactivate UI
+ TM -> TM: parseDates(startDate, endDate)
+ TM -> TM: getCustomDateTransactions(startDate, endDate)
+ TM -> UI: printCustomDateTransactions(transactions)
+ activate UI
+ UI --> TM: Display custom date transactions
+ TM --> User: Confirmation message
+
+ deactivate UI
+else 5: Account Transactions
+ TM -> UI: printAccountList(accounts)
+ activate UI
+ UI --> User: Display account list
+ deactivate UI
+ TM -> UI: getSelectedAccountNumber(accounts)
+ activate UI
+ UI --> TM: accountNumber
+ deactivate UI
+ TM -> AM: getAccountByAccountNumber(accountNumber)
+ activate AM
+ AM --> TM: account
+ deactivate AM
+ TM -> TM: getAccountTransactions(transactions, accountNumber)
+ TM -> UI: printAccountTransactions(accountTransactions, accountName, accountNumber)
+ activate UI
+ UI ---> TM: Display account transactions
+ TM ---> User: Confirmation message
+
+ deactivate UI
+else 6: Category Transactions
+ TM -> UI: listCategories()
+ activate UI
+ UI --> User: Display category list
+ deactivate UI
+ TM -> UI: getSelectedCategory()
+ activate UI
+ UI --> TM: categorySelected
+ deactivate UI
+ TM -> TM: fromNumber(category)
+ TM -> TM: getCategoryName()
+ TM -> TM: getCategoryTransactions(transactions, categorySelected)
+ TM -> UI: printCategoryTransactions(categoryTransactions, categoryName)
+ activate UI
+ UI --> TM: Display category transactions
+ TM --> User: Confirmation message
+
+ deactivate UI
+end
+@enduml
\ No newline at end of file
diff --git a/docs/diagrams/removeTransactionDiagram.puml b/docs/diagrams/removeTransactionDiagram.puml
new file mode 100644
index 0000000000..0156798974
--- /dev/null
+++ b/docs/diagrams/removeTransactionDiagram.puml
@@ -0,0 +1,38 @@
+@startuml
+'https://plantuml.com/sequence-diagram
+
+autonumber
+
+actor User
+participant "TransactionList" as TL
+participant "UserInterface" as UI
+participant ":AccountManager" as AM
+participant ":Account" as AC
+participant "EmptyArgumentException" as EAE
+participant "InvalidIndexException" as IIE
+User -> TL: removeTransaction [delete index]
+
+alt input.trim().length() < DELETE_BEGIN_INDEX
+ TL -> EAE: throw EmptyArgumentException
+end
+ TL -> TL: id = parseInt(data)
+ alt id >= LOWER_BOUND and id < size
+ TL -> AM: getAccountByAccountNumber(accountNumber)
+ activate AM
+ AM --> TL: :Account
+ deactivate AM
+
+ TL -> TL: get(id)
+ TL -> TL: remove(id)
+
+ TL -> AC: setBalance(newBalance)
+ TL -> UI:
+ activate UI
+ UI --> TL: Display delete confirmation message
+ deactivate UI
+ TL --> User: Confirmation message
+ else
+ TL -> IIE: throw InvalidIndexException
+ end
+
+@enduml
diff --git a/docs/images/AddAccountClassDiagram.png b/docs/images/AddAccountClassDiagram.png
new file mode 100644
index 0000000000..cda8631327
Binary files /dev/null and b/docs/images/AddAccountClassDiagram.png differ
diff --git a/docs/images/RemoveAccounrSequenceDiagram.png b/docs/images/RemoveAccounrSequenceDiagram.png
new file mode 100644
index 0000000000..585c4f98f6
Binary files /dev/null and b/docs/images/RemoveAccounrSequenceDiagram.png differ
diff --git a/docs/images/Shyam.jpg b/docs/images/Shyam.jpg
new file mode 100644
index 0000000000..5932994ba9
Binary files /dev/null and b/docs/images/Shyam.jpg differ
diff --git a/docs/images/TransactionListDiagram.png b/docs/images/TransactionListDiagram.png
new file mode 100644
index 0000000000..325f2b37e8
Binary files /dev/null and b/docs/images/TransactionListDiagram.png differ
diff --git a/docs/images/addAccountDiagram.png b/docs/images/addAccountDiagram.png
new file mode 100644
index 0000000000..efe4ca825d
Binary files /dev/null and b/docs/images/addAccountDiagram.png differ
diff --git a/docs/images/addTransactionDiagram.png b/docs/images/addTransactionDiagram.png
new file mode 100644
index 0000000000..2dc1c2ef2f
Binary files /dev/null and b/docs/images/addTransactionDiagram.png differ
diff --git a/docs/images/architectureDiagram.png b/docs/images/architectureDiagram.png
new file mode 100644
index 0000000000..69d844cbd5
Binary files /dev/null and b/docs/images/architectureDiagram.png differ
diff --git a/docs/images/insightDiagram.png b/docs/images/insightDiagram.png
new file mode 100644
index 0000000000..5d047bf795
Binary files /dev/null and b/docs/images/insightDiagram.png differ
diff --git a/docs/images/isaac.jpg b/docs/images/isaac.jpg
new file mode 100644
index 0000000000..45f031400d
Binary files /dev/null and b/docs/images/isaac.jpg differ
diff --git a/docs/images/list_options.png b/docs/images/list_options.png
new file mode 100644
index 0000000000..9135bf6427
Binary files /dev/null and b/docs/images/list_options.png differ
diff --git a/docs/images/processEditTransactionDiagram.png b/docs/images/processEditTransactionDiagram.png
new file mode 100644
index 0000000000..e191d58fc8
Binary files /dev/null and b/docs/images/processEditTransactionDiagram.png differ
diff --git a/docs/images/processList.png b/docs/images/processList.png
new file mode 100644
index 0000000000..9ec77df2d9
Binary files /dev/null and b/docs/images/processList.png differ
diff --git a/docs/images/removeTransactionDiagram.png b/docs/images/removeTransactionDiagram.png
new file mode 100644
index 0000000000..c14e3d98a3
Binary files /dev/null and b/docs/images/removeTransactionDiagram.png differ
diff --git a/docs/images/successful_add_acc_feature.png b/docs/images/successful_add_acc_feature.png
new file mode 100644
index 0000000000..f984a25012
Binary files /dev/null and b/docs/images/successful_add_acc_feature.png differ
diff --git a/docs/images/successful_add_feature.png b/docs/images/successful_add_feature.png
new file mode 100644
index 0000000000..a0cf0e5d26
Binary files /dev/null and b/docs/images/successful_add_feature.png differ
diff --git a/docs/images/successful_delete_acc_feature.png b/docs/images/successful_delete_acc_feature.png
new file mode 100644
index 0000000000..005873e674
Binary files /dev/null and b/docs/images/successful_delete_acc_feature.png differ
diff --git a/docs/images/successful_delete_transaction.png b/docs/images/successful_delete_transaction.png
new file mode 100644
index 0000000000..d68855a401
Binary files /dev/null and b/docs/images/successful_delete_transaction.png differ
diff --git a/docs/images/successful_edit_acc_feature.png b/docs/images/successful_edit_acc_feature.png
new file mode 100644
index 0000000000..7b2bb5c644
Binary files /dev/null and b/docs/images/successful_edit_acc_feature.png differ
diff --git a/docs/images/successful_edit_transaction.png b/docs/images/successful_edit_transaction.png
new file mode 100644
index 0000000000..0efcffacdf
Binary files /dev/null and b/docs/images/successful_edit_transaction.png differ
diff --git a/docs/images/successful_list_acc_feature.png b/docs/images/successful_list_acc_feature.png
new file mode 100644
index 0000000000..25303da5b9
Binary files /dev/null and b/docs/images/successful_list_acc_feature.png differ
diff --git a/docs/images/successful_list_feature.png b/docs/images/successful_list_feature.png
new file mode 100644
index 0000000000..6769384e67
Binary files /dev/null and b/docs/images/successful_list_feature.png differ
diff --git a/docs/images/successful_search.png b/docs/images/successful_search.png
new file mode 100644
index 0000000000..fc52f6bc2e
Binary files /dev/null and b/docs/images/successful_search.png differ
diff --git a/docs/images/vaibhav.png b/docs/images/vaibhav.png
new file mode 100644
index 0000000000..23e6062bc0
Binary files /dev/null and b/docs/images/vaibhav.png differ
diff --git a/docs/images/vavinan.jpg b/docs/images/vavinan.jpg
new file mode 100644
index 0000000000..c58022e1cd
Binary files /dev/null and b/docs/images/vavinan.jpg differ
diff --git a/docs/team/isaaceng7.md b/docs/team/isaaceng7.md
new file mode 100644
index 0000000000..f844181a04
--- /dev/null
+++ b/docs/team/isaaceng7.md
@@ -0,0 +1,29 @@
+# Isaac Eng Hong Yeow - Project Portfolio Page
+
+## Overview
+BudgetBuddy is a desktop financial tracker application that helps users to manage their personal
+finances. It allows users to track their income and expenses across multiple accounts and provides
+insights into their financial activities. It is optimized for use via a Command Line Interface (CLI)
+and is written in Java, and has about 3 kLoC.
+
+## Summary of Contributions
+Given below are my contributions to the project.
+* **Code Contributed**: [RepoSense Link](https://nus-cs2113-ay2324s2.github.io/tp-dashboard/?search=isaaceng7&breakdown=true&sort=groupTitle%20dsc&sortWithin=title&since=2024-02-23&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other)
+* **List Feature**: Added the ability to view existing transactions.
+ * What it does: allows users to view their existing transactions.
+ * Justification: this feature is key to the BudgetBuddy as users need to be aware of their finances through their transactions.
+ * Enhancements: users can choose between 6 different types of list to view:
+ 1. All Transactions
+ 2. Past Week Transactions
+ 3. Past Month Transactions
+ 4. Custom Date Transactions
+ 5. Account Transactions
+ 6. Category Transactions
+* **Documentation**:
+ * User Guide:
+ * Added documentation for the `list` feature.
+ * Developer Guide:
+ * Added implementation details for the `list` feature.
+ * Added architecture diagram of BudgetBuddy.
+* **Community**:
+ * Reported bugs and suggestions for other teams in class and PE-D.
\ 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/shyamkrishna33.md b/docs/team/shyamkrishna33.md
new file mode 100644
index 0000000000..c11e63412f
--- /dev/null
+++ b/docs/team/shyamkrishna33.md
@@ -0,0 +1,75 @@
+# Arun Gandhi Shyam Krishna - Project Portfolio Page
+
+## Overview
+
+BudgetBuddy is a desktop financial tracker application that helps users to manage their personal
+finances. It allows users to track their income and expenses across multiple accounts and provides
+insights into their financial activities. It is optimized for use via a Command Line Interface (CLI)
+and is written in Java, and has about 3 kLoC.
+
+## Summary of Contributions
+
+### Code Contributed:
+#### [RepoSense Link](https://nus-cs2113-ay2324s2.github.io/tp-dashboard/?search=Shyam&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2024-02-23&tabOpen=true&tabType=authorship&tabAuthor=ShyamKrishna33&tabRepo=AY2324S2-CS2113-T15-2%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code~other&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false)
+
+### Documentation:
+
+* User Guide:
+ * Added documentation of feature `add`.
+ * Added documentation of feature `insights`.
+* Developer Guide:
+ * Added implementation and details of `Category` feature.
+ * Added implementation and details of processTransaction method with sequence diagram and class diagram.
+* Community:
+ * Reported bugs and suggestions for other teams in class and PE-D.
+* Others:
+ * Added headers for methods in TransactionList, Transaction and DataStorage classes.
+
+### Feature 1 - Adding Transaction
+
+1. Initiated the transaction class with appropriate parameters.
+2. Created the `Add` command with the basic arguments to add a transaction to a
+ list of available transactions.
+3. Added JUnit test cases for all the important methods contributing to this feature.
+4. Performed exception handling for many types of edge cases with respect to the input arguments.
+
+**What it does:** Allows users to add a transaction.
+**Justification:** This feature is the backbone to the BudgetBuddy as users need to be able to enter a transaction to
+use the application.
+
+### Feature 2 - Data Storage
+
+1. Initiated file paths and file format to store the data of the list of transactions.
+2. Created `saveTransactions` method to take in an array of transactions, convert the objects into string format
+ ensuring no
+ information is lost in the process and writing the data in the specified file paths.
+3. Enabled reading of saved data back into an array of transaction objects using the method `readTransactionFile`.
+4. Performed exception handling to ensure that in all scenarios the data storage works as intended without any runtime
+ errors. For example, ensuring that the program does not crash when the files get corrupted due to external
+ intervention.
+
+**What it does:** Stores all the transactions entered by the user to the database.
+**Justification:** This feature is very important as it allows the user to track all the transactions in previous
+sessions as it would be meaningless to not have a data storage.
+
+### Feature 3 - Insights
+
+1. Used XChart library to display pie-charts on the available data.
+2. Segregated all the available transactions into income and expense type and calculated total amount for each
+ category in each type. Displayed the proportions of transactions in each category for each type.
+
+**What it does:** Displays 2 pie-charts, one for each type of transaction, which shows the proportion
+of each category in all the transactions.
+**Justification:** This feature is useful since it allows the user to visualize the distribution of
+his/her income/expense among the available categories.
+
+### Exception Handling
+
+* Performed overall exception handling for many peculiar cases like user deleting the storage when program
+ is running, user entering special characters in command syntax, user force quitting the program etc.
+* Also added JUnit test cases for many crucial methods to ensure robustness.
+
+### General Contribution
+
+* Took part regularly in code reviews and team meetings.
+* Reviewed around 20 Pull Requests.
diff --git a/docs/team/vavinan.md b/docs/team/vavinan.md
new file mode 100644
index 0000000000..4249b4608e
--- /dev/null
+++ b/docs/team/vavinan.md
@@ -0,0 +1,73 @@
+# Jeevanandham Vavinan - Project Portfolio Page
+
+### Overview
+BudgetBuddy is a desktop financial tracker application that helps users to manage their personal finances. It allows
+users to track their income and expenses across multiple accounts and provides insights into their financial activities.
+It is optimized for use via a Command Line Interface (CLI) and is written in Java, and has about 3 kLoC.
+
+### Summary of Contributions
+##### Code Contributed: [RepoSense Link](https://nus-cs2113-ay2324s2.github.io/tp-dashboard/?search=vavinan&breakdown=true&sort=groupTitle%20dsc&sortWithin=title&since=2024-02-23&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other)
+##### Documentation:
+ * User Guide:
+ * Added documentation for the feature `delete`.
+ * Added documentation for the feature `edit`.
+ * Added documentation for the feature `search`
+ * Developer Guide:
+ * Added implementation details for the feature `delete`.
+ * Added implementation details for the feature `edit`.
+ * Added implementation details for the feature `insights`.
+ * Added implementation details for the feature `search`.
+
+ * Community:
+ * Reported bugs and suggestions for other teams in class and PE-D.
+
+ * Others:
+ * Added headers for UserInterface class and Parser class
+
+
+### Feature 1 - Deleting Transaction
+
+1. Created the `removeTransaction` method to handle deletion of a transaction based on its index in the list.
+2. Facilitated with the command `delete` followed by the index ID of the transaction to be deleted.
+3. Implemented error handling for various edge cases, including empty input, invalid index, and out-of-bound
+ index.
+4. Removed the transaction from the transaction list and updated the account balance. Then the user will
+ get the confirmation message about the process.
+
+**What it does:** Allows users to delete the transaction.
+**Justification:** This feature is key to the BudgetBuddy as users need to be able to delete the transactions
+which has errors or added by mistake.
+
+### Feature 2 - Editing Transaction
+
+1. Implemented the `processEditTransaction` method to facilitate editing of a transaction based on its index.
+2. Facilitated by the command `edit` followed by the index ID of the transaction to be edited.
+3. Validated user input for the index and transaction data, handling exceptions such as empty input and
+ non-integer index.
+4. Prompted the user to provide updated information for the transaction and validated each piece of data,
+ and throw appropriate exceptions associated with the input being given.
+5. Updated the transaction list with the edited transaction and printed a confirmation message.
+
+**What it does:** Allows users to edit the transaction.
+**Justification:** This feature is key to the BudgetBuddy as users need to be able to edit the transactions
+which has been added with some mistakes and need to be updated.
+**Highlights:** Instead of expecting the user to type long command this feature prompts the user to input
+value for each data
+
+### Feature 3 - Search Transaction
+
+1. Implemented the `searchTransaction` method to facilitate searching of transactions using a keyword
+ based on description, date, category or amount.
+2. Facilitated by the command `search` followed by the keyword.
+3. If the keyword is missing, exception is thrown and the suer will be alerted
+4. The keyword is used to search from the list and the matching results will be shows as a table along
+ with its true **index ID**. If there is no matching transactions, then the user will be notified that there
+ is no matching transactions.
+
+**What it does:** Allows users to search for transactions
+**Justification:** This feature is key to the BudgetBuddy as if the transaction history is too long and
+a user wants to delete or edit transaction. Then this search functionality will be helpful as the user can
+enter a keyword to search for it to get the true index ID of the transaction. Then that ID can be used in
+delete or edit command.
+
+
diff --git a/docs/team/vibes-863.md b/docs/team/vibes-863.md
new file mode 100644
index 0000000000..25678d4530
--- /dev/null
+++ b/docs/team/vibes-863.md
@@ -0,0 +1,76 @@
+# Vaibhav Dileep Pillai's Project Portfolio Page
+
+## Overview
+
+BudgetBuddy is a desktop financial tracker application that helps users to manage their personal finances. It allows
+users to track their income and expenses across multiple accounts and provides insights into their financial activities.
+It is optimized for use via a Command Line Interface (CLI) and is written in Java, and has more than 4 kLoC.
+
+## Summary of Contributions
+
+Given below is a summary of my contributions to the project.
+
+### Code Contributed:
+
+#### [RepoSense Link](https://nus-cs2113-ay2324s2.github.io/tp-dashboard/?search=vibes-863&breakdown=true)
+
+### Features implemented:
+
+#### 1. Feature: Account Addition
+
+- **What it does:** Enables users to create new financial accounts within the application.
+- **Justification:** Fundamental for financial tracking, this feature allows users to start managing their finances by
+ setting up accounts with a name and initial balance.
+- **Highlights:** The implementation ensures that each account has a unique account number and integrates seamlessly
+ with the rest of the system to reflect updates across transactions and balances.
+
+#### 2. Feature: Account Deletion
+
+- **What it does:** Provides the functionality to remove existing accounts from the application.
+- **Justification:** Essential for maintaining current and relevant financial data, this feature allows users to delete
+ accounts that are obsolete or were created in error.
+- **Highlights:** Includes checks to prevent deletion of the last remaining account, thereby ensuring there is always at
+ least one account active for transaction logging.
+
+#### 3. Feature: Account Listing
+
+- **What it does:** Displays a comprehensive list of all user accounts with details such as account number, name, and
+ current balance.
+- **Justification:** Users need a quick overview of all their accounts to make informed financial decisions. This
+ feature supports financial awareness and planning.
+- **Highlights:** The list is dynamically updated, reflecting real-time changes in account details and balances.
+
+#### 4. Feature: Account Editing
+
+- **What it does:** Allows users to modify the details of existing accounts, such as changing the account name.
+- **Justification:** Flexibility in updating account information is crucial as it evolves with the user's financial
+ landscape.
+- **Highlights:** This feature ensures data integrity while allowing modifications, enhancing user experience by
+ allowing personalization of account information.
+
+#### 5. Feature: Account Data Storage
+
+- **What it does:** Ensures that account data is saved to a file system and can be reloaded each time the application
+ starts, preserving user data between sessions.
+- **Justification:** Vital for long-term financial tracking, users can shut down and restart the application without
+ losing their data, enabling continuous financial management.
+- **Highlights:** Implements robust error handling to manage file integrity issues and provides a seamless user
+ experience by automatically handling data storage and retrieval.
+
+### Contributions to User Guide:
+
+- Updated documentation for the features `delete-acc`, `list-acc`, and `edit-acc`.
+- Formatted the user guide to improve readability and consistency.
+
+### Contributions to Developer Guide:
+
+- Added implementation details for the features `Add Account` and `Remove Account`.
+- Formatted the developer guide to improve readability and consistency.
+
+### General Contributions:
+
+- Participated in team meetings and discussions to plan and implement features.
+- Managed `v1.0` release on GitHub.
+- Reviewed around 20 plus PRs and provided feedback to teammates.
+-
+
diff --git a/src/main/java/budgetbuddy/BudgetBuddy.java b/src/main/java/budgetbuddy/BudgetBuddy.java
new file mode 100644
index 0000000000..2c661f446f
--- /dev/null
+++ b/src/main/java/budgetbuddy/BudgetBuddy.java
@@ -0,0 +1,174 @@
+package budgetbuddy;
+
+import budgetbuddy.account.Account;
+import budgetbuddy.account.AccountManager;
+import budgetbuddy.exceptions.EmptyArgumentException;
+import budgetbuddy.exceptions.InvalidAddTransactionSyntax;
+import budgetbuddy.exceptions.InvalidArgumentSyntaxException;
+import budgetbuddy.exceptions.InvalidCategoryException;
+import budgetbuddy.exceptions.InvalidEditTransactionData;
+import budgetbuddy.exceptions.InvalidIndexException;
+import budgetbuddy.exceptions.InvalidTransactionTypeException;
+import budgetbuddy.insights.Insight;
+import budgetbuddy.parser.Parser;
+import budgetbuddy.storage.DataStorage;
+import budgetbuddy.transaction.TransactionList;
+import budgetbuddy.ui.UserInterface;
+
+import java.io.File;
+import java.io.IOException;
+
+import java.util.Scanner;
+import java.util.logging.FileHandler;
+import java.util.logging.Level;
+import java.util.logging.LogManager;
+import java.util.logging.Logger;
+import java.util.logging.SimpleFormatter;
+
+public class BudgetBuddy {
+ public static final int LIST_LENGTH = 5;
+ public static final String BYE = "bye";
+ public static final String LIST = "list";
+ public static final String DELETE = "delete";
+ public static final String ADD = "add";
+ public static final String EDIT = "edit";
+ public static final String HELP = "help";
+ public static final String ADD_ACC = "add-acc";
+ public static final String INSIGHTS = "insights";
+ public static final String LIST_ACC = "list-acc";
+ public static final String DELETE_ACC = "delete-acc";
+ public static final String EDIT_ACC = "edit-acc";
+ public static final String SEARCH = "search";
+ public static final Logger LOGGER = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
+ private final AccountManager accountManager;
+ private final TransactionList transactions;
+
+ /**
+ * Creates a BudgetBuddy object with the account manager and transaction list.
+ */
+ public BudgetBuddy() {
+ DataStorage dataStorage = new DataStorage();
+ this.accountManager = dataStorage.loadAccounts();
+ this.transactions = dataStorage.loadTransactions(accountManager.getExistingAccountNumbers());
+ }
+
+ /**
+ * Sets up the logger for the BudgetBuddy application.
+ */
+ private static void setupLogger() {
+ LogManager.getLogManager().reset();
+ LOGGER.setLevel(java.util.logging.Level.ALL);
+ try {
+ File logsDir = new File("logs");
+ if (!logsDir.exists()) {
+ logsDir.mkdir();
+ }
+ FileHandler fh = new FileHandler("logs/budgetBuggyLog.log");
+ fh.setFormatter(new SimpleFormatter());
+ fh.setLevel(Level.INFO);
+ LOGGER.addHandler(fh);
+ } catch (IOException e) {
+ UserInterface.printLoggerSetupError();
+ }
+ }
+
+ /**
+ * Main entry-point for the java.BudgetBuddy application.
+ */
+ public static void main(String[] args) {
+ setupLogger();
+ new BudgetBuddy().run();
+ }
+
+ /**
+ * Runs the BudgetBuddy application.
+ */
+ public void run() {
+ Scanner in = UserInterface.in;
+ String logo = "BUDGET BUDDY";
+
+ System.out.println("Hello from\n" + logo);
+ System.out.println("What can I do for you?");
+
+
+ boolean isRunning = true;
+
+ while (isRunning) {
+ String input = in.nextLine();
+ try {
+ if (input.contains(",")){
+ throw new InvalidArgumentSyntaxException("Input cannot contain ',' comma.");
+ }
+ switch (input.split(" ")[0].toLowerCase()) {
+ case BYE:
+ Insight.closeInsightFrames();
+ UserInterface.printGoodBye();
+ isRunning = false;
+ break;
+ case LIST:
+ transactions.processList(accountManager.getAccounts(), accountManager);
+ break;
+ case DELETE:
+ transactions.removeTransaction(input, accountManager);
+ break;
+ case ADD:
+ int accountNumber = Parser.parseAccountNumber(input);
+ Account account = accountManager.getAccountByAccountNumber(accountNumber);
+ transactions.processTransaction(input, account);
+ break;
+ case EDIT:
+ transactions.processEditTransaction(input, accountManager);
+ break;
+ case HELP:
+ transactions.helpWithUserCommands(input);
+ break;
+ case ADD_ACC:
+ accountManager.processAddAccount(input);
+ break;
+ case INSIGHTS:
+ transactions.displayInsights();
+ break;
+ case LIST_ACC:
+ UserInterface.printListOfAccounts(accountManager.getAccounts());
+ break;
+ case DELETE_ACC:
+ accountManager.removeAccount(input, transactions);
+ break;
+ case EDIT_ACC:
+ accountManager.processEditAccount(input);
+ break;
+ case SEARCH:
+ transactions.searchTransactions(input);
+ break;
+ default:
+ UserInterface.printNoCommandExists();
+ }
+ transactions.saveTransactionList();
+ accountManager.saveAccounts();
+
+ } catch (InvalidAddTransactionSyntax e) {
+ UserInterface.printInvalidAddSyntax(e.getMessage());
+ } catch (NumberFormatException e) {
+ UserInterface.printNumberFormatError(e.getMessage());
+ } catch (InvalidTransactionTypeException e) {
+ UserInterface.printTransactionTypeError(e.getMessage());
+ } catch (EmptyArgumentException e) {
+ UserInterface.printEmptyArgumentError(e.getMessage());
+ } catch (InvalidIndexException e) {
+ UserInterface.printInvalidIndex("Given index id is out of bound",
+ Integer.parseInt(e.getMessage()));
+ } catch (IndexOutOfBoundsException ignored) {
+ UserInterface.printInvalidInput("Please check your command syntax");
+ } catch (InvalidEditTransactionData e) {
+ UserInterface.printInvalidInput(e.getMessage());
+ } catch (InvalidArgumentSyntaxException e) {
+ UserInterface.printInvalidArgumentSyntax(e.getMessage());
+ } catch (InvalidCategoryException e) {
+ UserInterface.printInvalidCategoryError();
+ } catch (Exception e) {
+ UserInterface.printExceptionErrorMessage(e.getMessage());
+ }
+ }
+
+ }
+}
diff --git a/src/main/java/budgetbuddy/account/Account.java b/src/main/java/budgetbuddy/account/Account.java
new file mode 100644
index 0000000000..67a12832ce
--- /dev/null
+++ b/src/main/java/budgetbuddy/account/Account.java
@@ -0,0 +1,102 @@
+package budgetbuddy.account;
+
+import java.util.logging.Logger;
+
+/**
+ * Represents an account in the budget buddy system.
+ */
+public class Account {
+ public static final Logger LOGGER = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
+ private final int accountNumber;
+ private String name;
+ private double balance;
+
+ /**
+ * Creates an account with the given account number, and default name and balance.
+ *
+ * @param accountNumber the account number
+ */
+ public Account(int accountNumber) {
+ assert accountNumber > 0 : "Account number must be positive";
+ this.accountNumber = accountNumber;
+ this.name = "";
+ this.balance = 0.00;
+ LOGGER.info("Account created with default name and balance");
+ }
+
+ /**
+ * Creates an account with the given account number, name and balance.
+ *
+ * @param accountNumber the account number
+ * @param name the name
+ * @param balance the balance
+ */
+ public Account(int accountNumber, String name, double balance) {
+ assert accountNumber > 0 : "Account number must be positive";
+ assert name != null : "Name cannot be null";
+ this.accountNumber = accountNumber;
+ this.name = name;
+ this.balance = balance;
+ LOGGER.info("Account created with specified name and balance");
+ }
+
+ /**
+ * Returns the current balance of the account.
+ *
+ * @return the current balance
+ */
+ public double getBalance() {
+ return balance;
+ }
+
+ /**
+ * Sets the balance of the account to the given amount.
+ *
+ * @param balance the new balance
+ */
+ public void setBalance(double balance) {
+ this.balance = balance;
+ LOGGER.info("Account balance updated");
+ }
+
+ /**
+ * Returns the account number.
+ *
+ * @return the account number
+ */
+ public int getAccountNumber() {
+ return accountNumber;
+ }
+
+ /**
+ * Returns the name of the account.
+ *
+ * @return the name of the account
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the name of the account to the given name.
+ *
+ * @param name the new name
+ */
+ public void setName(String name) {
+ assert name != null : "Name cannot be null";
+ this.name = name;
+ LOGGER.info("Account name updated");
+ }
+
+ /**
+ * Returns a string representation of the account, including the account number, name, and balance.
+ *
+ * @return a string representation of the account
+ */
+ @Override
+ public String toString() {
+ return (" Account Number: " + getAccountNumber() + " | " +
+ " Name: " + getName() + " | " +
+ " Balance: " + getBalance());
+ }
+}
diff --git a/src/main/java/budgetbuddy/account/AccountManager.java b/src/main/java/budgetbuddy/account/AccountManager.java
new file mode 100644
index 0000000000..cfc4da855c
--- /dev/null
+++ b/src/main/java/budgetbuddy/account/AccountManager.java
@@ -0,0 +1,217 @@
+package budgetbuddy.account;
+
+import budgetbuddy.exceptions.EmptyArgumentException;
+import budgetbuddy.exceptions.InvalidArgumentSyntaxException;
+import budgetbuddy.exceptions.InvalidIndexException;
+import budgetbuddy.parser.Parser;
+import budgetbuddy.storage.DataStorage;
+import budgetbuddy.transaction.TransactionList;
+import budgetbuddy.transaction.type.Transaction;
+import budgetbuddy.ui.UserInterface;
+
+import java.util.ArrayList;
+import java.util.Random;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Manages the accounts in the budget buddy system.
+ */
+public class AccountManager {
+ public static final int INDEX_OFFSET = 1;
+ public static final Logger LOGGER = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
+ private final DataStorage dataStorage = new DataStorage();
+ private final ArrayList accounts;
+ private final ArrayList existingAccountNumbers;
+
+
+ /**
+ * Creates an AccountManager with empty account and account number lists.
+ */
+ public AccountManager() {
+ this.accounts = new ArrayList<>();
+ this.existingAccountNumbers = new ArrayList<>();
+ LOGGER.log(Level.INFO, "AccountManager created with empty account and account number lists");
+ }
+
+ /**
+ * Creates an AccountManager with the given account and account number lists.
+ *
+ * @param accounts the list of accounts
+ * @param existingAccountNumbers the list of existing account numbers
+ */
+ public AccountManager(ArrayList accounts, ArrayList existingAccountNumbers) {
+ assert accounts != null : "Accounts list cannot be null";
+ assert existingAccountNumbers != null : "Existing account numbers list cannot be null";
+ this.accounts = accounts;
+ this.existingAccountNumbers = existingAccountNumbers;
+ LOGGER.log(Level.INFO, "AccountManager created with specified account and account number lists");
+ }
+
+ /**
+ * Adds an account with the given name and initial balance.
+ *
+ * @param name the name of the account
+ * @param initialBalance the initial balance of the account
+ */
+ public void addAccount(String name, double initialBalance) {
+ assert name != null : "Name cannot be null";
+ int newAccountNumber = generateAccountNumber();
+ accounts.add(new Account(newAccountNumber, name, initialBalance));
+ existingAccountNumbers.add(newAccountNumber);
+ LOGGER.log(Level.INFO, "Account added");
+ }
+
+ /**
+ * Generates a unique four-digit account number.
+ *
+ * @return the generated account number
+ */
+ public int generateAccountNumber() {
+ Random random = new Random();
+ boolean noMatchFound = true;
+ int fourDigitNumber;
+ do {
+ fourDigitNumber = 1000 + random.nextInt(9000);
+ for (int accountNumber : existingAccountNumbers) {
+ if (accountNumber == fourDigitNumber) {
+ noMatchFound = false;
+ LOGGER.log(Level.WARNING, "Account number already exists. Generating new account number.");
+ break;
+ }
+ }
+ } while (!noMatchFound);
+
+ LOGGER.log(Level.INFO, "Account number generated");
+ return fourDigitNumber;
+ }
+
+ /**
+ * Processes the addition of an account from the given input.
+ *
+ * @param input the input string
+ * @throws InvalidArgumentSyntaxException if the input syntax is invalid
+ * @throws NumberFormatException if the input contains an invalid number
+ * @throws EmptyArgumentException if the input is empty
+ */
+ public void processAddAccount(String input)
+ throws InvalidArgumentSyntaxException, NumberFormatException, EmptyArgumentException {
+ assert input != null : "Input cannot be null";
+ LOGGER.log(Level.INFO, "Processing add account command");
+ String[] arguments = {"/n/", "/$/"};
+ for (String argument : arguments) {
+ if (!input.contains(argument)) {
+ LOGGER.log(Level.WARNING, "Invalid add account syntax.");
+ throw new InvalidArgumentSyntaxException("Invalid add account syntax.");
+ }
+ }
+ String[] parsedData = Parser.parseAddAccount(input);
+ addAccount(parsedData[0], Double.parseDouble(parsedData[1]));
+ UserInterface.printAddAccountMessage(getAccount(accounts.size() - INDEX_OFFSET).toString());
+ LOGGER.log(Level.INFO, "Account added successfully");
+ }
+
+ /**
+ * Removes an account with the given input and updates the transaction list.
+ *
+ * @param input the input string
+ * @param transactions the transaction list
+ * @throws NumberFormatException if the input contains an invalid number
+ * @throws InvalidArgumentSyntaxException if the input syntax is invalid
+ * @throws EmptyArgumentException if the input is empty
+ * @throws InvalidIndexException if the input contains an invalid index
+ */
+ public void removeAccount(String input, TransactionList transactions)
+ throws NumberFormatException, InvalidArgumentSyntaxException, EmptyArgumentException,
+ InvalidIndexException {
+ assert input != null : "Input cannot be null";
+ assert transactions != null : "Transactions cannot be null";
+ LOGGER.log(Level.INFO, "Processing remove account command");
+ int accountNumber = Parser.parseRemoveAccount(input);
+ Account accountRemoved = getAccountByAccountNumber(accountNumber);
+ if (accounts.size() == 1) {
+ UserInterface.printCannotDeleteLastAccountMessage();
+ LOGGER.log(Level.WARNING, "Cannot delete last account.");
+ return;
+ }
+ accounts.remove(accountRemoved);
+ existingAccountNumbers.remove(Integer.valueOf(accountNumber));
+ ArrayList transactionsRemoved = transactions.removeTransactionsByAccountNumber(accountNumber);
+ UserInterface.printDeleteAccountMessage(accountRemoved.toString(), transactionsRemoved);
+ LOGGER.log(Level.INFO, "Account removed successfully");
+ }
+
+ /**
+ * Returns the account with the given account ID.
+ *
+ * @param accountId the account ID
+ * @return the account
+ */
+ public Account getAccount(int accountId) {
+ assert accountId >= 0 : "Account ID cannot be negative";
+ return accounts.get(accountId);
+ }
+
+ /**
+ * Returns the account with the given account number.
+ *
+ * @param accountNumber the account number
+ * @return the account
+ * @throws IllegalArgumentException if the account is not found
+ */
+ public Account getAccountByAccountNumber(int accountNumber) {
+ assert accountNumber > 0 : "Account number must be positive";
+ for (Account account : accounts) {
+ if (account.getAccountNumber() == accountNumber) {
+ return account;
+ }
+ }
+ LOGGER.log(Level.WARNING, "Account not found.");
+ throw new IllegalArgumentException("Account not found.");
+ }
+
+ /**
+ * Returns the list of accounts.
+ *
+ * @return the list of accounts
+ */
+ public ArrayList getAccounts() {
+ return accounts;
+ }
+
+ /**
+ * Processes the editing of an account from the given input.
+ *
+ * @param input the input string
+ * @throws EmptyArgumentException if the input is empty
+ * @throws IllegalArgumentException if the input is invalid
+ */
+ public void processEditAccount(String input) throws EmptyArgumentException, IllegalArgumentException,
+ InvalidArgumentSyntaxException {
+ assert input != null : "Input cannot be null";
+ LOGGER.log(Level.INFO, "Processing edit account command");
+ int accountNumber = Parser.parseEditAccount(input);
+ Account account = getAccountByAccountNumber(accountNumber);
+ String newName = UserInterface.getNewAccountName(account.toString());
+ account.setName(newName);
+ UserInterface.printUpdatedAccount(account.toString());
+ LOGGER.log(Level.INFO, "Account edited successfully");
+ }
+
+ /**
+ * Saves the accounts to the data storage.
+ */
+ public void saveAccounts() {
+ assert accounts != null : "Accounts list cannot be null";
+ dataStorage.saveAccounts(accounts);
+ }
+
+ /**
+ * Returns the list of existing account numbers.
+ *
+ * @return the list of existing account numbers
+ */
+ public ArrayList getExistingAccountNumbers() {
+ return existingAccountNumbers;
+ }
+}
diff --git a/src/main/java/budgetbuddy/categories/Category.java b/src/main/java/budgetbuddy/categories/Category.java
new file mode 100644
index 0000000000..6537f0e95b
--- /dev/null
+++ b/src/main/java/budgetbuddy/categories/Category.java
@@ -0,0 +1,40 @@
+package budgetbuddy.categories;
+
+import budgetbuddy.exceptions.InvalidCategoryException;
+
+public enum Category {
+ DINING(1, "Dining"),
+ GROCERIES(2, "Groceries"),
+ UTILITIES(3, "Utilities"),
+ TRANSPORTATION(4, "Transportation"),
+ HEALTHCARE(5, "Healthcare"),
+ ENTERTAINMENT(6, "Entertainment"),
+ RENT(7, "Rent"),
+ SALARY(8, "Salary"),
+ OTHERS(9, "Others");
+
+ private final int categoryNum;
+ private final String categoryName;
+
+ Category(int categoryNum, String categoryName) {
+ this.categoryNum = categoryNum;
+ this.categoryName = categoryName;
+ }
+
+ public static Category fromNumber(int number) throws InvalidCategoryException {
+ for (Category category : Category.values()) {
+ if (category.categoryNum == number) {
+ return category;
+ }
+ }
+ throw new InvalidCategoryException("Category index out of bounds");
+ }
+
+ public String getCategoryName() {
+ return categoryName;
+ }
+
+ public int getCategoryNum() {
+ return categoryNum;
+ }
+}
diff --git a/src/main/java/budgetbuddy/exceptions/EmptyArgumentException.java b/src/main/java/budgetbuddy/exceptions/EmptyArgumentException.java
new file mode 100644
index 0000000000..9d2582a60c
--- /dev/null
+++ b/src/main/java/budgetbuddy/exceptions/EmptyArgumentException.java
@@ -0,0 +1,7 @@
+package budgetbuddy.exceptions;
+
+public class EmptyArgumentException extends Exception {
+ public EmptyArgumentException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/budgetbuddy/exceptions/FileCorruptedException.java b/src/main/java/budgetbuddy/exceptions/FileCorruptedException.java
new file mode 100644
index 0000000000..8925be8854
--- /dev/null
+++ b/src/main/java/budgetbuddy/exceptions/FileCorruptedException.java
@@ -0,0 +1,9 @@
+package budgetbuddy.exceptions;
+
+//@@author ShyamKrishna33
+public class FileCorruptedException extends Exception {
+ public FileCorruptedException(String message) {
+ super(message);
+ }
+}
+//@@author
diff --git a/src/main/java/budgetbuddy/exceptions/InvalidAddTransactionSyntax.java b/src/main/java/budgetbuddy/exceptions/InvalidAddTransactionSyntax.java
new file mode 100644
index 0000000000..1c870a3211
--- /dev/null
+++ b/src/main/java/budgetbuddy/exceptions/InvalidAddTransactionSyntax.java
@@ -0,0 +1,7 @@
+package budgetbuddy.exceptions;
+
+public class InvalidAddTransactionSyntax extends Exception{
+ public InvalidAddTransactionSyntax(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/budgetbuddy/exceptions/InvalidArgumentSyntaxException.java b/src/main/java/budgetbuddy/exceptions/InvalidArgumentSyntaxException.java
new file mode 100644
index 0000000000..127ffff902
--- /dev/null
+++ b/src/main/java/budgetbuddy/exceptions/InvalidArgumentSyntaxException.java
@@ -0,0 +1,7 @@
+package budgetbuddy.exceptions;
+
+public class InvalidArgumentSyntaxException extends Exception{
+ public InvalidArgumentSyntaxException (String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/budgetbuddy/exceptions/InvalidCategoryException.java b/src/main/java/budgetbuddy/exceptions/InvalidCategoryException.java
new file mode 100644
index 0000000000..4568b7bf3a
--- /dev/null
+++ b/src/main/java/budgetbuddy/exceptions/InvalidCategoryException.java
@@ -0,0 +1,7 @@
+package budgetbuddy.exceptions;
+
+public class InvalidCategoryException extends Exception{
+ public InvalidCategoryException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/budgetbuddy/exceptions/InvalidEditTransactionData.java b/src/main/java/budgetbuddy/exceptions/InvalidEditTransactionData.java
new file mode 100644
index 0000000000..3f1520c8ac
--- /dev/null
+++ b/src/main/java/budgetbuddy/exceptions/InvalidEditTransactionData.java
@@ -0,0 +1,7 @@
+package budgetbuddy.exceptions;
+
+public class InvalidEditTransactionData extends Exception{
+ public InvalidEditTransactionData(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/budgetbuddy/exceptions/InvalidIndexException.java b/src/main/java/budgetbuddy/exceptions/InvalidIndexException.java
new file mode 100644
index 0000000000..fde96f40bd
--- /dev/null
+++ b/src/main/java/budgetbuddy/exceptions/InvalidIndexException.java
@@ -0,0 +1,7 @@
+package budgetbuddy.exceptions;
+
+public class InvalidIndexException extends Exception {
+ public InvalidIndexException (String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/budgetbuddy/exceptions/InvalidTransactionTypeException.java b/src/main/java/budgetbuddy/exceptions/InvalidTransactionTypeException.java
new file mode 100644
index 0000000000..40f6960432
--- /dev/null
+++ b/src/main/java/budgetbuddy/exceptions/InvalidTransactionTypeException.java
@@ -0,0 +1,7 @@
+package budgetbuddy.exceptions;
+
+public class InvalidTransactionTypeException extends Exception{
+ public InvalidTransactionTypeException (String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/budgetbuddy/insights/Insight.java b/src/main/java/budgetbuddy/insights/Insight.java
new file mode 100644
index 0000000000..198ffc2d7d
--- /dev/null
+++ b/src/main/java/budgetbuddy/insights/Insight.java
@@ -0,0 +1,120 @@
+package budgetbuddy.insights;
+
+import budgetbuddy.categories.Category;
+import budgetbuddy.transaction.type.Expense;
+import budgetbuddy.transaction.type.Income;
+import budgetbuddy.transaction.type.Transaction;
+import org.knowm.xchart.PieChart;
+import org.knowm.xchart.PieChartBuilder;
+import org.knowm.xchart.XChartPanel;
+import org.knowm.xchart.style.PieStyler;
+
+
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import java.awt.BorderLayout;
+import java.util.ArrayList;
+import java.awt.Window;
+
+import static java.lang.Math.abs;
+
+public class Insight {
+ //@@author ShyamKrishna33
+ public static void displayCategoryInsight(ArrayList transactionArrayList) {
+ Category[] categoryArray = Category.values();
+ Double[] expenseArray = new Double[categoryArray.length];
+ Double[] incomeArray = new Double[categoryArray.length];
+
+ for (int i = 0; i < categoryArray.length; i++) {
+ expenseArray[i] = 0.0;
+ incomeArray[i] = 0.0;
+ }
+ for(Transaction t : transactionArrayList){
+ Category category = t.getCategory();
+ int index = indexOf(categoryArray, category);
+ if (t instanceof Expense) {
+ expenseArray[index] += abs(t.getAmount());
+ } else if (t instanceof Income){
+ incomeArray[index] += abs(t.getAmount());
+ }
+ }
+ displayPieChart(categoryArray, incomeArray, expenseArray);
+ }
+
+ //@@author
+ private static void displayPieChart(Category[] categoryArray, Double[] incomeArray, Double[] expenseArray) {
+ JFrame incomeFrame = new JFrame("Income Insights");
+ incomeFrame.setLayout(new BorderLayout());
+ JFrame expenseFrame = new JFrame("Expense Insights");
+ expenseFrame.setLayout(new BorderLayout());
+
+ JPanel incomePanel = new JPanel();
+ incomeFrame.add(incomePanel, BorderLayout.CENTER);
+ JPanel expensePanel = new JPanel();
+ expenseFrame.add(expensePanel, BorderLayout.CENTER);
+
+ PieChart incomeChart = new PieChartBuilder().width(800).height(600).title("Income Divide").build();
+
+ // Customize Chart
+ incomeChart.getStyler().setCircular(true);
+ incomeChart.getStyler().setLegendVisible(true);
+ incomeChart.getStyler().setAnnotationType(PieStyler.AnnotationType.LabelAndPercentage);
+
+ for (int i = 0; i < categoryArray.length; i++) {
+ if (incomeArray[i] != 0) {
+ incomeChart.addSeries(categoryArray[i].getCategoryName(), incomeArray[i]);
+ }
+ }
+
+ // Show it
+ incomePanel.add(new XChartPanel<>(incomeChart));
+ incomeFrame.pack();
+ incomeFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+ incomeFrame.setVisible(true);
+
+ PieChart expenseChart = new PieChartBuilder().width(800).height(600).title("Expense Divide").build();
+
+ // Customize Chart
+ expenseChart.getStyler().setCircular(true);
+ expenseChart.getStyler().setLegendVisible(true);
+ expenseChart.getStyler().setAnnotationType(PieStyler.AnnotationType.LabelAndPercentage);
+
+ for (int i = 0; i < categoryArray.length; i++) {
+ if (expenseArray[i] != 0) {
+ expenseChart.addSeries(categoryArray[i].getCategoryName(), expenseArray[i]);
+ }
+ }
+
+ // Show it
+ expensePanel.add(new XChartPanel<>(expenseChart));
+ expenseFrame.pack();
+ expenseFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+ expenseFrame.setVisible(true);
+
+ }
+
+ //@@author ShyamKrishna33
+ private static int indexOf(Category[] array, Category target) {
+ for (int i = 0; i < array.length; i++) {
+ if (array[i].equals(target)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ //@@author Vavinan
+ public static void closeInsightFrames() {
+ // Close any open insight frames here
+ for (Window window : Window.getWindows()) {
+ if (window instanceof JFrame) {
+ String title = ((JFrame) window).getTitle();
+ if (title != null && (title.equals("Income Insights") || title.equals("Expense Insights"))) {
+ window.dispose();
+ }
+ }
+ }
+ }
+ //@@author
+
+}
diff --git a/src/main/java/budgetbuddy/parser/Parser.java b/src/main/java/budgetbuddy/parser/Parser.java
new file mode 100644
index 0000000000..c0672f7370
--- /dev/null
+++ b/src/main/java/budgetbuddy/parser/Parser.java
@@ -0,0 +1,255 @@
+package budgetbuddy.parser;
+
+import budgetbuddy.account.Account;
+import budgetbuddy.categories.Category;
+
+import budgetbuddy.exceptions.EmptyArgumentException;
+import budgetbuddy.exceptions.InvalidAddTransactionSyntax;
+import budgetbuddy.exceptions.InvalidArgumentSyntaxException;
+import budgetbuddy.exceptions.InvalidCategoryException;
+import budgetbuddy.exceptions.InvalidEditTransactionData;
+import budgetbuddy.exceptions.InvalidTransactionTypeException;
+import budgetbuddy.transaction.TransactionList;
+import budgetbuddy.transaction.type.Expense;
+import budgetbuddy.transaction.type.Income;
+import budgetbuddy.transaction.type.Transaction;
+import budgetbuddy.ui.UserInterface;
+
+import java.util.logging.Logger;
+import java.util.logging.Level;
+
+/**
+ * Parses the user input into data that is easily understandable by other classes and methods.
+ */
+public class Parser {
+
+ public static final int ADD_COMMAND_INDEX = 3;
+ public static final int HELP_BEGIN_INDEX = 4;
+ public static final Logger LOGGER = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
+ private static final int ADD_ACC_COMMAND_INDEX = 7;
+
+ /**
+ * The function `parseAccountNumber` extracts and returns an account number from a given input
+ * string following a specific syntax.
+ *
+ * @param input The input string that contains the account number to be parsed.
+ * @return The method is returning an integer value, which is the account number parsed from the input string.
+ */
+ public static int parseAccountNumber(String input) throws InvalidArgumentSyntaxException {
+ String data = input.substring(ADD_COMMAND_INDEX + 1);
+ String[] parseData = data.split("/");
+ for (int i = 0; i < parseData.length - 1; i++) {
+ if (parseData[i].trim().equals("a")) {
+ return Integer.parseInt(parseData[i + 1].trim());
+ }
+ }
+ throw new InvalidArgumentSyntaxException("Invalid add syntax.");
+ }
+
+ /**
+ * The function `parseUserInputToTransaction` takes user input, parses it to create a transaction
+ * object (either income or expense), and handles various exceptions related to invalid input.
+ *
+ * @param input takes a user input that contain transaction details in a specific format.
+ * @param account The `account` parameter in this method represents the account to which the transaction belongs.
+ * @return The method `parseUserInputToTransaction` is returning a `Transaction` object, which can
+ * be either an `Income` or an `Expense` object based on the type provided in the input.
+ */
+
+ public Transaction parseUserInputToTransaction(String input, Account account)
+ throws InvalidTransactionTypeException, NumberFormatException,
+ EmptyArgumentException, InvalidCategoryException, InvalidAddTransactionSyntax {
+ LOGGER.log(Level.INFO, "Parsing user input.");
+
+ String data = input.substring(ADD_COMMAND_INDEX + 1);
+ String[] parseData = data.split("/");
+ String type = null;
+ String description = null;
+ String date = null;
+ String amount = null;
+ int category = -1;
+ for (int i = 0; i < parseData.length - 1; i++) {
+ switch (parseData[i].trim()) {
+ case "t":
+ type = parseData[i + 1].trim();
+ break;
+ case "n":
+ description = parseData[i + 1].trim();
+ break;
+ case "$":
+ if (TransactionList.isNotDouble(parseData[i + 1].trim())) {
+ LOGGER.log(Level.SEVERE, "Number format incorrect");
+ throw new NumberFormatException(parseData[i + 1].trim());
+ } else {
+ amount = parseData[i + 1].trim();
+ break;
+ }
+ case "d":
+ date = parseData[i + 1].trim();
+ break;
+ case "c":
+ category = Integer.parseInt(parseData[i + 1].trim());
+ break;
+ default:
+ break;
+ }
+ }
+ assert amount != null;
+ assert type != null;
+
+ if (category == -1) {
+ LOGGER.log(Level.INFO, "Category not entered. Prompting for category.");
+ UserInterface.listCategories();
+ category = UserInterface.getCategoryNum();
+ }
+
+ if (category < 1 || category > 9) {
+ LOGGER.log(Level.SEVERE, "Category index out of bounds");
+ throw new InvalidCategoryException("Category Index out of bounds");
+ }
+
+ if (Double.parseDouble(amount) < 0) {
+ LOGGER.log(Level.SEVERE, "Received negative amount.");
+ throw new InvalidAddTransactionSyntax("Amount cannot be negative");
+ }
+
+ if (description.trim().isEmpty() || type.trim().isEmpty()) {
+ LOGGER.log(Level.SEVERE, "One or more arguments are empty");
+ throw new EmptyArgumentException("data for the arguments ");
+ } else if (type.equalsIgnoreCase("income")) {
+ Income income = new Income(account.getAccountNumber(), account.getName(), description,
+ Double.parseDouble(amount), date, account);
+ income.setCategory(Category.fromNumber(category));
+ LOGGER.log(Level.INFO, "Successfully created transaction object");
+ return income;
+ } else if (type.equalsIgnoreCase("expense")) {
+ Expense expense = new Expense(account.getAccountNumber(), account.getName(), description,
+ Double.parseDouble(amount), date, account);
+ expense.setCategory(Category.fromNumber(category));
+ LOGGER.log(Level.INFO, "Successfully created transaction object");
+ return expense;
+ } else {
+ LOGGER.log(Level.SEVERE, "Received invalid transaction type");
+ throw new InvalidTransactionTypeException(type);
+ }
+ }
+
+ //@@author Vavinan
+
+ /**
+ * The `parseEditTransaction` function in Java parses a new transaction string and creates either
+ * an Income or Expense object based on the transaction type, validating the category number and
+ * throwing exceptions for invalid data.
+ *
+ * @param newTransaction The `newTransaction` string is expected to be in a specific format
+ * @param account The `account` parameter in the `parseEditTransaction` method represents the
+ * account to which the transaction belongs.
+ * @return The `parseEditTransaction` method is returning a `Transaction` object, which can be either
+ * an `Income` or `Expense` object based on the type provided in the `newTransaction` string.
+ */
+
+ public Transaction parseEditTransaction(String newTransaction, Account account) throws InvalidEditTransactionData,
+ InvalidCategoryException {
+ String[] parts = newTransaction.split(" \\| ");
+
+ String type = parts[0].trim();
+ String description = parts[1].trim();
+ String date = parts[2].trim();
+ String amount = parts[3].trim();
+ String category = parts[4].trim();
+ int categoryValue = Integer.parseInt(category);
+ if (categoryValue <= 0 || categoryValue > 9) {
+ throw new InvalidEditTransactionData("Choose category number from the list 1-9");
+ }
+ if (type.equalsIgnoreCase("income")) {
+ Income income = new Income(account.getAccountNumber(), account.getName(), description,
+ Double.parseDouble(amount), date, account);
+ income.setCategory(Category.fromNumber(categoryValue));
+ return income;
+ } else if (type.equalsIgnoreCase("expense")) {
+ Expense expense = new Expense(account.getAccountNumber(), account.getName(), description,
+ Double.parseDouble(amount), date, account);
+ expense.setCategory(Category.fromNumber(categoryValue));
+ return expense;
+ } else {
+ throw new InvalidEditTransactionData(" One or more data is wrong. ");
+ }
+ }
+
+ public String parseHelpCommand(String input) {
+ return input.substring(HELP_BEGIN_INDEX).trim();
+ }
+ //@@author
+
+ /**
+ * The `parseAddAccount` function in Java parses the input and returns the account name and
+ * balance of the new account that is to added.
+ *
+ * @param input It contains details about account name and initial balance
+ * @return A string array that contains the account name and initial balance
+ */
+ public static String[] parseAddAccount(String input) throws NumberFormatException, EmptyArgumentException {
+ String data = input.substring(ADD_ACC_COMMAND_INDEX + 1).trim();
+ String[] parseData = data.split("/");
+ String name = null;
+ String initialBalance = null;
+ for (int i = 0; i < parseData.length - 1; i++) {
+ if (parseData[i].trim().equals("n")) {
+ name = parseData[i + 1].trim();
+ } else if (parseData[i].trim().equals("$")) {
+ initialBalance = parseData[i + 1].trim();
+ }
+ }
+
+ if (name == null || name.isEmpty()) {
+ throw new EmptyArgumentException("name ");
+ }
+
+ if (initialBalance == null) {
+ throw new EmptyArgumentException("initial balance ");
+ }
+
+ if (TransactionList.isNotDouble(initialBalance)) {
+ throw new NumberFormatException(initialBalance);
+ }
+
+ return new String[]{name, initialBalance};
+ }
+
+ /**
+ * The `parseRemoveAccount` function in Java parses the input and returns the account
+ * number that is to be deleted
+ *
+ * @param input It contains details about account number that is to be deleted
+ * @return A integer value representing the account number
+ */
+ public static int parseRemoveAccount(String input)
+ throws NumberFormatException, EmptyArgumentException {
+ if (input.trim().length() < 11) {
+ throw new EmptyArgumentException("delete-acc index ");
+ }
+ String data = input.substring(11).trim();
+ if (TransactionList.isNotInteger(data)) {
+ throw new NumberFormatException(data);
+ }
+ return Integer.parseInt(data);
+ }
+
+ /**
+ * The `parseEditAccount` function in Java parses the input and returns the account
+ * number that is to be edited
+ *
+ * @param input It contains details about account number that is to be edited
+ * @return A integer value representing the account number
+ */
+ public static int parseEditAccount(String input) throws EmptyArgumentException {
+ if (input.trim().length() < 9) {
+ throw new EmptyArgumentException("edit-acc index ");
+ }
+ String data = input.substring(9).trim();
+ if (TransactionList.isNotInteger(data)) {
+ throw new NumberFormatException(data);
+ }
+ return Integer.parseInt(data);
+ }
+}
diff --git a/src/main/java/budgetbuddy/storage/DataStorage.java b/src/main/java/budgetbuddy/storage/DataStorage.java
new file mode 100644
index 0000000000..e181064bba
--- /dev/null
+++ b/src/main/java/budgetbuddy/storage/DataStorage.java
@@ -0,0 +1,392 @@
+package budgetbuddy.storage;
+
+import budgetbuddy.account.Account;
+import budgetbuddy.account.AccountManager;
+import budgetbuddy.categories.Category;
+import budgetbuddy.exceptions.FileCorruptedException;
+import budgetbuddy.exceptions.InvalidArgumentSyntaxException;
+import budgetbuddy.exceptions.InvalidCategoryException;
+import budgetbuddy.transaction.TransactionList;
+import budgetbuddy.transaction.type.Expense;
+import budgetbuddy.transaction.type.Income;
+import budgetbuddy.transaction.type.Transaction;
+import budgetbuddy.ui.UserInterface;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Scanner;
+import java.util.logging.Logger;
+import java.util.logging.Level;
+
+/**
+ * This class provides methods for storing and retrieving data related to transactions and accounts.
+ * It includes methods for saving and loading transactions and accounts to/from files.
+ */
+public class DataStorage {
+ public static final String TRANSACTIONS_FILE_PATH = "./data/transactions.txt";
+ public static final String ACCOUNTS_FILE_PATH = "./data/accounts.txt";
+ public static final String FOLDER_PATH = "./data";
+ public static final Logger LOGGER = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
+
+ //@@author ShyamKrishna33
+
+ /**
+ * Writes the provided string to a file at the given file path.
+ *
+ * @param stringToWrite The string to write to the file.
+ * @param filePath The path of the file to write to.
+ * @throws IOException If an I/O error occurs while writing to the file.
+ */
+ private static void writeToFile(String stringToWrite, String filePath) throws IOException {
+ FileWriter fw = new FileWriter(filePath, true);
+ fw.write(stringToWrite);
+ fw.close();
+ }
+
+ private static String getStringToWrite(Transaction t) {
+ LocalDate date = t.getDate();
+ String stringDate = date.format(DateTimeFormatter.ofPattern("dd-MM-yyyy"));
+ return t.getDescription() + " ," + t.getCategory().getCategoryNum() + " ,"
+ + t.getTransactionType() + " ," + stringDate + " ," + t.getAmount() + " ," + t.getAccountNumber()
+ + " ," + t.getAccountName() + "\n";
+ }
+
+ private static void createDataFolderIfNotExists() throws IOException {
+ Path dataFolderPath = Paths.get(FOLDER_PATH);
+ if (!Files.exists(dataFolderPath)) {
+ Files.createDirectories(dataFolderPath);
+ }
+ }
+ //@@author
+
+ /**
+ * Saves the list of accounts to the file in the ACCOUNT_FILE_PATH.
+ *
+ * @param accounts The list of accounts to save.
+ */
+ public void saveAccounts(ArrayList accounts) {
+ LOGGER.log(Level.INFO, "Saving accounts to file");
+ try {
+ File f = new File(ACCOUNTS_FILE_PATH);
+ if (!f.exists()) {
+ LOGGER.log(Level.WARNING, "File does not exist. Creating new file.");
+ createDataFolderIfNotExists();
+ if (!f.createNewFile()) {
+ LOGGER.log(Level.SEVERE, "Failed to create file");
+ throw new IOException("Failed to create file");
+ }
+ }
+ FileWriter fw = new FileWriter(ACCOUNTS_FILE_PATH, false);
+ for (Account account : accounts) {
+ String stringToWrite = account.getAccountNumber() + " ," + account.getName() + " ,"
+ + account.getBalance() + "\n";
+ writeToFile(stringToWrite, ACCOUNTS_FILE_PATH);
+ }
+ fw.close();
+ } catch (IOException e) {
+ System.out.println("Error saving accounts.");
+ LOGGER.log(Level.SEVERE, "Error saving accounts");
+ }
+ LOGGER.log(Level.INFO, "Accounts saved to file");
+ }
+
+ //@@author ShyamKrishna33
+
+ /**
+ * Saves the list of transactions to a file.
+ *
+ * @param transactionArrayList The list of transactions to save.
+ * @throws IOException If an I/O error occurs while saving the transactions.
+ */
+ public void saveTransactions(ArrayList transactionArrayList) throws IOException {
+ LOGGER.log(Level.INFO, "Saving transactions to file");
+ File f = new File(TRANSACTIONS_FILE_PATH);
+
+ assert f.exists() : "File does not exist";
+ FileWriter fw = new FileWriter(TRANSACTIONS_FILE_PATH, false);
+
+ for (Transaction transaction : transactionArrayList) {
+ if (transaction == null) {
+ break;
+ }
+ String stringToWrite = getStringToWrite(transaction);
+ writeToFile(stringToWrite, TRANSACTIONS_FILE_PATH);
+ }
+ LOGGER.log(Level.INFO, "Transactions saved to file");
+ }
+
+ /**
+ * Parses a string representing transaction data into a Transaction object.
+ *
+ * @param s The string representing the transaction data.
+ * @param existingAccountNumbers A list of existing account numbers.
+ * @return The parsed Transaction object.
+ * @throws FileCorruptedException If the file containing transaction data is corrupted.
+ * @throws InvalidCategoryException If the category specified in the transaction data is invalid.
+ */
+ private Transaction parseDataToTransaction(String s, ArrayList existingAccountNumbers)
+ throws FileCorruptedException, InvalidCategoryException {
+ String[] transactionInfo = s.split(" ,");
+ int categoryNum;
+ try {
+ categoryNum = Integer.parseInt(transactionInfo[1]);
+ } catch (NumberFormatException e) {
+ throw new FileCorruptedException("Invalid type for category number");
+ }
+
+ if (categoryNum < 1 || categoryNum > 9) {
+ throw new FileCorruptedException("Invalid category number");
+ }
+
+ if (transactionInfo.length != 7) {
+ throw new FileCorruptedException("Invalid transaction information format");
+ }
+
+ if (!transactionInfo[2].equals("Income") && !transactionInfo[2].equals("Expense")) {
+ throw new FileCorruptedException("Invalid transaction type");
+ }
+
+ double amount;
+
+ try {
+ amount = Double.parseDouble(transactionInfo[4]);
+ } catch (NumberFormatException e) {
+ throw new FileCorruptedException("Invalid type for transaction amount");
+ }
+
+ if (!existingAccountNumbers.contains(Integer.parseInt(transactionInfo[5]))) {
+ throw new FileCorruptedException("Invalid account number");
+ }
+
+ switch (transactionInfo[2]) {
+ case "Income":
+ Income incomeObj = new Income(Integer.parseInt(transactionInfo[5]), transactionInfo[6], transactionInfo[0],
+ amount, transactionInfo[3]);
+ incomeObj.setCategory(Category.fromNumber(categoryNum));
+ return incomeObj;
+ case "Expense":
+ Expense expenseObj = new Expense(Integer.parseInt(transactionInfo[5]), transactionInfo[6],
+ transactionInfo[0], -amount, transactionInfo[3]);
+ expenseObj.setCategory(Category.fromNumber(categoryNum));
+ return expenseObj;
+ default:
+ return null;
+ }
+ }
+ //@@author
+
+ /**
+ * Reads account data from a file and returns a list of Account objects.
+ *
+ * @param existingAccountNumbers A list of existing account numbers.
+ * @return The list of Account objects read from the file.
+ * @throws IOException If an I/O error occurs while reading the file.
+ * @throws FileCorruptedException If the file containing account data is corrupted.
+ */
+ public ArrayList readAccountFile(ArrayList existingAccountNumbers)
+ throws IOException, FileCorruptedException {
+ LOGGER.log(Level.INFO, "Reading accounts from file");
+ File f = new File(ACCOUNTS_FILE_PATH);
+ Scanner s = new Scanner(f);
+
+ ArrayList accounts = new ArrayList<>();
+ while (s.hasNext()) {
+ String line = s.nextLine();
+ if (line.trim().isEmpty()) {
+ continue;
+ }
+ accounts.add(processAccountLine(line, existingAccountNumbers));
+ }
+ LOGGER.log(Level.INFO, "Accounts read from file");
+ return accounts;
+ }
+
+ /**
+ * Reads account data from a file and returns a list of Account objects.
+ *
+ * @param existingAccountNumbers A list of existing account numbers.
+ * @return The list of Account objects read from the file.
+ * @throws FileCorruptedException If the file containing account data is corrupted.
+ */
+ private Account processAccountLine(String line, ArrayList existingAccountNumbers)
+ throws FileCorruptedException {
+ LOGGER.log(Level.INFO, "Processing account line");
+ String[] accountInfo = line.split(" ,");
+ validateAccountInfo(accountInfo, existingAccountNumbers);
+ LOGGER.log(Level.INFO, "Account line processed");
+
+ int accountNumber = Integer.parseInt(accountInfo[0]);
+ double balance = Double.parseDouble(accountInfo[2]);
+ String accountName = accountInfo[1].trim();
+
+ existingAccountNumbers.add(accountNumber);
+ LOGGER.log(Level.INFO, "Account added to existing account numbers list");
+ LOGGER.log(Level.INFO, "Account created");
+ return new Account(accountNumber, accountName, balance);
+ }
+
+ /**
+ * Validates a line of account data.
+ *
+ * @param accountInfo The line of account data to validate.
+ * @param existingAccountNumbers A list of existing account numbers.
+ * @throws FileCorruptedException If the line of account data is invalid.
+ */
+ private void validateAccountInfo(String[] accountInfo, ArrayList existingAccountNumbers)
+ throws FileCorruptedException {
+ if (accountInfo.length != 3) {
+ LOGGER.log(Level.SEVERE, "Invalid account information format");
+ throw new FileCorruptedException("Invalid account information format");
+ }
+
+ try {
+ int accountNumber = Integer.parseInt(accountInfo[0]);
+ if (accountNumber < 1000 || accountNumber > 9999) {
+ LOGGER.log(Level.SEVERE, "Invalid account number");
+ throw new FileCorruptedException("Invalid account number");
+ }
+ if (existingAccountNumbers.contains(accountNumber)) {
+ LOGGER.log(Level.SEVERE, "Duplicate account number");
+ throw new FileCorruptedException("Duplicate account number");
+ }
+ } catch (NumberFormatException e) {
+ LOGGER.log(Level.SEVERE, "Invalid type for account number");
+ throw new FileCorruptedException("Invalid type for account number");
+ }
+
+ try {
+ double balance = Double.parseDouble(accountInfo[2]);
+ } catch (NumberFormatException e) {
+ LOGGER.log(Level.SEVERE, "Invalid type for account balance");
+ throw new FileCorruptedException("Invalid type for account balance");
+ }
+
+ String accountName = accountInfo[1].trim();
+ if (accountName.isEmpty()) {
+ LOGGER.log(Level.SEVERE, "Invalid account name");
+ throw new FileCorruptedException("Invalid account name");
+ }
+ }
+
+ //@@author ShyamKrishna33
+ /**
+ * Reads transaction data from the transactions file and returns a list of Transaction objects.
+ *
+ * @param existingAccountNumbers A list of existing account numbers.
+ * @return The list of Transaction objects read from the file.
+ * @throws IOException If an I/O error occurs while reading the file.
+ */
+ public ArrayList readTransactionFile(ArrayList existingAccountNumbers) throws IOException {
+ LOGGER.log(Level.INFO, "Fetching transactions from storage");
+ createDataFolderIfNotExists();
+ File f = new File(TRANSACTIONS_FILE_PATH);
+ if (!f.exists()) {
+ LOGGER.log(Level.INFO, "File does not exists. Creating a new one.");
+ if (!f.createNewFile()) {
+ throw new IOException("Failed to create file");
+ }
+ }
+
+ assert f.exists() : "File does not exist";
+
+ Scanner s = new Scanner(f);
+ ArrayList transactionList = new ArrayList<>();
+ try {
+ while (s.hasNext()) {
+ String line = s.nextLine();
+ if (line.trim().isEmpty()) {
+ continue;
+ }
+ transactionList.add(parseDataToTransaction(line, existingAccountNumbers));
+ }
+ } catch (FileCorruptedException | InvalidCategoryException e) {
+ LOGGER.log(Level.SEVERE, "File got corrupted");
+ UserInterface.printFileCorruptedError();
+ FileWriter fw = new FileWriter(TRANSACTIONS_FILE_PATH, false);
+ return new ArrayList<>();
+ }
+ LOGGER.log(Level.INFO, "Transactions are fetched successfully");
+ return transactionList;
+ }
+ //@@author
+
+ /**
+ * Loads the accounts from the accounts file and returns an AccountManager object.
+ *
+ * @return The loaded AccountManager object.
+ */
+ public AccountManager loadAccounts() {
+ LOGGER.log(Level.INFO, "Loading accounts from file");
+ try {
+ File f = new File(ACCOUNTS_FILE_PATH);
+ if (!f.exists()) {
+ LOGGER.log(Level.WARNING, "File does not exist. Creating new file.");
+ createDataFolderIfNotExists();
+ if (!f.createNewFile()) {
+ LOGGER.log(Level.SEVERE, "Failed to create file");
+ throw new IOException("Failed to create file");
+ }
+ return createNewAccountManager();
+ }
+ ArrayList existingAccountNumbers = new ArrayList<>();
+ ArrayList accounts = null;
+ try {
+ accounts = readAccountFile(existingAccountNumbers);
+ } catch (FileCorruptedException e) {
+ LOGGER.log(Level.SEVERE, "File corrupted");
+ UserInterface.printFileCorruptedError();
+ FileWriter fw = new FileWriter(ACCOUNTS_FILE_PATH, false);
+ LOGGER.log(Level.WARNING, "Creating new account manager");
+ return createNewAccountManager();
+ }
+ if (accounts.isEmpty()) {
+ LOGGER.log(Level.WARNING, "Creating new account manager");
+ return createNewAccountManager();
+ }
+ return new AccountManager(accounts, existingAccountNumbers);
+ } catch (IOException e) {
+ LOGGER.log(Level.SEVERE, "Error loading accounts");
+ UserInterface.printFileCorruptedError();
+ LOGGER.log(Level.WARNING, "Creating new account manager");
+ return createNewAccountManager();
+ }
+ }
+
+ private AccountManager createNewAccountManager() {
+ String accountName = null;
+ try {
+ accountName = UserInterface.getInitialAccountName();
+ } catch (InvalidArgumentSyntaxException e) {
+ UserInterface.printInvalidArgumentSyntax(e.getMessage());
+ return createNewAccountManager();
+ }
+ Double initialBalance = UserInterface.getInitialAccountBalance();
+ AccountManager accountManager = new AccountManager();
+ accountManager.addAccount(accountName, initialBalance);
+ return accountManager;
+ }
+
+ //@@author ShyamKrishna33
+ /**
+ * Loads the transactions from the transactions file and returns a TransactionList object.
+ *
+ * @param existingAccountNumbers A list of existing account numbers.
+ * @return The loaded TransactionList object.
+ */
+ public TransactionList loadTransactions(ArrayList existingAccountNumbers) {
+ try {
+ ArrayList transactions = readTransactionFile(existingAccountNumbers);
+ return new TransactionList(transactions);
+ } catch (IOException e) {
+ return new TransactionList();
+ }
+ }
+ //@@author
+}
diff --git a/src/main/java/budgetbuddy/transaction/TransactionList.java b/src/main/java/budgetbuddy/transaction/TransactionList.java
new file mode 100644
index 0000000000..9da18b25df
--- /dev/null
+++ b/src/main/java/budgetbuddy/transaction/TransactionList.java
@@ -0,0 +1,464 @@
+package budgetbuddy.transaction;
+
+import budgetbuddy.account.Account;
+
+import budgetbuddy.account.AccountManager;
+
+
+import budgetbuddy.categories.Category;
+import budgetbuddy.exceptions.EmptyArgumentException;
+import budgetbuddy.exceptions.InvalidAddTransactionSyntax;
+import budgetbuddy.exceptions.InvalidArgumentSyntaxException;
+import budgetbuddy.exceptions.InvalidCategoryException;
+import budgetbuddy.exceptions.InvalidEditTransactionData;
+import budgetbuddy.exceptions.InvalidIndexException;
+import budgetbuddy.exceptions.InvalidTransactionTypeException;
+import budgetbuddy.insights.Insight;
+import budgetbuddy.parser.Parser;
+import budgetbuddy.storage.DataStorage;
+import budgetbuddy.transaction.type.Transaction;
+import budgetbuddy.ui.UserInterface;
+
+import java.io.IOException;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Represents a list of transactions and provides methods for managing them.
+ */
+public class TransactionList {
+
+ public static final int DELETE_BEGIN_INDEX = 7;
+ public static final int INDEX_OFFSET = 1;
+ public static final int LOWER_BOUND = 0;
+ public static final int EDIT_BEGIN_INDEX = 5;
+ public static final Logger LOGGER = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
+
+ public static final String ACCOUNT = "acc";
+ public static final String ALL = "all";
+ public static final String ADD = "add";
+ public static final String DELETE = "delete";
+ public static final String EDIT = "edit";
+ public static final String LIST = "list";
+ public static final String SEARCH = "search";
+ private static final int DAYS_IN_WEEK = 7;
+ private static final int DAYS_IN_MONTH = 30;
+ private static final int DAYS_OFFSET = 1;
+
+ private final ArrayList transactions;
+ private final Parser parser;
+ private final DataStorage dataStorage = new DataStorage();
+
+ /**
+ * Constructs a new TransactionList object with an empty list of transactions and a parser.
+ */
+ public TransactionList() {
+ this.transactions = new ArrayList<>();
+ this.parser = new Parser();
+ LOGGER.log(Level.INFO, "TransactionList created with empty transactions and parser");
+
+ }
+
+ /**
+ * Constructs a new TransactionList object with the specified list of transactions and a parser.
+ *
+ * @param transactions The list of transactions.
+ */
+ public TransactionList(ArrayList transactions) {
+ this.transactions = transactions;
+ this.parser = new Parser();
+ LOGGER.log(Level.INFO, "TransactionList created with transactions and parser");
+
+ }
+
+ public ArrayList getTransactions() {
+ return transactions;
+ }
+
+ public void printTransactions() {
+ UserInterface.printAllTransactions(transactions);
+ }
+
+ /**
+ * Removes a transaction from the list based on the user input.
+ *
+ * @param input The user input specifying the transaction to be removed.
+ * @param accountManager The account manager for retrieving account information.
+ * @throws EmptyArgumentException If the input string is empty or does not contain the required parameter.
+ * @throws NumberFormatException If the index parsed from the input string is not a valid integer.
+ * @throws InvalidIndexException If the index is out of bounds or invalid.
+ */
+ public void removeTransaction(String input, AccountManager accountManager) throws EmptyArgumentException,
+ NumberFormatException, InvalidIndexException {
+ if (input.trim().length() < DELETE_BEGIN_INDEX) {
+ LOGGER.log(Level.WARNING, "Index id is not given for delete command");
+ throw new EmptyArgumentException("delete index");
+ }
+ String data = input.substring(DELETE_BEGIN_INDEX).trim();
+ int id = Integer.parseInt(data) - INDEX_OFFSET;
+ int size = transactions.size();
+ if (id >= LOWER_BOUND && id < size) {
+ String itemRemoved = transactions.get(id).toString();
+ Account account = accountManager.getAccountByAccountNumber(transactions.get(id).getAccountNumber());
+ assert itemRemoved != null : "String representation of item to remove is null";
+ account.setBalance(account.getBalance() - transactions.get(id).getAmount());
+ transactions.remove(id);
+ assert transactions.size() == size - 1 : "Transaction list size did not decrease after removal";
+ UserInterface.printDeleteMessage(itemRemoved, account.getBalance());
+ LOGGER.log(Level.INFO, "Transaction is removed successfully");
+
+ } else {
+ LOGGER.log(Level.WARNING, "Invalid index for delete command");
+ throw new InvalidIndexException(String.valueOf(size));
+ }
+ }
+
+ public static boolean isNotInteger(String data) {
+ try {
+ Integer.parseInt(data);
+ return false;
+ } catch (NumberFormatException e) {
+ return true;
+ }
+ }
+
+ public static boolean isNotDouble(String data) {
+ try {
+ Double.parseDouble(data);
+ return false;
+ } catch (NumberFormatException e) {
+ return true;
+ }
+ }
+
+ void addTransaction(Transaction t) {
+ transactions.add(t);
+ }
+
+ /**
+ * Processes a transaction based on the user input and adds it to the transaction list.
+ *
+ * @param input The user input specifying the transaction details.
+ * @param account The account associated with the transaction.
+ * @throws InvalidTransactionTypeException If the transaction type is invalid.
+ * @throws InvalidAddTransactionSyntax If the syntax for adding a transaction is invalid.
+ * @throws EmptyArgumentException If the input string is empty or missing required arguments.
+ * @throws InvalidCategoryException If the category specified in the transaction is invalid.
+ */
+ public void processTransaction(String input, Account account)
+ throws InvalidTransactionTypeException, InvalidAddTransactionSyntax, EmptyArgumentException,
+ InvalidCategoryException {
+ // Check for syntax for add transaction
+ String[] arguments = {"/a/", "/t/", "/n/", "/$/", "/d/"};
+ for (String argument : arguments) {
+ if (!input.contains(argument)) {
+ LOGGER.log(Level.WARNING, "Invalid add transaction syntax");
+ throw new InvalidAddTransactionSyntax("Invalid add syntax.");
+ }
+ }
+
+ Transaction t = parser.parseUserInputToTransaction(input, account);
+ assert t != null : "Parsed transaction is null";
+ addTransaction(t);
+ assert transactions.get(transactions.size() - 1) != null : "Added transaction is null after adding to the list";
+ String fetchData = String.valueOf(transactions.get(transactions.size() - 1));
+ UserInterface.printAddMessage(fetchData, account.getBalance());
+ LOGGER.log(Level.INFO, "Transaction added successfully");
+
+ }
+
+ /**
+ * Saves the current list of transactions to a data storage.
+ *
+ * @throws IOException If an I/O error occurs while saving the transactions.
+ */
+ public void saveTransactionList() throws IOException {
+ dataStorage.saveTransactions(transactions);
+ }
+
+ /**
+ * Retrieves past transactions based on the specified duration.
+ *
+ * @param transactions The list of transactions to filter.
+ * @param duration The duration of past transactions to retrieve ("week" or "month").
+ * @return An ArrayList containing past transactions based on the specified duration.
+ */
+ //@@author isaaceng7
+ public static ArrayList getPastTransactions(ArrayList transactions, String duration) {
+ LocalDate today = LocalDate.now();
+ LocalDate startDate = null;
+ switch (duration) {
+ case "week":
+ startDate = today.minusDays(DAYS_IN_WEEK + DAYS_OFFSET);
+ break;
+ case "month":
+ startDate = today.minusDays(DAYS_IN_MONTH + DAYS_OFFSET);
+ break;
+ default:
+ break;
+ }
+ ArrayList pastTransactions = new ArrayList<>();
+ for (Transaction transaction : transactions) {
+ if (transaction.getDate().isAfter(startDate)) {
+ pastTransactions.add(transaction);
+ }
+ }
+ LOGGER.log(Level.INFO, "Past transactions loaded successfully");
+ return pastTransactions;
+ }
+
+ /**
+ * Retrieves transactions within a custom date range.
+ *
+ * @param transactions The list of transactions to filter.
+ * @return An ArrayList containing transactions within the specified custom date range.
+ */
+ public static ArrayList getCustomDateTransactions(ArrayList transactions) {
+ String start = UserInterface.getStartDate();
+ String end = UserInterface.getEndDate();
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy");
+ LocalDate startDate = LocalDate.parse(start, formatter).minusDays(DAYS_OFFSET);
+ LocalDate endDate = LocalDate.parse(end, formatter).plusDays(DAYS_OFFSET);
+ ArrayList customDateTransactions = new ArrayList<>();
+ for (Transaction transaction : transactions) {
+ if (transaction.getDate().isAfter(startDate) && transaction.getDate().isBefore(endDate)) {
+ customDateTransactions.add(transaction);
+ }
+ }
+ LOGGER.log(Level.INFO, "Custom date transactions loaded successfully");
+
+ return customDateTransactions;
+ }
+
+ /**
+ * Retrieves transactions associated with a specific account number.
+ *
+ * @param transactions The list of transactions to filter.
+ * @param accountNumber The account number for which transactions are to be retrieved.
+ * @return An ArrayList containing transactions associated with the specified account number.
+ */
+ public static ArrayList getAccountTransactions(ArrayList transactions,
+ int accountNumber) {
+ ArrayList accountTransactions = new ArrayList<>();
+ for (Transaction transaction : transactions) {
+ if (transaction.getAccountNumber() == accountNumber) {
+ accountTransactions.add(transaction);
+ }
+ }
+ LOGGER.log(Level.INFO, "Transaction based on account loaded successfully");
+ return accountTransactions;
+ }
+
+ /**
+ * Retrieves transactions associated with a specific category.
+ *
+ * @param transactions The list of transactions to filter.
+ * @param category The category for which transactions are to be retrieved.
+ * @return An ArrayList containing transactions associated with the specified category.
+ */
+ public static ArrayList getCategoryTransactions(ArrayList transactions,
+ Category category) {
+ ArrayList categoryTransactions = new ArrayList<>();
+ for (Transaction transaction : transactions) {
+ if (transaction.getCategory() == category) {
+ categoryTransactions.add(transaction);
+ }
+ }
+ LOGGER.log(Level.INFO, "Transactions based on a category loaded successfully");
+
+ return categoryTransactions;
+ }
+
+ /**
+ * Processes the user-selected list option to list a specific set of transactions
+ * and perform the corresponding action.
+ *
+ * @param accounts The list of accounts.
+ * @param accountManager The account manager for retrieving account information.
+ * @throws InvalidIndexException If the selected option index is invalid.
+ * @throws InvalidCategoryException If the selected category is invalid.
+ */
+ public void processList(ArrayList accounts, AccountManager accountManager) throws InvalidIndexException,
+ InvalidCategoryException {
+ UserInterface.printListOptions();
+ String data = UserInterface.getListOption().trim();
+ int option = Integer.parseInt(data);
+ switch (option) {
+ // 1 - ALL TRANSACTIONS
+ case 1:
+ printTransactions();
+ break;
+ // 2 - PAST WEEK TRANSACTIONS
+ case 2:
+ ArrayList pastWeekTransactions = getPastTransactions(transactions, "week");
+ UserInterface.printPastTransactions(pastWeekTransactions, "week");
+ break;
+ // 3 - PAST MONTH TRANSACTIONS
+ case 3:
+ ArrayList pastMonthTransactions = getPastTransactions(transactions, "month");
+ UserInterface.printPastTransactions(pastMonthTransactions, "month");
+ break;
+ // 4 - CUSTOM DATE TRANSACTIONS
+ case 4:
+ ArrayList customDateTransactions = getCustomDateTransactions(transactions);
+ UserInterface.printCustomDateTransactions(customDateTransactions);
+ break;
+ // 5 - ACCOUNT TRANSACTIONS
+ case 5:
+ String accountData = UserInterface.getSelectedAccountNumber(accounts);
+ int accountNumber = Integer.parseInt(accountData);
+ Account account = accountManager.getAccountByAccountNumber(accountNumber);
+ String accountName = account.getName();
+ ArrayList accountTransactions = getAccountTransactions(transactions, accountNumber);
+ UserInterface.printAccountTransactions(accountTransactions, accountName, accountNumber);
+ break;
+ // 6 - CATEGORY TRANSACTIONS
+ case 6:
+ UserInterface.listCategories();
+ int input = UserInterface.getSelectedCategory();
+ Category categorySelected = Category.fromNumber(input);
+ String categoryName = categorySelected.getCategoryName();
+ ArrayList categoryTransactions = getCategoryTransactions(transactions, categorySelected);
+ UserInterface.printCategoryTransactions(categoryTransactions, categoryName);
+ break;
+ default:
+ LOGGER.log(Level.WARNING, "Invalid index for 'list' command");
+ throw new InvalidIndexException("6");
+ }
+
+ }
+
+ /**
+ * Processes the user input for editing a transaction and updates the transaction accordingly.
+ *
+ * @param input The user input specifying the transaction index to be edited.
+ * @param accountManager The account manager for retrieving account information.
+ * @throws EmptyArgumentException If the input string is empty or missing required arguments.
+ * @throws NumberFormatException If the index parsed from the input string is not a valid integer.
+ * @throws InvalidIndexException If the index is out of bounds or invalid.
+ * @throws InvalidEditTransactionData If the data for editing the transaction is invalid.
+ * @throws InvalidCategoryException If the specified category is invalid.
+ */
+ //@@author Vavinan
+ public void processEditTransaction(String input, AccountManager accountManager) throws EmptyArgumentException,
+ NumberFormatException, InvalidIndexException, InvalidEditTransactionData, InvalidCategoryException,
+ InvalidArgumentSyntaxException {
+ if (input.trim().length() < EDIT_BEGIN_INDEX) {
+ LOGGER.log(Level.WARNING, "Index id is missing for edit command");
+ throw new EmptyArgumentException("edit index ");
+ }
+ String data = input.substring(EDIT_BEGIN_INDEX).trim();
+
+ if (isNotInteger(data)) {
+ LOGGER.log(Level.WARNING, "Given index id for 'edit' command is not an integer");
+ throw new NumberFormatException(data);
+ }
+ int index = Integer.parseInt(data) - INDEX_OFFSET;
+ if ((index >= LOWER_BOUND) && (index < transactions.size())) {
+ Transaction transaction = transactions.get(index);
+ Account account = accountManager.getAccountByAccountNumber(transaction.getAccountNumber());
+ String newTransaction = UserInterface.getEditInformation(transaction.toString());
+ Transaction t = parser.parseEditTransaction(newTransaction, account);
+ transactions.set(index, t);
+ UserInterface.printUpdatedTransaction(t);
+ LOGGER.log(Level.INFO, "Transaction is edited successfully");
+
+ } else {
+ LOGGER.log(Level.WARNING, "Given index id for 'edit' command is not valid");
+ throw new InvalidIndexException(String.valueOf(transactions.size()));
+ }
+ }
+
+ /**
+ * Provides help messages for user commands.
+ *
+ * @param input The user input specifying the command for which help is requested.
+ */
+ public void helpWithUserCommands(String input) {
+ String helpCommand = parser.parseHelpCommand(input);
+ switch (helpCommand.toLowerCase()) {
+ case ALL:
+ UserInterface.printAllCommands();
+ break;
+ case ADD:
+ UserInterface.printAddHelp();
+ break;
+ case DELETE:
+ UserInterface.printDeleteHelp();
+ break;
+ case EDIT:
+ UserInterface.printEditHelp();
+ break;
+ case LIST:
+ UserInterface.printListHelp();
+ break;
+ case ACCOUNT:
+ UserInterface.printAccountHelp();
+ break;
+
+ case SEARCH:
+ UserInterface.printSearchHelp();
+ break;
+ default:
+ UserInterface.printUseAvailableHelp();
+ break;
+ }
+ }
+ //@@author
+
+ /**
+ * Displays insights based on transaction categories.
+ */
+ //@@author ShyamKrishna33
+ public void displayInsights() {
+ Insight.displayCategoryInsight(transactions);
+ }
+
+ public ArrayList removeTransactionsByAccountNumber(int accountNumber) {
+ ArrayList transactionsToRemove = new ArrayList<>();
+ for (Transaction transaction : transactions) {
+ if (transaction.getAccountNumber() == accountNumber) {
+ transactionsToRemove.add(transaction);
+ }
+ }
+ transactions.removeAll(transactionsToRemove);
+ LOGGER.log(Level.INFO, "Transactions were removed successfully from the specified account number");
+ return transactionsToRemove;
+ }
+
+ /**
+ * Searches transactions based on a keyword and prints search results.
+ *
+ * @param input The user input specifying the keyword to search for transactions.
+ */
+ public void searchTransactions(String input) {
+ try {
+ String keyword = input.split(" ")[1];
+ ArrayList searchResults = new ArrayList<>();
+ ArrayList indices = new ArrayList<>();
+ int index = 0;
+ for (Transaction transaction : transactions) {
+ if (transaction.getDescription().toLowerCase().contains(keyword.toLowerCase()) ||
+ String.valueOf(transaction.getAmount()).contains(keyword) ||
+ transaction.getCategory().getCategoryName().toLowerCase()
+ .contains(keyword.toLowerCase()) ||
+ transaction.getDate().toString().contains(keyword)) {
+ searchResults.add(transaction);
+ indices.add(index);
+ }
+ index++;
+ }
+ LOGGER.log(Level.INFO, "Transactions are filtered out for 'search' command");
+ UserInterface.printSearchResults(searchResults, indices);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ LOGGER.log(Level.WARNING, "Keyword is not provided for search command");
+ UserInterface.printInvalidInput("Please enter a keyword to search for transactions.");
+ } catch (Exception e) {
+ LOGGER.log(Level.WARNING, "Search command failed");
+ UserInterface.printExceptionErrorMessage(e.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/budgetbuddy/transaction/type/Expense.java b/src/main/java/budgetbuddy/transaction/type/Expense.java
new file mode 100644
index 0000000000..400a09553c
--- /dev/null
+++ b/src/main/java/budgetbuddy/transaction/type/Expense.java
@@ -0,0 +1,59 @@
+package budgetbuddy.transaction.type;
+
+import budgetbuddy.account.Account;
+
+/**
+ * Represents an expense transaction in the budget buddy system.
+ * An expense transaction decreases the balance of an account.
+ */
+public class Expense extends Transaction {
+ private static final String TRANSACTION_TYPE = "Expense";
+
+ /**
+ * Creates an expense transaction with the given account number, account name, description, amount, date,
+ * and account.
+ * The amount is automatically negated to represent an expense.
+ * The balance of the account is decreased by the amount of the expense.
+ *
+ * @param accountNumber the account number
+ * @param accountName the name of the account
+ * @param description the description of the expense
+ * @param amount the amount of the expense
+ * @param date the date of the expense
+ * @param account the account the expense is made from
+ */
+ public Expense(int accountNumber, String accountName, String description, double amount, String date,
+ Account account) {
+ super(accountNumber, accountName, description, -amount, date);
+ assert this.getAmount() < 0 : "Expense amount must be positive";
+ assert description != null && !description.isEmpty() : "Description cannot be null or empty";
+ assert date != null : "Date cannot be null";
+ assert account != null : "Account cannot be null";
+
+ account.setBalance(account.getBalance() + this.getAmount());
+ }
+
+ /**
+ * Creates an expense transaction with the given account number, account name, description, amount, and date.
+ * The amount is automatically negated to represent an expense.
+ *
+ * @param accountNumber the account number
+ * @param accountName the name of the account
+ * @param description the description of the expense
+ * @param amount the amount of the expense
+ * @param date the date of the expense
+ */
+ public Expense(int accountNumber, String accountName, String description, double amount, String date) {
+ super(accountNumber, accountName, description, -amount, date);
+ }
+
+ /**
+ * Returns the type of the transaction.
+ *
+ * @return the type of the transaction
+ */
+ @Override
+ public String getTransactionType() {
+ return TRANSACTION_TYPE;
+ }
+}
diff --git a/src/main/java/budgetbuddy/transaction/type/Income.java b/src/main/java/budgetbuddy/transaction/type/Income.java
new file mode 100644
index 0000000000..9430bfd825
--- /dev/null
+++ b/src/main/java/budgetbuddy/transaction/type/Income.java
@@ -0,0 +1,59 @@
+package budgetbuddy.transaction.type;
+
+import budgetbuddy.account.Account;
+
+/**
+ * Represents an income transaction in the budget buddy system.
+ * An income transaction increases the balance of an account.
+ */
+public class Income extends Transaction {
+ private static final String TRANSACTION_TYPE = "Income";
+
+ //@@author vibes-863
+
+ /**
+ * Creates an income transaction with the given account number, account name, description, amount, date,
+ * and account.
+ * The balance of the account is increased by the amount of the income.
+ *
+ * @param accountNumber the account number
+ * @param accountName the name of the account
+ * @param description the description of the income
+ * @param amount the amount of the income
+ * @param date the date of the income
+ * @param account the account the income is made to
+ */
+ public Income(int accountNumber, String accountName, String description, double amount, String date,
+ Account account) {
+ super(accountNumber, accountName, description, amount, date);
+ assert this.getAmount() > 0 : "Income amount must be positive";
+ assert description != null && !description.isEmpty() : "Description cannot be null or empty";
+ assert date != null : "Date cannot be null";
+ assert account != null : "Account cannot be null";
+
+ account.setBalance(account.getBalance() + this.getAmount());
+ }
+
+ /**
+ * Creates an income transaction with the given account number, account name, description, amount, and date.
+ *
+ * @param accountNumber the account number
+ * @param accountName the name of the account
+ * @param description the description of the income
+ * @param amount the amount of the income
+ * @param date the date of the income
+ */
+ public Income(int accountNumber, String accountName, String description, double amount, String date) {
+ super(accountNumber, accountName, description, amount, date);
+ }
+
+ /**
+ * Returns the type of the transaction.
+ *
+ * @return the type of the transaction
+ */
+ @Override
+ public String getTransactionType() {
+ return TRANSACTION_TYPE;
+ }
+}
diff --git a/src/main/java/budgetbuddy/transaction/type/Transaction.java b/src/main/java/budgetbuddy/transaction/type/Transaction.java
new file mode 100644
index 0000000000..8b80ebf0bb
--- /dev/null
+++ b/src/main/java/budgetbuddy/transaction/type/Transaction.java
@@ -0,0 +1,101 @@
+package budgetbuddy.transaction.type;
+
+import budgetbuddy.categories.Category;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * Represents a transaction involving a financial account.
+ * This is an abstract class and must be subclassed to provide specific transaction types.
+ */
+public abstract class Transaction {
+ // The account number associated with the transaction
+ private final int accountNumber;
+
+ // The name of the account associated with the transaction
+ private final String accountName;
+
+ // A brief description of the transaction
+ private final String description;
+
+ // The amount of money involved in the transaction
+ private final double amount;
+
+ // The category of the transaction
+ private Category category;
+
+ // The date of the transaction
+ private final LocalDate date;
+
+ /**
+ * Constructs a new Transaction object with the specified parameters.
+ *
+ * @param accountNumber The account number associated with the transaction.
+ * @param accountName The name of the account associated with the transaction.
+ * @param description A brief description of the transaction.
+ * @param amount The amount of money involved in the transaction.
+ * @param date The date of the transaction in the format "dd-mm-yyyy".
+ */
+ public Transaction(int accountNumber, String accountName, String description, double amount, String date) {
+ this.accountNumber = accountNumber;
+ this.accountName = accountName;
+ this.description = description;
+ this.amount = amount;
+ this.date = parseDate(date);
+ }
+
+ public int getAccountNumber() {
+ return accountNumber;
+ }
+
+ public String getAccountName() {
+ return accountName;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public double getAmount() {
+ return amount;
+ }
+
+ public LocalDate getDate() {
+ return date;
+ }
+
+ /**
+ * Parses the date string into a LocalDate object.
+ *
+ * @param by The date string in the format "dd-MM-yyyy".
+ * @return The LocalDate object representing the date.
+ */
+ private LocalDate parseDate(String by) {
+ return LocalDate.parse(by, DateTimeFormatter.ofPattern("dd-MM-yyyy"));
+ }
+
+ public Category getCategory() {
+ return category;
+ }
+
+ public abstract String getTransactionType();
+
+ /**
+ * Returns a string representation of the transaction.
+ */
+ @Override
+ public String toString() {
+ return (" Account Number: " + getAccountNumber() + " | " +
+ " Account Name: " + getAccountName() + " | " +
+ " Transaction Type: " + getTransactionType() + " | " +
+ " Description: " + getDescription() + " | " +
+ " Date: " + getDate() + " | " +
+ " Amount: " + getAmount() + " | " +
+ " Category: " + getCategory().getCategoryName());
+ }
+
+ public void setCategory(Category category) {
+ this.category = category;
+ }
+}
diff --git a/src/main/java/budgetbuddy/ui/UserInterface.java b/src/main/java/budgetbuddy/ui/UserInterface.java
new file mode 100644
index 0000000000..9158dd135e
--- /dev/null
+++ b/src/main/java/budgetbuddy/ui/UserInterface.java
@@ -0,0 +1,1124 @@
+package budgetbuddy.ui;
+
+import budgetbuddy.account.Account;
+import budgetbuddy.categories.Category;
+import budgetbuddy.exceptions.InvalidArgumentSyntaxException;
+import budgetbuddy.transaction.type.Transaction;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.Scanner;
+
+/**
+ * Manages Overall interaction between users and the program.
+ * It handles all the input and output related tasks.
+ */
+public class UserInterface {
+
+ public static final int START_INDEX = 0;
+
+ public static final String HELP_BORDER = "```````````````````````````````````````````````````";
+
+ private static final String LINE = "----------------------------------------------------";
+ private static final String TABLE_BORDER = "____________________________________________________";
+ private static final String ACCOUNT_TABLE_BORDER = "____________________________________________________";
+
+ private static final String TAB_SPACE = " ";
+ public static Scanner in = new Scanner(System.in);
+
+ /**
+ * The function `listCategories` prints out the available categories along with
+ * their corresponding
+ * category names and numbers.
+ */
+ public static void listCategories() {
+ System.out.println(LINE);
+ System.out.println(TAB_SPACE + "Here are the available categories:");
+ for (Category category : Category.values()) {
+ System.out.println(TAB_SPACE + TAB_SPACE + category.getCategoryName() + ": " + category.getCategoryNum());
+ }
+ System.out.println(LINE);
+ }
+
+ /**
+ * The function prompts the user to enter a number between 1 and 9 to categorize
+ * a transaction and
+ * returns the input as an integer.
+ *
+ * @return The method `getCategoryNum()` returns an integer value representing
+ * the category number
+ * input by the user.
+ */
+ public static int getCategoryNum() {
+ System.out.println("In which category do you want to list this transaction? [Enter number between 1 and 9]");
+ String input = in.nextLine();
+ return Integer.parseInt(input);
+ }
+
+ /**
+ * The function `printDeleteMessage` takes a transaction string and balance as
+ * input, splits the
+ * transaction string, and prints a message confirming the deletion of the
+ * transaction along with
+ * the updated account balance.
+ *
+ * @param transaction The `transaction` parameter is a string that contains
+ * information about a
+ * transaction, with different parts separated by the "|"
+ * character.
+ * @param balance The `balance` parameter represents the updated account
+ */
+ public static void printDeleteMessage(String transaction, double balance) {
+ String[] parts = transaction.split("\\|");
+ System.out.println(LINE);
+ System.out.println(TAB_SPACE + "Got it. I have removed the following transaction from the history \n");
+ for (String part : parts) {
+ System.out.println(TAB_SPACE + part.trim());
+ }
+ System.out.println("\n" + TAB_SPACE + "Your updated account balance is $" + balance);
+ System.out.println(LINE);
+ }
+
+ //@@author Vavinan
+ /**
+ * The function `printAddMessage` takes a transaction string and balance amount,
+ * formats and prints
+ * the transaction details along with the updated balance.
+ *
+ * @param transaction The `transaction` parameter is a string that contains
+ * information about a transaction.
+ * @param balance The `balance` parameter in the `printAddMessage` method
+ * represents the updated
+ * account balance after a transaction has been added.
+ */
+ public static void printAddMessage(String transaction, double balance) {
+ String[] parts = transaction.split("\\|");
+ System.out.println(LINE);
+ System.out.println(TAB_SPACE + "Got it. I have added the following transaction \n");
+ for (String part : parts) {
+ System.out.println(TAB_SPACE + part.trim());
+ }
+ System.out.println("\n" + TAB_SPACE + "Your updated account balance is $" + balance);
+ System.out.println(LINE);
+ }
+
+ /**
+ * The function `printInvalidIndex` prints a message indicating an invalid index
+ * along with the
+ * valid index range.
+ *
+ * @param message The `message` parameter is a string that contains the error
+ * message to be
+ * displayed when an invalid index is provided.
+ * @param id The `id` parameter in the `printInvalidIndex` method
+ * represents the upper limit of the
+ * index range that is valid for the message being displayed.
+ */
+ public static void printInvalidIndex(String message, int id) {
+ System.out.println(LINE);
+ System.out.println(TAB_SPACE + message);
+ System.out.println(TAB_SPACE + "Please use index within the range of: 1 to " + id);
+ System.out.println(LINE);
+ }
+
+ /**
+ * The function `printExceptionErrorMessage` prints an error message with a
+ * given message and
+ * provides guidance on checking command syntax.
+ *
+ * @param message The `message` parameter is a string that represents the error
+ * message associated
+ * with the exception that occurred.
+ */
+ public static void printExceptionErrorMessage(String message) {
+ System.out.println(LINE);
+ System.out.println(TAB_SPACE + "An error occurred with the message: " + message);
+ System.out.println(TAB_SPACE + "Please check your command Syntax. \n" + TAB_SPACE +
+ " If you need assistance use `help` command to know further about each command syntax.");
+ System.out.println(LINE);
+ }
+
+ /**
+ * The function `printInvalidInput` prints an error message surrounded by a line
+ * to indicate
+ * invalid input.
+ *
+ * @param message The `message` parameter is a String that contains the error
+ * message to be displayed.
+ */
+ public static void printInvalidInput(String message) {
+ System.out.println(LINE);
+ System.out.println(TAB_SPACE + "Error occurred with message: " + message);
+ System.out.println(LINE);
+ }
+
+ /**
+ * The function `printInvalidAddSyntax` prints an error message along with a
+ * reminder to ensure
+ * correct argument entry.
+ *
+ * @param message The `message` parameter is a string that represents the error
+ * message to be displayed
+ * when the syntax for adding an item is invalid.
+ */
+ //@@author isaaceng7
+ public static void printInvalidAddSyntax(String message) {
+ System.out.println(LINE);
+ System.out.println(TAB_SPACE + message);
+ System.out.println(TAB_SPACE + "Please ensure that you have entered all the arguments correctly.");
+ System.out.println(LINE);
+ }
+
+ //@@author
+
+ /**
+ * The function `printTransactionTypeError` prints an error message for an
+ * invalid transaction type
+ * in Java.
+ *
+ * @param message The `message` parameter is a string that represents the
+ * specific error message related
+ * to an invalid transaction type.
+ */
+ //@@author isaaceng7
+ public static void printTransactionTypeError(String message) {
+ System.out.println(LINE);
+ System.out.println(TAB_SPACE + "Invalid transaction type: " + message);
+ System.out.println(TAB_SPACE + "Please enter Expense or Income only.");
+ System.out.println(LINE);
+ }
+
+ //@@author
+
+ /**
+ * The function `printNumberFormatError` prints an error message related to
+ * number format issues in
+ * Java.
+ *
+ * @param message The `message` parameter is a string that represents the error
+ * message
+ * related to a number format issue.
+ */
+ //@@author isaaceng7
+ public static void printNumberFormatError(String message) {
+ System.out.println(LINE);
+ System.out.println(TAB_SPACE + "Error occurred with the input: " + message);
+ System.out.println(TAB_SPACE + "Please enter a valid value.");
+ System.out.println(LINE);
+ }
+
+ //@@author
+
+ /**
+ * The function `printEmptyArgumentError` prints an error message reminding the
+ * user to include
+ * proper argument in a command.
+ *
+ * @param message The `message` parameter is used to specify the type of
+ * argument that is
+ * missing in the command.
+ */
+ //@@author isaaceng7
+ public static void printEmptyArgumentError(String message) {
+ System.out.println(LINE);
+ System.out.println(TAB_SPACE + "Please include the " + message + "in the command.");
+ System.out.println(LINE);
+ }
+
+ //@@author
+
+ /**
+ * The `printAllTransactions` function prints a formatted list of transaction
+ * details from an
+ * ArrayList of Transaction objects.
+ *
+ * @param transactions The `printAllTransactions` method takes an `ArrayList` of
+ * `Transaction`
+ * objects as a parameter. It then iterates over each
+ * `Transaction` object in the list and prints
+ * out the details of each transaction in a formatted
+ * table-like structure.
+ */
+ public static void printAllTransactions(ArrayList transactions) {
+ int index = transactions.size();
+ System.out.println(LINE);
+ System.out.println(TAB_SPACE + "Your Transaction history:");
+ System.out.println(TAB_SPACE + TABLE_BORDER);
+ System.out.printf(TAB_SPACE + TAB_SPACE + "%-5s %-10s %-20s %-20s %-30s %-15s %-15s %-15s%n", "ID", "Type",
+ "Account Number", "Account Name", "Transaction", "Date", "Amount", "Category");
+ for (int i = START_INDEX; i < index; i++) {
+ Transaction transaction = transactions.get(i);
+ int accountNumber = transaction.getAccountNumber();
+ String accountName = transaction.getAccountName();
+ String type = transaction.getTransactionType();
+ String description = transaction.getDescription();
+ LocalDate date = transaction.getDate();
+ double amount = transaction.getAmount();
+ String category = transaction.getCategory().getCategoryName();
+
+ System.out.printf(TAB_SPACE + TAB_SPACE + "%-5d %-10s %-20d %-20.45s %-30.45s %-15s %-15.2f %-15s%n",
+ i + 1, type, accountNumber, accountName, description, date, amount, category);
+ }
+ System.out.println(TAB_SPACE + TABLE_BORDER);
+ System.out.println(LINE);
+ }
+
+ /**
+ * The function `printGoodBye` prints a farewell message with a reminder to keep
+ * track of future
+ * transactions.
+ */
+ public static void printGoodBye() {
+ System.out.println(LINE);
+ System.out.println(TAB_SPACE +
+ "Bye... Don't forget to keep track of your future transactions");
+ System.out.println(LINE);
+ }
+
+ /**
+ * The function `printNoCommandExists` prints a message indicating that no such
+ * command exists.
+ */
+ public static void printNoCommandExists() {
+ System.out.println(LINE);
+ System.out.println(TAB_SPACE + "No such command exists.");
+ System.out.println(LINE);
+ }
+
+ //@@author Vavinan
+ /**
+ * The function `getEditInformation` prompts the user to edit transaction
+ * details and returns the
+ * updated information as a formatted string.
+ *
+ * @param string The `getEditInformation` method takes a `String` parameter
+ * named `string`, which
+ * represents the transaction information that needs to be edited.
+ * The method then prompts the user
+ * to enter new values for the transaction type, description,
+ * date, amount, and category.
+ * @return The `getEditInformation` method is returning a formatted string that
+ * contains the edited
+ * transaction information.
+ */
+ public static String getEditInformation(String string) throws InvalidArgumentSyntaxException {
+ System.out.println(LINE);
+ System.out.println(TAB_SPACE + "Please edit the following transaction");
+ System.out.println(string);
+ System.out.println(LINE);
+ System.out.print(TAB_SPACE + "Enter transaction type: ");
+ String type = in.nextLine();
+ System.out.print(TAB_SPACE + "Enter description: ");
+ String description = in.nextLine();
+ if (description.contains(",")) {
+ throw new InvalidArgumentSyntaxException("Input cannot contain ',' comma.");
+ }
+ System.out.print(TAB_SPACE + "Enter transaction date: ");
+ String date = in.nextLine();
+ System.out.print(TAB_SPACE + "Enter transaction amount: ");
+ String amount = in.nextLine();
+ System.out.println(" ");
+ for (Category category : Category.values()) {
+ System.out.println(TAB_SPACE + TAB_SPACE + category.getCategoryName() + ": " + category.getCategoryNum());
+ }
+ System.out.println("In which category do you want to list this transaction? [Enter number between 1 and 9]");
+ System.out.print(TAB_SPACE + "Enter Category: ");
+ String category = in.next();
+ in.nextLine();
+ return type + " | " + description + " | " + date + " | " + amount + " | " + category;
+
+ }
+ //@@author
+
+ /**
+ * The function `printUpdatedTransaction` prints a success message along with
+ * the updated
+ * transaction details.
+ *
+ * @param t The parameter `t` is an object of the `Transaction` class. This
+ * method is designed to
+ * print a message indicating that the transaction was updated
+ * successfully, followed by
+ * the details of the updated transaction object `t`.
+ */
+ public static void printUpdatedTransaction(Transaction t) {
+ System.out.println("\n" + TAB_SPACE + "Updated Successfully");
+ System.out.println(t.toString());
+ System.out.println(LINE);
+ }
+
+ /**
+ * The function `printAllCommands` prints a list of available commands with
+ * their syntax and
+ * further help options.
+ */
+ public static void printAllCommands() {
+ System.out.println(HELP_BORDER);
+ System.out.printf("%-20s %-75s %-20s%n", "Command", "Syntax", "Further help");
+ System.out.printf("%-20s %-75s %-20s%n", "Add", "add /t/[TYPE] /n/[DESCRIPTION] /d/[DD-MM-YYYY] " +
+ "/$/[AMOUNT] /c/[CATEGORY]", "help add");
+ System.out.printf("%-20s %-75s %-20s%n", "Edit", "edit [INDEX]", "help edit");
+ System.out.printf("%-20s %-75s %-20s%n", "Delete", "delete [INDEX]", "help delete");
+ System.out.printf("%-20s %-75s %-20s%n", "List", "list", "help list");
+ System.out.println(HELP_BORDER);
+
+ }
+
+ /**
+ * The function `printAddHelp` prints out helpful information and syntax
+ * examples for adding
+ * entries with different categories.
+ */
+ public static void printAddHelp() {
+ System.out.println(HELP_BORDER);
+ System.out.println("Method 1:");
+ System.out.println(TAB_SPACE + "SYNTAX : add /t/[TYPE] /n/[DESCRIPTION] /d/[DD-MM-YYYY]" +
+ "/$/[AMOUNT] \n");
+ System.out.println("followed by choosing category from the given list:");
+
+ for (Category category : Category.values()) {
+ System.out.println(TAB_SPACE + TAB_SPACE + category.getCategoryName() + ": " + category.getCategoryNum());
+ }
+
+ System.out.println("\n Method 2");
+ System.out.println(TAB_SPACE + " SYNTAX : add /t/[TYPE] /n/[DESCRIPTION] /d/[DD-MM-YYYY] " +
+ "/$/[AMOUNT] /c/[CATEGORY] \n");
+ System.out.println("Provide the category number along with the add command");
+ System.out.println("\n ");
+ System.out.println(HELP_BORDER);
+ }
+
+ /**
+ * The function `printDeleteHelp` prints out syntax and guidelines for deleting
+ * an item from a
+ * transaction list.
+ */
+ public static void printDeleteHelp() {
+ System.out.println(HELP_BORDER);
+ System.out.println(TAB_SPACE + "SYNTAX : delete [INDEX] \n");
+ System.out.println(TAB_SPACE + "Make sure the index is above 0 and below or equal to the size of " +
+ "the transaction list");
+ System.out.println(HELP_BORDER);
+ }
+
+ /**
+ * The function `printEditHelp` provides guidance on how to use the "edit"
+ * command in a Java
+ * program for managing transactions.
+ */
+ public static void printEditHelp() {
+ System.out.println(HELP_BORDER);
+ System.out.println(TAB_SPACE + "SYNTAX : edit [INDEX] \n");
+ System.out.println("Make sure the index is above 0 and below or equal to the size of the " +
+ "transaction list");
+ System.out.println("Then you will be asked to input the data for each parameters like");
+ System.out.println(" Enter transaction type: [EXPENSE / INCOME] \n" +
+ " Enter description: [NEW DESCRIPTION] \n" +
+ " Enter transaction date: [NEW DATE] \n" +
+ " Enter transaction amount: [NEW AMOUNT] \n" + " \n");
+ System.out.println("Then you will be given categories to choose from (like as add command). \n" +
+ " In which category do you want to list this transaction? [Enter number between 1 and " +
+ "9]\n" +
+ " Enter Category: [NEW CATEGORY] ");
+ System.out.println(HELP_BORDER);
+ }
+
+ /**
+ * The function `printListHelp` prints out a help message with syntax and
+ * available options for
+ * viewing transactions.
+ */
+ public static void printListHelp() {
+ System.out.println(HELP_BORDER);
+ System.out.println(TAB_SPACE + "SYNTAX : list \n");
+ System.out.println(TAB_SPACE + "This will give some available options to choose from:");
+ System.out.println("What would you like to view?\n" +
+ " 1. All Transactions\n" +
+ " 2. Past Week Transactions\n" +
+ " 3. Past Month Transactions\n" +
+ " 4. Custom Date Transactions\n");
+ System.out.println("From this you can choose 1-4 :");
+ System.out.println("To print Custom date transaction: \n" +
+ " 4\n" + "Start Date: [dd-MM-yyyy]\n" + "End Date: [dd-MM-yyyy] ");
+ System.out.println(HELP_BORDER);
+ }
+
+ /**
+ * The function `printUseAvailableHelp` prints out a list of available commands
+ * for getting help
+ * related to transactions and accounts.
+ */
+ public static void printUseAvailableHelp() {
+ System.out.println(HELP_BORDER);
+ System.out.println(TAB_SPACE + "Please use the following commands for help");
+ System.out.println(TAB_SPACE + "To get idea about all commands use: help all");
+ System.out.println(TAB_SPACE + "Add transaction: help add");
+ System.out.println(TAB_SPACE + "Delete transaction: help delete");
+ System.out.println(TAB_SPACE + "Search : help search");
+ System.out.println(TAB_SPACE + "Edit transaction: help edit");
+ System.out.println(TAB_SPACE + "List transaction: help list");
+ System.out.println(TAB_SPACE + "Help related to accounts: help acc");
+ System.out.println(HELP_BORDER);
+ }
+
+ //@@author isaaceng7
+ /**
+ * The function `printListOptions` displays a menu of transaction viewing
+ * options.
+ */
+ public static void printListOptions() {
+ System.out.println(LINE);
+ System.out.println("What would you like to view?");
+ System.out.println(TAB_SPACE + "1. All Transactions");
+ System.out.println(TAB_SPACE + "2. Past Week Transactions");
+ System.out.println(TAB_SPACE + "3. Past Month Transactions");
+ System.out.println(TAB_SPACE + "4. Custom Date Transactions");
+ System.out.println(TAB_SPACE + "5. Account Transactions");
+ System.out.println(TAB_SPACE + "6. Category Transactions");
+ System.out.println(LINE);
+ }
+ //@@author
+
+ /**
+ * The function `getListOption` reads a user input as a String and returns it.
+ *
+ * @return This method returns a String value that is read from the user input
+ * using the `Scanner` object `in`.
+ */
+ //@@author isaaceng7
+ public static String getListOption() {
+ String data = in.next();
+ in.nextLine();
+ return data;
+ }
+
+ //@@author
+
+ /**
+ * The function `getStartDate()` prompts the user for a start date input and
+ * returns the entered
+ * data as a String.
+ *
+ * @return The method `getStartDate()` returns a String value, which is the user
+ * input for the
+ * start date.
+ */
+ //@@author isaaceng7
+ public static String getStartDate() {
+ System.out.print("Start Date: ");
+ String data = in.next();
+ in.nextLine();
+ return data;
+ }
+
+ //@@author
+
+ /**
+ * The function `getEndDate()` prompts the user for an end date input and
+ * returns the entered data
+ * as a String.
+ *
+ * @return The method `getEndDate()` is returning a String value that represents
+ * the end date
+ * entered by the user.
+ */
+ //@@author isaaceng7
+ public static String getEndDate() {
+ System.out.print("End Date: ");
+ String data = in.next();
+ in.nextLine();
+ return data;
+ }
+
+ //@@author
+
+ /**
+ * The function `printAccountList` prints a list of account numbers and names in
+ * a formatted table.
+ *
+ * @param accounts An ArrayList of Account objects. Each Account object
+ * represents an account with
+ * an account number and a name.
+ */
+ //@@author isaaceng7
+ public static void printAccountList(ArrayList accounts) {
+ int maxIndex = accounts.size();
+ System.out.println(LINE);
+ System.out.println("Please select an account to view...");
+ System.out.println(TAB_SPACE + "Your accounts:");
+ System.out.println(TAB_SPACE + ACCOUNT_TABLE_BORDER);
+ System.out.printf(TAB_SPACE + TAB_SPACE + "%-20s %-30s", "Account Number",
+ "Account Name");
+
+ for (int i = START_INDEX; i < maxIndex; i++) {
+ Account account = accounts.get(i);
+ int accountNumber = account.getAccountNumber();
+ String name = account.getName();
+
+ System.out.printf("\n" +TAB_SPACE + TAB_SPACE + "%-20d %-30.45s", accountNumber, name);
+ }
+ System.out.println("\n" + TAB_SPACE + ACCOUNT_TABLE_BORDER);
+ System.out.println(LINE);
+ }
+ //@@author
+
+ /**
+ * The function `getSelectedAccountNumber` prompts the user to select an account
+ * number from a list
+ * of accounts and returns the selected account number as a String.
+ *
+ * @param accounts An ArrayList of Account objects.
+ * @return The method `getSelectedAccountNumber` returns a `String` value, which
+ * is the account
+ * number selected by the user from the list of accounts provided.
+ */
+ //@@author isaaceng7
+ public static String getSelectedAccountNumber(ArrayList accounts) {
+ printAccountList(accounts);
+ System.out.print("Account number: ");
+ String data = in.next();
+ in.nextLine();
+ return data;
+ }
+
+ //@@author
+
+ /**
+ * The function `getSelectedCategory` prompts the user to select a category
+ * number and returns the
+ * integer value entered by the user.
+ *
+ * @return The method `getSelectedCategory` is returning an integer value
+ * representing the selected
+ * category number entered by the user.
+ */
+ //@@author isaaceng7
+ public static int getSelectedCategory() {
+ System.out.print("Select a category number: ");
+ String data = in.nextLine();
+ return Integer.parseInt(data);
+ }
+
+ //@@author
+
+ /**
+ * The function `printPastTransactions` prints a formatted list of past
+ * transactions based on a
+ * specified duration.
+ *
+ * @param transactions transactions is an ArrayList that contains Transaction
+ * objects.
+ * @param duration The `duration` parameter is a String that represents the
+ * time period for
+ * which the user want to display past transactions.
+ */
+ //@@author isaaceng7
+ public static void printPastTransactions(ArrayList transactions, String duration) {
+ int index = transactions.size();
+ System.out.println(LINE);
+ System.out.println(TAB_SPACE + "Displaying transactions from the past " + duration + ":");
+ System.out.println(TAB_SPACE + TABLE_BORDER);
+ System.out.printf(TAB_SPACE + TAB_SPACE + "%-5s %-10s %-20s %-20s %-30s %-15s %-15s %-15s%n", "ID", "Type",
+ "Account Number", "Account Name", "Transaction", "Date", "Amount", "Category");
+ for (int i = START_INDEX; i < index; i++) {
+ Transaction transaction = transactions.get(i);
+ int accountNumber = transaction.getAccountNumber();
+ String accountName = transaction.getAccountName();
+ String type = transaction.getTransactionType();
+ String description = transaction.getDescription();
+ LocalDate date = transaction.getDate();
+ double amount = transaction.getAmount();
+ String category = transaction.getCategory().getCategoryName();
+
+ System.out.printf(TAB_SPACE + TAB_SPACE + "%-5d %-10s %-20d %-20.45s %-30.45s %-15s %-15.2f %-15s%n",
+ i + 1, type, accountNumber, accountName, description, date, amount, category);
+ }
+ System.out.println(TAB_SPACE + TABLE_BORDER);
+ System.out.println(LINE);
+
+ }
+
+ //@@author
+
+ /**
+ * The `printCustomDateTransactions` function prints a formatted list of
+ * transactions within a
+ * specified date range.
+ *
+ * @param transactions The `printCustomDateTransactions` method takes an
+ * `ArrayList` of
+ * `Transaction` objects as input. It then iterates over the
+ * transactions in the list and prints
+ * out specific details of each transaction in a formatted
+ * table-like structure.
+ */
+ //@@author isaaceng7
+ public static void printCustomDateTransactions(ArrayList transactions) {
+ int index = transactions.size();
+ System.out.println(LINE);
+ System.out.println(TAB_SPACE + "Displaying transactions of specified date range:");
+ System.out.println(TAB_SPACE + TABLE_BORDER);
+ System.out.printf(TAB_SPACE + TAB_SPACE + "%-5s %-10s %-20s %-20s %-30s %-15s %-15s %-15s%n", "ID", "Type",
+ "Account Number", "Account Name", "Transaction", "Date", "Amount", "Category");
+ for (int i = START_INDEX; i < index; i++) {
+ Transaction transaction = transactions.get(i);
+ int accountNumber = transaction.getAccountNumber();
+ String accountName = transaction.getAccountName();
+ String type = transaction.getTransactionType();
+ String description = transaction.getDescription();
+ LocalDate date = transaction.getDate();
+ double amount = transaction.getAmount();
+ String category = transaction.getCategory().getCategoryName();
+
+ System.out.printf(TAB_SPACE + TAB_SPACE + "%-5d %-10s %-20d %-20.45s %-30.45s %-15s %-15.2f %-15s%n",
+ i + 1, type, accountNumber, accountName, description, date, amount, category);
+ }
+ System.out.println(TAB_SPACE + TABLE_BORDER);
+ System.out.println(LINE);
+ }
+
+ //@@author
+
+ /**
+ * The function `printAccountTransactions` prints a formatted list of
+ * transactions for a specific
+ * account.
+ *
+ * @param transactions An ArrayList of Transaction objects containing the
+ * transaction details.
+ * @param accountName The `accountName` parameter in the
+ * `printAccountTransactions` method is a
+ * `String` that represents the name of the account for
+ * which the user want to display transactions.
+ * @param accountNumber The `accountNumber` parameter in the
+ * `printAccountTransactions` method is
+ * an integer value that represents the account number of
+ * the account for which the user want to display
+ * transactions.
+ */
+ //@@author isaaceng7
+ public static void printAccountTransactions(ArrayList transactions, String accountName,
+ int accountNumber) {
+ int index = transactions.size();
+ System.out.println(LINE);
+ System.out
+ .println(TAB_SPACE + "Displaying transactions of account: " + accountName + "(" + accountNumber + ")");
+ System.out.println(TAB_SPACE + TABLE_BORDER);
+ System.out.printf(TAB_SPACE + TAB_SPACE + "%-5s %-10s %-30s %-15s %-15s %-15s%n", "ID", "Type",
+ "Transaction", "Date", "Amount", "Category");
+ for (int i = START_INDEX; i < index; i++) {
+ Transaction transaction = transactions.get(i);
+ String type = transaction.getTransactionType();
+ String description = transaction.getDescription();
+ LocalDate date = transaction.getDate();
+ double amount = transaction.getAmount();
+ String category = transaction.getCategory().getCategoryName();
+
+ System.out.printf(TAB_SPACE + TAB_SPACE + "%-5d %-10s %-30.45s %-15s %-15.2f %-15s%n",
+ i + 1, type, description, date, amount, category);
+ }
+ System.out.println(TAB_SPACE + TABLE_BORDER);
+ System.out.println(LINE);
+ }
+
+ //@@author
+
+ /**
+ * The function `printCategoryTransactions` prints out a formatted list of
+ * transactions belonging
+ * to a specific category.
+ *
+ * @param transactions transactions is an ArrayList that contains Transaction
+ * objects.
+ * @param categoryName Category name is a string representing the name of a
+ * category for which the
+ * user want to display transactions.
+ */
+ //@@author isaaceng7
+ public static void printCategoryTransactions(ArrayList transactions, String categoryName) {
+ int index = transactions.size();
+ System.out.println(LINE);
+ System.out.println(TAB_SPACE + "Displaying transactions of category: " + categoryName);
+ System.out.println(TAB_SPACE + TABLE_BORDER);
+ System.out.printf(TAB_SPACE + TAB_SPACE + "%-5s %-10s %-20s %-20s %-30s %-15s %-15s%n", "ID", "Type",
+ "Account Number", "Account Name", "Transaction", "Date", "Amount");
+ for (int i = START_INDEX; i < index; i++) {
+ Transaction transaction = transactions.get(i);
+ int accountNumber = transaction.getAccountNumber();
+ String accountName = transaction.getAccountName();
+ String type = transaction.getTransactionType();
+ String description = transaction.getDescription();
+ LocalDate date = transaction.getDate();
+ double amount = transaction.getAmount();
+
+ System.out.printf(TAB_SPACE + TAB_SPACE + "%-5d %-10s %-20d %-20.45s %-30.45s %-15s %-15.2f%n",
+ i + 1, type, accountNumber, accountName, description, date, amount);
+ }
+ System.out.println(TAB_SPACE + TABLE_BORDER);
+ System.out.println(LINE);
+ }
+
+ //@@author
+
+ /**
+ * The function `printAddAccountMessage` takes a string representing an account,
+ * splits it into
+ * parts, and prints each part with proper formatting.
+ *
+ * @param account The parameter `account` is expected to contain account
+ * information separated
+ * by the pipe character `|`.
+ */
+ //@@author vibes-863
+ public static void printAddAccountMessage(String account) {
+ String[] parts = account.split("\\|");
+ System.out.println(LINE);
+ System.out.println(TAB_SPACE + "Got it. I have added the following account \n");
+ for (String part : parts) {
+ System.out.println(TAB_SPACE + part.trim());
+ }
+ System.out.println(LINE);
+ }
+
+ //@@author
+
+ /**
+ * The function `printInvalidArgumentSyntax` prints an error message along with
+ * a reminder to
+ * ensure correct argument entry.
+ *
+ * @param message The `message` parameter is a string that contains the specific
+ * error message to
+ * be displayed when the argument syntax is invalid.
+ */
+ //@@author vibes-863
+ public static void printInvalidArgumentSyntax(String message) {
+ System.out.println(LINE);
+ System.out.println(TAB_SPACE + message);
+ System.out.println(TAB_SPACE + "Please ensure that you have entered all the arguments correctly.");
+ System.out.println(LINE);
+ }
+
+ //@@author
+
+ /**
+ * The function `printListOfAccounts` prints a formatted list of account details
+ * including account
+ * number, name, and balance.
+ *
+ * @param accounts An ArrayList of Account objects.
+ */
+ //@@author vibes-863
+ public static void printListOfAccounts(ArrayList accounts) {
+ int maxIndex = accounts.size();
+ System.out.println(LINE);
+ System.out.println(TAB_SPACE + "Your accounts:");
+ System.out.println(TAB_SPACE + ACCOUNT_TABLE_BORDER);
+ System.out.printf(TAB_SPACE + TAB_SPACE + "%-20s %-30s %-15s ", "Account Number",
+ "Account Name", "Balance");
+
+ for (int i = START_INDEX; i < maxIndex; i++) {
+ Account account = accounts.get(i);
+ int accountNumber = account.getAccountNumber();
+ String name = account.getName();
+ double balance = account.getBalance();
+
+ System.out.printf("\n" + TAB_SPACE + TAB_SPACE + "%-20d %-30.45s %-15.2f", accountNumber, name, balance);
+ }
+ System.out.println("\n" + TAB_SPACE + ACCOUNT_TABLE_BORDER);
+ System.out.println(LINE);
+ }
+
+ //@@author
+
+ /**
+ * The function `printDeleteAccountMessage` prints a message confirming the
+ * deletion of an account
+ * and lists the transactions related to that account that have also been
+ * removed.
+ *
+ * @param account The `account` parameter is a string that contains
+ * account information.
+ * @param transactionsRemoved The `transactionsRemoved` parameter is an
+ * ArrayList of
+ * Transaction objects that represent the
+ * transactions related to the account
+ */
+ //@@author vibes-863
+ public static void printDeleteAccountMessage(String account, ArrayList transactionsRemoved) {
+ String[] parts = account.split("\\|");
+ System.out.println(LINE);
+ System.out.println(TAB_SPACE + "Got it. I have removed the following account \n");
+ for (String part : parts) {
+ System.out.println(TAB_SPACE + part.trim());
+ }
+ System.out.println("\n" + TAB_SPACE + "Transactions related to this account have also been removed:");
+ System.out.println(TAB_SPACE + TABLE_BORDER);
+ System.out.printf(TAB_SPACE + TAB_SPACE + "%-10s %-30s %-15s %-15s %-15s%n", "Type", "Transaction", "Date",
+ "Amount", "Category");
+ for (Transaction transaction : transactionsRemoved) {
+ String type = transaction.getTransactionType();
+ String description = transaction.getDescription();
+ LocalDate date = transaction.getDate();
+ double amount = transaction.getAmount();
+ String category = transaction.getCategory().getCategoryName();
+
+ System.out.printf(TAB_SPACE + TAB_SPACE + "%-10s %-30.45s %-15s %-15.2f %-15s%n", type, description, date,
+ amount, category);
+ }
+ System.out.println(TAB_SPACE + TABLE_BORDER);
+
+ System.out.println(LINE);
+ }
+
+ //@@author
+
+ /**
+ * The function `getNewAccountName` splits an account string into parts,
+ * displays them for editing,
+ * prompts the user to enter a new account name, and returns the trimmed input.
+ *
+ * @param account The `getNewAccountName` method takes a `String` parameter
+ * named `account`, which
+ * is expected to be in a specific format separated by the pipe
+ * character `|`.
+ * @return The method `getNewAccountName` returns a new account name entered by
+ * the user.
+ */
+ //@@author vibes-863
+ public static String getNewAccountName(String account) throws InvalidArgumentSyntaxException {
+ String[] parts = account.split("\\|");
+ System.out.println(LINE);
+ System.out.println(TAB_SPACE + "Please edit the following account \n");
+ for (String part : parts) {
+ System.out.println(TAB_SPACE + part.trim());
+ }
+ System.out.println(LINE);
+ System.out.print(TAB_SPACE + "Enter new account name: ");
+ String name = in.nextLine().trim();
+ if (name.contains(",")) {
+ throw new InvalidArgumentSyntaxException("Input cannot contain ',' comma.");
+ }
+ return name;
+ }
+
+ //@@author
+
+ /**
+ * The function `printUpdatedAccount` splits a given account string into parts,
+ * prints a success
+ * message, and then prints each part of the account details.
+ *
+ * @param account The `printUpdatedAccount` method takes a `String` parameter
+ * named `account`,
+ * which represents the account details.
+ */
+ //@@author vibes-863
+ public static void printUpdatedAccount(String account) {
+ String[] parts = account.split("\\|");
+ System.out.println(LINE);
+ System.out.println(TAB_SPACE + "Updated Successfully! Updated account details: \n");
+ for (String part : parts) {
+ System.out.println(TAB_SPACE + part.trim());
+ }
+ System.out.println(LINE);
+ }
+
+ //@@author
+
+ /**
+ * The function `printCannotDeleteLastAccountMessage` prints a message
+ * indicating that the last
+ * account cannot be deleted.
+ */
+ //@@author vibes-863
+ public static void printCannotDeleteLastAccountMessage() {
+ System.out.println(LINE);
+ System.out.println(TAB_SPACE + "You cannot delete the last account.");
+ System.out.println(TAB_SPACE + "Please edit the current account or add a new account before deleting " +
+ "this one.");
+ System.out.println(LINE);
+ }
+
+ //@@author
+
+ /**
+ * The function `printAccountHelp()` prints a help menu for account-related
+ * commands in a Java
+ * program.
+ */
+ //@@author Vavinan
+ public static void printAccountHelp() {
+ System.out.println(HELP_BORDER);
+ System.out.printf("%-30s %-90s%n", "Command", "Syntax");
+ System.out.printf("%-30s %-90s%n", "Add account", "add-acc /n/ [ACCOUNT_NAME] /$/ " +
+ "[INITIAL_BALANCE]");
+ System.out.printf("%-30s %-90s%n", "Delete Account", "delete-acc [ACCOUNT_NUMBER]");
+ System.out.printf("%-30s %-90s%n", "Delete Account", "edit-acc [ACCOUNT_NUMBER]");
+ System.out.printf("%-30s %-90s%n", "List all Accounts", "list-acc");
+ System.out.println(HELP_BORDER);
+ }
+
+ /**
+ * The function `printFileCorruptedError` prints an error message indicating
+ * that the storage file
+ * is corrupted and informs that a new file will be created.
+ */
+ //@@author ShyamKrishna33
+ public static void printFileCorruptedError() {
+ System.out.println(LINE);
+ System.out.println(TAB_SPACE + "The storage file is corrupted :(");
+ System.out.println(TAB_SPACE + "So, a new file will be created!");
+ System.out.println(LINE);
+ }
+
+ //@@author
+
+ /**
+ * The function `printSearchResults` prints search results of transactions with
+ * specific formatting
+ * and handles cases where no matching transactions are found.
+ *
+ * @param transactions The `transactions` parameter is an ArrayList of
+ * Transaction objects. Each
+ * Transaction object represents a specific transaction with
+ * details such as transaction type,
+ * account number, account name, description, date, amount,
+ * and category.
+ * @param indices The `indices` parameter in the `printSearchResults`
+ * method is an `ArrayList` of
+ * `Integer` values. These indices represent the positions
+ * of the matching transactions in the
+ * original list of transactions. The method uses these
+ * indices to display the search results with
+ * the corresponding transaction number.
+ */
+ //@@author Vavinan
+ public static void printSearchResults(ArrayList transactions, ArrayList indices) {
+ if (transactions.isEmpty()) {
+ System.out.println("No matching Transactions found");
+ } else {
+ System.out.println("Search results:");
+ System.out.println(TAB_SPACE + TABLE_BORDER);
+ System.out.printf(TAB_SPACE + TAB_SPACE + "%-5s %-10s %-20s %-20s %-30s %-15s %-15s %-15s%n", "ID", "Type",
+ "Account Number", "Account Name", "Transaction", "Date", "Amount", "Category");
+ int i = 0;
+ for (Transaction t : transactions) {
+ // System.out.println((indices.get(i) + 1) + ". " + transactions.get(i));
+ System.out.printf(TAB_SPACE + TAB_SPACE + "%-5d %-10s %-20d %-20.45s %-30.45s %-15s %-15.2f %-15s%n",
+ indices.get(i) + 1, t.getTransactionType(), t.getAccountNumber(), t.getAccountName(),
+ t.getDescription(), t.getDate(), t.getAmount(), t.getCategory().getCategoryName());
+ i++;
+ }
+ System.out.println(TAB_SPACE + TABLE_BORDER);
+ }
+
+ }
+
+ /**
+ * The function `printInvalidCategoryError` prints an error message for an
+ * invalid category.
+ */
+ //@@author ShyamKrishna33
+ public static void printInvalidCategoryError() {
+ System.out.println(LINE);
+ System.out.println(TAB_SPACE + "Invalid Category");
+ System.out.println(LINE);
+ }
+
+ //@@author
+
+ /**
+ * The function `printInvalidAccountNameError` prints an error message stating
+ * that the Account
+ * Name cannot be blank.
+ */
+ //@@author ShyamKrishna33
+ private static void printInvalidAccountNameError() {
+ System.out.println(LINE);
+ System.out.println(TAB_SPACE + "Account Name cannot be blank");
+ System.out.println(LINE);
+ }
+
+ //@@author
+
+ /**
+ * The function `printInvalidAccountBalanceError` prints an error message the
+ * invalid account balance.
+ */
+ //@@author ShyamKrishna33
+ private static void printInvalidAccountBalanceError() {
+ System.out.println(LINE);
+ System.out.println(TAB_SPACE + "Account Balance cannot be a valid number");
+ System.out.println(LINE);
+ }
+
+ //@@author
+
+ /**
+ * The function `getInitialAccountName` prompts the user to input a name for
+ * their account and
+ * validates it to ensure it is not empty.
+ *
+ * @return The method `getInitialAccountName()` returns a `String` value, which
+ * is the account name
+ * entered by the user.
+ */
+ //@@author ShyamKrishna33
+ public static String getInitialAccountName() throws InvalidArgumentSyntaxException {
+ System.out.println("Let's first create an account for you! What do you want to call it?");
+ String accountName = in.nextLine();
+ if (accountName.contains(",")) {
+ throw new InvalidArgumentSyntaxException("Input cannot contain ',' comma.");
+ }
+
+ if (accountName.trim().isEmpty()) {
+ printInvalidAccountNameError();
+ accountName = getInitialAccountName();
+ }
+ return accountName;
+ }
+
+ //@@author
+
+ /**
+ * The function `getInitialAccountBalance` prompts the user to input an initial
+ * account balance and
+ * handles any input errors.
+ *
+ * @return The method `getInitialAccountBalance()` returns a `Double` value,
+ * which represents the
+ * initial account balance entered by the user.
+ */
+ //@@author ShyamKrishna33
+ public static Double getInitialAccountBalance() {
+ System.out.println("Great! What's the initial balance?");
+ double initialBalance = 0;
+ try {
+ initialBalance = Double.parseDouble(UserInterface.in.nextLine().trim());
+ } catch (NumberFormatException e) {
+ printInvalidAccountBalanceError();
+ getInitialAccountBalance();
+ }
+ return initialBalance;
+ }
+
+ public static void printSearchHelp(){
+ System.out.println(HELP_BORDER);
+ System.out.println(TAB_SPACE + "SYNTAX : search [KEYWORD] \n");
+ System.out.println(TAB_SPACE + "The keyword can be anything representing description, date, " +
+ "category or amount");
+ System.out.println(HELP_BORDER);
+ }
+
+ /**
+ * The function `printLoggerSetupError` prints an error message indicating that
+ * there was an issue
+ * setting up the logger.
+ */
+ public static void printLoggerSetupError() {
+ System.out.println(LINE);
+ System.out.println(TAB_SPACE + "Error setting up logger");
+ System.out.println(LINE);
+ }
+}
diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java
deleted file mode 100644
index 5c74e68d59..0000000000
--- a/src/main/java/seedu/duke/Duke.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package seedu.duke;
-
-import java.util.Scanner;
-
-public class Duke {
- /**
- * Main entry-point for the java.duke.Duke application.
- */
- public static void main(String[] args) {
- String logo = " ____ _ \n"
- + "| _ \\ _ _| | _____ \n"
- + "| | | | | | | |/ / _ \\\n"
- + "| |_| | |_| | < __/\n"
- + "|____/ \\__,_|_|\\_\\___|\n";
- System.out.println("Hello from\n" + logo);
- System.out.println("What is your name?");
-
- Scanner in = new Scanner(System.in);
- System.out.println("Hello " + in.nextLine());
- }
-}
diff --git a/src/test/java/budgetbuddy/account/AccountManagerTest.java b/src/test/java/budgetbuddy/account/AccountManagerTest.java
new file mode 100644
index 0000000000..e99f6d993f
--- /dev/null
+++ b/src/test/java/budgetbuddy/account/AccountManagerTest.java
@@ -0,0 +1,58 @@
+package budgetbuddy.account;
+
+import budgetbuddy.exceptions.EmptyArgumentException;
+import budgetbuddy.exceptions.InvalidArgumentSyntaxException;
+import budgetbuddy.exceptions.InvalidIndexException;
+import budgetbuddy.transaction.TransactionList;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class AccountManagerTest {
+
+ @Test
+ void addingAccountShouldIncreaseExistingAccountNumbersSize() {
+ AccountManager accountManager = new AccountManager();
+ accountManager.addAccount("Test Account", 1000.00);
+ assertEquals(1, accountManager.getExistingAccountNumbers().size());
+ }
+
+ @Test
+ void addingAccountShouldGenerateUniqueAccountNumber() {
+ AccountManager accountManager = new AccountManager();
+ accountManager.addAccount("Test Account", 1000.00);
+ accountManager.addAccount("Test Account 2", 2000.00);
+ assertNotEquals(accountManager.getAccount(0).getAccountNumber(), accountManager
+ .getAccount(1).getAccountNumber());
+ }
+
+ @Test
+ void removingAccountShouldDecreaseExistingAccountNumbersSize()
+ throws InvalidIndexException, InvalidArgumentSyntaxException, EmptyArgumentException {
+ AccountManager accountManager = new AccountManager();
+ accountManager.addAccount("Test Account", 1000.00);
+ accountManager.addAccount("Test Account2", 1000.00);
+ accountManager.removeAccount("delete-acc " + String.valueOf(accountManager.getAccounts().get(1)
+ .getAccountNumber()), new TransactionList());
+ assertEquals(1, accountManager.getExistingAccountNumbers().size());
+ }
+
+ @Test
+ void getAccountByAccountNumberShouldReturnCorrectAccount() {
+ AccountManager accountManager = new AccountManager();
+ accountManager.addAccount("Test Account", 1000.00);
+ int accountNumber = accountManager.getAccount(0).getAccountNumber();
+ assertEquals("Test Account", accountManager.getAccountByAccountNumber(accountNumber).getName());
+ }
+
+ @Test
+ void getAccountByInvalidAccountNumberShouldThrowException() {
+ AccountManager accountManager = new AccountManager();
+ accountManager.addAccount("Test Account", 1000.00);
+ assertThrows(IllegalArgumentException.class, () -> accountManager.getAccountByAccountNumber(9999));
+ }
+
+
+}
diff --git a/src/test/java/budgetbuddy/account/AccountTest.java b/src/test/java/budgetbuddy/account/AccountTest.java
new file mode 100644
index 0000000000..4092972ac0
--- /dev/null
+++ b/src/test/java/budgetbuddy/account/AccountTest.java
@@ -0,0 +1,37 @@
+package budgetbuddy.account;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class AccountTest {
+
+ @Test
+ void creatingAccountWithValidParametersShouldSucceed() {
+ Account account = new Account(1234, "Test Account", 1000.00);
+ assertEquals(1234, account.getAccountNumber());
+ assertEquals("Test Account", account.getName());
+ assertEquals(1000.00, account.getBalance());
+ }
+
+ @Test
+ void settingBalanceShouldUpdateBalance() {
+ Account account = new Account(1234, "Test Account", 1000.00);
+ account.setBalance(2000.00);
+ assertEquals(2000.00, account.getBalance());
+ }
+
+ @Test
+ void settingNameShouldUpdateName() {
+ Account account = new Account(1234, "Test Account", 1000.00);
+ account.setName("Updated Account");
+ assertEquals("Updated Account", account.getName());
+ }
+
+ @Test
+ void toStringShouldReturnCorrectFormat() {
+ Account account = new Account(1234, "Test Account", 1000.00);
+ String expectedString = " Account Number: 1234 | Name: Test Account | Balance: 1000.0";
+ assertEquals(expectedString, account.toString());
+ }
+}
diff --git a/src/test/java/budgetbuddy/categories/CategoryTest.java b/src/test/java/budgetbuddy/categories/CategoryTest.java
new file mode 100644
index 0000000000..499f35704e
--- /dev/null
+++ b/src/test/java/budgetbuddy/categories/CategoryTest.java
@@ -0,0 +1,49 @@
+package budgetbuddy.categories;
+
+import budgetbuddy.exceptions.InvalidCategoryException;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class CategoryTest {
+ @Test
+ public void testGetCategoryName() {
+ assertEquals("Dining", Category.DINING.getCategoryName());
+ assertEquals("Groceries", Category.GROCERIES.getCategoryName());
+ assertEquals("Utilities", Category.UTILITIES.getCategoryName());
+ assertEquals("Transportation", Category.TRANSPORTATION.getCategoryName());
+ assertEquals("Healthcare", Category.HEALTHCARE.getCategoryName());
+ assertEquals("Entertainment", Category.ENTERTAINMENT.getCategoryName());
+ assertEquals("Rent", Category.RENT.getCategoryName());
+ assertEquals("Salary", Category.SALARY.getCategoryName());
+ assertEquals("Others", Category.OTHERS.getCategoryName());
+ }
+
+ @Test
+ public void testGetCategoryNum() {
+ assertEquals(1, Category.DINING.getCategoryNum());
+ assertEquals(2, Category.GROCERIES.getCategoryNum());
+ assertEquals(3, Category.UTILITIES.getCategoryNum());
+ assertEquals(4, Category.TRANSPORTATION.getCategoryNum());
+ assertEquals(5, Category.HEALTHCARE.getCategoryNum());
+ assertEquals(6, Category.ENTERTAINMENT.getCategoryNum());
+ assertEquals(7, Category.RENT.getCategoryNum());
+ assertEquals(8, Category.SALARY.getCategoryNum());
+ assertEquals(9, Category.OTHERS.getCategoryNum());
+ }
+
+ @Test
+ public void testFromNumber() throws InvalidCategoryException {
+ assertEquals(Category.DINING, Category.fromNumber(1));
+ assertEquals(Category.GROCERIES, Category.fromNumber(2));
+ assertEquals(Category.UTILITIES, Category.fromNumber(3));
+ assertEquals(Category.TRANSPORTATION, Category.fromNumber(4));
+ assertEquals(Category.HEALTHCARE, Category.fromNumber(5));
+ assertEquals(Category.ENTERTAINMENT, Category.fromNumber(6));
+ assertEquals(Category.RENT, Category.fromNumber(7));
+ assertEquals(Category.SALARY, Category.fromNumber(8));
+ assertEquals(Category.OTHERS, Category.fromNumber(9));
+ assertThrows(InvalidCategoryException.class, () -> Category.fromNumber(10));
+ }
+}
diff --git a/src/test/java/budgetbuddy/parser/ParserTest.java b/src/test/java/budgetbuddy/parser/ParserTest.java
new file mode 100644
index 0000000000..d55e57ffcc
--- /dev/null
+++ b/src/test/java/budgetbuddy/parser/ParserTest.java
@@ -0,0 +1,61 @@
+package budgetbuddy.parser;
+import budgetbuddy.account.Account;
+import budgetbuddy.exceptions.EmptyArgumentException;
+import budgetbuddy.exceptions.InvalidAddTransactionSyntax;
+import budgetbuddy.exceptions.InvalidCategoryException;
+import budgetbuddy.exceptions.InvalidEditTransactionData;
+import budgetbuddy.exceptions.InvalidTransactionTypeException;
+import org.junit.jupiter.api.Test;
+import budgetbuddy.transaction.type.Transaction;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class ParserTest {
+
+ @Test
+ public void testParseTransaction() throws InvalidTransactionTypeException, EmptyArgumentException,
+ InvalidCategoryException, InvalidAddTransactionSyntax {
+ Parser parser = new Parser();
+ Account account = new Account(1);
+ String input = "add /t/Income /n/Shopping /$/50 /d/14-03-2024 /c/1";
+ Transaction transaction = parser.parseUserInputToTransaction(input , account);
+ assertEquals("Shopping", transaction.getDescription());
+ assertEquals(50.0f, transaction.getAmount(), 0.001);
+ assertEquals(LocalDate.parse("14-03-2024", DateTimeFormatter.ofPattern("dd-MM-yyyy")),
+ transaction.getDate());
+ assertEquals("Dining", transaction.getCategory().getCategoryName());
+ }
+
+ @Test
+ public void testParseTransactionType() throws InvalidEditTransactionData, InvalidEditTransactionData,
+ InvalidCategoryException {
+ Parser parser = new Parser();
+ Account account = new Account(1);
+
+ // Test case for valid income transaction
+ String incomeTransactionString = "income | test1 | 10-11-2022 | 1000.00 | 1";
+ Transaction incomeTransaction = parser.parseEditTransaction(incomeTransactionString, account);
+ assertEquals("test1", incomeTransaction.getDescription());
+ assertEquals(1000.00, incomeTransaction.getAmount(), 0.001);
+ assertEquals("Dining", incomeTransaction.getCategory().getCategoryName());
+ assertEquals(LocalDate.parse("10-11-2022", DateTimeFormatter.ofPattern("dd-MM-yyyy")),
+ incomeTransaction.getDate());
+
+ // Test case for valid expense transaction
+ String expenseTransactionString = "expense | Grocery | 12-11-2022 | 50.00 | 2";
+ Transaction expenseTransaction = parser.parseEditTransaction(expenseTransactionString, account);
+ assertEquals("Grocery", expenseTransaction.getDescription());
+ assertEquals(-50.00, expenseTransaction.getAmount(), 0.001);
+ assertEquals("Groceries", expenseTransaction.getCategory().getCategoryName());
+ assertEquals(LocalDate.parse("12-11-2022", DateTimeFormatter.ofPattern("dd-MM-yyyy")),
+ expenseTransaction.getDate());
+
+ }
+
+
+
+
+}
diff --git a/src/test/java/budgetbuddy/storage/DataStorageTest.java b/src/test/java/budgetbuddy/storage/DataStorageTest.java
new file mode 100644
index 0000000000..7ce4310987
--- /dev/null
+++ b/src/test/java/budgetbuddy/storage/DataStorageTest.java
@@ -0,0 +1,76 @@
+package budgetbuddy.storage;
+
+import budgetbuddy.account.Account;
+import budgetbuddy.categories.Category;
+import budgetbuddy.exceptions.InvalidCategoryException;
+import budgetbuddy.transaction.type.Expense;
+import budgetbuddy.transaction.type.Transaction;
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+
+public class DataStorageTest {
+ @Test
+ public void testSaveTransactions() throws IOException, InvalidCategoryException {
+ DataStorage dataStorage = new DataStorage();
+ ArrayList transactionArrayList = new ArrayList<>();
+ Transaction t = new Expense(1, "test","Groceries", 50.0,
+ "25-03-2024", new Account(1));
+ t.setCategory(Category.fromNumber(1));
+ transactionArrayList.add(t);
+ ArrayList existingAccountNumbers = new ArrayList<>();
+ existingAccountNumbers.add(1);
+ try {
+ dataStorage.readTransactionFile(existingAccountNumbers);
+ dataStorage.saveTransactions(transactionArrayList);
+
+ File file = new File(DataStorage.TRANSACTIONS_FILE_PATH);
+ assertTrue(file.exists()); // Check if file exists after saving transactions
+ } catch (IOException e) {
+ fail("Exception thrown while saving transactions: " + e.getMessage());
+ } finally {
+ FileWriter fw = new FileWriter(DataStorage.TRANSACTIONS_FILE_PATH, false);
+ }
+ }
+
+ @Test
+ public void testReadFromFile() throws IOException, InvalidCategoryException {
+ DataStorage dataStorage = new DataStorage();
+ ArrayList expectedTransactions = new ArrayList<>();
+ Transaction t = new Expense( 1, "test","Groceries", 50.0,
+ "25-03-2024", new Account(1));
+ t.setCategory(Category.fromNumber(1));
+ expectedTransactions.add(t);
+ ArrayList existingAccountNumbers = new ArrayList<>();
+ existingAccountNumbers.add(1);
+ dataStorage.readTransactionFile(existingAccountNumbers);
+ dataStorage.saveTransactions(expectedTransactions);
+ try {
+ ArrayList actualTransactions = dataStorage.readTransactionFile(existingAccountNumbers);
+
+ // Check if read transactions match the expected transactions
+ assertEquals(expectedTransactions.size(), actualTransactions.size());
+ for (int i = 0; i < expectedTransactions.size(); i++) {
+ assertEquals(expectedTransactions.get(i).getDescription(), actualTransactions.get(i).getDescription());
+ assertEquals(expectedTransactions.get(i).getAmount(),
+ actualTransactions.get(i).getAmount(), 0.001);
+ assertEquals(expectedTransactions.get(i).getDate(), actualTransactions.get(i).getDate());
+ assertEquals(expectedTransactions.get(i).getTransactionType(),
+ actualTransactions.get(i).getTransactionType());
+ assertEquals(expectedTransactions.get(i).getCategory(), actualTransactions.get(i).getCategory());
+ }
+ } catch (IOException e) {
+ fail("Exception thrown while reading from file: " + e.getMessage());
+ } finally {
+ FileWriter fw = new FileWriter(DataStorage.TRANSACTIONS_FILE_PATH, false);
+ }
+ }
+}
diff --git a/src/test/java/budgetbuddy/transaction/TransactionListTest.java b/src/test/java/budgetbuddy/transaction/TransactionListTest.java
new file mode 100644
index 0000000000..aadb24fe39
--- /dev/null
+++ b/src/test/java/budgetbuddy/transaction/TransactionListTest.java
@@ -0,0 +1,175 @@
+package budgetbuddy.transaction;
+
+import budgetbuddy.account.Account;
+import budgetbuddy.account.AccountManager;
+import budgetbuddy.categories.Category;
+import budgetbuddy.exceptions.EmptyArgumentException;
+import budgetbuddy.exceptions.InvalidAddTransactionSyntax;
+import budgetbuddy.exceptions.InvalidCategoryException;
+import budgetbuddy.exceptions.InvalidIndexException;
+import budgetbuddy.exceptions.InvalidTransactionTypeException;
+import budgetbuddy.transaction.type.Income;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.BeforeEach;
+import budgetbuddy.transaction.type.Transaction;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class TransactionListTest {
+
+ private TransactionList transactionList;
+ private Account account;
+ private Account account2;
+ private AccountManager accountManager;
+
+ @BeforeEach
+ public void setUp() throws IOException {
+ transactionList = new TransactionList();
+ account = new Account(1);
+ account2 = new Account(2);
+ accountManager = new AccountManager();
+ accountManager.getAccounts().add(account); // need to change this
+ }
+
+ @Test
+ public void getTransactions_initiallyEmpty() {
+ assertEquals(0, transactionList.getTransactions().size());
+ }
+
+ @Test
+ public void processTransaction_addsTransaction()
+ throws InvalidTransactionTypeException, InvalidAddTransactionSyntax,
+ EmptyArgumentException, InvalidCategoryException {
+ Transaction testTransaction = new Income(1, "test","Test", 200,
+ "14-03-2024",
+ account);
+ testTransaction.setCategory(Category.fromNumber(1));
+ transactionList.processTransaction("add /a/ 1 /t/Income /n/Test /$/200 /d/14-03-2024 /c/1", account);
+
+ assertEquals(1, transactionList.getTransactions().size());
+ assertEquals(testTransaction.getDescription(), transactionList.getTransactions().get(0).getDescription());
+ assertEquals(testTransaction.getAmount(), transactionList.getTransactions().get(0).getAmount());
+ assertEquals(testTransaction.getCategory().getCategoryName(),
+ transactionList.getTransactions().get(0).getCategory().getCategoryName());
+ assertEquals(testTransaction.getDate(), transactionList.getTransactions().get(0).getDate());
+ }
+ @Test
+
+ public void processTransaction_withInvalidAddSyntax_throwsInvalidAddTransactionSyntax() {
+
+ assertThrows(InvalidAddTransactionSyntax.class, () -> transactionList.processTransaction(
+ "add Expense /n/Shopping /$/50 /d/14-03-2024 /c/2", account));
+ assertThrows(InvalidAddTransactionSyntax.class, () -> transactionList.processTransaction(
+ "add /t/Expense Shopping /$/50 /d/14-03-2024 /c/2", account));
+ assertThrows(InvalidAddTransactionSyntax.class, () -> transactionList.processTransaction(
+ "add /t/Expense /n/Shopping 50 /d/14-03-2024 /c/2", account));
+ assertThrows(InvalidAddTransactionSyntax.class, () -> transactionList.processTransaction(
+ "add /t/Expense /n/Shopping /$/50 14-03-2024 /c/2", account));
+ }
+
+ @Test
+ public void processTransaction_withInvalidTransactionType_throwsTransactionTypeException() {
+
+ assertThrows(InvalidTransactionTypeException.class, () -> transactionList.processTransaction(
+ "add /a/ 1 /t/Donation /n/Test /$/200 /d/14-03-2024 /c/2", account));
+ }
+
+ @Test
+ public void removeTransaction_removesCorrectTransaction() throws EmptyArgumentException,
+ InvalidIndexException, InvalidCategoryException {
+ Transaction testTransaction1 = new Income(1, "test","Test1", 100,
+ "14-03-2024", account);
+ testTransaction1.setCategory(Category.fromNumber(1));
+ Transaction testTransaction2 = new Income(1, "test","Test2", 200,
+ "16-03-2024", account);
+ testTransaction1.setCategory(Category.fromNumber(2));
+ transactionList.addTransaction(testTransaction1);
+ transactionList.addTransaction(testTransaction2);
+
+ transactionList.removeTransaction("delete 1", accountManager);
+
+ assertEquals(1, transactionList.getTransactions().size());
+ assertEquals(testTransaction2, transactionList.getTransactions().get(0));
+ }
+
+ @Test
+ public void removeTransaction_withInvalidIndex_throwsIndexOutOfBoundsException() {
+ Transaction testTransaction = new Income(1, "test","Test", 200,
+ "14-03-2024", account);
+ transactionList.addTransaction(testTransaction);
+
+ assertThrows(InvalidIndexException.class, () -> transactionList.removeTransaction(
+ "delete 2", accountManager));
+ }
+
+ @Test
+ public void removeTransaction_withMissingIndex_throwsEmptyArgumentException() {
+ Transaction testTransaction = new Income(1, "test","Test", 100,
+ "14-03-2024", account);
+ transactionList.addTransaction(testTransaction);
+
+ assertThrows(EmptyArgumentException.class, () -> transactionList.removeTransaction(
+ "delete", accountManager));
+ }
+
+ @Test
+ public void removeTransaction_withInvalidIndex_throwsNumberFormatException() {
+ Transaction testTransaction = new Income(1, "test","Test", 100,
+ "14-03-2024", account);
+ transactionList.addTransaction(testTransaction);
+
+ assertThrows(NumberFormatException.class, () -> transactionList.removeTransaction(
+ "delete one", accountManager));
+ }
+
+ @Test
+ public void getAccountTransactions_filtersCorrectTransactions() throws InvalidCategoryException {
+ Transaction testTransaction1 = new Income(1, "test","Test1", 100,
+ "15-03-2024", account);
+ testTransaction1.setCategory(Category.fromNumber(1));
+ Transaction testTransaction2 = new Income(1, "test","Test2", 200,
+ "16-03-2024", account);
+ testTransaction2.setCategory(Category.fromNumber(1));
+ Transaction testTransaction3 = new Income(2, "test","Test3", 300,
+ "18-03-2024", account2);
+ testTransaction3.setCategory(Category.fromNumber(1));
+ transactionList.addTransaction(testTransaction1);
+ transactionList.addTransaction(testTransaction2);
+ transactionList.addTransaction(testTransaction3);
+
+ ArrayList accountTransactions;
+ accountTransactions = TransactionList.getAccountTransactions(transactionList.getTransactions(),1);
+
+ assertEquals(2, accountTransactions.size());
+ assertEquals(testTransaction1, accountTransactions.get(0));
+ assertEquals(testTransaction2, accountTransactions.get(1));
+ }
+
+ @Test
+ public void getCategoryTransactions_filtersCorrectTransactions() throws InvalidCategoryException {
+ Transaction testTransaction1 = new Income(1, "test","Test1", 100,
+ "15-03-2024", account);
+ testTransaction1.setCategory(Category.fromNumber(1));
+ Transaction testTransaction2 = new Income(1, "test","Test2", 200,
+ "16-03-2024", account);
+ testTransaction2.setCategory(Category.fromNumber(1));
+ Transaction testTransaction3 = new Income(1, "test","Test3", 300,
+ "18-03-2024", account);
+ testTransaction3.setCategory(Category.fromNumber(5));
+ transactionList.addTransaction(testTransaction1);
+ transactionList.addTransaction(testTransaction2);
+ transactionList.addTransaction(testTransaction3);
+
+ ArrayList categoryTransactions;
+ categoryTransactions = TransactionList.getCategoryTransactions(transactionList.getTransactions(),
+ Category.fromNumber(1));
+
+ assertEquals(2, categoryTransactions.size());
+ assertEquals(testTransaction1, categoryTransactions.get(0));
+ assertEquals(testTransaction2, categoryTransactions.get(1));
+ }
+}
diff --git a/src/test/java/budgetbuddy/transaction/type/ExpenseTest.java b/src/test/java/budgetbuddy/transaction/type/ExpenseTest.java
new file mode 100644
index 0000000000..72fc46f1be
--- /dev/null
+++ b/src/test/java/budgetbuddy/transaction/type/ExpenseTest.java
@@ -0,0 +1,48 @@
+package budgetbuddy.transaction.type;
+
+import budgetbuddy.account.Account;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class ExpenseTest {
+ private Expense expense;
+ private Account account;
+
+ @BeforeEach
+ void setUp() {
+ account = new Account(1, "Test Account", 0.0);
+ expense = new Expense(1, "Test Account", "Test Expense", 100.0,
+ "01-01-2023", account);
+ }
+
+ @Test
+ void expenseDecreasesAccountBalance() {
+ assertEquals(-100.0, account.getBalance());
+ }
+
+ @Test
+ void expenseHasCorrectType() {
+ assertEquals("Expense", expense.getTransactionType());
+ }
+
+ @Test
+ void expenseWithNullDescriptionThrowsAssertionError() {
+ assertThrows(AssertionError.class, () -> new Expense(1, "Test Account",
+ null, 100.0, "01-01-2023", account));
+ }
+
+ @Test
+ void expenseWithEmptyDescriptionThrowsAssertionError() {
+ assertThrows(AssertionError.class, () -> new Expense(1, "Test Account", "",
+ 100.0, "01-01-2023", account));
+ }
+
+ @Test
+ void expenseWithNullAccountThrowsAssertionError() {
+ assertThrows(AssertionError.class, () -> new Expense(1, "Test Account",
+ "Test Expense", 100.0, "01-01-2023", null));
+ }
+}
diff --git a/src/test/java/budgetbuddy/transaction/type/IncomeTest.java b/src/test/java/budgetbuddy/transaction/type/IncomeTest.java
new file mode 100644
index 0000000000..e6ef40af8e
--- /dev/null
+++ b/src/test/java/budgetbuddy/transaction/type/IncomeTest.java
@@ -0,0 +1,54 @@
+package budgetbuddy.transaction.type;
+
+import budgetbuddy.account.Account;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class IncomeTest {
+ private Income income;
+ private Account account;
+
+ @BeforeEach
+ void setUp() {
+ account = new Account(1, "Test Account", 0.0);
+ income = new Income(1, "Test Account", "Test Income", 100.0,
+ "01-01-2023", account);
+ }
+
+ @Test
+ void incomeIncreasesAccountBalance() {
+ assertEquals(100.0, account.getBalance());
+ }
+
+ @Test
+ void incomeHasCorrectType() {
+ assertEquals("Income", income.getTransactionType());
+ }
+
+ @Test
+ void incomeWithNegativeAmountThrowsAssertionError() {
+ assertThrows(AssertionError.class, () -> new Income(1, "Test Account",
+ "Test Income", -100.0, "01-01-2023", account));
+ }
+
+ @Test
+ void incomeWithNullDescriptionThrowsAssertionError() {
+ assertThrows(AssertionError.class, () -> new Income(1, "Test Account",
+ null, 100.0, "01-01-2023", account));
+ }
+
+ @Test
+ void incomeWithEmptyDescriptionThrowsAssertionError() {
+ assertThrows(AssertionError.class, () -> new Income(1, "Test Account", "",
+ 100.0, "01-01-2023", account));
+ }
+
+ @Test
+ void incomeWithNullAccountThrowsAssertionError() {
+ assertThrows(AssertionError.class, () -> new Income(1, "Test Account",
+ "Test Income", 100.0, "01-01-2023", null));
+ }
+}
diff --git a/src/test/java/budgetbuddy/transaction/type/TransactionTest.java b/src/test/java/budgetbuddy/transaction/type/TransactionTest.java
new file mode 100644
index 0000000000..73fab1d786
--- /dev/null
+++ b/src/test/java/budgetbuddy/transaction/type/TransactionTest.java
@@ -0,0 +1,78 @@
+package budgetbuddy.transaction.type;
+
+import budgetbuddy.account.Account;
+import budgetbuddy.categories.Category;
+import budgetbuddy.exceptions.InvalidCategoryException;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class TransactionTest {
+
+ private Account account;
+
+ @BeforeEach
+ public void setUp() {
+ account = new Account(1);
+ }
+
+ @Test
+ public void testTransactionConstructor() {
+ Transaction transaction = new Income(1, "test","Groceries",
+ 50.0f, "14-03-2024", new Account(1));
+ assertEquals("Groceries", transaction.getDescription());
+ assertEquals(50.0f, transaction.getAmount(), 0.001);
+ LocalDate date = LocalDate.parse("14-03-2024", DateTimeFormatter.ofPattern("dd-MM-yyyy"));
+ assertEquals(date, transaction.getDate());
+ }
+
+ @Test
+ public void testGetDescription() {
+ Transaction transaction = new Income(1, "test","Groceries",
+ 50.0f, "14-03-2024",
+ account);
+ assertEquals("Groceries", transaction.getDescription());
+ }
+
+ @Test
+ public void testGetAmount() {
+ Transaction transaction = new Income(1, "test","Groceries",
+ 50.0f, "14-03-2024",
+ account);
+ assertEquals(50.0f, transaction.getAmount(), 0.001);
+ }
+
+ @Test
+ public void testGetCategory() throws InvalidCategoryException {
+ Transaction transaction = new Income(1, "test","Groceries",
+ 50.0f, "14-03-2024",
+ account);
+ transaction.setCategory(Category.fromNumber(1));
+ assertEquals("Dining", transaction.getCategory().getCategoryName());
+ }
+
+ @Test
+ public void testGetDate() {
+ Transaction transaction = new Income(1, "test","Groceries",
+ 50.0f, "14-03-2024",
+ account);
+ LocalDate date = LocalDate.parse("14-03-2024", DateTimeFormatter.ofPattern("dd-MM-yyyy"));
+ assertEquals(date, transaction.getDate());
+ }
+
+ @Test
+ public void testToString() throws InvalidCategoryException {
+ Transaction transaction = new Income(1, "test","Groceries",
+ 50.0f, "14-03-2024",
+ account);
+ transaction.setCategory(Category.fromNumber(1));
+ String expected = " Account Number: 1 | Account Name: test | Transaction Type: Income |" +
+ " Description: Groceries | Date: 2024-03-14 | Amount: 50.0 | " +
+ " Category: Dining";
+ assertEquals(expected, transaction.toString());
+ }
+}
diff --git a/src/test/java/seedu/duke/DukeTest.java b/src/test/java/seedu/duke/DukeTest.java
deleted file mode 100644
index 2dda5fd651..0000000000
--- a/src/test/java/seedu/duke/DukeTest.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package seedu.duke;
-
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-import org.junit.jupiter.api.Test;
-
-class DukeTest {
- @Test
- public void sampleTest() {
- assertTrue(true);
- }
-}
diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT
index 892cb6cae7..61619e10e3 100644
--- a/text-ui-test/EXPECTED.TXT
+++ b/text-ui-test/EXPECTED.TXT
@@ -1,9 +1,6 @@
Hello from
- ____ _
-| _ \ _ _| | _____
-| | | | | | | |/ / _ \
-| |_| | |_| | < __/
-|____/ \__,_|_|\_\___|
-
-What is your name?
-Hello James Gosling
+BUDGET BUDDY
+What can I do for you?
+----------------------------------------------------------------------------------------------------------------------------------------
+ Bye... Don't forget to keep track of your future transactions
+----------------------------------------------------------------------------------------------------------------------------------------
diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt
index f6ec2e9f95..b023018cab 100644
--- a/text-ui-test/input.txt
+++ b/text-ui-test/input.txt
@@ -1 +1 @@
-James Gosling
\ No newline at end of file
+bye