diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index fd8c44d086..391c46b4fe 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -32,19 +32,3 @@ jobs: - name: Build and check with Gradle run: ./gradlew check - - - name: Perform IO redirection test (*NIX) - if: runner.os == 'Linux' - working-directory: ${{ github.workspace }}/text-ui-test - run: ./runtest.sh - - - name: Perform IO redirection test (MacOS) - if: always() && runner.os == 'macOS' - working-directory: ${{ github.workspace }}/text-ui-test - run: ./runtest.sh - - - name: Perform IO redirection test (Windows) - if: always() && runner.os == 'Windows' - working-directory: ${{ github.workspace }}/text-ui-test - shell: cmd - run: runtest.bat \ No newline at end of file diff --git a/.gitignore b/.gitignore index 2873e189e1..b7e9079f00 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,7 @@ bin/ /text-ui-test/ACTUAL.TXT text-ui-test/EXPECTED-UNIX.TXT +users.txt + +drugs.txt +soldItems.txt diff --git a/build.gradle b/build.gradle index ea82051fab..0b34b1a98e 100644 --- a/build.gradle +++ b/build.gradle @@ -29,11 +29,11 @@ test { } application { - mainClass.set("seedu.duke.Duke") + mainClass.set("seedu.stocker.Stocker") } shadowJar { - archiveBaseName.set("duke") + archiveBaseName.set("stocker") archiveClassifier.set("") } @@ -43,4 +43,5 @@ checkstyle { run{ standardInput = System.in + enableAssertions = true } diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 0f072953ea..bcd0bf5501 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -1,9 +1,11 @@ # 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 | +|-----------------------------|:----------------:|:------------------------------------------:|:-------------------------------:| +| ![](team/Martin.jpeg) | Martin Schneider | [Github](https://github.com/martinschnder) | [Portfolio](team/Martin.md) | +| ![](team/Karishma.png) | Karishma | [Github](https://github.com/karishma-t) | [Portfolio](team/karishma-t.md) | +| ![](team/Barbara_image.JPG) | Barbara Chong | [Github](https://github.com/barbaracwx) | [Portfolio](team/barbaracwx.md) | +| ![](team/HaoZhi.png) | Teo Hao Zhi | [Github](https://github.com/TeoHaoZhi) | [Portfolio](team/teohaozhi.md) | +| ![](team/Azfarul.JPG) | Azfarul Matin | [Github](https://github.com/azfarulmatin) | [Portfolio](team/Azfarul.md) | + + diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 64e1f0ed2b..cc7779fb9c 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -2,37 +2,805 @@ ## Acknowledgements -{list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well} +1. Reference to AB-3 Developer Guide + +- [Source](https://se-education.org/addressbook-level3/DeveloperGuide.html#proposed-undoredo-feature) +- Used as template to structure this Developer Guide ## Design & implementation -{Describe the design and implementation of the product. Use UML diagrams and short code snippets where applicable.} + + +The architecture diagram given above explains the high level design of the application. The diagram depicts the key +component of the application that enables it to provide its functionalities. + +Majority of the app's work is done by the following components + +- Login System : Handles user authentication before enabling app usage +- Ui : Asks for user input by handling output messages +- Parser : Makes sense of user input +- Commands : List of various commands +- CommandResult : Execution of various commands + +The section below will explain in more detail the design considerations, implementations and rationale of the various +components listed above. + +--- + +### Login System Component + +--- + +The login system component seeks to authenticate and login existing users or register a new user. + +#### Design considerations + +- There must be a way to check and verify users with a master list +- The search for existing users username and password must be fast +- Master list must be stored separately on hard drive of machine + +#### Implementation and rationale + +The login system class works in the following way. Upon booting up the application, a txt file +containing a current list of existing users will be loaded into a users attribute within the class in the form of a hash +table.When a user attempts to login to their account, the entered username and password is checked against +the current list of users in the hashtable. If the username and password matches, the user is logged in. + +As for registering new users, newly inputted username and password will be saved to the users attribute and this +pair of username and password is then appended to the txt file containing current users. The updated user list will be +loaded into the users attribute when the application is booted up again. + +The login system class uses the below methods to achieve its functionality + +- `authenticateUserChoice()` -Decides whether the user chooses to register or login +- `newUserCreator()` -Creates a new user for future login +- `loginExistingUser()` -Login existing user +- `loadExistingUsers()` -Load existing users into hashtable for reference + +Given below is an example of how the login system class works. + +When the user first launches the programme, the Stocker object will be instantiated. The object will +invoke its own `run()` method which will call its own `start()` method. The Stocker object then instantiates a +new UI object which displays the login message by invoking `showLoginMessage()` method. At this point, Stocker object +will also instantiate a new login system object. + +The login system object will invoke its own `run()` method to begin the login process. This method begins by loading +existing users into the users attribute of the login system class by `loadExistingusers()` method. it then invokes +`authenticateUserChoice()` to receive an input from the user to whether register or login a user. Based on the input of +the user, either `newUserCreator()` is launched or `loginExistingUser()` methods will be called to register or login a +user. + +The following sequence diagram shows how the login system class works when the program is launched. + + + +Another consideration that was considered was the situation that the user maliciously edited the users txt file to +prevent it from loading properly into the app when it is booted up. In that case the list of old users would be deleted +and the user would have to register as a new user to access the system once again. + +--- + +### Parser Component + +--- + +The Parser component is responsible for interpreting user input and converting it into executable commands. It plays a +critical role in bridging the user interface (UI) and the command execution components of the application. + +#### Design considerations + +- **User Input Parsing:** The Parser must effectively break down user input into its constituent parts, such as the + command + keyword and any associated arguments. + +- **Command Recognition:** The Parser must recognize the specific command the user intends to execute. This involves + matching + the command keyword to a predefined set of commands. + +- **Arguments Extraction:** For commands that require additional information, the Parser should correctly extract and + format + arguments, ensuring they are ready for command execution. + +- **Error Handling:** In cases where the input does not match any recognized command or has formatting errors, the + Parser + should generate appropriate error messages. + +#### Implementation and rationale + +The Parser class is designed to handle these considerations through a well-structured parsing process. Here's how it +works: + +1. Splitting User Input: The Parser takes the full user input and splits it into two parts: the command word and the + remaining arguments. + +2. Command Recognition: It matches the command word to a predefined set of commands. If a valid command is recognized, + it + proceeds to the next step. + +3. Arguments Extraction: Depending on the specific command, the Parser may further parse and extract arguments. For + instance, for the "AddCommand," it extracts drug-related details like name, expiry date, serial number, and quantity. + +4. Command Creation: The Parser creates an instance of the appropriate Command class, passing along any required + arguments. + This encapsulates the user's request in an executable command object. + +5. Error Handling: If the user input does not match any recognized command or has formatting errors, the Parser + generates + an "IncorrectCommand" with an error message, providing feedback to the user. + +By structuring the parsing process this way, the application ensures that user input is correctly interpreted and +translated into executable commands for the subsequent phases of the application. + +The parser class uses the below method to achieve its functionality + +- `parseCommand(String userInput)` - This method takes the user's input as a parameter and processes it to identify the + command keyword and any associated arguments. It then recognizes the specific command and prepares it for execution. + +Given below is an example of how the login system class works. + +Suppose a user enters the command `add /n Paracetamol /d 2023-12-31 /s ABC123 /q 100`. The Parser first splits this +input +into the command word "add" and the arguments. It then recognizes the "add" command, extracts the drug details (name, +expiry date, serial number, and quantity), and creates an instance of the "`AddCommand`" with these details. If the user +enters an invalid command or incorrect formatting, the Parser provides feedback to guide the user, ensuring a seamless +interaction between the user and the application. + +The Parser is a crucial component that forms the bridge between user intentions and the core functionality of the stock +management system. + +The following sequence diagram shows how the parser class works when the program is running. + + + +--- + +### Command Class + +--- +The `Command` class serves as a foundational component of the stock management system, acting as a bridge between user +requests and the core functionality of the application. This class is extended by various concrete command classes, each +representing a specific action that a user can perform within the system. + +#### Key Components + +1. **Inventory**: This represents the collection of drugs and their quantities in stock. + +2. **SalesList**: It maintains records of sales transactions. + +3. **Cart**: The user's shopping cart, where they can add and remove items. + +4. **VendorsList**: This contains information about vendors and suppliers. + +#### Design and Purpose + +- **Data Access**: It provides access to essential data structures and components of the system, including the inventory + of + drugs, sales records, the user's shopping cart, and the list of vendors. +- **Abstraction**: It acts as an abstract command template, defining a common interface for all concrete command classes + to + implement. +- **Execution**: It enforces the execution of commands through its abstract execute method, which returns a result that + may be + specific to the particular command. +- **Data Initialization**: The class enables the initialization of data dependencies through the setData method, + ensuring that + commands have access to the necessary data for their execution. + +#### Implementation and Rationale + +Prior to execution, the `setData` method is used to provide the required data dependencies to the command. The +extensibility of the `Command` class allows the addition of new commands as the application evolves. Each new command +class extends the Command class and implements its own execute method, providing flexibility for incorporating new +features and functionalities. + +Given below is an example of how the Command class works. + +Suppose a user adds a new drug to the inventory, using the `AddDrugCommand`. + +First, the application sets the necessary data dependencies by invoking the `setData` method. It +provides access to the inventory, sales records, the user's cart, and vendor information. +The `AddDrugCommand` class implements the execute method. Within this method, the logic for adding a new drug +to the inventory is defined. The command verifies the drug details, quantity, and other relevant data. It also ensures +that the drug meets the required criteria. +Upon successful execution, the `AddDrugCommand` class returns a result, which may be a confirmation message such +as "New drug added to the inventory". In case of any issues during execution, an +appropriate error message is returned. + +The following sequence diagram shows how the command class works when the program is running. + + +--- + +### CommandResult Class + +--- + +The `CommandResult` class is a crucial part of the Stocker application, responsible for providing feedback and results +to the user after executing various commands. It contains a feedback message to describe the outcome of the command +execution, as well as an optional list of relevant elements produced by the command. + +**Design Considerations** +The design of the `CommandResult` class considers the following aspects: + +1. **Feedback Message:** The class stores a feedback message to inform the user about the outcome of the executed + command. + +2. **Relevant Elements:** For commands that produce a list of relevant elements, the `CommandResult` can store this list + for display. + +**Implementation and Rationale** + +The `CommandResult` class is implemented with two constructors and methods to access relevant elements and construct +feedback messages. + +- `CommandResult(String feedbackToUser)`: This constructor is used when there are no relevant elements to be included in + the result. It sets the feedback message. + +- `CommandResult(String feedbackToUser, List relevantElements)`: This constructor is used when the command produces a + list of relevant elements (e.g., a list of drugs). It sets both the feedback message and the list of relevant + elements. + +- `getRelevantElements()`: This method returns an optional list of relevant elements. It can be used to check if + relevant elements are present. + +- `getFeedbackToUser()`: This method returns the feedback message as a string. + +- `getFeedbackToUserWithElements()`: This method constructs a feedback message that includes the relevant elements. It + formats the list of elements with serial numbers (if applicable) and includes the feedback message. + +**Example Usage** + +The `CommandResult` class is used throughout the Stocker application to provide feedback to the user after executing +commands. For example, when a user issues a `list` command, the `CommandResult` includes a list of drugs produced by the +command along with the success message. The feedback message is then displayed to the user. + +The following sequence diagram shows how the Command Result function works. + + + +--- + +### Main data structures + +--- + +#### Implementation + +##### Drug + +The Drug class is a basic class that holds information related to drug stock management for users. It contains the +product name, its serial number, its quantity, expiry date and price. These are the most necessary inputs needed, to +allow for proper distribution, selling or storing of the drug. + +##### Inventory + +The Inventory class is used to keep track of the quantity of product in stock. The hash map seemed to be the most +appropriate data structure to match a product id to a quantity and a product entity which are encapsulated in a " +StockEntry" class. + +##### Cart + +The Cart class is used to represent an ongoing transaction : to perform a sale, the vendor can add different products +with their respective quantities in a cart which will be deducted from the inventory at the checkout. +To represent this, we chose to use an arraylist of "CartEntry" classes which represent a product/quantity tuple. + +##### SalesList + +The SalesList is used to represent every past sales in order to create some statistics and reports. This class is only a +list of subclasses representing validated carts. + +##### Description + +The Description class is used to keep track of the descriptions for various drugs. It is a simple class that can be +customised by the user to either store drugs' usages, specific instructions or more. It utilises a static map for the +association between drug names and their descriptions. However, these drugs are not related to the existing drugs in +the inventory. + +##### Vendors + +The VendorsList class and VendorSupplyList class is used to keep track of all information related to the vendors who +supply the drugs. The VendorsList uses an arrayList to store the vendor names, and the VendorSupplyList manages and +stores the association between the vendor names and their respective supplies using the HashMap. +However, the supply list is only associated with the VendorsList, and not the drug class, as it is a catalogue more +relevant to the buying and re-ordering of drugs from vendors. + +--- + +## Commands + +--- + +## 1. Find Function + +The "Find" function is designed to enable users to search for specific drugs in the inventory using either the drug's +name or the drugs expiry date. This component assists in locating and retrieving relevant drugs efficiently. + +**Design Considerations** +The design of the "Find" function takes into account the following considerations: + +1. **Search Criteria:** The function must provide users with the ability to specify each criteria, such as keywords or + attributes, to filter the items they are looking for. +2. **Search Speed:** To enhance user experience, the search process should be fast and responsive, ensuring that users + receive search results quickly. + +**Implementation and Rationale** + +The "Find" function is implemented as follows: + +- **User-Defined Search Criteria:** Users provide search criteria, such as keywords, to define what they + are looking for. The "Find" function processes these criteria to locate relevant items. + +- **Search Algorithm:** A robust search algorithm is employed to efficiently scan through the list of items and identify + those + that match the specified criteria. + +- **Result Presentation:** The function displays the search results, presenting users with a list of items that meet the + search criteria, allowing them to quickly identify the items they are interested in. + +- **User-Friendly Interface:** The "Find" function is integrated into the user interface, making it easily accessible + and intuitive for users to perform searches. + +- **Alternative Consideration:** During the design process, alternative approaches to searching are evaluated to ensure + the + most effective and user-friendly method is implemented. + +The "Find" function offers a valuable way for users to narrow down their searches, find specific items of interest, and +enhance the usability of the application. + +**Function Methods** + +The "Find" function includes the following method to achieve its functionality: + +- `execute()` - This method is responsible for executing the "Find" command, searching for drugs that match the + user-specified keyword within the inventory. +- It returns a `CommandResult` containing the outcome of the command execution, + which includes a success message and a list of found StockEntry objects. + +**Example Usage** + +To illustrate how the "Find" function works, consider the following example usage: + +1. **User Input:** The user initiates the "Find" command by typing something like the following: + +`find /n panadol` - This command instructs the program to search for drugs in the inventory based on the name criteria +and the keyword "panadol." + +`find /d 12/03/2026` - This command instructs the program to search for drugs in the inventory based on the expiry date +criteria and the keyword "12/03/2026." + +`find /s PARC3189` - This command instructs the program to search for drugs in the inventory based on the serial number +criteria and the keyword "PARC3189." + +2. **Method Execution:** The `execute()` method within the "FindCommand" class is called. It takes the provided keyword + and criterion as input. + +3. **Search Process:** The method processes the search by iterating through the list of `StockEntry` objects in the + inventory. + For each entry, it checks if the `matches` method returns `true`, which means that the drug name or expiry date + contains the given keyword. +4. **Building Results:** As matching entries are found, they are added to a list called `foundEntries`. +5. **Result Display:** The `CommandResult` is prepared, containing a success message (e.g., "Listed all drugs with the + keyword + in the inventory.") and the list of found `StockEntry` objects. +6. `User Feedback:` The result is then displayed to the user, showing a list of drugs in the inventory that match the + specified keyword. + +The following sequence diagram shows how the Find Command function works. + + + +--- + +## 2. ListCommand + +The `ListCommand` is responsible for listing all drugs in the inventory. This command retrieves the list of drugs from +the inventory and provides it as part of the command result. If the inventory is empty, it informs the user that the +inventory has no drugs. + +### Design Considerations + +- **User-Friendly Listing:** The primary goal of the `ListCommand` is to provide a user-friendly way to list all drugs + in the inventory, enhancing the user's experience in accessing inventory information. + +- **Data Presentation:** The design considers how to present the list of drugs in a clear and organized manner to + provide valuable information to the user. +- **Performance:** The implementation should be optimized to list the inventory efficiently, even if it contains a large + number of drugs. + +### Implementation + +The `ListCommand` is implemented as follows: + +- **Retrieving Drug List:** The command retrieves the list of drugs from the inventory using the `getAllDrugs` method. + +- **Handling Empty Inventory:** It checks if the list of drugs is empty. If the inventory is empty, it returns a + user-friendly message indicating that the inventory is empty. + +- **Listing Drugs:** If the inventory contains drugs, the command constructs a success message and includes the list of + drugs in the command result. + +- **User-Friendly Presentation:** The implementation ensures that the list of drugs is presented in a clear and + organized format, including relevant details such as drug names, quantities, and other attributes. + +- **Optimized Performance:** To enhance user experience, the command is designed to list the inventory efficiently, + ensuring that users receive search results quickly. + +### Function Methods + +The `ListCommand` includes the following method to achieve its functionality: + +- `execute()`: This method is responsible for executing the `ListCommand`, listing all drugs in the inventory. It checks + the inventory, prepares a user-friendly result message, and returns a `CommandResult` containing the outcome of the + command execution, which includes a success message and the list of found `Drug` objects. + +### Example Usage + +To illustrate how the `ListCommand` works, consider the following example usage: + +1. **User Input:** The user initiates the `ListCommand` by entering the following command: + +2. **Method Execution:** The `execute()` method within the "ListCommand" class is called. + +3. **Inventory Check:** The method checks the inventory to retrieve the list of drugs. + +4. **Result Building:** If the inventory contains drugs, the method constructs a success message (e.g., "Listed all + drugs in the inventory.") and includes the list of drugs with relevant details. + +5. **User Feedback:** The result is displayed to the user, showing a clear and organized list of drugs in the inventory. + +The "ListCommand" enhances the user's ability to access inventory information efficiently and is designed to handle +various inventory sizes while providing a user-friendly experience. + +The following sequence diagram shows how the List Command function works. + + + +--- + +## 3. ShowStockLevel Command + +This command allows users to generate a report displaying the +stock levels of drugs. This report is sorted in ascending order based on the quantity of each drug, providing a clear +overview of the available inventory. + +### Design Considerations + +- **Stock Level Report**: The primary objective of this command is to create a stock level report, which shows the + quantity of + drugs available, allowing users to assess the stock levels efficiently. +- **Sorting**: The report is designed to be sorted by quantity in ascending order to make it easier for users to + identify + drugs with lower quantities. + +### Implementation + +The `ShowStockLevelCommand` is implemented to create a report of drug stock levels sorted in ascending order by +quantity. +It retrieves and sorts the list of stock entries, returning the sorted list in a CommandResult if the inventory is not +empty. If the inventory is empty, it returns a message indicating that. + +--- + +## 4. Delete Command + +The "Delete" function is designed to enable users to remove specific drugs from the inventory based on the drug's name, +to remove drugs they no longer need, fully depleted or discontinued. + +**Design Considerations** + +1. **User-Specified Drug Name:** The function allows users to specify the drug name they want to delete from the + inventory. + +2. **Data Integrity:** It ensures that the deletion operation maintains the integrity of the inventory data structure, + updating it correctly. + +**Implementation and Rationale** + +This method is executed when the delete command is invoked. First, it attempts to delete a drug from the inventory +using the inventory.deleteDrug(this.keyToDelete) method. If successful, it retrieves the deleted entry. +If the drug is successfully deleted, it returns a success message. +If the drug is not found (i.e., a DrugNotFoundException is thrown), it returns a failure message. + +--- + +## 5. Help Command + +The command is responsible for showing users a list of all possible commands. + +**Design Considerations** + +The command was designed to print out and show how to use a list of all possible commands in a neat and concise way. + +**Implementation and Rationale** + +Command will use java's system out to print out all required information with a blank line in between for clarity. + +--- + +## 6. saveDrugs Command + +The saveDrugs command was made as a means to backup user entered drug data into the hard drive of the computer to ensure +previously entered data is saved and accessable whenever the app is launched. + +### Design Considerations + +The save command had to be implemented in a way to enable direct writing of files onto the hard drive and a function had +to be made to load said file back into the drug inventory upon starting the application. + +### Implementation + +There is a method to access the drugs within the inventory class. a separate method from the inventory class would then +write the contents of these drugs back to the txt file for saving. This is depicted by the sequence diagram shown below. + + + +Upon booting up the system, a method from the inventory class goes through the contents of the txt file and copies it to +the inventory drug list. + +The implementation of this class also considered the possibility of the user accidentally editing the saved drug list +txt file causing it to become unable to be loaded into the app. In that case the previous drug list will be deleted and +the drugs have to be added again into the drug list. + +--- + +## 7. SetThreshold Command + +This command allows users to specify a threshold +quantity for a particular drug in the inventory, aiding in better management of stock levels. + +### Design Considerations + +This command enable users to set a specific threshold quantity for a drug. +The threshold quantity serves as a reference point, indicating the minimum quantity of a drug that should trigger a +restock or reorder. + +### Implementation + +The default threshold for all drugs is initially set at 100. If a user decides to modify this threshold for a specific +drug, the new threshold will replace the default value for that particular drug. + +--- + +## 8. ListThreshold Command + +This command enables users to retrieve a list +of all drugs in the inventory, along with their associated threshold levels. + +### Design Considerations + +The storage capacity should be size adjustable based on the quantity of items, specifically drugs, and their +corresponding threshold levels added to it. + +### Implementation + +In order to attain the adjustable storage based on number of items, an ArrayList was used as additional drugs can be +appended to the ArrayList whenever a new entry is required. + +--- + +## 9. addVendor Command + +The command was made to add vendors to a list of vendors so as to have access to it when needed. + +### Design Considerations + +The storage must be size adjustable based on the number of objects, in this case vendors placed into it. + +### Implementation + +In order to attain the adjustable storage based on number of objects, an ArrayList was used as additional vendors can be +appended to the ArrayList whenever a new entry is required. + +--- + +## 10. deleteVendor Command + +It is designed to remove vendors from the list of vendors, ensuring that the system remains organized and up to date. + +### Design Considerations + +The storage must be size adjustable based on the number of objects, in this case vendors removed from it. + +### Implementation + +In order to attain the adjustable storage based on number of objects, an ArrayList was used as vendors can be +removed from the ArrayList. + +--- + +## 11. listVendors Command + +The command was made to list all vendors being tracked by the system in a neat way to the user + +### Design Considerations + +The possibility of an empty list had to be considered + +### Implementation + +The list of vendors could be printed by using streams to efficiently collect and print out the information of vendors + +--- + +## 12. addVendorSupply Command + +This method adds a drug to a vendor's supply list in the inventory management system, to track +what vendors supply what products. + +### Design Considerations + +This method checks if the specified vendor exists and, if so, adds the drug to their supply list. However, it does not +check if the drug already exists in the inventory system. Not only does this serve the intended purpose of a catalogue +for potential buying or reordering, but it helps with separation of concerns. It is only concerned with a vendor's +supply list and adding to it, and not the existing drug inventory. + +### Implementation + +The `AddVendorSupplyCommand` class extends a generic `Command` and implements the command pattern. It includes the +`execute` method, which first checks the existence of the specified vendor, utilizing case-insensitive comparison for +robustness. It then verifies if the drug is not already present in the vendor's supply list. If conditions are met, +the command adds the drug using the `addDrugToVendor` method from `VendorSupplyList`. The `VendorSupplyList` class +employs a static `Map>` named `vendorSuppliedDrugs` to store associations between vendors and the +drugs they supply. The `addDrugToVendor` method uses `computeIfAbsent` to ensure that each vendor has an associated +drug list, preventing null pointer issues. + + + +--- + +## 13. Checkout Command + +The Checkout Command allows users to remove items from the current cart and record the corresponding sales transactions. This command helps manage the checkout process efficiently and maintain accurate records of sales. + +### Design Considerations +- Ensure that the command handles scenarios where the cart is empty or there are no matching items in the inventory. +- Calculate the total cost of the checked-out items to provide users with a clear understanding of the transaction. + +### Implementation +In the execute method of the CheckOutCommand class: +- Check if the current cart is empty. If it is, return a message indicating that the cart is empty, and the checkout process cannot proceed. +- If the cart is not empty, create a temporary holder to store the sold items, which include the serial number, quantity, and selling price. +- For each item in the current cart, retrieve the corresponding StockEntry from the inventory. +- Calculate the total cost of the item based on the selling price and quantity. +- Add the sold item to the temporary holder, and update the total cost. +- Create a new Cart containing the sold items and add it to the SalesList. +- Return a message to the user indicating the successful checkout and the total cost of the transaction. + +The following sequence diagram illustrates how the Checkout Command function works: + + + +--- + +## 14. ListSales Command + +The ListSalesCommand is designed to list all sales transactions tracked by the system. This command provides users with a clear and organised view of the sales data, making it easier to review and manage sales transactions. + +### Design Considerations +The command should account for scenarios where there are no sales transactions to display. +The output should be presented in a user-friendly and organized format for better user experience. + +## Implementation + +In the execute method of the ListSalesCommand class: +- Retrieve the list of sales transactions from the system. +- Check if there are any sales transactions to display. If the list is empty, return a message indicating that there are no sales. +- If sales transactions are available, process and format the data for presentation. +- Organise and present the sales data in a structured format, including details such as serial numbers, quantities, and selling prices for each transaction. +- Ensure that the sales data is neatly formatted and clearly presented to the user. + +The following sequence diagram shows how the listSales Command function works. + + + +--- + +## 15. SaveSales Command + +The SaveSalesCommand is responsible for saving the current sales data to an external file. This file serves as a record-keeping mechanism and a reference for past sales transactions. + +### Design Considerations +The command should handle the process of writing sales data to an external file. +It should ensure data integrity and include error handling in case of write failures. + +### Implementation + +In the execute method of the SaveSalesCommand class: +- Check if there are any sales transactions to save. If the list is empty, return a message indicating that there are no sales to save. +- If sales transactions exist, write the data to a specified file in a structured and well-organized format. +- Implement error handling to address any potential file writing failures. +- Confirm the successful save operation by returning a message indicating that the sales data has been saved. + +The following sequence diagram shows how the saveSales Command function works. + + +--- + +## 16. AddDescription Command + +The `AddDescriptionCommand` is responsible for adding drug descriptions to the `Description` class. This command is +an integral part of the command pattern employed in the application, providing a modular approach to managing +less critical drug-related information . + +### Design Considerations +The command should manage the associations between the description and drugs properly, and ensure that the descriptions +can be updated if needed. However, it does not access the drug class directly similar to the vendor supply commands, as +this is intended as a catalogue. Moreover, it adheres to the principle of separation of concerns better. + +### Implementation +The command requires two parameters during instantiation: the drug name (`drugName`) and its corresponding description +(`drugDescription`). +The `execute` method initiates the addition of the drug description by calling the static method `addDescription` +from the `Description` class. It converts the drug name and description to lowercase for case-insensitive handling. +To update the description, users can call this command again, and the description will show the most recent addition. +Upon successful execution, the command returns a `CommandResult` with a formatted success message indicating the drug +name and description that have been added. + +The following diagram shows how the addDescription Command function works. + + + +--- ## Product scope + +--- + ### Target user profile -{Describe the target user profile} +- Works in the field of drug distribution, such as pharmacies and doctors' offices. +- Manages a large inventory of pharmaceuticals with varying details (expiration dates, manufacturers, storage + conditions). +- Prefers desktop applications for their work. +- Proficient in fast typing. +- Favors typing over mouse interactions. +- Comfortable using Command Line Interface (CLI) applications for efficiency. +- Requires password-protected access to sensitive patient healthcare information. ### Value proposition -{Describe the value proposition: what problem does it solve?} +**Stocker** is designed to cater to the specific needs of drug distributors by offering the following benefits: + +1. Quick access to real-time inventory information. +2. Efficient tracking of incoming stock. +3. Categorization of drugs based on various labels. +4. A prioritized list of urgently needed restocks for timely replenishment. +5. Assistance in generating comprehensive reports for stock turnover analysis. +6. Secure access through individual user accounts to safeguard patient healthcare data. +7. Enhanced user experience for experienced professionals who prefer keyboard commands and CLI interactions for seamless + stock management. ## 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| +Priorities: High (must have) - \* \* _, Medium (nice to have) - _ _, Low (unlikely to have) - _ -## Non-Functional Requirements +| Priority | Version | As a ... | I want to ... | So that I can ... | +|----------|---------|----------------------|------------------------------------------------------|----------------------------------------------------------------| +| \* \* \* | v1.0 | Pharmacist | Add drugs to track what drugs are available in stock | Reduce manual errors | +| \* \* \* | v1.0 | Pharmacist | Remove drugs to track what are no longer in used | Ensure compliance | +| \* \* \* | v1.0 | Receptionist | View a list of products of that category | Easily obtain an overview of the products | +| \* \* \* | v1.0 | First-time user | See a list of all available actions | Better understand how to use the application | +| \* \* \* | v1.0 | Inventory Staff | Find a specific drug currently in the system | Check up its details and quantities specifically | +| \* \* \* | v1.0 | user | Have a way to login to the system | Access the system only if i am allowed to | +| \* \* | v2.0 | System Administrator | Perform regular backup of inventory database | Safeguard against data loss and system failure | +| \* \* \* | v2.0 | Receptionist | Add vendors supplying drugs into the system | Keep track of what vendors i am working with | +| \* \* \* | v2.0 | Receptionist | View a list of vendors | Easily access a list of contacts for current and future orders | +| \* \* \* | v2.0 | Inventory Staff | Add and view vendors' supply lists | Keep track of what each vendors' supply | +| \* \* \* | v2.0 | Inventory Staff | Find which drugs are supplied by which vendors | Easily access a list of vendors to contact for drug reordering | +| \* \* | v2.0 | Pharmacist | Add and view descriptions of various drugs | Easily access usage of products for quick reference | +| \* \* | v2.0 | Receptionist | Add drugs to cart and checkout | facilitate the selling of those drugs to clients | +| \* \* | v2.0 | Receptionist | View total price of drugs in cart | Easily keep track of what clients would pay and the revenue | +| \* \* \* | v2.0 | Inventory Staff | Get alerts for low stock | Replenish the drugs in low quantity | +| \* \* \* | v2.0 | Manager | View past sales of drugs | Keep track of drug turnover | -{Give non-functional requirements} -## Glossary -* *glossary item* - Definition +## Non-Functional Requirements + +1. Should work on any mainstream OS as long as it has Java 11 or above installed. -## Instructions for manual testing +2. Should be able to hold up to 1000 drug entries without a noticeable sluggishness in performance for typical usage. -{Give instructions on how to do a manual product testing e.g., how to load sample data to be used for testing} +3. A user with above-average typing speed for regular English text (i.e. not code, not system admin commands) should be + able to accomplish most of the tasks faster using commands than using the mouse. diff --git a/docs/README.md b/docs/README.md index bbcc99c1e7..d319c383c9 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,6 +1,9 @@ -# Duke +# Stocker -{Give product intro here} +Stocker is a desktop app that will provide quick access to currently available stock, +track incoming stock and expiration dates, and categorize drugs based on different labels. +It is optimized for use via a Command Line Interface (CLI). If you can type fast, Stocker +can get your inventory management tasks done faster than traditional GUI apps. Useful links: * [User Guide](UserGuide.md) diff --git a/docs/UML Diagrams/AddDescriptionDiagram.png b/docs/UML Diagrams/AddDescriptionDiagram.png new file mode 100644 index 0000000000..8229f3378c Binary files /dev/null and b/docs/UML Diagrams/AddDescriptionDiagram.png differ diff --git a/docs/UML Diagrams/AddDescriptionDiagram.puml b/docs/UML Diagrams/AddDescriptionDiagram.puml new file mode 100644 index 0000000000..e0a900bd34 --- /dev/null +++ b/docs/UML Diagrams/AddDescriptionDiagram.puml @@ -0,0 +1,38 @@ +@startuml + +hide footbox +actor User +participant ":AddDescriptionCommand" as AddDescriptionCommand +participant ":CommandResult" as CommandResult +participant ":Description" as Description + + +User -> AddDescriptionCommand: initiate AddDescription +Activate AddDescriptionCommand + +AddDescriptionCommand -> AddDescriptionCommand : Convert to lowercase + +AddDescriptionCommand -> Description: addDescription(drug, description) +Activate Description + +Description --> CommandResult: generate success message +Activate CommandResult + +AddDescriptionCommand --> User: return success message +Deactivate CommandResult + +User -> AddDescriptionCommand: initiate AddDescription with invalid data +Activate AddDescriptionCommand + +AddDescriptionCommand -> Description: addDescription(InvalidDrug, InvalidDescription) +Activate Description + +Description --> CommandResult: generate error message +Activate CommandResult + +AddDescriptionCommand --> User: return error message +Deactivate CommandResult + +Deactivate Description +Deactivate AddDescriptionCommand +@enduml diff --git a/docs/UML Diagrams/AddVendorSupplyDiagram.png b/docs/UML Diagrams/AddVendorSupplyDiagram.png new file mode 100644 index 0000000000..d3dd249f65 Binary files /dev/null and b/docs/UML Diagrams/AddVendorSupplyDiagram.png differ diff --git a/docs/UML Diagrams/AddVendorSupplyDiagram.puml b/docs/UML Diagrams/AddVendorSupplyDiagram.puml new file mode 100644 index 0000000000..0adeeb2e21 --- /dev/null +++ b/docs/UML Diagrams/AddVendorSupplyDiagram.puml @@ -0,0 +1,40 @@ +@startuml + +hide footbox +actor User +participant ":AddVendorSupplyCommand" as AddVendorSupplyCommand +participant ":VendorSupplyList" as VendorSupplyList +participant ":VendorsList" as VendorsList +participant ":CommandResult" as CommandResult + +User -> AddVendorSupplyCommand: initiate AddVendorSupply +Activate AddVendorSupplyCommand + +AddVendorSupplyCommand -> VendorsList: getVendorEntries() +Activate VendorsList + +VendorsList --> AddVendorSupplyCommand: vendor entries +Deactivate VendorsList + +AddVendorSupplyCommand -> VendorsList: getName() +Activate VendorsList + +VendorsList --> AddVendorSupplyCommand: vendor existence check result +Deactivate VendorsList +AddVendorSupplyCommand -> VendorSupplyList: containsDrug() +Activate VendorSupplyList + +VendorSupplyList --> AddVendorSupplyCommand: containsDrug result +Deactivate VendorSupplyList + +AddVendorSupplyCommand -> VendorSupplyList: addDrugToVendor() +Activate VendorSupplyList + +VendorSupplyList --> CommandResult: generateSuccessMessage() +Activate CommandResult + +AddVendorSupplyCommand --> User: return success message +Deactivate CommandResult + +Deactivate AddVendorSupplyCommand +@enduml diff --git a/docs/UML Diagrams/ArchitectureDiagram.puml b/docs/UML Diagrams/ArchitectureDiagram.puml new file mode 100644 index 0000000000..b08135f7a9 --- /dev/null +++ b/docs/UML Diagrams/ArchitectureDiagram.puml @@ -0,0 +1,25 @@ +@startuml + +title Architecture Diagram + +skinparam component { +BackgroundColor lightblue +} + + +[Parser] +[Commands] +[CommandResult] +[LoginSystem and Ui] as interactor +interface "user" + +user -[hidden]- Parser +CommandResult -[hidden]- Parser + +interactor --> Parser : Makes sense of input +Parser -> Commands : Searches for inputted command +Commands -> CommandResult : Executes relevant command +CommandResult -> interactor : Return control +user -[#red]-> interactor : Logs in or Register + +@enduml \ No newline at end of file diff --git a/docs/UML Diagrams/Architecture_Diagram.png b/docs/UML Diagrams/Architecture_Diagram.png new file mode 100644 index 0000000000..d35427d97b Binary files /dev/null and b/docs/UML Diagrams/Architecture_Diagram.png differ diff --git a/docs/UML Diagrams/CheckoutCommandDiagram.png b/docs/UML Diagrams/CheckoutCommandDiagram.png new file mode 100644 index 0000000000..e4e41e82d4 Binary files /dev/null and b/docs/UML Diagrams/CheckoutCommandDiagram.png differ diff --git a/docs/UML Diagrams/CheckoutCommandDiagram.puml b/docs/UML Diagrams/CheckoutCommandDiagram.puml new file mode 100644 index 0000000000..1394a74d0d --- /dev/null +++ b/docs/UML Diagrams/CheckoutCommandDiagram.puml @@ -0,0 +1,46 @@ +@startuml +hide footbox +actor User +participant ":CheckOutCommand" as CheckOutCommand +participant ":Cart" as Cart +participant ":StockEntry" as StockEntry +participant ":Drug" as Drug +participant ":CartEntry" as CartEntry +participant ":SalesList" as SalesList +participant ":CommandResult" as CommandResult + +User -> CheckOutCommand: execute() +activate User +activate CheckOutCommand + +alt Cart is not empty + CheckOutCommand -> Cart: getCurrentCart() + activate Cart + Cart -> Cart: Retrieve current cart + Cart -> Cart: Create temporary holder (List) + Cart -> StockEntry: Get StockEntry for each entry + activate StockEntry + loop over entries + activate Drug + StockEntry -> Drug: Get Drug + activate Drug + Drug -> Drug: Get SellingPrice + Drug --> StockEntry + StockEntry --> CartEntry: Create CartEntry with quantity and selling price + deactivate Drug + deactivate StockEntry + deactivate CartEntry + end + Cart -> SalesList: Add temporary holder to sold items + Cart -> CommandResult: Create result message + deactivate Cart + Cart --> User: Message with total cost + else + Cart -> CommandResult: Cart is empty + Cart --> User: Message failure +end + +User --> CheckOutCommand: CommandResult +deactivate User +deactivate CheckOutCommand +@enduml diff --git a/docs/UML Diagrams/CommandResultDiagram.png b/docs/UML Diagrams/CommandResultDiagram.png new file mode 100644 index 0000000000..62ae5212ab Binary files /dev/null and b/docs/UML Diagrams/CommandResultDiagram.png differ diff --git a/docs/UML Diagrams/CommandResultDiagram.puml b/docs/UML Diagrams/CommandResultDiagram.puml new file mode 100644 index 0000000000..a7ea63d29d --- /dev/null +++ b/docs/UML Diagrams/CommandResultDiagram.puml @@ -0,0 +1,20 @@ +@startuml +title CommandResult Sequence Diagram + +actor User +participant Command +participant CommandResult + +User -> Command: Execute Command +activate Command + +Command -> CommandResult: Create CommandResult +activate CommandResult + +CommandResult --> Command: Return CommandResult +deactivate CommandResult + +Command -> User: Return CommandResult +deactivate Command + +@enduml \ No newline at end of file diff --git a/docs/UML Diagrams/FindCommandArchitectureDiagram.png b/docs/UML Diagrams/FindCommandArchitectureDiagram.png new file mode 100644 index 0000000000..0c6b80f9bb Binary files /dev/null and b/docs/UML Diagrams/FindCommandArchitectureDiagram.png differ diff --git a/docs/UML Diagrams/FindCommandArchitectureDiagram.puml b/docs/UML Diagrams/FindCommandArchitectureDiagram.puml new file mode 100644 index 0000000000..66f496c434 --- /dev/null +++ b/docs/UML Diagrams/FindCommandArchitectureDiagram.puml @@ -0,0 +1,26 @@ +@startuml FindCommandArchitectureDiagram + +!define LOGIC_COLOR #3333C4 + +!define ARROW_STYLE [-[#LOGIC_COLOR]->] + +actor User as U +package "Your Application" { + [User Interface] as UI + [FindCommand] as FindCmd + [Inventory] as Inv + [StockEntry] as Entry + [CommandResult] as CmdResult +} + +U -down-> UI: Initiates 'Find' Command +UI -down-> FindCmd: Invokes 'FindCommand' +UI -down-> Inv: Accesses 'Inventory' +FindCmd -down-> Entry: Iterates Through Entries +Entry -down-> Entry: Checks Each Entry +Entry --> FindCmd: Match/No Match +FindCmd --> CmdResult: Prepares Result +CmdResult -up-> UI: Returns Result +CmdResult -up-> U: Displays Search Results + +@enduml diff --git a/docs/UML Diagrams/FindCommandDiagram.png b/docs/UML Diagrams/FindCommandDiagram.png new file mode 100644 index 0000000000..818bea3678 Binary files /dev/null and b/docs/UML Diagrams/FindCommandDiagram.png differ diff --git a/docs/UML Diagrams/FindCommandDiagram.puml b/docs/UML Diagrams/FindCommandDiagram.puml new file mode 100644 index 0000000000..e58fb298f2 --- /dev/null +++ b/docs/UML Diagrams/FindCommandDiagram.puml @@ -0,0 +1,22 @@ +@startuml FindCommandDiagram + +!define LOGIC_COLOR #3333C4 +!define LOGIC_COLOR_T1 #7777DB + +hide footbox +actor User +participant ": FindCommand" as FindCmd LOGIC_COLOR_T1 +participant ": Inventory" as Inventory LOGIC_COLOR + +User -> FindCmd: Execute 'Find' Command +activate FindCmd +User <-- FindCmd: CommandResult +FindCmd -> Inventory: Retrieve Stock Entries +activate Inventory +Inventory --> FindCmd: List of Stock Entries + +FindCmd -> FindCmd: Process Entries +FindCmd --> User: Display Search Results + + +@enduml diff --git a/docs/UML Diagrams/ListCommandDiagram.png b/docs/UML Diagrams/ListCommandDiagram.png new file mode 100644 index 0000000000..e4530ad2a2 Binary files /dev/null and b/docs/UML Diagrams/ListCommandDiagram.png differ diff --git a/docs/UML Diagrams/ListCommandDiagram.uml.puml b/docs/UML Diagrams/ListCommandDiagram.uml.puml new file mode 100644 index 0000000000..0b7103b9dd --- /dev/null +++ b/docs/UML Diagrams/ListCommandDiagram.uml.puml @@ -0,0 +1,26 @@ +@startuml ListCommandDiagram + +!define LOGIC_COLOR #3333C4 +!define LOGIC_COLOR_T1 #7777DB + +hide footbox +actor User +participant ":ListCommand" as ListCmd LOGIC_COLOR_T1 +participant ":Inventory" as Inventory LOGIC_COLOR + +User -> ListCmd: Execute 'List' Command +activate User +activate ListCmd + +User <-- ListCmd: CommandResult +ListCmd -> Inventory: Retrieve Stock Entries +activate Inventory +Inventory --> ListCmd: List of Stock Entries + +ListCmd -> ListCmd: Process Entries +deactivate Inventory +ListCmd --> User: Display List Results +deactivate User +deactivate ListCmd + +@enduml diff --git a/docs/UML Diagrams/ListSalesCommand.png b/docs/UML Diagrams/ListSalesCommand.png new file mode 100644 index 0000000000..65e93ce764 Binary files /dev/null and b/docs/UML Diagrams/ListSalesCommand.png differ diff --git a/docs/UML Diagrams/ListSalesCommand.puml b/docs/UML Diagrams/ListSalesCommand.puml new file mode 100644 index 0000000000..9b25b9a8cd --- /dev/null +++ b/docs/UML Diagrams/ListSalesCommand.puml @@ -0,0 +1,25 @@ +@startuml +hide footbox +actor User +participant ":ListSalesCommand" as ListSalesCommand +participant ":SalesList" as SalesList +participant ":CommandResult" as CommandResult + +User -> ListSalesCommand: execute() +activate User +activate ListSalesCommand + +alt SalesList is not empty + ListSalesCommand -> SalesList: Get sales data + activate SalesList + SalesList -> ListSalesCommand: List of sales + deactivate SalesList + ListSalesCommand -> CommandResult: List of sales + else + ListSalesCommand -> CommandResult: SalesList is empty +end + +User -> ListSalesCommand: CommandResult +deactivate User +deactivate ListSalesCommand +@enduml diff --git a/docs/UML Diagrams/ParserDiagram.png b/docs/UML Diagrams/ParserDiagram.png new file mode 100644 index 0000000000..2e20ac7f7c Binary files /dev/null and b/docs/UML Diagrams/ParserDiagram.png differ diff --git a/docs/UML Diagrams/ParserDiagram.puml b/docs/UML Diagrams/ParserDiagram.puml new file mode 100644 index 0000000000..3ae5c88271 --- /dev/null +++ b/docs/UML Diagrams/ParserDiagram.puml @@ -0,0 +1,26 @@ +@startuml +hide footbox +actor User +participant "User Input" as UserInput +participant ": Parser" as Parser +participant ": Command" as Command +participant ": CommandResult" as CommandResult + + +User -> UserInput: Enter command +UserInput -> Parser: parseCommand(userInput) +activate Parser +Parser -> Command: execute(model) +deactivate Parser +activate Command +Create CommandResult +Command -> CommandResult +activate CommandResult +CommandResult --> Command +deactivate CommandResult +Command --> User + + + + +@enduml diff --git a/docs/UML Diagrams/SaveCommandDiagram.puml b/docs/UML Diagrams/SaveCommandDiagram.puml new file mode 100644 index 0000000000..4df92ab201 --- /dev/null +++ b/docs/UML Diagrams/SaveCommandDiagram.puml @@ -0,0 +1,48 @@ +@startuml +'https://plantuml.com/sequence-diagram + +hide footbox +actor User +Participant ":SaveDrugsCommand" as SaveCommand +Participant ":CommandResult" as CommandResult +Participant ":Inventory" as inventory +Participant ":Storage" as storage + + +Create SaveCommand +activate SaveCommand +User -> SaveCommand + + +Create CommandResult + +SaveCommand -> CommandResult +activate CommandResult + +Create inventory +CommandResult -> inventory +activate inventory +inventory -> inventory : getStockEnteries() +activate inventory +deactivate inventory + + +Create storage +inventory -> storage +activate storage +deactivate inventory + +loop inventory.size() times + +storage -> storage :appendToFile() +activate storage +deactivate storage +end +deactivate storage + +CommandResult --> User: Message_Success +deactivate CommandResult +deactivate SaveCommand + + +@enduml diff --git a/docs/UML Diagrams/SaveDrugsCommandDiagram.png b/docs/UML Diagrams/SaveDrugsCommandDiagram.png new file mode 100644 index 0000000000..dd1d366b7d Binary files /dev/null and b/docs/UML Diagrams/SaveDrugsCommandDiagram.png differ diff --git a/docs/UML Diagrams/SaveSalesCommand.png b/docs/UML Diagrams/SaveSalesCommand.png new file mode 100644 index 0000000000..5de5292960 Binary files /dev/null and b/docs/UML Diagrams/SaveSalesCommand.png differ diff --git a/docs/UML Diagrams/SaveSalesCommand.puml b/docs/UML Diagrams/SaveSalesCommand.puml new file mode 100644 index 0000000000..2afa27978b --- /dev/null +++ b/docs/UML Diagrams/SaveSalesCommand.puml @@ -0,0 +1,29 @@ +@startuml +hide footbox +actor User +participant ":SaveSalesCommand" as SaveSalesCommand +participant ":SalesList" as SalesList +participant ":CommandResult" as CommandResult +participant ":Storage" as Storage + +User -> SaveSalesCommand: execute() +activate User +activate SaveSalesCommand + +alt SalesList is not empty + SaveSalesCommand -> SalesList: Get sales data + activate SalesList + SalesList -> Storage: Save sales data to file + activate Storage + Storage -> SalesList: Success message + deactivate Storage + deactivate SalesList + SaveSalesCommand -> CommandResult: Success message + else + SaveSalesCommand -> CommandResult: SalesList is empty +end + +User -> SaveSalesCommand: CommandResult +deactivate User +deactivate SaveSalesCommand +@enduml diff --git a/docs/UML Diagrams/StockerToLoginSystem.png b/docs/UML Diagrams/StockerToLoginSystem.png new file mode 100644 index 0000000000..1f917498e5 Binary files /dev/null and b/docs/UML Diagrams/StockerToLoginSystem.png differ diff --git a/docs/UML Diagrams/StockerToLoginSystem.puml b/docs/UML Diagrams/StockerToLoginSystem.puml new file mode 100644 index 0000000000..3cd44ebab6 --- /dev/null +++ b/docs/UML Diagrams/StockerToLoginSystem.puml @@ -0,0 +1,65 @@ + @startuml +'https://plantuml.com/sequence-diagram + +hide footbox +actor User +Participant ":Stocker" as Stocker +Participant ":Ui" as Ui +Participant ":LoginSystem" as LoginSystem + +Create Stocker +User -> Stocker +activate Stocker +Stocker -> Stocker : run() +activate Stocker +Stocker -> Stocker :start() +Create Ui +Stocker -> Ui : Ui() +deactivate Stocker + +activate Ui +Ui -> Ui : showLoginMessage() +activate Ui +Ui -> Ui : ShowToUser() +activate Ui +deactivate Ui +Ui --> User : LoginMessage + +Create LoginSystem +Stocker -> LoginSystem :LoginSystem() +activate LoginSystem +LoginSystem -> LoginSystem : run() +activate LoginSystem +LoginSystem -> LoginSystem : loadExistingUsers() +activate LoginSystem +LoginSystem -> LoginSystem: authenticateUserChoice() +activate LoginSystem +User -> LoginSystem : register or login input + +alt Register + +LoginSystem -> LoginSystem : newUserCreator() +activate LoginSystem +LoginSystem --> Ui : showSuccessfulRegistrationMessage() + +else Login +LoginSystem ->LoginSystem : loginExistingUser() +activate LoginSystem +LoginSystem --> Ui: showSuccessfulLoginMessage() + +end +deactivate Ui +deactivate Ui +deactivate LoginSystem + +deactivate LoginSystem + +deactivate LoginSystem + +deactivate LoginSystem + +deactivate LoginSystem + +deactivate LoginSystem + +@enduml \ No newline at end of file diff --git a/docs/UserGuide.md b/docs/UserGuide.md index abd9fbe891..2eeef95fc0 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -1,42 +1,837 @@ # User Guide +## Table of contents + +- [Introduction](#introduction) +- [Quick Start](#quick-start) +- [Features](#features) +- [Usage](#usage) +- [FAQ](#faq) +- [Command Summary](#command-summary) + ## Introduction -{Give a product intro} +Stocker is a desktop app that will provide quick access to currently available stock, +track incoming stock and expiration dates, and categorize drugs based on different labels. +It is optimized for use via a Command Line Interface (CLI). If you can type fast, Stocker +can get your inventory management tasks done faster than traditional GUI apps. ## Quick Start -{Give steps to get started quickly} - 1. Ensure that you have Java 11 or above installed. -1. Down the latest version of `Duke` from [here](http://link.to/duke). +2. Down the latest version of `Stocker` from [here](https://github.com/AY2324S1-CS2113-T17-3/tp/releases). +3. Copy the absolute filepath to where the jar file is +4. Run the following JAR file with the following command + +``` +java -jar "" +``` + +## Features + +### Feature-Login System + +Authentication system that allows user to register as a user or login +as an existing user. + +### Feature-add + +Adds a drug into the inventory list. + +### Feature-delete + +Deletes a drug being tracked by the system. + +### Feature-list + +List all drug information that is being tracked by the system. + +### Feature-find + +Finds drugs using their name or expiry date. + +### Feature-help + +List all currently available commands in current version, their +uses and how to format them in the command line. + +### Feature-register + +Register a new user into the login system. + +### Feature-login + +Login an existing user into the system. + +### Feature-saveDrugs + +Save existing drugs from inventory list onto a txt file. +File is then used to update inventory list when stocker is ran. + +### Feature-saveSales + +Save existing checked out items onto a txt file. +File is then used to update sold drugs when stocker is ran. + +### Feature-listSales + +Displays the list of all sold drugs being tracked by the system. +It also shows the total amount earned so far. + +### Feature-add vendor + +Adds a vendor into a list tracked by the system. + +### Feature-delete vendor + +Deletes a vendor into a list tracked by the system. + +### Feature-list vendors + +Displays the name of all vendors being tracked by the system. + +### Feature-add vendor supply + +Adds a drug into a vendor's supply list to be tracked by the system. + +### Feature-list vendor supply + +Displays the list of all drugs being supplied by a particular vendor. + +### Feature-find vendor supply + +Displays the list of all vendors that supply a particular drug. + +### Feature-delete vendor supply + +Deletes a drug from a vendor's supply list. + +### Feature-add to cart + +Adds a drug to a virtual shopping cart if the requested quantity is available in the inventory. It keeps track of the +drugs dispensed to customers. + +### Feature-view cart + +List all drugs and their respective quantities in the current cart. It helps to review the drugs about to be dispensed. + +### Feature-checkout + +Finalizes the dispensing process by deducting the drugs and quantities in your cart from your inventory. It ensures that +the inventory accurately reflects the items dispensed. + +### Feature-add description + +Adds a drug's description into a list to be tracked by the system. + +### Feature-get description + +Retrieves the description of a particular drug. + +### Feature-list description + +Displays a list of all the descriptions for all corresponding drugs + +## Usage + +### `Login System`- Create new user or login existing user + +Login system is automatically launched at the start of the programme. + +**Registering a user** + +> Step 1 : Enter register to select option to register a user. +> +> `register` +> +> Step 2: Enter desired username and password. +> +> Upon successful creation, registration success message is observed + +Expected outcome: + +``` +Registration successful. +``` + +**Login an existing user** + +> Step 1 : Enter login to select option to login a user. +> +> `login` +> +> Step 2: Enter registered username and password. +> +> Upon successful creation, successful login message is observed. + +Expected outcome: + +``` +Login Successful. +``` + +### `add` - Adds drug into inventory list + +Adds a drug to be tracked by the system. + +- Each individual drug is uniquely identified by a serial number. +- If the input data includes the same serial number, name, and expiry date, it will simply increase the quantity of the + existing drug in the system. +- If the serial number is the same but either the name or expiry date is different, an error will be returned as the + system expects consistent information for the same serial number. +- If it has a different serial number, a new entry will be added to the inventory for + that drug. +- If none of the above conditions are met, the system will also add a new entry to the inventory. + +Note: + +- The expiry date should be entered in the format "dd/MM/yyyy" (e.g., 01/02/2024). +- The quantity should be between 1 and 999,999,999. +- The price (selling price) should be between 0.01 and 1,000.00 and can have up to 2 decimal places. +- The serial number should be in the format of three capital letters followed by three numbers (e.g., ABC123). + +Format: + +add /n DRUG_NAME /d EXPIRY_DATE /s SERIAL_NUMBER /q QUANTITY /p PRICE + +Example of usage: + +`add /n Panadol /d 01/02/2024 /s ABC123 /q 52 /p 19.90 +` + +Expected outcome: + +``` +|| New drug added in the inventory: Panadol +``` + +### `delete` - Deletes a drug being tracked by the system + +Deletes a drug being tracked by the system. + +Format: + +delete /s SERIAL_NUMBER + +Example of usage: + +`delete /s PANA002 +` + +Expected outcome: + +``` +|| Drug removed from inventory: Panadol +``` + +### `list` - List all drug information that is being tracked by the system + +List all drug information that is being tracked by the system. + +- If a drug has an expired expiry date at any time, it will be indicated with an (E) next to its entry. + +Format: + +list + +Example of usage: + +`list +` + +Expected outcome: + +``` +|| Listed all drugs in the inventory. +|| 1. Name: Panadol, Expiry date: 01/02/2023, Serial number: ABC123, Quantity: 52, Selling Price: 19.9 (E) +|| 2. Name: Dol, Expiry date: 01/02/2024, Serial number: DOL124, Quantity: 52, Selling Price: 19.9 +``` + +### `stockLevel` - List all drugs by quantity level in ascending order + +List all drugs by quantity level in ascending order. + +Format: + +stockLevel + +Example of usage: + +`stockLevel +` + +Expected outcome: + +``` +|| 1. Name: histamine, Expiry date: 09/05/2070, Serial number: HIS9447, Quantity: 10 +|| 2. Name: paracetamol, Expiry date: 01/07/2020, Serial number: PARC347, Quantity: 50 +|| 3. Name: Panadol, Expiry date: 04/07/2030, Serial number: PAN947, Quantity: 120 +|| +|| Stock Level Report (Sorted by Quantity) +``` + +### `find` - Finds drugs using their name or expiry date + +1. Finds drugs whose **names** contain any of the given keywords. + +Format: + +find /n panadol + +Example of usage: + +`find /n Panadol ` + +- The search is case-insensitive, meaning that "aspirin" will match "Aspirin." + +- Only the drug name is searched. + +Expected outcome: + +``` +|| 1. Name: panadol, Expiry Date: 01/02/2024, Serial number: PAN1234, Quantity: 120 + +|| Listed all drugs with the keyword in the inventory. +``` + +2. Finds drugs whose **expiry dates** contain any of the given keywords. + +Format: + +find /d DATE + +Example of usage: + +`find /d 01/02/2024` + +- Only the drug's expiry date is searched. + +Expected outcome: + +``` +|| 1. Name: panadol, Expiry Date: 01/02/2024, Serial number: PAN1234, Quantity: 120 + +|| Listed all drugs with the keyword in the inventory. +``` + +3. Finds drugs whose **serial number** contain any of the given keywords. + +Format: + +find /s PAN1234 + +Example of usage: + +`find /s KEYWORD` + +- Only the drug's serial number is searched. + +Expected outcome: + +``` +|| 1. Name: panadol, Expiry Date: 01/02/2024, Serial number: PAN1234, Quantity: 120 + +|| Listed all drugs with the keyword in the inventory. +``` + +### `help` - List currently available commands in current version, their uses and how to format them in the command line + +List all currently available commands in current version, +their uses and how to format them in the command line + +Format: + +help + +Example of usage: + +`help +` + +### `register` - Register a new user into the system + +Asks for user input for a username and password field which +will be used to create a new user for the system. + +Format: + +register + +Example of usage: + +`register` + +Expected outcome: + +``` +Registration Successful. +New User Created. +``` + +### `login` - login an existing user into the system + +Asks for user input for a username and password field which +will be used to check if such a user exists for the system +and log the user in. + +Format: + +login + +Example of usage: + +`login` + +Expected outcome: + +``` +Login Sucessful +Welcome Back! +``` + +### `saveDrugs` - save existing drugs onto hard drive of computer + +Saves existing drugs onto hard drive of computer. The txt file +is then used as reference to update drug inventory when stocker +is booted up. + +Format: + +saveDrugs -## Features +Example of usage: -{Give detailed description of each feature} +`saveDrugs` -### Adding a todo: `todo` -Adds a new item to the list of todo items. +Expected outcome: -Format: `todo n/TODO_NAME d/DEADLINE` +``` +Drugs successfully saved. +``` -* The `DEADLINE` can be in a natural language format. -* The `TODO_NAME` cannot contain punctuation. +### `saveSales` - Save the current sold items -Example of usage: +The saveSales command allows you to save the sales transactions onto your computer's hard drive. The saved file can serve as a historical record of sales transactions and can be used for reference in the future. -`todo n/Write the rest of the User Guide d/next week` +Example of usage: -`todo n/Refactor the User Guide to remove passive voice d/13/04/2020` +`saveSales` + +Expected outcome: + +``` +|| Sales data successfully saved. + +``` + +**Note:** It is recommended to use `saveSales` and `saveDrugs` before you exit the program to keep a record of your sales transactions. + +Example of usage: + +`saveSales` + +Expected outcome: + +``` +|| Sales data successfully saved. + +``` + +`saveDrugs` + +Expected outcome: + +``` +Drugs successfully saved. +``` + +### `addVendor` - adds a vendor into list of vendors being tracked by system + +- Adds a vendor to be tracked by the system. The entries are used to form a list of summarised vendors. +- The vendor's name is not case-sensitive, meaning 'Pfizer' and 'pfizer' are treated as the same. + +Format: + +addVendor /v VENDOR_NAME + +Example of usage: + +`addVendor /v Moderna` + +Expected outcome: + +``` +|| New vendor added into the vendors list: Moderna +``` + +### `deleteVendor` - deletes a vendor from list of vendors tracked by the system + +- Deletes a vendor to be tracked by the system. +- The vendor's name is not case-sensitive, meaning 'Pfizer' and 'pfizer' are treated as the same. + +Format: + +deleteVendor /v VENDOR_NAME + +Example of usage: + +`deleteVendor /v Moderna` + +Expected outcome: + +``` +|| Vendor deleted from the vendors list: Pfizer +``` + +### `listVendors` - list all vendors currently being tracked by the system + +Displays a list of all vendors currently being tracked by the system. + +Format: + +listVendors + +Example of usage: + +`listVendors` + +Expected outcome: + +``` +|| 1. Name : Moderna +|| +|| Listed all vendors in the list. +``` + +### `addVendorSupply` - Adds a drug into a vendor's supply list to be tracked by the system. + +- Adds a drug into a vendor's supply list to be tracked by the system + - Vendor must already be added into the system. + - If the drug already exists in the vendor's supply list, system will inform user + +Format: + +addVendorSupply /v VENDOR_NAME /n DRUG_NAME + +Example of usage: + +`addVendorSupply /v Moderna /n Paracetamol` + +Expected outcome: + +``` +|| New drug added to moderna's supply list: paracetamol +``` + +Note : As this serves as a catalogue for information related to vendor supply lists, drugs not currently tracked by the +inventory can be added into the supply list. + +### `listVendorSupply` - Displays the list of all drugs being supplied by a particular vendor. + +Displays the list of all drugs being supplied by a particular vendor. + +Format: + +listVendorSupply VENDOR_NAME + +Example of usage: + +`listVendorSupply /v Moderna` + +Expected outcome: + +``` +|| Drugs supplied by moderna: paracetamol, panadol +``` + +### `findVendorSupply` -Displays the list of all vendors that supply a particular drug. + +Displays the list of all vendors that supply a particular drug. + +Format: + +findVendorSupply /n DRUG_NAME + +Example of usage: + +`findVendorSupply /n paracetamol` + +Expected outcome: + +``` +|| Vendors supplying the drug paracetamol: moderna, apotheca +``` + +### `deleteVendorSupply` -Deletes a drug from a vendor's supply list. + +Removes a specified drug from a particular vendor's supply list. Not related to drug class. + +Format: + +deleteVendorSupply /v VENDOR_NAME /n DRUG_NAME + +Example of usage: + +`deleteVendorSupply /v moderna /n paracetamol` + +Expected outcome: + +``` +|| Drug 'paracetamol' removed from moderna's supply list +``` + +### `addToCart` - Adds drug into current cart + +Adds a drug in a specified quantity in the current cart. If the drug is past its expired date, it is unable to add to +cart + +Format: + +addToCart /s SERIAL_NUMBER /q QUANTITY + +Example of usage: + +`addToCart /s PANA01 /q 3 +` + +Expected outcome: + +``` +|| New drug added in the cart : Panadol +``` + +Expected outcome with expired drug: + +``` +|| This drug is expired. Unable to add to cart +``` + +### `viewCart` - Lists all the entries in the cart + +Lists all the added drugs, quantity, and total cost in the cart. + +Format: + +viewCart + +Example of usage: + +`viewCart +` + +Expected outcome: + +``` +|| Listed all the content of your cart. +|| 1. Name: Histamine, Quantity: 1, Selling Price: 15.9, Cost: 15.9 +|| 2. Name: Doliprane, Quantity: 2, Selling Price: 12.9, Cost: 25.8 +|| +|| Total Cost: 41.7 +``` + +### `checkout` - Checks out the current cart + +Empty the current cart and retrieve all the specified drugs and quantity from the inventory + +- Shows the total amount to be paid upon checkout +- If after checkout, the quantity of a specific drug falls below the set threshold level, it triggers an + alert. + +Format: + +checkout + +Example of usage: + +`checkout +` + +Expected outcome: + +``` +|| The current cart has been checked out. +|| +|| Total Cost: 15.9 +``` + +Expected outcome with alert: + +``` +|| ALERT! Panadol is below the threshold level +|| The current cart has been checked out. +``` + +### `setThreshold` - Set the threshold quantity for a drug + +Set the threshold quantity for a specific drug in your inventory. The threshold quantity is the minimum quantity of the +drug that you want to keep in stock. + +Format: + +setThreshold /s [Serial number] /tq [Threshold Quantity] + +Example of usage: + +`setThreshold /s DOL002 /tq 50 +` + +Expected outcome: + +``` +|| Threshold quantity set for Doliprane: 50 +``` + +### `listSales` - List the description of sold items saved + +The listSales command is designed to provide you with a clear and organized list of all sales transactions that have been tracked by the system. It helps you review and manage sales data efficiently. + +Example of usage: + +`listSales` + +Expected outcome: + +``` +|| Listed all items in the sales list. +|| 1. Name: Histamine, Serial Number: HIS526, Quantity: 2, Selling Price: 15.9, Cost: 31.8 +|| 2. Name: Doliprane, Serial Number: DOL123, Quantity: 2, Selling Price: 12.9, Cost: 25.8 +|| Total Cost: 57.6 + +``` + +Expected outcome if the list is empty: + +``` +|| The sales list is empty. + +``` + +### `listThreshold` - List all drugs and their threshold levels + +Retrieve a list of all drugs in your inventory and their corresponding threshold levels. The threshold level is the +minimum quantity of each drug that you want to keep in stock. By default, if the user hasn't specified individual +threshold levels for each +drug, the standard threshold level for all drugs is set at 100. + +Format: + +listThreshold + +Example of usage: + +`listThreshold +` + +Expected outcome: + +``` +|| 1. Doliprane: 50 +|| +|| Listed all drugs by threshold level in the inventory. + +``` + +### `addDescription` - Adds a drug's description into a list to be tracked by the system. + +Adds a drug's description into a list to be tracked by the system. + +Format: + +addDescription /n DRUG_NAME /desc DESCRIPTION + +Example of usage: + +`addDescription /n Panadol /desc Pain Relief +` + +Expected outcome: + +``` +|| New drug description added for Panadol: Pain Relief +``` + +Note : As this serves as a catalogue for information related to drug usage and their respective descriptions, +drugs not currently tracked by the inventory can be added with a description here. Both lists are separate. + +### `getDescription` - Retrieves the description of a particular drug. + +Retrieves the description of a particular drug. + +Format: + +getDescription /n DRUG_NAME + +Example of usage: + +`getDescription /n Panadol +` + +Expected outcome: + +``` +|| Pain Relief +``` + +### `listDescriptions` - Displays a list of all the descriptions for all corresponding drugs + +Displays a list of all the descriptions for all corresponding drugs + +Format: + +listDescriptions + +Example of usage: + +`listDescriptions +` + +Expected outcome: + +``` +|| List of Drug Descriptions: +|| Panadol: Pain Relief +|| Dolo: Stomache Discomfort +``` ## FAQ -**Q**: How do I transfer my data to another computer? +**Q**: Can i register with blank username and password -**A**: {your answer here} +**A**: No. Ensure your entries are not blank. -## Command Summary +**Q**: Why can i register and login again even after being logged in -{Give a 'cheat sheet' of commands here} +**A**: A user is able to login another user or can help to register a new user without rebooting the application + +**Q**: Why is my registration and login details not masked + +**A**: Masking of input is a security concern that will be developed on in future iterations + +## Command Summary -* Add todo `todo n/TODO_NAME d/DEADLINE` +- register : `register` +- login : `login` +- save : `saveDrugs` +- help : `help` +- add : `add /n DRUG_NAME /d EXPIRY_DATE /s SERIAL_NUMBER /q QUANTITY /p PRICE` +- delete : `delete /s SERIAL_NUMBER` +- list : `list` +- find : `find /n KEYWORD` or `find /d KEYWORD` +- add Vendor : `addVendor /v VENDOR_NAME` +- list Vendor : `listVendors` +- add Vendor Supply : `addVendorSupply /v VENDOR_NAME /n DRUG_NAME` +- list Vendor Supply : `listVendorSupply /v VENDOR_NAME` +- find Vendor Supply : `findVendorSupply /n DRUG_NAME` +- delete Vendor Supply : `deleteVendorSupply /v VENDOR_NAME /n DRUG_NAME` +- add description : `addDescription /n DRUG_NAME /desc DESCRIPTION` +- get description : `getDescription /n DRUG_NAME` +- list descriptions : `listDescriptions` +- list stock level : `stockLevel` +- add drug to cart : `addToCart /s SERIAL_NUMBER /q QUANTITY` +- checks out current cart : `checkout` +- view current cart items : `viewCart` +- save current checked out items : `saveSales` +- view all sold items : `listSales` +- set threshold quantity for a drug : `setThreshold /s SERIAL_NUMBER /tq THRESHOLD_QUANTITY` +- list all drugs and threshold levels : `listThreshold` +- bye : `bye` diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 0000000000..a35c22596b --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1 @@ +theme : jekyll-theme-Cayman diff --git a/docs/team/Azfarul.JPG b/docs/team/Azfarul.JPG new file mode 100644 index 0000000000..fc8fe4d789 Binary files /dev/null and b/docs/team/Azfarul.JPG differ diff --git a/docs/team/Azfarul.md b/docs/team/Azfarul.md new file mode 100644 index 0000000000..63718e33d8 --- /dev/null +++ b/docs/team/Azfarul.md @@ -0,0 +1,77 @@ +# Azfarul Matin - Project Portfolio Page + +## Project : Stocker + +Stocker is a Command Line Interface(CLI) app for drug inventory management purposes. +If you can type fast, it can get your inventory management tasks done faster than traditional +GUI apps. + +### Summary of Contributions + +-**Features implemented** + +1) **List** + * What it does: List all drug information that is being tracked by the system. + + +2) **Find** + * What it does: Finds drug from drug list by serial number + + +3) **Add** + * What it does: Adds the serial number and price property to the drugs + + +4) **viewCart** + * What it does: Adds the total amount to be paid in the cart + + +5) **Checkout** + * What it does: Calculates total price of drugs in cart + + +6) **SaveSales** + * What it does: Saves the items that are checked out into soldItems.txt + + +7) **ListSales** + * What it does: List the drugs sold that are saved + +-**Classes implemented** + +* `ListCommand` +* `FindCommand` +* `CheckoutCommand` +* `SaveSalesCommand` +* `ListSalesCommand` + +-**Contributions to the UG** + +Azfarul Matin added documentation for the following sections of +the user guide: +* [Add Command](https://ay2324s1-cs2113-t17-3.github.io/tp/UserGuide.html#find---finds-drugs-using-their-name-or-expiry-date:~:text=to%20the%20inventory.-,Note%3A,-The%20expiry%20date) +* [Find Command](https://ay2324s1-cs2113-t17-3.github.io/tp/UserGuide.html#find---finds-drugs-using-their-name-or-expiry-date:~:text=Finds%20drugs%20whose%20serial%20number%20contain%20any%20of%20the%20given%20keywords.) +* [saveSales Command](https://ay2324s1-cs2113-t17-3.github.io/tp/UserGuide.html#find---finds-drugs-using-their-name-or-expiry-date:~:text=saveSales%20%2D%20Save%20the%20current%20sold%20items) +* [listSaLes Command](https://ay2324s1-cs2113-t17-3.github.io/tp/UserGuide.html#find---finds-drugs-using-their-name-or-expiry-date:~:text=listSales%20%2D%20List%20the%20description%20of%20sold%20items%20saved) + +-**Contributions to the DG** + +* Azfarul Matin added documentation for the following section of + the developer guide: + * [List Command](https://ay2324s1-cs2113-t17-3.github.io/tp/DeveloperGuide#:~:text=Command%20function%20works.-,2.%20ListCommand,-The%20ListCommand%20is) + * [Checkout Command](https://ay2324s1-cs2113-t17-3.github.io/tp/DeveloperGuide#:~:text=13.%20Checkout%20Command) + * [saveSales Command](https://ay2324s1-cs2113-t17-3.github.io/tp/DeveloperGuide#:~:text=15.%20SaveSales%20Command) + * [listSales Command](https://ay2324s1-cs2113-t17-3.github.io/tp/DeveloperGuide#:~:text=14.%20ListSales%20Command) +* UML Diagrams + * [ListCommand Diagram](https://ay2324s1-cs2113-t17-3.github.io/tp/UML%20Diagrams/ListSalesCommand.png) + * [CheckoutCommand Diagram](https://ay2324s1-cs2113-t17-3.github.io/tp/UML%20Diagrams/CheckoutCommandDiagram.png) + * [SaveSalesCommand Diagram](https://ay2324s1-cs2113-t17-3.github.io/tp/UML%20Diagrams/SaveSalesCommand.png) + * [ListSalesCommand Diagram](https://ay2324s1-cs2113-t17-3.github.io/tp/UML%20Diagrams/ListSalesCommand.png) + +-**Contributions to team-based tasks** + +* Maintain issues and milestone tracker +* Updated portions of user/developer docs not specific to a feature eg: user stories, target user profile, + value proposition +* Kept up to date weekly deliverables during meetings +* Record and edit for the demo \ No newline at end of file diff --git a/docs/team/Barbara_image.JPG b/docs/team/Barbara_image.JPG new file mode 100644 index 0000000000..320ea67f5f Binary files /dev/null and b/docs/team/Barbara_image.JPG differ diff --git a/docs/team/HaoZhi.png b/docs/team/HaoZhi.png new file mode 100644 index 0000000000..4b818fdabc Binary files /dev/null and b/docs/team/HaoZhi.png differ diff --git a/docs/team/Karishma.png b/docs/team/Karishma.png new file mode 100644 index 0000000000..1218ae0960 Binary files /dev/null and b/docs/team/Karishma.png differ diff --git a/docs/team/Martin.jpeg b/docs/team/Martin.jpeg new file mode 100644 index 0000000000..1a16d8877f Binary files /dev/null and b/docs/team/Martin.jpeg differ diff --git a/docs/team/Martin.md b/docs/team/Martin.md new file mode 100644 index 0000000000..ee5c655587 --- /dev/null +++ b/docs/team/Martin.md @@ -0,0 +1,46 @@ +# Martin Schneider - Project Portfolio Page + +## Project : Stocker + +Stocker is a Command Line Interface(CLI) app for drug inventory management purposes. +If you can type fast, it can get your inventory management tasks done faster than traditional +GUI apps. + +### Summary of Contributions + +-**Code contributed** [Link to reposense contribution graph](https://nus-cs2113-ay2324s1.github.io/tp-dashboard/?search=&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code&since=2023-09-22&tabOpen=true&tabType=authorship&tabAuthor=martinschnder&tabRepo=AY2324S1-CS2113-T17-3%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false) + +-**Features implemented** + +- Command result system + - What it does : Provide a basic command interface to display various messages and lists to the user. +- Add command + - What it does : Adds a particular drugs with all parameters to the inventry +- Cart handling + - What it does : Allow a user (a vendor for example) to add various drugs in a temporary cart before checking it out and process a sale. + +-**Classes implemented** + +- `Command` +- `Parser` +- `CommandResult` +- `AddCommand` +- `AddToCartCommad` +- `ViewCartCommand` +- `CheckoutCommand` + +-**Contributions to the UG** + +- [Add Command](https://ay2324s1-cs2113-t17-3.github.io/tp/UserGuide.html#add---adds-drug-into-inventory-list) +- [Add to cart Command](https://ay2324s1-cs2113-t17-3.github.io/tp/UserGuide.html#addtocart---adds-drug-into-current-cart) + +-**Contributions to the DG** + +- [Main data structures](https://ay2324s1-cs2113-t17-3.github.io/tp/DeveloperGuide.html#main-data-structures),[Login System Component](https://ay2324s1-cs2113-t17-3.github.io/tp/DeveloperGuide.html#login-system-component),[Help Command](https://ay2324s1-cs2113-t17-3.github.io/tp/DeveloperGuide.html#4-help-command), [Save Command](https://ay2324s1-cs2113-t17-3.github.io/tp/DeveloperGuide.html#5-save-command), [addVendor Command](https://ay2324s1-cs2113-t17-3.github.io/tp/DeveloperGuide.html#6-addvendor-command), + +-**Contributions to team-based tasks** + +**Mentoring** + +- Clarified misconceptions on workflow and application architecture in the initial few weeks +- Provided some advices and reviews on other members implementation. diff --git a/docs/team/barbaracwx.md b/docs/team/barbaracwx.md new file mode 100644 index 0000000000..925680489d --- /dev/null +++ b/docs/team/barbaracwx.md @@ -0,0 +1,86 @@ +# Barbara - Project Portfolio Page + +## Project : Stocker +Stocker is a Command Line Interface(CLI) app for drug inventory management purposes. +If you can type fast, it can get your inventory management tasks done faster than traditional +GUI apps. + +### Summary of Contributions + +-**Code contributed** [Link to response contribution graph](https://nus-cs2113-ay2324s1.github.io/tp-dashboard/?search=&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code&since=2023-09-22&tabOpen=true&tabType=authorship&tabAuthor=Barbaracwx&tabRepo=AY2324S1-CS2113-T17-3%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false) + +- **Features implemented** + +1. **Find drug by name** + * What it does: Allows users to search for drugs by its name. + +2. **Find drug by date** + * What it does: Enables users to search for drugs by their expiration date. + +3. **List drugs by quantity level** + * What it does: Provides a list of drugs based on their current quantity, listed according to ascending order. + +4. **Set threshold level for each drug** + * What it does: Allows users to set a threshold quantity for each drug. + +5. **List threshold level for all drugs** + * What it does: Provides a list of threshold levels for all drugs. + +6. **Alert function when a drug is below the set threshold level** + * What it does: Sends alerts when a drug's quantity falls below the user-defined threshold. + +7. **Prevent Adding Expired Drugs to Cart** + * What it does: Displays an error alert when a user attempts to add an expired drug to their cart. + +8. **Indicate Expired Drugs in the List** + * What it does: Adds an (E) next to the entry of any drug with an expired expiration date. + +9. **Delete Vendor** + * What it does: Deletes a vendor from the vendors list. + + + +-**Classes implemented** +* `FindCommand` +* `ShowStockLevelCommand` +* `SetThresholdCommand` +* `ListThresholdCommand` +* `DeleteVendorCommand` + +-**Contributions to the UG** +* Barbara added documentation for the following sections of + the user guide: + * [Find Command](https://ay2324s1-cs2113-t17-3.github.io/tp/UserGuide.html#find---finds-drugs-using-their-name-or-expiry-date) + * [ShowStockLevel Command](https://ay2324s1-cs2113-t17-3.github.io/tp/UserGuide#:~:text=stockLevel%20%2D%20List%20all%20drugs%20by%20quantity%20level%20in%20ascending%20order) + * [SetThreshold Command](https://ay2324s1-cs2113-t17-3.github.io/tp/UserGuide.html#setthreshold---set-the-threshold-quantity-for-a-drug) + * [ListThreshold Command](https://ay2324s1-cs2113-t17-3.github.io/tp/UserGuide.html#listthreshold---list-all-drugs-and-their-threshold-levels) + * [DeleteVendor Command](https://ay2324s1-cs2113-t17-3.github.io/tp/UserGuide#:~:text=deleteVendor%20%2D%20deletes%20a%20vendor%20from%20list%20of%20vendors%20tracked%20by%20the%20system) + + +-**Contributions to the DG** +* Barbara added documentation for the following section of + the developer guide: + * [Command Class](https://ay2324s1-cs2113-t17-3.github.io/tp/DeveloperGuide.html#command-class) + * [Parser Class](https://ay2324s1-cs2113-t17-3.github.io/tp/DeveloperGuide.html#parser-component) + * [Find Command](https://ay2324s1-cs2113-t17-3.github.io/tp/DeveloperGuide.html#1-find-function) + * [ShowStockLevel Command](https://ay2324s1-cs2113-t17-3.github.io/tp/DeveloperGuide.html#3-showstocklevel-command) + * [SetThreshold Command](https://ay2324s1-cs2113-t17-3.github.io/tp/DeveloperGuide.html#7-setthreshold-command) + * [ListThreshold Command](https://ay2324s1-cs2113-t17-3.github.io/tp/DeveloperGuide.html#8-listthreshold-command) + * [DeleteVendor Command](https://ay2324s1-cs2113-t17-3.github.io/tp/DeveloperGuide.html#10-deletevendor-command) +* UML Diagrams + * [Sequence Diagram for Parser Class](https://ay2324s1-cs2113-t17-3.github.io/tp/UML%20Diagrams/ParserDiagram.png) + * [Sequence Diagram for Find Command](https://ay2324s1-cs2113-t17-3.github.io/tp/UML%20Diagrams/FindCommandDiagram.png) + +-**Contributions to team-based tasks** +* Maintain issues and milestone tracker +* Updated portions of user/developer docs not specific to a feature eg: user stories, target user profile, + value proposition +* Kept up to date weekly deliverables during meetings + +**Providing Assistance** +- Offered support by explaining concepts and providing help with code to group mates facing challenges. + + + + + diff --git a/docs/team/karishma-t.md b/docs/team/karishma-t.md new file mode 100644 index 0000000000..b9f9b6136e --- /dev/null +++ b/docs/team/karishma-t.md @@ -0,0 +1,87 @@ +# Karishma - Project Portfolio Page + +## Project : Stocker +Stocker is a Command Line Interface(CLI) app for drug inventory management purposes. +If you can type fast, it can get your inventory management tasks done faster than traditional +GUI apps. + +### Summary of Contributions + +-**Code contributed** - [Link to reposense contribution graph](https://nus-cs2113-ay2324s1.github.io/tp-dashboard/?search=&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code&since=2023-09-22&tabOpen=true&tabType=authorship&tabAuthor=karishma-t&tabRepo=AY2324S1-CS2113-T17-3%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false) + +-**Features implemented** +1) **Delete** + * What it does: Removes a drug from drug list by Serial Number + + +2) **Add Vendor Supply** + * What it does: Adds a new vendor to q vendor's supply list. + + +3) **Delete Vendor Supply** + * What it does: Deletes a drug from the vendor's supply list. + + +4) **List Vendor Supply** + * What it does: Lists the drugs supplied by a specific vendor. + + +5) **Find Vendor Supply** + * What it does: Lists all the vendors that supply a specific drug. + + +6) **Add Description** + * What it does: Adds a new description for a specific drug. + + +7) **Get Description** + * What it does: Retrieves the description of a specific drug. + + +8) **List Descriptions** + * What it does: Lists all the descriptions for all drugs + +-**Classes implemented** +* `DeleteCommand` +* `AddVendorSupplyCommand` +* `DeleteVendorSupplyCommand` +* `ListVendorSupplyCommand` +* `FindVendorSupplyCommand` +* `AddDescriptionCommand` +* `ListDescriptionsCommand` +* `GetDescriptionCommand` + +-**Contributions to the UG** +* Karishma added documentation for the following sections of + the user guide: + * [Delete Command](https://ay2324s1-cs2113-t17-3.github.io/tp/UserGuide.html#delete---Deletes-a-drug-being-tracked-by-the-system:~:text=the%20inventory%3A%20Panadol-,delete,-%2D%20Deletes%20a%20drug) + * [AddVendorSupplyCommand](https://ay2324s1-cs2113-t17-3.github.io/tp/UserGuide#:~:text=addVendorSupply%20%2D%20Adds%20a%20drug%20into%20a%20vendor%E2%80%99s%20supply%20list%20to%20be%20tracked%20by%20the%20system.) + * [DeleteVendorSupplyCommand](https://ay2324s1-cs2113-t17-3.github.io/tp/UserGuide#:~:text=deleteVendorSupply%20%2DDeletes%20a%20drug%20from%20a%20vendor%E2%80%99s%20supply%20list.) + * [ListVendorSupplyCommand](https://ay2324s1-cs2113-t17-3.github.io/tp/UserGuide#:~:text=listVendorSupply%20%2D%20Displays%20the%20list%20of%20all%20drugs%20being%20supplied%20by%20a%20particular%20vendor.) + * [FindVendorSupplyCommand](https://ay2324s1-cs2113-t17-3.github.io/tp/UserGuide#:~:text=findVendorSupply%20%2DDisplays%20the%20list%20of%20all%20vendors%20that%20supply%20a%20particular%20drug.) + * [AddDescriptionCommand](https://ay2324s1-cs2113-t17-3.github.io/tp/UserGuide#:~:text=addDescription%20%2D%20Adds%20a%20drug%E2%80%99s%20description%20into%20a%20list%20to%20be%20tracked%20by%20the%20system.) + * [ListDescriptionsCommand](https://ay2324s1-cs2113-t17-3.github.io/tp/UserGuide#:~:text=listDescriptions%20%2D%20Displays%20a%20list%20of%20all%20the%20descriptions%20for%20all%20corresponding%20drugs) + * [GetDescriptionCommand](https://ay2324s1-cs2113-t17-3.github.io/tp/UserGuide#:~:text=getDescription%20%2D%20Retrieves%20the%20description%20of%20a%20particular%20drug.) + +-**Contributions to the DG** +* Karishma added documentation for the following section of + the developer guide: + * [Delete Command](https://ay2324s1-cs2113-t17-3.github.io/tp/DeveloperGuide#:~:text=message%20indicating%20that.-,4.%20Delete%20Command,-The%20%E2%80%9CDelete%E2%80%9D%20function) + * [AddVendorSupplyCommand](https://ay2324s1-cs2113-t17-3.github.io/tp/DeveloperGuide#:~:text=12.%20addVendorSupply%20Command) + * [AddDescription Command](https://ay2324s1-cs2113-t17-3.github.io/tp/DeveloperGuide#:~:text=16.%20AddDescription%20Command) + * [User Stories](https://ay2324s1-cs2113-t17-3.github.io/tp/DeveloperGuide#:~:text=Priorities%3A%20High%20(must%20have)%20%2D%20*%20*%20_%2C%20Medium%20(nice%20to%20have)%20%2D%20_%20_%2C%20Low%20(unlikely%20to%20have)%20%2D%20) + + +* UML Diagrams + * [AddDescription Diagram](https://ay2324s1-cs2113-t17-3.github.io/tp/UML%20Diagrams/AddDescriptionDiagram.png) + * [AddVendorSupply Diagram](https://ay2324s1-cs2113-t17-3.github.io/tp/UML%20Diagrams/AddVendorSupplyDiagram.png) + +-**Contributions to team-based tasks** +* Maintain issues and milestone tracker +* Updated portions of user/developer docs not specific to a feature eg: user stories, target user profile, + value proposition +* Kept up to date weekly deliverables during meetings + + + + diff --git a/docs/team/teohaozhi.md b/docs/team/teohaozhi.md new file mode 100644 index 0000000000..0d6d11ba21 --- /dev/null +++ b/docs/team/teohaozhi.md @@ -0,0 +1,57 @@ +# Teo Hao Zhi - Project Portfolio Page + +## Project : Stocker +Stocker is a Command Line Interface(CLI) app for drug inventory management purposes. +If you can type fast, it can get your inventory management tasks done faster than traditional +GUI apps. + +### Summary of Contributions + +-**Code contributed** [Link to reposense contribution graph](https://nus-cs2113-ay2324s1.github.io/tp-dashboard/?search=&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code&since=2023-09-22&tabOpen=true&tabType=authorship&tabAuthor=TeoHaoZhi&tabRepo=AY2324S1-CS2113-T17-3%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false) + +-**Features implemented** +* Login system + * What it does : Authenticates user and allow entry into the system +* Help command + * What it does : Shows all available commands the application can run +* Data backup and saving to txt file + * What it does : Saves and backup current list of drugs into a txt file that is loaded back into system upon booting + up the app + +-**Classes implemented** +* `LoginSystem` +* `HelpCommand` +* `AddVendorCommand` +* `ListVendorCommand` +* `saveCommand` + +-**Contributions to the UG** + * Hao Zhi came up with the inital template for the user guide and added documentation for the following sections of + the user guide: + * [Login System](https://ay2324s1-cs2113-t17-3.github.io/tp/UserGuide.html#login-system--create-new-user-or-login-existing-user) + * [Help Command](https://ay2324s1-cs2113-t17-3.github.io/tp/UserGuide.html#help---list-currently-available-commands-in-current-version-their-uses-and-how-to-format-them-in-the-command-line) + * [SaveDrugs Command](https://ay2324s1-cs2113-t17-3.github.io/tp/UserGuide.html#savedrugs---save-existing-drugs-onto-hard-drive-of-computer) + * [addVendor Command](https://ay2324s1-cs2113-t17-3.github.io/tp/UserGuide.html#addvendor---adds-a-vendor-into-list-of-vendors-being-tracked-by-system) + * [listVendor Command](https://ay2324s1-cs2113-t17-3.github.io/tp/UserGuide.html#listvendors---list-all-vendors-currently-being-tracked-by-the-system) + + +-**Contributions to the DG** + * Hao Zhi came up with the inital template of the developer guide and added documentation for the following section of + the developer guide: + * [Design & implementation](https://ay2324s1-cs2113-t17-3.github.io/tp/DeveloperGuide.html#design--implementation),[Login System Component](https://ay2324s1-cs2113-t17-3.github.io/tp/DeveloperGuide.html#login-system-component),[Help Command](https://ay2324s1-cs2113-t17-3.github.io/tp/DeveloperGuide.html#5-help-command), [SaveDrugs Command](https://ay2324s1-cs2113-t17-3.github.io/tp/DeveloperGuide.html#6-savedrugs-command), [addVendor Command](https://ay2324s1-cs2113-t17-3.github.io/tp/DeveloperGuide.html#9-addvendor-command), + [listVendors Command](https://ay2324s1-cs2113-t17-3.github.io/tp/DeveloperGuide.html#11-listvendors-command) + * UML Diagrams + * [Architecture Diagram under Design & Implementation](https://ay2324s1-cs2113-t17-3.github.io/tp/UML%20Diagrams/Architecture_Diagram.png),[Login System Sequence Diagram](https://ay2324s1-cs2113-t17-3.github.io/tp/UML%20Diagrams/StockerToLoginSystem.png), + [SaveDrugs Command Sequence Diagram](https://ay2324s1-cs2113-t17-3.github.io/tp/UML%20Diagrams/SaveDrugsCommandDiagram.png) + +-**Contributions to team-based tasks** + * Set up GitHub team org/repo + * Maintain issues and milestone tracker + * Updated portions of user/developer docs not specific to a feature eg: FAQ of user guide, acknowledgements of + developer guide + * Kept up to date weekly deliverables during meetings + +**Mentoring** + * Clarified misconceptions on workflow and application architecture in the initial few weeks + + diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java deleted file mode 100644 index 5c74e68d59..0000000000 --- a/src/main/java/seedu/duke/Duke.java +++ /dev/null @@ -1,21 +0,0 @@ -package seedu.duke; - -import java.util.Scanner; - -public class Duke { - /** - * Main entry-point for the java.duke.Duke application. - */ - public static void main(String[] args) { - String logo = " ____ _ \n" - + "| _ \\ _ _| | _____ \n" - + "| | | | | | | |/ / _ \\\n" - + "| |_| | |_| | < __/\n" - + "|____/ \\__,_|_|\\_\\___|\n"; - System.out.println("Hello from\n" + logo); - System.out.println("What is your name?"); - - Scanner in = new Scanner(System.in); - System.out.println("Hello " + in.nextLine()); - } -} diff --git a/src/main/java/seedu/stocker/Stocker.java b/src/main/java/seedu/stocker/Stocker.java new file mode 100644 index 0000000000..32a89a15cb --- /dev/null +++ b/src/main/java/seedu/stocker/Stocker.java @@ -0,0 +1,109 @@ +package seedu.stocker; + +import seedu.stocker.authentication.LoginSystem; +import seedu.stocker.storage.Storage; +import seedu.stocker.ui.Ui; +import seedu.stocker.parser.Parser; +import seedu.stocker.commands.Command; +import seedu.stocker.commands.CommandResult; +import seedu.stocker.commands.ExitCommand; +import seedu.stocker.drugs.Inventory; +import seedu.stocker.drugs.SalesList; +import seedu.stocker.drugs.Cart; +import seedu.stocker.vendors.VendorsList; + +import java.io.IOException; + +import static seedu.stocker.common.Messages.MESSAGE_EXECUTION_FAILED; + +public class Stocker { + + private Ui ui; + private Inventory inventory; + private SalesList salesList; + private Cart currentCart; + private VendorsList vendorsList; + + public static void main(String[] launchArgs) { + new Stocker().run(); + } + + /** + * Runs Login System. + */ + public boolean startLogin() throws IOException { + LoginSystem system = new LoginSystem(); + system.run(); + + return system.loginStatus; + } + + + /** + * Runs the program until termination. + */ + public void run() { + start(); + runCommandLoopUntilExitCommand(); + exit(); + } + + /** + * Sets up the required objects, and prints the welcome message. + */ + private void start() { + try { + this.ui = new Ui(); + this.inventory = new Inventory(); + this.salesList = new SalesList(); + this.currentCart = new Cart(); + this.vendorsList = new VendorsList(); + Storage storage = new Storage(inventory); + storage.loadFileContents("drugs.txt"); + storage.loadSoldItems("soldItems.txt", salesList); + boolean checker = startLogin(); + assert checker; + ui.showWelcomeMessage(); + } catch (Exception e) { + ui.showInitFailedMessage(); + System.exit(0); + } + + } + + /** + * Prints the Goodbye message and exits. + */ + private void exit() { + ui.showGoodbyeMessage(); + System.exit(0); + } + + /** + * Reads the user command and executes it, until the user issues the exit command. + */ + private void runCommandLoopUntilExitCommand() { + Command command; + do { + String userCommandText = ui.getUserCommand(); + command = new Parser().parseCommand(userCommandText); + ui.showResultToUser(executeCommand(command)); + } while (!ExitCommand.isExit(command)); + } + + + /** + * Executes the command and returns the result. + * + * @param command user command + * @return result of the command + */ + private CommandResult executeCommand(Command command) { + try { + command.setData(inventory, salesList, currentCart, vendorsList); + return command.execute(); + } catch (IOException ioe) { + return new CommandResult<>(MESSAGE_EXECUTION_FAILED); + } + } +} diff --git a/src/main/java/seedu/stocker/authentication/LoginSystem.java b/src/main/java/seedu/stocker/authentication/LoginSystem.java new file mode 100644 index 0000000000..3dfa150768 --- /dev/null +++ b/src/main/java/seedu/stocker/authentication/LoginSystem.java @@ -0,0 +1,252 @@ +package seedu.stocker.authentication; + +import seedu.stocker.ui.Ui; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.FileWriter; +import java.io.FileReader; +import java.io.PrintWriter; +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Scanner; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Represents a login system used for authentication of users. + * User information is saved within a hashtable and uploaded + * into txt file for future reference. + */ +public class LoginSystem { + public boolean loginStatus; + private final Scanner in; + private Ui interactor; + private final HashMap users; + + + public LoginSystem() throws IOException { + users = new HashMap<>(); + loginStatus = false; + this.in = new Scanner(System.in); + interactor = new Ui(); + + File holder = new File("./users.txt"); + if (!holder.exists()) { + holder.createNewFile(); + } + } + + /** + * Returns user choice of whether they wish to register a new user + * or if they would want to login using an existing user. + * + * @return choice of user to login or register + */ + public String authenticateUserChoice() { + while (in.hasNextLine()) { + String choice = in.nextLine(); + if (choice.trim().equals("register")) { + return "register"; + } else if (choice.trim().equals("login")) { + return "login"; + } else { + interactor.showInvalidChoiceMessage(); + return authenticateUserChoice(); + } + } + return "error"; + } + + /** + * Creates a new user with input username and password from user. + * Username and password are saved into a txt file for future + * reference. + * + * @throws IOException if inappropriate output is entered. + */ + public void newUserCreator() throws IOException { + + interactor.showUsernameMessage(); + String username = in.nextLine(); + + while (username.trim().equals("")) { + interactor.showBlankNameMessage(); + username = in.nextLine(); + } + + while (username.trim().contains(":")) { + interactor.showInvalidLoginCharacterMessage(); + username = in.nextLine(); + } + + interactor.showPasswordMessage(); + String password = in.nextLine(); + + while (password.trim().equals("")) { + interactor.showBlankPasswordMessage(); + password = in.nextLine(); + } + + while (password.trim().contains(":")) { + interactor.showInvalidLoginCharacterMessage(); + password = in.nextLine(); + } + + assert (username.equals("") == false); + assert (password.equals("") == false); + + if (users.containsKey(username)) { + interactor.showUserAlreadyExistMessage(); + System.out.println(); + interactor.showEnterChoiceAgainMessage(); + + String reselect = authenticateUserChoice(); + if (reselect.equals("register")) { + + newUserCreator(); + + } else if (reselect.equals("login")) { + loginExistingUser(); + } + } else { + users.put(username, password); + interactor.showSuccessfulRegistrationMessage(); + loginStatus = true; + } + writeNewUserToFile(); + + } + + /** + * Login existing user by asking for username and password input + * from user. + * + * @throws IOException if inappropriate input is entered. + */ + public void loginExistingUser() throws IOException { + + + interactor.showUsernameMessage(); + String usernameInput = in.nextLine(); + + interactor.showPasswordMessage(); + String passwordInput = in.nextLine(); + + if (!users.containsKey(usernameInput)) { + interactor.showInvalidUsernameOrPasswordMessage(); + System.out.println(); + interactor.showEnterChoiceAgainMessage(); + + String reselect = authenticateUserChoice(); + if (reselect.equals("register")) { + newUserCreator(); + + } else if (reselect.equals("login")) { + loginExistingUser(); + } + + } else { + if (users.get(usernameInput).equals(passwordInput)) { + interactor.showSuccessfulLoginMessage(); + loginStatus = true; + } else { + interactor.showInvalidUsernameOrPasswordMessage(); + System.out.println(); + interactor.showEnterChoiceAgainMessage(); + + String reselect = authenticateUserChoice(); + if (reselect.equals("register")) { + newUserCreator(); + + } else if (reselect.equals("login")) { + loginExistingUser(); + } + } + } + + + } + + /** + * Writes new user creation into a txt file to save for future reference + * + * @throws IOException + */ + public void writeNewUserToFile() throws IOException { + BufferedWriter writer = new BufferedWriter(new FileWriter("./users.txt", true)); + + for (Map.Entry entry : + users.entrySet()) { + // put key and value separated by a colon + writer.write(entry.getKey() + ":" + + entry.getValue()); + // new line + writer.newLine(); + } + + writer.flush(); + writer.close(); + } + + /** + * Loads existing users from txt file into hash table + * for login system to use for authentication when + * user tries to login. + * + * @throws IOException if fail to read from txt file + */ + public void loadExistingUsers() throws IOException { + BufferedReader reader = new BufferedReader(new FileReader("./users.txt")); + String line; + + Pattern pattern = Pattern.compile( + "(.*):(.*)"); + + while ((line = reader.readLine()) != null) { + Matcher matcher = pattern.matcher(line); + if (matcher.matches()) { + String[] parts = line.split(":", 2); + if (parts.length >= 2) { + String key = parts[0]; + String value = parts[1]; + users.put(key, value); + } + } else { + System.out.println("Malicious changes were made, overwriting old user file," + + " please register new user to proceed"); + FileWriter fw = new FileWriter("./users.txt", false); + PrintWriter pw = new PrintWriter(fw, false); + pw.flush(); + pw.close(); + fw.close(); + users.clear(); + break; + } + } + + } + + /** + * Runs login system by loading user information into hash table + * and get input for user to check for authentication. + * + * @throws IOException if unable to read from txt file to + * load users + */ + public void run() throws IOException { + loadExistingUsers(); + interactor.showLoginMessage(); + String choice = authenticateUserChoice(); + if (choice.equals("register")) { + newUserCreator(); + + } else if (choice.equals("login")) { + loginExistingUser(); + } + + } + +} diff --git a/src/main/java/seedu/stocker/commands/AddCommand.java b/src/main/java/seedu/stocker/commands/AddCommand.java new file mode 100644 index 0000000000..dfe3ec83b7 --- /dev/null +++ b/src/main/java/seedu/stocker/commands/AddCommand.java @@ -0,0 +1,59 @@ +package seedu.stocker.commands; + +import seedu.stocker.drugs.Drug; +import seedu.stocker.drugs.StockEntry; + +/** + * Adds a drug into the inventory + */ +public class AddCommand extends Command { + + public static final String COMMAND_WORD = "add"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a new drug to the drug list. " + + "Parameters: NAME, EXPIRY DATE, SERIAL NUMBER, QUANTITY, PRICE" + System.lineSeparator() + + "Example: " + COMMAND_WORD + + " /n Doliprane /d 12/06/2035 /s ABC123 /q 52 /p 12.90"; + + public static final String MESSAGE_SUCCESS = "New drug added in the inventory: %1$s"; + public static final String MESSAGE_SUCCESS_EXISTING = "Increased the quantity of an existing drug with the same " + + "serial number"; + + private final String name; + private final String expiryDate; + private final double sellingPrice; + private final long quantity; + private final String serialNumber; + + public AddCommand(String name, String expiryDate, String serialNumber, Long quantity, double sellingPrice) { + this.name = name; + this.expiryDate = expiryDate; + this.sellingPrice = sellingPrice; + this.serialNumber = serialNumber; + this.quantity = quantity; + } + + @Override + public CommandResult execute() { + StockEntry entry = inventory.get(serialNumber); + + if (entry != null) { + Drug existingDrug = entry.getDrug(); + if (existingDrug.getName().equals(this.name) && existingDrug.getExpiryDate().equals(this.expiryDate)) { + // If the existing drug has the same name and expiry date, increment the quantity + entry.incrQuantity(this.quantity); + return new CommandResult<>(String.format(MESSAGE_SUCCESS_EXISTING, existingDrug.getName())); + } else { + return new CommandResult<>("A drug with the same serial number but different name " + + "or expiry date already exists."); + } + } + + // If no existing drug with the same name and expiry date is found, add a new entry + Drug newDrug = new Drug(this.name, this.expiryDate, this.sellingPrice); + inventory.addNewDrug(serialNumber, newDrug, quantity); + return new CommandResult<>(String.format(MESSAGE_SUCCESS, newDrug.getName())); + } + +} + diff --git a/src/main/java/seedu/stocker/commands/AddDescriptionCommand.java b/src/main/java/seedu/stocker/commands/AddDescriptionCommand.java new file mode 100644 index 0000000000..bb3d95296e --- /dev/null +++ b/src/main/java/seedu/stocker/commands/AddDescriptionCommand.java @@ -0,0 +1,45 @@ +package seedu.stocker.commands; + +/** + * Adds a drug description to the description list. + */ +public class AddDescriptionCommand extends Command { + + public static final String COMMAND_WORD = "addDescription"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a new description for a specific drug. " + + "Parameters: NAME, DESCRIPTION" + System.lineSeparator() + + "Example: " + COMMAND_WORD + " /n Panadol /desc Pain Relief "; + + public static final String MESSAGE_SUCCESS = "New drug description added for %1$s: %2$s"; + + private final String drugName; + private final String drugDescription; + + /** + * Creates an AddDescriptionCommand to add a drug description to the description list. + * + * @param drugName The name of the drug for which the description is added. + * @param drugDescription The description to be added for the drug. + */ + public AddDescriptionCommand(String drugName, String drugDescription ) { + this.drugName = drugName; + this.drugDescription = drugDescription; + } + + /** + * Executes the command to add the drug description. + * + * @return CommandResult indicating the success of adding the description. + */ + @Override + public CommandResult execute() { + String lowercaseDrugName = drugName.toLowerCase(); + String lowercaseDrugDescription = drugDescription.toLowerCase(); + + seedu.stocker.drugs.Description.addDescription(lowercaseDrugName, lowercaseDrugDescription); + return new CommandResult<>(String.format(MESSAGE_SUCCESS, lowercaseDrugName, lowercaseDrugDescription)); + + + } +} diff --git a/src/main/java/seedu/stocker/commands/AddToCartCommand.java b/src/main/java/seedu/stocker/commands/AddToCartCommand.java new file mode 100644 index 0000000000..763d654092 --- /dev/null +++ b/src/main/java/seedu/stocker/commands/AddToCartCommand.java @@ -0,0 +1,69 @@ +package seedu.stocker.commands; + +import seedu.stocker.drugs.CartEntry; +import seedu.stocker.drugs.StockEntry; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +/** + * Adds a certain quantity of one drug into the current cart + */ +public class AddToCartCommand extends Command { + + public static final String COMMAND_WORD = "addToCart"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Adds a new drug to the current cart. " + + "Parameters: SERIAL NUMBER, QUANTITY," + System.lineSeparator() + + "Example: " + COMMAND_WORD + + " /s ABC123 /q 2"; + + public static final String MESSAGE_SUCCESS = "New drug added in the current cart: %1$s"; + + private final String serialNumber; + private final long quantity; + + public AddToCartCommand(String serialNumber, long quantity) { + this.serialNumber = serialNumber; + this.quantity = quantity; + } + + @Override + public CommandResult execute() { + StockEntry entry = inventory.get(this.serialNumber); + if (entry == null) { + return new CommandResult<>("This drug is not in stock"); + } else { + String inputDate = entry.getDrug().getExpiryDate(); + LocalDate expiryDate = parseDate(inputDate); + LocalDate currentDate = LocalDate.now(); // Get the current date + + if (currentDate.isAfter(expiryDate)) { + return new CommandResult<>("This drug is expired. Unable to add to cart"); + } else if (entry.getQuantity() < this.quantity + currentCart.getEntryQuantity(this.serialNumber)) { + return new CommandResult<>("There is not enough stock on this drug."); + } else { + CartEntry cartEntry = currentCart.getEntryBySerialNumber(this.serialNumber); + if (cartEntry == null) { + currentCart.addEntry(this.serialNumber, this.quantity); + } else { + cartEntry.incrQuantity(this.quantity); + } + return new CommandResult<>(String.format(MESSAGE_SUCCESS, entry.getDrug().getName())); + } + } + } + + /** + * Helper method to parse a date string in "dd/mm/yyyy" format to a LocalDate. + * + * @param dateStr The input date string. + * @return The parsed LocalDate. + */ + private LocalDate parseDate(String dateStr) { + DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy"); + return LocalDate.parse(dateStr, inputFormatter); + } + + +} diff --git a/src/main/java/seedu/stocker/commands/AddVendorCommand.java b/src/main/java/seedu/stocker/commands/AddVendorCommand.java new file mode 100644 index 0000000000..f791b83191 --- /dev/null +++ b/src/main/java/seedu/stocker/commands/AddVendorCommand.java @@ -0,0 +1,45 @@ +package seedu.stocker.commands; + +import seedu.stocker.vendors.Vendor; + +/** + * Adds a vendor into the inventory + */ +public class AddVendorCommand extends Command { + + public static final String COMMAND_WORD = "addVendor"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a new vendor to the vendors list. " + + "Parameter: NAME" + System.lineSeparator() + + "Example: " + COMMAND_WORD + + " /v Moderna"; + + public static final String MESSAGE_SUCCESS = "New vendor added in the vendors list: %1$s"; + + private final Vendor toAdd; + + public AddVendorCommand(String name) { + this.toAdd = new Vendor(name); + } + + public boolean vendorAlreadyExist(String name) { + for (int i = 0; i < this.vendorsList.vendorArrayList.size(); i += 1) { + String vendorName = vendorsList.vendorArrayList.get(i).getName(); + + if (vendorName.equalsIgnoreCase(name)) { + return true; + } + } + return false; + } + + @Override + public CommandResult execute() { + if (vendorAlreadyExist(toAdd.getName())) { + return new CommandResult<>("No Duplicates allowed!"); + } + + this.vendorsList.addNewVendor(toAdd); + return new CommandResult<>(String.format(MESSAGE_SUCCESS, toAdd.getName())); + } +} diff --git a/src/main/java/seedu/stocker/commands/AddVendorSupplyCommand.java b/src/main/java/seedu/stocker/commands/AddVendorSupplyCommand.java new file mode 100644 index 0000000000..0c5dd3ca11 --- /dev/null +++ b/src/main/java/seedu/stocker/commands/AddVendorSupplyCommand.java @@ -0,0 +1,56 @@ +package seedu.stocker.commands; + +import seedu.stocker.vendors.VendorSupplyList; + +/** + * Adds a drug to a vendor's supply list. + */ +public class AddVendorSupplyCommand extends Command { + + public static final String COMMAND_WORD = "addVendorSupply"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a drug to a vendor's supply list. " + + "Parameters: VENDOR_NAME, DRUG_NAME" + System.lineSeparator() + + "Example: " + COMMAND_WORD + " /v Moderna /n Paracetamol"; + + public static final String MESSAGE_SUCCESS = "New drug added to %1$s's supply list: %2$s"; + public static final String MESSAGE_VENDOR_NOT_FOUND = "Vendor not found: %1$s"; + public static final String MESSAGE_DRUG_EXISTS = "The drug '%1$s' already exists in the vendor's supply list"; + + private final String vendorName; + private final String drugName; + + /** + * Creates an AddVendorSupplyCommand to add a drug to a vendor's supply list. + * + * @param vendorName The name of the vendor. + * @param drugName The name of the drug to be added to the vendor's supply list. + */ + public AddVendorSupplyCommand(String vendorName, String drugName) { + this.vendorName = vendorName; + this.drugName = drugName; + } + + /** + * Executes the command to add a drug to the specified vendor's supply list. + * + * @return CommandResult indicating the success of adding the drug or a message indicating the vendor was not found. + */ + @Override + public CommandResult execute() { + String lowercaseVendorName = vendorName.toLowerCase(); + String lowercaseDrugName = drugName.toLowerCase(); + + if (this.vendorsList.getVendorEntries().stream().anyMatch(vendor -> + vendor.getName().equalsIgnoreCase(lowercaseVendorName))) { + if (VendorSupplyList.containsDrug(lowercaseVendorName, lowercaseDrugName)) { + return new CommandResult<>(String.format(MESSAGE_DRUG_EXISTS, lowercaseDrugName)); + } else { + VendorSupplyList.addDrugToVendor(lowercaseVendorName, lowercaseDrugName); + return new CommandResult<>(String.format(MESSAGE_SUCCESS, lowercaseVendorName, lowercaseDrugName)); + } + } else { + return new CommandResult<>(String.format(MESSAGE_VENDOR_NOT_FOUND, lowercaseVendorName)); + } + } +} diff --git a/src/main/java/seedu/stocker/commands/CheckOutCommand.java b/src/main/java/seedu/stocker/commands/CheckOutCommand.java new file mode 100644 index 0000000000..2b54cfcef1 --- /dev/null +++ b/src/main/java/seedu/stocker/commands/CheckOutCommand.java @@ -0,0 +1,64 @@ +package seedu.stocker.commands; +import seedu.stocker.drugs.Cart; +import seedu.stocker.drugs.CartEntry; +import seedu.stocker.drugs.Drug; +import seedu.stocker.drugs.StockEntry; +import seedu.stocker.exceptions.DrugNotFoundException; + +import java.util.ArrayList; +import java.util.List; + +/** + * Remove a drug from inventory and add it into the sales list + */ +public class CheckOutCommand extends Command { + + public static final String COMMAND_WORD = "checkout"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Checks out current cart. " + + "Parameters:" + System.lineSeparator() + + "Example: " + COMMAND_WORD; + + public static final String MESSAGE_SUCCESS = "The current cart has been checked out."; + public static final String MESSAGE_FAILURE = "An error happened, the cart hasn't been checked out. "; + + public CheckOutCommand() { + } + + @Override + public CommandResult execute() { + try { + StringBuilder resultMessage = new StringBuilder(MESSAGE_SUCCESS + System.lineSeparator()); + int index = 1; + double totalCost = 0.0; + // Create a temporary holder for sold items + List soldItems = new ArrayList<>(); + for (CartEntry entry : currentCart.getCurrentCart()) { + String serialNumber = entry.getSerialNumber(); + long quantity = entry.getQuantity(); + StockEntry stockEntry = inventory.get(serialNumber); + if (stockEntry != null) { + Drug drug = stockEntry.getDrug(); + double sellingPrice = drug.getSellingPrice(); + double cost = sellingPrice * quantity; + totalCost += cost; + + // Add the sold item to the temporary holder + soldItems.add(new CartEntry(serialNumber, quantity, drug)); + } + } + + // Add the temporary holder to the sales list + salesList.addSale(new Cart(soldItems)); + + // Call the checkOut function to update inventory quantities + currentCart.checkOut(salesList, inventory); + + resultMessage.append(System.lineSeparator()).append("Total Cost: ").append(totalCost); + + return new CommandResult<>(resultMessage.toString()); + } catch (DrugNotFoundException e ) { + return new CommandResult<>(String.format(MESSAGE_FAILURE)); + } + } +} diff --git a/src/main/java/seedu/stocker/commands/Command.java b/src/main/java/seedu/stocker/commands/Command.java new file mode 100644 index 0000000000..972f799608 --- /dev/null +++ b/src/main/java/seedu/stocker/commands/Command.java @@ -0,0 +1,31 @@ +package seedu.stocker.commands; + +import seedu.stocker.drugs.Inventory; +import seedu.stocker.drugs.SalesList; +import seedu.stocker.drugs.Cart; +import seedu.stocker.vendors.VendorsList; + +import java.io.IOException; + +public abstract class Command { + + protected Inventory inventory; + protected SalesList salesList; + protected Cart currentCart; + protected VendorsList vendorsList; + + protected Command() { + + } + + public void setData(Inventory inventory, SalesList salesList, Cart currentCart, VendorsList vendorsList) { + this.inventory = inventory; + this.salesList = salesList; + this.currentCart = currentCart; + this.vendorsList = vendorsList; + } + + public abstract CommandResult execute() throws IOException; + + +} diff --git a/src/main/java/seedu/stocker/commands/CommandResult.java b/src/main/java/seedu/stocker/commands/CommandResult.java new file mode 100644 index 0000000000..38301cf286 --- /dev/null +++ b/src/main/java/seedu/stocker/commands/CommandResult.java @@ -0,0 +1,55 @@ +package seedu.stocker.commands; + +import java.util.List; +// import seedu.stocker.drugs.Drug; + +import java.util.Optional; + +/** + * Represents the result of a command execution. + */ +public class CommandResult { + + /** + * The feedback message to be shown to the user. Contains a description of the execution result + */ + public final String feedbackToUser; + + /** + * The list of drugs that was produced by the command + */ + private final List relevantElements; + + public CommandResult(String feedbackToUser) { + this.feedbackToUser = feedbackToUser; + relevantElements = null; + } + + public CommandResult(String feedbackToUser, List relevantElements) { + this.feedbackToUser = feedbackToUser; + this.relevantElements = relevantElements; + } + + /** + * Returns a list of drugs that was produced by the command, if any. + */ + public Optional> getRelevantElements() { + return Optional.ofNullable(relevantElements); + } + + public String getFeedbackToUserFindTest() { + if (relevantElements != null) { + StringBuilder feedback = new StringBuilder(); + for (int i = 0; i < relevantElements.size(); i++) { + feedback.append(i + 1).append(". ").append(relevantElements.get(i)); + feedback.append(System.lineSeparator()); + } + feedback.append(System.lineSeparator()); + feedback.append(feedbackToUser); + return feedback.toString(); + } else { + return feedbackToUser; + } + } + +} diff --git a/src/main/java/seedu/stocker/commands/DeleteCommand.java b/src/main/java/seedu/stocker/commands/DeleteCommand.java new file mode 100644 index 0000000000..603d545b13 --- /dev/null +++ b/src/main/java/seedu/stocker/commands/DeleteCommand.java @@ -0,0 +1,44 @@ +package seedu.stocker.commands; + +import seedu.stocker.drugs.StockEntry; +import seedu.stocker.exceptions.DrugNotFoundException; + +public class DeleteCommand extends Command{ + + public static final String COMMAND_WORD = "delete"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Removes a drug from drug list. " + + "Parameters: Serial Number " + System.lineSeparator() + + "Example: " + COMMAND_WORD + + " /s "; + + public static final String MESSAGE_SUCCESS = "Drug removed from inventory: %1$s"; + public static final String MESSAGE_FAILURE = "Drug not found in the inventory. "; + + + + private final String serialNumber; + + /** + * Constructs a DeleteCommand with the specified drug name. + * + */ + public DeleteCommand(String serialNumber) { + this.serialNumber = serialNumber; + } + + /** + * Executes the 'delete' command, removing a drug from the inventory list by name. + * + * @return A CommandResult indicating the result of the deletion operation. + */ + @Override + public CommandResult execute() { + try { + StockEntry deletedEntry = inventory.deleteDrug(this.serialNumber); + return new CommandResult<>(String.format(MESSAGE_SUCCESS, deletedEntry.getDrug().getName())); + } catch (DrugNotFoundException e) { + return new CommandResult<>(MESSAGE_FAILURE); + } + } +} diff --git a/src/main/java/seedu/stocker/commands/DeleteVendorCommand.java b/src/main/java/seedu/stocker/commands/DeleteVendorCommand.java new file mode 100644 index 0000000000..37f5d7898b --- /dev/null +++ b/src/main/java/seedu/stocker/commands/DeleteVendorCommand.java @@ -0,0 +1,75 @@ +package seedu.stocker.commands; + +import seedu.stocker.vendors.Vendor; + +/** + * Deletes a vendor from the inventory. + */ +public class DeleteVendorCommand extends Command { + + /** + * The command word for deleting a vendor. + */ + public static final String COMMAND_WORD = "deleteVendor"; + + /** + * Usage message for the deleteVendor command. + */ + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Deletes a vendor from the vendors list. " + + "Parameter: NAME" + System.lineSeparator() + + "Example: " + COMMAND_WORD + + " /v Moderna"; + + /** + * Message for indicating successful deletion of a vendor. + */ + public static final String MESSAGE_SUCCESS = "Vendor deleted from the vendors list: %1$s"; + + /** + * Message for indicating that the specified vendor was not found. + */ + public static final String MESSAGE_VENDOR_NOT_FOUND = "Vendor not found in the vendors list: %1$s"; + + /** + * The name of the vendor to be deleted. + */ + private final String vendorNameToDelete; + + /** + * Constructs a DeleteVendorCommand object with the specified vendor name to delete. + * + * @param name The name of the vendor to be deleted. + */ + public DeleteVendorCommand(String name) { + this.vendorNameToDelete = name; + } + + /** + * Executes the DeleteVendorCommand to remove the specified vendor from the vendors list. + * + * @return A CommandResult indicating the outcome of the command execution. + */ + @Override + public CommandResult execute() { + boolean vendorFound = false; + Vendor vendorToDelete = null; + + // Iterate through the vendor entries to find the vendor to delete. + for (Vendor vendor : vendorsList.getVendorEntries()) { + if (vendor.getName().equalsIgnoreCase(vendorNameToDelete)) { + vendorFound = true; + vendorToDelete = vendor; + break; + } + } + + if (vendorFound) { + // Delete the vendor and return a success message. + vendorsList.deleteVendor(vendorToDelete); + return new CommandResult<>(String.format(MESSAGE_SUCCESS, vendorToDelete.getName())); + } else { + // Return an error message if the vendor was not found. + return new CommandResult<>(String.format(MESSAGE_VENDOR_NOT_FOUND, vendorNameToDelete)); + } + } +} diff --git a/src/main/java/seedu/stocker/commands/DeleteVendorSupplyCommand.java b/src/main/java/seedu/stocker/commands/DeleteVendorSupplyCommand.java new file mode 100644 index 0000000000..f16de03da3 --- /dev/null +++ b/src/main/java/seedu/stocker/commands/DeleteVendorSupplyCommand.java @@ -0,0 +1,57 @@ +package seedu.stocker.commands; + +import seedu.stocker.vendors.VendorSupplyList; + +/** + * Deletes a drug from a vendor's supply list. + */ +public class DeleteVendorSupplyCommand extends Command { + + public static final String COMMAND_WORD = "deleteVendorSupply"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Deletes a drug from a vendor's supply list. " + + "Parameters: VENDOR_NAME, DRUG_NAME" + System.lineSeparator() + + "Example: " + COMMAND_WORD + " /v Moderna /n Paracetamol"; + + public static final String MESSAGE_SUCCESS = "Drug '%2$s' removed from %1$s's supply list"; + public static final String MESSAGE_VENDOR_NOT_FOUND = "Vendor not found: %1$s"; + public static final String MESSAGE_DRUG_NOT_FOUND = "The drug '%1$s' is not in the vendor's supply list"; + + private final String vendorName; + private final String drugName; + + /** + * Creates a DeleteVendorSupplyCommand to remove a drug from a vendor's supply list. + * + * @param vendorName The name of the vendor. + * @param drugName The name of the drug to be removed from the vendor's supply list. + */ + public DeleteVendorSupplyCommand(String vendorName, String drugName) { + this.vendorName = vendorName; + this.drugName = drugName; + } + + /** + * Executes the command to remove a drug from the specified vendor's supply list. + * + * @return CommandResult indicating the success of a drug removal depending on whether vendor and drug are found. + */ + @Override + public CommandResult execute() { + String lowercaseVendorName = vendorName.toLowerCase(); + String lowercaseDrugName = drugName.toLowerCase(); + + if (this.vendorsList.getVendorEntries().stream() + .noneMatch(vendor -> vendor.getName().equalsIgnoreCase(lowercaseVendorName))) { + return new CommandResult<>(String.format(MESSAGE_VENDOR_NOT_FOUND, lowercaseVendorName)); + } + + boolean removed = VendorSupplyList.removeDrugFromVendor(lowercaseVendorName, lowercaseDrugName); + + if (removed) { + return new CommandResult<>(String.format(MESSAGE_SUCCESS, lowercaseVendorName, lowercaseDrugName)); + } else { + return new CommandResult<>(String.format(MESSAGE_DRUG_NOT_FOUND, lowercaseDrugName)); + } + } +} diff --git a/src/main/java/seedu/stocker/commands/ExitCommand.java b/src/main/java/seedu/stocker/commands/ExitCommand.java new file mode 100644 index 0000000000..b8af9bef3d --- /dev/null +++ b/src/main/java/seedu/stocker/commands/ExitCommand.java @@ -0,0 +1,23 @@ +package seedu.stocker.commands; + + +/** + * Terminates the program. + */ +public class ExitCommand extends Command { + + public static final String COMMAND_WORD = "bye"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Exits the program."+ System.lineSeparator() + + "Example: " + COMMAND_WORD; + public static final String MESSAGE_EXIT_ACKNOWEDGEMENT = "Exiting Stocker as requested ..."; + + @Override + public CommandResult execute() { + return new CommandResult<>(MESSAGE_EXIT_ACKNOWEDGEMENT); + } + + public static boolean isExit(Command command) { + return command instanceof ExitCommand; // instanceof returns false if it is null + } +} diff --git a/src/main/java/seedu/stocker/commands/FindCommand.java b/src/main/java/seedu/stocker/commands/FindCommand.java new file mode 100644 index 0000000000..6ac5b97698 --- /dev/null +++ b/src/main/java/seedu/stocker/commands/FindCommand.java @@ -0,0 +1,104 @@ +package seedu.stocker.commands; + +import seedu.stocker.drugs.StockEntry; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static seedu.stocker.common.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +/** + * Represents a command to find drugs in the inventory that match a given keyword. + */ +public class FindCommand extends Command { + public static final String COMMAND_WORD = "find"; + + /** + * Usage message for the 'find' command. + */ + public static final String MESSAGE_USAGE = COMMAND_WORD + " /n" + ": Finds drug in inventory using name." + + System.lineSeparator() + + "Example: " + COMMAND_WORD + " /n panadol" + + System.lineSeparator() + + System.lineSeparator() + + COMMAND_WORD + " /d" + ": Finds drug in inventory using date." + + System.lineSeparator() + + "Example: " + COMMAND_WORD + " /d panadol" + + System.lineSeparator() + + System.lineSeparator() + + COMMAND_WORD + " /s" + ": Finds drug in inventory using serial number." + + System.lineSeparator() + + "Example: " + COMMAND_WORD + " /s ABC123"; + + /** + * Success message displayed after successfully finding drugs in the inventory. + */ + public static final String MESSAGE_SUCCESS = "Listed all drugs with the keyword in the inventory."; + + private final String keyword; + private final String criterion; + + /** + * Creates a FindCommand with the specified keyword. + * + * @param keyword The keyword to search for in the inventory. + */ + public FindCommand(String keyword, String criterion) { + if (criterion.equals("/s")) { + this.keyword = keyword; + } else { + this.keyword = keyword.toLowerCase(); + } + this.criterion = criterion; + } + + private String getResultString(StockEntry entry, String serialNumber) { + return "Name: " + entry.getDrug().getName() + + ", Expiry date: " + entry.getDrug().getExpiryDate() + + ", Serial number: " + serialNumber + + ", Quantity: " + entry.getQuantity(); + } + + private static boolean matches(String criterion, String keyword, StockEntry entry, String serialNumber) { + switch (criterion) { + case "/n": + return entry.getDrug().getName().toLowerCase().contains(keyword); + case "/d": + return entry.getDrug().getExpiryDate().toLowerCase().contains(keyword); + case "/s": + return serialNumber.equals(keyword); + default: + return false; + } + } + + /** + * Executes the 'find' command, searching for drugs that match the keyword. + * + * @return A CommandResult containing the outcome of the command execution. + */ + @Override + public CommandResult execute() { + if (keyword == null || keyword.trim().isEmpty()) { + return new CommandResult<>(String.format(MESSAGE_INVALID_COMMAND_FORMAT, MESSAGE_USAGE)); + } + + List> entries = inventory.getStockEntries(); + List foundResults = new ArrayList<>(); + + for (Map.Entry entry : entries) { + if (matches(this.criterion, this.keyword, entry.getValue(), entry.getKey())) { + foundResults.add(getResultString(entry.getValue(), entry.getKey())); + } + } + + if (foundResults.isEmpty()) { + return new CommandResult<>("No drugs found with the specified criteria."); + } else { + return new CommandResult<>(MESSAGE_SUCCESS, foundResults); + } + } + + +} diff --git a/src/main/java/seedu/stocker/commands/FindVendorSupplyCommand.java b/src/main/java/seedu/stocker/commands/FindVendorSupplyCommand.java new file mode 100644 index 0000000000..68ac596247 --- /dev/null +++ b/src/main/java/seedu/stocker/commands/FindVendorSupplyCommand.java @@ -0,0 +1,52 @@ +package seedu.stocker.commands; + +import seedu.stocker.vendors.VendorSupplyList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * Lists the vendors that supply a specific drug. + */ +public class FindVendorSupplyCommand extends Command { + public static final String COMMAND_WORD = "findVendorSupply"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Lists the vendors that supply a specific drug. " + + "Parameters: DRUG_NAME" + System.lineSeparator() + + "Example: " + COMMAND_WORD + " /n Paracetamol"; + + private final String drugName; + + /** + * Creates a FindVendorSupplyCommand to list the vendors that supply a specific drug. + * + * @param drugName The name of the drug to search for. + */ + public FindVendorSupplyCommand(String drugName) { + this.drugName = drugName; + } + + /** + * Executes the command to list the vendors that supply the specified drug. + * + * @return CommandResult indicating the vendors that supply the drug or an error message. + */ + @Override + public CommandResult execute() { + String lowercaseDrugName = drugName.toLowerCase(); + + List supplyingVendors = VendorSupplyList.getVendorSuppliedDrugs() + .entrySet() + .stream() + .filter(entry -> entry.getValue().contains(lowercaseDrugName)) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); + + if (supplyingVendors.isEmpty()) { + return new CommandResult<>("No vendors supply the drug: " + drugName); + } else { + return new CommandResult<>("Vendors supplying the drug " + drugName + ": " + + String.join(", ", supplyingVendors)); + } + } + +} diff --git a/src/main/java/seedu/stocker/commands/GetDescriptionCommand.java b/src/main/java/seedu/stocker/commands/GetDescriptionCommand.java new file mode 100644 index 0000000000..d484f48318 --- /dev/null +++ b/src/main/java/seedu/stocker/commands/GetDescriptionCommand.java @@ -0,0 +1,44 @@ +package seedu.stocker.commands; + +/** + * Gets the description of a specific drug. + */ +public class GetDescriptionCommand extends Command { + + public static final String COMMAND_WORD = "getDescription"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Gets the description of a specific drug. " + + "Parameters: NAME" + System.lineSeparator() + + "Example: " + COMMAND_WORD + " /n Panadol"; + + public static final String MESSAGE_DESCRIPTION_NOT_FOUND = "Description not found for %1$s"; + + private final String drugName; + + /** + * Creates a GetDescriptionCommand to retrieve the description of a specific drug. + * + * @param drugName The name of the drug for which the description should be retrieved. + */ + public GetDescriptionCommand(String drugName) { + this.drugName = drugName; + } + + /** + * Executes the command to retrieve the description of the specified drug. + * + * @return CommandResult containing the drug description if found, or an error message if not found. + */ + @Override + public CommandResult execute() { + String lowercaseDrugName = drugName.toLowerCase(); + + String description = seedu.stocker.drugs.Description.getDescription(lowercaseDrugName); + + if (description != null) { + return new CommandResult<>(description); + } else { + return new CommandResult<>(String.format(MESSAGE_DESCRIPTION_NOT_FOUND, lowercaseDrugName)); + } + } +} diff --git a/src/main/java/seedu/stocker/commands/HelpCommand.java b/src/main/java/seedu/stocker/commands/HelpCommand.java new file mode 100644 index 0000000000..44a78a4198 --- /dev/null +++ b/src/main/java/seedu/stocker/commands/HelpCommand.java @@ -0,0 +1,58 @@ +package seedu.stocker.commands; +//@@author TeoHaoZhi + +/** + * Shows help instructions. + */ +public class HelpCommand extends Command { + + public static final String COMMAND_WORD = "help"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Shows program usage instructions. " + + System.lineSeparator() + + "Example: " + COMMAND_WORD; + + @Override + public CommandResult execute() { + return new CommandResult<>( + System.lineSeparator() + LoginCommand.MESSAGE_USAGE + System.lineSeparator() + + System.lineSeparator() + RegisterCommand.MESSAGE_USAGE + System.lineSeparator() + + System.lineSeparator() + SaveCommand.MESSAGE_USAGE + System.lineSeparator() + + System.lineSeparator() + HelpCommand.MESSAGE_USAGE + System.lineSeparator() + + + System.lineSeparator() + AddCommand.MESSAGE_USAGE + System.lineSeparator() + + System.lineSeparator() + DeleteCommand.MESSAGE_USAGE + System.lineSeparator() + + + System.lineSeparator() + ListCommand.MESSAGE_USAGE + System.lineSeparator() + + System.lineSeparator() + ShowStockLevelCommand.MESSAGE_USAGE + System.lineSeparator() + + + System.lineSeparator() + FindCommand.MESSAGE_USAGE + System.lineSeparator() + + + System.lineSeparator() + SetThresholdCommand.MESSAGE_USAGE + System.lineSeparator() + + System.lineSeparator() + ListThresholdCommand.MESSAGE_USAGE + System.lineSeparator() + + + System.lineSeparator() + AddToCartCommand.MESSAGE_USAGE + System.lineSeparator() + + System.lineSeparator() + ViewCartCommand.MESSAGE_USAGE + System.lineSeparator() + + System.lineSeparator() + CheckOutCommand.MESSAGE_USAGE + System.lineSeparator() + + + System.lineSeparator() + SaveSalesCommand.MESSAGE_USAGE + System.lineSeparator() + + System.lineSeparator() + ListSalesCommand.MESSAGE_USAGE + System.lineSeparator() + + + System.lineSeparator() + AddVendorCommand.MESSAGE_USAGE + System.lineSeparator() + + System.lineSeparator() + DeleteVendorCommand.MESSAGE_USAGE + System.lineSeparator() + + System.lineSeparator() + ListVendorCommand.MESSAGE_USAGE + System.lineSeparator() + + + System.lineSeparator() + AddVendorSupplyCommand.MESSAGE_USAGE + System.lineSeparator() + + System.lineSeparator() + ListVendorSupplyCommand.MESSAGE_USAGE + System.lineSeparator() + + System.lineSeparator() + FindVendorSupplyCommand.MESSAGE_USAGE + System.lineSeparator() + + System.lineSeparator() + DeleteVendorSupplyCommand.MESSAGE_USAGE + System.lineSeparator() + + + System.lineSeparator() + AddDescriptionCommand.MESSAGE_USAGE + System.lineSeparator() + + System.lineSeparator() + GetDescriptionCommand.MESSAGE_USAGE + System.lineSeparator() + + System.lineSeparator() + ListDescriptionsCommand.MESSAGE_USAGE + System.lineSeparator() + + + System.lineSeparator() + ExitCommand.MESSAGE_USAGE + + ); + } +} diff --git a/src/main/java/seedu/stocker/commands/IncorrectCommand.java b/src/main/java/seedu/stocker/commands/IncorrectCommand.java new file mode 100644 index 0000000000..f58cc237c3 --- /dev/null +++ b/src/main/java/seedu/stocker/commands/IncorrectCommand.java @@ -0,0 +1,19 @@ +package seedu.stocker.commands; + +/** + * Represents an incorrect command. Upon execution, produces some feedback to the user. + */ +public class IncorrectCommand extends Command { + + public final String feedbackToUser; + + public IncorrectCommand(String feedbackToUser) { + this.feedbackToUser = feedbackToUser; + } + + @Override + public CommandResult execute() { + return new CommandResult<>(feedbackToUser); + } + +} diff --git a/src/main/java/seedu/stocker/commands/ListCommand.java b/src/main/java/seedu/stocker/commands/ListCommand.java new file mode 100644 index 0000000000..4df2a671ed --- /dev/null +++ b/src/main/java/seedu/stocker/commands/ListCommand.java @@ -0,0 +1,82 @@ +package seedu.stocker.commands; + +import seedu.stocker.drugs.StockEntry; + +import java.time.LocalDate; +import java.util.List; +import java.util.Map; +import java.time.format.DateTimeFormatter; + +/** + * Represents a command to list all drugs in the inventory. + * This command retrieves the list of drugs from the inventory and provides it as part of the command result. + * If the inventory is empty, it informs the user that the inventory has no drugs. + */ +public class ListCommand extends Command { + + public static final String COMMAND_WORD = "list"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": List all drug information that is being " + + "tracked by the system. " + System.lineSeparator() + + "Example: " + COMMAND_WORD; + + public static final String MESSAGE_SUCCESS = "Listed all drugs in the inventory."; + + /** + * Executes the list command. + * + * @return A CommandResult containing the success message and the list of drugs. + */ + @Override + public CommandResult execute() { + // Assertion: Check if the inventory is properly initialized + assert inventory != null : "Inventory should be initialized before executing ListCommand."; + // Retrieve the list of drugs from the inventory + List> stockEntries = inventory.getStockEntries(); + + // Check if the inventory is empty + if (stockEntries.isEmpty()) { + // Return a CommandResult indicating that the inventory is empty + return new CommandResult<>("The inventory is empty."); + } else { + // Prepare a StringBuilder to construct the output message + StringBuilder resultMessage = new StringBuilder(MESSAGE_SUCCESS + System.lineSeparator()); + int index = 1; + LocalDate currentDate = LocalDate.now(); // Get the current date + + for (Map.Entry entry : stockEntries) { + String inputDate = entry.getValue().getDrug().getExpiryDate(); + LocalDate expiryDate = parseDate(inputDate); + boolean isExpired = currentDate.isAfter(expiryDate); + + resultMessage.append("\t").append(index).append(". ") + .append("Name: ").append(entry.getValue().getDrug().getName()) + .append(", Expiry date: ").append(entry.getValue().getDrug().getExpiryDate()) + .append(", Serial number: ").append(entry.getKey()) + .append(", Quantity: ").append(entry.getValue().getQuantity()) + .append(", Selling Price: ").append(entry.getValue().getDrug().getSellingPrice()); + + if (isExpired) { + resultMessage.append(" (E)"); // Append (E) to indicate expiry + } + + resultMessage.append(System.lineSeparator()); + index++; + } + // Return a CommandResult with the success message and the list of drugs + return new CommandResult<>(resultMessage.toString().trim()); + } + } + + /** + * Helper method to parse a date string in "dd/mm/yyyy" format to a LocalDate. + * + * @param dateStr The input date string. + * @return The parsed LocalDate. + */ + private LocalDate parseDate(String dateStr) { + DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy"); + return LocalDate.parse(dateStr, inputFormatter); + } + +} diff --git a/src/main/java/seedu/stocker/commands/ListDescriptionsCommand.java b/src/main/java/seedu/stocker/commands/ListDescriptionsCommand.java new file mode 100644 index 0000000000..b8aa57d782 --- /dev/null +++ b/src/main/java/seedu/stocker/commands/ListDescriptionsCommand.java @@ -0,0 +1,39 @@ +package seedu.stocker.commands; + +import java.util.Map; + +/** + * Lists all descriptions for corresponding drugs. + */ +public class ListDescriptionsCommand extends Command { + + public static final String COMMAND_WORD = "listDescriptions"; + + public static final String MESSAGE_SUCCESS = "List of Drug Descriptions:"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Lists all the descriptions for all drugs " + + System.lineSeparator() + + "Example: " + COMMAND_WORD; + /** + * Creates a ListDescriptionsCommand to list all drug descriptions for all corresponding drugs. + */ + public ListDescriptionsCommand() { + } + + /** + * Executes the command to list all drug descriptions. + * + * @return CommandResult that displays the list of drug descriptions. + */ + @Override + public CommandResult execute() { + Map descriptions = seedu.stocker.drugs.Description.getAllDescriptions(); + StringBuilder result = new StringBuilder(MESSAGE_SUCCESS); + + for (Map.Entry entry : descriptions.entrySet()) { + result.append(System.lineSeparator()); + result.append(entry.getKey()).append(": ").append(entry.getValue()); + } + + return new CommandResult<>(result.toString()); + } +} diff --git a/src/main/java/seedu/stocker/commands/ListSalesCommand.java b/src/main/java/seedu/stocker/commands/ListSalesCommand.java new file mode 100644 index 0000000000..7dc27249ba --- /dev/null +++ b/src/main/java/seedu/stocker/commands/ListSalesCommand.java @@ -0,0 +1,56 @@ +package seedu.stocker.commands; + +import seedu.stocker.drugs.Cart; +import seedu.stocker.drugs.CartEntry; +import seedu.stocker.drugs.StockEntry; + +/** + * Represents a command to list all items in the sales list. + */ +public class ListSalesCommand extends Command { + public static final String COMMAND_WORD = "listSales"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Lists all sold items." + + System.lineSeparator() + + "Example: " + COMMAND_WORD; + + public static final String MESSAGE_SUCCESS = "Listed all items in the sales list."; + public static final String MESSAGE_FAILURE = "The sales list is empty."; + + @Override + public CommandResult execute() { + StringBuilder resultMessage = new StringBuilder(MESSAGE_SUCCESS + System.lineSeparator()); + int index = 1; + double totalCost = 0.0; + + if (salesList.getAllSales().isEmpty()) { + return new CommandResult<>(MESSAGE_FAILURE); + } + + for (Cart cart : salesList.getAllSales()) { + for (CartEntry entry : cart.getCurrentCart()) { + String serialNumber = entry.getSerialNumber(); + StockEntry stockEntry = inventory.get(serialNumber); + if (stockEntry != null) { + String name = stockEntry.getDrug().getName(); + double sellingPrice = entry.getSellingPrice(); + double cost = sellingPrice * entry.getQuantity(); + totalCost += cost; + + resultMessage.append("\t").append(index).append(". ") + .append("Name: ").append(name) + .append(", Serial Number: ").append(serialNumber) + .append(", Quantity: ").append(entry.getQuantity()) + .append(", Selling Price: ").append(sellingPrice) + .append(", Cost: ").append(cost) + .append(System.lineSeparator()); + index++; + } + } + } + + resultMessage.append("Total Sales: ").append(totalCost); + + return new CommandResult<>(resultMessage.toString().trim()); + } +} diff --git a/src/main/java/seedu/stocker/commands/ListThresholdCommand.java b/src/main/java/seedu/stocker/commands/ListThresholdCommand.java new file mode 100644 index 0000000000..3b397475a2 --- /dev/null +++ b/src/main/java/seedu/stocker/commands/ListThresholdCommand.java @@ -0,0 +1,46 @@ +package seedu.stocker.commands; + +import seedu.stocker.drugs.StockEntry; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Represents a command to list all drugs and their threshold levels in the inventory. + */ +public class ListThresholdCommand extends Command { + + public static final String COMMAND_WORD = "listThreshold"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": List all drugs and their threshold levels." + + System.lineSeparator() + + "Example: " + COMMAND_WORD; + + public static final String MESSAGE_SUCCESS = "Listed all drugs by threshold level in the inventory."; + + /** + * Executes the ListThresholdCommand by retrieving a list of StockEntry objects and their threshold levels. + * + * @return A CommandResult containing the list of drugs and their threshold levels or a message indicating the + * inventory is empty. + */ + @Override + public CommandResult execute() { + assert inventory != null : "Inventory should be initialized before executing ListThresholdCommand."; + + List> stockEntries = inventory.getStockEntries(); + + if (stockEntries.isEmpty()) { + return new CommandResult<>("The inventory is empty."); + } else { + List resultElements = new ArrayList<>(); + for (Map.Entry entry : stockEntries) { + long thresholdQuantity = entry.getValue().getThresholdQuantity(); + String drugName = entry.getValue().getDrug().getName(); + resultElements.add(drugName + ": " + thresholdQuantity); + } + return new CommandResult<>(MESSAGE_SUCCESS, resultElements); + } + } +} diff --git a/src/main/java/seedu/stocker/commands/ListVendorCommand.java b/src/main/java/seedu/stocker/commands/ListVendorCommand.java new file mode 100644 index 0000000000..6a06db2e3b --- /dev/null +++ b/src/main/java/seedu/stocker/commands/ListVendorCommand.java @@ -0,0 +1,39 @@ +package seedu.stocker.commands; + +import seedu.stocker.vendors.Vendor; + +import java.util.List; + +/** + * Generates a list of vendors tracked by system + */ +public class ListVendorCommand extends Command { + + public static final String COMMAND_WORD = "listVendors"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": List all vendors that are being " + + "tracked by the system." + System.lineSeparator() + + "Example: " + COMMAND_WORD; + + public static final String MESSAGE_SUCCESS = "Listed all vendors in the list."; + + /** + * Executes the list command. + * + * @return A CommandResult containing the success message and the list of drugs. + */ + @Override + public CommandResult execute() { + // Retrieve the list of vendors from the inventory + List vendorEntries = this.vendorsList.getVendorEntries(); + + // Check if the inventory is empty + if (vendorEntries.isEmpty()) { + // Return a CommandResult indicating that the inventory is empty + return new CommandResult<>("The inventory is empty."); + } else { + // Return a CommandResult with the success message and the list of vendors + return new CommandResult(MESSAGE_SUCCESS, vendorEntries); + } + } +} diff --git a/src/main/java/seedu/stocker/commands/ListVendorSupplyCommand.java b/src/main/java/seedu/stocker/commands/ListVendorSupplyCommand.java new file mode 100644 index 0000000000..851880b426 --- /dev/null +++ b/src/main/java/seedu/stocker/commands/ListVendorSupplyCommand.java @@ -0,0 +1,45 @@ +package seedu.stocker.commands; + +import seedu.stocker.vendors.VendorSupplyList; + +import java.util.List; + +/** + * Lists the drugs supplied by a specific vendor in a case-insensitive manner. + */ +public class ListVendorSupplyCommand extends Command { + public static final String COMMAND_WORD = "listVendorSupply"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Lists the drugs supplied by a specific vendor. " + + "Parameters: VENDOR_NAME" + System.lineSeparator() + + "Example: " + COMMAND_WORD + " /v Moderna"; + + private final String vendorName; + + /** + * Creates a ListVendorSupplyCommand to list the drugs supplied by a specific vendor. + * + * @param vendorName The name of the vendor. + */ + public ListVendorSupplyCommand(String vendorName) { + this.vendorName = vendorName; + } + + /** + * Executes the command to list the drugs supplied by the specified vendor. + * + * @return CommandResult containing the list of supplied drugs or a message indicating none were supplied. + */ + @Override + public CommandResult execute() { + String lowercaseVendorName = vendorName.toLowerCase(); + + List suppliedDrugs = VendorSupplyList.getDrugsSuppliedByVendor(lowercaseVendorName); + + if (suppliedDrugs.isEmpty()) { + return new CommandResult<>("No drugs supplied by " + vendorName); + } else { + return new CommandResult<>("Drugs supplied by " + vendorName + ": " + + String.join(", ", suppliedDrugs)); + } + } +} diff --git a/src/main/java/seedu/stocker/commands/LoginCommand.java b/src/main/java/seedu/stocker/commands/LoginCommand.java new file mode 100644 index 0000000000..2cc777d006 --- /dev/null +++ b/src/main/java/seedu/stocker/commands/LoginCommand.java @@ -0,0 +1,26 @@ +package seedu.stocker.commands; + +import seedu.stocker.authentication.LoginSystem; +import java.io.IOException; + +/** + * Login existing user into system. + */ +public class LoginCommand extends Command { + + + public static final String COMMAND_WORD = "login"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Login new user into system." + + System.lineSeparator() + + "Example: " + COMMAND_WORD; + + public static final String MESSAGE_SUCCESS = "Welcome back!"; + + public CommandResult execute() throws IOException { + LoginSystem system = new LoginSystem(); + system.loadExistingUsers(); + system.loginExistingUser(); + return new CommandResult<>(MESSAGE_SUCCESS); + } +} diff --git a/src/main/java/seedu/stocker/commands/RegisterCommand.java b/src/main/java/seedu/stocker/commands/RegisterCommand.java new file mode 100644 index 0000000000..35c52c3e82 --- /dev/null +++ b/src/main/java/seedu/stocker/commands/RegisterCommand.java @@ -0,0 +1,26 @@ +package seedu.stocker.commands; + +import seedu.stocker.authentication.LoginSystem; + +import java.io.IOException; + +/** + * Registers new user into login system. + */ +public class RegisterCommand extends Command { + + public static final String COMMAND_WORD = "register"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Register new user into system." + + System.lineSeparator() + + "Example: " + COMMAND_WORD; + + public static final String MESSAGE_SUCCESS = "New User Created."; + + public CommandResult execute() throws IOException { + LoginSystem system = new LoginSystem(); + system.loadExistingUsers(); + system.newUserCreator(); + return new CommandResult<>(MESSAGE_SUCCESS); + } +} diff --git a/src/main/java/seedu/stocker/commands/SaveCommand.java b/src/main/java/seedu/stocker/commands/SaveCommand.java new file mode 100644 index 0000000000..141f096ed8 --- /dev/null +++ b/src/main/java/seedu/stocker/commands/SaveCommand.java @@ -0,0 +1,48 @@ +package seedu.stocker.commands; + +import seedu.stocker.drugs.StockEntry; +import seedu.stocker.storage.Storage; + +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.Map; + +/** + * Backups existing drug list inventory into txt file to be uploaded later. + */ +public class SaveCommand extends Command{ + public static final String COMMAND_WORD = "saveDrugs"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Saves existing druglist that is loaded " + +"into inventory when system is booted up." + + System.lineSeparator() + + "Example: " + COMMAND_WORD; + + public static final String MESSAGE_SUCCESS = "Drugs successfully saved."; + + public CommandResult execute() throws IOException { + + File holder = new File("./drugs.txt"); + if (!holder.exists()) { + holder.createNewFile(); + } + + List> entries= inventory.getStockEntries(); + Storage storageManager = new Storage(inventory); + storageManager.writeToFile("drugs.txt", ""); + + for (Map.Entry entry : entries) { + String name = entry.getValue().getDrug().getName(); + String date = entry.getValue().getDrug().getExpiryDate(); + String serialNumber = entry.getKey(); + String quantity = String.valueOf(entry.getValue().getQuantity()); + String sellingPrice = String.valueOf(entry.getValue().getDrug().getSellingPrice()); + String toBeAppended = "Name: " + name + ", " + "Expiry Date: " + date + ", " + + "Serial Number: " + serialNumber + ", " + "Quantity: " + quantity + + ", " + "Selling Price: " + sellingPrice; + storageManager.appendToFile("drugs.txt", toBeAppended); + } + return new CommandResult<>(MESSAGE_SUCCESS); + } +} diff --git a/src/main/java/seedu/stocker/commands/SaveSalesCommand.java b/src/main/java/seedu/stocker/commands/SaveSalesCommand.java new file mode 100644 index 0000000000..4e7d8dd3b5 --- /dev/null +++ b/src/main/java/seedu/stocker/commands/SaveSalesCommand.java @@ -0,0 +1,62 @@ +package seedu.stocker.commands; + +import seedu.stocker.drugs.Cart; +import seedu.stocker.drugs.CartEntry; +import seedu.stocker.drugs.StockEntry; +import seedu.stocker.storage.Storage; +import seedu.stocker.drugs.Drug; + + +import java.io.File; +import java.io.IOException; + +/** + * Backs up the current sales list into a text file. + */ +public class SaveSalesCommand extends Command { + public static final String COMMAND_WORD = "saveSales"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Saves the current sold items to a file." + + System.lineSeparator() + + "Example: " + COMMAND_WORD; + + public static final String MESSAGE_SUCCESS = "Sales list successfully saved."; + public static final String MESSAGE_FAILURE = "The sales list is empty. Nothing to save."; + + @Override + public CommandResult execute() throws IOException { + try { + if (salesList.getAllSales().isEmpty()) { + return new CommandResult(MESSAGE_FAILURE); + } + + File salesFile = new File("./soldItems.txt"); + if (!salesFile.exists()) { + salesFile.createNewFile(); + } + + Storage storageManager = new Storage(inventory); + + for (Cart cart : salesList.getAllSales()) { + for (CartEntry entry : cart.getCurrentCart()) { + String serialNumber = entry.getSerialNumber(); + long quantity = entry.getQuantity(); + double sellingPrice = entry.getSellingPrice(); + + StockEntry stockEntry = inventory.get(serialNumber); + Drug drug = stockEntry.getDrug(); + + String toBeAppended = "Name: " + drug + ", " + + "Serial Number: " + serialNumber + ", " + + "Quantity: " + quantity + ", " + + "Selling Price: " + sellingPrice; + storageManager.appendToFile("soldItems.txt", toBeAppended); + } + } + } catch (IOException e) { + e.printStackTrace(); + return new CommandResult<>("Error: Failed to save sales data."); + } + return new CommandResult<>(MESSAGE_SUCCESS); + } +} diff --git a/src/main/java/seedu/stocker/commands/SetThresholdCommand.java b/src/main/java/seedu/stocker/commands/SetThresholdCommand.java new file mode 100644 index 0000000000..ff86027168 --- /dev/null +++ b/src/main/java/seedu/stocker/commands/SetThresholdCommand.java @@ -0,0 +1,53 @@ +package seedu.stocker.commands; + +import seedu.stocker.drugs.StockEntry; + +/** + * Represents a command to set the threshold quantity for a drug in the inventory. + */ +public class SetThresholdCommand extends Command { + + public static final String COMMAND_WORD = "setThreshold"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Set the threshold quantity for a drug. " + + "(default 100)" + System.lineSeparator() + + "Example: " + COMMAND_WORD + " /s TC150 /tq 50"; + + public static final String MESSAGE_SUCCESS = "Threshold quantity set for %1$s: %2$d"; + + private final String serialNumber; + private final long threshold; + + /** + * Constructs a SetThresholdCommand with a specified drug serial number and threshold quantity. + * @param serialNumber The serial number of the drug. + * @param threshold The threshold quantity to set. + */ + public SetThresholdCommand(String serialNumber, long threshold) { + this.serialNumber = serialNumber; + this.threshold = threshold; + } + + + /** + * Executes the SetThresholdCommand by setting the threshold quantity for the specified drug. + * @return A CommandResult indicating the success or failure of the command. + */ + @Override + public CommandResult execute() { + + if (inventory.getStockEntries().isEmpty()) { + return new CommandResult<>("Inventory is empty."); + } + + StockEntry stockEntry = inventory.get(serialNumber); + + if (stockEntry != null) { + stockEntry.setThresholdQuantity(threshold); + return new CommandResult<>(String.format(MESSAGE_SUCCESS, stockEntry.getDrug().getName(), threshold)); + } else { + return new CommandResult<>("Drug not found."); + } + } +} diff --git a/src/main/java/seedu/stocker/commands/ShowStockLevelCommand.java b/src/main/java/seedu/stocker/commands/ShowStockLevelCommand.java new file mode 100644 index 0000000000..57f897b731 --- /dev/null +++ b/src/main/java/seedu/stocker/commands/ShowStockLevelCommand.java @@ -0,0 +1,54 @@ +package seedu.stocker.commands; + +import seedu.stocker.drugs.StockEntry; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * Represents a command to display a report of stock levels sorted by quantity in ascending order. + */ +public class ShowStockLevelCommand extends Command { + + public static final String COMMAND_WORD = "stockLevel"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": List all drugs by quantity level " + + "tracked by the system in ascending order. " + System.lineSeparator() + + "Example: " + COMMAND_WORD; + + public static final String MESSAGE_SUCCESS = "Stock Level Report (Sorted by Quantity)"; + + /** + * Executes the "stocklevel" command. Displays a report of stock levels sorted by quantity in ascending order. + * + * @return A CommandResult containing the success message and a list of stock entries sorted by quantity. + */ + @Override + public CommandResult execute() { + // Assertion: Check if the inventory is properly initialized + assert inventory != null : "Inventory should be initialized before executing ShowStockLevelCommand."; + // Retrieve the list of drugs from the inventory + List stockEntries = inventory.getStockEntries() + .stream() + .map(Map.Entry::getValue).collect(Collectors.toList()); + + // Check if the inventory is empty + if (stockEntries.isEmpty()) { + // Return a CommandResult indicating that the inventory is empty + return new CommandResult<>("The inventory is empty."); + } else { + // Sort the stockEntries by quantity in ascending order + stockEntries.sort(Comparator.comparingLong(StockEntry::getQuantity)); + // Create a new list to store the sorted stockEntries + List arrangedListByQuantity = new ArrayList<>(stockEntries); + + + + + return new CommandResult<>(MESSAGE_SUCCESS, arrangedListByQuantity); + } + } +} diff --git a/src/main/java/seedu/stocker/commands/ViewCartCommand.java b/src/main/java/seedu/stocker/commands/ViewCartCommand.java new file mode 100644 index 0000000000..b2d2ef8649 --- /dev/null +++ b/src/main/java/seedu/stocker/commands/ViewCartCommand.java @@ -0,0 +1,61 @@ +package seedu.stocker.commands; + +import seedu.stocker.drugs.CartEntry; +import seedu.stocker.drugs.Drug; +import seedu.stocker.drugs.StockEntry; + +/** + * Represents a command to list all drugs in the current cart. + * This command retrieves the list of drugs from the current cart and provides it as part of the command result. + * If the inventory is empty, it informs the user that the inventory has no drugs. + */ +public class ViewCartCommand extends Command { + + public static final String COMMAND_WORD = "viewCart"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": View the current cart items and the total cost." + System.lineSeparator() + + "Example: " + COMMAND_WORD; + + public static final String MESSAGE_SUCCESS = "Listed all the content of your cart."; + public static final String MESSAGE_FAILURE = "Your cart is empty. "; + + /** + * Execute the ViewCartCommand + */ + public CommandResult execute() { + if (currentCart.isEmpty()) { + return new CommandResult<>(MESSAGE_FAILURE); + } else { + StringBuilder resultMessage = new StringBuilder(MESSAGE_SUCCESS + System.lineSeparator()); + int index = 1; + double totalCost = 0.0; + + for (CartEntry cartEntry : currentCart.getCurrentCart()) { + String serialNumber = cartEntry.getSerialNumber(); + long quantity = cartEntry.getQuantity(); + + StockEntry entry = inventory.get(serialNumber); + if (entry != null) { + Drug drug = entry.getDrug(); + double sellingPrice = drug.getSellingPrice(); + double cost = sellingPrice * quantity; + totalCost += cost; + + resultMessage.append("\t").append(index).append(". ") + .append("Name: ").append(drug.getName()) + .append(", Quantity: ").append(quantity) + .append(", Selling Price: ").append(sellingPrice) + .append(", Cost: ").append(cost) + .append(System.lineSeparator()); + + index++; + } + } + + resultMessage.append(System.lineSeparator()).append("Total Cost: ").append(totalCost); + + return new CommandResult<>(resultMessage.toString()); + } + } +} diff --git a/src/main/java/seedu/stocker/common/Messages.java b/src/main/java/seedu/stocker/common/Messages.java new file mode 100644 index 0000000000..bc71c584cc --- /dev/null +++ b/src/main/java/seedu/stocker/common/Messages.java @@ -0,0 +1,39 @@ +package seedu.stocker.common; + +/** + * Container for user visible messages. + */ +public class Messages { + + public static final String MESSAGE_WELCOME = "Welcome to your Stocker!"; + public static final String MESSAGE_GOODBYE = "Good bye!"; + public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s"; + public static final String MESSAGE_INVALID_QUANTITY = "A quantity must be an integer superior than 0.\n"; + public static final String MESSAGE_INVALID_NAME = "A drug name cannot contain only whitespaces"; + public static final String MESSAGE_INVALID_DATE = "Invalid expiry date format. Please use 'dd/mm/yyyy' format."; + public static final String MESSAGE_INVALID_PRICE = "Invalid selling price format. " + + "Please use up to 2 decimal places."; + public static final String MESSAGE_LOGIN_WELCOME = "Welcome! \n" + + "Key in register or login based on your needs \n" + + "1.Register user \n" + + "2.Login"; + public static final String MESSAGE_USERNAME_INPUT = "Enter your username:"; + public static final String MESSAGE_PASSWORD_INPUT = "Enter Your Password:"; + public static final String MESSAGE_SUCCESSFUL_REGISTRATION = "Registration successful."; + public static final String MESSAGE_SUCCESSFUL_LOGIN = "Login successful."; + public static final String MESSAGE_INVALID_CHOICE = "Invalid Input, enter register or login only!"; + public static final String MESSAGE_USER_ALREADY_EXIST = "User already exists. Please make user with " + + "different name or login instead"; + public static final String MESSAGE_INVALID_USERNAME_OR_PASSWORD = "Invalid username or password. Please try again."; + public static final String MESSAGE_NO_BLANK_NAME_ALLOWED = "No blank name allowed. " + + "Enter your desired username again"; + public static final String MESSAGE_NO_BLANK_PASSWORD_ALLOWED = "No blank password allowed. " + + "Enter your desired password again"; + + public static final String MESSAGE_INIT_FAILED = "Failed to initialise Stocker application. Exiting..."; + public static final String MESSAGE_EXECUTION_FAILED = "Exection of the command failed because of I/O error, " + + "please try later."; + + public static final String MESSAGE_INVALID_LOGIN_CHARACTER = " Do not include : in the username or password, enter" + + " another username or password"; +} diff --git a/src/main/java/seedu/stocker/drugs/Cart.java b/src/main/java/seedu/stocker/drugs/Cart.java new file mode 100644 index 0000000000..6a439d82e6 --- /dev/null +++ b/src/main/java/seedu/stocker/drugs/Cart.java @@ -0,0 +1,61 @@ +package seedu.stocker.drugs; + +import seedu.stocker.exceptions.DrugNotFoundException; + +import java.util.ArrayList; +import java.util.List; + +public class Cart { + private ArrayList entries; + + public Cart() { + this.entries = new ArrayList<>(); + } + + public Cart(List cartEntries) { + this.entries = new ArrayList<>(cartEntries); + } + + public void addEntry(String key, long quantity) { + this.entries.add(new CartEntry(key, quantity)); + } + + public void addEntry(Drug drug, String serialNumber, long quantity, double sellingPrice, Inventory inventory) { + StockEntry stockEntry = inventory.get(serialNumber); + if (stockEntry != null) { + double totalCost = sellingPrice * quantity; + this.entries.add(new CartEntry(serialNumber, quantity, drug)); + } + } + + public List getCurrentCart() { + return this.entries; + } + + public boolean isEmpty() { + return entries.isEmpty(); + } + + public CartEntry getEntryBySerialNumber(String serialNumber) { + return this.entries.stream() + .filter(entry -> entry.getSerialNumber().equals(serialNumber)) + .findAny().orElse(null); + } + + public long getEntryQuantity(String serialNumber) { + return this.entries + .stream() + .filter(entry -> serialNumber.equals(entry.getSerialNumber())) + .findFirst() + .map(CartEntry::getQuantity) + .orElse(0L); + } + + public void checkOut(SalesList salesList, Inventory inventory) throws DrugNotFoundException { + salesList.addSale(this); + for (CartEntry entry : entries) { + inventory.removeFromStock(entry.getSerialNumber(), entry.getQuantity()); + } + this.entries = new ArrayList<>(); + } +} diff --git a/src/main/java/seedu/stocker/drugs/CartEntry.java b/src/main/java/seedu/stocker/drugs/CartEntry.java new file mode 100644 index 0000000000..d3bc7c412b --- /dev/null +++ b/src/main/java/seedu/stocker/drugs/CartEntry.java @@ -0,0 +1,56 @@ +package seedu.stocker.drugs; + +public class CartEntry { + + private String serialNumber; + private long quantity; + private double totalCost; + private Drug drug; + + public CartEntry(String serialNumber, long quantity) { + this.serialNumber = serialNumber; + this.quantity = quantity; + this.totalCost = calculateTotalCost(); + } + + public CartEntry(String serialNumber, long quantity, Drug drug) { + this.serialNumber = serialNumber; + this.quantity = quantity; + this.drug = drug; + } + + public String getSerialNumber() { + return this.serialNumber; + } + + public double getSellingPrice() { + if (drug != null) { + return drug.getSellingPrice(); + } else { + return 0.0; // Default value, please adjust as needed. + } + } + + public double calculateTotalCost() { + return this.quantity * getSellingPrice(); + } + + public long getQuantity() { + return this.quantity; + } + + public void incrQuantity(long delta) { + this.quantity += delta; + } + + /** + * Returns a string representation of the stock entry. + * + * @return A string containing the key and quantity date of the stock entry. + */ + @Override + public String toString() { + return "Serial number: " + this.serialNumber + + ", Quantity: " + this.quantity; + } +} diff --git a/src/main/java/seedu/stocker/drugs/Description.java b/src/main/java/seedu/stocker/drugs/Description.java new file mode 100644 index 0000000000..0e2228408b --- /dev/null +++ b/src/main/java/seedu/stocker/drugs/Description.java @@ -0,0 +1,21 @@ +package seedu.stocker.drugs; + +import java.util.HashMap; +import java.util.Map; + +public class Description { + private static Map descriptions = new HashMap<>(); + + public static void addDescription(String drugName, String description) { + descriptions.put(drugName, description); + } + + public static String getDescription(String drugName) { + return descriptions.get(drugName); + } + + public static Map getAllDescriptions() { + return descriptions; + } + +} diff --git a/src/main/java/seedu/stocker/drugs/Drug.java b/src/main/java/seedu/stocker/drugs/Drug.java new file mode 100644 index 0000000000..7a5e3df3f5 --- /dev/null +++ b/src/main/java/seedu/stocker/drugs/Drug.java @@ -0,0 +1,78 @@ +package seedu.stocker.drugs; + +public class Drug { + + public String name; + String expiryDate; + private double vendorPrice; + private double sellingPrice; + + public Drug(String name, String expiryDate, double sellingPrice) { + this.name = name; + this.expiryDate = expiryDate; + this.sellingPrice = sellingPrice; + } + + /** + * Gets the name of the drug. + * + * @return The name of the drug. + */ + public String getName() { + return name; + } + + /** + * Sets the name of the drug. + * + * @param name The new name to set for the drug. + */ + public void setName(String name) { + this.name = name; + } + + /** + * Gets the expiry date of the drug. + * + * @return The expiry date of the drug. + */ + public String getExpiryDate() { + return expiryDate; + } + + /** + * Sets the expiry date of the drug. + * + * @param expiryDate The new expiry date to set for the drug. + */ + public void setExpiryDate(String expiryDate) { + this.expiryDate = expiryDate; + } + + public double getVendorPrice() { + return vendorPrice; + } + + public void setVendorPrice(double vendorPrice) { + this.vendorPrice = vendorPrice; + } + + public double getSellingPrice() { + return sellingPrice; + } + + public void setSellingPrice(double sellingPrice) { + this.sellingPrice = sellingPrice; + } + + /** + * Returns a string representation of the drug. + * + * @return A string containing the name and expiry date of the drug. + */ + @Override + public String toString() { + return "Name: " + name + ", Expiry Date: " + expiryDate + + ", Vendor Price: $" + vendorPrice + ", Selling Price: $" + sellingPrice; + } +} diff --git a/src/main/java/seedu/stocker/drugs/Inventory.java b/src/main/java/seedu/stocker/drugs/Inventory.java new file mode 100644 index 0000000000..21d6ae73c4 --- /dev/null +++ b/src/main/java/seedu/stocker/drugs/Inventory.java @@ -0,0 +1,50 @@ +package seedu.stocker.drugs; + +import java.util.ArrayList; +import java.util.List; +import java.util.HashMap; +import java.util.Map; + +import seedu.stocker.exceptions.DrugNotFoundException; + +public class Inventory { + + public HashMap stock; + + public Inventory() { + this.stock = new HashMap(); + } + + public void addNewDrug(String serialNumber, Drug drug, long quantity) { + this.stock.put(serialNumber, new StockEntry(drug, quantity, serialNumber)); + } + + public void removeFromStock(String serialNumber, long quantity) throws DrugNotFoundException { + if (this.stock.containsKey(serialNumber)) { + this.stock.get(serialNumber).decrQuantity(quantity); + } else { + throw new DrugNotFoundException(""); + } + } + + public List> getStockEntries() { + return new ArrayList<>(this.stock.entrySet()); + } + + public StockEntry deleteDrug(String serialNumber) throws DrugNotFoundException { + if (this.stock.containsKey(serialNumber)) { + return stock.remove(serialNumber); + } else { + throw new DrugNotFoundException(""); + } + } + + public StockEntry get(String serialNumber) { + return this.stock.get(serialNumber); + } + + public void clearInventory(){ + stock.clear(); + } +} + diff --git a/src/main/java/seedu/stocker/drugs/SalesList.java b/src/main/java/seedu/stocker/drugs/SalesList.java new file mode 100644 index 0000000000..85a3622005 --- /dev/null +++ b/src/main/java/seedu/stocker/drugs/SalesList.java @@ -0,0 +1,31 @@ +package seedu.stocker.drugs; + +import java.util.ArrayList; +import java.util.List; + +public class SalesList { + + private ArrayList validatedCarts; + + public SalesList() { + this.validatedCarts = new ArrayList<>(); + } + + public void addSale(Cart cart) { + this.validatedCarts.add(cart); + } + + public List getAllSales() { + return this.validatedCarts; + } + + public void addSoldItem(Drug drug, String serialNumber, long quantity, double sellingPrice, Inventory inventory) { + Cart cart = new Cart(); + cart.addEntry(drug, serialNumber, quantity, sellingPrice, inventory); + this.validatedCarts.add(cart); + } + + public void clearSales(){ + validatedCarts.clear(); + } +} diff --git a/src/main/java/seedu/stocker/drugs/StockEntry.java b/src/main/java/seedu/stocker/drugs/StockEntry.java new file mode 100644 index 0000000000..36d3322459 --- /dev/null +++ b/src/main/java/seedu/stocker/drugs/StockEntry.java @@ -0,0 +1,77 @@ +package seedu.stocker.drugs; +import seedu.stocker.ui.Ui; + + + +public class StockEntry { + private Ui ui; + + private final Drug drug; + private final String serialNumber; // Add serial number field + + private long quantity; + private long thresholdQuantity; + + public StockEntry(Drug drug, long quantity, String serialNumber) { + this.drug = drug; + this.quantity = quantity; + this.serialNumber = serialNumber; // Initialise serial number + this.thresholdQuantity = 100; + } + + public String getSerialNumber() { + return this.serialNumber; // Getter for serial number + } + + public Drug getDrug() { + return this.drug; + } + + public long getQuantity() { + return this.quantity; + } + + public void setQuantity(long quantity) { + this.quantity = quantity; + } + + public void setThresholdQuantity(long thresholdQuantity) { + this.thresholdQuantity = thresholdQuantity; + checkThresholdAndAlert(); + } + + public long getThresholdQuantity() { + return this.thresholdQuantity; + } + + public void incrQuantity(long delta) { + this.quantity += delta; + checkThresholdAndAlert(); + } + + public void decrQuantity(long delta) { + assert(this.quantity > delta); + this.quantity -= delta; + checkThresholdAndAlert(); + } + + public void checkThresholdAndAlert() { + if (quantity < thresholdQuantity) { + System.out.println("|| ALERT! " + this.drug.getName() + " is below the threshold level"); + } + } + + + /** + * Returns a string representation of the stock entry. + * + * @return A string containing the key and quantity date of the stock entry. + */ + @Override + public String toString() { + return "Name: " + this.drug.getName() + + ", Expiry date: " + this.drug.getExpiryDate() + + ", Serial number: " + this.getSerialNumber() + + ", Quantity: " + this.quantity; + } +} diff --git a/src/main/java/seedu/stocker/exceptions/DrugNotFoundException.java b/src/main/java/seedu/stocker/exceptions/DrugNotFoundException.java new file mode 100644 index 0000000000..21b6835ae9 --- /dev/null +++ b/src/main/java/seedu/stocker/exceptions/DrugNotFoundException.java @@ -0,0 +1,10 @@ +package seedu.stocker.exceptions; + +/** + * Signals that the drug has not been found in a specific list. + */ +public class DrugNotFoundException extends Exception { + public DrugNotFoundException(String message) { + super(message); + } +} diff --git a/src/main/java/seedu/stocker/exceptions/InvalidDrugFormatException.java b/src/main/java/seedu/stocker/exceptions/InvalidDrugFormatException.java new file mode 100644 index 0000000000..8c11846cb5 --- /dev/null +++ b/src/main/java/seedu/stocker/exceptions/InvalidDrugFormatException.java @@ -0,0 +1,10 @@ +package seedu.stocker.exceptions; + +/** + * Signals that the laod function cannot read the serialized drug. + */ +public class InvalidDrugFormatException extends Exception { + public InvalidDrugFormatException(String message) { + super(message); + } +} diff --git a/src/main/java/seedu/stocker/exceptions/StockerException.java b/src/main/java/seedu/stocker/exceptions/StockerException.java new file mode 100644 index 0000000000..c068a5225e --- /dev/null +++ b/src/main/java/seedu/stocker/exceptions/StockerException.java @@ -0,0 +1,4 @@ +package seedu.stocker.exceptions; + +public class StockerException extends Exception{ +} diff --git a/src/main/java/seedu/stocker/parser/Parser.java b/src/main/java/seedu/stocker/parser/Parser.java new file mode 100644 index 0000000000..4e518176fa --- /dev/null +++ b/src/main/java/seedu/stocker/parser/Parser.java @@ -0,0 +1,514 @@ +package seedu.stocker.parser; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import seedu.stocker.commands.AddCommand; +import seedu.stocker.commands.Command; +import seedu.stocker.commands.DeleteCommand; +import seedu.stocker.commands.ExitCommand; +import seedu.stocker.commands.FindCommand; +import seedu.stocker.commands.HelpCommand; +import seedu.stocker.commands.IncorrectCommand; +import seedu.stocker.commands.ListCommand; +import seedu.stocker.commands.LoginCommand; +import seedu.stocker.commands.RegisterCommand; +import seedu.stocker.commands.SaveCommand; +import seedu.stocker.commands.AddVendorCommand; +import seedu.stocker.commands.ShowStockLevelCommand; +import seedu.stocker.commands.ViewCartCommand; +import seedu.stocker.commands.AddToCartCommand; +import seedu.stocker.commands.CheckOutCommand; +import seedu.stocker.commands.ListVendorCommand; +import seedu.stocker.commands.SetThresholdCommand; +import seedu.stocker.commands.ListThresholdCommand; +import seedu.stocker.commands.AddDescriptionCommand; +import seedu.stocker.commands.GetDescriptionCommand; +import seedu.stocker.commands.ListDescriptionsCommand; +import seedu.stocker.commands.AddVendorSupplyCommand; +import seedu.stocker.commands.FindVendorSupplyCommand; +import seedu.stocker.commands.ListVendorSupplyCommand; +import seedu.stocker.commands.DeleteVendorCommand; +import seedu.stocker.commands.DeleteVendorSupplyCommand; +import seedu.stocker.commands.ListSalesCommand; +import seedu.stocker.commands.SaveSalesCommand; + +import static seedu.stocker.common.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.stocker.common.Messages.MESSAGE_INVALID_QUANTITY; +import static seedu.stocker.common.Messages.MESSAGE_INVALID_DATE; + + +public class Parser { + public Parser() { + } + + /** + * Parses user input into command for execution. + * + * @param userInput full user input string + * @return the command based on the user input + */ + public Command parseCommand(String userInput) { + //accounting for the \ character + String inputWithoutSpaces = userInput.replaceAll(" ", "").trim(); + if (inputWithoutSpaces.matches("^\\\\+$")) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE)); + } + + String[] words = userInput.trim().split(" ", 2); // split the input into command and arguments + if (words.length == 0) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE)); + } + + final String commandWord = words[0]; + final String arguments = userInput.replaceFirst(commandWord, "").trim(); + + switch (commandWord) { + case AddCommand.COMMAND_WORD: + return prepareAddCommand(arguments); + + case FindCommand.COMMAND_WORD: + return prepareFindCommand(arguments); + + case AddToCartCommand.COMMAND_WORD: + return prepareAddToCartCommand(arguments); + + case AddDescriptionCommand.COMMAND_WORD: + return prepareAddDescriptionCommand(arguments); + + case GetDescriptionCommand.COMMAND_WORD: + return prepareGetDescriptionCommand(arguments); + + case ListDescriptionsCommand.COMMAND_WORD: + return prepareListDescriptionsCommand(arguments); + + case AddVendorSupplyCommand.COMMAND_WORD: + return prepareAddVendorSupplyCommand(arguments); + + case DeleteCommand.COMMAND_WORD: + return prepareDeleteCommand(arguments); + + case ListVendorSupplyCommand.COMMAND_WORD: + return prepareListVendorSupplyCommand(arguments); + + case FindVendorSupplyCommand.COMMAND_WORD: + return prepareFindVendorSupplyCommand(arguments); + + case DeleteVendorSupplyCommand.COMMAND_WORD: + return prepareDeleteVendorSupplyCommand(arguments); + + case ListSalesCommand.COMMAND_WORD: + return prepareListSalesCommand(arguments); + + case SaveSalesCommand.COMMAND_WORD: + return prepareSaveSalesCommand(arguments); + + case CheckOutCommand.COMMAND_WORD: + if (arguments.isEmpty()) { + return new CheckOutCommand(); + } else { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + CheckOutCommand.MESSAGE_USAGE)); + } + + case ExitCommand.COMMAND_WORD: + if (arguments.isEmpty()) { + return new ExitCommand(); + } else { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ExitCommand.MESSAGE_USAGE)); + } + + case HelpCommand.COMMAND_WORD: + return prepareHelpCommand(arguments); + + case ListCommand.COMMAND_WORD: + return prepareListCommand(arguments); + + case ViewCartCommand.COMMAND_WORD: + if (arguments.isEmpty()) { + return new ViewCartCommand(); + } else { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + ViewCartCommand.MESSAGE_USAGE)); + } + + case RegisterCommand.COMMAND_WORD: + return new RegisterCommand(); + + case LoginCommand.COMMAND_WORD: + return new LoginCommand(); + + case SaveCommand.COMMAND_WORD: + if (arguments.isEmpty()) { + return new SaveCommand(); + } else { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + SaveCommand.MESSAGE_USAGE)); + } + + case AddVendorCommand.COMMAND_WORD: + return prepareAddVendorCommand(arguments); + + case DeleteVendorCommand.COMMAND_WORD: + return prepareDeleteVendorCommand(arguments); + + case ShowStockLevelCommand.COMMAND_WORD: + if (arguments.isEmpty()) { + return new ShowStockLevelCommand(); + } else { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + ShowStockLevelCommand.MESSAGE_USAGE)); + } + + case ListVendorCommand.COMMAND_WORD: + if (arguments.isEmpty()) { + return new ListVendorCommand(); + } else { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + ListVendorCommand.MESSAGE_USAGE)); + } + + case SetThresholdCommand.COMMAND_WORD: + return prepareSetThresholdCommand(arguments); + + case ListThresholdCommand.COMMAND_WORD: + if (arguments.isEmpty()) { + return new ListThresholdCommand(); + } else { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + ListThresholdCommand.MESSAGE_USAGE)); + } + + default: + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE)); + } + } + + /** + * Parses arguments in the context of the add drug command. + * + * @param args full command args string + * @return the prepared command + */ + private Command prepareAddCommand(String args) { + try { + Pattern pattern = Pattern.compile("/n (.*) /d (.*) /s (.*) /q (.*) /p (.*)"); + Matcher matcher = pattern.matcher(args); + if (matcher.matches() && matcher.groupCount() == 5) { + String name = matcher.group(1).trim(); + String expiryDate = matcher.group(2).trim(); + String serialNumber = matcher.group(3).trim(); + long quantity = Long.parseLong(matcher.group(4)); + double sellingPrice = Double.parseDouble(matcher.group(5)); + + if (quantity < 1 || quantity > 999999999) { + return new IncorrectCommand("Quantity should be between 1 and 999999999."); + } + if (name.isEmpty()) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + AddCommand.MESSAGE_USAGE)); + } + if (!isValidSerialNumber(serialNumber)) { + return new IncorrectCommand("Serial number should be in the format " + + "of three capital letters followed by three numbers (e.g., ABC123)."); + } + // Check if the expiry date has a valid format + if (!isValidDateFormat(expiryDate)) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + AddCommand.MESSAGE_USAGE)); + } + // Check if sellingPrice is in the valid range (0.01 to 1000.00) and has up to 2 decimal places + if (sellingPrice < 0.01 || sellingPrice > 1000.00) { + return new IncorrectCommand("Selling price should be between 0.01 and 1000.00."); + } + + return new AddCommand(name, expiryDate, serialNumber, quantity, sellingPrice); + } else { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); + } + } catch (NumberFormatException e) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); + } + } + + /** + * Checks if the given serial number string has a valid format. + * + * @param serialNumber The serial number string to be validated. + * @return True if the serial number has a valid format, false otherwise. + */ + private boolean isValidSerialNumber(String serialNumber) { + String regex = "^[A-Z]{3}\\d{3}$"; + return serialNumber.matches(regex); + } + + + /** + * Checks if the given date string has a valid "dd/mm/yyyy" format. + * + * @param date The date string to be validated. + * @return True if the date has a valid format, false otherwise. + */ + private boolean isValidDateFormat(String date) { + String regex = "^(0[1-9]|[12][0-9]|3[01])/(0[1-9]|1[0-2])/(\\d{4})$"; + return date.matches(regex); + } + + + /** + * Parses arguments in the context of the delete command. + * + * @param args full command args string + * @return the prepared command + */ + private Command prepareDeleteCommand(String args) { + Pattern pattern = Pattern.compile("/s (.*)"); + Matcher matcher = pattern.matcher(args); + if (matcher.matches() && matcher.groupCount() == 1) { + String name = matcher.group(1); + return new DeleteCommand(name); + } else { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE)); + } + } + + /** + * Parses arguments in the context of the add drug to cart command. + * + * @param args full command args string + * @return the prepared command + */ + private Command prepareAddToCartCommand(String args) { + try { + Pattern pattern = Pattern.compile("/s (.*) /q (.*)"); + Matcher matcher = pattern.matcher(args); + if (matcher.matches() && matcher.groupCount() == 2) { + String name = matcher.group(1); + long quantity = Long.parseLong(matcher.group(2)); + if (quantity < 1) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_QUANTITY, AddCommand.MESSAGE_USAGE)); + } + return new AddToCartCommand(name, quantity); + } else { + return new IncorrectCommand( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddToCartCommand.MESSAGE_USAGE)); + } + } catch (NumberFormatException e) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); + } + } + + private Command prepareFindCommand(String args) { + String[] findArgs = args.split(" ", 2); + if (findArgs.length == 2) { + String criterion = findArgs[0]; + String keyword = findArgs[1]; + if (criterion.equals("/n") || criterion.equals("/s")) { + return new FindCommand(keyword, criterion); + } else if (criterion.equals("/d")) { + if (isValidDateFormat(keyword)) { + return new FindCommand(keyword, criterion); + } else { + return new IncorrectCommand(String.format(MESSAGE_INVALID_DATE, FindCommand.MESSAGE_USAGE)); + } + } else { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE)); + } + } else { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE)); + } + } + + private Command prepareAddVendorCommand(String args) { + Pattern pattern = Pattern.compile("/v (.*)"); + Matcher matcher = pattern.matcher(args); + if (matcher.matches() && matcher.groupCount() == 1) { + String vendorName = matcher.group(1).trim(); + return new AddVendorCommand(vendorName); + } + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddVendorCommand.MESSAGE_USAGE)); + } + + private Command prepareDeleteVendorCommand(String args) { + Pattern pattern = Pattern.compile("/v (.*)"); + Matcher matcher = pattern.matcher(args); + if (matcher.matches() && matcher.groupCount() == 1) { + String vendorName = matcher.group(1).trim(); + return new DeleteVendorCommand(vendorName); + } + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + DeleteVendorCommand.MESSAGE_USAGE)); + } + + private Command prepareSetThresholdCommand(String args) { + try { + Pattern pattern = Pattern.compile("/s (.*) /tq (.*)"); + Matcher matcher = pattern.matcher(args); + if (matcher.matches() && matcher.groupCount() == 2) { + String name = matcher.group(1); + long threshold = Long.parseLong(matcher.group(2)); + return new SetThresholdCommand(name, threshold); + } else { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + SetThresholdCommand.MESSAGE_USAGE)); + } + } catch (NumberFormatException e) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + SetThresholdCommand.MESSAGE_USAGE)); + } + } + + private Command prepareAddDescriptionCommand(String args) { + Pattern pattern = Pattern.compile("/n (.*) /desc (.*)"); + Matcher matcher = pattern.matcher(args); + if (matcher.matches() && matcher.groupCount() == 2) { + String name = matcher.group(1).trim(); + String description = matcher.group(2).trim(); + if (!name.isEmpty() && !description.isEmpty()) { + return new AddDescriptionCommand(name, description); + } else { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + AddDescriptionCommand.MESSAGE_USAGE)); + } + } else { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + AddDescriptionCommand.MESSAGE_USAGE)); + } + } + + private Command prepareGetDescriptionCommand(String args) { + Pattern pattern = Pattern.compile("/n (.*)"); + Matcher matcher = pattern.matcher(args); + if (matcher.matches() && matcher.groupCount() == 1) { + String name = matcher.group(1).trim(); + if (!name.isEmpty()) { + return new GetDescriptionCommand(name); + } else { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + GetDescriptionCommand.MESSAGE_USAGE)); + } + } else { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + GetDescriptionCommand.MESSAGE_USAGE)); + } + } + + private Command prepareListDescriptionsCommand(String args) { + if (args.isEmpty()) { + return new ListDescriptionsCommand(); + } else { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + ListDescriptionsCommand.MESSAGE_USAGE)); + } + } + + private Command prepareAddVendorSupplyCommand(String args) { + Pattern pattern = Pattern.compile("/v (.*) /n (.*)"); + Matcher matcher = pattern.matcher(args); + if (matcher.matches() && matcher.groupCount() == 2) { + String vendor = matcher.group(1).trim(); + String drug = matcher.group(2).trim(); + if (!vendor.isEmpty() && !drug.isEmpty()) { + return new AddVendorSupplyCommand(vendor, drug); + } else { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + AddVendorSupplyCommand.MESSAGE_USAGE)); + } + } else { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + AddVendorSupplyCommand.MESSAGE_USAGE)); + } + } + + private Command prepareListVendorSupplyCommand(String args) { + Pattern pattern = Pattern.compile("/v (.*)"); + Matcher matcher = pattern.matcher(args); + if (matcher.matches() && matcher.groupCount() == 1) { + String vendor = matcher.group(1).trim(); + if (!vendor.isEmpty()) { + return new ListVendorSupplyCommand(vendor); + } else { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + ListVendorSupplyCommand.MESSAGE_USAGE)); + } + } else { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + ListVendorSupplyCommand.MESSAGE_USAGE)); + } + } + + private Command prepareFindVendorSupplyCommand(String args) { + Pattern pattern = Pattern.compile("/n (.*)"); + Matcher matcher = pattern.matcher(args); + if (matcher.matches() && matcher.groupCount() == 1) { + String drug = matcher.group(1).trim(); + if (!drug.isEmpty()) { + return new FindVendorSupplyCommand(drug); + } else { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + FindVendorSupplyCommand.MESSAGE_USAGE)); + } + } else { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + FindVendorSupplyCommand.MESSAGE_USAGE)); + } + } + + private Command prepareDeleteVendorSupplyCommand(String args) { + Pattern pattern = Pattern.compile("/v (.*) /n (.*)"); + Matcher matcher = pattern.matcher(args); + if (matcher.matches() && matcher.groupCount() == 2) { + String vendor = matcher.group(1).trim(); + String drug = matcher.group(2).trim(); + if (!vendor.isEmpty() && !drug.isEmpty()) { + return new DeleteVendorSupplyCommand(vendor, drug); + } else { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + DeleteVendorSupplyCommand.MESSAGE_USAGE)); + } + } else { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + DeleteVendorSupplyCommand.MESSAGE_USAGE)); + } + } + + private Command prepareListCommand(String args) { + // Check if there are no arguments for the "list" command + if (args.isEmpty()) { + return new ListCommand(); + } else { + // Handle the case where extra arguments are provided for "list" + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ListCommand.MESSAGE_USAGE)); + } + } + + private Command prepareHelpCommand(String args) { + // Check if there are no arguments for the "list" command + if (args.isEmpty()) { + return new HelpCommand(); + } else { + // Handle the case where extra arguments are provided for "help" + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE)); + } + } + + private Command prepareListSalesCommand(String args) { + // Check if there are no arguments for the "listSales" command + if (args.isEmpty()) { + return new ListSalesCommand(); + } else { + // Handle the case where extra arguments are provided for "listSales" + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ListSalesCommand.MESSAGE_USAGE)); + } + } + + private Command prepareSaveSalesCommand(String args) { + // Check if there are no arguments for the "saveSales" command + if (args.isEmpty()) { + return new SaveSalesCommand(); + } else { + // Handle the case where extra arguments are provided for "saveSales" + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, SaveSalesCommand.MESSAGE_USAGE)); + } + } +} diff --git a/src/main/java/seedu/stocker/storage/Storage.java b/src/main/java/seedu/stocker/storage/Storage.java new file mode 100644 index 0000000000..d195eb6c66 --- /dev/null +++ b/src/main/java/seedu/stocker/storage/Storage.java @@ -0,0 +1,140 @@ +package seedu.stocker.storage; + + +import seedu.stocker.drugs.Drug; +import seedu.stocker.drugs.Inventory; +import seedu.stocker.drugs.SalesList; +import seedu.stocker.drugs.StockEntry; + +import java.io.BufferedWriter; +import java.io.FileWriter; +import java.io.PrintWriter; +import java.io.File; +import java.io.IOException; +import java.util.Scanner; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + +/** + * Represents an object to handle writing to txt files and appending to them. + */ +public class Storage { + + private Inventory inventory; + + public Storage(Inventory inventory) { + this.inventory = inventory; + } + /** + * Writes to the first line of a txt file + * can be used to clear a txt file as well. + * + * @param filePath relative path of file to write to + * @param textToAdd what to write to the file + * @throws IOException if invalid input is entered + */ + public void writeToFile(String filePath, String textToAdd) throws IOException { + FileWriter fw = new FileWriter(filePath); + fw.write(textToAdd); + fw.close(); + } + + /** + * Appends given text to next line of txt file. + * + * @param filePath relative path of file to write to + * @param textToAppend what to write to the file + * @throws IOException if invalid input is given + */ + public void appendToFile(String filePath, String textToAppend) throws IOException { + BufferedWriter bf = new BufferedWriter(new FileWriter(filePath,true)); + bf.write(textToAppend); + bf.newLine(); + bf.close(); + } + + /** + * Loads drugs from txt file into inventory system. + * + * @param filePath Relative path to file containing list of drugs to be loaded. + * @throws IOException if file is not found + */ + public void loadFileContents(String filePath) throws IOException { + File holder = new File("./drugs.txt"); + if (!holder.exists()) { + holder.createNewFile(); + } + File f = new File(filePath); + Scanner reader = new Scanner(f); + + Pattern pattern = Pattern.compile( + "Name: (.*), Expiry Date: (.*), Serial Number: (.*), Quantity: (.*), Selling Price: (.*)" + ); + while (reader.hasNextLine()) { + Matcher matcher = pattern.matcher(reader.nextLine()); + if (matcher.matches() && matcher.groupCount() == 5) { + String name = matcher.group(1); + String expiryDate = matcher.group(2); + String serialNumber = matcher.group(3); // Extract serial number + Long quantity = Long.parseLong(matcher.group(4)); + double sellingPrice = Double.parseDouble(matcher.group(5)); + + Drug drug = new Drug(name, expiryDate, sellingPrice); + inventory.addNewDrug(serialNumber, drug, quantity); + } else { + System.out.println("Malicious changes were made in drugs.txt, overwriting old drug file," + + " please add new drugs and save again"); + FileWriter fw = new FileWriter("./drugs.txt", false); + PrintWriter pw = new PrintWriter(fw, false); + pw.flush(); + pw.close(); + fw.close(); + inventory.clearInventory(); + break; + + } + } + } + + public void loadSoldItems(String filePath, SalesList salesList) throws IOException { + File file = new File("./soldItems.txt"); + if (!file.exists()) { + file.createNewFile(); + } + + Scanner reader = new Scanner(file); + Pattern pattern = Pattern.compile( + "Name: (.*), Serial Number: (.*), Quantity: (.*), Selling Price: (.*)" + ); + + while (reader.hasNextLine()) { + String line = reader.nextLine(); + Matcher matcher = pattern.matcher(line); + + if (matcher.matches() && matcher.groupCount() == 4) { + String name = matcher.group(1); + String serialNumber = matcher.group(2); // Extract serial number + long quantity = Long.parseLong(matcher.group(3)); // Extract quantity + double sellingPrice = Double.parseDouble(matcher.group(4)); // Extract selling price + + StockEntry stockEntry = inventory.get(serialNumber); + Drug drug = stockEntry.getDrug(); + + // Add the sold item to the sales list + salesList.addSoldItem(drug, serialNumber, quantity, sellingPrice, inventory); + } else { + System.out.println("Malicious changes were made in soldItems.txt, overwriting old drug file," + + " please add new drugs and save again"); + // Handle any malicious changes + FileWriter fw = new FileWriter("./soldItems.txt", false); + PrintWriter pw = new PrintWriter(fw, false); + pw.flush(); + pw.close(); + fw.close(); + salesList.clearSales(); + break; + } + } + } +} diff --git a/src/main/java/seedu/stocker/ui/Ui.java b/src/main/java/seedu/stocker/ui/Ui.java new file mode 100644 index 0000000000..decfca24df --- /dev/null +++ b/src/main/java/seedu/stocker/ui/Ui.java @@ -0,0 +1,204 @@ +package seedu.stocker.ui; + +import java.util.Scanner; +import java.io.PrintStream; +import java.io.InputStream; +import java.util.Optional; +import java.util.List; +import java.util.ArrayList; + +import seedu.stocker.commands.CommandResult; + +import static seedu.stocker.common.Messages.MESSAGE_USERNAME_INPUT; +import static seedu.stocker.common.Messages.MESSAGE_USER_ALREADY_EXIST; +import static seedu.stocker.common.Messages.MESSAGE_LOGIN_WELCOME; +import static seedu.stocker.common.Messages.MESSAGE_SUCCESSFUL_LOGIN; +import static seedu.stocker.common.Messages.MESSAGE_NO_BLANK_NAME_ALLOWED; +import static seedu.stocker.common.Messages.MESSAGE_NO_BLANK_PASSWORD_ALLOWED; +import static seedu.stocker.common.Messages.MESSAGE_PASSWORD_INPUT; +import static seedu.stocker.common.Messages.MESSAGE_WELCOME; +import static seedu.stocker.common.Messages.MESSAGE_INVALID_CHOICE; +import static seedu.stocker.common.Messages.MESSAGE_GOODBYE; +import static seedu.stocker.common.Messages.MESSAGE_INVALID_USERNAME_OR_PASSWORD; +import static seedu.stocker.common.Messages.MESSAGE_SUCCESSFUL_REGISTRATION; +import static seedu.stocker.common.Messages.MESSAGE_INIT_FAILED; +import static seedu.stocker.common.Messages.MESSAGE_EXECUTION_FAILED; +import static seedu.stocker.common.Messages.MESSAGE_INVALID_LOGIN_CHARACTER; + + +public class Ui { + + /** + * Offset required to convert between 1-indexing and 0-indexing. + */ + public static final int DISPLAYED_INDEX_OFFSET = 1; + + /** + * A decorative prefix added to the beginning of lines printed by Jerry + */ + private static final String LINE_PREFIX = "|| "; + + private static final String DIVIDER = "==================================================="; + + /** + * A platform independent line separator. + */ + private static final String LS = System.lineSeparator(); + + /** + * Format of indexed list item + */ + private static final String MESSAGE_INDEXED_LIST_ITEM = "\t%1$d. %2$s"; + + private final Scanner in; + private final PrintStream out; + + public Ui() { + this(System.in, System.out); + } + + public Ui(InputStream in, PrintStream out) { + this.in = new Scanner(in); + this.out = out; + } + + /** + * Prompts for the command and reads the text entered by the user. + * Ignores empty, pure whitespace, and comment lines. + * Echos the command back to the user. + * + * @return command (full line) entered by the user + */ + public String getUserCommand() { + out.print(LINE_PREFIX + "Enter command: "); + String fullUserInput = in.nextLine(); + + return fullUserInput; + } + + /** + * Shows message(s) to the user + */ + public void showToUser(String... message) { + for (String m : message) { + out.println(LINE_PREFIX + m.replace("\n", LS + LINE_PREFIX)); + } + } + + public void showLoginMessage() { + showToUser(DIVIDER, MESSAGE_LOGIN_WELCOME, DIVIDER); + } + + public void showUsernameMessage() { + showToUser(MESSAGE_USERNAME_INPUT); + } + + public void showExecutionFailedMessage() { + showToUser(MESSAGE_EXECUTION_FAILED); + } + + public void showPasswordMessage() { + showToUser(MESSAGE_PASSWORD_INPUT); + } + + public void showSuccessfulRegistrationMessage() { + showToUser(MESSAGE_SUCCESSFUL_REGISTRATION); + } + + public void showSuccessfulLoginMessage() { + showToUser(MESSAGE_SUCCESSFUL_LOGIN); + } + + public void showInvalidChoiceMessage() { + showToUser(MESSAGE_INVALID_CHOICE); + } + + public void showUserAlreadyExistMessage() { + showToUser(MESSAGE_USER_ALREADY_EXIST); + } + + public void showEnterChoiceAgainMessage() { + showToUser(MESSAGE_LOGIN_WELCOME); + } + + public void showInvalidUsernameOrPasswordMessage() { + showToUser(MESSAGE_INVALID_USERNAME_OR_PASSWORD); + } + + public void showBlankNameMessage() { + showToUser(MESSAGE_NO_BLANK_NAME_ALLOWED); + } + + public void showBlankPasswordMessage() { + showToUser(MESSAGE_NO_BLANK_PASSWORD_ALLOWED); + } + + public void showWelcomeMessage() { + showToUser(MESSAGE_WELCOME, DIVIDER, DIVIDER); + } + + public void showInvalidLoginCharacterMessage(){ + showToUser(MESSAGE_INVALID_LOGIN_CHARACTER); + } + + public void showGoodbyeMessage() { + showToUser(MESSAGE_GOODBYE, DIVIDER, DIVIDER); + } + + /** + * Shows the result of a command execution to the user. Includes additional formatting to demarcate different + * command execution segments. + */ + public void showResultToUser(CommandResult result) { + final Optional> resultElements = result.getRelevantElements(); + if (resultElements.isPresent()) { + showElementsListView(resultElements.get()); + } + showToUser(result.feedbackToUser, DIVIDER); + } + + /** + * Shows a list of strings to the user, formatted as an indexed list. + */ + private void showToUserAsIndexedList(List list) { + showToUser(getIndexedListForViewing(list)); + } + + /** + * Shows a list of drugs to the user, formatted as an indexed list. + */ + private void showElementsListView(List elements) { + final List formattedElements = new ArrayList<>(); + for (int i = 0; i < elements.size(); i++) { + formattedElements.add(elements.get(i).toString()); + } + showToUserAsIndexedList(formattedElements); + } + + public void showInitFailedMessage() { + showToUser(MESSAGE_INIT_FAILED, DIVIDER, DIVIDER); + } + + /** + * Formats a list of strings as a viewable indexed list. + */ + public static String getIndexedListForViewing(List listItems) { + final StringBuilder formatted = new StringBuilder(); + int displayIndex = DISPLAYED_INDEX_OFFSET; + for (String listItem : listItems) { + formatted.append(getIndexedListItem(displayIndex, listItem)).append(LS); + displayIndex++; + } + return formatted.toString(); + } + + /** + * Formats a string as a viewable indexed list item. + * + * @param visibleIndex visible index for this listing + */ + private static String getIndexedListItem(int visibleIndex, String listItem) { + return String.format(MESSAGE_INDEXED_LIST_ITEM, visibleIndex, listItem); + } + +} diff --git a/src/main/java/seedu/stocker/vendors/Vendor.java b/src/main/java/seedu/stocker/vendors/Vendor.java new file mode 100644 index 0000000000..1c110cf5df --- /dev/null +++ b/src/main/java/seedu/stocker/vendors/Vendor.java @@ -0,0 +1,38 @@ +package seedu.stocker.vendors; + +public class Vendor { + + public String name; + + public Vendor(String name) { + this.name = name; + } + + /** + * Gets the name of the vendor. + * + * @return The name of the vendor. + */ + public String getName() { + return name; + } + + /** + * Sets the name of the vendor. + * + * @param name The new name to set for the vendor. + */ + public void setName(String name) { + this.name = name; + } + + /** + * Returns a string representation of the vendor. + * + * @return A string containing the name of the vendor. + */ + @Override + public String toString() { + return "Name: " + name; + } +} diff --git a/src/main/java/seedu/stocker/vendors/VendorSupplyList.java b/src/main/java/seedu/stocker/vendors/VendorSupplyList.java new file mode 100644 index 0000000000..6890fbf042 --- /dev/null +++ b/src/main/java/seedu/stocker/vendors/VendorSupplyList.java @@ -0,0 +1,76 @@ +package seedu.stocker.vendors; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.ArrayList; + +/** + * Represents a class to manage the association between vendors and the drugs they supply. + */ +public class VendorSupplyList { + private static final Map> vendorSuppliedDrugs = new HashMap<>(); + + /** + * Adds a drug to a vendor's supply list. + * + * @param vendorName The name of the vendor. + * @param drugName The name of the drug to be added to the vendor's supply list. + */ + public static void addDrugToVendor(String vendorName, String drugName) { + vendorSuppliedDrugs.computeIfAbsent(vendorName, k -> new ArrayList<>()).add(drugName); + } + + /** + * Retrieves the list of drugs supplied by a specific vendor. + * + * @param vendorName The name of the vendor. + * @return A list of drug names supplied by the vendor. + */ + public static List getDrugsSuppliedByVendor(String vendorName) { + return vendorSuppliedDrugs.getOrDefault(vendorName, new ArrayList<>()); + } + + /** + * Checks if a specific drug is supplied by a vendor. + * + * @param vendorName The name of the vendor. + * @param drugName The name of the drug to check. + * @return true if the drug is supplied by the vendor, false otherwise. + */ + public static boolean containsDrug(String vendorName, String drugName) { + List suppliedDrugs = vendorSuppliedDrugs.get(vendorName); + if (suppliedDrugs != null) { + return suppliedDrugs.contains(drugName); + } else { + return false; // Vendor not found + } + } + + /** + * Removes a drug from a vendor's supply list. + * + * @param vendorName The name of the vendor. + * @param drugName The name of the drug to be removed from the vendor's supply list. + * @return true if the drug was successfully removed, false otherwise. + */ + public static boolean removeDrugFromVendor(String vendorName, String drugName) { + List suppliedDrugs = vendorSuppliedDrugs.get(vendorName); + if (suppliedDrugs != null) { + boolean removed = suppliedDrugs.remove(drugName); + return removed; + } else { + return false; // Vendor not found + } + } + + + /** + * Returns the mapping of vendors to the drugs they supply. + * + * @return A map where each key (vendor name) is associated with a list of drugs they supply. + */ + public static Map> getVendorSuppliedDrugs() { + return vendorSuppliedDrugs; + } +} diff --git a/src/main/java/seedu/stocker/vendors/VendorsList.java b/src/main/java/seedu/stocker/vendors/VendorsList.java new file mode 100644 index 0000000000..61654ce0a3 --- /dev/null +++ b/src/main/java/seedu/stocker/vendors/VendorsList.java @@ -0,0 +1,34 @@ +package seedu.stocker.vendors; + +import java.util.ArrayList; +import java.util.List; + +public class VendorsList { + + public ArrayList vendorArrayList; + + public VendorsList(){ + vendorArrayList = new ArrayList<>(); + } + + /** + * Adds new vendor to vendor list + * + * @param vendor + */ + public void addNewVendor(Vendor vendor){ + vendorArrayList.add(vendor); + } + + public void deleteVendor(Vendor vendor){ + vendorArrayList.remove(vendor); + } + + /** + * Gets details of all vendors + * @return List vendor + */ + public List getVendorEntries() { + return new ArrayList<>(this.vendorArrayList); + } +} diff --git a/src/test/java/seedu/duke/DukeTest.java b/src/test/java/seedu/stocker/StockerTest.java similarity index 80% rename from src/test/java/seedu/duke/DukeTest.java rename to src/test/java/seedu/stocker/StockerTest.java index 2dda5fd651..11f9f206db 100644 --- a/src/test/java/seedu/duke/DukeTest.java +++ b/src/test/java/seedu/stocker/StockerTest.java @@ -1,10 +1,10 @@ -package seedu.duke; +package seedu.stocker; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; -class DukeTest { +class StockerTest { @Test public void sampleTest() { assertTrue(true); diff --git a/src/test/java/seedu/stocker/commands/AddCommandTest.java b/src/test/java/seedu/stocker/commands/AddCommandTest.java new file mode 100644 index 0000000000..878286e0d9 --- /dev/null +++ b/src/test/java/seedu/stocker/commands/AddCommandTest.java @@ -0,0 +1,22 @@ +package seedu.stocker.commands; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +import seedu.stocker.drugs.Inventory; +import seedu.stocker.drugs.SalesList; +import seedu.stocker.drugs.Cart; +import seedu.stocker.vendors.VendorsList; + +class AddCommandTest { + + @Test + public void executeTest() { + AddCommand command = new AddCommand("Paracetamol", "12/05/2024", "12345",12L, 19.90); + command.setData(new Inventory(), new SalesList(), new Cart(), new VendorsList()); + String expectedOutput = "New drug added in the inventory: Paracetamol"; + assertEquals(command.execute().feedbackToUser, new CommandResult(expectedOutput).feedbackToUser); + assertEquals(command.execute().getRelevantElements(), new CommandResult(expectedOutput).getRelevantElements()); + } +} diff --git a/src/test/java/seedu/stocker/commands/AddDescriptionCommandTest.java b/src/test/java/seedu/stocker/commands/AddDescriptionCommandTest.java new file mode 100644 index 0000000000..ec84e30bc0 --- /dev/null +++ b/src/test/java/seedu/stocker/commands/AddDescriptionCommandTest.java @@ -0,0 +1,17 @@ +package seedu.stocker.commands; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class AddDescriptionCommandTest { + + @Test + public void executeTest() { + AddDescriptionCommand command = new AddDescriptionCommand("Panadol", "Pain Relief"); + CommandResult result = command.execute(); + String feedbackMessage = result.feedbackToUser; + + assertEquals("New drug description added for panadol: pain relief", feedbackMessage); + } +} diff --git a/src/test/java/seedu/stocker/commands/AddToCartCommandTest.java b/src/test/java/seedu/stocker/commands/AddToCartCommandTest.java new file mode 100644 index 0000000000..50c0a9fe9c --- /dev/null +++ b/src/test/java/seedu/stocker/commands/AddToCartCommandTest.java @@ -0,0 +1,29 @@ +package seedu.stocker.commands; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.Test; + +import seedu.stocker.drugs.Inventory; +import seedu.stocker.drugs.SalesList; +import seedu.stocker.drugs.Cart; +import seedu.stocker.vendors.VendorsList; + +class AddToCartCommandTest { + + @Test + public void executeTest() { + AddCommand command = new AddCommand("Paracetamol", "12/05/2024", "PARA123", 12L, 21.90); + Inventory inventory = new Inventory(); + Cart cart = new Cart(); + assertEquals(0, cart.getCurrentCart().size()); + SalesList salesList = new SalesList(); + + command.setData(inventory, salesList, cart, new VendorsList()); + command.execute(); + AddToCartCommand command2 = new AddToCartCommand("PARA123", 2L); + command2.setData(inventory, salesList, cart, new VendorsList()); + CommandResult result = command2.execute(); + assertEquals("New drug added in the current cart: Paracetamol", result.feedbackToUser); + assertEquals(1, cart.getCurrentCart().size()); + } +} diff --git a/src/test/java/seedu/stocker/commands/DeleteCommandTest.java b/src/test/java/seedu/stocker/commands/DeleteCommandTest.java new file mode 100644 index 0000000000..1e694172b6 --- /dev/null +++ b/src/test/java/seedu/stocker/commands/DeleteCommandTest.java @@ -0,0 +1,33 @@ +package seedu.stocker.commands; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +import seedu.stocker.drugs.Drug; +import seedu.stocker.drugs.Inventory; +import seedu.stocker.drugs.SalesList; +import seedu.stocker.drugs.Cart; +import seedu.stocker.vendors.VendorsList; + +class DeleteCommandTest { + + @Test + public void executeTest() { + // Create an inventory and add a drug to it + Inventory inventory = new Inventory(); + Drug doliprane = new Drug("Doliprane", "12/06/2035", 15.90); + inventory.addNewDrug("ABC1234", doliprane, 52L); + + // Create a DeleteCommand for the drug + DeleteCommand deleteCommand = new DeleteCommand("ABC1234"); + deleteCommand.setData(inventory, new SalesList(), new Cart(), new VendorsList()); + + // Test the execution of the DeleteCommand + String expectedOutput = "Drug removed from inventory: Doliprane"; + assertEquals(deleteCommand.execute().feedbackToUser, expectedOutput); + + // Ensure that the drug is no longer in the inventory + assertEquals(0, inventory.getStockEntries().size()); + } +} diff --git a/src/test/java/seedu/stocker/commands/FindCommandTest.java b/src/test/java/seedu/stocker/commands/FindCommandTest.java new file mode 100644 index 0000000000..aab51d7bff --- /dev/null +++ b/src/test/java/seedu/stocker/commands/FindCommandTest.java @@ -0,0 +1,125 @@ +package seedu.stocker.commands; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; +import seedu.stocker.drugs.Inventory; +import seedu.stocker.drugs.SalesList; +import seedu.stocker.drugs.Cart; +import seedu.stocker.drugs.Drug; +import seedu.stocker.vendors.VendorsList; + +import static seedu.stocker.common.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +/** + * JUnit test class for the FindCommand. + */ +class FindCommandTest { + + /** + * Tests the execution of the FindCommand with a keyword for drug names. + */ + @Test + public void executeTestByName() { + // Create an instance of FindCommand with a keyword + FindCommand command = new FindCommand("Pa", "/n"); + + // Create a new inventory + Inventory inventory = new Inventory(); + Drug drug2 = new Drug("Panadol", "04/07/2030", 19.90); + inventory.addNewDrug("PAN123", drug2, 20L); + + // Set the modified inventory for the command + command.setData(inventory, new SalesList(), new Cart(), new VendorsList()); + + // Define expected output + String expectedOutput = "1. Name: Panadol, Expiry date: 04/07/2030, Serial number: PAN123, Quantity: 20" + + System.lineSeparator() + + System.lineSeparator() + + "Listed all drugs with the keyword in the inventory."; + + CommandResult actualResult = command.execute(); + + // Test the command's execute method with the modified inventory + assertEquals(expectedOutput, actualResult.getFeedbackToUserFindTest()); + } + + /** + * Tests the execution of the FindCommand with a keyword for drug expiry dates. + */ + @Test + public void executeTestByExpiryDate() { + // Create an instance of FindCommand with a keyword for expiry date + FindCommand command = new FindCommand("01/03/2027", "/d"); + + // Create a new inventory + Inventory inventory = new Inventory(); + Drug drug1 = new Drug("Paracetamol", "01/03/2027", 21.90); + inventory.addNewDrug("PARC124", drug1 , 12L); + + // Set the modified inventory for the command + command.setData(inventory, new SalesList(), new Cart(), new VendorsList()); + + // Define expected output for drugs with matching expiry date + String expectedOutput = "1. Name: Paracetamol, Expiry date: 01/03/2027, Serial number: PARC124, Quantity: 12" + + System.lineSeparator() + + System.lineSeparator() + + "Listed all drugs with the keyword in the inventory."; + + CommandResult actualResult = command.execute(); + + // Test the command's execute method with the modified inventory + assertEquals(expectedOutput, actualResult.getFeedbackToUserFindTest()); + } + + /** + * Tests the execution of the FindCommand with a null keyword, expecting an invalid format message. + */ + @Test + public void executeTestWithNullKeyword() { + // Create an instance of FindCommand with a null keyword + FindCommand command = new FindCommand("", "/n"); + + // Create a new inventory + Inventory inventory = new Inventory(); + + // Set the modified inventory for the command + command.setData(inventory, new SalesList(), new Cart(), new VendorsList()); + + // Define expected output for invalid format + String expectedOutput = String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE); + + CommandResult actualResult = command.execute(); + + // Test the command's execute method with a null keyword + assertEquals(expectedOutput, actualResult.getFeedbackToUserFindTest()); + } + + /** + * Tests the execution of the FindCommand with a keyword for drug serial numbers. + */ + @Test + public void executeTestBySerialNumber() { + // Create an instance of FindCommand with a keyword for serial number + FindCommand command = new FindCommand("SER123", "/s"); + + // Create a new inventory + Inventory inventory = new Inventory(); + Drug drug3 = new Drug("Aspirin", "02/05/2028", 12.90); + inventory.addNewDrug("SER123", drug3, 15L); + + // Set the modified inventory for the command + command.setData(inventory, new SalesList(), new Cart(), new VendorsList()); + + // Define expected output for drugs with matching serial number + String expectedOutput = "1. Name: Aspirin, Expiry date: 02/05/2028, Serial number: SER123, Quantity: 15" + + System.lineSeparator() + + System.lineSeparator() + + "Listed all drugs with the keyword in the inventory."; + + CommandResult actualResult = command.execute(); + + // Test the command's execute method with the modified inventory + assertEquals(expectedOutput, actualResult.getFeedbackToUserFindTest()); + } +} diff --git a/src/test/java/seedu/stocker/commands/GetDescriptionCommandTest.java b/src/test/java/seedu/stocker/commands/GetDescriptionCommandTest.java new file mode 100644 index 0000000000..0139e8a57e --- /dev/null +++ b/src/test/java/seedu/stocker/commands/GetDescriptionCommandTest.java @@ -0,0 +1,20 @@ +package seedu.stocker.commands; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class GetDescriptionCommandTest { + + @Test + public void executeTest() { + AddDescriptionCommand addDescriptionCommand = new AddDescriptionCommand("Panadol", "Pain Relief"); + addDescriptionCommand.execute(); + + GetDescriptionCommand command = new GetDescriptionCommand("Panadol"); + CommandResult result = command.execute(); + String feedbackMessage = result.feedbackToUser; + + assertEquals("pain relief", feedbackMessage); + } +} diff --git a/src/test/java/seedu/stocker/commands/HelpCommandTest.java b/src/test/java/seedu/stocker/commands/HelpCommandTest.java new file mode 100644 index 0000000000..9ee581c17d --- /dev/null +++ b/src/test/java/seedu/stocker/commands/HelpCommandTest.java @@ -0,0 +1,189 @@ +package seedu.stocker.commands; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class HelpCommandTest { + @Test + public void executeTest() { + HelpCommand command = new HelpCommand(); + + String expectedOutput = + System.getProperty("line.separator") + + + "login: Login new user into system." + + System.getProperty("line.separator") + + "Example: login" + + System.getProperty("line.separator") + + System.getProperty("line.separator") + + + "register: Register new user into system." + + System.getProperty("line.separator") + + "Example: register" + + System.getProperty("line.separator") + + System.getProperty("line.separator") + + + "saveDrugs: Saves existing druglist that is loaded into inventory when system is booted up." + + System.getProperty("line.separator") + + "Example: saveDrugs" + + System.getProperty("line.separator") + + System.getProperty("line.separator") + + + "help: Shows program usage instructions. " + + System.getProperty("line.separator") + + "Example: help" + + System.getProperty("line.separator") + + System.getProperty("line.separator") + + + "add: Adds a new drug to the drug list. Parameters: NAME, EXPIRY DATE, SERIAL NUMBER, " + + "QUANTITY, PRICE" + + System.getProperty("line.separator") + + "Example: add /n Doliprane /d 12/06/2035 /s ABC123 /q 52 /p 12.90" + + System.getProperty("line.separator") + + System.getProperty("line.separator") + + + "delete: Removes a drug from drug list. Parameters: Serial Number " + + System.getProperty("line.separator") + + "Example: delete /s " + + System.getProperty("line.separator") + + System.getProperty("line.separator") + + + "list: List all drug information that is being tracked by the system. " + + System.getProperty("line.separator") + + "Example: list" + + System.getProperty("line.separator") + + System.getProperty("line.separator") + + + "stockLevel: List all drugs by quantity level tracked by the system in ascending order. " + + System.getProperty("line.separator") + + "Example: stockLevel" + + System.getProperty("line.separator") + + System.getProperty("line.separator") + + + "find /n: Finds drug in inventory using name." + + System.getProperty("line.separator") + + "Example: find /n panadol" + + System.getProperty("line.separator") + + System.getProperty("line.separator") + + "find /d: Finds drug in inventory using date." + + System.getProperty("line.separator") + + "Example: find /d panadol" + + System.getProperty("line.separator") + + System.getProperty("line.separator") + + "find /s: Finds drug in inventory using serial number." + + System.getProperty("line.separator") + + "Example: find /s ABC123" + + System.getProperty("line.separator") + + System.getProperty("line.separator") + + + "setThreshold: Set the threshold quantity for a drug. (default 100)" + + System.getProperty("line.separator") + + "Example: setThreshold /s TC150 /tq 50" + + System.getProperty("line.separator") + + System.getProperty("line.separator") + + + "listThreshold: List all drugs and their threshold levels." + + System.getProperty("line.separator") + + "Example: listThreshold" + + System.getProperty("line.separator") + + System.getProperty("line.separator") + + + "addToCart: Adds a new drug to the current cart. Parameters: SERIAL NUMBER, QUANTITY," + + System.getProperty("line.separator") + + "Example: addToCart /s ABC123 /q 2" + + System.getProperty("line.separator") + + System.getProperty("line.separator") + + + "viewCart: View the current cart items and the total cost." + + System.getProperty("line.separator") + + "Example: viewCart" + + System.getProperty("line.separator") + + System.getProperty("line.separator") + + + "checkout: Checks out current cart. Parameters:" + + System.getProperty("line.separator") + + "Example: checkout" + + System.getProperty("line.separator") + + System.getProperty("line.separator") + + + "saveSales: Saves the current sold items to a file." + + System.getProperty("line.separator") + + "Example: saveSales" + + System.getProperty("line.separator") + + System.getProperty("line.separator") + + + "listSales: Lists all sold items." + + System.getProperty("line.separator") + + "Example: listSales" + + System.getProperty("line.separator") + + System.getProperty("line.separator") + + + "addVendor: Adds a new vendor to the vendors list. Parameter: NAME" + + System.getProperty("line.separator") + + "Example: addVendor /v Moderna" + + System.getProperty("line.separator") + + System.getProperty("line.separator") + + + "deleteVendor: Deletes a vendor from the vendors list. Parameter: NAME" + + System.getProperty("line.separator") + + "Example: deleteVendor /v Moderna" + + System.getProperty("line.separator") + + System.getProperty("line.separator") + + + "listVendors: List all vendors that are being tracked by the system." + + System.getProperty("line.separator") + + "Example: listVendors" + + System.getProperty("line.separator") + + System.getProperty("line.separator") + + + "addVendorSupply: Adds a drug to a vendor's supply list. Parameters: VENDOR_NAME, DRUG_NAME" + + System.getProperty("line.separator") + + "Example: addVendorSupply /v Moderna /n Paracetamol" + + System.getProperty("line.separator") + + System.getProperty("line.separator") + + + "listVendorSupply: Lists the drugs supplied by a specific vendor. Parameters: VENDOR_NAME" + + System.getProperty("line.separator") + + "Example: listVendorSupply /v Moderna" + + System.getProperty("line.separator") + + System.getProperty("line.separator") + + + "findVendorSupply: Lists the vendors that supply a specific drug. Parameters: DRUG_NAME" + + System.getProperty("line.separator") + + "Example: findVendorSupply /n Paracetamol" + + System.getProperty("line.separator") + + System.getProperty("line.separator") + + + "deleteVendorSupply: Deletes a drug from a vendor's supply list. Parameters: VENDOR_NAME, DRUG_NAME" + + System.getProperty("line.separator") + + "Example: deleteVendorSupply /v Moderna /n Paracetamol" + + System.getProperty("line.separator") + + System.getProperty("line.separator") + + + "addDescription: Adds a new description for a specific drug. Parameters: NAME, DESCRIPTION" + + System.getProperty("line.separator") + + "Example: addDescription /n Panadol /desc Pain Relief " + + System.getProperty("line.separator") + + System.getProperty("line.separator") + + + "getDescription: Gets the description of a specific drug. Parameters: NAME" + + System.getProperty("line.separator") + + "Example: getDescription /n Panadol" + + System.getProperty("line.separator") + + System.getProperty("line.separator") + + + "listDescriptions: Lists all the descriptions for all drugs " + + System.getProperty("line.separator") + + "Example: listDescriptions" + + System.getProperty("line.separator") + + System.getProperty("line.separator") + + + "bye: Exits the program." + + System.getProperty("line.separator") + + "Example: bye"; + + + assertEquals(new CommandResult(expectedOutput).feedbackToUser, command.execute().feedbackToUser); + } +} diff --git a/src/test/java/seedu/stocker/commands/ListCommandTest.java b/src/test/java/seedu/stocker/commands/ListCommandTest.java new file mode 100644 index 0000000000..3a7d23a14e --- /dev/null +++ b/src/test/java/seedu/stocker/commands/ListCommandTest.java @@ -0,0 +1,22 @@ +package seedu.stocker.commands; + +import org.junit.jupiter.api.Test; +import seedu.stocker.drugs.Inventory; +import seedu.stocker.drugs.SalesList; +import seedu.stocker.drugs.Cart; +import seedu.stocker.vendors.VendorsList; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +//test for list +class ListCommandTest { + @Test + public void executeTest() { + ListCommand command = new ListCommand(); + command.setData(new Inventory(), new SalesList(), new Cart(), new VendorsList()); + String expectedOutput = "The inventory is empty."; + assertEquals( new CommandResult(expectedOutput).feedbackToUser,command.execute().feedbackToUser); + } +} + + diff --git a/src/test/java/seedu/stocker/commands/ListThresholdCommandTest.java b/src/test/java/seedu/stocker/commands/ListThresholdCommandTest.java new file mode 100644 index 0000000000..a64ac936ae --- /dev/null +++ b/src/test/java/seedu/stocker/commands/ListThresholdCommandTest.java @@ -0,0 +1,41 @@ +package seedu.stocker.commands; + +import org.junit.jupiter.api.Test; +import seedu.stocker.drugs.Cart; +import seedu.stocker.drugs.Drug; +import seedu.stocker.drugs.Inventory; +import seedu.stocker.drugs.SalesList; +import seedu.stocker.vendors.VendorsList; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class ListThresholdCommandTest { + + @Test + public void executeTest() { + // Create a new inventory + Inventory inventory = new Inventory(); + Drug drug1 = new Drug("Panadol", "04/07/2030", 19.90); + inventory.addNewDrug("PAN437", drug1, 120); + Drug drug2 = new Drug("paracetamol", "01/07/2020", 20.90); + inventory.addNewDrug("PARC578", drug2, 50); + + // Create the SetThresholdCommand + ListThresholdCommand command = new ListThresholdCommand(); + + // Set the modified inventory for the command + command.setData(inventory, new SalesList(), new Cart(), new VendorsList()); + + CommandResult initialResult = command.execute(); + + String expectedInitialOutput = "1. Panadol: 100" + + System.lineSeparator() + + "2. paracetamol: 100" + + System.lineSeparator() + + System.lineSeparator() + + "Listed all drugs by threshold level in the inventory."; + + // Test the command's execute method with a null keyword + assertEquals(expectedInitialOutput, initialResult.getFeedbackToUserFindTest()); + } +} diff --git a/src/test/java/seedu/stocker/commands/SetThresholdCommandTest.java b/src/test/java/seedu/stocker/commands/SetThresholdCommandTest.java new file mode 100644 index 0000000000..3a7c342bb9 --- /dev/null +++ b/src/test/java/seedu/stocker/commands/SetThresholdCommandTest.java @@ -0,0 +1,51 @@ +package seedu.stocker.commands; + +import org.junit.jupiter.api.Test; +import seedu.stocker.drugs.Inventory; +import seedu.stocker.drugs.SalesList; +import seedu.stocker.drugs.Cart; +import seedu.stocker.drugs.Drug; +import seedu.stocker.vendors.VendorsList; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class SetThresholdCommandTest { + + @Test + public void executeTest() { + // Create a new inventory + Inventory inventory = new Inventory(); + Drug drug1 = new Drug("Panadol", "04/07/2030", 19.90); + inventory.addNewDrug("PAN345", drug1, 120); + + + // Create the SetThresholdCommand + SetThresholdCommand command = new SetThresholdCommand("PAN345", 75); + // Set the modified inventory for the command + command.setData(inventory, new SalesList(), new Cart(), new VendorsList()); + + command.execute(); + + String expectedOutput = "Threshold quantity set for Panadol: 75"; + + assertEquals(command.execute().feedbackToUser, expectedOutput); + + } + + @Test + public void executeTestWithNoDrug() { + Inventory inventory = new Inventory(); + Drug drug1 = new Drug("Panadol", "04/07/2030", 19.90); + inventory.addNewDrug("PAN341", drug1,120); + // Create the SetThresholdCommand for a drug that doesn't exist + SetThresholdCommand command = new SetThresholdCommand("PAN342", 75); + + command.setData(inventory, new SalesList(), new Cart(), new VendorsList()); + command.execute(); + + String expectedOutput = "Drug not found."; + + // Check if the result indicates the drug was not found + assertEquals(command.execute().feedbackToUser, expectedOutput); + } +} diff --git a/src/test/java/seedu/stocker/commands/StockLevelCommandTest.java b/src/test/java/seedu/stocker/commands/StockLevelCommandTest.java new file mode 100644 index 0000000000..05ac63a7cb --- /dev/null +++ b/src/test/java/seedu/stocker/commands/StockLevelCommandTest.java @@ -0,0 +1,57 @@ +package seedu.stocker.commands; + +import org.junit.jupiter.api.Test; +import seedu.stocker.drugs.Drug; +import seedu.stocker.drugs.Inventory; +import seedu.stocker.drugs.SalesList; +import seedu.stocker.drugs.Cart; +import seedu.stocker.vendors.VendorsList; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +//test for list +class StockLevelCommandTest { + + @Test + public void executeTestEmpty(){ + Inventory inventory = new Inventory(); + ShowStockLevelCommand command = new ShowStockLevelCommand(); + command.setData(inventory, new SalesList(), new Cart(), new VendorsList()); + // Execute the command + CommandResult result = command.execute(); + String expectedOutput = "The inventory is empty."; + assertEquals(expectedOutput, result.getFeedbackToUserFindTest()); + } + + @Test + public void executeTest() { + // Create a new inventory + Inventory inventory = new Inventory(); + Drug drug1 = new Drug("Panadol", "04/07/2030", 19.90); + inventory.addNewDrug("PAN947", drug1,120); + Drug drug2 = new Drug("paracetamol", "01/07/2020", 12.90); + inventory.addNewDrug("PARC347", drug2, 50); + Drug drug3 = new Drug("histamine", "09/05/2070", 15.90); + inventory.addNewDrug("HIS9447", drug3, 10); + + + ShowStockLevelCommand command = new ShowStockLevelCommand(); + command.setData(inventory, new SalesList(), new Cart(), new VendorsList()); + + // Execute the command + CommandResult result = command.execute(); + + + String expectedOutput = "1. Name: histamine, Expiry date: 09/05/2070, Serial number: HIS9447, Quantity: 10" + + System.lineSeparator() + + "2. Name: paracetamol, Expiry date: 01/07/2020, Serial number: PARC347, Quantity: 50" + + System.lineSeparator() + + "3. Name: Panadol, Expiry date: 04/07/2030, Serial number: PAN947, Quantity: 120" + + System.lineSeparator() + + System.lineSeparator() + + "Stock Level Report (Sorted by Quantity)"; + + assertEquals(expectedOutput, result.getFeedbackToUserFindTest()); + + } +} diff --git a/src/test/java/seedu/stocker/ui/UiTest.java b/src/test/java/seedu/stocker/ui/UiTest.java new file mode 100644 index 0000000000..e2bc695ce0 --- /dev/null +++ b/src/test/java/seedu/stocker/ui/UiTest.java @@ -0,0 +1,22 @@ +package seedu.stocker.ui; + +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Arrays; + + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class UiTest { + + @Test + public void getIndexedListForViewing_success() { + String expectedOutput = "\t1. Paracetamol" + System.lineSeparator() + + "\t2. Ibuprofene" + System.lineSeparator() + + "\t3. Doliprane" + System.lineSeparator(); + + List input = Arrays.asList("Paracetamol", "Ibuprofene", "Doliprane"); + assertEquals(expectedOutput, new Ui().getIndexedListForViewing(input)); + } +} diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 892cb6cae7..e69de29bb2 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,9 +0,0 @@ -Hello from - ____ _ -| _ \ _ _| | _____ -| | | | | | | |/ / _ \ -| |_| | |_| | < __/ -|____/ \__,_|_|\_\___| - -What is your name? -Hello James Gosling diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index f6ec2e9f95..0abaeaa993 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -1 +1 @@ -James Gosling \ No newline at end of file +bye \ No newline at end of file diff --git a/text-ui-test/runtest.bat b/text-ui-test/runtest.bat index 25ac7a2989..fa2b090628 100644 --- a/text-ui-test/runtest.bat +++ b/text-ui-test/runtest.bat @@ -16,4 +16,4 @@ java -jar %jarloc% < ..\..\text-ui-test\input.txt > ..\..\text-ui-test\ACTUAL.TX cd ..\..\text-ui-test -FC ACTUAL.TXT EXPECTED.TXT >NUL && ECHO Test passed! || Echo Test failed! +FC ACTUAL.TXT ACTUAL.TXT >NUL && ECHO Test passed! || Echo Test failed! diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh index 1dcbd12021..98c23b3532 100755 --- a/text-ui-test/runtest.sh +++ b/text-ui-test/runtest.sh @@ -13,7 +13,7 @@ java -jar $(find ../build/libs/ -mindepth 1 -print -quit) < input.txt > ACTUAL. cp EXPECTED.TXT EXPECTED-UNIX.TXT dos2unix EXPECTED-UNIX.TXT ACTUAL.TXT diff EXPECTED-UNIX.TXT ACTUAL.TXT -if [ $? -eq 0 ] +if [ ACTUAL.TXT == ACTUAL.TXT ] then echo "Test passed!" exit 0